Compare commits

..

16 Commits

Author SHA1 Message Date
riperiperi
9ef94c8292 ARMeilleure: Move TPIDR_EL0 and TPIDRRO_EL0 to NativeContext (#4661)
* ARMeilleure: Move TPIDR_EL0 and TPIDRRO_EL0 to NativeContext

Some games access these system registers several tens of thousands of times in a second from many different threads. While this isn't really crippling, it is a lot of wasted time spent in a reverse pinvoke transition.

Example games are Pokemon Scarlet/Violet and BOTW. These games have a lot of different potential bottlenecks so it's unlikely you will see a consistent improvement, but it definitely disappears from the cpu profile.

* Remove unreachable code.

* Add ulong conversion for offsets

* Nit
2023-04-11 08:55:04 +02:00
riperiperi
915d6d044c OpenGL: Fix OBS/Overlays again by binding FB before present (#4668)
This seems to have been removed by the Post-Processing PR, but it is required for the display in OBS to be the right way up and properly scaled.

I've tested this with AA and FSR on MK8D and it seems to behave properly. Testing is welcome.
2023-04-11 08:32:31 +02:00
MutantAura
a4780ab33b Force activate parent window before dialog is shown (#4663) 2023-04-11 00:04:31 +02:00
TSRBerry
a947a45d81 gtk: Fix a NRE when disposing OpenGL (#4648) 2023-04-10 17:00:23 +02:00
riperiperi
9db73f74cf ARMeilleure: Respect FZ/RM flags for all floating point operations (#4618)
* ARMeilleure: Respect Fz flag for all floating point operations.

This is a change in strategy for emulating the Fz FPCR flag. Before, it was set before instructions that "needed it" and reset after. However, this missed a few hot instructions like the multiplication instruction, and the entirety of A32.

The new strategy is to set the Fz flag only in the following circumstances:

- Set to match FPCR before translated functions/loop are executed.
- Reset when calling SoftFloat methods, set when returning.
- Reset when exiting execution.

This allows us to remove the code around the existing Fz aware instructions, and get the accuracy benefits on all floating point instructions executed while in translated code.

Single step executions now need to be called with a context wrapper - right now it just contains the Fz flag initialization, and won't actually do anything on ARM.

This fixes a bug in Breath of the Wild where some physics interactions could randomly crash the game due to subnormal values not flushing to zero.

This is draft right now because I need to answer the questions:
- Does dotnet avoid changing the value of Mxcsr?
- Is it a good idea to assume that? Or should the flag set/restore be done on every managed method call, not just softfloat?
- If we assume that, do we want a unit test to verify the behaviour?

I recommend testing a bunch of games, especially games affected when this was originally added, such as #1611.

* Remove unused method

* Use FMA for Fmadd, Fmsub, Fnmadd, Fnmsub, Fmla, Fmls

...when available.

Similar implementation to A32

* Use FMA for Frecps, Frsqrts

* Don't set DAZ.

* Add round mode to ARM FP mode

* Fix mistakes

* Add test for FP state when calling managed methods

* Add explanatory comment to test.

* Cleanup

* Add A64 FPCR flags

* Vrintx_S A32 fast path on A64 backend

* Address feedback 1, re-enable DAZ

* Fix FMA instructions By Elem

* Address feedback
2023-04-10 12:22:58 +02:00
gdkchan
a1efd87c45 Implement remaining Arm64 HINT instructions as NOP (#4658)
* Implement remaining HINT instructions as NOP

* Split HINT encodings more to account for CSDB
2023-04-09 13:21:16 -03:00
jhorv
49be977588 Eliminate boxing allocations caused by ISampledData structs (#4556)
* Redesign use of ISampledData for accessing the SamplingNumber value on input data structs.

* Always read SamplingNumber as little-endian

* Restored field order for SixAxisSensorState. Rework to allow possibility of non-zero offsets for the SamplingNumber field. Set StructLayout Pack=8 - the KeyboardState struct is 4 bytes shorter with any other value.

* fix spelling

Co-authored-by: riperiperi <rhy3756547@hotmail.com>

* set Pack = 1 for ISampledDataStruct types, added Unknown field to KeyboardState

* extend size of KeyboardModifier

---------

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2023-04-05 17:42:32 -03:00
Mary
c95be55091 vulkan: Cleanup PhysicalDevice and Instance querying (#4632)
* vulkan: Move most of the properties enumeration to VulkanPhysicalDevice

That clean up a bit of duplicate logic.
Also move to use an hashset for device extensions.

* vulkan: Move instance querying to VulkanInstance

Also cleanup code to use span when possible instead of unsafe pointers.

* Address gdkchan's comments
2023-04-05 14:48:38 -03:00
dependabot[bot]
63dedbda86 nuget: bump System.IdentityModel.Tokens.Jwt from 6.27.0 to 6.28.1 (#4639)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.27.0 to 6.28.1.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.27.0...6.28.1)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-05 07:55:57 +02:00
gdkchan
c532118d94 Use index fragment shader output when dual source blend is enabled (#4404)
* Use index fragment shader output when dual source blend is enabled

* Shader cache version bump

* Actually set DualSourceBlendEnabled to true

* Fix XML doc

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-04-05 05:25:19 +02:00
TSRBerry
52d6f2e656 hle: Set ProcessResult name from NACP (#4633)
* Extract titleName from nacp

* Address formatting feedback

* Check if the desired language is actually available
2023-04-05 03:34:21 +02:00
TSRBerry
c9bc4eaf58 Fix missing string enum converters for the config (#4634)
* Fix missing string enum converters for the config

* Revert changing KeyboardHotkeys to struct

This needs to be done because
Avalonia's TwoWay Binding breaks otherwise.
2023-04-03 15:37:27 +02:00
Andrey Sukharev
3249f8ff41 Source generated json serializers (#4582)
* Use source generated json serializers in order to improve code trimming

* Use strongly typed github releases model to fetch updates instead of raw Newtonsoft.Json parsing

* Use separate model for LogEventArgs serialization

* Make dynamic object formatter static. Fix string builder pooling.

* Do not inherit json version of LogEventArgs from EventArgs

* Fix extra space in object formatting

* Write log json directly to stream instead of using buffer writer

* Rebase fixes

* Rebase fixes

* Rebase fixes

* Enforce block-scoped namespaces in the solution. Convert style for existing code

* Apply suggestions from code review

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Rebase indent fix

* Fix indent

* Delete unnecessary json properties

* Rebase fix

* Remove overridden json property names as they are handled in the options

* Apply suggestions from code review

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Use default json options in github api calls

* Indentation and spacing fixes

* Fix json serialization

* Fix missing JsonConverter for config enums

* Add double \n\n after the whole string, not inside join

---------

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2023-04-03 10:14:19 +00:00
dependabot[bot]
1b41b285ac nuget: bump DynamicData from 7.12.11 to 7.13.1 (#4490)
Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 7.12.11 to 7.13.1.
- [Release notes](https://github.com/reactiveui/DynamicData/releases)
- [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md)
- [Commits](https://github.com/reactiveui/DynamicData/compare/7.12.11...7.13.1)

---
updated-dependencies:
- dependency-name: DynamicData
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-04-01 08:23:09 +00:00
Mary
f5a6f45b27 vulkan: Separate debug utils logic from VulkanInitialization (#4609)
* vulkan: Separate debug utils logic from VulkanInitialization

Also checks for VK_EXT_debug_utils existence instead of force enabling it and allow possible error during messenger init

* Address gdkchan's comment

* Use CreateDebugUtilsMessenger Span variant
2023-04-01 08:05:02 +00:00
TSRBerry
210557951b nuget: bump Avalonia dependencies from 0.10.18 to 0.10.19 (#4602)
* infra: Update Avalonia to 0.10.19

* infra: Update XamlNameReferenceGenerator to 1.6.1
2023-04-01 07:27:34 +00:00
142 changed files with 2591 additions and 1307 deletions

View File

@@ -63,6 +63,10 @@ dotnet_code_quality_unused_parameters = all:suggestion
#### C# Coding Conventions #### #### C# Coding Conventions ####
# Namespace preferences
csharp_style_namespace_declarations = block_scoped:warning
resharper_csharp_namespace_body = block_scoped
# var preferences # var preferences
csharp_style_var_elsewhere = false:silent csharp_style_var_elsewhere = false:silent
csharp_style_var_for_built_in_types = false:silent csharp_style_var_for_built_in_types = false:silent

View File

@@ -226,6 +226,8 @@ namespace ARMeilleure.CodeGen.Arm64
Add(Intrinsic.Arm64MlsVe, new IntrinsicInfo(0x2f004000u, IntrinsicType.VectorTernaryRdByElem)); Add(Intrinsic.Arm64MlsVe, new IntrinsicInfo(0x2f004000u, IntrinsicType.VectorTernaryRdByElem));
Add(Intrinsic.Arm64MlsV, new IntrinsicInfo(0x2e209400u, IntrinsicType.VectorTernaryRd)); Add(Intrinsic.Arm64MlsV, new IntrinsicInfo(0x2e209400u, IntrinsicType.VectorTernaryRd));
Add(Intrinsic.Arm64MoviV, new IntrinsicInfo(0x0f000400u, IntrinsicType.VectorMovi)); Add(Intrinsic.Arm64MoviV, new IntrinsicInfo(0x0f000400u, IntrinsicType.VectorMovi));
Add(Intrinsic.Arm64MrsFpcr, new IntrinsicInfo(0xd53b4400u, IntrinsicType.GetRegister));
Add(Intrinsic.Arm64MsrFpcr, new IntrinsicInfo(0xd51b4400u, IntrinsicType.SetRegister));
Add(Intrinsic.Arm64MrsFpsr, new IntrinsicInfo(0xd53b4420u, IntrinsicType.GetRegister)); Add(Intrinsic.Arm64MrsFpsr, new IntrinsicInfo(0xd53b4420u, IntrinsicType.GetRegister));
Add(Intrinsic.Arm64MsrFpsr, new IntrinsicInfo(0xd51b4420u, IntrinsicType.SetRegister)); Add(Intrinsic.Arm64MsrFpsr, new IntrinsicInfo(0xd51b4420u, IntrinsicType.SetRegister));
Add(Intrinsic.Arm64MulVe, new IntrinsicInfo(0x0f008000u, IntrinsicType.VectorBinaryByElem)); Add(Intrinsic.Arm64MulVe, new IntrinsicInfo(0x0f008000u, IntrinsicType.VectorBinaryByElem));

View File

@@ -268,11 +268,13 @@ namespace ARMeilleure.CodeGen.X86
Add(X86Instruction.Vblendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4a, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Vblendvps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3a4a, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vcvtph2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3813, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Vcvtph2ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f3813, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vcvtps2ph, new InstructionInfo(0x000f3a1d, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Vcvtps2ph, new InstructionInfo(0x000f3a1d, BadOp, BadOp, BadOp, BadOp, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vfmadd231pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b8, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
Add(X86Instruction.Vfmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b8, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Vfmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b8, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vfmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); Add(X86Instruction.Vfmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
Add(X86Instruction.Vfmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Vfmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38b9, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vfmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); Add(X86Instruction.Vfmsub231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
Add(X86Instruction.Vfmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Vfmsub231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bb, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vfnmadd231pd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bc, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
Add(X86Instruction.Vfnmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bc, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Vfnmadd231ps, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bc, InstructionFlags.Vex | InstructionFlags.Prefix66));
Add(X86Instruction.Vfnmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW)); Add(X86Instruction.Vfnmadd231sd, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66 | InstructionFlags.RexW));
Add(X86Instruction.Vfnmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66)); Add(X86Instruction.Vfnmadd231ss, new InstructionInfo(BadOp, BadOp, BadOp, BadOp, 0x000f38bd, InstructionFlags.Vex | InstructionFlags.Prefix66));

View File

@@ -249,10 +249,9 @@ namespace ARMeilleure.CodeGen.X86
case IntrinsicType.Mxcsr: case IntrinsicType.Mxcsr:
{ {
Operand offset = operation.GetSource(0); Operand offset = operation.GetSource(0);
Operand bits = operation.GetSource(1);
Debug.Assert(offset.Kind == OperandKind.Constant && bits.Kind == OperandKind.Constant); Debug.Assert(offset.Kind == OperandKind.Constant);
Debug.Assert(offset.Type == OperandType.I32 && bits.Type == OperandType.I32); Debug.Assert(offset.Type == OperandType.I32);
int offs = offset.AsInt32() + context.CallArgsRegionSize; int offs = offset.AsInt32() + context.CallArgsRegionSize;
@@ -261,21 +260,23 @@ namespace ARMeilleure.CodeGen.X86
Debug.Assert(HardwareCapabilities.SupportsSse || HardwareCapabilities.SupportsVexEncoding); Debug.Assert(HardwareCapabilities.SupportsSse || HardwareCapabilities.SupportsVexEncoding);
context.Assembler.Stmxcsr(memOp); if (operation.Intrinsic == Intrinsic.X86Ldmxcsr)
if (operation.Intrinsic == Intrinsic.X86Mxcsrmb)
{ {
context.Assembler.Or(memOp, bits, OperandType.I32); Operand bits = operation.GetSource(1);
Debug.Assert(bits.Type == OperandType.I32);
context.Assembler.Mov(memOp, bits, OperandType.I32);
context.Assembler.Ldmxcsr(memOp);
} }
else /* if (intrinOp.Intrinsic == Intrinsic.X86Mxcsrub) */ else if (operation.Intrinsic == Intrinsic.X86Stmxcsr)
{ {
Operand notBits = Const(~bits.AsInt32()); Operand dest = operation.Destination;
Debug.Assert(dest.Type == OperandType.I32);
context.Assembler.And(memOp, notBits, OperandType.I32); context.Assembler.Stmxcsr(memOp);
context.Assembler.Mov(dest, memOp, OperandType.I32);
} }
context.Assembler.Ldmxcsr(memOp);
break; break;
} }

View File

@@ -60,6 +60,7 @@ namespace ARMeilleure.CodeGen.X86
Add(Intrinsic.X86Haddpd, new IntrinsicInfo(X86Instruction.Haddpd, IntrinsicType.Binary)); Add(Intrinsic.X86Haddpd, new IntrinsicInfo(X86Instruction.Haddpd, IntrinsicType.Binary));
Add(Intrinsic.X86Haddps, new IntrinsicInfo(X86Instruction.Haddps, IntrinsicType.Binary)); Add(Intrinsic.X86Haddps, new IntrinsicInfo(X86Instruction.Haddps, IntrinsicType.Binary));
Add(Intrinsic.X86Insertps, new IntrinsicInfo(X86Instruction.Insertps, IntrinsicType.TernaryImm)); Add(Intrinsic.X86Insertps, new IntrinsicInfo(X86Instruction.Insertps, IntrinsicType.TernaryImm));
Add(Intrinsic.X86Ldmxcsr, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr));
Add(Intrinsic.X86Maxpd, new IntrinsicInfo(X86Instruction.Maxpd, IntrinsicType.Binary)); Add(Intrinsic.X86Maxpd, new IntrinsicInfo(X86Instruction.Maxpd, IntrinsicType.Binary));
Add(Intrinsic.X86Maxps, new IntrinsicInfo(X86Instruction.Maxps, IntrinsicType.Binary)); Add(Intrinsic.X86Maxps, new IntrinsicInfo(X86Instruction.Maxps, IntrinsicType.Binary));
Add(Intrinsic.X86Maxsd, new IntrinsicInfo(X86Instruction.Maxsd, IntrinsicType.Binary)); Add(Intrinsic.X86Maxsd, new IntrinsicInfo(X86Instruction.Maxsd, IntrinsicType.Binary));
@@ -75,8 +76,6 @@ namespace ARMeilleure.CodeGen.X86
Add(Intrinsic.X86Mulps, new IntrinsicInfo(X86Instruction.Mulps, IntrinsicType.Binary)); Add(Intrinsic.X86Mulps, new IntrinsicInfo(X86Instruction.Mulps, IntrinsicType.Binary));
Add(Intrinsic.X86Mulsd, new IntrinsicInfo(X86Instruction.Mulsd, IntrinsicType.Binary)); Add(Intrinsic.X86Mulsd, new IntrinsicInfo(X86Instruction.Mulsd, IntrinsicType.Binary));
Add(Intrinsic.X86Mulss, new IntrinsicInfo(X86Instruction.Mulss, IntrinsicType.Binary)); Add(Intrinsic.X86Mulss, new IntrinsicInfo(X86Instruction.Mulss, IntrinsicType.Binary));
Add(Intrinsic.X86Mxcsrmb, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Mask bits.
Add(Intrinsic.X86Mxcsrub, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr)); // Unmask bits.
Add(Intrinsic.X86Paddb, new IntrinsicInfo(X86Instruction.Paddb, IntrinsicType.Binary)); Add(Intrinsic.X86Paddb, new IntrinsicInfo(X86Instruction.Paddb, IntrinsicType.Binary));
Add(Intrinsic.X86Paddd, new IntrinsicInfo(X86Instruction.Paddd, IntrinsicType.Binary)); Add(Intrinsic.X86Paddd, new IntrinsicInfo(X86Instruction.Paddd, IntrinsicType.Binary));
Add(Intrinsic.X86Paddq, new IntrinsicInfo(X86Instruction.Paddq, IntrinsicType.Binary)); Add(Intrinsic.X86Paddq, new IntrinsicInfo(X86Instruction.Paddq, IntrinsicType.Binary));
@@ -160,6 +159,7 @@ namespace ARMeilleure.CodeGen.X86
Add(Intrinsic.X86Sqrtps, new IntrinsicInfo(X86Instruction.Sqrtps, IntrinsicType.Unary)); Add(Intrinsic.X86Sqrtps, new IntrinsicInfo(X86Instruction.Sqrtps, IntrinsicType.Unary));
Add(Intrinsic.X86Sqrtsd, new IntrinsicInfo(X86Instruction.Sqrtsd, IntrinsicType.Unary)); Add(Intrinsic.X86Sqrtsd, new IntrinsicInfo(X86Instruction.Sqrtsd, IntrinsicType.Unary));
Add(Intrinsic.X86Sqrtss, new IntrinsicInfo(X86Instruction.Sqrtss, IntrinsicType.Unary)); Add(Intrinsic.X86Sqrtss, new IntrinsicInfo(X86Instruction.Sqrtss, IntrinsicType.Unary));
Add(Intrinsic.X86Stmxcsr, new IntrinsicInfo(X86Instruction.None, IntrinsicType.Mxcsr));
Add(Intrinsic.X86Subpd, new IntrinsicInfo(X86Instruction.Subpd, IntrinsicType.Binary)); Add(Intrinsic.X86Subpd, new IntrinsicInfo(X86Instruction.Subpd, IntrinsicType.Binary));
Add(Intrinsic.X86Subps, new IntrinsicInfo(X86Instruction.Subps, IntrinsicType.Binary)); Add(Intrinsic.X86Subps, new IntrinsicInfo(X86Instruction.Subps, IntrinsicType.Binary));
Add(Intrinsic.X86Subsd, new IntrinsicInfo(X86Instruction.Subsd, IntrinsicType.Binary)); Add(Intrinsic.X86Subsd, new IntrinsicInfo(X86Instruction.Subsd, IntrinsicType.Binary));
@@ -170,11 +170,13 @@ namespace ARMeilleure.CodeGen.X86
Add(Intrinsic.X86Unpcklps, new IntrinsicInfo(X86Instruction.Unpcklps, IntrinsicType.Binary)); Add(Intrinsic.X86Unpcklps, new IntrinsicInfo(X86Instruction.Unpcklps, IntrinsicType.Binary));
Add(Intrinsic.X86Vcvtph2ps, new IntrinsicInfo(X86Instruction.Vcvtph2ps, IntrinsicType.Unary)); Add(Intrinsic.X86Vcvtph2ps, new IntrinsicInfo(X86Instruction.Vcvtph2ps, IntrinsicType.Unary));
Add(Intrinsic.X86Vcvtps2ph, new IntrinsicInfo(X86Instruction.Vcvtps2ph, IntrinsicType.BinaryImm)); Add(Intrinsic.X86Vcvtps2ph, new IntrinsicInfo(X86Instruction.Vcvtps2ph, IntrinsicType.BinaryImm));
Add(Intrinsic.X86Vfmadd231pd, new IntrinsicInfo(X86Instruction.Vfmadd231pd, IntrinsicType.Fma));
Add(Intrinsic.X86Vfmadd231ps, new IntrinsicInfo(X86Instruction.Vfmadd231ps, IntrinsicType.Fma)); Add(Intrinsic.X86Vfmadd231ps, new IntrinsicInfo(X86Instruction.Vfmadd231ps, IntrinsicType.Fma));
Add(Intrinsic.X86Vfmadd231sd, new IntrinsicInfo(X86Instruction.Vfmadd231sd, IntrinsicType.Fma)); Add(Intrinsic.X86Vfmadd231sd, new IntrinsicInfo(X86Instruction.Vfmadd231sd, IntrinsicType.Fma));
Add(Intrinsic.X86Vfmadd231ss, new IntrinsicInfo(X86Instruction.Vfmadd231ss, IntrinsicType.Fma)); Add(Intrinsic.X86Vfmadd231ss, new IntrinsicInfo(X86Instruction.Vfmadd231ss, IntrinsicType.Fma));
Add(Intrinsic.X86Vfmsub231sd, new IntrinsicInfo(X86Instruction.Vfmsub231sd, IntrinsicType.Fma)); Add(Intrinsic.X86Vfmsub231sd, new IntrinsicInfo(X86Instruction.Vfmsub231sd, IntrinsicType.Fma));
Add(Intrinsic.X86Vfmsub231ss, new IntrinsicInfo(X86Instruction.Vfmsub231ss, IntrinsicType.Fma)); Add(Intrinsic.X86Vfmsub231ss, new IntrinsicInfo(X86Instruction.Vfmsub231ss, IntrinsicType.Fma));
Add(Intrinsic.X86Vfnmadd231pd, new IntrinsicInfo(X86Instruction.Vfnmadd231pd, IntrinsicType.Fma));
Add(Intrinsic.X86Vfnmadd231ps, new IntrinsicInfo(X86Instruction.Vfnmadd231ps, IntrinsicType.Fma)); Add(Intrinsic.X86Vfnmadd231ps, new IntrinsicInfo(X86Instruction.Vfnmadd231ps, IntrinsicType.Fma));
Add(Intrinsic.X86Vfnmadd231sd, new IntrinsicInfo(X86Instruction.Vfnmadd231sd, IntrinsicType.Fma)); Add(Intrinsic.X86Vfnmadd231sd, new IntrinsicInfo(X86Instruction.Vfnmadd231sd, IntrinsicType.Fma));
Add(Intrinsic.X86Vfnmadd231ss, new IntrinsicInfo(X86Instruction.Vfnmadd231ss, IntrinsicType.Fma)); Add(Intrinsic.X86Vfnmadd231ss, new IntrinsicInfo(X86Instruction.Vfnmadd231ss, IntrinsicType.Fma));

View File

@@ -0,0 +1,15 @@
using System;
namespace ARMeilleure.CodeGen.X86
{
[Flags]
enum Mxcsr
{
Ftz = 1 << 15, // Flush To Zero.
Rhi = 1 << 14, // Round Mode high bit.
Rlo = 1 << 13, // Round Mode low bit.
Um = 1 << 11, // Underflow Mask.
Dm = 1 << 8, // Denormal Mask.
Daz = 1 << 6 // Denormals Are Zero.
}
}

View File

@@ -120,12 +120,18 @@ namespace ARMeilleure.CodeGen.X86
break; break;
case Instruction.Extended: case Instruction.Extended:
if (node.Intrinsic == Intrinsic.X86Mxcsrmb || node.Intrinsic == Intrinsic.X86Mxcsrub) if (node.Intrinsic == Intrinsic.X86Ldmxcsr)
{ {
int stackOffset = stackAlloc.Allocate(OperandType.I32); int stackOffset = stackAlloc.Allocate(OperandType.I32);
node.SetSources(new Operand[] { Const(stackOffset), node.GetSource(0) }); node.SetSources(new Operand[] { Const(stackOffset), node.GetSource(0) });
} }
else if (node.Intrinsic == Intrinsic.X86Stmxcsr)
{
int stackOffset = stackAlloc.Allocate(OperandType.I32);
node.SetSources(new Operand[] { Const(stackOffset) });
}
break; break;
} }
} }

View File

@@ -208,11 +208,13 @@ namespace ARMeilleure.CodeGen.X86
Vblendvps, Vblendvps,
Vcvtph2ps, Vcvtph2ps,
Vcvtps2ph, Vcvtps2ph,
Vfmadd231pd,
Vfmadd231ps, Vfmadd231ps,
Vfmadd231sd, Vfmadd231sd,
Vfmadd231ss, Vfmadd231ss,
Vfmsub231sd, Vfmsub231sd,
Vfmsub231ss, Vfmsub231ss,
Vfnmadd231pd,
Vfnmadd231ps, Vfnmadd231ps,
Vfnmadd231sd, Vfnmadd231sd,
Vfnmadd231ss, Vfnmadd231ss,

View File

@@ -1,6 +1,7 @@
namespace ARMeilleure.Decoders; namespace ARMeilleure.Decoders
interface IOpCode32Exception
{ {
int Id { get; } interface IOpCode32Exception
{
int Id { get; }
}
} }

View File

@@ -108,6 +108,13 @@ namespace ARMeilleure.Decoders
SetA64("11001010xx0xxxxxxxxxxxxxxxxxxxxx", InstName.Eor, InstEmit.Eor, OpCodeAluRs.Create); SetA64("11001010xx0xxxxxxxxxxxxxxxxxxxxx", InstName.Eor, InstEmit.Eor, OpCodeAluRs.Create);
SetA64("00010011100xxxxx0xxxxxxxxxxxxxxx", InstName.Extr, InstEmit.Extr, OpCodeAluRs.Create); SetA64("00010011100xxxxx0xxxxxxxxxxxxxxx", InstName.Extr, InstEmit.Extr, OpCodeAluRs.Create);
SetA64("10010011110xxxxxxxxxxxxxxxxxxxxx", InstName.Extr, InstEmit.Extr, OpCodeAluRs.Create); SetA64("10010011110xxxxxxxxxxxxxxxxxxxxx", InstName.Extr, InstEmit.Extr, OpCodeAluRs.Create);
SetA64("11010101000000110010000011011111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
SetA64("11010101000000110010000011111111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
SetA64("110101010000001100100001xxx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
SetA64("1101010100000011001000100xx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
SetA64("1101010100000011001000101>>11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
SetA64("110101010000001100100011xxx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
SetA64("11010101000000110010>>xxxxx11111", InstName.Hint, InstEmit.Nop, OpCodeSystem.Create); // Reserved Hint
SetA64("11010101000000110011xxxx11011111", InstName.Isb, InstEmit.Isb, OpCodeSystem.Create); SetA64("11010101000000110011xxxx11011111", InstName.Isb, InstEmit.Isb, OpCodeSystem.Create);
SetA64("xx001000110xxxxx1xxxxxxxxxxxxxxx", InstName.Ldar, InstEmit.Ldar, OpCodeMemEx.Create); SetA64("xx001000110xxxxx1xxxxxxxxxxxxxxx", InstName.Ldar, InstEmit.Ldar, OpCodeMemEx.Create);
SetA64("1x001000011xxxxx1xxxxxxxxxxxxxxx", InstName.Ldaxp, InstEmit.Ldaxp, OpCodeMemEx.Create); SetA64("1x001000011xxxxx1xxxxxxxxxxxxxxx", InstName.Ldaxp, InstEmit.Ldaxp, OpCodeMemEx.Create);

View File

@@ -615,14 +615,11 @@ namespace ARMeilleure.Instructions
{ {
return EmitSse41ProcessNaNsOpF(context, (op1, op2) => return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
{ {
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
{
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
Intrinsic addInst = (op.Size & 1) == 0 ? Intrinsic.X86Addps : Intrinsic.X86Addpd; Intrinsic addInst = (op.Size & 1) == 0 ? Intrinsic.X86Addps : Intrinsic.X86Addpd;
return context.AddIntrinsic(addInst, op1, op2); return context.AddIntrinsic(addInst, op1, op2);
}, scalar: false, op1, op2);
}, scalar: false, op1, op2); }, scalar: false, op1, op2);
}); });
} }
@@ -696,17 +693,33 @@ namespace ARMeilleure.Instructions
Operand n = GetVec(op.Rn); Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm); Operand m = GetVec(op.Rm);
Operand res;
if (op.Size == 0) if (op.Size == 0)
{ {
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); if (Optimizations.UseFma)
res = context.AddIntrinsic(Intrinsic.X86Addss, a, res); {
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ss, a, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
res = context.AddIntrinsic(Intrinsic.X86Addss, a, res);
}
context.Copy(d, context.VectorZeroUpper96(res)); context.Copy(d, context.VectorZeroUpper96(res));
} }
else /* if (op.Size == 1) */ else /* if (op.Size == 1) */
{ {
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); if (Optimizations.UseFma)
res = context.AddIntrinsic(Intrinsic.X86Addsd, a, res); {
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231sd, a, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
res = context.AddIntrinsic(Intrinsic.X86Addsd, a, res);
}
context.Copy(d, context.VectorZeroUpper64(res)); context.Copy(d, context.VectorZeroUpper64(res));
} }
@@ -730,10 +743,7 @@ namespace ARMeilleure.Instructions
{ {
EmitSse41ProcessNaNsOpF(context, (op1, op2) => EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
{ {
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
{
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
}, scalar: true, op1, op2);
}, scalar: true); }, scalar: true);
} }
else else
@@ -755,10 +765,7 @@ namespace ARMeilleure.Instructions
{ {
EmitSse41ProcessNaNsOpF(context, (op1, op2) => EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
{ {
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
{
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
}, scalar: false, op1, op2);
}, scalar: false); }, scalar: false);
} }
else else
@@ -886,10 +893,7 @@ namespace ARMeilleure.Instructions
{ {
return EmitSse41ProcessNaNsOpF(context, (op1, op2) => return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
{ {
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
{
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
}, scalar: false, op1, op2);
}, scalar: false, op1, op2); }, scalar: false, op1, op2);
}); });
} }
@@ -914,10 +918,7 @@ namespace ARMeilleure.Instructions
{ {
return EmitSse41ProcessNaNsOpF(context, (op1, op2) => return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
{ {
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
{
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
}, scalar: false, op1, op2);
}, scalar: false, op1, op2); }, scalar: false, op1, op2);
}); });
} }
@@ -940,10 +941,7 @@ namespace ARMeilleure.Instructions
{ {
EmitSse41ProcessNaNsOpF(context, (op1, op2) => EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
{ {
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
{
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
}, scalar: true, op1, op2);
}, scalar: true); }, scalar: true);
} }
else else
@@ -965,10 +963,7 @@ namespace ARMeilleure.Instructions
{ {
EmitSse41ProcessNaNsOpF(context, (op1, op2) => EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
{ {
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
{
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
}, scalar: false, op1, op2);
}, scalar: false); }, scalar: false);
} }
else else
@@ -1096,10 +1091,7 @@ namespace ARMeilleure.Instructions
{ {
return EmitSse41ProcessNaNsOpF(context, (op1, op2) => return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
{ {
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
{
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
}, scalar: false, op1, op2);
}, scalar: false, op1, op2); }, scalar: false, op1, op2);
}); });
} }
@@ -1124,10 +1116,7 @@ namespace ARMeilleure.Instructions
{ {
return EmitSse41ProcessNaNsOpF(context, (op1, op2) => return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
{ {
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
{
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
}, scalar: false, op1, op2);
}, scalar: false, op1, op2); }, scalar: false, op1, op2);
}); });
} }
@@ -1146,6 +1135,37 @@ namespace ARMeilleure.Instructions
{ {
InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlaSe); InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlaSe);
} }
else if (Optimizations.UseFma)
{
OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp;
Operand d = GetVec(op.Rd);
Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm);
int sizeF = op.Size & 1;
if (sizeF == 0)
{
int shuffleMask = op.Index | op.Index << 2 | op.Index << 4 | op.Index << 6;
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ss, d, n, res);
context.Copy(d, context.VectorZeroUpper96(res));
}
else /* if (sizeF == 1) */
{
int shuffleMask = op.Index | op.Index << 1;
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231sd, d, n, res);
context.Copy(d, context.VectorZeroUpper64(res));
}
}
else else
{ {
EmitScalarTernaryOpByElemF(context, (op1, op2, op3) => EmitScalarTernaryOpByElemF(context, (op1, op2, op3) =>
@@ -1171,11 +1191,19 @@ namespace ARMeilleure.Instructions
int sizeF = op.Size & 1; int sizeF = op.Size & 1;
Operand res;
if (sizeF == 0) if (sizeF == 0)
{ {
Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m); if (Optimizations.UseFma)
{
res = context.AddIntrinsic(Intrinsic.X86Addps, d, res); res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ps, d, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
res = context.AddIntrinsic(Intrinsic.X86Addps, d, res);
}
if (op.RegisterSize == RegisterSize.Simd64) if (op.RegisterSize == RegisterSize.Simd64)
{ {
@@ -1186,9 +1214,15 @@ namespace ARMeilleure.Instructions
} }
else /* if (sizeF == 1) */ else /* if (sizeF == 1) */
{ {
Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m); if (Optimizations.UseFma)
{
res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res); res = context.AddIntrinsic(Intrinsic.X86Vfmadd231pd, d, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res);
}
context.Copy(d, res); context.Copy(d, res);
} }
@@ -1224,8 +1258,15 @@ namespace ARMeilleure.Instructions
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask)); Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res); if (Optimizations.UseFma)
res = context.AddIntrinsic(Intrinsic.X86Addps, d, res); {
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231ps, d, n, res);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res);
res = context.AddIntrinsic(Intrinsic.X86Addps, d, res);
}
if (op.RegisterSize == RegisterSize.Simd64) if (op.RegisterSize == RegisterSize.Simd64)
{ {
@@ -1240,8 +1281,15 @@ namespace ARMeilleure.Instructions
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask)); Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res); if (Optimizations.UseFma)
res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res); {
res = context.AddIntrinsic(Intrinsic.X86Vfmadd231pd, d, n, res);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res);
res = context.AddIntrinsic(Intrinsic.X86Addpd, d, res);
}
context.Copy(d, res); context.Copy(d, res);
} }
@@ -1261,6 +1309,37 @@ namespace ARMeilleure.Instructions
{ {
InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlsSe); InstEmitSimdHelperArm64.EmitScalarTernaryOpFRdByElem(context, Intrinsic.Arm64FmlsSe);
} }
else if (Optimizations.UseFma)
{
OpCodeSimdRegElemF op = (OpCodeSimdRegElemF)context.CurrOp;
Operand d = GetVec(op.Rd);
Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm);
int sizeF = op.Size & 1;
if (sizeF == 0)
{
int shuffleMask = op.Index | op.Index << 2 | op.Index << 4 | op.Index << 6;
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, d, n, res);
context.Copy(d, context.VectorZeroUpper96(res));
}
else /* if (sizeF == 1) */
{
int shuffleMask = op.Index | op.Index << 1;
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, d, n, res);
context.Copy(d, context.VectorZeroUpper64(res));
}
}
else else
{ {
EmitScalarTernaryOpByElemF(context, (op1, op2, op3) => EmitScalarTernaryOpByElemF(context, (op1, op2, op3) =>
@@ -1286,11 +1365,19 @@ namespace ARMeilleure.Instructions
int sizeF = op.Size & 1; int sizeF = op.Size & 1;
Operand res;
if (sizeF == 0) if (sizeF == 0)
{ {
Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m); if (Optimizations.UseFma)
{
res = context.AddIntrinsic(Intrinsic.X86Subps, d, res); res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, d, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
res = context.AddIntrinsic(Intrinsic.X86Subps, d, res);
}
if (op.RegisterSize == RegisterSize.Simd64) if (op.RegisterSize == RegisterSize.Simd64)
{ {
@@ -1301,9 +1388,15 @@ namespace ARMeilleure.Instructions
} }
else /* if (sizeF == 1) */ else /* if (sizeF == 1) */
{ {
Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m); if (Optimizations.UseFma)
{
res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res); res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, d, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res);
}
context.Copy(d, res); context.Copy(d, res);
} }
@@ -1339,8 +1432,15 @@ namespace ARMeilleure.Instructions
Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask)); Operand res = context.AddIntrinsic(Intrinsic.X86Shufps, m, m, Const(shuffleMask));
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res); if (Optimizations.UseFma)
res = context.AddIntrinsic(Intrinsic.X86Subps, d, res); {
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, d, n, res);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, res);
res = context.AddIntrinsic(Intrinsic.X86Subps, d, res);
}
if (op.RegisterSize == RegisterSize.Simd64) if (op.RegisterSize == RegisterSize.Simd64)
{ {
@@ -1355,8 +1455,15 @@ namespace ARMeilleure.Instructions
Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask)); Operand res = context.AddIntrinsic(Intrinsic.X86Shufpd, m, m, Const(shuffleMask));
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res); if (Optimizations.UseFma)
res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res); {
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, d, n, res);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, res);
res = context.AddIntrinsic(Intrinsic.X86Subpd, d, res);
}
context.Copy(d, res); context.Copy(d, res);
} }
@@ -1385,17 +1492,33 @@ namespace ARMeilleure.Instructions
Operand n = GetVec(op.Rn); Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm); Operand m = GetVec(op.Rm);
Operand res;
if (op.Size == 0) if (op.Size == 0)
{ {
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); if (Optimizations.UseFma)
res = context.AddIntrinsic(Intrinsic.X86Subss, a, res); {
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, a, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
res = context.AddIntrinsic(Intrinsic.X86Subss, a, res);
}
context.Copy(d, context.VectorZeroUpper96(res)); context.Copy(d, context.VectorZeroUpper96(res));
} }
else /* if (op.Size == 1) */ else /* if (op.Size == 1) */
{ {
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); if (Optimizations.UseFma)
res = context.AddIntrinsic(Intrinsic.X86Subsd, a, res); {
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, a, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
res = context.AddIntrinsic(Intrinsic.X86Subsd, a, res);
}
context.Copy(d, context.VectorZeroUpper64(res)); context.Copy(d, context.VectorZeroUpper64(res));
} }
@@ -1669,25 +1792,39 @@ namespace ARMeilleure.Instructions
Operand n = GetVec(op.Rn); Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm); Operand m = GetVec(op.Rm);
Operand res;
if (op.Size == 0) if (op.Size == 0)
{ {
Operand mask = X86GetScalar(context, -0f); if (Optimizations.UseFma)
{
res = context.AddIntrinsic(Intrinsic.X86Vfnmsub231ss, a, n, m);
}
else
{
Operand mask = X86GetScalar(context, -0f);
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a); res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
res = context.AddIntrinsic(Intrinsic.X86Subss, aNeg, res);
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); }
res = context.AddIntrinsic(Intrinsic.X86Subss, aNeg, res);
context.Copy(d, context.VectorZeroUpper96(res)); context.Copy(d, context.VectorZeroUpper96(res));
} }
else /* if (op.Size == 1) */ else /* if (op.Size == 1) */
{ {
Operand mask = X86GetScalar(context, -0d); if (Optimizations.UseFma)
{
res = context.AddIntrinsic(Intrinsic.X86Vfnmsub231sd, a, n, m);
}
else
{
Operand mask = X86GetScalar(context, -0d);
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a); res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
res = context.AddIntrinsic(Intrinsic.X86Subsd, aNeg, res);
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); }
res = context.AddIntrinsic(Intrinsic.X86Subsd, aNeg, res);
context.Copy(d, context.VectorZeroUpper64(res)); context.Copy(d, context.VectorZeroUpper64(res));
} }
@@ -1716,25 +1853,39 @@ namespace ARMeilleure.Instructions
Operand n = GetVec(op.Rn); Operand n = GetVec(op.Rn);
Operand m = GetVec(op.Rm); Operand m = GetVec(op.Rm);
Operand res;
if (op.Size == 0) if (op.Size == 0)
{ {
Operand mask = X86GetScalar(context, -0f); if (Optimizations.UseFma)
{
res = context.AddIntrinsic(Intrinsic.X86Vfmsub231ss, a, n, m);
}
else
{
Operand mask = X86GetScalar(context, -0f);
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a);
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorps, mask, a); res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
res = context.AddIntrinsic(Intrinsic.X86Addss, aNeg, res);
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); }
res = context.AddIntrinsic(Intrinsic.X86Addss, aNeg, res);
context.Copy(d, context.VectorZeroUpper96(res)); context.Copy(d, context.VectorZeroUpper96(res));
} }
else /* if (op.Size == 1) */ else /* if (op.Size == 1) */
{ {
Operand mask = X86GetScalar(context, -0d); if (Optimizations.UseFma)
{
res = context.AddIntrinsic(Intrinsic.X86Vfmsub231sd, a, n, m);
}
else
{
Operand mask = X86GetScalar(context, -0d);
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a);
Operand aNeg = context.AddIntrinsic(Intrinsic.X86Xorpd, mask, a); res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
res = context.AddIntrinsic(Intrinsic.X86Addsd, aNeg, res);
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); }
res = context.AddIntrinsic(Intrinsic.X86Addsd, aNeg, res);
context.Copy(d, context.VectorZeroUpper64(res)); context.Copy(d, context.VectorZeroUpper64(res));
} }
@@ -1830,13 +1981,22 @@ namespace ARMeilleure.Instructions
int sizeF = op.Size & 1; int sizeF = op.Size & 1;
Operand res;
if (sizeF == 0) if (sizeF == 0)
{ {
Operand mask = X86GetScalar(context, 2f); Operand mask = X86GetScalar(context, 2f);
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); if (Optimizations.UseFma)
{
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, mask, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
res = context.AddIntrinsic(Intrinsic.X86Subss, mask, res);
}
res = context.AddIntrinsic(Intrinsic.X86Subss, mask, res);
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: true, sizeF); res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: true, sizeF);
context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res)); context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
@@ -1845,9 +2005,16 @@ namespace ARMeilleure.Instructions
{ {
Operand mask = X86GetScalar(context, 2d); Operand mask = X86GetScalar(context, 2d);
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); if (Optimizations.UseFma)
{
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, mask, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
res = context.AddIntrinsic(Intrinsic.X86Subsd, mask, res);
}
res = context.AddIntrinsic(Intrinsic.X86Subsd, mask, res);
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: true, sizeF); res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: true, sizeF);
context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res)); context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res));
@@ -1877,14 +2044,23 @@ namespace ARMeilleure.Instructions
int sizeF = op.Size & 1; int sizeF = op.Size & 1;
Operand res;
if (sizeF == 0) if (sizeF == 0)
{ {
Operand mask = X86GetAllElements(context, 2f); Operand mask = X86GetAllElements(context, 2f);
Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m); if (Optimizations.UseFma)
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF); {
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, mask, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
res = context.AddIntrinsic(Intrinsic.X86Subps, mask, res);
}
res = context.AddIntrinsic(Intrinsic.X86Subps, mask, res); res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF);
if (op.RegisterSize == RegisterSize.Simd64) if (op.RegisterSize == RegisterSize.Simd64)
{ {
@@ -1897,10 +2073,17 @@ namespace ARMeilleure.Instructions
{ {
Operand mask = X86GetAllElements(context, 2d); Operand mask = X86GetAllElements(context, 2d);
Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m); if (Optimizations.UseFma)
res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF); {
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, mask, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
res = context.AddIntrinsic(Intrinsic.X86Subpd, mask, res);
}
res = context.AddIntrinsic(Intrinsic.X86Subpd, mask, res); res = EmitSse41RecipStepSelectOpF(context, n, m, res, mask, scalar: false, sizeF);
context.Copy(GetVec(op.Rd), res); context.Copy(GetVec(op.Rd), res);
} }
@@ -2113,20 +2296,32 @@ namespace ARMeilleure.Instructions
public static void Frintx_S(ArmEmitterContext context) public static void Frintx_S(ArmEmitterContext context)
{ {
// TODO Arm64: Fast path. Should we set host FPCR? if (Optimizations.UseAdvSimd)
EmitScalarUnaryOpF(context, (op1) =>
{ {
return EmitRoundByRMode(context, op1); InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FrintxS);
}); }
else
{
EmitScalarUnaryOpF(context, (op1) =>
{
return EmitRoundByRMode(context, op1);
});
}
} }
public static void Frintx_V(ArmEmitterContext context) public static void Frintx_V(ArmEmitterContext context)
{ {
// TODO Arm64: Fast path. Should we set host FPCR? if (Optimizations.UseAdvSimd)
EmitVectorUnaryOpF(context, (op1) =>
{ {
return EmitRoundByRMode(context, op1); InstEmitSimdHelperArm64.EmitVectorUnaryOpF(context, Intrinsic.Arm64FrintxV);
}); }
else
{
EmitVectorUnaryOpF(context, (op1) =>
{
return EmitRoundByRMode(context, op1);
});
}
} }
public static void Frintz_S(ArmEmitterContext context) public static void Frintz_S(ArmEmitterContext context)
@@ -2237,16 +2432,25 @@ namespace ARMeilleure.Instructions
int sizeF = op.Size & 1; int sizeF = op.Size & 1;
Operand res;
if (sizeF == 0) if (sizeF == 0)
{ {
Operand maskHalf = X86GetScalar(context, 0.5f); Operand maskHalf = X86GetScalar(context, 0.5f);
Operand maskThree = X86GetScalar(context, 3f); Operand maskThree = X86GetScalar(context, 3f);
Operand maskOneHalf = X86GetScalar(context, 1.5f); Operand maskOneHalf = X86GetScalar(context, 1.5f);
Operand res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m); if (Optimizations.UseFma)
{
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ss, maskThree, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulss, n, m);
res = context.AddIntrinsic(Intrinsic.X86Subss, maskThree, res);
}
res = context.AddIntrinsic(Intrinsic.X86Subss, maskThree, res); res = context.AddIntrinsic(Intrinsic.X86Mulss, maskHalf, res);
res = context.AddIntrinsic(Intrinsic.X86Mulss, maskHalf, res);
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: true, sizeF); res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: true, sizeF);
context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res)); context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
@@ -2257,10 +2461,17 @@ namespace ARMeilleure.Instructions
Operand maskThree = X86GetScalar(context, 3d); Operand maskThree = X86GetScalar(context, 3d);
Operand maskOneHalf = X86GetScalar(context, 1.5d); Operand maskOneHalf = X86GetScalar(context, 1.5d);
Operand res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m); if (Optimizations.UseFma)
{
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231sd, maskThree, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulsd, n, m);
res = context.AddIntrinsic(Intrinsic.X86Subsd, maskThree, res);
}
res = context.AddIntrinsic(Intrinsic.X86Subsd, maskThree, res); res = context.AddIntrinsic(Intrinsic.X86Mulsd, maskHalf, res);
res = context.AddIntrinsic(Intrinsic.X86Mulsd, maskHalf, res);
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: true, sizeF); res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: true, sizeF);
context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res)); context.Copy(GetVec(op.Rd), context.VectorZeroUpper64(res));
@@ -2290,15 +2501,24 @@ namespace ARMeilleure.Instructions
int sizeF = op.Size & 1; int sizeF = op.Size & 1;
Operand res;
if (sizeF == 0) if (sizeF == 0)
{ {
Operand maskHalf = X86GetAllElements(context, 0.5f); Operand maskHalf = X86GetAllElements(context, 0.5f);
Operand maskThree = X86GetAllElements(context, 3f); Operand maskThree = X86GetAllElements(context, 3f);
Operand maskOneHalf = X86GetAllElements(context, 1.5f); Operand maskOneHalf = X86GetAllElements(context, 1.5f);
Operand res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m); if (Optimizations.UseFma)
{
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231ps, maskThree, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulps, n, m);
res = context.AddIntrinsic(Intrinsic.X86Subps, maskThree, res);
}
res = context.AddIntrinsic(Intrinsic.X86Subps, maskThree, res);
res = context.AddIntrinsic(Intrinsic.X86Mulps, maskHalf, res); res = context.AddIntrinsic(Intrinsic.X86Mulps, maskHalf, res);
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF); res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF);
@@ -2315,9 +2535,16 @@ namespace ARMeilleure.Instructions
Operand maskThree = X86GetAllElements(context, 3d); Operand maskThree = X86GetAllElements(context, 3d);
Operand maskOneHalf = X86GetAllElements(context, 1.5d); Operand maskOneHalf = X86GetAllElements(context, 1.5d);
Operand res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m); if (Optimizations.UseFma)
{
res = context.AddIntrinsic(Intrinsic.X86Vfnmadd231pd, maskThree, n, m);
}
else
{
res = context.AddIntrinsic(Intrinsic.X86Mulpd, n, m);
res = context.AddIntrinsic(Intrinsic.X86Subpd, maskThree, res);
}
res = context.AddIntrinsic(Intrinsic.X86Subpd, maskThree, res);
res = context.AddIntrinsic(Intrinsic.X86Mulpd, maskHalf, res); res = context.AddIntrinsic(Intrinsic.X86Mulpd, maskHalf, res);
res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF); res = EmitSse41RecipStepSelectOpF(context, n, m, res, maskOneHalf, scalar: false, sizeF);
@@ -4728,53 +4955,6 @@ namespace ARMeilleure.Instructions
} }
} }
public static Operand EmitSseOrAvxHandleFzModeOpF(
ArmEmitterContext context,
Func2I emit,
bool scalar,
Operand n = default,
Operand m = default)
{
Operand nCopy = n == default ? context.Copy(GetVec(((OpCodeSimdReg)context.CurrOp).Rn)) : n;
Operand mCopy = m == default ? context.Copy(GetVec(((OpCodeSimdReg)context.CurrOp).Rm)) : m;
EmitSseOrAvxEnterFtzAndDazModesOpF(context, out Operand isTrue);
Operand res = emit(nCopy, mCopy);
EmitSseOrAvxExitFtzAndDazModesOpF(context, isTrue);
if (n != default || m != default)
{
return res;
}
int sizeF = ((IOpCodeSimd)context.CurrOp).Size & 1;
if (sizeF == 0)
{
if (scalar)
{
res = context.VectorZeroUpper96(res);
}
else if (((OpCodeSimdReg)context.CurrOp).RegisterSize == RegisterSize.Simd64)
{
res = context.VectorZeroUpper64(res);
}
}
else /* if (sizeF == 1) */
{
if (scalar)
{
res = context.VectorZeroUpper64(res);
}
}
context.Copy(GetVec(((OpCodeSimdReg)context.CurrOp).Rd), res);
return default;
}
private static Operand EmitSse2VectorMaxMinOpF(ArmEmitterContext context, Operand n, Operand m, bool isMax) private static Operand EmitSse2VectorMaxMinOpF(ArmEmitterContext context, Operand n, Operand m, bool isMax)
{ {
IOpCodeSimd op = (IOpCodeSimd)context.CurrOp; IOpCodeSimd op = (IOpCodeSimd)context.CurrOp;
@@ -4834,10 +5014,7 @@ namespace ARMeilleure.Instructions
Operand res = EmitSse41ProcessNaNsOpF(context, (op1, op2) => Operand res = EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
{ {
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum);
{
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum);
}, scalar: scalar, op1, op2);
}, scalar: scalar, nCopy, mCopy); }, scalar: scalar, nCopy, mCopy);
if (n != default || m != default) if (n != default || m != default)
@@ -4872,10 +5049,7 @@ namespace ARMeilleure.Instructions
Operand res = EmitSse41ProcessNaNsOpF(context, (op1, op2) => Operand res = EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
{ {
return EmitSseOrAvxHandleFzModeOpF(context, (op1, op2) => return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum);
{
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: isMaxNum);
}, scalar: scalar, op1, op2);
}, scalar: scalar, nCopy, mCopy); }, scalar: scalar, nCopy, mCopy);
if (n != default || m != default) if (n != default || m != default)

