Compare commits

...

9 Commits

Author SHA1 Message Date
9ba73ffbe5 Prefetch capabilities before spawning translation threads. (#3338)
* Prefetch capabilities before spawning translation threads.

The Backend Multithreading only expects one thread to submit commands at a time. When compiling shaders, the translator may request the host GPU capabilities from the backend. It's possible for a bunch of translators to do this at the same time.

There's a caching mechanism in place so that the capabilities are only fetched once. By triggering this before spawning the thread, the async translation threads no longer try to queue onto the backend queue all at the same time.

The Capabilities do need to be checked from the GPU thread, due to OpenGL needing a context to check them, so it's not possible to call the underlying backend directly.

* Initialize the capabilities when setting the GPU thread + missing call in headless

* Remove private variables
2022-05-14 11:58:33 -03:00
43b4b34376 Implement Viewport Transform Disable (#3328)
* Initial implementation (no specialization)

* Use specialization

* Fix render scale, increase code gen version

* Revert accidental change

* Address Feedback
2022-05-12 10:47:13 -03:00
92ca1cb0cb hid: Various fixes and cleanup (#3326)
* hid: Various fix and cleanup

* Add IsValidNpadIdType

* remove ()
2022-05-08 00:28:54 +02:00
50d7ecf76d Add alternative "GL" enum values for StencilOp (#3321)
This PR adds the alternative enum values for StencilOp. Similar to the other enums, I added these with the same names but with Gl added to the end. These are used by homebrew using Nouveau, though they might be used by games with the official Vulkan driver.

39d90be897/rnndb/graph/nv_3ddefs.xml (L77)

Fixes some broken graphics in Citra, such as missing shadows in Mario Kart 7. Likely fixes other homebrew.
2022-05-05 21:16:58 +02:00
42a2a80b87 Enable JIT service LLE (#2959)
* Enable JIT service LLE

* Force disable PPTC when using the JIT service

PPTC does not support multiple guest processes

* Fix build

* Make SM service registration per emulation context rather than global

* Address PR feedback
2022-05-05 15:23:30 -03:00
54deded929 Fix shared memory leak on Windows (#3319)
* Fix shared memory leak on Windows

* Fix memory leak caused by RO session disposal not decrementing the memory manager ref count

* Fix UnmapViewInternal deadlock

* Was not supposed to add those back
2022-05-05 14:58:59 -03:00
39bdf6d41e infra: Warn about support drop of old Windows versions (#3299)
* infra: Warn about support drop of old Windows versions

See #3298.

* Address comment
2022-05-04 20:21:27 +02:00
074190e03c Remove AddProtection count > 0 assert (#3315) 2022-05-04 14:07:10 -03:00
256514c7c9 Update the artifact build's version number (#3297) 2022-05-03 23:35:12 +02:00
59 changed files with 844 additions and 266 deletions

View File

@ -46,6 +46,7 @@ jobs:
env: env:
POWERSHELL_TELEMETRY_OPTOUT: 1 POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1 DOTNET_CLI_TELEMETRY_OPTOUT: 1
RYUJINX_BASE_VERSION: "1.1.0"
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- uses: actions/setup-dotnet@v1 - uses: actions/setup-dotnet@v1
@ -59,24 +60,24 @@ jobs:
- name: Clear - name: Clear
run: dotnet clean && dotnet nuget locals all --clear run: dotnet clean && dotnet nuget locals all --clear
- name: Build - name: Build
run: dotnet build -c "${{ matrix.configuration }}" /p:Version="1.1.0" /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER run: dotnet build -c "${{ matrix.configuration }}" /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER
- name: Test - name: Test
run: dotnet test -c "${{ matrix.configuration }}" run: dotnet test -c "${{ matrix.configuration }}"
- name: Publish Ryujinx - name: Publish Ryujinx
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="1.1.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx --self-contained
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request'
- name: Publish Ryujinx.Headless.SDL2 - name: Publish Ryujinx.Headless.SDL2
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="1.1.0" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless /p:Version="${{ env.RYUJINX_BASE_VERSION }}" /p:DebugType=embedded /p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" /p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Headless.SDL2 --self-contained
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request'
- name: Upload Ryujinx artifact - name: Upload Ryujinx artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: ryujinx-${{ matrix.configuration }}-1.0.0+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
path: publish path: publish
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request'
- name: Upload Ryujinx.Headless.SDL2 artifact - name: Upload Ryujinx.Headless.SDL2 artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
with: with:
name: ryujinx-headless-sdl2-${{ matrix.configuration }}-1.0.0+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} name: ryujinx-headless-sdl2-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }}
path: publish_sdl2_headless path: publish_sdl2_headless
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request'

View File

@ -94,7 +94,7 @@ namespace Ryujinx.Graphics.GAL
void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs); void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs);
void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers); void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers);
void SetViewports(int first, ReadOnlySpan<Viewport> viewports); void SetViewports(int first, ReadOnlySpan<Viewport> viewports, bool disableTransform);
void TextureBarrier(); void TextureBarrier();
void TextureBarrierTiled(); void TextureBarrierTiled();

View File

@ -9,17 +9,19 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
public CommandType CommandType => CommandType.SetViewports; public CommandType CommandType => CommandType.SetViewports;
private int _first; private int _first;
private SpanRef<Viewport> _viewports; private SpanRef<Viewport> _viewports;
private bool _disableTransform;
public void Set(int first, SpanRef<Viewport> viewports) public void Set(int first, SpanRef<Viewport> viewports, bool disableTransform)
{ {
_first = first; _first = first;
_viewports = viewports; _viewports = viewports;
_disableTransform = disableTransform;
} }
public static void Run(ref SetViewportsCommand command, ThreadedRenderer threaded, IRenderer renderer) public static void Run(ref SetViewportsCommand command, ThreadedRenderer threaded, IRenderer renderer)
{ {
ReadOnlySpan<Viewport> viewports = command._viewports.Get(threaded); ReadOnlySpan<Viewport> viewports = command._viewports.Get(threaded);
renderer.Pipeline.SetViewports(command._first, viewports); renderer.Pipeline.SetViewports(command._first, viewports, command._disableTransform);
command._viewports.Dispose(threaded); command._viewports.Dispose(threaded);
} }
} }

View File

@ -304,9 +304,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public void SetViewports(int first, ReadOnlySpan<Viewport> viewports) public void SetViewports(int first, ReadOnlySpan<Viewport> viewports, bool disableTransform)
{ {
_renderer.New<SetViewportsCommand>().Set(first, _renderer.CopySpan(viewports)); _renderer.New<SetViewportsCommand>().Set(first, _renderer.CopySpan(viewports), disableTransform);
_renderer.QueueCommand(); _renderer.QueueCommand();
} }

View File

@ -9,6 +9,15 @@ namespace Ryujinx.Graphics.GAL
DecrementAndClamp, DecrementAndClamp,
Invert, Invert,
IncrementAndWrap, IncrementAndWrap,
DecrementAndWrap DecrementAndWrap,
ZeroGl = 0x0,
InvertGl = 0x150a,
KeepGl = 0x1e00,
ReplaceGl = 0x1e01,
IncrementAndClampGl = 0x1e02,
DecrementAndClampGl = 0x1e03,
IncrementAndWrapGl = 0x8507,
DecrementAndWrapGl = 0x8508
} }
} }

View File

@ -72,6 +72,13 @@ namespace Ryujinx.Graphics.GAL
UpdateGenericField(SupportBuffer.FragmentIsBgraOffset, data, Data.FragmentIsBgra.ToSpan(), offset, count); UpdateGenericField(SupportBuffer.FragmentIsBgraOffset, data, Data.FragmentIsBgra.ToSpan(), offset, count);
} }
public void UpdateViewportInverse(Vector4<float> data)
{
Data.ViewportInverse = data;
MarkDirty(SupportBuffer.ViewportInverseOffset, SupportBuffer.FieldSize);
}
public void Commit() public void Commit()
{ {
if (_startOffset != -1) if (_startOffset != -1)

View File

@ -113,7 +113,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
nameof(ThreedClassState.DepthMode), nameof(ThreedClassState.DepthMode),
nameof(ThreedClassState.ViewportTransform), nameof(ThreedClassState.ViewportTransform),
nameof(ThreedClassState.ViewportExtents), nameof(ThreedClassState.ViewportExtents),
nameof(ThreedClassState.YControl)), nameof(ThreedClassState.YControl),
nameof(ThreedClassState.ViewportTransformEnable)),
new StateUpdateCallbackEntry(UpdatePolygonMode, new StateUpdateCallbackEntry(UpdatePolygonMode,
nameof(ThreedClassState.PolygonModeFront), nameof(ThreedClassState.PolygonModeFront),
@ -200,7 +201,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
// of the shader for the new state. // of the shader for the new state.
if (_shaderSpecState != null) if (_shaderSpecState != null)
{ {
if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState())) if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState()))
{ {
ForceShaderUpdate(); ForceShaderUpdate();
} }
@ -568,6 +569,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
var yControl = _state.State.YControl; var yControl = _state.State.YControl;
var face = _state.State.FaceState; var face = _state.State.FaceState;
bool disableTransform = _state.State.ViewportTransformEnable == 0;
UpdateFrontFace(yControl, face.FrontFace); UpdateFrontFace(yControl, face.FrontFace);
UpdateDepthMode(); UpdateDepthMode();
@ -577,6 +580,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
for (int index = 0; index < Constants.TotalViewports; index++) for (int index = 0; index < Constants.TotalViewports; index++)
{ {
if (disableTransform)
{
ref var scissor = ref _state.State.ScreenScissorState;
float rScale = _channel.TextureManager.RenderTargetScale;
var scissorRect = new RectangleF(0, 0, (scissor.X + scissor.Width) * rScale, (scissor.Y + scissor.Height) * rScale);
viewports[index] = new Viewport(scissorRect, ViewportSwizzle.PositiveX, ViewportSwizzle.PositiveY, ViewportSwizzle.PositiveZ, ViewportSwizzle.PositiveW, 0, 1);
continue;
}
ref var transform = ref _state.State.ViewportTransform[index]; ref var transform = ref _state.State.ViewportTransform[index];
ref var extents = ref _state.State.ViewportExtents[index]; ref var extents = ref _state.State.ViewportExtents[index];
@ -628,7 +642,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
viewports[index] = new Viewport(region, swizzleX, swizzleY, swizzleZ, swizzleW, depthNear, depthFar); viewports[index] = new Viewport(region, swizzleX, swizzleY, swizzleZ, swizzleW, depthNear, depthFar);
} }
_context.Renderer.Pipeline.SetViewports(0, viewports); _context.Renderer.Pipeline.SetViewports(0, viewports, disableTransform);
} }
/// <summary> /// <summary>
@ -1194,7 +1208,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
return new GpuChannelGraphicsState( return new GpuChannelGraphicsState(
_state.State.EarlyZForce, _state.State.EarlyZForce,
_drawState.Topology, _drawState.Topology,
_state.State.TessMode); _state.State.TessMode,
_state.State.ViewportTransformEnable == 0);
} }
/// <summary> /// <summary>

View File

@ -82,27 +82,13 @@ namespace Ryujinx.Graphics.Gpu
/// <summary> /// <summary>
/// Host hardware capabilities. /// Host hardware capabilities.
/// </summary> /// </summary>
internal ref Capabilities Capabilities internal Capabilities Capabilities { get; private set; }
{
get
{
if (!_capsLoaded)
{
_caps = Renderer.GetCapabilities();
_capsLoaded = true;
}
return ref _caps;
}
}
/// <summary> /// <summary>
/// Event for signalling shader cache loading progress. /// Event for signalling shader cache loading progress.
/// </summary> /// </summary>
public event Action<ShaderCacheState, int, int> ShaderCacheStateChanged; public event Action<ShaderCacheState, int, int> ShaderCacheStateChanged;
private bool _capsLoaded;
private Capabilities _caps;
private Thread _gpuThread; private Thread _gpuThread;
/// <summary> /// <summary>
@ -254,6 +240,8 @@ namespace Ryujinx.Graphics.Gpu
public void SetGpuThread() public void SetGpuThread()
{ {
_gpuThread = Thread.CurrentThread; _gpuThread = Thread.CurrentThread;
Capabilities = Renderer.GetCapabilities();
} }
/// <summary> /// <summary>

View File

@ -166,7 +166,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
GpuChannelGraphicsState graphicsState = new GpuChannelGraphicsState( GpuChannelGraphicsState graphicsState = new GpuChannelGraphicsState(
accessorHeader.StateFlags.HasFlag(GuestGpuStateFlags.EarlyZForce), accessorHeader.StateFlags.HasFlag(GuestGpuStateFlags.EarlyZForce),
topology, topology,
tessMode); tessMode,
false);
TransformFeedbackDescriptor[] tfdNew = null; TransformFeedbackDescriptor[] tfdNew = null;

View File