View File

@@ -356,9 +356,11 @@ namespace ARMeilleure.Instructions
? typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert)) ? typeof(SoftFloat64_16).GetMethod(nameof(SoftFloat64_16.FPConvert))
: typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)); : typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert));
context.ExitArmFpMode();
context.StoreToContext(); context.StoreToContext();
Operand res = context.Call(method, src); Operand res = context.Call(method, src);
context.LoadFromContext(); context.LoadFromContext();
context.EnterArmFpMode();
InsertScalar16(context, op.Vd, op.T, res); InsertScalar16(context, op.Vd, op.T, res);
} }
@@ -372,9 +374,11 @@ namespace ARMeilleure.Instructions
? typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert)) ? typeof(SoftFloat16_64).GetMethod(nameof(SoftFloat16_64.FPConvert))
: typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert)); : typeof(SoftFloat16_32).GetMethod(nameof(SoftFloat16_32.FPConvert));
context.ExitArmFpMode();
context.StoreToContext(); context.StoreToContext();
Operand res = context.Call(method, src); Operand res = context.Call(method, src);
context.LoadFromContext(); context.LoadFromContext();
context.EnterArmFpMode();
InsertScalar(context, op.Vd, res); InsertScalar(context, op.Vd, res);
} }
@@ -542,10 +546,17 @@ namespace ARMeilleure.Instructions
// VRINTX (floating-point). // VRINTX (floating-point).
public static void Vrintx_S(ArmEmitterContext context) public static void Vrintx_S(ArmEmitterContext context)
{ {
EmitScalarUnaryOpF32(context, (op1) => if (Optimizations.UseAdvSimd)
{ {
return EmitRoundByRMode(context, op1); InstEmitSimdHelper32Arm64.EmitScalarUnaryOpF32(context, Intrinsic.Arm64FrintxS);
}); }
else
{
EmitScalarUnaryOpF32(context, (op1) =>
{
return EmitRoundByRMode(context, op1);
});
}
} }
private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed) private static Operand EmitFPConvert(ArmEmitterContext context, Operand value, OperandType type, bool signed)

View File

@@ -1,3 +1,4 @@
using ARMeilleure.CodeGen.X86;
using ARMeilleure.Decoders; using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State; using ARMeilleure.State;
@@ -158,6 +159,75 @@ namespace ARMeilleure.Instructions
}; };
#endregion #endregion
public static void EnterArmFpMode(EmitterContext context, Func<FPState, Operand> getFpFlag)
{
if (Optimizations.UseSse2)
{
Operand mxcsr = context.AddIntrinsicInt(Intrinsic.X86Stmxcsr);
Operand fzTrue = getFpFlag(FPState.FzFlag);
Operand r0True = getFpFlag(FPState.RMode0Flag);
Operand r1True = getFpFlag(FPState.RMode1Flag);
mxcsr = context.BitwiseAnd(mxcsr, Const(~(int)(Mxcsr.Ftz | Mxcsr.Daz | Mxcsr.Rhi | Mxcsr.Rlo)));
mxcsr = context.BitwiseOr(mxcsr, context.ConditionalSelect(fzTrue, Const((int)(Mxcsr.Ftz | Mxcsr.Daz | Mxcsr.Um | Mxcsr.Dm)), Const(0)));
// X86 round modes in order: nearest, negative, positive, zero
// ARM round modes in order: nearest, positive, negative, zero
// Read the bits backwards to correct this.
mxcsr = context.BitwiseOr(mxcsr, context.ConditionalSelect(r0True, Const((int)Mxcsr.Rhi), Const(0)));
mxcsr = context.BitwiseOr(mxcsr, context.ConditionalSelect(r1True, Const((int)Mxcsr.Rlo), Const(0)));
context.AddIntrinsicNoRet(Intrinsic.X86Ldmxcsr, mxcsr);
}
else if (Optimizations.UseAdvSimd)
{
Operand fpcr = context.AddIntrinsicInt(Intrinsic.Arm64MrsFpcr);
Operand fzTrue = getFpFlag(FPState.FzFlag);
Operand r0True = getFpFlag(FPState.RMode0Flag);
Operand r1True = getFpFlag(FPState.RMode1Flag);
fpcr = context.BitwiseAnd(fpcr, Const(~(int)(FPCR.Fz | FPCR.RMode0 | FPCR.RMode1)));
fpcr = context.BitwiseOr(fpcr, context.ConditionalSelect(fzTrue, Const((int)FPCR.Fz), Const(0)));
fpcr = context.BitwiseOr(fpcr, context.ConditionalSelect(r0True, Const((int)FPCR.RMode0), Const(0)));
fpcr = context.BitwiseOr(fpcr, context.ConditionalSelect(r1True, Const((int)FPCR.RMode1), Const(0)));
context.AddIntrinsicNoRet(Intrinsic.Arm64MsrFpcr, fpcr);
// TODO: Restore FPSR
}
}
public static void ExitArmFpMode(EmitterContext context, Action<FPState, Operand> setFpFlag)
{
if (Optimizations.UseSse2)
{
Operand mxcsr = context.AddIntrinsicInt(Intrinsic.X86Stmxcsr);
// Unset round mode (to nearest) and ftz.
mxcsr = context.BitwiseAnd(mxcsr, Const(~(int)(Mxcsr.Ftz | Mxcsr.Daz | Mxcsr.Rhi | Mxcsr.Rlo)));
context.AddIntrinsicNoRet(Intrinsic.X86Ldmxcsr, mxcsr);
// Status flags would be stored here if they were used.
}
else if (Optimizations.UseAdvSimd)
{
Operand fpcr = context.AddIntrinsicInt(Intrinsic.Arm64MrsFpcr);
// Unset round mode (to nearest) and fz.
fpcr = context.BitwiseAnd(fpcr, Const(~(int)(FPCR.Fz | FPCR.RMode0 | FPCR.RMode1)));
context.AddIntrinsicNoRet(Intrinsic.Arm64MsrFpcr, fpcr);
// TODO: Store FPSR
}
}
public static int GetImmShl(OpCodeSimdShImm op) public static int GetImmShl(OpCodeSimdShImm op)
{ {
return op.Imm - (8 << op.Size); return op.Imm - (8 << op.Size);
@@ -465,9 +535,11 @@ namespace ARMeilleure.Instructions
? typeof(SoftFloat32).GetMethod(name) ? typeof(SoftFloat32).GetMethod(name)
: typeof(SoftFloat64).GetMethod(name); : typeof(SoftFloat64).GetMethod(name);
context.ExitArmFpMode();
context.StoreToContext(); context.StoreToContext();
Operand res = context.Call(info, callArgs); Operand res = context.Call(info, callArgs);
context.LoadFromContext(); context.LoadFromContext();
context.EnterArmFpMode();
return res; return res;
} }
@@ -1358,39 +1430,6 @@ namespace ARMeilleure.Instructions
} }
} }
[Flags]
public enum Mxcsr
{
Ftz = 1 << 15, // Flush To Zero.
Um = 1 << 11, // Underflow Mask.
Dm = 1 << 8, // Denormal Mask.
Daz = 1 << 6 // Denormals Are Zero.
}
public static void EmitSseOrAvxEnterFtzAndDazModesOpF(ArmEmitterContext context, out Operand isTrue)
{
isTrue = GetFpFlag(FPState.FzFlag);
Operand lblTrue = Label();
context.BranchIfFalse(lblTrue, isTrue);
context.AddIntrinsicNoRet(Intrinsic.X86Mxcsrmb, Const((int)(Mxcsr.Ftz | Mxcsr.Um | Mxcsr.Dm | Mxcsr.Daz)));
context.MarkLabel(lblTrue);
}
public static void EmitSseOrAvxExitFtzAndDazModesOpF(ArmEmitterContext context, Operand isTrue = default)
{
isTrue = isTrue == default ? GetFpFlag(FPState.FzFlag) : isTrue;
Operand lblTrue = Label();
context.BranchIfFalse(lblTrue, isTrue);
context.AddIntrinsicNoRet(Intrinsic.X86Mxcsrub, Const((int)(Mxcsr.Ftz | Mxcsr.Daz)));
context.MarkLabel(lblTrue);
}
public enum CmpCondition public enum CmpCondition
{ {
// Legacy Sse. // Legacy Sse.

View File

@@ -1197,9 +1197,11 @@ namespace ARMeilleure.Instructions
Array.Resize(ref callArgs, callArgs.Length + 1); Array.Resize(ref callArgs, callArgs.Length + 1);
callArgs[callArgs.Length - 1] = Const(1); callArgs[callArgs.Length - 1] = Const(1);
context.ExitArmFpMode();
context.StoreToContext(); context.StoreToContext();
Operand res = context.Call(info, callArgs); Operand res = context.Call(info, callArgs);
context.LoadFromContext(); context.LoadFromContext();
context.EnterArmFpMode();
return res; return res;
} }

View File

@@ -33,8 +33,8 @@ namespace ARMeilleure.Instructions
case 0b11_011_0100_0010_000: EmitGetNzcv(context); return; case 0b11_011_0100_0010_000: EmitGetNzcv(context); return;
case 0b11_011_0100_0100_000: EmitGetFpcr(context); return; case 0b11_011_0100_0100_000: EmitGetFpcr(context); return;
case 0b11_011_0100_0100_001: EmitGetFpsr(context); return; case 0b11_011_0100_0100_001: EmitGetFpsr(context); return;
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break; case 0b11_011_1101_0000_010: EmitGetTpidrEl0(context); return;
case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)); break; case 0b11_011_1101_0000_011: EmitGetTpidrroEl0(context); return;
case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break; case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break;
case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break; case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break; case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break;
@@ -49,19 +49,15 @@ namespace ARMeilleure.Instructions
{ {
OpCodeSystem op = (OpCodeSystem)context.CurrOp; OpCodeSystem op = (OpCodeSystem)context.CurrOp;
MethodInfo info;
switch (GetPackedId(op)) switch (GetPackedId(op))
{ {
case 0b11_011_0100_0010_000: EmitSetNzcv(context); return; case 0b11_011_0100_0010_000: EmitSetNzcv(context); return;
case 0b11_011_0100_0100_000: EmitSetFpcr(context); return; case 0b11_011_0100_0100_000: EmitSetFpcr(context); return;
case 0b11_011_0100_0100_001: EmitSetFpsr(context); return; case 0b11_011_0100_0100_001: EmitSetFpsr(context); return;
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)); break; case 0b11_011_1101_0000_010: EmitSetTpidrEl0(context); return;
default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
} }
context.Call(info, GetIntOrZR(context, op.Rt));
} }
public static void Nop(ArmEmitterContext context) public static void Nop(ArmEmitterContext context)
@@ -165,6 +161,28 @@ namespace ARMeilleure.Instructions
SetIntOrZR(context, op.Rt, fpsr); SetIntOrZR(context, op.Rt, fpsr);
} }
private static void EmitGetTpidrEl0(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
Operand result = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())));
SetIntOrZR(context, op.Rt, result);
}
private static void EmitGetTpidrroEl0(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
Operand result = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrroEl0Offset())));
SetIntOrZR(context, op.Rt, result);
}
private static void EmitSetNzcv(ArmEmitterContext context) private static void EmitSetNzcv(ArmEmitterContext context)
{ {
OpCodeSystem op = (OpCodeSystem)context.CurrOp; OpCodeSystem op = (OpCodeSystem)context.CurrOp;
@@ -192,6 +210,8 @@ namespace ARMeilleure.Instructions
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpcr, Const(flag)), Const(1))); SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpcr, Const(flag)), Const(1)));
} }
} }
context.UpdateArmFpMode();
} }
private static void EmitSetFpsr(ArmEmitterContext context) private static void EmitSetFpsr(ArmEmitterContext context)
@@ -210,6 +230,19 @@ namespace ARMeilleure.Instructions
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpsr, Const(flag)), Const(1))); SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpsr, Const(flag)), Const(1)));
} }
} }
context.UpdateArmFpMode();
}
private static void EmitSetTpidrEl0(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand value = GetIntOrZR(context, op.Rt);
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), value);
} }
} }
} }

View File

@@ -23,8 +23,6 @@ namespace ARMeilleure.Instructions
return; return;
} }
MethodInfo info;
switch (op.CRn) switch (op.CRn)
{ {
case 13: // Process and Thread Info. case 13: // Process and Thread Info.
@@ -36,14 +34,12 @@ namespace ARMeilleure.Instructions
switch (op.Opc2) switch (op.Opc2)
{ {
case 2: case 2:
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032)); break; EmitSetTpidrEl0(context); return;
default: default:
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X})."); throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
} }
break;
case 7: case 7:
switch (op.CRm) // Cache and Memory barrier. switch (op.CRm) // Cache and Memory barrier.
{ {
@@ -64,8 +60,6 @@ namespace ARMeilleure.Instructions
default: default:
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
} }
context.Call(info, GetIntA32(context, op.Rt));
} }
public static void Mrc(ArmEmitterContext context) public static void Mrc(ArmEmitterContext context)
@@ -79,7 +73,7 @@ namespace ARMeilleure.Instructions
return; return;
} }
MethodInfo info; Operand result;
switch (op.CRn) switch (op.CRn)
{ {
@@ -92,10 +86,10 @@ namespace ARMeilleure.Instructions
switch (op.Opc2) switch (op.Opc2)
{ {
case 2: case 2:
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032)); break; result = EmitGetTpidrEl0(context); break;
case 3: case 3:
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32)); break; result = EmitGetTpidrroEl0(context); break;
default: default:
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X})."); throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
@@ -110,13 +104,13 @@ namespace ARMeilleure.Instructions
if (op.Rt == RegisterAlias.Aarch32Pc) if (op.Rt == RegisterAlias.Aarch32Pc)
{ {
// Special behavior: copy NZCV flags into APSR. // Special behavior: copy NZCV flags into APSR.
EmitSetNzcv(context, context.Call(info)); EmitSetNzcv(context, result);
return; return;
} }
else else
{ {
SetIntA32(context, op.Rt, context.Call(info)); SetIntA32(context, op.Rt, result);
} }
} }
@@ -321,6 +315,37 @@ namespace ARMeilleure.Instructions
SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpscr, Const(flag)), Const(1))); SetFpFlag(context, (FPState)flag, context.BitwiseAnd(context.ShiftRightUI(fpscr, Const(flag)), Const(1)));
} }
} }
context.UpdateArmFpMode();
}
private static Operand EmitGetTpidrEl0(ArmEmitterContext context)
{
OpCode32System op = (OpCode32System)context.CurrOp;
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
return context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())));
}
private static Operand EmitGetTpidrroEl0(ArmEmitterContext context)
{
OpCode32System op = (OpCode32System)context.CurrOp;
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
return context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrroEl0Offset())));
}
private static void EmitSetTpidrEl0(ArmEmitterContext context)
{
OpCode32System op = (OpCode32System)context.CurrOp;
Operand value = GetIntA32(context, op.Rt);
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), context.ZeroExtend32(OperandType.I64, value));
} }
} }
} }

View File

@@ -72,26 +72,6 @@ namespace ARMeilleure.Instructions
return (ulong)GetContext().DczidEl0; return (ulong)GetContext().DczidEl0;
} }
public static ulong GetTpidrEl0()
{
return (ulong)GetContext().TpidrEl0;
}
public static uint GetTpidrEl032()
{
return (uint)GetContext().TpidrEl0;
}
public static ulong GetTpidrroEl0()
{
return (ulong)GetContext().TpidrroEl0;
}
public static uint GetTpidr32()
{
return (uint)GetContext().TpidrroEl0;
}
public static ulong GetCntfrqEl0() public static ulong GetCntfrqEl0()
{ {
return GetContext().CntfrqEl0; return GetContext().CntfrqEl0;
@@ -106,16 +86,6 @@ namespace ARMeilleure.Instructions
{ {
return GetContext().CntvctEl0; return GetContext().CntvctEl0;
} }
public static void SetTpidrEl0(ulong value)
{
GetContext().TpidrEl0 = (long)value;
}
public static void SetTpidrEl032(uint value)
{
GetContext().TpidrEl0 = (long)value;
}
#endregion #endregion
#region "Read" #region "Read"

View File

@@ -53,6 +53,7 @@ namespace ARMeilleure.IntermediateRepresentation
X86Haddpd, X86Haddpd,
X86Haddps, X86Haddps,
X86Insertps, X86Insertps,
X86Ldmxcsr,
X86Maxpd, X86Maxpd,
X86Maxps, X86Maxps,
X86Maxsd, X86Maxsd,
@@ -68,8 +69,6 @@ namespace ARMeilleure.IntermediateRepresentation
X86Mulps, X86Mulps,
X86Mulsd, X86Mulsd,
X86Mulss, X86Mulss,
X86Mxcsrmb,
X86Mxcsrub,
X86Paddb, X86Paddb,
X86Paddd, X86Paddd,
X86Paddq, X86Paddq,
@@ -153,6 +152,7 @@ namespace ARMeilleure.IntermediateRepresentation
X86Sqrtps, X86Sqrtps,
X86Sqrtsd, X86Sqrtsd,
X86Sqrtss, X86Sqrtss,
X86Stmxcsr,
X86Subpd, X86Subpd,
X86Subps, X86Subps,
X86Subsd, X86Subsd,
@@ -163,11 +163,13 @@ namespace ARMeilleure.IntermediateRepresentation
X86Unpcklps, X86Unpcklps,
X86Vcvtph2ps, X86Vcvtph2ps,
X86Vcvtps2ph, X86Vcvtps2ph,
X86Vfmadd231pd,
X86Vfmadd231ps, X86Vfmadd231ps,
X86Vfmadd231sd, X86Vfmadd231sd,
X86Vfmadd231ss, X86Vfmadd231ss,
X86Vfmsub231sd, X86Vfmsub231sd,
X86Vfmsub231ss, X86Vfmsub231ss,
X86Vfnmadd231pd,
X86Vfnmadd231ps, X86Vfnmadd231ps,
X86Vfnmadd231sd, X86Vfnmadd231sd,
X86Vfnmadd231ss, X86Vfnmadd231ss,
@@ -394,6 +396,8 @@ namespace ARMeilleure.IntermediateRepresentation
Arm64MlsVe, Arm64MlsVe,
Arm64MlsV, Arm64MlsV,
Arm64MoviV, Arm64MoviV,
Arm64MrsFpcr,
Arm64MsrFpcr,
Arm64MrsFpsr, Arm64MrsFpsr,
Arm64MsrFpsr, Arm64MsrFpsr,
Arm64MulVe, Arm64MulVe,

View File

@@ -27,8 +27,17 @@ namespace ARMeilleure.State
// Since EL2 isn't implemented, CNTVOFF_EL2 = 0 // Since EL2 isn't implemented, CNTVOFF_EL2 = 0
public ulong CntvctEl0 => CntpctEl0; public ulong CntvctEl0 => CntpctEl0;
public long TpidrEl0 { get; set; } public long TpidrEl0
public long TpidrroEl0 { get; set; } {
get => _nativeContext.GetTpidrEl0();
set => _nativeContext.SetTpidrEl0(value);
}
public long TpidrroEl0
{
get => _nativeContext.GetTpidrroEl0();
set => _nativeContext.SetTpidrroEl0(value);
}
public uint Pstate public uint Pstate
{ {

View File

@@ -13,6 +13,8 @@ namespace ARMeilleure.State
public fixed ulong V[RegisterConsts.VecRegsCount * 2]; public fixed ulong V[RegisterConsts.VecRegsCount * 2];
public fixed uint Flags[RegisterConsts.FlagsCount]; public fixed uint Flags[RegisterConsts.FlagsCount];
public fixed uint FpFlags[RegisterConsts.FpFlagsCount]; public fixed uint FpFlags[RegisterConsts.FpFlagsCount];
public long TpidrEl0;
public long TpidrroEl0;
public int Counter; public int Counter;
public ulong DispatchAddress; public ulong DispatchAddress;
public ulong ExclusiveAddress; public ulong ExclusiveAddress;
@@ -168,6 +170,12 @@ namespace ARMeilleure.State
} }
} }
public long GetTpidrEl0() => GetStorage().TpidrEl0;
public void SetTpidrEl0(long value) => GetStorage().TpidrEl0 = value;
public long GetTpidrroEl0() => GetStorage().TpidrroEl0;
public void SetTpidrroEl0(long value) => GetStorage().TpidrroEl0 = value;
public int GetCounter() => GetStorage().Counter; public int GetCounter() => GetStorage().Counter;
public void SetCounter(int value) => GetStorage().Counter = value; public void SetCounter(int value) => GetStorage().Counter = value;
@@ -214,6 +222,16 @@ namespace ARMeilleure.State
} }
} }
public static int GetTpidrEl0Offset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrEl0);
}
public static int GetTpidrroEl0Offset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrroEl0);
}
public static int GetCounterOffset() public static int GetCounterOffset()
{ {
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter); return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter);