@ -185,6 +185,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return _oldSpecState.GraphicsState.EarlyZForce; return _oldSpecState.GraphicsState.EarlyZForce;
} }
/// <inheritdoc/>
public bool QueryViewportTransformDisable()
{
return _oldSpecState.GraphicsState.ViewportTransformDisable;
}
/// <inheritdoc/> /// <inheritdoc/>
public void RegisterTexture(int handle, int cbufSlot) public void RegisterTexture(int handle, int cbufSlot)
{ {

View File

@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 1; private const ushort FileFormatVersionMinor = 1;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 0; private const uint CodeGenVersion = 1;
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

@ -217,6 +217,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
return _state.GraphicsState.EarlyZForce; return _state.GraphicsState.EarlyZForce;
} }
/// <inheritdoc/>
public bool QueryViewportTransformDisable()
{
return _state.GraphicsState.ViewportTransformDisable;
}
/// <inheritdoc/> /// <inheritdoc/>
public void RegisterTexture(int handle, int cbufSlot) public void RegisterTexture(int handle, int cbufSlot)
{ {

View File

@ -25,17 +25,24 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary> /// </summary>
public readonly TessMode TessellationMode; public readonly TessMode TessellationMode;
/// <summary>
/// Indicates whenever the viewport transform is disabled.
/// </summary>
public readonly bool ViewportTransformDisable;
/// <summary> /// <summary>
/// Creates a new GPU graphics state. /// Creates a new GPU graphics state.
/// </summary> /// </summary>
/// <param name="earlyZForce">Early Z force enable</param> /// <param name="earlyZForce">Early Z force enable</param>
/// <param name="topology">Primitive topology</param> /// <param name="topology">Primitive topology</param>
/// <param name="tessellationMode">Tessellation mode</param> /// <param name="tessellationMode">Tessellation mode</param>
public GpuChannelGraphicsState(bool earlyZForce, PrimitiveTopology topology, TessMode tessellationMode) /// <param name="viewportTransformDisable">Indicates whenever the viewport transform is disabled</param>
public GpuChannelGraphicsState(bool earlyZForce, PrimitiveTopology topology, TessMode tessellationMode, bool viewportTransformDisable)
{ {
EarlyZForce = earlyZForce; EarlyZForce = earlyZForce;
Topology = topology; Topology = topology;
TessellationMode = tessellationMode; TessellationMode = tessellationMode;
ViewportTransformDisable = viewportTransformDisable;
} }
} }
} }

View File

@ -249,12 +249,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
GpuChannelGraphicsState graphicsState, GpuChannelGraphicsState graphicsState,
ShaderAddresses addresses) ShaderAddresses addresses)
{ {
if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, poolState, gpShaders, addresses)) if (_gpPrograms.TryGetValue(addresses, out var gpShaders) && IsShaderEqual(channel, poolState, graphicsState, gpShaders, addresses))
{ {
return gpShaders; return gpShaders;
} }
if (_graphicsShaderCache.TryFind(channel, poolState, addresses, out gpShaders, out var cachedGuestCode)) if (_graphicsShaderCache.TryFind(channel, poolState, graphicsState, addresses, out gpShaders, out var cachedGuestCode))
{ {
_gpPrograms[addresses] = gpShaders; _gpPrograms[addresses] = gpShaders;
return gpShaders; return gpShaders;
@ -429,12 +429,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary> /// </summary>
/// <param name="channel">GPU channel using the shader</param> /// <param name="channel">GPU channel using the shader</param>
/// <param name="poolState">GPU channel state to verify shader compatibility</param> /// <param name="poolState">GPU channel state to verify shader compatibility</param>
/// <param name="graphicsState">GPU channel graphics state to verify shader compatibility</param>
/// <param name="gpShaders">Cached graphics shaders</param> /// <param name="gpShaders">Cached graphics shaders</param>
/// <param name="addresses">GPU virtual addresses of all enabled shader stages</param> /// <param name="addresses">GPU virtual addresses of all enabled shader stages</param>
/// <returns>True if the code is different, false otherwise</returns> /// <returns>True if the code is different, false otherwise</returns>
private static bool IsShaderEqual( private static bool IsShaderEqual(
GpuChannel channel, GpuChannel channel,
GpuChannelPoolState poolState, GpuChannelPoolState poolState,
GpuChannelGraphicsState graphicsState,
CachedShaderProgram gpShaders, CachedShaderProgram gpShaders,
ShaderAddresses addresses) ShaderAddresses addresses)
{ {
@ -452,7 +454,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
} }
} }
return gpShaders.SpecializationState.MatchesGraphics(channel, poolState); return gpShaders.SpecializationState.MatchesGraphics(channel, poolState, graphicsState);
} }
/// <summary> /// <summary>

View File

@ -208,6 +208,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </remarks> /// </remarks>
/// <param name="channel">GPU channel</param> /// <param name="channel">GPU channel</param>
/// <param name="poolState">Texture pool state</param> /// <param name="poolState">Texture pool state</param>
/// <param name="graphicsState">Graphics state</param>
/// <param name="addresses">Guest addresses of the shaders to find</param> /// <param name="addresses">Guest addresses of the shaders to find</param>
/// <param name="program">Cached host program for the given state, if found</param> /// <param name="program">Cached host program for the given state, if found</param>
/// <param name="guestCode">Cached guest code, if any found</param> /// <param name="guestCode">Cached guest code, if any found</param>
@ -215,6 +216,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
public bool TryFind( public bool TryFind(
GpuChannel channel, GpuChannel channel,
GpuChannelPoolState poolState, GpuChannelPoolState poolState,
GpuChannelGraphicsState graphicsState,
ShaderAddresses addresses, ShaderAddresses addresses,
out CachedShaderProgram program, out CachedShaderProgram program,
out CachedGraphicsGuestCode guestCode) out CachedGraphicsGuestCode guestCode)
@ -234,7 +236,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
if (found && _shaderPrograms.TryGetValue(idTable, out ShaderSpecializationList specList)) if (found && _shaderPrograms.TryGetValue(idTable, out ShaderSpecializationList specList))
{ {
return specList.TryFindForGraphics(channel, poolState, out program); return specList.TryFindForGraphics(channel, poolState, graphicsState, out program);
} }
return false; return false;

View File

@ -24,13 +24,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary> /// </summary>
/// <param name="channel">GPU channel</param> /// <param name="channel">GPU channel</param>
/// <param name="poolState">Texture pool state</param> /// <param name="poolState">Texture pool state</param>
/// <param name="graphicsState">Graphics state</param>
/// <param name="program">Cached program, if found</param> /// <param name="program">Cached program, if found</param>
/// <returns>True if a compatible program is found, false otherwise</returns> /// <returns>True if a compatible program is found, false otherwise</returns>
public bool TryFindForGraphics(GpuChannel channel, GpuChannelPoolState poolState, out CachedShaderProgram program) public bool TryFindForGraphics(
GpuChannel channel,
GpuChannelPoolState poolState,
GpuChannelGraphicsState graphicsState,
out CachedShaderProgram program)
{ {
foreach (var entry in _entries) foreach (var entry in _entries)
{ {
if (entry.SpecializationState.MatchesGraphics(channel, poolState)) if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState))
{ {
program = entry; program = entry;
return true; return true;

View File

@ -395,9 +395,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary> /// </summary>
/// <param name="channel">GPU channel</param> /// <param name="channel">GPU channel</param>
/// <param name="poolState">Texture pool state</param> /// <param name="poolState">Texture pool state</param>
/// <param name="graphicsState">Graphics state</param>
/// <returns>True if the state matches, false otherwise</returns> /// <returns>True if the state matches, false otherwise</returns>
public bool MatchesGraphics(GpuChannel channel, GpuChannelPoolState poolState) public bool MatchesGraphics(GpuChannel channel, GpuChannelPoolState poolState, GpuChannelGraphicsState graphicsState)
{ {
if (graphicsState.ViewportTransformDisable != GraphicsState.ViewportTransformDisable)
{
return false;
}
return Matches(channel, poolState, isCompute: false); return Matches(channel, poolState, isCompute: false);
} }

View File

@ -379,20 +379,28 @@ namespace Ryujinx.Graphics.OpenGL
switch (op) switch (op)
{ {
case GAL.StencilOp.Keep: case GAL.StencilOp.Keep:
case GAL.StencilOp.KeepGl:
return OpenTK.Graphics.OpenGL.StencilOp.Keep; return OpenTK.Graphics.OpenGL.StencilOp.Keep;
case GAL.StencilOp.Zero: case GAL.StencilOp.Zero:
case GAL.StencilOp.ZeroGl:
return OpenTK.Graphics.OpenGL.StencilOp.Zero; return OpenTK.Graphics.OpenGL.StencilOp.Zero;
case GAL.StencilOp.Replace: case GAL.StencilOp.Replace:
case GAL.StencilOp.ReplaceGl:
return OpenTK.Graphics.OpenGL.StencilOp.Replace; return OpenTK.Graphics.OpenGL.StencilOp.Replace;
case GAL.StencilOp.IncrementAndClamp: case GAL.StencilOp.IncrementAndClamp:
case GAL.StencilOp.IncrementAndClampGl:
return OpenTK.Graphics.OpenGL.StencilOp.Incr; return OpenTK.Graphics.OpenGL.StencilOp.Incr;
case GAL.StencilOp.DecrementAndClamp: case GAL.StencilOp.DecrementAndClamp:
case GAL.StencilOp.DecrementAndClampGl:
return OpenTK.Graphics.OpenGL.StencilOp.Decr; return OpenTK.Graphics.OpenGL.StencilOp.Decr;
case GAL.StencilOp.Invert: case GAL.StencilOp.Invert:
case GAL.StencilOp.InvertGl:
return OpenTK.Graphics.OpenGL.StencilOp.Invert; return OpenTK.Graphics.OpenGL.StencilOp.Invert;
case GAL.StencilOp.IncrementAndWrap: case GAL.StencilOp.IncrementAndWrap:
case GAL.StencilOp.IncrementAndWrapGl:
return OpenTK.Graphics.OpenGL.StencilOp.IncrWrap; return OpenTK.Graphics.OpenGL.StencilOp.IncrWrap;
case GAL.StencilOp.DecrementAndWrap: case GAL.StencilOp.DecrementAndWrap:
case GAL.StencilOp.DecrementAndWrapGl:
return OpenTK.Graphics.OpenGL.StencilOp.DecrWrap; return OpenTK.Graphics.OpenGL.StencilOp.DecrWrap;
} }

View File

@ -1266,7 +1266,7 @@ namespace Ryujinx.Graphics.OpenGL
_vertexArray.SetVertexBuffers(vertexBuffers); _vertexArray.SetVertexBuffers(vertexBuffers);
} }
public void SetViewports(int first, ReadOnlySpan<Viewport> viewports) public void SetViewports(int first, ReadOnlySpan<Viewport> viewports, bool disableTransform)
{ {
Array.Resize(ref _viewportArray, viewports.Length * 4); Array.Resize(ref _viewportArray, viewports.Length * 4);
Array.Resize(ref _depthRangeArray, viewports.Length * 2); Array.Resize(ref _depthRangeArray, viewports.Length * 2);
@ -1305,6 +1305,19 @@ namespace Ryujinx.Graphics.OpenGL
GL.ViewportArray(first, viewports.Length, viewportArray); GL.ViewportArray(first, viewports.Length, viewportArray);
GL.DepthRangeArray(first, viewports.Length, depthRangeArray); GL.DepthRangeArray(first, viewports.Length, depthRangeArray);
float disableTransformF = disableTransform ? 1.0f : 0.0f;
if (_supportBuffer.Data.ViewportInverse.W != disableTransformF || disableTransform)
{
float scale = _renderScale[0].X;
_supportBuffer.UpdateViewportInverse(new Vector4<float>
{
X = scale * 2f / viewports[first].Region.Width,
Y = scale * 2f / viewports[first].Region.Height,
Z = 1,
W = disableTransformF
});
}
} }
public void TextureBarrier() public void TextureBarrier()

View File

@ -249,7 +249,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine(); context.AppendLine();
} }
} }
else if (isFragment) else if (isFragment || context.Config.Stage == ShaderStage.Vertex)
{ {
DeclareSupportUniformBlock(context, context.Config.Stage, 0); DeclareSupportUniformBlock(context, context.Config.Stage, 0);
} }
@ -615,8 +615,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements) private static void DeclareSupportUniformBlock(CodeGenContext context, ShaderStage stage, int scaleElements)
{ {
bool isFragment = stage == ShaderStage.Fragment; bool needsSupportBlock = stage == ShaderStage.Fragment ||
if (!isFragment && scaleElements == 0) (context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable());
if (!needsSupportBlock && scaleElements == 0)
{ {
return; return;
} }
@ -630,6 +632,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
case ShaderStage.Vertex: case ShaderStage.Vertex:
context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};"); context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};");
context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];"); context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];");
context.AppendLine($"vec4 {DefaultNames.SupportBlockViewportInverse};");
context.AppendLine($"int {DefaultNames.SupportBlockFragmentScaleCount};"); context.AppendLine($"int {DefaultNames.SupportBlockFragmentScaleCount};");
break; break;
case ShaderStage.Compute: case ShaderStage.Compute:

View File