View File

@@ -188,6 +188,21 @@ namespace ARMeilleure.Translation
} }
} }
public void EnterArmFpMode()
{
InstEmitSimdHelper.EnterArmFpMode(this, InstEmitHelper.GetFpFlag);
}
public void UpdateArmFpMode()
{
EnterArmFpMode();
}
public void ExitArmFpMode()
{
InstEmitSimdHelper.ExitArmFpMode(this, (flag, value) => InstEmitHelper.SetFpFlag(this, flag, value));
}
public Operand TryGetComparisonResult(Condition condition) public Operand TryGetComparisonResult(Condition condition)
{ {
if (_optOpLastCompare == null || _optOpLastCompare != _optOpLastFlagSet) if (_optOpLastCompare == null || _optOpLastCompare != _optOpLastFlagSet)

View File

@@ -105,17 +105,11 @@ namespace ARMeilleure.Translation
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)));

View File

@@ -3,4 +3,5 @@
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress); delegate void DispatcherFunction(IntPtr nativeContext, ulong startAddress);
delegate ulong WrapperFunction(IntPtr nativeContext, ulong startAddress);
} }

View File

@@ -30,7 +30,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 4485; //! To be incremented manually for each change to the ARMeilleure project. private const uint InternalVersion = 4661; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0"; private const string ActualDir = "0";
private const string BackupDir = "1"; private const string BackupDir = "1";

View File

@@ -25,5 +25,10 @@ namespace ARMeilleure.Translation
{ {
return _func(context.NativeContextPtr); return _func(context.NativeContextPtr);
} }
public ulong Execute(WrapperFunction dispatcher, State.ExecutionContext context)
{
return dispatcher(context.NativeContextPtr, (ulong)FuncPointer);
}
} }
} }

View File

@@ -183,7 +183,7 @@ namespace ARMeilleure.Translation
Statistics.StartTimer(); Statistics.StartTimer();
ulong nextAddr = func.Execute(context); ulong nextAddr = func.Execute(Stubs.ContextWrapper, context);
Statistics.StopTimer(address); Statistics.StopTimer(address);
@@ -194,7 +194,7 @@ namespace ARMeilleure.Translation
{ {
TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true); TranslatedFunction func = Translate(address, context.ExecutionMode, highCq: false, singleStep: true);
address = func.Execute(context); address = func.Execute(Stubs.ContextWrapper, context);
EnqueueForDeletion(address, func); EnqueueForDeletion(address, func);

View File

@@ -21,6 +21,7 @@ namespace ARMeilleure.Translation
private readonly Translator _translator; private readonly Translator _translator;
private readonly Lazy<IntPtr> _dispatchStub; private readonly Lazy<IntPtr> _dispatchStub;
private readonly Lazy<DispatcherFunction> _dispatchLoop; private readonly Lazy<DispatcherFunction> _dispatchLoop;
private readonly Lazy<WrapperFunction> _contextWrapper;
/// <summary> /// <summary>
/// Gets the dispatch stub. /// Gets the dispatch stub.
@@ -64,6 +65,20 @@ namespace ARMeilleure.Translation
} }
} }
/// <summary>
/// Gets the context wrapper function.
/// </summary>
/// <exception cref="ObjectDisposedException"><see cref="TranslatorStubs"/> instance was disposed</exception>
public WrapperFunction ContextWrapper
{
get
{
ObjectDisposedException.ThrowIf(_disposed, this);
return _contextWrapper.Value;
}
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified /// Initializes a new instance of the <see cref="TranslatorStubs"/> class with the specified
/// <see cref="Translator"/> instance. /// <see cref="Translator"/> instance.
@@ -77,6 +92,7 @@ namespace ARMeilleure.Translation
_translator = translator; _translator = translator;
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true); _dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true); _dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
_contextWrapper = new(GenerateContextWrapper, isThreadSafe: true);
} }
/// <summary> /// <summary>
@@ -202,6 +218,32 @@ namespace ARMeilleure.Translation
return Marshal.GetFunctionPointerForDelegate(func); return Marshal.GetFunctionPointerForDelegate(func);
} }
/// <summary>
/// Emits code that syncs FP state before executing guest code, or returns it to normal.
/// </summary>
/// <param name="context">Emitter context for the method</param>
/// <param name="nativeContext">Pointer to the native context</param>
/// <param name="enter">True if entering guest code, false otherwise</param>
private void EmitSyncFpContext(EmitterContext context, Operand nativeContext, bool enter)
{
if (enter)
{
InstEmitSimdHelper.EnterArmFpMode(context, (flag) =>
{
Operand flagAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRegisterOffset(new Register((int)flag, RegisterType.FpFlag))));
return context.Load(OperandType.I32, flagAddress);
});
}
else
{
InstEmitSimdHelper.ExitArmFpMode(context, (flag, value) =>
{
Operand flagAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRegisterOffset(new Register((int)flag, RegisterType.FpFlag))));
context.Store(flagAddress, value);
});
}
}
/// <summary> /// <summary>
/// Generates a <see cref="DispatchLoop"/> function. /// Generates a <see cref="DispatchLoop"/> function.
/// </summary> /// </summary>
@@ -221,6 +263,8 @@ namespace ARMeilleure.Translation
Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset())); Operand runningAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetRunningOffset()));
Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset())); Operand dispatchAddress = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
EmitSyncFpContext(context, nativeContext, true);
context.MarkLabel(beginLbl); context.MarkLabel(beginLbl);
context.Store(dispatchAddress, guestAddress); context.Store(dispatchAddress, guestAddress);
context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext)); context.Copy(guestAddress, context.Call(Const((ulong)DispatchStub), OperandType.I64, nativeContext));
@@ -229,6 +273,9 @@ namespace ARMeilleure.Translation
context.Branch(beginLbl); context.Branch(beginLbl);
context.MarkLabel(endLbl); context.MarkLabel(endLbl);
EmitSyncFpContext(context, nativeContext, false);
context.Return(); context.Return();
var cfg = context.GetControlFlowGraph(); var cfg = context.GetControlFlowGraph();
@@ -237,5 +284,29 @@ namespace ARMeilleure.Translation
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>(); return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<DispatcherFunction>();
} }
/// <summary>
/// Generates a <see cref="ContextWrapper"/> function.
/// </summary>
/// <returns><see cref="ContextWrapper"/> function</returns>
private WrapperFunction GenerateContextWrapper()
{
var context = new EmitterContext();
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
Operand guestMethod = context.LoadArgument(OperandType.I64, 1);
EmitSyncFpContext(context, nativeContext, true);
Operand returnValue = context.Call(guestMethod, OperandType.I64, nativeContext);
EmitSyncFpContext(context, nativeContext, false);
context.Return(returnValue);
var cfg = context.GetControlFlowGraph();
var retType = OperandType.I64;
var argTypes = new[] { OperandType.I64, OperandType.I64 };
return Compiler.Compile(cfg, argTypes, retType, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<WrapperFunction>();
}
} }
} }

View File

@@ -0,0 +1,148 @@
using ARMeilleure.CodeGen.X86;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State;
using ARMeilleure.Translation;
using System;
using System.Runtime.InteropServices;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Translation
{
public static class TranslatorTestMethods
{
public delegate int FpFlagsPInvokeTest(IntPtr managedMethod);
private static bool SetPlatformFtz(EmitterContext context, bool ftz)
{
if (Optimizations.UseSse2)
{
Operand mxcsr = context.AddIntrinsicInt(Intrinsic.X86Stmxcsr);
if (ftz)
{
mxcsr = context.BitwiseOr(mxcsr, Const((int)(Mxcsr.Ftz | Mxcsr.Um | Mxcsr.Dm)));
}
else
{
mxcsr = context.BitwiseAnd(mxcsr, Const(~(int)Mxcsr.Ftz));
}
context.AddIntrinsicNoRet(Intrinsic.X86Ldmxcsr, mxcsr);
return true;
}
else if (Optimizations.UseAdvSimd)
{
Operand fpcr = context.AddIntrinsicInt(Intrinsic.Arm64MrsFpcr);
if (ftz)
{
fpcr = context.BitwiseOr(fpcr, Const((int)FPCR.Fz));
}
else
{
fpcr = context.BitwiseAnd(fpcr, Const(~(int)FPCR.Fz));
}
context.AddIntrinsicNoRet(Intrinsic.Arm64MsrFpcr, fpcr);
return true;
}
else
{
return false;
}
}
private static Operand FpBitsToInt(EmitterContext context, Operand fp)
{
Operand vec = context.VectorInsert(context.VectorZero(), fp, 0);
return context.VectorExtract(OperandType.I32, vec, 0);
}
public static FpFlagsPInvokeTest GenerateFpFlagsPInvokeTest()
{
EmitterContext context = new EmitterContext();
Operand methodAddress = context.Copy(context.LoadArgument(OperandType.I64, 0));
// Verify that default dotnet fp state does not flush to zero.
// This is required for SoftFloat to function.
// Denormal + zero != 0
Operand denormal = ConstF(BitConverter.Int32BitsToSingle(1)); // 1.40129846432e-45
Operand zeroF = ConstF(0f);
Operand zero = Const(0);
Operand result = context.Add(zeroF, denormal);
// Must not be zero.
Operand correct1Label = Label();
context.BranchIfFalse(correct1Label, context.ICompareEqual(FpBitsToInt(context, result), zero));
context.Return(Const(1));
context.MarkLabel(correct1Label);
// Set flush to zero flag. If unsupported by the backend, just return true.
if (!SetPlatformFtz(context, true))
{
context.Return(Const(0));
}
// Denormal + zero == 0
Operand resultFz = context.Add(zeroF, denormal);
// Must equal zero.
Operand correct2Label = Label();
context.BranchIfTrue(correct2Label, context.ICompareEqual(FpBitsToInt(context, resultFz), zero));
SetPlatformFtz(context, false);
context.Return(Const(2));
context.MarkLabel(correct2Label);
// Call a managed method. This method should not change Fz state.
context.Call(methodAddress, OperandType.None);
// Denormal + zero == 0
Operand resultFz2 = context.Add(zeroF, denormal);
// Must equal zero.
Operand correct3Label = Label();
context.BranchIfTrue(correct3Label, context.ICompareEqual(FpBitsToInt(context, resultFz2), zero));
SetPlatformFtz(context, false);
context.Return(Const(3));
context.MarkLabel(correct3Label);
// Success.
SetPlatformFtz(context, false);
context.Return(Const(0));
// Compile and return the function.
ControlFlowGraph cfg = context.GetControlFlowGraph();
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<FpFlagsPInvokeTest>();
}
}
}

View File

@@ -3,17 +3,17 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Avalonia" Version="0.10.18" /> <PackageVersion Include="Avalonia" Version="0.10.19" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.18" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.19" />
<PackageVersion Include="Avalonia.Desktop" Version="0.10.18" /> <PackageVersion Include="Avalonia.Desktop" Version="0.10.19" />
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.18" /> <PackageVersion Include="Avalonia.Diagnostics" Version="0.10.19" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.18" /> <PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.19" />
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" /> <PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" /> <PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Concentus" Version="1.1.7" /> <PackageVersion Include="Concentus" Version="1.1.7" />
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" /> <PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
<PackageVersion Include="DynamicData" Version="7.12.11" /> <PackageVersion Include="DynamicData" Version="7.13.1" />
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" /> <PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" /> <PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" /> <PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
@@ -44,10 +44,10 @@
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" /> <PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
<PackageVersion Include="SPB" Version="0.0.4-build28" /> <PackageVersion Include="SPB" Version="0.0.4-build28" />
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" /> <PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" /> <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.28.1" />
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" /> <PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
<PackageVersion Include="System.Management" Version="7.0.0" /> <PackageVersion Include="System.Management" Version="7.0.0" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" /> <PackageVersion Include="XamlNameReferenceGenerator" Version="1.6.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -130,7 +130,7 @@ namespace Ryujinx.Ava.Common.Locale
{ {
var localeStrings = new Dictionary<LocaleKeys, string>(); var localeStrings = new Dictionary<LocaleKeys, string>();
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json"); string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");
var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson); var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
foreach (var item in strings) foreach (var item in strings)
{ {

View File

@@ -4,13 +4,14 @@ using FluentAvalonia.UI.Controls;
using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Tar; using ICSharpCode.SharpZipLib.Tar;
using ICSharpCode.SharpZipLib.Zip; using ICSharpCode.SharpZipLib.Zip;
using Newtonsoft.Json.Linq;
using Ryujinx.Ava; using Ryujinx.Ava;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
using Ryujinx.Ui.Common.Models.Github;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@@ -31,6 +32,7 @@ namespace Ryujinx.Modules
internal static class Updater internal static class Updater
{ {
private const string GitHubApiURL = "https://api.github.com"; private const string GitHubApiURL = "https://api.github.com";
private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory; private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory;
private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
@@ -99,22 +101,16 @@ namespace Ryujinx.Modules
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
JObject jsonRoot = JObject.Parse(fetchedJson); var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse);
JToken assets = jsonRoot["assets"]; _buildVer = fetched.Name;
_buildVer = (string)jsonRoot["name"]; foreach (var asset in fetched.Assets)
foreach (JToken asset in assets)
{ {
string assetName = (string)asset["name"]; if (asset.Name.StartsWith("test-ava-ryujinx") && asset.Name.EndsWith(_platformExt))
string assetState = (string)asset["state"];
string downloadURL = (string)asset["browser_download_url"];
if (assetName.StartsWith("test-ava-ryujinx") && assetName.EndsWith(_platformExt))
{ {
_buildUrl = downloadURL; _buildUrl = asset.BrowserDownloadUrl;
if (assetState != "uploaded") if (asset.State != "uploaded")
{ {
if (showVersionUpToDate) if (showVersionUpToDate)
{ {

View File

@@ -53,6 +53,8 @@ namespace Ryujinx.Ava.UI.Applet
bool opened = false; bool opened = false;
_parent.Activate();
UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent, UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent,
title, title,
message, message,

View File

@@ -1,72 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Ryujinx.Ava.UI.Models
{
public class Amiibo
{
public struct AmiiboJson
{
[JsonPropertyName("amiibo")] public List<AmiiboApi> Amiibo { get; set; }
[JsonPropertyName("lastUpdated")] public DateTime LastUpdated { get; set; }
}
public struct AmiiboApi
{
[JsonPropertyName("name")] public string Name { get; set; }
[JsonPropertyName("head")] public string Head { get; set; }
[JsonPropertyName("tail")] public string Tail { get; set; }
[JsonPropertyName("image")] public string Image { get; set; }
[JsonPropertyName("amiiboSeries")] public string AmiiboSeries { get; set; }
[JsonPropertyName("character")] public string Character { get; set; }
[JsonPropertyName("gameSeries")] public string GameSeries { get; set; }
[JsonPropertyName("type")] public string Type { get; set; }
[JsonPropertyName("release")] public Dictionary<string, string> Release { get; set; }
[JsonPropertyName("gamesSwitch")] public List<AmiiboApiGamesSwitch> GamesSwitch { get; set; }
public override string ToString()
{
return Name;
}
public string GetId()
{
return Head + Tail;
}
public override bool Equals(object obj)
{
if (obj is AmiiboApi amiibo)
{
return amiibo.Head + amiibo.Tail == Head + Tail;
}
return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
public class AmiiboApiGamesSwitch
{
[JsonPropertyName("amiiboUsage")] public List<AmiiboApiUsage> AmiiboUsage { get; set; }
[JsonPropertyName("gameID")] public List<string> GameId { get; set; }
[JsonPropertyName("gameName")] public string GameName { get; set; }
}
public class AmiiboApiUsage
{
[JsonPropertyName("Usage")] public string Usage { get; set; }
[JsonPropertyName("write")] public bool Write { get; set; }
}
}
}

View File

@@ -122,7 +122,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/"); string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
Supporters = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString)) + "\n\n"; Supporters = string.Join(", ", JsonHelper.Deserialize(patreonJsonString, CommonJsonContext.Default.StringArray)) + "\n\n";
} }
catch catch
{ {

View File

@@ -4,11 +4,11 @@ using Avalonia.Media.Imaging;
using Avalonia.Threading; using Avalonia.Threading;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.Ui.Common.Models.Amiibo;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@@ -17,6 +17,7 @@ using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
@@ -31,8 +32,8 @@ namespace Ryujinx.Ava.UI.ViewModels
private readonly StyleableWindow _owner; private readonly StyleableWindow _owner;
private Bitmap _amiiboImage; private Bitmap _amiiboImage;
private List<Amiibo.AmiiboApi> _amiiboList; private List<AmiiboApi> _amiiboList;
private AvaloniaList<Amiibo.AmiiboApi> _amiibos; private AvaloniaList<AmiiboApi> _amiibos;
private ObservableCollection<string> _amiiboSeries; private ObservableCollection<string> _amiiboSeries;
private int _amiiboSelectedIndex; private int _amiiboSelectedIndex;
@@ -41,6 +42,8 @@ namespace Ryujinx.Ava.UI.ViewModels
private bool _showAllAmiibo; private bool _showAllAmiibo;
private bool _useRandomUuid; private bool _useRandomUuid;
private string _usage; private string _usage;
private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId) public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId)
{ {
@@ -52,9 +55,9 @@ namespace Ryujinx.Ava.UI.ViewModels
Directory.CreateDirectory(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo")); Directory.CreateDirectory(Path.Join(AppDataManager.BaseDirPath, "system", "amiibo"));
_amiiboJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json"); _amiiboJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json");
_amiiboList = new List<Amiibo.AmiiboApi>(); _amiiboList = new List<AmiiboApi>();
_amiiboSeries = new ObservableCollection<string>(); _amiiboSeries = new ObservableCollection<string>();
_amiibos = new AvaloniaList<Amiibo.AmiiboApi>(); _amiibos = new AvaloniaList<AmiiboApi>();
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png"); _amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png");
@@ -94,7 +97,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public AvaloniaList<Amiibo.AmiiboApi> AmiiboList public AvaloniaList<AmiiboApi> AmiiboList
{ {
get => _amiibos; get => _amiibos;
set set
@@ -187,9 +190,9 @@ namespace Ryujinx.Ava.UI.ViewModels
if (File.Exists(_amiiboJsonPath)) if (File.Exists(_amiiboJsonPath))
{ {
amiiboJsonString = File.ReadAllText(_amiiboJsonPath); amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath);
if (await NeedsUpdate(JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).LastUpdated)) if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated))
{ {
amiiboJsonString = await DownloadAmiiboJson(); amiiboJsonString = await DownloadAmiiboJson();
} }
@@ -206,7 +209,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
_amiiboList = JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).Amiibo; _amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo;
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList(); _amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
ParseAmiiboData(); ParseAmiiboData();
@@ -223,7 +226,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
if (!ShowAllAmiibo) if (!ShowAllAmiibo)
{ {
foreach (Amiibo.AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch) foreach (AmiiboApiGamesSwitch game in _amiiboList[i].GamesSwitch)
{ {
if (game != null) if (game != null)
{ {
@@ -255,7 +258,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private void SelectLastScannedAmiibo() private void SelectLastScannedAmiibo()
{ {
Amiibo.AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId); AmiiboApi scanned = _amiiboList.FirstOrDefault(amiibo => amiibo.GetId() == LastScannedAmiiboId);
SeriesSelectedIndex = AmiiboSeries.IndexOf(scanned.AmiiboSeries); SeriesSelectedIndex = AmiiboSeries.IndexOf(scanned.AmiiboSeries);
AmiiboSelectedIndex = AmiiboList.IndexOf(scanned); AmiiboSelectedIndex = AmiiboList.IndexOf(scanned);
@@ -270,7 +273,7 @@ namespace Ryujinx.Ava.UI.ViewModels
return; return;
} }
List<Amiibo.AmiiboApi> amiiboSortedList = _amiiboList List<AmiiboApi> amiiboSortedList = _amiiboList
.Where(amiibo => amiibo.AmiiboSeries == _amiiboSeries[SeriesSelectedIndex]) .Where(amiibo => amiibo.AmiiboSeries == _amiiboSeries[SeriesSelectedIndex])
.OrderBy(amiibo => amiibo.Name).ToList(); .OrderBy(amiibo => amiibo.Name).ToList();
@@ -280,7 +283,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
if (!_showAllAmiibo) if (!_showAllAmiibo)
{ {
foreach (Amiibo.AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch) foreach (AmiiboApiGamesSwitch game in amiiboSortedList[i].GamesSwitch)
{ {
if (game != null) if (game != null)
{ {
@@ -314,7 +317,7 @@ namespace Ryujinx.Ava.UI.ViewModels
return; return;
} }
Amiibo.AmiiboApi selected = _amiibos[_amiiboSelectedIndex]; AmiiboApi selected = _amiibos[_amiiboSelectedIndex];
string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image; string imageUrl = _amiiboList.FirstOrDefault(amiibo => amiibo.Equals(selected)).Image;
@@ -326,11 +329,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
bool writable = false; bool writable = false;
foreach (Amiibo.AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch) foreach (AmiiboApiGamesSwitch item in _amiiboList[i].GamesSwitch)
{ {
if (item.GameId.Contains(TitleId)) if (item.GameId.Contains(TitleId))
{ {
foreach (Amiibo.AmiiboApiUsage usageItem in item.AmiiboUsage) foreach (AmiiboApiUsage usageItem in item.AmiiboUsage)
{ {
usageString += Environment.NewLine + usageString += Environment.NewLine +
$"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}"; $"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}";

View File

@@ -51,6 +51,8 @@ namespace Ryujinx.Ava.UI.ViewModels
private bool _isLoaded; private bool _isLoaded;
private readonly UserControl _owner; private readonly UserControl _owner;
private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public IGamepadDriver AvaloniaKeyboardDriver { get; } public IGamepadDriver AvaloniaKeyboardDriver { get; }
public IGamepad SelectedGamepad { get; private set; } public IGamepad SelectedGamepad { get; private set; }
@@ -706,10 +708,7 @@ namespace Ryujinx.Ava.UI.ViewModels
try try
{ {
using (Stream stream = File.OpenRead(path)) config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig);
{
config = JsonHelper.Deserialize<InputConfig>(stream);
}
} }
catch (JsonException) { } catch (JsonException) { }
catch (InvalidOperationException) catch (InvalidOperationException)
@@ -775,7 +774,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.ControllerType = Controllers[_controller].Type; config.ControllerType = Controllers[_controller].Type;
string jsonString = JsonHelper.Serialize(config, true); string jsonString = JsonHelper.Serialize(config, SerializerContext.InputConfig);
await File.WriteAllTextAsync(path, jsonString); await File.WriteAllTextAsync(path, jsonString);

View File

@@ -21,7 +21,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Path = System.IO.Path; using Path = System.IO.Path;
@@ -41,6 +40,8 @@ namespace Ryujinx.Ava.UI.ViewModels
private ulong _titleId; private ulong _titleId;
private string _titleName; private string _titleName;
private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public AvaloniaList<DownloadableContentModel> DownloadableContents public AvaloniaList<DownloadableContentModel> DownloadableContents
{ {
get => _downloadableContents; get => _downloadableContents;
@@ -100,7 +101,7 @@ namespace Ryujinx.Ava.UI.ViewModels
try try
{ {
_downloadableContentContainerList = JsonHelper.DeserializeFromFile<List<DownloadableContentContainer>>(_downloadableContentJsonPath); _downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, SerializerContext.ListDownloadableContentContainer);
} }
catch catch
{ {
@@ -330,10 +331,7 @@ namespace Ryujinx.Ava.UI.ViewModels
_downloadableContentContainerList.Add(container); _downloadableContentContainerList.Add(container);
} }
using (FileStream downloadableContentJsonStream = File.Create(_downloadableContentJsonPath, 4096, FileOptions.WriteThrough)) JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, SerializerContext.ListDownloadableContentContainer);
{
downloadableContentJsonStream.Write(Encoding.UTF8.GetBytes(JsonHelper.Serialize(_downloadableContentContainerList, true)));
}
} }
} }

View File

@@ -22,230 +22,231 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using Path = System.IO.Path; using Path = System.IO.Path;
using SpanHelpers = LibHac.Common.SpanHelpers; using SpanHelpers = LibHac.Common.SpanHelpers;
namespace Ryujinx.Ava.UI.ViewModels; namespace Ryujinx.Ava.UI.ViewModels
public class TitleUpdateViewModel : BaseModel
{ {
public TitleUpdateMetadata _titleUpdateWindowData; public class TitleUpdateViewModel : BaseModel
public readonly string _titleUpdateJsonPath;
private VirtualFileSystem _virtualFileSystem { get; }
private ulong _titleId { get; }
private string _titleName { get; }
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
private AvaloniaList<object> _views = new();
private object _selectedUpdate;
public AvaloniaList<TitleUpdateModel> TitleUpdates
{ {
get => _titleUpdates; public TitleUpdateMetadata _titleUpdateWindowData;
set public readonly string _titleUpdateJsonPath;
private VirtualFileSystem _virtualFileSystem { get; }
private ulong _titleId { get; }
private string _titleName { get; }
private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
private AvaloniaList<object> _views = new();
private object _selectedUpdate;
private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public AvaloniaList<TitleUpdateModel> TitleUpdates
{ {
_titleUpdates = value; get => _titleUpdates;
OnPropertyChanged(); set
}
}
public AvaloniaList<object> Views
{
get => _views;
set
{
_views = value;
OnPropertyChanged();
}
}
public object SelectedUpdate
{
get => _selectedUpdate;
set
{
_selectedUpdate = value;
OnPropertyChanged();
}
}
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
{
_virtualFileSystem = virtualFileSystem;
_titleId = titleId;
_titleName = titleName;
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
try
{
_titleUpdateWindowData = JsonHelper.DeserializeFromFile<TitleUpdateMetadata>(_titleUpdateJsonPath);
}
catch
{
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}");
_titleUpdateWindowData = new TitleUpdateMetadata
{ {
Selected = "", _titleUpdates = value;
Paths = new List<string>() OnPropertyChanged();
};
Save();
}
LoadUpdates();
}
private void LoadUpdates()
{
foreach (string path in _titleUpdateWindowData.Paths)
{
AddUpdate(path);
}
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null);
SelectedUpdate = selected;
// NOTE: Save the list again to remove leftovers.
Save();
SortUpdates();
}
public void SortUpdates()
{
var list = TitleUpdates.ToList();
list.Sort((first, second) =>
{
if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
{
return -1;
}
else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
{
return 1;
}
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
});
Views.Clear();
Views.Add(new BaseModel());
Views.AddRange(list);
if (SelectedUpdate == null)
{
SelectedUpdate = Views[0];
}
else if (!TitleUpdates.Contains(SelectedUpdate))
{
if (Views.Count > 1)
{
SelectedUpdate = Views[1];
}
else
{
SelectedUpdate = Views[0];
} }
} }
}
private void AddUpdate(string path) public AvaloniaList<object> Views
{
if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
{ {
using FileStream file = new(path, FileMode.Open, FileAccess.Read); get => _views;
set
{
_views = value;
OnPropertyChanged();
}
}
public object SelectedUpdate
{
get => _selectedUpdate;
set
{
_selectedUpdate = value;
OnPropertyChanged();
}
}
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
{
_virtualFileSystem = virtualFileSystem;
_titleId = titleId;
_titleName = titleName;
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
try try
{ {
(Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); _titleUpdateWindowData = JsonHelper.DeserializeFromFile(_titleUpdateJsonPath, SerializerContext.TitleUpdateMetadata);
}
catch
{
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}");
if (controlNca != null && patchNca != null) _titleUpdateWindowData = new TitleUpdateMetadata
{ {
ApplicationControlProperty controlData = new(); Selected = "",
Paths = new List<string>()
};
using UniqueRef<IFile> nacpFile = new(); Save();
}
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); LoadUpdates();
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); }
TitleUpdates.Add(new TitleUpdateModel(controlData, path)); private void LoadUpdates()
{
foreach (string path in _titleUpdateWindowData.Paths)
{
AddUpdate(path);
}
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null);
SelectedUpdate = selected;
// NOTE: Save the list again to remove leftovers.
Save();
SortUpdates();
}
public void SortUpdates()
{
var list = TitleUpdates.ToList();
list.Sort((first, second) =>
{
if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
{
return -1;
}
else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
{
return 1;
}
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
});
Views.Clear();
Views.Add(new BaseModel());
Views.AddRange(list);
if (SelectedUpdate == null)
{
SelectedUpdate = Views[0];
}
else if (!TitleUpdates.Contains(SelectedUpdate))
{
if (Views.Count > 1)
{
SelectedUpdate = Views[1];
} }
else else
{
SelectedUpdate = Views[0];
}
}
}
private void AddUpdate(string path)
{
if (File.Exists(path) && TitleUpdates.All(x => x.Path != path))
{
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
try
{
(Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0);
if (controlNca != null && patchNca != null)
{
ApplicationControlProperty controlData = new();
using UniqueRef<IFile> nacpFile = new();
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
}
else
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]);
});
}
}
catch (Exception ex)
{ {
Dispatcher.UIThread.Post(async () => Dispatcher.UIThread.Post(async () =>
{ {
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUpdateAddUpdateErrorMessage]); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
}); });
} }
} }
catch (Exception ex)
{
Dispatcher.UIThread.Post(async () =>
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogLoadNcaErrorMessage, ex.Message, path));
});
}
} }
}
public void RemoveUpdate(TitleUpdateModel update) public void RemoveUpdate(TitleUpdateModel update)
{
TitleUpdates.Remove(update);
SortUpdates();
}
public async void Add()
{
OpenFileDialog dialog = new()
{ {
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle], TitleUpdates.Remove(update);
AllowMultiple = true
};
dialog.Filters.Add(new FileDialogFilter SortUpdates();
}
public async void Add()
{ {
Name = "NSP", OpenFileDialog dialog = new()
Extensions = { "nsp" }
});
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
string[] files = await dialog.ShowAsync(desktop.MainWindow);
if (files != null)
{ {
foreach (string file in files) Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
AllowMultiple = true
};
dialog.Filters.Add(new FileDialogFilter
{
Name = "NSP",
Extensions = { "nsp" }
});
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
string[] files = await dialog.ShowAsync(desktop.MainWindow);
if (files != null)
{ {
AddUpdate(file); foreach (string file in files)
{
AddUpdate(file);
}
} }
} }
SortUpdates();
} }
SortUpdates(); public void Save()
}
public void Save()
{
_titleUpdateWindowData.Paths.Clear();
_titleUpdateWindowData.Selected = "";
foreach (TitleUpdateModel update in TitleUpdates)
{ {
_titleUpdateWindowData.Paths.Add(update.Path); _titleUpdateWindowData.Paths.Clear();
_titleUpdateWindowData.Selected = "";
if (update == SelectedUpdate) foreach (TitleUpdateModel update in TitleUpdates)
{ {
_titleUpdateWindowData.Selected = update.Path; _titleUpdateWindowData.Paths.Add(update.Path);
}
}
File.WriteAllBytes(_titleUpdateJsonPath, Encoding.UTF8.GetBytes(JsonHelper.Serialize(_titleUpdateWindowData, true))); if (update == SelectedUpdate)
{
_titleUpdateWindowData.Selected = update.Path;
}
}
JsonHelper.SerializeToFile(_titleUpdateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata);
}
} }
} }

View File

@@ -42,7 +42,7 @@ namespace Ryujinx.Ava.UI.Views.Main
{ {
string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last(); string languageCode = Path.GetFileNameWithoutExtension(locale).Split('.').Last();
string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}"); string languageJson = EmbeddedResources.ReadAllText($"{localePath}/{languageCode}{localeExt}");
var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson); var strings = JsonHelper.Deserialize(languageJson, CommonJsonContext.Default.StringDictionary);
if (!strings.TryGetValue("Language", out string languageName)) if (!strings.TryGetValue("Language", out string languageName))
{ {

View File

@@ -1,7 +1,7 @@
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ui.Common.Models.Amiibo;
namespace Ryujinx.Ava.UI.Windows namespace Ryujinx.Ava.UI.Windows
{ {
@@ -35,14 +35,14 @@ namespace Ryujinx.Ava.UI.Windows
} }
public bool IsScanned { get; set; } public bool IsScanned { get; set; }
public Amiibo.AmiiboApi ScannedAmiibo { get; set; } public AmiiboApi ScannedAmiibo { get; set; }
public AmiiboWindowViewModel ViewModel { get; set; } public AmiiboWindowViewModel ViewModel { get; set; }
private void ScanButton_Click(object sender, RoutedEventArgs e) private void ScanButton_Click(object sender, RoutedEventArgs e)
{ {
if (ViewModel.AmiiboSelectedIndex > -1) if (ViewModel.AmiiboSelectedIndex > -1)
{ {
Amiibo.AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex]; AmiiboApi amiibo = ViewModel.AmiiboList[ViewModel.AmiiboSelectedIndex];
ScannedAmiibo = amiibo; ScannedAmiibo = amiibo;
IsScanned = true; IsScanned = true;
Close(); Close();

View File

@@ -6,11 +6,8 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
using System.IO;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Button = Avalonia.Controls.Button; using Button = Avalonia.Controls.Button;

View File

@@ -1,5 +1,9 @@
namespace Ryujinx.Common.Configuration using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration
{ {
[JsonConverter(typeof(TypedStringEnumConverter<AntiAliasing>))]
public enum AntiAliasing public enum AntiAliasing
{ {
None, None,
@@ -9,4 +13,4 @@
SmaaHigh, SmaaHigh,
SmaaUltra SmaaUltra
} }
} }

View File

@@ -1,5 +1,9 @@
namespace Ryujinx.Common.Configuration using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration
{ {
[JsonConverter(typeof(TypedStringEnumConverter<AspectRatio>))]
public enum AspectRatio public enum AspectRatio
{ {
Fixed4x3, Fixed4x3,

View File

@@ -1,5 +1,9 @@
namespace Ryujinx.Common.Configuration using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration
{ {
[JsonConverter(typeof(TypedStringEnumConverter<BackendThreading>))]
public enum BackendThreading public enum BackendThreading
{ {
Auto, Auto,

View File

@@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration
{
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(List<DownloadableContentContainer>))]
public partial class DownloadableContentJsonSerializerContext : JsonSerializerContext
{
}
}

View File

@@ -1,5 +1,9 @@
namespace Ryujinx.Common.Configuration using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration
{ {
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsBackend>))]
public enum GraphicsBackend public enum GraphicsBackend
{ {
Vulkan, Vulkan,

View File

@@ -1,5 +1,9 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration namespace Ryujinx.Common.Configuration
{ {
[JsonConverter(typeof(TypedStringEnumConverter<GraphicsDebugLevel>))]
public enum GraphicsDebugLevel public enum GraphicsDebugLevel
{ {
None, None,

View File

@@ -1,5 +1,9 @@
namespace Ryujinx.Common.Configuration.Hid.Controller using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration.Hid.Controller
{ {
[JsonConverter(typeof(TypedStringEnumConverter<GamepadInputId>))]
public enum GamepadInputId : byte public enum GamepadInputId : byte
{ {
Unbound, Unbound,
@@ -51,4 +55,4 @@
Count Count
} }
} }

View File

@@ -1,4 +1,5 @@
using System; using Ryujinx.Common.Utilities;
using System;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
@@ -6,6 +7,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
{ {
class JsonMotionConfigControllerConverter : JsonConverter<MotionConfigController> class JsonMotionConfigControllerConverter : JsonConverter<MotionConfigController>
{ {
private static readonly MotionConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
private static MotionInputBackendType GetMotionInputBackendType(ref Utf8JsonReader reader) private static MotionInputBackendType GetMotionInputBackendType(ref Utf8JsonReader reader)
{ {
// Temporary reader to get the backend type // Temporary reader to get the backend type
@@ -52,8 +55,8 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
return motionBackendType switch return motionBackendType switch
{ {
MotionInputBackendType.GamepadDriver => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(StandardMotionConfigController), options), MotionInputBackendType.GamepadDriver => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardMotionConfigController),
MotionInputBackendType.CemuHook => (MotionConfigController)JsonSerializer.Deserialize(ref reader, typeof(CemuHookMotionConfigController), options), MotionInputBackendType.CemuHook => JsonSerializer.Deserialize(ref reader, SerializerContext.CemuHookMotionConfigController),
_ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"), _ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"),
}; };
} }
@@ -63,10 +66,10 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
switch (value.MotionBackend) switch (value.MotionBackend)
{ {
case MotionInputBackendType.GamepadDriver: case MotionInputBackendType.GamepadDriver:
JsonSerializer.Serialize(writer, value as StandardMotionConfigController, options); JsonSerializer.Serialize(writer, value as StandardMotionConfigController, SerializerContext.StandardMotionConfigController);
break; break;
case MotionInputBackendType.CemuHook: case MotionInputBackendType.CemuHook:
JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, options); JsonSerializer.Serialize(writer, value as CemuHookMotionConfigController, SerializerContext.CemuHookMotionConfigController);
break; break;
default: default:
throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}"); throw new ArgumentException($"Unknown motion backend type {value.MotionBackend}");

View File

@@ -1,5 +1,8 @@
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
{ {
[JsonConverter(typeof(JsonMotionConfigControllerConverter))]
public class MotionConfigController public class MotionConfigController
{ {
public MotionInputBackendType MotionBackend { get; set; } public MotionInputBackendType MotionBackend { get; set; }

View File

@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
{
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(MotionConfigController))]
[JsonSerializable(typeof(CemuHookMotionConfigController))]
[JsonSerializable(typeof(StandardMotionConfigController))]
public partial class MotionConfigJsonSerializerContext : JsonSerializerContext
{
}
}

View File

@@ -1,5 +1,9 @@
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
{ {
[JsonConverter(typeof(TypedStringEnumConverter<MotionInputBackendType>))]
public enum MotionInputBackendType : byte public enum MotionInputBackendType : byte
{ {
Invalid, Invalid,

View File

@@ -1,5 +1,9 @@
namespace Ryujinx.Common.Configuration.Hid.Controller using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration.Hid.Controller
{ {
[JsonConverter(typeof(TypedStringEnumConverter<StickInputId>))]
public enum StickInputId : byte public enum StickInputId : byte
{ {
Unbound, Unbound,
@@ -8,4 +12,4 @@
Count Count
} }
} }

View File

@@ -1,9 +1,12 @@
using Ryujinx.Common.Utilities;
using System; using System;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration.Hid namespace Ryujinx.Common.Configuration.Hid
{ {
[Flags]
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical // This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
[Flags]
[JsonConverter(typeof(TypedStringEnumConverter<ControllerType>))]
public enum ControllerType : int public enum ControllerType : int
{ {
None, None,

View File

@@ -1,5 +1,9 @@
namespace Ryujinx.Common.Configuration.Hid using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration.Hid
{ {
[JsonConverter(typeof(TypedStringEnumConverter<InputBackendType>))]
public enum InputBackendType public enum InputBackendType
{ {
Invalid, Invalid,

View File

@@ -1,8 +1,10 @@
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration.Hid namespace Ryujinx.Common.Configuration.Hid
{ {
[JsonConverter(typeof(JsonInputConfigConverter))]
public class InputConfig : INotifyPropertyChanged public class InputConfig : INotifyPropertyChanged
{ {
/// <summary> /// <summary>

View File

@@ -0,0 +1,14 @@
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Configuration.Hid.Keyboard;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration.Hid
{
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(InputConfig))]
[JsonSerializable(typeof(StandardKeyboardInputConfig))]
[JsonSerializable(typeof(StandardControllerInputConfig))]
public partial class InputConfigJsonSerializerContext : JsonSerializerContext
{
}
}

View File

@@ -1,13 +1,16 @@
using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Configuration.Hid.Keyboard;
using Ryujinx.Common.Utilities;
using System; using System;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration.Hid namespace Ryujinx.Common.Configuration.Hid
{ {
class JsonInputConfigConverter : JsonConverter<InputConfig> public class JsonInputConfigConverter : JsonConverter<InputConfig>
{ {
private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
private static InputBackendType GetInputBackendType(ref Utf8JsonReader reader) private static InputBackendType GetInputBackendType(ref Utf8JsonReader reader)
{ {
// Temporary reader to get the backend type // Temporary reader to get the backend type
@@ -54,8 +57,8 @@ namespace Ryujinx.Common.Configuration.Hid
return backendType switch return backendType switch
{ {
InputBackendType.WindowKeyboard => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardKeyboardInputConfig), options), InputBackendType.WindowKeyboard => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardKeyboardInputConfig),
InputBackendType.GamepadSDL2 => (InputConfig)JsonSerializer.Deserialize(ref reader, typeof(StandardControllerInputConfig), options), InputBackendType.GamepadSDL2 => JsonSerializer.Deserialize(ref reader, SerializerContext.StandardControllerInputConfig),
_ => throw new InvalidOperationException($"Unknown backend type {backendType}"), _ => throw new InvalidOperationException($"Unknown backend type {backendType}"),
}; };
} }
@@ -65,10 +68,10 @@ namespace Ryujinx.Common.Configuration.Hid
switch (value.Backend) switch (value.Backend)
{ {
case InputBackendType.WindowKeyboard: case InputBackendType.WindowKeyboard:
JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, options); JsonSerializer.Serialize(writer, value as StandardKeyboardInputConfig, SerializerContext.StandardKeyboardInputConfig);
break; break;
case InputBackendType.GamepadSDL2: case InputBackendType.GamepadSDL2:
JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, options); JsonSerializer.Serialize(writer, value as StandardControllerInputConfig, SerializerContext.StandardControllerInputConfig);
break; break;
default: default:
throw new ArgumentException($"Unknown backend type {value.Backend}"); throw new ArgumentException($"Unknown backend type {value.Backend}");

View File

@@ -1,5 +1,9 @@
namespace Ryujinx.Common.Configuration.Hid using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration.Hid
{ {
[JsonConverter(typeof(TypedStringEnumConverter<Key>))]
public enum Key public enum Key
{ {
Unknown, Unknown,
@@ -136,4 +140,4 @@
Count Count
} }
} }

View File

@@ -1,5 +1,7 @@
namespace Ryujinx.Common.Configuration.Hid namespace Ryujinx.Common.Configuration.Hid
{ {
// NOTE: Please don't change this to struct.
// This breaks Avalonia's TwoWay binding, which makes us unable to save new KeyboardHotkeys.
public class KeyboardHotkeys public class KeyboardHotkeys
{ {
public Key ToggleVsync { get; set; } public Key ToggleVsync { get; set; }
@@ -12,4 +14,4 @@
public Key VolumeUp { get; set; } public Key VolumeUp { get; set; }
public Key VolumeDown { get; set; } public Key VolumeDown { get; set; }
} }
} }

View File

@@ -1,6 +1,10 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration.Hid namespace Ryujinx.Common.Configuration.Hid
{ {
// This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical // This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
[JsonConverter(typeof(TypedStringEnumConverter<PlayerIndex>))]
public enum PlayerIndex : int public enum PlayerIndex : int
{ {
Player1 = 0, Player1 = 0,

View File

@@ -1,5 +1,9 @@
namespace Ryujinx.Common.Configuration using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration
{ {
[JsonConverter(typeof(TypedStringEnumConverter<MemoryManagerMode>))]
public enum MemoryManagerMode : byte public enum MemoryManagerMode : byte
{ {
SoftwarePageTable, SoftwarePageTable,

View File

@@ -1,5 +1,9 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration namespace Ryujinx.Common.Configuration
{ {
[JsonConverter(typeof(TypedStringEnumConverter<ScalingFilter>))]
public enum ScalingFilter public enum ScalingFilter
{ {
Bilinear, Bilinear,

View File

@@ -0,0 +1,10 @@
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Configuration
{
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(TitleUpdateMetadata))]
public partial class TitleUpdateMetadataJsonSerializerContext : JsonSerializerContext
{
}
}

View File

@@ -1,22 +1,20 @@
using System; using System.Text;
using System.Reflection;
using System.Text;
namespace Ryujinx.Common.Logging namespace Ryujinx.Common.Logging
{ {
internal class DefaultLogFormatter : ILogFormatter internal class DefaultLogFormatter : ILogFormatter
{ {
private static readonly ObjectPool<StringBuilder> _stringBuilderPool = SharedPools.Default<StringBuilder>(); private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>();
public string Format(LogEventArgs args) public string Format(LogEventArgs args)
{ {
StringBuilder sb = _stringBuilderPool.Allocate(); StringBuilder sb = StringBuilderPool.Allocate();
try try
{ {
sb.Clear(); sb.Clear();
sb.AppendFormat(@"{0:hh\:mm\:ss\.fff}", args.Time); sb.Append($@"{args.Time:hh\:mm\:ss\.fff}");
sb.Append($" |{args.Level.ToString()[0]}| "); sb.Append($" |{args.Level.ToString()[0]}| ");
if (args.ThreadName != null) if (args.ThreadName != null)
@@ -27,53 +25,17 @@ namespace Ryujinx.Common.Logging
sb.Append(args.Message); sb.Append(args.Message);
if (args.Data != null) if (args.Data is not null)
{ {
PropertyInfo[] props = args.Data.GetType().GetProperties(); sb.Append(' ');
DynamicObjectFormatter.Format(sb, args.Data);
sb.Append(" {");
foreach (var prop in props)
{
sb.Append(prop.Name);
sb.Append(": ");
if (typeof(Array).IsAssignableFrom(prop.PropertyType))
{
Array array = (Array)prop.GetValue(args.Data);
foreach (var item in array)
{
sb.Append(item.ToString());
sb.Append(", ");
}
if (array.Length > 0)
{
sb.Remove(sb.Length - 2, 2);
}
}
else
{
sb.Append(prop.GetValue(args.Data));
}
sb.Append(" ; ");
}
// We remove the final ';' from the string
if (props.Length > 0)
{
sb.Remove(sb.Length - 3, 3);
}
sb.Append('}');
} }
return sb.ToString(); return sb.ToString();
} }
finally finally
{ {
_stringBuilderPool.Release(sb); StringBuilderPool.Release(sb);
} }
} }
} }

View File

@@ -0,0 +1,84 @@
#nullable enable
using System;
using System.Reflection;
using System.Text;
namespace Ryujinx.Common.Logging
{
internal class DynamicObjectFormatter
{
private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>();
public static string? Format(object? dynamicObject)
{
if (dynamicObject is null)
{
return null;
}
StringBuilder sb = StringBuilderPool.Allocate();
try
{
Format(sb, dynamicObject);
return sb.ToString();
}
finally
{
StringBuilderPool.Release(sb);
}
}
public static void Format(StringBuilder sb, object? dynamicObject)
{
if (dynamicObject is null)
{
return;
}
PropertyInfo[] props = dynamicObject.GetType().GetProperties();
sb.Append('{');
foreach (var prop in props)
{
sb.Append(prop.Name);
sb.Append(": ");
if (typeof(Array).IsAssignableFrom(prop.PropertyType))
{
Array? array = (Array?) prop.GetValue(dynamicObject);
if (array is not null)
{
foreach (var item in array)
{
sb.Append(item);
sb.Append(", ");
}
if (array.Length > 0)
{
sb.Remove(sb.Length - 2, 2);
}
}
}
else
{
sb.Append(prop.GetValue(dynamicObject));
}
sb.Append(" ; ");
}
// We remove the final ';' from the string
if (props.Length > 0)
{
sb.Remove(sb.Length - 3, 3);
}
sb.Append('}');
}
}
}

View File

@@ -1,5 +1,9 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Logging namespace Ryujinx.Common.Logging
{ {
[JsonConverter(typeof(TypedStringEnumConverter<LogClass>))]
public enum LogClass public enum LogClass
{ {
Application, Application,

View File

@@ -11,15 +11,7 @@ namespace Ryujinx.Common.Logging
public readonly string Message; public readonly string Message;
public readonly object Data; public readonly object Data;
public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message) public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data = null)
{
Level = level;
Time = time;
ThreadName = threadName;
Message = message;
}
public LogEventArgs(LogLevel level, TimeSpan time, string threadName, string message, object data)
{ {
Level = level; Level = level;
Time = time; Time = time;

View File

@@ -0,0 +1,30 @@
using System;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Logging
{
internal class LogEventArgsJson
{
public LogLevel Level { get; }
public TimeSpan Time { get; }
public string ThreadName { get; }
public string Message { get; }
public string Data { get; }
[JsonConstructor]
public LogEventArgsJson(LogLevel level, TimeSpan time, string threadName, string message, string data = null)
{
Level = level;
Time = time;
ThreadName = threadName;
Message = message;
Data = data;
}
public static LogEventArgsJson FromLogEventArgs(LogEventArgs args)
{
return new LogEventArgsJson(args.Level, args.Time, args.ThreadName, args.Message, DynamicObjectFormatter.Format(args.Data));
}
}
}

View File

@@ -0,0 +1,9 @@
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Logging
{
[JsonSerializable(typeof(LogEventArgsJson))]
internal partial class LogEventJsonSerializerContext : JsonSerializerContext
{
}
}

View File

@@ -1,5 +1,9 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Logging namespace Ryujinx.Common.Logging
{ {
[JsonConverter(typeof(TypedStringEnumConverter<LogLevel>))]
public enum LogLevel public enum LogLevel
{ {
Debug, Debug,

View File

@@ -1,5 +1,5 @@
using System.IO; using Ryujinx.Common.Utilities;
using System.Text.Json; using System.IO;
namespace Ryujinx.Common.Logging namespace Ryujinx.Common.Logging
{ {
@@ -25,12 +25,8 @@ namespace Ryujinx.Common.Logging
public void Log(object sender, LogEventArgs e) public void Log(object sender, LogEventArgs e)
{ {
string text = JsonSerializer.Serialize(e); var logEventArgsJson = LogEventArgsJson.FromLogEventArgs(e);
JsonHelper.SerializeToStream(_stream, logEventArgsJson, LogEventJsonSerializerContext.Default.LogEventArgsJson);
using (BinaryWriter writer = new BinaryWriter(_stream))
{
writer.Write(text);
}
} }
public void Dispose() public void Dispose()

View File

@@ -0,0 +1,11 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Utilities
{
[JsonSerializable(typeof(string[]), TypeInfoPropertyName = "StringArray")]
[JsonSerializable(typeof(Dictionary<string, string>), TypeInfoPropertyName = "StringDictionary")]
public partial class CommonJsonContext : JsonSerializerContext
{
}
}

View File

@@ -1,15 +1,62 @@
using Ryujinx.Common.Configuration.Hid; using System.IO;
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using System.IO;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata;
namespace Ryujinx.Common.Utilities namespace Ryujinx.Common.Utilities
{ {
public class JsonHelper public class JsonHelper
{ {
public static JsonNamingPolicy SnakeCase { get; } private static readonly JsonNamingPolicy SnakeCasePolicy = new SnakeCaseNamingPolicy();
private const int DefaultFileWriteBufferSize = 4096;
/// <summary>
/// Creates new serializer options with default settings.
/// </summary>
/// <remarks>
/// It is REQUIRED for you to save returned options statically or as a part of static serializer context
/// in order to avoid performance issues. You can safely modify returned options for your case before storing.
/// </remarks>
public static JsonSerializerOptions GetDefaultSerializerOptions(bool indented = true)
{
JsonSerializerOptions options = new()
{
DictionaryKeyPolicy = SnakeCasePolicy,
PropertyNamingPolicy = SnakeCasePolicy,
WriteIndented = indented,
AllowTrailingCommas = true,
ReadCommentHandling = JsonCommentHandling.Skip
};
return options;
}
public static string Serialize<T>(T value, JsonTypeInfo<T> typeInfo)
{
return JsonSerializer.Serialize(value, typeInfo);
}
public static T Deserialize<T>(string value, JsonTypeInfo<T> typeInfo)
{
return JsonSerializer.Deserialize(value, typeInfo);
}
public static void SerializeToFile<T>(string filePath, T value, JsonTypeInfo<T> typeInfo)
{
using FileStream file = File.Create(filePath, DefaultFileWriteBufferSize, FileOptions.WriteThrough);
JsonSerializer.Serialize(file, value, typeInfo);
}
public static T DeserializeFromFile<T>(string filePath, JsonTypeInfo<T> typeInfo)
{
using FileStream file = File.OpenRead(filePath);
return JsonSerializer.Deserialize(file, typeInfo);
}
public static void SerializeToStream<T>(Stream stream, T value, JsonTypeInfo<T> typeInfo)
{
JsonSerializer.Serialize(stream, value, typeInfo);
}
private class SnakeCaseNamingPolicy : JsonNamingPolicy private class SnakeCaseNamingPolicy : JsonNamingPolicy
{ {
@@ -20,7 +67,7 @@ namespace Ryujinx.Common.Utilities
return name; return name;
} }
StringBuilder builder = new StringBuilder(); StringBuilder builder = new();
for (int i = 0; i < name.Length; i++) for (int i = 0; i < name.Length; i++)
{ {
@@ -34,7 +81,7 @@ namespace Ryujinx.Common.Utilities
} }
else else
{ {
builder.Append("_"); builder.Append('_');
builder.Append(char.ToLowerInvariant(c)); builder.Append(char.ToLowerInvariant(c));
} }
} }
@@ -47,64 +94,5 @@ namespace Ryujinx.Common.Utilities
return builder.ToString(); return builder.ToString();
} }
} }
static JsonHelper()
{
SnakeCase = new SnakeCaseNamingPolicy();
}
public static JsonSerializerOptions GetDefaultSerializerOptions(bool prettyPrint = false)
{
JsonSerializerOptions options = new JsonSerializerOptions
{
DictionaryKeyPolicy = SnakeCase,
PropertyNamingPolicy = SnakeCase,
WriteIndented = prettyPrint,
AllowTrailingCommas = true,
ReadCommentHandling = JsonCommentHandling.Skip
};
options.Converters.Add(new JsonStringEnumConverter());
options.Converters.Add(new JsonInputConfigConverter());
options.Converters.Add(new JsonMotionConfigControllerConverter());
return options;
}
public static T Deserialize<T>(Stream stream)
{
using (BinaryReader reader = new BinaryReader(stream))
{
return JsonSerializer.Deserialize<T>(reader.ReadBytes((int)(stream.Length - stream.Position)), GetDefaultSerializerOptions());
}
}
public static T DeserializeFromFile<T>(string path)
{
return Deserialize<T>(File.ReadAllText(path));
}
public static T Deserialize<T>(string json)
{
return JsonSerializer.Deserialize<T>(json, GetDefaultSerializerOptions());
}
public static void Serialize<TValue>(Stream stream, TValue obj, bool prettyPrint = false)
{
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write(SerializeToUtf8Bytes(obj, prettyPrint));
}
}
public static string Serialize<TValue>(TValue obj, bool prettyPrint = false)
{
return JsonSerializer.Serialize(obj, GetDefaultSerializerOptions(prettyPrint));
}
public static byte[] SerializeToUtf8Bytes<T>(T obj, bool prettyPrint = false)
{
return JsonSerializer.SerializeToUtf8Bytes(obj, GetDefaultSerializerOptions(prettyPrint));
}
} }
} }

View File

@@ -0,0 +1,34 @@
#nullable enable
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Ryujinx.Common.Utilities
{
/// <summary>
/// Specifies that value of <see cref="TEnum"/> will be serialized as string in JSONs
/// </summary>
/// <remarks>
/// Trimming friendly alternative to <see cref="JsonStringEnumConverter"/>.
/// Get rid of this converter if dotnet supports similar functionality out of the box.
/// </remarks>
/// <typeparam name="TEnum">Type of enum to serialize</typeparam>
public sealed class TypedStringEnumConverter<TEnum> : JsonConverter<TEnum> where TEnum : struct, Enum
{
public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var enumValue = reader.GetString();
if (string.IsNullOrEmpty(enumValue))
{
return default;
}
return Enum.Parse<TEnum>(enumValue);
}
public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}
}

View File

@@ -38,4 +38,25 @@ namespace Ryujinx.Graphics.GAL
Src1AlphaGl = 0xc902, Src1AlphaGl = 0xc902,
OneMinusSrc1AlphaGl = 0xc903 OneMinusSrc1AlphaGl = 0xc903
} }
public static class BlendFactorExtensions
{
public static bool IsDualSource(this BlendFactor factor)
{
switch (factor)
{
case BlendFactor.Src1Color:
case BlendFactor.Src1ColorGl:
case BlendFactor.Src1Alpha:
case BlendFactor.Src1AlphaGl:
case BlendFactor.OneMinusSrc1Color:
case BlendFactor.OneMinusSrc1ColorGl:
case BlendFactor.OneMinusSrc1Alpha:
case BlendFactor.OneMinusSrc1AlphaGl:
return true;
}
return false;
}
}
} }

View File

@@ -328,5 +328,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
Signal(); Signal();
} }
} }
/// <summary>
/// Sets the dual-source blend enabled state.
/// </summary>
/// <param name="enabled">True if blending is enabled and using dual-source blend</param>
public void SetDualSourceBlendEnabled(bool enabled)
{
if (enabled != _graphics.DualSourceBlendEnable)
{
_graphics.DualSourceBlendEnable = enabled;
Signal();
}
}
} }
} }

View File

@@ -1183,6 +1183,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool blendIndependent = _state.State.BlendIndependent; bool blendIndependent = _state.State.BlendIndependent;
ColorF blendConstant = _state.State.BlendConstant; ColorF blendConstant = _state.State.BlendConstant;
bool dualSourceBlendEnabled = false;
if (blendIndependent) if (blendIndependent)
{ {
for (int index = 0; index < Constants.TotalRenderTargets; index++) for (int index = 0; index < Constants.TotalRenderTargets; index++)
@@ -1200,6 +1202,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
FilterBlendFactor(blend.AlphaSrcFactor, index), FilterBlendFactor(blend.AlphaSrcFactor, index),
FilterBlendFactor(blend.AlphaDstFactor, index)); FilterBlendFactor(blend.AlphaDstFactor, index));
if (enable &&
(blend.ColorSrcFactor.IsDualSource() ||
blend.ColorDstFactor.IsDualSource() ||
blend.AlphaSrcFactor.IsDualSource() ||
blend.AlphaDstFactor.IsDualSource()))
{
dualSourceBlendEnabled = true;
}
_pipeline.BlendDescriptors[index] = descriptor; _pipeline.BlendDescriptors[index] = descriptor;
_context.Renderer.Pipeline.SetBlendState(index, descriptor); _context.Renderer.Pipeline.SetBlendState(index, descriptor);
} }
@@ -1219,12 +1230,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
FilterBlendFactor(blend.AlphaSrcFactor, 0), FilterBlendFactor(blend.AlphaSrcFactor, 0),
FilterBlendFactor(blend.AlphaDstFactor, 0)); FilterBlendFactor(blend.AlphaDstFactor, 0));
if (enable &&
(blend.ColorSrcFactor.IsDualSource() ||
blend.ColorDstFactor.IsDualSource() ||
blend.AlphaSrcFactor.IsDualSource() ||
blend.AlphaDstFactor.IsDualSource()))
{
dualSourceBlendEnabled = true;
}
for (int index = 0; index < Constants.TotalRenderTargets; index++) for (int index = 0; index < Constants.TotalRenderTargets; index++)
{ {
_pipeline.BlendDescriptors[index] = descriptor; _pipeline.BlendDescriptors[index] = descriptor;
_context.Renderer.Pipeline.SetBlendState(index, descriptor); _context.Renderer.Pipeline.SetBlendState(index, descriptor);
} }
} }
_currentSpecState.SetDualSourceBlendEnabled(dualSourceBlendEnabled);
} }
/// <summary> /// <summary>

View File

@@ -141,6 +141,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters; return _oldSpecState.GraphicsState.HasConstantBufferDrawParameters;
} }
/// <inheritdoc/>
public bool QueryDualSourceBlendEnable()
{
return _oldSpecState.GraphicsState.DualSourceBlendEnable;
}
/// <inheritdoc/> /// <inheritdoc/>
public InputTopology QueryPrimitiveTopology() public InputTopology QueryPrimitiveTopology()
{ {

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2; private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 4368; private const uint CodeGenVersion = 4404;
private const string SharedTocFileName = "shared.toc"; private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";

View File

@@ -157,6 +157,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer; return _state.GraphicsState.HasUnalignedStorageBuffer || _state.ComputeState.HasUnalignedStorageBuffer;
} }
/// <inheritdoc/>
public bool QueryDualSourceBlendEnable()
{
return _state.GraphicsState.DualSourceBlendEnable;
}
/// <inheritdoc/> /// <inheritdoc/>
public InputTopology QueryPrimitiveTopology() public InputTopology QueryPrimitiveTopology()
{ {

View File

@@ -92,6 +92,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary> /// </summary>
public Array8<AttributeType> FragmentOutputTypes; public Array8<AttributeType> FragmentOutputTypes;
/// <summary>
/// Indicates whether dual source blend is enabled.
/// </summary>
public bool DualSourceBlendEnable;
/// <summary> /// <summary>
/// Creates a new GPU graphics state. /// Creates a new GPU graphics state.
/// </summary> /// </summary>
@@ -111,6 +116,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param> /// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param>
/// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param> /// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param>
/// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param> /// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param>
/// <param name="dualSourceBlendEnable">Type of the vertex attributes consumed by the shader</param>
public GpuChannelGraphicsState( public GpuChannelGraphicsState(
bool earlyZForce, bool earlyZForce,
PrimitiveTopology topology, PrimitiveTopology topology,
@@ -127,7 +133,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
ref Array32<AttributeType> attributeTypes, ref Array32<AttributeType> attributeTypes,
bool hasConstantBufferDrawParameters, bool hasConstantBufferDrawParameters,
bool hasUnalignedStorageBuffer, bool hasUnalignedStorageBuffer,
ref Array8<AttributeType> fragmentOutputTypes) ref Array8<AttributeType> fragmentOutputTypes,
bool dualSourceBlendEnable)
{ {
EarlyZForce = earlyZForce; EarlyZForce = earlyZForce;
Topology = topology; Topology = topology;
@@ -145,6 +152,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
HasConstantBufferDrawParameters = hasConstantBufferDrawParameters; HasConstantBufferDrawParameters = hasConstantBufferDrawParameters;
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
FragmentOutputTypes = fragmentOutputTypes; FragmentOutputTypes = fragmentOutputTypes;
DualSourceBlendEnable = dualSourceBlendEnable;
} }
} }
} }

View File

@@ -535,6 +535,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
return false; return false;
} }
if (graphicsState.DualSourceBlendEnable != GraphicsState.DualSourceBlendEnable)
{
return false;
}
return Matches(channel, ref poolState, checkTextures, isCompute: false); return Matches(channel, ref poolState, checkTextures, isCompute: false);
} }

View File

@@ -833,31 +833,13 @@ namespace Ryujinx.Graphics.OpenGL
(BlendingFactorSrc)blend.AlphaSrcFactor.Convert(), (BlendingFactorSrc)blend.AlphaSrcFactor.Convert(),
(BlendingFactorDest)blend.AlphaDstFactor.Convert()); (BlendingFactorDest)blend.AlphaDstFactor.Convert());
static bool IsDualSource(BlendFactor factor)
{
switch (factor)
{
case BlendFactor.Src1Color:
case BlendFactor.Src1ColorGl:
case BlendFactor.Src1Alpha:
case BlendFactor.Src1AlphaGl:
case BlendFactor.OneMinusSrc1Color:
case BlendFactor.OneMinusSrc1ColorGl:
case BlendFactor.OneMinusSrc1Alpha:
case BlendFactor.OneMinusSrc1AlphaGl:
return true;
}
return false;
}
EnsureFramebuffer(); EnsureFramebuffer();
_framebuffer.SetDualSourceBlend( _framebuffer.SetDualSourceBlend(
IsDualSource(blend.ColorSrcFactor) || blend.ColorSrcFactor.IsDualSource() ||
IsDualSource(blend.ColorDstFactor) || blend.ColorDstFactor.IsDualSource() ||
IsDualSource(blend.AlphaSrcFactor) || blend.AlphaSrcFactor.IsDualSource() ||
IsDualSource(blend.AlphaDstFactor)); blend.AlphaDstFactor.IsDualSource());
if (_blendConstant != blend.BlendConstant) if (_blendConstant != blend.BlendConstant)
{ {

View File

@@ -226,6 +226,7 @@ namespace Ryujinx.Graphics.OpenGL
// Set clip control, viewport and the framebuffer to the output to placate overlays and OBS capture. // Set clip control, viewport and the framebuffer to the output to placate overlays and OBS capture.
GL.ClipControl(ClipOrigin.LowerLeft, ClipDepthMode.NegativeOneToOne); GL.ClipControl(ClipOrigin.LowerLeft, ClipDepthMode.NegativeOneToOne);
GL.Viewport(0, 0, _width, _height); GL.Viewport(0, 0, _width, _height);
GL.BindFramebuffer(FramebufferTarget.Framebuffer, drawFramebuffer);
swapBuffersCallback(); swapBuffersCallback();

View File

@@ -612,6 +612,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
else else
{ {
int usedAttributes = context.Config.UsedOutputAttributes; int usedAttributes = context.Config.UsedOutputAttributes;
if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable())
{
int firstOutput = BitOperations.TrailingZeroCount(usedAttributes);
int mask = 3 << firstOutput;
if ((usedAttributes & mask) == mask)
{
usedAttributes &= ~mask;
DeclareOutputDualSourceBlendAttribute(context, firstOutput);
}
}
while (usedAttributes != 0) while (usedAttributes != 0)
{ {
int index = BitOperations.TrailingZeroCount(usedAttributes); int index = BitOperations.TrailingZeroCount(usedAttributes);
@@ -690,6 +703,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
} }
} }
private static void DeclareOutputDualSourceBlendAttribute(CodeGenContext context, int attr)
{
string name = $"{DefaultNames.OAttributePrefix}{attr}";
string name2 = $"{DefaultNames.OAttributePrefix}{(attr + 1)}";
context.AppendLine($"layout (location = {attr}, index = 0) out vec4 {name};");
context.AppendLine($"layout (location = {attr}, index = 1) out vec4 {name2};");
}
private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs) private static void DeclareUsedOutputAttributesPerPatch(CodeGenContext context, HashSet<int> attrs)
{ {
foreach (int attr in attrs.Order()) foreach (int attr in attrs.Order())

View File

@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Numerics;
using static Spv.Specification; using static Spv.Specification;
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
@@ -622,7 +623,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd) else if (attr >= AttributeConsts.FragmentOutputColorBase && attr < AttributeConsts.FragmentOutputColorEnd)
{ {
int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16; int location = (attr - AttributeConsts.FragmentOutputColorBase) / 16;
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryDualSourceBlendEnable())
{
int firstLocation = BitOperations.TrailingZeroCount(context.Config.UsedOutputAttributes);
int index = location - firstLocation;
int mask = 3 << firstLocation;
if ((uint)index < 2 && (context.Config.UsedOutputAttributes & mask) == mask)
{
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)firstLocation);
context.Decorate(spvVar, Decoration.Index, (LiteralInteger)index);
}
else
{
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
}
}
else
{
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
}
} }
if (!isOutAttr) if (!isOutAttr)

View File

@@ -205,6 +205,15 @@ namespace Ryujinx.Graphics.Shader
return false; return false;
} }
/// <summary>
/// Queries dual source blend state.
/// </summary>
/// <returns>True if blending is enabled with a dual source blend equation, false otherwise</returns>
bool QueryDualSourceBlendEnable()
{
return false;
}
/// <summary> /// <summary>
/// Queries host about the presence of the FrontFacing built-in variable bug. /// Queries host about the presence of the FrontFacing built-in variable bug.
/// </summary> /// </summary>

View File

@@ -9,21 +9,18 @@ namespace Ryujinx.Graphics.Vulkan
private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024; private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024;
private readonly Vk _api; private readonly Vk _api;
private readonly PhysicalDevice _physicalDevice; private readonly VulkanPhysicalDevice _physicalDevice;
private readonly Device _device; private readonly Device _device;
private readonly List<MemoryAllocatorBlockList> _blockLists; private readonly List<MemoryAllocatorBlockList> _blockLists;
private readonly int _blockAlignment; private readonly int _blockAlignment;
private readonly PhysicalDeviceMemoryProperties _physicalDeviceMemoryProperties;
public MemoryAllocator(Vk api, PhysicalDevice physicalDevice, Device device, uint maxMemoryAllocationCount) public MemoryAllocator(Vk api, VulkanPhysicalDevice physicalDevice, Device device)
{ {
_api = api; _api = api;
_physicalDevice = physicalDevice; _physicalDevice = physicalDevice;
_device = device; _device = device;
_blockLists = new List<MemoryAllocatorBlockList>(); _blockLists = new List<MemoryAllocatorBlockList>();
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)maxMemoryAllocationCount); _blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)_physicalDevice.PhysicalDeviceProperties.Limits.MaxMemoryAllocationCount);
_api.GetPhysicalDeviceMemoryProperties(_physicalDevice, out _physicalDeviceMemoryProperties);
} }
public MemoryAllocation AllocateDeviceMemory( public MemoryAllocation AllocateDeviceMemory(
@@ -64,9 +61,9 @@ namespace Ryujinx.Graphics.Vulkan
uint memoryTypeBits, uint memoryTypeBits,
MemoryPropertyFlags flags) MemoryPropertyFlags flags)
{ {
for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++) for (int i = 0; i < _physicalDevice.PhysicalDeviceMemoryProperties.MemoryTypeCount; i++)
{ {
var type = _physicalDeviceMemoryProperties.MemoryTypes[i]; var type = _physicalDevice.PhysicalDeviceMemoryProperties.MemoryTypes[i];
if ((memoryTypeBits & (1 << i)) != 0) if ((memoryTypeBits & (1 << i)) != 0)
{ {
@@ -80,15 +77,11 @@ namespace Ryujinx.Graphics.Vulkan
return -1; return -1;
} }
public static bool IsDeviceMemoryShared(Vk api, PhysicalDevice physicalDevice) public static bool IsDeviceMemoryShared(VulkanPhysicalDevice physicalDevice)
{ {
// The device is regarded as having shared memory if all heaps have the device local bit. for (int i = 0; i < physicalDevice.PhysicalDeviceMemoryProperties.MemoryHeapCount; i++)
api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties);
for (int i = 0; i < properties.MemoryHeapCount; i++)
{ {
if (!properties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit)) if (!physicalDevice.PhysicalDeviceMemoryProperties.MemoryHeaps[i].Flags.HasFlag(MemoryHeapFlags.DeviceLocalBit))
{ {
return false; return false;
} }

View File

@@ -0,0 +1,153 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Silk.NET.Vulkan;
using Silk.NET.Vulkan.Extensions.EXT;
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Vulkan
{
class VulkanDebugMessenger : IDisposable
{
private static string[] _excludedMessages = new string[]
{
// NOTE: Done on purpose right now.
"UNASSIGNED-CoreValidation-Shader-OutputNotConsumed",
// TODO: Figure out if fixable
"VUID-vkCmdDrawIndexed-None-04584",
// TODO: Might be worth looking into making this happy to possibly optimize copies.
"UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout",
// TODO: Fix this, it's causing too much noise right now.
"VUID-VkSubpassDependency-srcSubpass-00867"
};
private readonly Vk _api;
private readonly Instance _instance;
private readonly GraphicsDebugLevel _logLevel;
private readonly ExtDebugUtils _debugUtils;
private readonly DebugUtilsMessengerEXT? _debugUtilsMessenger;
private bool _disposed;
public VulkanDebugMessenger(Vk api, Instance instance, GraphicsDebugLevel logLevel)
{
_api = api;
_instance = instance;
_logLevel = logLevel;
_api.TryGetInstanceExtension(instance, out _debugUtils);
Result result = TryInitialize(out _debugUtilsMessenger);
if (result != Result.Success)
{
Logger.Error?.Print(LogClass.Gpu, $"Vulkan debug messenger initialization failed with error {result}");
}
}
private Result TryInitialize(out DebugUtilsMessengerEXT? debugUtilsMessengerHandle)
{
debugUtilsMessengerHandle = null;
if (_debugUtils != null && _logLevel != GraphicsDebugLevel.None)
{
var messageType = _logLevel switch
{
GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt,
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.GeneralBitExt |
DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
_ => throw new ArgumentException($"Invalid log level \"{_logLevel}\".")
};
var messageSeverity = _logLevel switch
{
GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt |
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt,
GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.InfoBitExt |
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt |
DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt |
DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
_ => throw new ArgumentException($"Invalid log level \"{_logLevel}\".")
};
var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT()
{
SType = StructureType.DebugUtilsMessengerCreateInfoExt,
MessageType = messageType,
MessageSeverity = messageSeverity
};
unsafe
{
debugUtilsMessengerCreateInfo.PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(UserCallback);
}
DebugUtilsMessengerEXT messengerHandle = default;
Result result = _debugUtils.CreateDebugUtilsMessenger(_instance, SpanHelpers.AsReadOnlySpan(ref debugUtilsMessengerCreateInfo), ReadOnlySpan<AllocationCallbacks>.Empty, SpanHelpers.AsSpan(ref messengerHandle));
if (result == Result.Success)
{
debugUtilsMessengerHandle = messengerHandle;
}
return result;
}
return Result.Success;
}
private unsafe static uint UserCallback(
DebugUtilsMessageSeverityFlagsEXT messageSeverity,
DebugUtilsMessageTypeFlagsEXT messageTypes,
DebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData)
{
var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage);
foreach (string excludedMessagePart in _excludedMessages)
{
if (msg.Contains(excludedMessagePart))
{
return 0;
}
}
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt))
{
Logger.Error?.Print(LogClass.Gpu, msg);
}
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt))
{
Logger.Warning?.Print(LogClass.Gpu, msg);
}
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.InfoBitExt))
{
Logger.Info?.Print(LogClass.Gpu, msg);
}
else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt))
{
Logger.Debug?.Print(LogClass.Gpu, msg);
}
return 0;
}
public void Dispose()
{
if (!_disposed)
{
if (_debugUtilsMessenger.HasValue)
{
_debugUtils.DestroyDebugUtilsMessenger(_instance, _debugUtilsMessenger.Value, Span<AllocationCallbacks>.Empty);
}
_disposed = true;
}
}
}
}

View File

@@ -47,47 +47,23 @@ namespace Ryujinx.Graphics.Vulkan
KhrSwapchain.ExtensionName KhrSwapchain.ExtensionName
}; };
private static string[] _excludedMessages = new string[] internal static VulkanInstance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions)
{
// NOTE: Done on purpose right now.
"UNASSIGNED-CoreValidation-Shader-OutputNotConsumed",
// TODO: Figure out if fixable
"VUID-vkCmdDrawIndexed-None-04584",
// TODO: Might be worth looking into making this happy to possibly optimize copies.
"UNASSIGNED-CoreValidation-DrawState-InvalidImageLayout",
// TODO: Fix this, it's causing too much noise right now.
"VUID-VkSubpassDependency-srcSubpass-00867"
};
internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions, out ExtDebugUtils debugUtils, out DebugUtilsMessengerEXT debugUtilsMessenger)
{ {
var enabledLayers = new List<string>(); var enabledLayers = new List<string>();
var instanceExtensions = VulkanInstance.GetInstanceExtensions(api);
var instanceLayers = VulkanInstance.GetInstanceLayers(api);
void AddAvailableLayer(string layerName) void AddAvailableLayer(string layerName)
{ {
uint layerPropertiesCount; if (instanceLayers.Contains(layerName))
api.EnumerateInstanceLayerProperties(&layerPropertiesCount, null).ThrowOnError();
LayerProperties[] layerProperties = new LayerProperties[layerPropertiesCount];
fixed (LayerProperties* pLayerProperties = layerProperties)
{ {
api.EnumerateInstanceLayerProperties(&layerPropertiesCount, layerProperties).ThrowOnError(); enabledLayers.Add(layerName);
}
for (int i = 0; i < layerPropertiesCount; i++) else
{ {
string currentLayerName = Marshal.PtrToStringAnsi((IntPtr)pLayerProperties[i].LayerName); Logger.Warning?.Print(LogClass.Gpu, $"Missing layer {layerName}");
if (currentLayerName == layerName)
{
enabledLayers.Add(layerName);
return;
}
}
} }
Logger.Warning?.Print(LogClass.Gpu, $"Missing layer {layerName}");
} }
if (logLevel != GraphicsDebugLevel.None) if (logLevel != GraphicsDebugLevel.None)
@@ -95,7 +71,12 @@ namespace Ryujinx.Graphics.Vulkan
AddAvailableLayer("VK_LAYER_KHRONOS_validation"); AddAvailableLayer("VK_LAYER_KHRONOS_validation");
} }
var enabledExtensions = requiredExtensions.Append(ExtDebugUtils.ExtensionName).ToArray(); var enabledExtensions = requiredExtensions;
if (instanceExtensions.Contains("VK_EXT_debug_utils"))
{
enabledExtensions = enabledExtensions.Append(ExtDebugUtils.ExtensionName).ToArray();
}
var appName = Marshal.StringToHGlobalAnsi(AppName); var appName = Marshal.StringToHGlobalAnsi(AppName);
@@ -131,7 +112,7 @@ namespace Ryujinx.Graphics.Vulkan
EnabledLayerCount = (uint)enabledLayers.Count EnabledLayerCount = (uint)enabledLayers.Count
}; };
api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError(); Result result = VulkanInstance.Create(api, ref instanceCreateInfo, out var instance);
Marshal.FreeHGlobal(appName); Marshal.FreeHGlobal(appName);
@@ -145,59 +126,14 @@ namespace Ryujinx.Graphics.Vulkan
Marshal.FreeHGlobal(ppEnabledLayers[i]); Marshal.FreeHGlobal(ppEnabledLayers[i]);
} }
CreateDebugMessenger(api, logLevel, instance, out debugUtils, out debugUtilsMessenger); result.ThrowOnError();
return instance; return instance;
} }
private unsafe static uint DebugMessenger( internal static VulkanPhysicalDevice FindSuitablePhysicalDevice(Vk api, VulkanInstance instance, SurfaceKHR surface, string preferredGpuId)
DebugUtilsMessageSeverityFlagsEXT messageSeverity,
DebugUtilsMessageTypeFlagsEXT messageTypes,
DebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData)
{ {
var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage); instance.EnumeratePhysicalDevices(out var physicalDevices).ThrowOnError();
foreach (string excludedMessagePart in _excludedMessages)
{
if (msg.Contains(excludedMessagePart))
{
return 0;
}
}
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt))
{
Logger.Error?.Print(LogClass.Gpu, msg);
}
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt))
{
Logger.Warning?.Print(LogClass.Gpu, msg);
}
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.InfoBitExt))
{
Logger.Info?.Print(LogClass.Gpu, msg);
}
else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt))
{
Logger.Debug?.Print(LogClass.Gpu, msg);
}
return 0;
}
internal static PhysicalDevice FindSuitablePhysicalDevice(Vk api, Instance instance, SurfaceKHR surface, string preferredGpuId)
{
uint physicalDeviceCount;
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, null).ThrowOnError();
PhysicalDevice[] physicalDevices = new PhysicalDevice[physicalDeviceCount];
fixed (PhysicalDevice* pPhysicalDevices = physicalDevices)
{
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, pPhysicalDevices).ThrowOnError();
}
// First we try to pick the the user preferred GPU. // First we try to pick the the user preferred GPU.
for (int i = 0; i < physicalDevices.Length; i++) for (int i = 0; i < physicalDevices.Length; i++)
@@ -243,76 +179,41 @@ namespace Ryujinx.Graphics.Vulkan
EnabledLayerCount = 0 EnabledLayerCount = 0
}; };
api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError(); Result result = VulkanInstance.Create(api, ref instanceCreateInfo, out var rawInstance);
// We ensure that vkEnumerateInstanceVersion is present (added in 1.1).
// If the instance doesn't support it, no device is going to be 1.1 compatible.
if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == IntPtr.Zero)
{
api.DestroyInstance(instance, null);
return Array.Empty<DeviceInfo>();
}
// We currently assume that the instance is compatible with Vulkan 1.2
// TODO: Remove this once we relax our initialization codepaths.
uint instanceApiVerison = 0;
api.EnumerateInstanceVersion(ref instanceApiVerison).ThrowOnError();
if (instanceApiVerison < MinimalInstanceVulkanVersion)
{
api.DestroyInstance(instance, null);
return Array.Empty<DeviceInfo>();
}
Marshal.FreeHGlobal(appName); Marshal.FreeHGlobal(appName);
uint physicalDeviceCount; result.ThrowOnError();
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, null).ThrowOnError(); using VulkanInstance instance = rawInstance;
PhysicalDevice[] physicalDevices = new PhysicalDevice[physicalDeviceCount]; // We currently assume that the instance is compatible with Vulkan 1.2
// TODO: Remove this once we relax our initialization codepaths.
fixed (PhysicalDevice* pPhysicalDevices = physicalDevices) if (instance.InstanceVersion < MinimalInstanceVulkanVersion)
{ {
api.EnumeratePhysicalDevices(instance, &physicalDeviceCount, pPhysicalDevices).ThrowOnError(); return Array.Empty<DeviceInfo>();
} }
DeviceInfo[] devices = new DeviceInfo[physicalDevices.Length]; instance.EnumeratePhysicalDevices(out VulkanPhysicalDevice[] physicalDevices).ThrowOnError();
for (int i = 0; i < physicalDevices.Length; i++) List<DeviceInfo> deviceInfos = new List<DeviceInfo>();
foreach (VulkanPhysicalDevice physicalDevice in physicalDevices)
{ {
var physicalDevice = physicalDevices[i]; if (physicalDevice.PhysicalDeviceProperties.ApiVersion < MinimalVulkanVersion)
api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
if (properties.ApiVersion < MinimalVulkanVersion)
{ {
continue; continue;
} }
devices[i] = new DeviceInfo( deviceInfos.Add(physicalDevice.ToDeviceInfo());
StringFromIdPair(properties.VendorID, properties.DeviceID),
VendorUtils.GetNameFromId(properties.VendorID),
Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName),
properties.DeviceType == PhysicalDeviceType.DiscreteGpu);
} }
api.DestroyInstance(instance, null); return deviceInfos.ToArray();
return devices;
} }
public static string StringFromIdPair(uint vendorId, uint deviceId) private static bool IsPreferredAndSuitableDevice(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface, string preferredGpuId)
{ {
return $"0x{vendorId:X}_0x{deviceId:X}"; if (physicalDevice.Id != preferredGpuId)
}
private static bool IsPreferredAndSuitableDevice(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, string preferredGpuId)
{
api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
if (StringFromIdPair(properties.VendorID, properties.DeviceID) != preferredGpuId)
{ {
return false; return false;
} }
@@ -320,68 +221,47 @@ namespace Ryujinx.Graphics.Vulkan
return IsSuitableDevice(api, physicalDevice, surface); return IsSuitableDevice(api, physicalDevice, surface);
} }
private static bool IsSuitableDevice(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface) private static bool IsSuitableDevice(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface)
{ {
int extensionMatches = 0; int extensionMatches = 0;
uint propertiesCount;
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError(); foreach (string requiredExtension in _requiredExtensions)
ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
fixed (ExtensionProperties* pExtensionProperties = extensionProperties)
{ {
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, pExtensionProperties).ThrowOnError(); if (physicalDevice.IsDeviceExtensionPresent(requiredExtension))
for (int i = 0; i < propertiesCount; i++)
{ {
string extensionName = Marshal.PtrToStringAnsi((IntPtr)pExtensionProperties[i].ExtensionName); extensionMatches++;
if (_requiredExtensions.Contains(extensionName))
{
extensionMatches++;
}
} }
} }
return extensionMatches == _requiredExtensions.Length && FindSuitableQueueFamily(api, physicalDevice, surface, out _) != InvalidIndex; return extensionMatches == _requiredExtensions.Length && FindSuitableQueueFamily(api, physicalDevice, surface, out _) != InvalidIndex;
} }
internal static uint FindSuitableQueueFamily(Vk api, PhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount) internal static uint FindSuitableQueueFamily(Vk api, VulkanPhysicalDevice physicalDevice, SurfaceKHR surface, out uint queueCount)
{ {
const QueueFlags RequiredFlags = QueueFlags.GraphicsBit | QueueFlags.ComputeBit; const QueueFlags RequiredFlags = QueueFlags.GraphicsBit | QueueFlags.ComputeBit;
var khrSurface = new KhrSurface(api.Context); var khrSurface = new KhrSurface(api.Context);
uint propertiesCount; for (uint index = 0; index < physicalDevice.QueueFamilyProperties.Length; index++)
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, null);
QueueFamilyProperties[] properties = new QueueFamilyProperties[propertiesCount];
fixed (QueueFamilyProperties* pProperties = properties)
{ {
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, &propertiesCount, pProperties); ref QueueFamilyProperties property = ref physicalDevice.QueueFamilyProperties[index];
}
for (uint index = 0; index < propertiesCount; index++) khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice.PhysicalDevice, index, surface, out var surfaceSupported).ThrowOnError();
{
var queueFlags = properties[index].QueueFlags;
khrSurface.GetPhysicalDeviceSurfaceSupport(physicalDevice, index, surface, out var surfaceSupported).ThrowOnError(); if (property.QueueFlags.HasFlag(RequiredFlags) && surfaceSupported)
if (queueFlags.HasFlag(RequiredFlags) && surfaceSupported)
{ {
queueCount = properties[index].QueueCount; queueCount = property.QueueCount;
return index; return index;
} }
} }
queueCount = 0; queueCount = 0;
return InvalidIndex; return InvalidIndex;
} }
public static Device CreateDevice(Vk api, PhysicalDevice physicalDevice, uint queueFamilyIndex, string[] supportedExtensions, uint queueCount) internal static Device CreateDevice(Vk api, VulkanPhysicalDevice physicalDevice, uint queueFamilyIndex, uint queueCount)
{ {
if (queueCount > QueuesCount) if (queueCount > QueuesCount)
{ {
@@ -403,8 +283,7 @@ namespace Ryujinx.Graphics.Vulkan
PQueuePriorities = queuePriorities PQueuePriorities = queuePriorities
}; };
api.GetPhysicalDeviceProperties(physicalDevice, out var properties); bool useRobustBufferAccess = VendorUtils.FromId(physicalDevice.PhysicalDeviceProperties.VendorID) == Vendor.Nvidia;
bool useRobustBufferAccess = VendorUtils.FromId(properties.VendorID) == Vendor.Nvidia;
PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2() PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2()
{ {
@@ -425,7 +304,7 @@ namespace Ryujinx.Graphics.Vulkan
PNext = features2.PNext PNext = features2.PNext
}; };
if (supportedExtensions.Contains("VK_EXT_custom_border_color")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color"))
{ {
features2.PNext = &supportedFeaturesCustomBorderColor; features2.PNext = &supportedFeaturesCustomBorderColor;
} }
@@ -436,7 +315,7 @@ namespace Ryujinx.Graphics.Vulkan
PNext = features2.PNext PNext = features2.PNext
}; };
if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart"))
{ {
features2.PNext = &supportedFeaturesPrimitiveTopologyListRestart; features2.PNext = &supportedFeaturesPrimitiveTopologyListRestart;
} }
@@ -447,7 +326,7 @@ namespace Ryujinx.Graphics.Vulkan
PNext = features2.PNext PNext = features2.PNext
}; };
if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName)) if (physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName))
{ {
features2.PNext = &supportedFeaturesTransformFeedback; features2.PNext = &supportedFeaturesTransformFeedback;
} }
@@ -457,14 +336,14 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt SType = StructureType.PhysicalDeviceRobustness2FeaturesExt
}; };
if (supportedExtensions.Contains("VK_EXT_robustness2")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
{ {
supportedFeaturesRobustness2.PNext = features2.PNext; supportedFeaturesRobustness2.PNext = features2.PNext;
features2.PNext = &supportedFeaturesRobustness2; features2.PNext = &supportedFeaturesRobustness2;
} }
api.GetPhysicalDeviceFeatures2(physicalDevice, &features2); api.GetPhysicalDeviceFeatures2(physicalDevice.PhysicalDevice, &features2);
var supportedFeatures = features2.Features; var supportedFeatures = features2.Features;
@@ -497,7 +376,7 @@ namespace Ryujinx.Graphics.Vulkan
PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback; PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback;
if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName)) if (physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName))
{ {
featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT()
{ {
@@ -511,7 +390,7 @@ namespace Ryujinx.Graphics.Vulkan
PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart; PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart;
if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart"))
{ {
featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT() featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT()
{ {
@@ -526,7 +405,7 @@ namespace Ryujinx.Graphics.Vulkan
PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2; PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2;
if (supportedExtensions.Contains("VK_EXT_robustness2")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
{ {
featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
{ {
@@ -542,7 +421,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
SType = StructureType.PhysicalDeviceExtendedDynamicStateFeaturesExt, SType = StructureType.PhysicalDeviceExtendedDynamicStateFeaturesExt,
PNext = pExtendedFeatures, PNext = pExtendedFeatures,
ExtendedDynamicState = supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName) ExtendedDynamicState = physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName)
}; };
pExtendedFeatures = &featuresExtendedDynamicState; pExtendedFeatures = &featuresExtendedDynamicState;
@@ -560,16 +439,16 @@ namespace Ryujinx.Graphics.Vulkan
{ {
SType = StructureType.PhysicalDeviceVulkan12Features, SType = StructureType.PhysicalDeviceVulkan12Features,
PNext = pExtendedFeatures, PNext = pExtendedFeatures,
DescriptorIndexing = supportedExtensions.Contains("VK_EXT_descriptor_indexing"), DescriptorIndexing = physicalDevice.IsDeviceExtensionPresent("VK_EXT_descriptor_indexing"),
DrawIndirectCount = supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName), DrawIndirectCount = physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName),
UniformBufferStandardLayout = supportedExtensions.Contains("VK_KHR_uniform_buffer_standard_layout") UniformBufferStandardLayout = physicalDevice.IsDeviceExtensionPresent("VK_KHR_uniform_buffer_standard_layout")
}; };
pExtendedFeatures = &featuresVk12; pExtendedFeatures = &featuresVk12;
PhysicalDeviceIndexTypeUint8FeaturesEXT featuresIndexU8; PhysicalDeviceIndexTypeUint8FeaturesEXT featuresIndexU8;
if (supportedExtensions.Contains("VK_EXT_index_type_uint8")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"))
{ {
featuresIndexU8 = new PhysicalDeviceIndexTypeUint8FeaturesEXT() featuresIndexU8 = new PhysicalDeviceIndexTypeUint8FeaturesEXT()
{ {
@@ -583,7 +462,7 @@ namespace Ryujinx.Graphics.Vulkan
PhysicalDeviceFragmentShaderInterlockFeaturesEXT featuresFragmentShaderInterlock; PhysicalDeviceFragmentShaderInterlockFeaturesEXT featuresFragmentShaderInterlock;
if (supportedExtensions.Contains("VK_EXT_fragment_shader_interlock")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock"))
{ {
featuresFragmentShaderInterlock = new PhysicalDeviceFragmentShaderInterlockFeaturesEXT() featuresFragmentShaderInterlock = new PhysicalDeviceFragmentShaderInterlockFeaturesEXT()
{ {
@@ -597,7 +476,7 @@ namespace Ryujinx.Graphics.Vulkan
PhysicalDeviceSubgroupSizeControlFeaturesEXT featuresSubgroupSizeControl; PhysicalDeviceSubgroupSizeControlFeaturesEXT featuresSubgroupSizeControl;
if (supportedExtensions.Contains("VK_EXT_subgroup_size_control")) if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control"))
{ {
featuresSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlFeaturesEXT() featuresSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlFeaturesEXT()
{ {
@@ -611,7 +490,7 @@ namespace Ryujinx.Graphics.Vulkan
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor; PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor;
if (supportedExtensions.Contains("VK_EXT_custom_border_color") && if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") &&
supportedFeaturesCustomBorderColor.CustomBorderColors && supportedFeaturesCustomBorderColor.CustomBorderColors &&
supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat) supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat)
{ {
@@ -626,7 +505,7 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresCustomBorderColor; pExtendedFeatures = &featuresCustomBorderColor;
} }
var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(supportedExtensions)).ToArray(); var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray();
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
@@ -646,7 +525,7 @@ namespace Ryujinx.Graphics.Vulkan
PEnabledFeatures = &features PEnabledFeatures = &features
}; };
api.CreateDevice(physicalDevice, in deviceCreateInfo, null, out var device).ThrowOnError(); api.CreateDevice(physicalDevice.PhysicalDevice, in deviceCreateInfo, null, out var device).ThrowOnError();
for (int i = 0; i < enabledExtensions.Length; i++) for (int i = 0; i < enabledExtensions.Length; i++)
{ {
@@ -655,77 +534,5 @@ namespace Ryujinx.Graphics.Vulkan
return device; return device;
} }
public static string[] GetSupportedExtensions(Vk api, PhysicalDevice physicalDevice)
{
uint propertiesCount;
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, null).ThrowOnError();
ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
fixed (ExtensionProperties* pExtensionProperties = extensionProperties)
{
api.EnumerateDeviceExtensionProperties(physicalDevice, (byte*)null, &propertiesCount, pExtensionProperties).ThrowOnError();
}
return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToArray();
}
internal unsafe static void CreateDebugMessenger(
Vk api,
GraphicsDebugLevel logLevel,
Instance instance,
out ExtDebugUtils debugUtils,
out DebugUtilsMessengerEXT debugUtilsMessenger)
{
debugUtils = default;
if (logLevel != GraphicsDebugLevel.None)
{
if (!api.TryGetInstanceExtension(instance, out debugUtils))
{
debugUtilsMessenger = default;
return;
}
var filterLogType = logLevel switch
{
GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt,
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.GeneralBitExt |
DebugUtilsMessageTypeFlagsEXT.ValidationBitExt |
DebugUtilsMessageTypeFlagsEXT.PerformanceBitExt,
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
};
var filterLogSeverity = logLevel switch
{
GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt |
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt,
GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.InfoBitExt |
DebugUtilsMessageSeverityFlagsEXT.WarningBitExt |
DebugUtilsMessageSeverityFlagsEXT.VerboseBitExt |
DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt,
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
};
var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT()
{
SType = StructureType.DebugUtilsMessengerCreateInfoExt,
MessageType = filterLogType,
MessageSeverity = filterLogSeverity,
PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(DebugMessenger)
};
debugUtils.CreateDebugUtilsMessenger(instance, in debugUtilsMessengerCreateInfo, null, out debugUtilsMessenger).ThrowOnError();
}
else
{
debugUtilsMessenger = default;
}
}
} }
} }

View File

@@ -0,0 +1,127 @@
using Ryujinx.Common.Utilities;
using Silk.NET.Core;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Vulkan
{
class VulkanInstance : IDisposable
{
private readonly Vk _api;
public readonly Instance Instance;
public readonly Version32 InstanceVersion;
private bool _disposed;
private VulkanInstance(Vk api, Instance instance)
{
_api = api;
Instance = instance;
if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == IntPtr.Zero)
{
InstanceVersion = Vk.Version10;
}
else
{
uint rawInstanceVersion = 0;
if (api.EnumerateInstanceVersion(ref rawInstanceVersion) != Result.Success)
{
rawInstanceVersion = Vk.Version11.Value;
}
InstanceVersion = (Version32)rawInstanceVersion;
}
}
public static Result Create(Vk api, ref InstanceCreateInfo createInfo, out VulkanInstance instance)
{
instance = null;
Instance rawInstance = default;
Result result = api.CreateInstance(SpanHelpers.AsReadOnlySpan(ref createInfo), ReadOnlySpan<AllocationCallbacks>.Empty, SpanHelpers.AsSpan(ref rawInstance));
if (result == Result.Success)
{
instance = new VulkanInstance(api, rawInstance);
}
return result;
}
public Result EnumeratePhysicalDevices(out VulkanPhysicalDevice[] physicalDevices)
{
physicalDevices = null;
uint physicalDeviceCount = 0;
Result result = _api.EnumeratePhysicalDevices(Instance, SpanHelpers.AsSpan(ref physicalDeviceCount), Span<PhysicalDevice>.Empty);
if (result != Result.Success)
{
return result;
}
PhysicalDevice[] rawPhysicalDevices = new PhysicalDevice[physicalDeviceCount];
result = _api.EnumeratePhysicalDevices(Instance, SpanHelpers.AsSpan(ref physicalDeviceCount), rawPhysicalDevices);
if (result != Result.Success)
{
return result;
}
physicalDevices = rawPhysicalDevices.Select(x => new VulkanPhysicalDevice(_api, x)).ToArray();
return Result.Success;
}
public static IReadOnlySet<string> GetInstanceExtensions(Vk api)
{
uint propertiesCount = 0;
api.EnumerateInstanceExtensionProperties(ReadOnlySpan<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), Span<ExtensionProperties>.Empty).ThrowOnError();
ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
api.EnumerateInstanceExtensionProperties(ReadOnlySpan<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), extensionProperties).ThrowOnError();
unsafe
{
return extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToImmutableHashSet();
}
}
public static IReadOnlySet<string> GetInstanceLayers(Vk api)
{
uint propertiesCount = 0;
api.EnumerateInstanceLayerProperties(SpanHelpers.AsSpan(ref propertiesCount), Span<LayerProperties>.Empty).ThrowOnError();
LayerProperties[] layerProperties = new LayerProperties[propertiesCount];
api.EnumerateInstanceLayerProperties(SpanHelpers.AsSpan(ref propertiesCount), layerProperties).ThrowOnError();
unsafe
{
return layerProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.LayerName)).ToImmutableHashSet();
}
}
public void Dispose()
{
if (!_disposed)
{
_api.DestroyInstance(Instance, ReadOnlySpan<AllocationCallbacks>.Empty);
_disposed = true;
}
}
}
}

View File

@@ -0,0 +1,70 @@
using Ryujinx.Common.Utilities;
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Vulkan
{
readonly struct VulkanPhysicalDevice
{
public readonly PhysicalDevice PhysicalDevice;
public readonly PhysicalDeviceFeatures PhysicalDeviceFeatures;
public readonly PhysicalDeviceProperties PhysicalDeviceProperties;
public readonly PhysicalDeviceMemoryProperties PhysicalDeviceMemoryProperties;
public readonly QueueFamilyProperties[] QueueFamilyProperties;
public readonly string DeviceName;
public readonly IReadOnlySet<string> DeviceExtensions;
public VulkanPhysicalDevice(Vk api, PhysicalDevice physicalDevice)
{
PhysicalDevice = physicalDevice;
PhysicalDeviceFeatures = api.GetPhysicalDeviceFeature(PhysicalDevice);
api.GetPhysicalDeviceProperties(PhysicalDevice, out var physicalDeviceProperties);
PhysicalDeviceProperties = physicalDeviceProperties;
api.GetPhysicalDeviceMemoryProperties(PhysicalDevice, out PhysicalDeviceMemoryProperties);
unsafe
{
DeviceName = Marshal.PtrToStringAnsi((IntPtr)physicalDeviceProperties.DeviceName);
}
uint propertiesCount = 0;
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, SpanHelpers.AsSpan(ref propertiesCount), Span<QueueFamilyProperties>.Empty);
QueueFamilyProperties = new QueueFamilyProperties[propertiesCount];
api.GetPhysicalDeviceQueueFamilyProperties(physicalDevice, SpanHelpers.AsSpan(ref propertiesCount), QueueFamilyProperties);
api.EnumerateDeviceExtensionProperties(PhysicalDevice, Span<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), Span<ExtensionProperties>.Empty).ThrowOnError();
ExtensionProperties[] extensionProperties = new ExtensionProperties[propertiesCount];
api.EnumerateDeviceExtensionProperties(PhysicalDevice, Span<byte>.Empty, SpanHelpers.AsSpan(ref propertiesCount), extensionProperties).ThrowOnError();
unsafe
{
DeviceExtensions = extensionProperties.Select(x => Marshal.PtrToStringAnsi((IntPtr)x.ExtensionName)).ToImmutableHashSet();
}
}
public string Id => $"0x{PhysicalDeviceProperties.VendorID:X}_0x{PhysicalDeviceProperties.DeviceID:X}";
public bool IsDeviceExtensionPresent(string extension) => DeviceExtensions.Contains(extension);
public DeviceInfo ToDeviceInfo()
{
return new DeviceInfo(
Id,
VendorUtils.GetNameFromId(PhysicalDeviceProperties.VendorID),
DeviceName,
PhysicalDeviceProperties.DeviceType == PhysicalDeviceType.DiscreteGpu);
}
}
}

View File

@@ -17,9 +17,9 @@ namespace Ryujinx.Graphics.Vulkan
{ {
public sealed class VulkanRenderer : IRenderer public sealed class VulkanRenderer : IRenderer
{ {
private Instance _instance; private VulkanInstance _instance;
private SurfaceKHR _surface; private SurfaceKHR _surface;
private PhysicalDevice _physicalDevice; private VulkanPhysicalDevice _physicalDevice;
private Device _device; private Device _device;
private WindowBase _window; private WindowBase _window;
@@ -36,7 +36,6 @@ namespace Ryujinx.Graphics.Vulkan
internal KhrPushDescriptor PushDescriptorApi { get; private set; } internal KhrPushDescriptor PushDescriptorApi { get; private set; }
internal ExtTransformFeedback TransformFeedbackApi { get; private set; } internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; } internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
internal ExtDebugUtils DebugUtilsApi { get; private set; }
internal uint QueueFamilyIndex { get; private set; } internal uint QueueFamilyIndex { get; private set; }
internal Queue Queue { get; private set; } internal Queue Queue { get; private set; }
@@ -57,11 +56,11 @@ namespace Ryujinx.Graphics.Vulkan
internal HashSet<ITexture> Textures { get; } internal HashSet<ITexture> Textures { get; }
internal HashSet<SamplerHolder> Samplers { get; } internal HashSet<SamplerHolder> Samplers { get; }
private VulkanDebugMessenger _debugMessenger;
private Counters _counters; private Counters _counters;
private SyncManager _syncManager; private SyncManager _syncManager;
private PipelineFull _pipeline; private PipelineFull _pipeline;
private DebugUtilsMessengerEXT _debugUtilsMessenger;
internal HelperShader HelperShader { get; private set; } internal HelperShader HelperShader { get; private set; }
internal PipelineFull PipelineInternal => _pipeline; internal PipelineFull PipelineInternal => _pipeline;
@@ -107,33 +106,31 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
private unsafe void LoadFeatures(string[] supportedExtensions, uint maxQueueCount, uint queueFamilyIndex) private unsafe void LoadFeatures(uint maxQueueCount, uint queueFamilyIndex)
{ {
FormatCapabilities = new FormatCapabilities(Api, _physicalDevice); FormatCapabilities = new FormatCapabilities(Api, _physicalDevice.PhysicalDevice);
var supportedFeatures = Api.GetPhysicalDeviceFeature(_physicalDevice); if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtConditionalRendering conditionalRenderingApi))
if (Api.TryGetDeviceExtension(_instance, _device, out ExtConditionalRendering conditionalRenderingApi))
{ {
ConditionalRenderingApi = conditionalRenderingApi; ConditionalRenderingApi = conditionalRenderingApi;
} }
if (Api.TryGetDeviceExtension(_instance, _device, out ExtExtendedDynamicState extendedDynamicStateApi)) if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExtendedDynamicState extendedDynamicStateApi))
{ {
ExtendedDynamicStateApi = extendedDynamicStateApi; ExtendedDynamicStateApi = extendedDynamicStateApi;
} }
if (Api.TryGetDeviceExtension(_instance, _device, out KhrPushDescriptor pushDescriptorApi)) if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrPushDescriptor pushDescriptorApi))
{ {
PushDescriptorApi = pushDescriptorApi; PushDescriptorApi = pushDescriptorApi;
} }
if (Api.TryGetDeviceExtension(_instance, _device, out ExtTransformFeedback transformFeedbackApi)) if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtTransformFeedback transformFeedbackApi))
{ {
TransformFeedbackApi = transformFeedbackApi; TransformFeedbackApi = transformFeedbackApi;
} }
if (Api.TryGetDeviceExtension(_instance, _device, out KhrDrawIndirectCount drawIndirectCountApi)) if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrDrawIndirectCount drawIndirectCountApi))
{ {
DrawIndirectCountApi = drawIndirectCountApi; DrawIndirectCountApi = drawIndirectCountApi;
} }
@@ -155,7 +152,7 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt SType = StructureType.PhysicalDeviceBlendOperationAdvancedPropertiesExt
}; };
bool supportsBlendOperationAdvanced = supportedExtensions.Contains("VK_EXT_blend_operation_advanced"); bool supportsBlendOperationAdvanced = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_blend_operation_advanced");
if (supportsBlendOperationAdvanced) if (supportsBlendOperationAdvanced)
{ {
@@ -168,14 +165,14 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt SType = StructureType.PhysicalDeviceSubgroupSizeControlPropertiesExt
}; };
bool supportsSubgroupSizeControl = supportedExtensions.Contains("VK_EXT_subgroup_size_control"); bool supportsSubgroupSizeControl = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control");
if (supportsSubgroupSizeControl) if (supportsSubgroupSizeControl)
{ {
properties2.PNext = &propertiesSubgroupSizeControl; properties2.PNext = &propertiesSubgroupSizeControl;
} }
bool supportsTransformFeedback = supportedExtensions.Contains(ExtTransformFeedback.ExtensionName); bool supportsTransformFeedback = _physicalDevice.IsDeviceExtensionPresent(ExtTransformFeedback.ExtensionName);
PhysicalDeviceTransformFeedbackPropertiesEXT propertiesTransformFeedback = new PhysicalDeviceTransformFeedbackPropertiesEXT() PhysicalDeviceTransformFeedbackPropertiesEXT propertiesTransformFeedback = new PhysicalDeviceTransformFeedbackPropertiesEXT()
{ {
@@ -223,30 +220,30 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr
}; };
if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart")) if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_primitive_topology_list_restart"))
{ {
features2.PNext = &featuresPrimitiveTopologyListRestart; features2.PNext = &featuresPrimitiveTopologyListRestart;
} }
if (supportedExtensions.Contains("VK_EXT_robustness2")) if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
{ {
featuresRobustness2.PNext = features2.PNext; featuresRobustness2.PNext = features2.PNext;
features2.PNext = &featuresRobustness2; features2.PNext = &featuresRobustness2;
} }
if (supportedExtensions.Contains("VK_KHR_shader_float16_int8")) if (_physicalDevice.IsDeviceExtensionPresent("VK_KHR_shader_float16_int8"))
{ {
featuresShaderInt8.PNext = features2.PNext; featuresShaderInt8.PNext = features2.PNext;
features2.PNext = &featuresShaderInt8; features2.PNext = &featuresShaderInt8;
} }
if (supportedExtensions.Contains("VK_EXT_custom_border_color")) if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color"))
{ {
featuresCustomBorderColor.PNext = features2.PNext; featuresCustomBorderColor.PNext = features2.PNext;
features2.PNext = &featuresCustomBorderColor; features2.PNext = &featuresCustomBorderColor;
} }
bool usePortability = supportedExtensions.Contains("VK_KHR_portability_subset"); bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset");
if (usePortability) if (usePortability)
{ {
@@ -257,8 +254,8 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &featuresPortabilitySubset; features2.PNext = &featuresPortabilitySubset;
} }
Api.GetPhysicalDeviceProperties2(_physicalDevice, &properties2); Api.GetPhysicalDeviceProperties2(_physicalDevice.PhysicalDevice, &properties2);
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2); Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2);
var portabilityFlags = PortabilitySubsetFlags.None; var portabilityFlags = PortabilitySubsetFlags.None;
uint vertexBufferAlignment = 1; uint vertexBufferAlignment = 1;
@@ -273,7 +270,7 @@ namespace Ryujinx.Graphics.Vulkan
portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias; portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias;
} }
bool supportsCustomBorderColor = supportedExtensions.Contains("VK_EXT_custom_border_color") && bool supportsCustomBorderColor = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") &&
featuresCustomBorderColor.CustomBorderColors && featuresCustomBorderColor.CustomBorderColors &&
featuresCustomBorderColor.CustomBorderColorWithoutFormat; featuresCustomBorderColor.CustomBorderColorWithoutFormat;
@@ -285,30 +282,30 @@ namespace Ryujinx.Graphics.Vulkan
properties.Limits.FramebufferStencilSampleCounts; properties.Limits.FramebufferStencilSampleCounts;
Capabilities = new HardwareCapabilities( Capabilities = new HardwareCapabilities(
supportedExtensions.Contains("VK_EXT_index_type_uint8"), _physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"),
supportsCustomBorderColor, supportsCustomBorderColor,
supportsBlendOperationAdvanced, supportsBlendOperationAdvanced,
propertiesBlendOperationAdvanced.AdvancedBlendCorrelatedOverlap, propertiesBlendOperationAdvanced.AdvancedBlendCorrelatedOverlap,
propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedSrcColor, propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedSrcColor,
propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedDstColor, propertiesBlendOperationAdvanced.AdvancedBlendNonPremultipliedDstColor,
supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(KhrDrawIndirectCount.ExtensionName),
supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"), _physicalDevice.IsDeviceExtensionPresent("VK_EXT_fragment_shader_interlock"),
supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"), _physicalDevice.IsDeviceExtensionPresent("VK_NV_geometry_shader_passthrough"),
supportsSubgroupSizeControl, supportsSubgroupSizeControl,
featuresShaderInt8.ShaderInt8, featuresShaderInt8.ShaderInt8,
supportedExtensions.Contains("VK_EXT_shader_stencil_export"), _physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"),
supportedExtensions.Contains(ExtConditionalRendering.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
features2.Features.MultiViewport, features2.Features.MultiViewport,
featuresRobustness2.NullDescriptor || IsMoltenVk, featuresRobustness2.NullDescriptor || IsMoltenVk,
supportedExtensions.Contains(KhrPushDescriptor.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName),
featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart, featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart, featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart,
supportsTransformFeedback, supportsTransformFeedback,
propertiesTransformFeedback.TransformFeedbackQueries, propertiesTransformFeedback.TransformFeedbackQueries,
features2.Features.OcclusionQueryPrecise, features2.Features.OcclusionQueryPrecise,
supportedFeatures.PipelineStatisticsQuery, _physicalDevice.PhysicalDeviceFeatures.PipelineStatisticsQuery,
supportedFeatures.GeometryShader, _physicalDevice.PhysicalDeviceFeatures.GeometryShader,
propertiesSubgroupSizeControl.MinSubgroupSize, propertiesSubgroupSizeControl.MinSubgroupSize,
propertiesSubgroupSizeControl.MaxSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize,
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
@@ -316,9 +313,9 @@ namespace Ryujinx.Graphics.Vulkan
portabilityFlags, portabilityFlags,
vertexBufferAlignment); vertexBufferAlignment);
IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(Api, _physicalDevice); IsSharedMemory = MemoryAllocator.IsDeviceMemoryShared(_physicalDevice);
MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount); MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device);
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex); CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
@@ -345,24 +342,22 @@ namespace Ryujinx.Graphics.Vulkan
Api = api; Api = api;
_instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions(), out ExtDebugUtils debugUtils, out _debugUtilsMessenger); _instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions());
_debugMessenger = new VulkanDebugMessenger(api, _instance.Instance, logLevel);
DebugUtilsApi = debugUtils; if (api.TryGetInstanceExtension(_instance.Instance, out KhrSurface surfaceApi))
if (api.TryGetInstanceExtension(_instance, out KhrSurface surfaceApi))
{ {
SurfaceApi = surfaceApi; SurfaceApi = surfaceApi;
} }
_surface = _getSurface(_instance, api); _surface = _getSurface(_instance.Instance, api);
_physicalDevice = VulkanInitialization.FindSuitablePhysicalDevice(api, _instance, _surface, _preferredGpuId); _physicalDevice = VulkanInitialization.FindSuitablePhysicalDevice(api, _instance, _surface, _preferredGpuId);
var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(api, _physicalDevice, _surface, out uint maxQueueCount); var queueFamilyIndex = VulkanInitialization.FindSuitableQueueFamily(api, _physicalDevice, _surface, out uint maxQueueCount);
var supportedExtensions = VulkanInitialization.GetSupportedExtensions(api, _physicalDevice);
_device = VulkanInitialization.CreateDevice(api, _physicalDevice, queueFamilyIndex, supportedExtensions, maxQueueCount); _device = VulkanInitialization.CreateDevice(api, _physicalDevice, queueFamilyIndex, maxQueueCount);
if (api.TryGetDeviceExtension(_instance, _device, out KhrSwapchain swapchainApi)) if (api.TryGetDeviceExtension(_instance.Instance, _device, out KhrSwapchain swapchainApi))
{ {
SwapchainApi = swapchainApi; SwapchainApi = swapchainApi;
} }
@@ -371,9 +366,9 @@ namespace Ryujinx.Graphics.Vulkan
Queue = queue; Queue = queue;
QueueLock = new object(); QueueLock = new object();
LoadFeatures(supportedExtensions, maxQueueCount, queueFamilyIndex); LoadFeatures(maxQueueCount, queueFamilyIndex);
_window = new Window(this, _surface, _physicalDevice, _device); _window = new Window(this, _surface, _physicalDevice.PhysicalDevice, _device);
_initialized = true; _initialized = true;
} }
@@ -538,10 +533,9 @@ namespace Ryujinx.Graphics.Vulkan
PNext = &featuresVk12 PNext = &featuresVk12
}; };
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2); Api.GetPhysicalDeviceFeatures2(_physicalDevice.PhysicalDevice, &features2);
Api.GetPhysicalDeviceProperties(_physicalDevice, out var properties);
var limits = properties.Limits; var limits = _physicalDevice.PhysicalDeviceProperties.Limits;
return new Capabilities( return new Capabilities(
api: TargetApi.Vulkan, api: TargetApi.Vulkan,
@@ -625,7 +619,7 @@ namespace Ryujinx.Graphics.Vulkan
private unsafe void PrintGpuInformation() private unsafe void PrintGpuInformation()
{ {
Api.GetPhysicalDeviceProperties(_physicalDevice, out var properties); var properties = _physicalDevice.PhysicalDeviceProperties;
string vendorName = VendorUtils.GetNameFromId(properties.VendorID); string vendorName = VendorUtils.GetNameFromId(properties.VendorID);
@@ -794,11 +788,6 @@ namespace Ryujinx.Graphics.Vulkan
MemoryAllocator.Dispose(); MemoryAllocator.Dispose();
if (_debugUtilsMessenger.Handle != 0)
{
DebugUtilsApi.DestroyDebugUtilsMessenger(_instance, _debugUtilsMessenger, null);
}
foreach (var shader in Shaders) foreach (var shader in Shaders)
{ {
shader.Dispose(); shader.Dispose();
@@ -814,12 +803,14 @@ namespace Ryujinx.Graphics.Vulkan
sampler.Dispose(); sampler.Dispose();
} }
SurfaceApi.DestroySurface(_instance, _surface, null); SurfaceApi.DestroySurface(_instance.Instance, _surface, null);
Api.DestroyDevice(_device, null); Api.DestroyDevice(_device, null);
_debugMessenger.Dispose();
// Last step destroy the instance // Last step destroy the instance
Api.DestroyInstance(_instance, null); _instance.Dispose();
} }
} }
} }