@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
public const string SupportBlockName = "support_block"; public const string SupportBlockName = "support_block";
public const string SupportBlockAlphaTestName = "s_alpha_test"; public const string SupportBlockAlphaTestName = "s_alpha_test";
public const string SupportBlockIsBgraName = "s_is_bgra"; public const string SupportBlockIsBgraName = "s_is_bgra";
public const string SupportBlockViewportInverse = "s_viewport_inverse";
public const string SupportBlockFragmentScaleCount = "s_frag_scale_count"; public const string SupportBlockFragmentScaleCount = "s_frag_scale_count";
public const string SupportBlockRenderScaleName = "s_render_scale"; public const string SupportBlockRenderScaleName = "s_render_scale";

View File

@ -84,7 +84,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", VariableType.Bool) }, { AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", VariableType.Bool) }, { AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", VariableType.Bool) }, { AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[6]", VariableType.Bool) },
{ AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", VariableType.Bool) } { AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[7]", VariableType.Bool) },
{ AttributeConsts.SupportBlockViewInverseX, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.x", VariableType.F32) },
{ AttributeConsts.SupportBlockViewInverseY, new BuiltInAttribute($"{DefaultNames.SupportBlockViewportInverse}.y", VariableType.F32) }
}; };
private Dictionary<AstOperand, string> _locals; private Dictionary<AstOperand, string> _locals;

View File

@ -329,6 +329,15 @@ namespace Ryujinx.Graphics.Shader
return false; return false;
} }
/// <summary>
/// Queries if host state disables the viewport transform.
/// </summary>
/// <returns>True if the viewport transform is disabled</returns>
bool QueryViewportTransformDisable()
{
return false;
}
/// <summary> /// <summary>
/// Registers a texture used by the shader. /// Registers a texture used by the shader.
/// </summary> /// </summary>

View File

@ -206,7 +206,33 @@ namespace Ryujinx.Graphics.Shader.Instructions
if (emit) if (emit)
{ {
context.EmitVertex(); if (context.Config.LastInVertexPipeline)
{
context.PrepareForVertexReturn(out var tempXLocal, out var tempYLocal, out var tempZLocal);
context.EmitVertex();
// Restore output position value before transformation.
if (tempXLocal != null)
{
context.Copy(Attribute(AttributeConsts.PositionX), tempXLocal);
}
if (tempYLocal != null)
{
context.Copy(Attribute(AttributeConsts.PositionY), tempYLocal);
}
if (tempZLocal != null)
{
context.Copy(Attribute(AttributeConsts.PositionZ), tempZLocal);
}
}
else
{
context.EmitVertex();
}
} }
if (cut) if (cut)

View File

@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Shader
public static int FragmentAlphaTestOffset; public static int FragmentAlphaTestOffset;
public static int FragmentIsBgraOffset; public static int FragmentIsBgraOffset;
public static int ViewportInverseOffset;
public static int FragmentRenderScaleCountOffset; public static int FragmentRenderScaleCountOffset;
public static int GraphicsRenderScaleOffset; public static int GraphicsRenderScaleOffset;
public static int ComputeRenderScaleOffset; public static int ComputeRenderScaleOffset;
@ -40,6 +41,7 @@ namespace Ryujinx.Graphics.Shader
FragmentAlphaTestOffset = OffsetOf(ref instance, ref instance.FragmentAlphaTest); FragmentAlphaTestOffset = OffsetOf(ref instance, ref instance.FragmentAlphaTest);
FragmentIsBgraOffset = OffsetOf(ref instance, ref instance.FragmentIsBgra); FragmentIsBgraOffset = OffsetOf(ref instance, ref instance.FragmentIsBgra);
ViewportInverseOffset = OffsetOf(ref instance, ref instance.ViewportInverse);
FragmentRenderScaleCountOffset = OffsetOf(ref instance, ref instance.FragmentRenderScaleCount); FragmentRenderScaleCountOffset = OffsetOf(ref instance, ref instance.FragmentRenderScaleCount);
GraphicsRenderScaleOffset = OffsetOf(ref instance, ref instance.RenderScale); GraphicsRenderScaleOffset = OffsetOf(ref instance, ref instance.RenderScale);
ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize; ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize;
@ -47,6 +49,7 @@ namespace Ryujinx.Graphics.Shader
public Vector4<int> FragmentAlphaTest; public Vector4<int> FragmentAlphaTest;
public Array8<Vector4<int>> FragmentIsBgra; public Array8<Vector4<int>> FragmentIsBgra;
public Vector4<float> ViewportInverse;
public Vector4<int> FragmentRenderScaleCount; public Vector4<int> FragmentRenderScaleCount;
// Render scale max count: 1 + 32 + 8. First scale is fragment output scale, others are textures/image inputs. // Render scale max count: 1 + 32 + 8. First scale is fragment output scale, others are textures/image inputs.

View File

@ -67,6 +67,9 @@ namespace Ryujinx.Graphics.Shader.Translation
public const int FragmentOutputIsBgraBase = 0x1000100; public const int FragmentOutputIsBgraBase = 0x1000100;
public const int FragmentOutputIsBgraEnd = FragmentOutputIsBgraBase + 8 * 4; public const int FragmentOutputIsBgraEnd = FragmentOutputIsBgraBase + 8 * 4;
public const int SupportBlockViewInverseX = 0x1000200;
public const int SupportBlockViewInverseY = 0x1000204;
public const int ThreadIdX = 0x2000000; public const int ThreadIdX = 0x2000000;
public const int ThreadIdY = 0x2000004; public const int ThreadIdY = 0x2000004;
public const int ThreadIdZ = 0x2000008; public const int ThreadIdZ = 0x2000008;

View File

@ -154,9 +154,56 @@ namespace Ryujinx.Graphics.Shader.Translation
return label; return label;
} }
public void PrepareForVertexReturn()
{
if (Config.GpuAccessor.QueryViewportTransformDisable())
{
Operand x = Attribute(AttributeConsts.PositionX | AttributeConsts.LoadOutputMask);
Operand y = Attribute(AttributeConsts.PositionY | AttributeConsts.LoadOutputMask);
Operand xScale = Attribute(AttributeConsts.SupportBlockViewInverseX);
Operand yScale = Attribute(AttributeConsts.SupportBlockViewInverseY);
Operand negativeOne = ConstF(-1.0f);
this.Copy(Attribute(AttributeConsts.PositionX), this.FPFusedMultiplyAdd(x, xScale, negativeOne));
this.Copy(Attribute(AttributeConsts.PositionY), this.FPFusedMultiplyAdd(y, yScale, negativeOne));
}
}
public void PrepareForVertexReturn(out Operand oldXLocal, out Operand oldYLocal, out Operand oldZLocal)
{
if (Config.GpuAccessor.QueryViewportTransformDisable())
{
oldXLocal = Local();
this.Copy(oldXLocal, Attribute(AttributeConsts.PositionX | AttributeConsts.LoadOutputMask));
oldYLocal = Local();
this.Copy(oldYLocal, Attribute(AttributeConsts.PositionY | AttributeConsts.LoadOutputMask));
}
else
{
oldXLocal = null;
oldYLocal = null;
}
// Will be used by Vulkan backend for depth mode emulation.
oldZLocal = null;
PrepareForVertexReturn();
}
public void PrepareForReturn() public void PrepareForReturn()
{ {
if (!IsNonMain && Config.Stage == ShaderStage.Fragment) if (IsNonMain)
{
return;
}
if (Config.LastInVertexPipeline &&
(Config.Stage == ShaderStage.Vertex || Config.Stage == ShaderStage.TessellationEvaluation) &&
(Config.Options.Flags & TranslationFlags.VertexA) == 0)
{
PrepareForVertexReturn();
}
else if (Config.Stage == ShaderStage.Fragment)
{ {
if (Config.OmapDepth) if (Config.OmapDepth)
{ {

View File

@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.Shader.Translation
public ShaderStage Stage { get; } public ShaderStage Stage { get; }
public bool GpPassthrough { get; } public bool GpPassthrough { get; }
public bool LastInVertexPipeline { get; private set; }
public int ThreadsPerInputPrimitive { get; } public int ThreadsPerInputPrimitive { get; }
@ -135,6 +136,7 @@ namespace Ryujinx.Graphics.Shader.Translation
OmapSampleMask = header.OmapSampleMask; OmapSampleMask = header.OmapSampleMask;
OmapDepth = header.OmapDepth; OmapDepth = header.OmapDepth;
TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled(); TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled();
LastInVertexPipeline = header.Stage < ShaderStage.Fragment;
} }
public int GetDepthRegister() public int GetDepthRegister()
@ -274,6 +276,11 @@ namespace Ryujinx.Graphics.Shader.Translation
NextInputAttributesPerPatchComponents = config.ThisInputAttributesPerPatchComponents; NextInputAttributesPerPatchComponents = config.ThisInputAttributesPerPatchComponents;
NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr); NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr);
MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch); MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch);
if (config.Stage != ShaderStage.Fragment)
{
LastInVertexPipeline = false;
}
} }
public void MergeOutputUserAttributes(int mask, int maskPerPatch) public void MergeOutputUserAttributes(int mask, int maskPerPatch)

View File

@ -24,6 +24,7 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
using static LibHac.Fs.ApplicationSaveDataManagement; using static LibHac.Fs.ApplicationSaveDataManagement;
using static Ryujinx.HLE.HOS.ModLoader; using static Ryujinx.HLE.HOS.ModLoader;
@ -308,6 +309,94 @@ namespace Ryujinx.HLE.HOS
LoadNca(nca, null, null); LoadNca(nca, null, null);
} }
public void LoadServiceNca(string ncaFile)
{
// Disable PPTC here as it does not support multiple processes running.
// TODO: This should be eventually removed and it should stop using global state and
// instead manage the cache per process.
Ptc.Close();
PtcProfiler.Stop();
FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
Nca mainNca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
if (mainNca.Header.ContentType != NcaContentType.Program)
{
Logger.Error?.Print(LogClass.Loader, "Selected NCA is not a \"Program\" NCA");
return;
}
IFileSystem codeFs = null;
if (mainNca.CanOpenSection(NcaSectionType.Code))
{
codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, _device.System.FsIntegrityCheckLevel);
}
if (codeFs == null)
{
Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA");
return;
}
using var npdmFile = new UniqueRef<IFile>();
Result result = codeFs.OpenFile(ref npdmFile.Ref(), "/main.npdm".ToU8Span(), OpenMode.Read);
MetaLoader metaData;
npdmFile.Get.GetSize(out long fileSize).ThrowIfFailure();
var npdmBuffer = new byte[fileSize];
npdmFile.Get.Read(out _, 0, npdmBuffer).ThrowIfFailure();
metaData = new MetaLoader();
metaData.Load(npdmBuffer).ThrowIfFailure();
NsoExecutable[] nsos = new NsoExecutable[ExeFsPrefixes.Length];
for (int i = 0; i < nsos.Length; i++)
{
string name = ExeFsPrefixes[i];
if (!codeFs.FileExists($"/{name}"))
{
continue; // File doesn't exist, skip.
}
Logger.Info?.Print(LogClass.Loader, $"Loading {name}...");
using var nsoFile = new UniqueRef<IFile>();
codeFs.OpenFile(ref nsoFile.Ref(), $"/{name}".ToU8Span(), OpenMode.Read).ThrowIfFailure();
nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name);
}
// Collect the nsos, ignoring ones that aren't used.
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
MemoryManagerMode memoryManagerMode = _device.Configuration.MemoryManagerMode;
if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
{
memoryManagerMode = MemoryManagerMode.SoftwarePageTable;
}
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit: false);
ProgramLoader.LoadNsos(_device.System.KernelContext, out _, metaData, programInfo, executables: programs);
string titleIdText = npdm.Aci.Value.ProgramId.Value.ToString("x16");
bool titleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0;
string programName = Encoding.ASCII.GetString(npdm.Meta.Value.ProgramName).TrimEnd('\0');
Logger.Info?.Print(LogClass.Loader, $"Service Loaded: {programName} [{titleIdText}] [{(titleIs64Bit ? "64-bit" : "32-bit")}]");
}
private void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca) private void LoadNca(Nca mainNca, Nca patchNca, Nca controlNca)
{ {
if (mainNca.Header.ContentType != NcaContentType.Program) if (mainNca.Header.ContentType != NcaContentType.Program)
@ -508,7 +597,7 @@ namespace Ryujinx.HLE.HOS
{ {
if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs)) if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs))
{ {
metaData = null; //TODO: Check if we should retain old npdm metaData = null; // TODO: Check if we should retain old npdm.
} }
metaData ??= ReadNpdm(codeFs); metaData ??= ReadNpdm(codeFs);
@ -521,7 +610,7 @@ namespace Ryujinx.HLE.HOS
if (!codeFs.FileExists($"/{name}")) if (!codeFs.FileExists($"/{name}"))
{ {
continue; // file doesn't exist, skip continue; // File doesn't exist, skip.
} }
Logger.Info?.Print(LogClass.Loader, $"Loading {name}..."); Logger.Info?.Print(LogClass.Loader, $"Loading {name}...");
@ -533,13 +622,13 @@ namespace Ryujinx.HLE.HOS
nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name); nsos[i] = new NsoExecutable(nsoFile.Release().AsStorage(), name);
} }
// ExeFs file replacements // ExeFs file replacements.
ModLoadResult modLoadResult = _device.Configuration.VirtualFileSystem.ModLoader.ApplyExefsMods(TitleId, nsos); ModLoadResult modLoadResult = _device.Configuration.VirtualFileSystem.ModLoader.ApplyExefsMods(TitleId, nsos);
// collect the nsos, ignoring ones that aren't used // Collect the nsos, ignoring ones that aren't used.
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray(); NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
// take the npdm from mods if present // Take the npdm from mods if present.
if (modLoadResult.Npdm != null) if (modLoadResult.Npdm != null)
{ {
metaData = modLoadResult.Npdm; metaData = modLoadResult.Npdm;
@ -598,7 +687,7 @@ namespace Ryujinx.HLE.HOS
executable = obj; executable = obj;
// homebrew NRO can actually have some data after the actual NRO // Homebrew NRO can actually have some data after the actual NRO.
if (input.Length > obj.FileSize) if (input.Length > obj.FileSize)
{ {
input.Position = obj.FileSize; input.Position = obj.FileSize;
@ -677,7 +766,7 @@ namespace Ryujinx.HLE.HOS
TitleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0; TitleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0;
_device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId); _device.System.LibHacHorizonManager.ArpIReader.ApplicationId = new LibHac.ApplicationId(TitleId);
// Explicitly null titleid to disable the shader cache // Explicitly null titleid to disable the shader cache.
Graphics.Gpu.GraphicsConfig.TitleId = null; Graphics.Gpu.GraphicsConfig.TitleId = null;
_device.Gpu.HostInitalized.Set(); _device.Gpu.HostInitalized.Set();

View File

@ -72,6 +72,8 @@ namespace Ryujinx.HLE.HOS
internal List<NfpDevice> NfpDevices { get; private set; } internal List<NfpDevice> NfpDevices { get; private set; }
internal SmRegistry SmRegistry { get; private set; }
internal ServerBase SmServer { get; private set; } internal ServerBase SmServer { get; private set; }
internal ServerBase BsdServer { get; private set; } internal ServerBase BsdServer { get; private set; }
internal ServerBase AudRenServer { get; private set; } internal ServerBase AudRenServer { get; private set; }
@ -291,7 +293,8 @@ namespace Ryujinx.HLE.HOS
private void InitializeServices() private void InitializeServices()
{ {
SmServer = new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext)); SmRegistry = new SmRegistry();
SmServer = new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext, SmRegistry));
// Wait until SM server thread is done with initialization, // Wait until SM server thread is done with initialization,
// only then doing connections to SM is safe. // only then doing connections to SM is safe.
@ -468,7 +471,7 @@ namespace Ryujinx.HLE.HOS
AudioRendererManager.Dispose(); AudioRendererManager.Dispose();
LibHacHorizonManager.PmClient.Fs.UnregisterProgram(LibHacHorizonManager.ApplicationClient.Os.GetCurrentProcessId().Value).ThrowIfFailure(); LibHacHorizonManager.PmClient.Fs.UnregisterProgram(LibHacHorizonManager.ApplicationClient.Os.GetCurrentProcessId().Value).ThrowIfFailure();
KernelContext.Dispose(); KernelContext.Dispose();
} }
} }

View File

@ -2,9 +2,12 @@ using LibHac;
using LibHac.Account; using LibHac.Account;
using LibHac.Common; using LibHac.Common;
using LibHac.Fs; using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Ns; using LibHac.Ns;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Memory;
@ -15,6 +18,7 @@ using Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService;
using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.HOS.SystemState;
using System; using System;
using System.Numerics; using System.Numerics;
using System.Threading;
using static LibHac.Fs.ApplicationSaveDataManagement; using static LibHac.Fs.ApplicationSaveDataManagement;
using AccountUid = Ryujinx.HLE.HOS.Services.Account.Acc.UserId; using AccountUid = Ryujinx.HLE.HOS.Services.Account.Acc.UserId;
@ -37,6 +41,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
private int _notificationStorageChannelEventHandle; private int _notificationStorageChannelEventHandle;
private int _healthWarningDisappearedSystemEventHandle; private int _healthWarningDisappearedSystemEventHandle;
private bool _gamePlayRecordingState;
private int _jitLoaded;
private HorizonClient _horizon; private HorizonClient _horizon;
public IApplicationFunctions(Horizon system) public IApplicationFunctions(Horizon system)
@ -342,6 +350,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
return ResultCode.Success; return ResultCode.Success;
} }
[CommandHipc(65)] // 3.0.0+
// IsGamePlayRecordingSupported() -> u8
public ResultCode IsGamePlayRecordingSupported(ServiceCtx context)
{
context.ResponseData.Write(_gamePlayRecordingState);
return ResultCode.Success;
}
[CommandHipc(66)] // 3.0.0+ [CommandHipc(66)] // 3.0.0+
// InitializeGamePlayRecording(u64, handle<copy>) // InitializeGamePlayRecording(u64, handle<copy>)
public ResultCode InitializeGamePlayRecording(ServiceCtx context) public ResultCode InitializeGamePlayRecording(ServiceCtx context)
@ -355,9 +372,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
// SetGamePlayRecordingState(u32) // SetGamePlayRecordingState(u32)
public ResultCode SetGamePlayRecordingState(ServiceCtx context) public ResultCode SetGamePlayRecordingState(ServiceCtx context)
{ {
int state = context.RequestData.ReadInt32(); _gamePlayRecordingState = context.RequestData.ReadInt32() != 0;
Logger.Stub?.PrintStub(LogClass.ServiceAm, new { state }); Logger.Stub?.PrintStub(LogClass.ServiceAm, new { _gamePlayRecordingState });
return ResultCode.Success; return ResultCode.Success;
} }
@ -631,5 +648,31 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
return ResultCode.Success; return ResultCode.Success;
} }
[CommandHipc(1001)] // 10.0.0+
// PrepareForJit()
public ResultCode PrepareForJit(ServiceCtx context)
{
if (Interlocked.Exchange(ref _jitLoaded, 1) == 0)
{
string jitPath = context.Device.System.ContentManager.GetInstalledContentPath(0x010000000000003B, StorageId.BuiltInSystem, NcaContentType.Program);
string filePath = context.Device.FileSystem.SwitchPathToSystemPath(jitPath);
if (string.IsNullOrWhiteSpace(filePath))
{
throw new InvalidSystemResourceException($"JIT (010000000000003B) system title not found! The JIT will not work, provide the system archive to fix this error. (See https://github.com/Ryujinx/Ryujinx#requirements for more information)");
}
context.Device.Application.LoadServiceNca(filePath);
// FIXME: Most likely not how this should be done?
while (!context.Device.System.SmRegistry.IsServiceRegistered("jit:u"))
{
context.Device.System.SmRegistry.WaitForServiceRegistration();
}
}
return ResultCode.Success;
}
} }
} }

View File

@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
private ControllerType[] _configuredTypes; private ControllerType[] _configuredTypes;
private KEvent[] _styleSetUpdateEvents; private KEvent[] _styleSetUpdateEvents;
private bool[] _supportedPlayers; private bool[] _supportedPlayers;
private static HidVibrationValue _neutralVibrationValue = new HidVibrationValue private static VibrationValue _neutralVibrationValue = new VibrationValue
{ {
AmplitudeLow = 0f, AmplitudeLow = 0f,
FrequencyLow = 160f, FrequencyLow = 160f,
@ -33,8 +33,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
internal bool SixAxisActive = false; // TODO: link to hidserver when implemented internal bool SixAxisActive = false; // TODO: link to hidserver when implemented
internal ControllerType SupportedStyleSets { get; set; } internal ControllerType SupportedStyleSets { get; set; }
public Dictionary<PlayerIndex, ConcurrentQueue<(HidVibrationValue, HidVibrationValue)>> RumbleQueues = new Dictionary<PlayerIndex, ConcurrentQueue<(HidVibrationValue, HidVibrationValue)>>(); public Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>> RumbleQueues = new Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>>();
public Dictionary<PlayerIndex, (HidVibrationValue, HidVibrationValue)> LastVibrationValues = new Dictionary<PlayerIndex, (HidVibrationValue, HidVibrationValue)>(); public Dictionary<PlayerIndex, (VibrationValue, VibrationValue)> LastVibrationValues = new Dictionary<PlayerIndex, (VibrationValue, VibrationValue)>();
public NpadDevices(Switch device, bool active = true) : base(device, active) public NpadDevices(Switch device, bool active = true) : base(device, active)
{ {
@ -588,21 +588,21 @@ namespace Ryujinx.HLE.HOS.Services.Hid
WriteNewSixInputEntry(ref currentNpad.JoyRightSixAxisSensor, ref newState); WriteNewSixInputEntry(ref currentNpad.JoyRightSixAxisSensor, ref newState);
} }
public void UpdateRumbleQueue(PlayerIndex index, Dictionary<byte, HidVibrationValue> dualVibrationValues) public void UpdateRumbleQueue(PlayerIndex index, Dictionary<byte, VibrationValue> dualVibrationValues)
{ {
if (RumbleQueues.TryGetValue(index, out ConcurrentQueue<(HidVibrationValue, HidVibrationValue)> currentQueue)) if (RumbleQueues.TryGetValue(index, out ConcurrentQueue<(VibrationValue, VibrationValue)> currentQueue))
{ {
if (!dualVibrationValues.TryGetValue(0, out HidVibrationValue leftVibrationValue)) if (!dualVibrationValues.TryGetValue(0, out VibrationValue leftVibrationValue))
{ {
leftVibrationValue = _neutralVibrationValue; leftVibrationValue = _neutralVibrationValue;
} }
if (!dualVibrationValues.TryGetValue(1, out HidVibrationValue rightVibrationValue)) if (!dualVibrationValues.TryGetValue(1, out VibrationValue rightVibrationValue))
{ {
rightVibrationValue = _neutralVibrationValue; rightVibrationValue = _neutralVibrationValue;
} }
if (!LastVibrationValues.TryGetValue(index, out (HidVibrationValue, HidVibrationValue) dualVibrationValue) || !leftVibrationValue.Equals(dualVibrationValue.Item1) || !rightVibrationValue.Equals(dualVibrationValue.Item2)) if (!LastVibrationValues.TryGetValue(index, out (VibrationValue, VibrationValue) dualVibrationValue) || !leftVibrationValue.Equals(dualVibrationValue.Item1) || !rightVibrationValue.Equals(dualVibrationValue.Item2))
{ {
currentQueue.Enqueue((leftVibrationValue, rightVibrationValue)); currentQueue.Enqueue((leftVibrationValue, rightVibrationValue));
@ -611,9 +611,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
} }
} }
public HidVibrationValue GetLastVibrationValue(PlayerIndex index, byte position) public VibrationValue GetLastVibrationValue(PlayerIndex index, byte position)
{ {
if (!LastVibrationValues.TryGetValue(index, out (HidVibrationValue, HidVibrationValue) dualVibrationValue)) if (!LastVibrationValues.TryGetValue(index, out (VibrationValue, VibrationValue) dualVibrationValue))
{ {
return _neutralVibrationValue; return _neutralVibrationValue;
} }
@ -621,11 +621,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid
return (position == 0) ? dualVibrationValue.Item1 : dualVibrationValue.Item2; return (position == 0) ? dualVibrationValue.Item1 : dualVibrationValue.Item2;
} }
public ConcurrentQueue<(HidVibrationValue, HidVibrationValue)> GetRumbleQueue(PlayerIndex index) public ConcurrentQueue<(VibrationValue, VibrationValue)> GetRumbleQueue(PlayerIndex index)
{ {
if (!RumbleQueues.TryGetValue(index, out ConcurrentQueue<(HidVibrationValue, HidVibrationValue)> rumbleQueue)) if (!RumbleQueues.TryGetValue(index, out ConcurrentQueue<(VibrationValue, VibrationValue)> rumbleQueue))
{ {
rumbleQueue = new ConcurrentQueue<(HidVibrationValue, HidVibrationValue)>(); rumbleQueue = new ConcurrentQueue<(VibrationValue, VibrationValue)>();
_device.Hid.Npads.RumbleQueues[index] = rumbleQueue; _device.Hid.Npads.RumbleQueues[index] = rumbleQueue;
} }

View File

@ -35,5 +35,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer
PlayerIndex.Unknown => NpadIdType.Unknown, PlayerIndex.Unknown => NpadIdType.Unknown,
_ => throw new ArgumentOutOfRangeException(nameof(index)) _ => throw new ArgumentOutOfRangeException(nameof(index))
}; };
public static bool IsValidNpadIdType(NpadIdType npadIdType)
{
return npadIdType <= NpadIdType.Player8 || npadIdType == NpadIdType.Handheld || npadIdType == NpadIdType.Unknown;
}
} }
} }

View File

@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Hid
{
public enum HidNpadJoyAssignmentMode
{
Dual,
Single
}
}

View File