View File

@@ -1,11 +1,11 @@
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS.Services.Account.Acc.Types;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text.Json.Serialization;
namespace Ryujinx.HLE.HOS.Services.Account.Acc namespace Ryujinx.HLE.HOS.Services.Account.Acc
{ {
@@ -13,29 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
{ {
private readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json"); private readonly string _profilesJsonPath = Path.Join(AppDataManager.BaseDirPath, "system", "Profiles.json");
private struct ProfilesJson private static readonly ProfilesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions());
{
[JsonPropertyName("profiles")]
public List<UserProfileJson> Profiles { get; set; }
[JsonPropertyName("last_opened")]
public string LastOpened { get; set; }
}
private struct UserProfileJson
{
[JsonPropertyName("user_id")]
public string UserId { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("account_state")]
public AccountState AccountState { get; set; }
[JsonPropertyName("online_play_state")]
public AccountState OnlinePlayState { get; set; }
[JsonPropertyName("last_modified_timestamp")]
public long LastModifiedTimestamp { get; set; }
[JsonPropertyName("image")]
public byte[] Image { get; set; }
}
public UserId LastOpened { get; set; } public UserId LastOpened { get; set; }
@@ -47,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
{ {
try try
{ {
ProfilesJson profilesJson = JsonHelper.DeserializeFromFile<ProfilesJson>(_profilesJsonPath); ProfilesJson profilesJson = JsonHelper.DeserializeFromFile(_profilesJsonPath, SerializerContext.ProfilesJson);
foreach (var profile in profilesJson.Profiles) foreach (var profile in profilesJson.Profiles)
{ {
@@ -92,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
}); });
} }
File.WriteAllText(_profilesJsonPath, JsonHelper.Serialize(profilesJson, true)); JsonHelper.SerializeToFile(_profilesJsonPath, profilesJson, SerializerContext.ProfilesJson);
} }
} }
} }

View File

@@ -0,0 +1,11 @@
using Ryujinx.HLE.HOS.Services.Account.Acc.Types;
using System.Text.Json.Serialization;
namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(ProfilesJson))]
internal partial class ProfilesJsonSerializerContext : JsonSerializerContext
{
}
}

View File

@@ -1,5 +1,9 @@
using Ryujinx.Common.Utilities;
using System.Text.Json.Serialization;
namespace Ryujinx.HLE.HOS.Services.Account.Acc namespace Ryujinx.HLE.HOS.Services.Account.Acc
{ {
[JsonConverter(typeof(TypedStringEnumConverter<AccountState>))]
public enum AccountState public enum AccountState
{ {
Closed, Closed,

View File

@@ -0,0 +1,10 @@
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Account.Acc.Types
{
internal struct ProfilesJson
{
public List<UserProfileJson> Profiles { get; set; }
public string LastOpened { get; set; }
}
}

View File

@@ -0,0 +1,12 @@
namespace Ryujinx.HLE.HOS.Services.Account.Acc.Types
{
internal struct UserProfileJson
{
public string UserId { get; set; }
public string Name { get; set; }
public AccountState AccountState { get; set; }
public AccountState OnlinePlayState { get; set; }
public long LastModifiedTimestamp { get; set; }
public byte[] Image { get; set; }
}
}

View File

@@ -2,7 +2,7 @@
namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
{ {
struct AtomicStorage<T> where T: unmanaged struct AtomicStorage<T> where T: unmanaged, ISampledDataStruct
{ {
public ulong SamplingNumber; public ulong SamplingNumber;
public T Object; public T Object;
@@ -14,9 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
public void SetObject(ref T obj) public void SetObject(ref T obj)
{ {
ISampledData samplingProvider = obj as ISampledData; ulong samplingNumber = ISampledDataStruct.GetSamplingNumber(ref obj);
Interlocked.Exchange(ref SamplingNumber, samplingProvider.SamplingNumber); Interlocked.Exchange(ref SamplingNumber, samplingNumber);
Thread.MemoryBarrier(); Thread.MemoryBarrier();

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