@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Hid namespace Ryujinx.HLE.HOS.Services.Hid
{ {
public enum HidNpadHandheldActivationMode public enum NpadHandheldActivationMode
{ {
Dual, Dual,
Single, Single,

View File

@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Hid namespace Ryujinx.HLE.HOS.Services.Hid
{ {
public enum HidNpadJoyDeviceType public enum NpadJoyDeviceType
{ {
Left, Left,
Right Right

View File

@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Hid namespace Ryujinx.HLE.HOS.Services.Hid
{ {
public struct HidAccelerometerParameters public struct AccelerometerParameters
{ {
public float X; public float X;
public float Y; public float Y;

View File

@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Hid namespace Ryujinx.HLE.HOS.Services.Hid
{ {
public enum HidGyroscopeZeroDriftMode public enum GyroscopeZeroDriftMode
{ {
Loose, Loose,
Standard, Standard,

View File

@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Hid namespace Ryujinx.HLE.HOS.Services.Hid
{ {
public struct HidSensorFusionParameters public struct SensorFusionParameters
{ {
public float RevisePower; public float RevisePower;
public float ReviseRange; public float ReviseRange;

View File

@ -1,8 +0,0 @@
namespace Ryujinx.HLE.HOS.Services.Hid
{
public struct HidVibrationDeviceValue
{
public HidVibrationDeviceType DeviceType;
public HidVibrationDevicePosition Position;
}
}

View File

@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Hid namespace Ryujinx.HLE.HOS.Services.Hid
{ {
public struct HidVibrationDeviceHandle public struct VibrationDeviceHandle
{ {
public byte DeviceType; public byte DeviceType;
public byte PlayerId; public byte PlayerId;

View File

@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Hid namespace Ryujinx.HLE.HOS.Services.Hid
{ {
public enum HidVibrationDevicePosition public enum VibrationDevicePosition
{ {
None, None,
Left, Left,

View File

@ -1,6 +1,6 @@
namespace Ryujinx.HLE.HOS.Services.Hid namespace Ryujinx.HLE.HOS.Services.Hid
{ {
public enum HidVibrationDeviceType public enum VibrationDeviceType
{ {
None, None,
LinearResonantActuator, LinearResonantActuator,

View File

@ -0,0 +1,8 @@
namespace Ryujinx.HLE.HOS.Services.Hid
{
public struct VibrationDeviceValue
{
public VibrationDeviceType DeviceType;
public VibrationDevicePosition Position;
}
}

View File

@ -1,9 +1,8 @@
using Ryujinx.HLE.HOS.Tamper; using System;
using System;
namespace Ryujinx.HLE.HOS.Services.Hid namespace Ryujinx.HLE.HOS.Services.Hid
{ {
public struct HidVibrationValue public struct VibrationValue
{ {
public float AmplitudeLow; public float AmplitudeLow;
public float FrequencyLow; public float FrequencyLow;
@ -12,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return obj is HidVibrationValue value && return obj is VibrationValue value &&
AmplitudeLow == value.AmplitudeLow && AmplitudeLow == value.AmplitudeLow &&
AmplitudeHigh == value.AmplitudeHigh; AmplitudeHigh == value.AmplitudeHigh;
} }

View File

@ -5,6 +5,7 @@ using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Hid.HidServer; using Ryujinx.HLE.HOS.Services.Hid.HidServer;
using Ryujinx.HLE.HOS.Services.Hid.Types; using Ryujinx.HLE.HOS.Services.Hid.Types;
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -26,9 +27,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
private bool _isFirmwareUpdateAvailableForSixAxisSensor; private bool _isFirmwareUpdateAvailableForSixAxisSensor;
private bool _isSixAxisSensorUnalteredPassthroughEnabled; private bool _isSixAxisSensorUnalteredPassthroughEnabled;
private HidNpadJoyAssignmentMode _npadJoyAssignmentMode; private NpadHandheldActivationMode _npadHandheldActivationMode;
private HidNpadHandheldActivationMode _npadHandheldActivationMode; private GyroscopeZeroDriftMode _gyroscopeZeroDriftMode;
private HidGyroscopeZeroDriftMode _gyroscopeZeroDriftMode;
private long _npadCommunicationMode; private long _npadCommunicationMode;
private uint _accelerometerPlayMode; private uint _accelerometerPlayMode;
@ -37,22 +37,21 @@ namespace Ryujinx.HLE.HOS.Services.Hid
#pragma warning restore CS0649 #pragma warning restore CS0649
private float _sevenSixAxisSensorFusionStrength; private float _sevenSixAxisSensorFusionStrength;
private HidSensorFusionParameters _sensorFusionParams; private SensorFusionParameters _sensorFusionParams;
private HidAccelerometerParameters _accelerometerParams; private AccelerometerParameters _accelerometerParams;
public IHidServer(ServiceCtx context) : base(context.Device.System.HidServer) public IHidServer(ServiceCtx context) : base(context.Device.System.HidServer)
{ {
_xpadIdEvent = new KEvent(context.Device.System.KernelContext); _xpadIdEvent = new KEvent(context.Device.System.KernelContext);
_palmaOperationCompleteEvent = new KEvent(context.Device.System.KernelContext); _palmaOperationCompleteEvent = new KEvent(context.Device.System.KernelContext);
_npadJoyAssignmentMode = HidNpadJoyAssignmentMode.Dual; _npadHandheldActivationMode = NpadHandheldActivationMode.Dual;
_npadHandheldActivationMode = HidNpadHandheldActivationMode.Dual; _gyroscopeZeroDriftMode = GyroscopeZeroDriftMode.Standard;
_gyroscopeZeroDriftMode = HidGyroscopeZeroDriftMode.Standard;
_isFirmwareUpdateAvailableForSixAxisSensor = false; _isFirmwareUpdateAvailableForSixAxisSensor = false;
_sensorFusionParams = new HidSensorFusionParameters(); _sensorFusionParams = new SensorFusionParameters();
_accelerometerParams = new HidAccelerometerParameters(); _accelerometerParams = new AccelerometerParameters();
// TODO: signal event at right place // TODO: signal event at right place
_xpadIdEvent.ReadableEvent.Signal(); _xpadIdEvent.ReadableEvent.Signal();
@ -393,7 +392,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
int sixAxisSensorHandle = context.RequestData.ReadInt32(); int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding context.RequestData.BaseStream.Position += 4; // Padding
_sensorFusionParams = new HidSensorFusionParameters _sensorFusionParams = new SensorFusionParameters
{ {
RevisePower = context.RequestData.ReadInt32(), RevisePower = context.RequestData.ReadInt32(),
ReviseRange = context.RequestData.ReadInt32() ReviseRange = context.RequestData.ReadInt32()
@ -445,7 +444,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
int sixAxisSensorHandle = context.RequestData.ReadInt32(); int sixAxisSensorHandle = context.RequestData.ReadInt32();
context.RequestData.BaseStream.Position += 4; // Padding context.RequestData.BaseStream.Position += 4; // Padding
_accelerometerParams = new HidAccelerometerParameters _accelerometerParams = new AccelerometerParameters
{ {
X = context.RequestData.ReadInt32(), X = context.RequestData.ReadInt32(),
Y = context.RequestData.ReadInt32() Y = context.RequestData.ReadInt32()
@ -539,7 +538,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public ResultCode SetGyroscopeZeroDriftMode(ServiceCtx context) public ResultCode SetGyroscopeZeroDriftMode(ServiceCtx context)
{ {
int sixAxisSensorHandle = context.RequestData.ReadInt32(); int sixAxisSensorHandle = context.RequestData.ReadInt32();
_gyroscopeZeroDriftMode = (HidGyroscopeZeroDriftMode)context.RequestData.ReadInt32(); _gyroscopeZeroDriftMode = (GyroscopeZeroDriftMode)context.RequestData.ReadInt32();
long appletResourceUserId = context.RequestData.ReadInt64(); long appletResourceUserId = context.RequestData.ReadInt64();
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _gyroscopeZeroDriftMode }); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _gyroscopeZeroDriftMode });
@ -570,7 +569,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
context.RequestData.BaseStream.Position += 4; // Padding context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64(); long appletResourceUserId = context.RequestData.ReadInt64();
_gyroscopeZeroDriftMode = HidGyroscopeZeroDriftMode.Standard; _gyroscopeZeroDriftMode = GyroscopeZeroDriftMode.Standard;
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _gyroscopeZeroDriftMode }); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, sixAxisSensorHandle, _gyroscopeZeroDriftMode });
@ -909,54 +908,63 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// SetNpadJoyAssignmentModeSingleByDefault(uint HidControllerId, nn::applet::AppletResourceUserId) // SetNpadJoyAssignmentModeSingleByDefault(uint HidControllerId, nn::applet::AppletResourceUserId)
public ResultCode SetNpadJoyAssignmentModeSingleByDefault(ServiceCtx context) public ResultCode SetNpadJoyAssignmentModeSingleByDefault(ServiceCtx context)
{ {
PlayerIndex hidControllerId = (PlayerIndex)context.RequestData.ReadInt32(); NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32();
long appletResourceUserId = context.RequestData.ReadInt64(); context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
_npadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single; if (HidUtils.IsValidNpadIdType(npadIdType))
{
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, hidControllerId, _npadJoyAssignmentMode }); context.Device.Hid.SharedMemory.Npads[(int)HidUtils.GetIndexFromNpadIdType(npadIdType)].InternalState.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
}
return ResultCode.Success; return ResultCode.Success;
} }
[CommandHipc(123)] [CommandHipc(123)]
// SetNpadJoyAssignmentModeSingle(uint HidControllerId, nn::applet::AppletResourceUserId, long HidNpadJoyDeviceType) // SetNpadJoyAssignmentModeSingle(uint npadIdType, nn::applet::AppletResourceUserId, uint npadJoyDeviceType)
public ResultCode SetNpadJoyAssignmentModeSingle(ServiceCtx context) public ResultCode SetNpadJoyAssignmentModeSingle(ServiceCtx context)
{ {
PlayerIndex hidControllerId = (PlayerIndex)context.RequestData.ReadInt32(); NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32();
long appletResourceUserId = context.RequestData.ReadInt64(); context.RequestData.BaseStream.Position += 4; // Padding
HidNpadJoyDeviceType hidNpadJoyDeviceType = (HidNpadJoyDeviceType)context.RequestData.ReadInt64(); long appletResourceUserId = context.RequestData.ReadInt64();
NpadJoyDeviceType npadJoyDeviceType = (NpadJoyDeviceType)context.RequestData.ReadUInt32();
_npadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single; if (HidUtils.IsValidNpadIdType(npadIdType))
{
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, hidControllerId, hidNpadJoyDeviceType, _npadJoyAssignmentMode }); SetNpadJoyAssignmentModeSingleWithDestinationImpl(context, npadIdType, appletResourceUserId, npadJoyDeviceType, out _, out _);
}
return ResultCode.Success; return ResultCode.Success;
} }
[CommandHipc(124)] [CommandHipc(124)]
// SetNpadJoyAssignmentModeDual(uint HidControllerId, nn::applet::AppletResourceUserId) // SetNpadJoyAssignmentModeDual(uint npadIdType, nn::applet::AppletResourceUserId)
public ResultCode SetNpadJoyAssignmentModeDual(ServiceCtx context) public ResultCode SetNpadJoyAssignmentModeDual(ServiceCtx context)
{ {
PlayerIndex hidControllerId = HidUtils.GetIndexFromNpadIdType((NpadIdType)context.RequestData.ReadInt32()); NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32();
long appletResourceUserId = context.RequestData.ReadInt64(); context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
_npadJoyAssignmentMode = HidNpadJoyAssignmentMode.Dual; if (HidUtils.IsValidNpadIdType(npadIdType))
{
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, hidControllerId, _npadJoyAssignmentMode }); context.Device.Hid.SharedMemory.Npads[(int)HidUtils.GetIndexFromNpadIdType(npadIdType)].InternalState.JoyAssignmentMode = NpadJoyAssignmentMode.Dual;
}
return ResultCode.Success; return ResultCode.Success;
} }
[CommandHipc(125)] [CommandHipc(125)]
// MergeSingleJoyAsDualJoy(uint SingleJoyId0, uint SingleJoyId1, nn::applet::AppletResourceUserId) // MergeSingleJoyAsDualJoy(uint npadIdType0, uint npadIdType1, nn::applet::AppletResourceUserId)
public ResultCode MergeSingleJoyAsDualJoy(ServiceCtx context) public ResultCode MergeSingleJoyAsDualJoy(ServiceCtx context)
{ {
long singleJoyId0 = context.RequestData.ReadInt32(); NpadIdType npadIdType0 = (NpadIdType)context.RequestData.ReadUInt32();
long singleJoyId1 = context.RequestData.ReadInt32(); NpadIdType npadIdType1 = (NpadIdType)context.RequestData.ReadUInt32();
long appletResourceUserId = context.RequestData.ReadInt64(); long appletResourceUserId = context.RequestData.ReadInt64();
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, singleJoyId0, singleJoyId1 }); if (HidUtils.IsValidNpadIdType(npadIdType0) && HidUtils.IsValidNpadIdType(npadIdType1))
{
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, npadIdType0, npadIdType1 });
}
return ResultCode.Success; return ResultCode.Success;
} }
@ -988,7 +996,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public ResultCode SetNpadHandheldActivationMode(ServiceCtx context) public ResultCode SetNpadHandheldActivationMode(ServiceCtx context)
{ {
long appletResourceUserId = context.RequestData.ReadInt64(); long appletResourceUserId = context.RequestData.ReadInt64();
_npadHandheldActivationMode = (HidNpadHandheldActivationMode)context.RequestData.ReadInt64(); _npadHandheldActivationMode = (NpadHandheldActivationMode)context.RequestData.ReadInt64();
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, _npadHandheldActivationMode }); Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, _npadHandheldActivationMode });
@ -1049,37 +1057,47 @@ namespace Ryujinx.HLE.HOS.Services.Hid
} }
[CommandHipc(133)] // 5.0.0+ [CommandHipc(133)] // 5.0.0+
// SetNpadJoyAssignmentModeSingleWithDestination(uint HidControllerId, long HidNpadJoyDeviceType, nn::applet::AppletResourceUserId) -> bool Unknown0, uint Unknown1 // SetNpadJoyAssignmentModeSingleWithDestination(uint npadIdType, uint npadJoyDeviceType, nn::applet::AppletResourceUserId) -> bool npadIdTypeIsSet, uint npadIdTypeSet
public ResultCode SetNpadJoyAssignmentModeSingleWithDestination(ServiceCtx context) public ResultCode SetNpadJoyAssignmentModeSingleWithDestination(ServiceCtx context)
{ {
PlayerIndex hidControllerId = (PlayerIndex)context.RequestData.ReadInt32(); NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadInt32();
HidNpadJoyDeviceType hidNpadJoyDeviceType = (HidNpadJoyDeviceType)context.RequestData.ReadInt64(); NpadJoyDeviceType npadJoyDeviceType = (NpadJoyDeviceType)context.RequestData.ReadInt32();
long appletResourceUserId = context.RequestData.ReadInt64(); context.RequestData.BaseStream.Position += 4; // Padding
long appletResourceUserId = context.RequestData.ReadInt64();
_npadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single; if (HidUtils.IsValidNpadIdType(npadIdType))
{
SetNpadJoyAssignmentModeSingleWithDestinationImpl(context, npadIdType, appletResourceUserId, npadJoyDeviceType, out NpadIdType npadIdTypeSet, out bool npadIdTypeIsSet);
context.ResponseData.Write(0); //Unknown0 if (npadIdTypeIsSet)
context.ResponseData.Write(0); //Unknown1 {
context.ResponseData.Write(npadIdTypeIsSet);
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { context.ResponseData.Write((uint)npadIdTypeSet);
appletResourceUserId, }
hidControllerId, }
hidNpadJoyDeviceType,
_npadJoyAssignmentMode,
Unknown0 = 0,
Unknown1 = 0
});
return ResultCode.Success; return ResultCode.Success;
} }
private void SetNpadJoyAssignmentModeSingleWithDestinationImpl(ServiceCtx context, NpadIdType npadIdType, long appletResourceUserId, NpadJoyDeviceType npadJoyDeviceType, out NpadIdType npadIdTypeSet, out bool npadIdTypeIsSet)
{
npadIdTypeSet = default;
npadIdTypeIsSet = false;
context.Device.Hid.SharedMemory.Npads[(int)HidUtils.GetIndexFromNpadIdType(npadIdType)].InternalState.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
// TODO: Service seems to use the npadJoyDeviceType to find the nearest other Npad available and merge them to dual.
// If one is found, it returns the npadIdType of the other Npad and a bool.
// If not, it returns nothing.
}
[CommandHipc(200)] [CommandHipc(200)]
// GetVibrationDeviceInfo(nn::hid::VibrationDeviceHandle) -> nn::hid::VibrationDeviceInfo // GetVibrationDeviceInfo(nn::hid::VibrationDeviceHandle) -> nn::hid::VibrationDeviceInfo
public ResultCode GetVibrationDeviceInfo(ServiceCtx context) public ResultCode GetVibrationDeviceInfo(ServiceCtx context)
{ {
HidVibrationDeviceHandle deviceHandle = context.RequestData.ReadStruct<HidVibrationDeviceHandle>(); VibrationDeviceHandle deviceHandle = context.RequestData.ReadStruct<VibrationDeviceHandle>();
NpadStyleIndex deviceType = (NpadStyleIndex)deviceHandle.DeviceType; NpadStyleIndex deviceType = (NpadStyleIndex)deviceHandle.DeviceType;
NpadIdType npadIdType = (NpadIdType)deviceHandle.PlayerId; NpadIdType npadIdType = (NpadIdType)deviceHandle.PlayerId;
if (deviceType < NpadStyleIndex.System || deviceType >= NpadStyleIndex.FullKey) if (deviceType < NpadStyleIndex.System || deviceType >= NpadStyleIndex.FullKey)
{ {
@ -1093,28 +1111,28 @@ namespace Ryujinx.HLE.HOS.Services.Hid
return ResultCode.InvalidDeviceIndex; return ResultCode.InvalidDeviceIndex;
} }
HidVibrationDeviceType vibrationDeviceType = HidVibrationDeviceType.None; VibrationDeviceType vibrationDeviceType = VibrationDeviceType.None;
if (Enum.IsDefined<NpadStyleIndex>(deviceType)) if (Enum.IsDefined(deviceType))
{ {
vibrationDeviceType = HidVibrationDeviceType.LinearResonantActuator; vibrationDeviceType = VibrationDeviceType.LinearResonantActuator;
} }
else if ((uint)deviceType == 8) else if ((uint)deviceType == 8)
{ {
vibrationDeviceType = HidVibrationDeviceType.GcErm; vibrationDeviceType = VibrationDeviceType.GcErm;
} }
HidVibrationDevicePosition vibrationDevicePosition = HidVibrationDevicePosition.None; VibrationDevicePosition vibrationDevicePosition = VibrationDevicePosition.None;
if (vibrationDeviceType == HidVibrationDeviceType.LinearResonantActuator) if (vibrationDeviceType == VibrationDeviceType.LinearResonantActuator)
{ {
if (deviceHandle.Position == 0) if (deviceHandle.Position == 0)
{ {
vibrationDevicePosition = HidVibrationDevicePosition.Left; vibrationDevicePosition = VibrationDevicePosition.Left;
} }
else if (deviceHandle.Position == 1) else if (deviceHandle.Position == 1)
{ {
vibrationDevicePosition = HidVibrationDevicePosition.Right; vibrationDevicePosition = VibrationDevicePosition.Right;
} }
else else
{ {
@ -1122,7 +1140,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
} }
} }
HidVibrationDeviceValue deviceInfo = new HidVibrationDeviceValue VibrationDeviceValue deviceInfo = new VibrationDeviceValue
{ {
DeviceType = vibrationDeviceType, DeviceType = vibrationDeviceType,
Position = vibrationDevicePosition Position = vibrationDevicePosition
@ -1140,7 +1158,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// SendVibrationValue(nn::hid::VibrationDeviceHandle, nn::hid::VibrationValue, nn::applet::AppletResourceUserId) // SendVibrationValue(nn::hid::VibrationDeviceHandle, nn::hid::VibrationValue, nn::applet::AppletResourceUserId)
public ResultCode SendVibrationValue(ServiceCtx context) public ResultCode SendVibrationValue(ServiceCtx context)
{ {
HidVibrationDeviceHandle deviceHandle = new HidVibrationDeviceHandle VibrationDeviceHandle deviceHandle = new VibrationDeviceHandle
{ {
DeviceType = context.RequestData.ReadByte(), DeviceType = context.RequestData.ReadByte(),
PlayerId = context.RequestData.ReadByte(), PlayerId = context.RequestData.ReadByte(),
@ -1148,7 +1166,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
Reserved = context.RequestData.ReadByte() Reserved = context.RequestData.ReadByte()
}; };
HidVibrationValue vibrationValue = new HidVibrationValue VibrationValue vibrationValue = new VibrationValue
{ {
AmplitudeLow = context.RequestData.ReadSingle(), AmplitudeLow = context.RequestData.ReadSingle(),
FrequencyLow = context.RequestData.ReadSingle(), FrequencyLow = context.RequestData.ReadSingle(),
@ -1158,7 +1176,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
long appletResourceUserId = context.RequestData.ReadInt64(); long appletResourceUserId = context.RequestData.ReadInt64();
Dictionary<byte, HidVibrationValue> dualVibrationValues = new Dictionary<byte, HidVibrationValue>(); Dictionary<byte, VibrationValue> dualVibrationValues = new Dictionary<byte, VibrationValue>();
dualVibrationValues[deviceHandle.Position] = vibrationValue; dualVibrationValues[deviceHandle.Position] = vibrationValue;
@ -1171,7 +1189,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// GetActualVibrationValue(nn::hid::VibrationDeviceHandle, nn::applet::AppletResourceUserId) -> nn::hid::VibrationValue // GetActualVibrationValue(nn::hid::VibrationDeviceHandle, nn::applet::AppletResourceUserId) -> nn::hid::VibrationValue
public ResultCode GetActualVibrationValue(ServiceCtx context) public ResultCode GetActualVibrationValue(ServiceCtx context)
{ {
HidVibrationDeviceHandle deviceHandle = new HidVibrationDeviceHandle VibrationDeviceHandle deviceHandle = new VibrationDeviceHandle
{ {
DeviceType = context.RequestData.ReadByte(), DeviceType = context.RequestData.ReadByte(),
PlayerId = context.RequestData.ReadByte(), PlayerId = context.RequestData.ReadByte(),
@ -1181,7 +1199,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
long appletResourceUserId = context.RequestData.ReadInt64(); long appletResourceUserId = context.RequestData.ReadInt64();
HidVibrationValue vibrationValue = context.Device.Hid.Npads.GetLastVibrationValue((PlayerIndex)deviceHandle.PlayerId, deviceHandle.Position); VibrationValue vibrationValue = context.Device.Hid.Npads.GetLastVibrationValue((PlayerIndex)deviceHandle.PlayerId, deviceHandle.Position);
context.ResponseData.Write(vibrationValue.AmplitudeLow); context.ResponseData.Write(vibrationValue.AmplitudeLow);
context.ResponseData.Write(vibrationValue.FrequencyLow); context.ResponseData.Write(vibrationValue.FrequencyLow);
@ -1234,12 +1252,12 @@ namespace Ryujinx.HLE.HOS.Services.Hid
context.Memory.Read(context.Request.PtrBuff[1].Position, vibrationValueBuffer); context.Memory.Read(context.Request.PtrBuff[1].Position, vibrationValueBuffer);
Span<HidVibrationDeviceHandle> deviceHandles = MemoryMarshal.Cast<byte, HidVibrationDeviceHandle>(vibrationDeviceHandleBuffer); Span<VibrationDeviceHandle> deviceHandles = MemoryMarshal.Cast<byte, VibrationDeviceHandle>(vibrationDeviceHandleBuffer);
Span<HidVibrationValue> vibrationValues = MemoryMarshal.Cast<byte, HidVibrationValue>(vibrationValueBuffer); Span<VibrationValue> vibrationValues = MemoryMarshal.Cast<byte, VibrationValue>(vibrationValueBuffer);
if (!deviceHandles.IsEmpty && vibrationValues.Length == deviceHandles.Length) if (!deviceHandles.IsEmpty && vibrationValues.Length == deviceHandles.Length)
{ {
Dictionary<byte, HidVibrationValue> dualVibrationValues = new Dictionary<byte, HidVibrationValue>(); Dictionary<byte, VibrationValue> dualVibrationValues = new Dictionary<byte, VibrationValue>();
PlayerIndex currentIndex = (PlayerIndex)deviceHandles[0].PlayerId; PlayerIndex currentIndex = (PlayerIndex)deviceHandles[0].PlayerId;
for (int deviceCounter = 0; deviceCounter < deviceHandles.Length; deviceCounter++) for (int deviceCounter = 0; deviceCounter < deviceHandles.Length; deviceCounter++)
@ -1250,7 +1268,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
if (index != currentIndex || dualVibrationValues.Count == 2) if (index != currentIndex || dualVibrationValues.Count == 2)
{ {
context.Device.Hid.Npads.UpdateRumbleQueue(currentIndex, dualVibrationValues); context.Device.Hid.Npads.UpdateRumbleQueue(currentIndex, dualVibrationValues);
dualVibrationValues = new Dictionary<byte, HidVibrationValue>(); dualVibrationValues = new Dictionary<byte, VibrationValue>();
} }
dualVibrationValues[position] = vibrationValues[deviceCounter]; dualVibrationValues[position] = vibrationValues[deviceCounter];

View File

@ -30,6 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
private List<NroInfo> _nroInfos; private List<NroInfo> _nroInfos;
private KProcess _owner; private KProcess _owner;
private IVirtualMemoryManager _ownerMm;
private static Random _random = new Random(); private static Random _random = new Random();
@ -38,6 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
_nrrInfos = new List<NrrInfo>(MaxNrr); _nrrInfos = new List<NrrInfo>(MaxNrr);
_nroInfos = new List<NroInfo>(MaxNro); _nroInfos = new List<NroInfo>(MaxNro);
_owner = null; _owner = null;
_ownerMm = null;
} }
private ResultCode ParseNrr(out NrrInfo nrrInfo, ServiceCtx context, ulong nrrAddress, ulong nrrSize) private ResultCode ParseNrr(out NrrInfo nrrInfo, ServiceCtx context, ulong nrrAddress, ulong nrrSize)
@ -564,10 +566,12 @@ namespace Ryujinx.HLE.HOS.Services.Ro
return ResultCode.InvalidSession; return ResultCode.InvalidSession;
} }
_owner = context.Process.HandleTable.GetKProcess(context.Request.HandleDesc.ToCopy[0]); int processHandle = context.Request.HandleDesc.ToCopy[0];
context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]); _owner = context.Process.HandleTable.GetKProcess(processHandle);
_ownerMm = _owner?.CpuMemory;
context.Device.System.KernelContext.Syscall.CloseHandle(processHandle);
if (_owner?.CpuMemory is IRefCounted rc) if (_ownerMm is IRefCounted rc)
{ {
rc.IncrementReferenceCount(); rc.IncrementReferenceCount();
} }
@ -575,6 +579,15 @@ namespace Ryujinx.HLE.HOS.Services.Ro
return ResultCode.Success; return ResultCode.Success;
} }
[CommandHipc(10)]
// LoadNrr2(u64, u64, u64, pid)
public ResultCode LoadNrr2(ServiceCtx context)
{
context.Device.System.KernelContext.Syscall.CloseHandle(context.Request.HandleDesc.ToCopy[0]);
return LoadNrr(context);
}
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
if (isDisposing) if (isDisposing)
@ -586,7 +599,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
_nroInfos.Clear(); _nroInfos.Clear();
if (_owner?.CpuMemory is IRefCounted rc) if (_ownerMm is IRefCounted rc)
{ {
rc.DecrementReferenceCount(); rc.DecrementReferenceCount();
} }

View File

@ -4,7 +4,6 @@ using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Ipc; using Ryujinx.HLE.HOS.Kernel.Ipc;
using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Sm;
using System; using System;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Collections.Generic; using System.Collections.Generic;

View File

@ -222,7 +222,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings
return ResultCode.Success; return ResultCode.Success;
} }
[CommandHipc(60)] [CommandHipc(60)]
// IsUserSystemClockAutomaticCorrectionEnabled() -> bool // IsUserSystemClockAutomaticCorrectionEnabled() -> bool
public ResultCode IsUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context) public ResultCode IsUserSystemClockAutomaticCorrectionEnabled(ServiceCtx context)
{ {
@ -234,6 +234,17 @@ namespace Ryujinx.HLE.HOS.Services.Settings
return ResultCode.Success; return ResultCode.Success;
} }
[CommandHipc(62)]
// GetDebugModeFlag() -> bool
public ResultCode GetDebugModeFlag(ServiceCtx context)
{
context.ResponseData.Write(false);
Logger.Stub?.PrintStub(LogClass.ServiceSet);
return ResultCode.Success;
}
[CommandHipc(77)] [CommandHipc(77)]
// GetDeviceNickName() -> buffer<nn::settings::system::DeviceNickName, 0x16> // GetDeviceNickName() -> buffer<nn::settings::system::DeviceNickName, 0x16>
public ResultCode GetDeviceNickName(ServiceCtx context) public ResultCode GetDeviceNickName(ServiceCtx context)

View File

@ -1,11 +1,9 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Ipc; using Ryujinx.HLE.HOS.Kernel.Ipc;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -17,21 +15,19 @@ namespace Ryujinx.HLE.HOS.Services.Sm
{ {
private static Dictionary<string, Type> _services; private static Dictionary<string, Type> _services;
private static readonly ConcurrentDictionary<string, KPort> _registeredServices; private readonly SmRegistry _registry;
private readonly ServerBase _commonServer; private readonly ServerBase _commonServer;
private bool _isInitialized; private bool _isInitialized;
public IUserInterface(KernelContext context) public IUserInterface(KernelContext context, SmRegistry registry)
{ {
_commonServer = new ServerBase(context, "CommonServer"); _commonServer = new ServerBase(context, "CommonServer");
_registry = registry;
} }
static IUserInterface() static IUserInterface()
{ {
_registeredServices = new ConcurrentDictionary<string, KPort>();
_services = Assembly.GetExecutingAssembly().GetTypes() _services = Assembly.GetExecutingAssembly().GetTypes()
.SelectMany(type => type.GetCustomAttributes(typeof(ServiceAttribute), true) .SelectMany(type => type.GetCustomAttributes(typeof(ServiceAttribute), true)
.Select(service => (((ServiceAttribute)service).Name, type))) .Select(service => (((ServiceAttribute)service).Name, type)))
@ -74,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
KSession session = new KSession(context.Device.System.KernelContext); KSession session = new KSession(context.Device.System.KernelContext);
if (_registeredServices.TryGetValue(name, out KPort port)) if (_registry.TryGetService(name, out KPort port))
{ {
KernelResult result = port.EnqueueIncomingSession(session.ServerSession); KernelResult result = port.EnqueueIncomingSession(session.ServerSession);
@ -82,6 +78,15 @@ namespace Ryujinx.HLE.HOS.Services.Sm
{ {
throw new InvalidOperationException($"Session enqueue on port returned error \"{result}\"."); throw new InvalidOperationException($"Session enqueue on port returned error \"{result}\".");
} }
if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
session.ClientSession.DecrementReferenceCount();
context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
} }
else else
{ {
@ -107,18 +112,18 @@ namespace Ryujinx.HLE.HOS.Services.Sm
throw new NotImplementedException(name); throw new NotImplementedException(name);
} }
} }
if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
session.ServerSession.DecrementReferenceCount();
session.ClientSession.DecrementReferenceCount();
context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
} }
if (context.Process.HandleTable.GenerateHandle(session.ClientSession, out int handle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
session.ServerSession.DecrementReferenceCount();
session.ClientSession.DecrementReferenceCount();
context.Response.HandleDesc = IpcHandleDesc.MakeMove(handle);
return ResultCode.Success; return ResultCode.Success;
} }
@ -179,7 +184,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
KPort port = new KPort(context.Device.System.KernelContext, maxSessions, isLight, 0); KPort port = new KPort(context.Device.System.KernelContext, maxSessions, isLight, 0);
if (!_registeredServices.TryAdd(name, port)) if (!_registry.TryRegister(name, port))
{ {
return ResultCode.AlreadyRegistered; return ResultCode.AlreadyRegistered;
} }
@ -219,7 +224,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
return ResultCode.InvalidName; return ResultCode.InvalidName;
} }
if (!_registeredServices.TryRemove(name, out _)) if (!_registry.Unregister(name))
{ {
return ResultCode.NotRegistered; return ResultCode.NotRegistered;
} }

View File

@ -0,0 +1,49 @@
using Ryujinx.HLE.HOS.Kernel.Ipc;
using System.Collections.Concurrent;
using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Sm
{
class SmRegistry
{
private readonly ConcurrentDictionary<string, KPort> _registeredServices;
private readonly AutoResetEvent _serviceRegistrationEvent;
public SmRegistry()
{
_registeredServices = new ConcurrentDictionary<string, KPort>();
_serviceRegistrationEvent = new AutoResetEvent(false);
}
public bool TryGetService(string name, out KPort port)
{
return _registeredServices.TryGetValue(name, out port);
}
public bool TryRegister(string name, KPort port)
{
if (_registeredServices.TryAdd(name, port))
{
_serviceRegistrationEvent.Set();
return true;
}
return false;
}
public bool Unregister(string name)
{
return _registeredServices.TryRemove(name, out _);
}
public bool IsServiceRegistered(string name)
{
return _registeredServices.TryGetValue(name, out _);
}
public void WaitForServiceRegistration()
{
_serviceRegistrationEvent.WaitOne();
}
}
}

View File

@ -164,6 +164,7 @@ namespace Ryujinx.Headless.SDL2
Device.Gpu.Renderer.RunLoop(() => Device.Gpu.Renderer.RunLoop(() =>
{ {
Device.Gpu.SetGpuThread();
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
Translator.IsReadyForTranslation.Set(); Translator.IsReadyForTranslation.Set();

View File

@ -543,14 +543,14 @@ namespace Ryujinx.Input.HLE
Dispose(true); Dispose(true);
} }
public void UpdateRumble(ConcurrentQueue<(HidVibrationValue, HidVibrationValue)> queue) public void UpdateRumble(ConcurrentQueue<(VibrationValue, VibrationValue)> queue)
{ {
if (queue.TryDequeue(out (HidVibrationValue, HidVibrationValue) dualVibrationValue)) if (queue.TryDequeue(out (VibrationValue, VibrationValue) dualVibrationValue))
{ {
if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Rumble.EnableRumble) if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Rumble.EnableRumble)
{ {
HidVibrationValue leftVibrationValue = dualVibrationValue.Item1; VibrationValue leftVibrationValue = dualVibrationValue.Item1;
HidVibrationValue rightVibrationValue = dualVibrationValue.Item2; VibrationValue rightVibrationValue = dualVibrationValue.Item2;
float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15) * controllerConfig.Rumble.StrongRumble)); float low = Math.Min(1f, (float)((rightVibrationValue.AmplitudeLow * 0.85 + rightVibrationValue.AmplitudeHigh * 0.15) * controllerConfig.Rumble.StrongRumble));
float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85) * controllerConfig.Rumble.WeakRumble)); float high = Math.Min(1f, (float)((leftVibrationValue.AmplitudeLow * 0.15 + leftVibrationValue.AmplitudeHigh * 0.85) * controllerConfig.Rumble.WeakRumble));

View File

@ -48,7 +48,7 @@ namespace Ryujinx.Memory
{ {
_viewCompatible = flags.HasFlag(MemoryAllocationFlags.ViewCompatible); _viewCompatible = flags.HasFlag(MemoryAllocationFlags.ViewCompatible);
_forceWindows4KBView = flags.HasFlag(MemoryAllocationFlags.ForceWindows4KBViewMapping); _forceWindows4KBView = flags.HasFlag(MemoryAllocationFlags.ForceWindows4KBViewMapping);
_pointer = MemoryManagement.Reserve(size, _viewCompatible); _pointer = MemoryManagement.Reserve(size, _viewCompatible, _forceWindows4KBView);
} }
else else
{ {
@ -404,7 +404,7 @@ namespace Ryujinx.Memory
} }
else else
{ {
MemoryManagement.Free(ptr); MemoryManagement.Free(ptr, Size, _forceWindows4KBView);
} }
foreach (MemoryBlock viewStorage in _viewStorages.Keys) foreach (MemoryBlock viewStorage in _viewStorages.Keys)

View File

@ -8,9 +8,7 @@ namespace Ryujinx.Memory
{ {
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
IntPtr sizeNint = new IntPtr((long)size); return MemoryManagementWindows.Allocate((IntPtr)size);
return MemoryManagementWindows.Allocate(sizeNint);
} }
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{ {
@ -22,13 +20,11 @@ namespace Ryujinx.Memory
} }
} }
public static IntPtr Reserve(ulong size, bool viewCompatible) public static IntPtr Reserve(ulong size, bool viewCompatible, bool force4KBMap)
{ {
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
IntPtr sizeNint = new IntPtr((long)size); return MemoryManagementWindows.Reserve((IntPtr)size, viewCompatible, force4KBMap);
return MemoryManagementWindows.Reserve(sizeNint, viewCompatible);
} }
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{ {
@ -44,9 +40,7 @@ namespace Ryujinx.Memory
{ {
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
IntPtr sizeNint = new IntPtr((long)size); return MemoryManagementWindows.Commit(address, (IntPtr)size);
return MemoryManagementWindows.Commit(address, sizeNint);
} }
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{ {
@ -62,9 +56,7 @@ namespace Ryujinx.Memory
{ {
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
IntPtr sizeNint = new IntPtr((long)size); return MemoryManagementWindows.Decommit(address, (IntPtr)size);
return MemoryManagementWindows.Decommit(address, sizeNint);
} }
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{ {
@ -80,15 +72,13 @@ namespace Ryujinx.Memory
{ {
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
IntPtr sizeNint = new IntPtr((long)size);
if (force4KBMap) if (force4KBMap)
{ {
MemoryManagementWindows.MapView4KB(sharedMemory, srcOffset, address, sizeNint); MemoryManagementWindows.MapView4KB(sharedMemory, srcOffset, address, (IntPtr)size);
} }
else else
{ {
MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, sizeNint); MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (IntPtr)size);
} }
} }
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
@ -105,15 +95,13 @@ namespace Ryujinx.Memory
{ {
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
IntPtr sizeNint = new IntPtr((long)size);
if (force4KBMap) if (force4KBMap)
{ {
MemoryManagementWindows.UnmapView4KB(address, sizeNint); MemoryManagementWindows.UnmapView4KB(address, (IntPtr)size);
} }
else else
{ {
MemoryManagementWindows.UnmapView(sharedMemory, address, sizeNint); MemoryManagementWindows.UnmapView(sharedMemory, address, (IntPtr)size);
} }
} }
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
@ -132,15 +120,13 @@ namespace Ryujinx.Memory
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
IntPtr sizeNint = new IntPtr((long)size);
if (forView && force4KBMap) if (forView && force4KBMap)
{ {
result = MemoryManagementWindows.Reprotect4KB(address, sizeNint, permission, forView); result = MemoryManagementWindows.Reprotect4KB(address, (IntPtr)size, permission, forView);
} }
else else
{ {
result = MemoryManagementWindows.Reprotect(address, sizeNint, permission, forView); result = MemoryManagementWindows.Reprotect(address, (IntPtr)size, permission, forView);
} }
} }
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
@ -158,11 +144,11 @@ namespace Ryujinx.Memory
} }
} }
public static bool Free(IntPtr address) public static bool Free(IntPtr address, ulong size, bool force4KBMap)
{ {
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
return MemoryManagementWindows.Free(address); return MemoryManagementWindows.Free(address, (IntPtr)size, force4KBMap);
} }
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{ {
@ -178,9 +164,7 @@ namespace Ryujinx.Memory
{ {
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
IntPtr sizeNint = new IntPtr((long)size); return MemoryManagementWindows.CreateSharedMemory((IntPtr)size, reserve);
return MemoryManagementWindows.CreateSharedMemory(sizeNint, reserve);
} }
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{ {

View File

@ -7,21 +7,27 @@ namespace Ryujinx.Memory
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
static class MemoryManagementWindows static class MemoryManagementWindows
{ {
private const int PageSize = 0x1000; public const int PageSize = 0x1000;
private static readonly PlaceholderManager _placeholders = new PlaceholderManager(); private static readonly PlaceholderManager _placeholders = new PlaceholderManager();
private static readonly PlaceholderManager4KB _placeholders4KB = new PlaceholderManager4KB();
public static IntPtr Allocate(IntPtr size) public static IntPtr Allocate(IntPtr size)
{ {
return AllocateInternal(size, AllocationType.Reserve | AllocationType.Commit); return AllocateInternal(size, AllocationType.Reserve | AllocationType.Commit);
} }
public static IntPtr Reserve(IntPtr size, bool viewCompatible) public static IntPtr Reserve(IntPtr size, bool viewCompatible, bool force4KBMap)
{ {
if (viewCompatible) if (viewCompatible)
{ {
IntPtr baseAddress = AllocateInternal2(size, AllocationType.Reserve | AllocationType.ReservePlaceholder); IntPtr baseAddress = AllocateInternal2(size, AllocationType.Reserve | AllocationType.ReservePlaceholder);
_placeholders.ReserveRange((ulong)baseAddress, (ulong)size);
if (!force4KBMap)
{
_placeholders.ReserveRange((ulong)baseAddress, (ulong)size);
}
return baseAddress; return baseAddress;
} }
@ -69,6 +75,8 @@ namespace Ryujinx.Memory
public static void MapView4KB(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size) public static void MapView4KB(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size)
{ {
_placeholders4KB.UnmapAndMarkRangeAsMapped(location, size);
ulong uaddress = (ulong)location; ulong uaddress = (ulong)location;
ulong usize = (ulong)size; ulong usize = (ulong)size;
IntPtr endLocation = (IntPtr)(uaddress + usize); IntPtr endLocation = (IntPtr)(uaddress + usize);
@ -105,20 +113,7 @@ namespace Ryujinx.Memory
public static void UnmapView4KB(IntPtr location, IntPtr size) public static void UnmapView4KB(IntPtr location, IntPtr size)
{ {
ulong uaddress = (ulong)location; _placeholders4KB.UnmapView(location, size);
ulong usize = (ulong)size;
IntPtr endLocation = (IntPtr)(uaddress + usize);
while (location != endLocation)
{
bool result = WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, location, 2);
if (!result)
{
throw new WindowsApiException("UnmapViewOfFile2");
}
location += PageSize;
}
} }
public static bool Reprotect(IntPtr address, IntPtr size, MemoryPermission permission, bool forView) public static bool Reprotect(IntPtr address, IntPtr size, MemoryPermission permission, bool forView)
@ -151,8 +146,17 @@ namespace Ryujinx.Memory
return true; return true;
} }
public static bool Free(IntPtr address) public static bool Free(IntPtr address, IntPtr size, bool force4KBMap)
{ {
if (force4KBMap)
{
_placeholders4KB.UnmapRange(address, size);
}
else
{
_placeholders.UnmapView(IntPtr.Zero, address, size);
}
return WindowsApi.VirtualFree(address, IntPtr.Zero, AllocationType.Release); return WindowsApi.VirtualFree(address, IntPtr.Zero, AllocationType.Release);
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.Versioning;
using System.Threading; using System.Threading;
namespace Ryujinx.Memory.WindowsShared namespace Ryujinx.Memory.WindowsShared
@ -7,6 +8,7 @@ namespace Ryujinx.Memory.WindowsShared
/// <summary> /// <summary>
/// Windows memory placeholder manager. /// Windows memory placeholder manager.
/// </summary> /// </summary>
[SupportedOSPlatform("windows")]
class PlaceholderManager class PlaceholderManager
{ {
private const ulong MinimumPageSize = 0x1000; private const ulong MinimumPageSize = 0x1000;
@ -203,7 +205,7 @@ namespace Ryujinx.Memory.WindowsShared
ulong endAddress = startAddress + unmapSize; ulong endAddress = startAddress + unmapSize;
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>(); var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
int count = 0; int count;
lock (_mappings) lock (_mappings)
{ {
@ -226,8 +228,11 @@ namespace Ryujinx.Memory.WindowsShared
ulong overlapEnd = overlap.End; ulong overlapEnd = overlap.End;
ulong overlapValue = overlap.Value; ulong overlapValue = overlap.Value;
_mappings.Remove(overlap); lock (_mappings)
_mappings.Add(overlapStart, overlapEnd, ulong.MaxValue); {
_mappings.Remove(overlap);
_mappings.Add(overlapStart, overlapEnd, ulong.MaxValue);
}
bool overlapStartsBefore = overlapStart < startAddress; bool overlapStartsBefore = overlapStart < startAddress;
bool overlapEndsAfter = overlapEnd > endAddress; bool overlapEndsAfter = overlapEnd > endAddress;
@ -364,7 +369,7 @@ namespace Ryujinx.Memory.WindowsShared
ulong endAddress = reprotectAddress + reprotectSize; ulong endAddress = reprotectAddress + reprotectSize;
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>(); var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
int count = 0; int count;
lock (_mappings) lock (_mappings)
{ {
@ -469,14 +474,12 @@ namespace Ryujinx.Memory.WindowsShared
{ {
ulong endAddress = address + size; ulong endAddress = address + size;
var overlaps = Array.Empty<IntervalTreeNode<ulong, MemoryPermission>>(); var overlaps = Array.Empty<IntervalTreeNode<ulong, MemoryPermission>>();
int count = 0; int count;
lock (_protections) lock (_protections)
{ {
count = _protections.Get(address, endAddress, ref overlaps); count = _protections.Get(address, endAddress, ref overlaps);
Debug.Assert(count > 0);
if (count == 1 && if (count == 1 &&
overlaps[0].Start <= address && overlaps[0].Start <= address &&
overlaps[0].End >= endAddress && overlaps[0].End >= endAddress &&
@ -536,7 +539,7 @@ namespace Ryujinx.Memory.WindowsShared
{ {
ulong endAddress = address + size; ulong endAddress = address + size;
var overlaps = Array.Empty<IntervalTreeNode<ulong, MemoryPermission>>(); var overlaps = Array.Empty<IntervalTreeNode<ulong, MemoryPermission>>();
int count = 0; int count;
lock (_protections) lock (_protections)
{ {
@ -574,7 +577,7 @@ namespace Ryujinx.Memory.WindowsShared
{ {
ulong endAddress = address + size; ulong endAddress = address + size;
var overlaps = Array.Empty<IntervalTreeNode<ulong, MemoryPermission>>(); var overlaps = Array.Empty<IntervalTreeNode<ulong, MemoryPermission>>();
int count = 0; int count;
lock (_protections) lock (_protections)
{ {

View File

@ -0,0 +1,170 @@
using System;
using System.Runtime.Versioning;
namespace Ryujinx.Memory.WindowsShared
{
/// <summary>
/// Windows 4KB memory placeholder manager.
/// </summary>
[SupportedOSPlatform("windows")]
class PlaceholderManager4KB
{
private const int PageSize = MemoryManagementWindows.PageSize;
private readonly IntervalTree<ulong, byte> _mappings;
/// <summary>
/// Creates a new instance of the Windows 4KB memory placeholder manager.
/// </summary>
public PlaceholderManager4KB()
{
_mappings = new IntervalTree<ulong, byte>();
}
/// <summary>
/// Unmaps the specified range of memory and marks it as mapped internally.
/// </summary>
/// <remarks>
/// Since this marks the range as mapped, the expectation is that the range will be mapped after calling this method.
/// </remarks>
/// <param name="location">Memory address to unmap and mark as mapped</param>
/// <param name="size">Size of the range in bytes</param>
public void UnmapAndMarkRangeAsMapped(IntPtr location, IntPtr size)
{
ulong startAddress = (ulong)location;
ulong unmapSize = (ulong)size;
ulong endAddress = startAddress + unmapSize;
var overlaps = Array.Empty<IntervalTreeNode<ulong, byte>>();
int count = 0;
lock (_mappings)
{
count = _mappings.Get(startAddress, endAddress, ref overlaps);
}
for (int index = 0; index < count; index++)
{
var overlap = overlaps[index];
// Tree operations might modify the node start/end values, so save a copy before we modify the tree.
ulong overlapStart = overlap.Start;
ulong overlapEnd = overlap.End;
ulong overlapValue = overlap.Value;
_mappings.Remove(overlap);
ulong unmapStart = Math.Max(overlapStart, startAddress);
ulong unmapEnd = Math.Min(overlapEnd, endAddress);
if (overlapStart < startAddress)
{
startAddress = overlapStart;
}
if (overlapEnd > endAddress)
{
endAddress = overlapEnd;
}
ulong currentAddress = unmapStart;
while (currentAddress < unmapEnd)
{
WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)currentAddress, 2);
currentAddress += PageSize;
}
}
_mappings.Add(startAddress, endAddress, 0);
}
/// <summary>
/// Unmaps views at the specified memory range.
/// </summary>
/// <param name="location">Address of the range</param>
/// <param name="size">Size of the range in bytes</param>
public void UnmapView(IntPtr location, IntPtr size)
{
ulong startAddress = (ulong)location;
ulong unmapSize = (ulong)size;
ulong endAddress = startAddress + unmapSize;
var overlaps = Array.Empty<IntervalTreeNode<ulong, byte>>();
int count = 0;
lock (_mappings)
{
count = _mappings.Get(startAddress, endAddress, ref overlaps);
}
for (int index = 0; index < count; index++)
{
var overlap = overlaps[index];
// Tree operations might modify the node start/end values, so save a copy before we modify the tree.
ulong overlapStart = overlap.Start;
ulong overlapEnd = overlap.End;
_mappings.Remove(overlap);
if (overlapStart < startAddress)
{
_mappings.Add(overlapStart, startAddress, 0);
}
if (overlapEnd > endAddress)
{
_mappings.Add(endAddress, overlapEnd, 0);
}
ulong unmapStart = Math.Max(overlapStart, startAddress);
ulong unmapEnd = Math.Min(overlapEnd, endAddress);
ulong currentAddress = unmapStart;
while (currentAddress < unmapEnd)
{
WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)currentAddress, 2);
currentAddress += PageSize;
}
}
}
/// <summary>
/// Unmaps mapped memory at a given range.
/// </summary>
/// <param name="location">Address of the range</param>
/// <param name="size">Size of the range in bytes</param>
public void UnmapRange(IntPtr location, IntPtr size)
{
ulong startAddress = (ulong)location;
ulong unmapSize = (ulong)size;
ulong endAddress = startAddress + unmapSize;
var overlaps = Array.Empty<IntervalTreeNode<ulong, byte>>();
int count = 0;
lock (_mappings)
{
count = _mappings.Get(startAddress, endAddress, ref overlaps);
}
for (int index = 0; index < count; index++)
{
var overlap = overlaps[index];
// Tree operations might modify the node start/end values, so save a copy before we modify the tree.
ulong unmapStart = Math.Max(overlap.Start, startAddress);
ulong unmapEnd = Math.Min(overlap.End, endAddress);
_mappings.Remove(overlap);
ulong currentAddress = unmapStart;
while (currentAddress < unmapEnd)
{
WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)currentAddress, 2);
currentAddress += PageSize;
}
}
}
}
}

View File

@ -32,8 +32,20 @@ namespace Ryujinx
[DllImport("libX11")] [DllImport("libX11")]
private extern static int XInitThreads(); private extern static int XInitThreads();
[DllImport("user32.dll", SetLastError = true)]
public static extern int MessageBoxA(IntPtr hWnd, string text, string caption, uint type);
private const uint MB_ICONWARNING = 0x30;
static void Main(string[] args) static void Main(string[] args)
{ {
Version = ReleaseInformations.GetVersion();
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
{
MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MB_ICONWARNING);
}
// Parse Arguments. // Parse Arguments.
string launchPathArg = null; string launchPathArg = null;
string baseDirPathArg = null; string baseDirPathArg = null;
@ -82,8 +94,6 @@ namespace Ryujinx
// Delete backup files after updating. // Delete backup files after updating.
Task.Run(Updater.CleanupUpdate); Task.Run(Updater.CleanupUpdate);
Version = ReleaseInformations.GetVersion();
Console.Title = $"Ryujinx Console {Version}"; Console.Title = $"Ryujinx Console {Version}";
// NOTE: GTK3 doesn't init X11 in a multi threaded way. // NOTE: GTK3 doesn't init X11 in a multi threaded way.