Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
43b4b34376 | ||
|
92ca1cb0cb |
@@ -94,7 +94,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs);
|
||||
void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers);
|
||||
|
||||
void SetViewports(int first, ReadOnlySpan<Viewport> viewports);
|
||||
void SetViewports(int first, ReadOnlySpan<Viewport> viewports, bool disableTransform);
|
||||
|
||||
void TextureBarrier();
|
||||
void TextureBarrierTiled();
|
||||
|
@@ -9,17 +9,19 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
public CommandType CommandType => CommandType.SetViewports;
|
||||
private int _first;
|
||||
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;
|
||||
_viewports = viewports;
|
||||
_disableTransform = disableTransform;
|
||||
}
|
||||
|
||||
public static void Run(ref SetViewportsCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@@ -304,9 +304,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_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();
|
||||
}
|
||||
|
||||
|
@@ -72,6 +72,13 @@ namespace Ryujinx.Graphics.GAL
|
||||
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()
|
||||
{
|
||||
if (_startOffset != -1)
|
||||
|
@@ -113,7 +113,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
nameof(ThreedClassState.DepthMode),
|
||||
nameof(ThreedClassState.ViewportTransform),
|
||||
nameof(ThreedClassState.ViewportExtents),
|
||||
nameof(ThreedClassState.YControl)),
|
||||
nameof(ThreedClassState.YControl),
|
||||
nameof(ThreedClassState.ViewportTransformEnable)),
|
||||
|
||||
new StateUpdateCallbackEntry(UpdatePolygonMode,
|
||||
nameof(ThreedClassState.PolygonModeFront),
|
||||
@@ -200,7 +201,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
// of the shader for the new state.
|
||||
if (_shaderSpecState != null)
|
||||
{
|
||||
if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState()))
|
||||
if (!_shaderSpecState.MatchesGraphics(_channel, GetPoolState(), GetGraphicsState()))
|
||||
{
|
||||
ForceShaderUpdate();
|
||||
}
|
||||
@@ -568,6 +569,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
var yControl = _state.State.YControl;
|
||||
var face = _state.State.FaceState;
|
||||
|
||||
bool disableTransform = _state.State.ViewportTransformEnable == 0;
|
||||
|
||||
UpdateFrontFace(yControl, face.FrontFace);
|
||||
UpdateDepthMode();
|
||||
|
||||
@@ -577,6 +580,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
||||
_context.Renderer.Pipeline.SetViewports(0, viewports);
|
||||
_context.Renderer.Pipeline.SetViewports(0, viewports, disableTransform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1194,7 +1208,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
return new GpuChannelGraphicsState(
|
||||
_state.State.EarlyZForce,
|
||||
_drawState.Topology,
|
||||
_state.State.TessMode);
|
||||
_state.State.TessMode,
|
||||
_state.State.ViewportTransformEnable == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -166,7 +166,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.Cache
|
||||
GpuChannelGraphicsState graphicsState = new GpuChannelGraphicsState(
|
||||
accessorHeader.StateFlags.HasFlag(GuestGpuStateFlags.EarlyZForce),
|
||||
topology,
|
||||
tessMode);
|
||||
tessMode,
|
||||
false);
|
||||
|
||||
TransformFeedbackDescriptor[] tfdNew = null;
|
||||
|
||||
|
@@ -185,6 +185,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
return _oldSpecState.GraphicsState.EarlyZForce;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool QueryViewportTransformDisable()
|
||||
{
|
||||
return _oldSpecState.GraphicsState.ViewportTransformDisable;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RegisterTexture(int handle, int cbufSlot)
|
||||
{
|
||||
|
@@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
private const ushort FileFormatVersionMajor = 1;
|
||||
private const ushort FileFormatVersionMinor = 1;
|
||||
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 SharedDataFileName = "shared.data";
|
||||
|
@@ -217,6 +217,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
return _state.GraphicsState.EarlyZForce;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool QueryViewportTransformDisable()
|
||||
{
|
||||
return _state.GraphicsState.ViewportTransformDisable;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void RegisterTexture(int handle, int cbufSlot)
|
||||
{
|
||||
|
@@ -25,17 +25,24 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// </summary>
|
||||
public readonly TessMode TessellationMode;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whenever the viewport transform is disabled.
|
||||
/// </summary>
|
||||
public readonly bool ViewportTransformDisable;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GPU graphics state.
|
||||
/// </summary>
|
||||
/// <param name="earlyZForce">Early Z force enable</param>
|
||||
/// <param name="topology">Primitive topology</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;
|
||||
Topology = topology;
|
||||
TessellationMode = tessellationMode;
|
||||
ViewportTransformDisable = viewportTransformDisable;
|
||||
}
|
||||
}
|
||||
}
|
@@ -249,12 +249,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
GpuChannelGraphicsState graphicsState,
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
return gpShaders;
|
||||
@@ -429,12 +429,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// </summary>
|
||||
/// <param name="channel">GPU channel using the shader</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="addresses">GPU virtual addresses of all enabled shader stages</param>
|
||||
/// <returns>True if the code is different, false otherwise</returns>
|
||||
private static bool IsShaderEqual(
|
||||
GpuChannel channel,
|
||||
GpuChannelPoolState poolState,
|
||||
GpuChannelGraphicsState graphicsState,
|
||||
CachedShaderProgram gpShaders,
|
||||
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>
|
||||
|
@@ -208,6 +208,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// </remarks>
|
||||
/// <param name="channel">GPU channel</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="program">Cached host program for the given state, if found</param>
|
||||
/// <param name="guestCode">Cached guest code, if any found</param>
|
||||
@@ -215,6 +216,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
public bool TryFind(
|
||||
GpuChannel channel,
|
||||
GpuChannelPoolState poolState,
|
||||
GpuChannelGraphicsState graphicsState,
|
||||
ShaderAddresses addresses,
|
||||
out CachedShaderProgram program,
|
||||
out CachedGraphicsGuestCode guestCode)
|
||||
@@ -234,7 +236,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
|
||||
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;
|
||||
|
@@ -24,13 +24,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// </summary>
|
||||
/// <param name="channel">GPU channel</param>
|
||||
/// <param name="poolState">Texture pool state</param>
|
||||
/// <param name="graphicsState">Graphics state</param>
|
||||
/// <param name="program">Cached program, if found</param>
|
||||
/// <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)
|
||||
{
|
||||
if (entry.SpecializationState.MatchesGraphics(channel, poolState))
|
||||
if (entry.SpecializationState.MatchesGraphics(channel, poolState, graphicsState))
|
||||
{
|
||||
program = entry;
|
||||
return true;
|
||||
|
@@ -395,9 +395,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// </summary>
|
||||
/// <param name="channel">GPU channel</param>
|
||||
/// <param name="poolState">Texture pool state</param>
|
||||
/// <param name="graphicsState">Graphics state</param>
|
||||
/// <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);
|
||||
}
|
||||
|
||||
|
@@ -1266,7 +1266,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
_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 _depthRangeArray, viewports.Length * 2);
|
||||
@@ -1305,6 +1305,19 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
|
||||
GL.ViewportArray(first, viewports.Length, viewportArray);
|
||||
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()
|
||||
|
@@ -249,7 +249,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
context.AppendLine();
|
||||
}
|
||||
}
|
||||
else if (isFragment)
|
||||
else if (isFragment || context.Config.Stage == ShaderStage.Vertex)
|
||||
{
|
||||
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)
|
||||
{
|
||||
bool isFragment = stage == ShaderStage.Fragment;
|
||||
if (!isFragment && scaleElements == 0)
|
||||
bool needsSupportBlock = stage == ShaderStage.Fragment ||
|
||||
(context.Config.LastInVertexPipeline && context.Config.GpuAccessor.QueryViewportTransformDisable());
|
||||
|
||||
if (!needsSupportBlock && scaleElements == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -630,6 +632,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
case ShaderStage.Vertex:
|
||||
context.AppendLine($"uint {DefaultNames.SupportBlockAlphaTestName};");
|
||||
context.AppendLine($"bool {DefaultNames.SupportBlockIsBgraName}[{SupportBuffer.FragmentIsBgraCount}];");
|
||||
context.AppendLine($"vec4 {DefaultNames.SupportBlockViewportInverse};");
|
||||
context.AppendLine($"int {DefaultNames.SupportBlockFragmentScaleCount};");
|
||||
break;
|
||||
case ShaderStage.Compute:
|
||||
|
@@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
public const string SupportBlockName = "support_block";
|
||||
public const string SupportBlockAlphaTestName = "s_alpha_test";
|
||||
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 SupportBlockRenderScaleName = "s_render_scale";
|
||||
|
||||
|
@@ -84,7 +84,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
{ AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[4]", VariableType.Bool) },
|
||||
{ AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.SupportBlockIsBgraName}[5]", 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;
|
||||
|
@@ -329,6 +329,15 @@ namespace Ryujinx.Graphics.Shader
|
||||
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>
|
||||
/// Registers a texture used by the shader.
|
||||
/// </summary>
|
||||
|
@@ -206,7 +206,33 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
|
||||
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)
|
||||
|
@@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Shader
|
||||
|
||||
public static int FragmentAlphaTestOffset;
|
||||
public static int FragmentIsBgraOffset;
|
||||
public static int ViewportInverseOffset;
|
||||
public static int FragmentRenderScaleCountOffset;
|
||||
public static int GraphicsRenderScaleOffset;
|
||||
public static int ComputeRenderScaleOffset;
|
||||
@@ -40,6 +41,7 @@ namespace Ryujinx.Graphics.Shader
|
||||
|
||||
FragmentAlphaTestOffset = OffsetOf(ref instance, ref instance.FragmentAlphaTest);
|
||||
FragmentIsBgraOffset = OffsetOf(ref instance, ref instance.FragmentIsBgra);
|
||||
ViewportInverseOffset = OffsetOf(ref instance, ref instance.ViewportInverse);
|
||||
FragmentRenderScaleCountOffset = OffsetOf(ref instance, ref instance.FragmentRenderScaleCount);
|
||||
GraphicsRenderScaleOffset = OffsetOf(ref instance, ref instance.RenderScale);
|
||||
ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize;
|
||||
@@ -47,6 +49,7 @@ namespace Ryujinx.Graphics.Shader
|
||||
|
||||
public Vector4<int> FragmentAlphaTest;
|
||||
public Array8<Vector4<int>> FragmentIsBgra;
|
||||
public Vector4<float> ViewportInverse;
|
||||
public Vector4<int> FragmentRenderScaleCount;
|
||||
|
||||
// Render scale max count: 1 + 32 + 8. First scale is fragment output scale, others are textures/image inputs.
|
||||
|
@@ -67,6 +67,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
public const int FragmentOutputIsBgraBase = 0x1000100;
|
||||
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 ThreadIdY = 0x2000004;
|
||||
public const int ThreadIdZ = 0x2000008;
|
||||
|
@@ -154,9 +154,56 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
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()
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
public ShaderStage Stage { get; }
|
||||
|
||||
public bool GpPassthrough { get; }
|
||||
public bool LastInVertexPipeline { get; private set; }
|
||||
|
||||
public int ThreadsPerInputPrimitive { get; }
|
||||
|
||||
@@ -135,6 +136,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
OmapSampleMask = header.OmapSampleMask;
|
||||
OmapDepth = header.OmapDepth;
|
||||
TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled();
|
||||
LastInVertexPipeline = header.Stage < ShaderStage.Fragment;
|
||||
}
|
||||
|
||||
public int GetDepthRegister()
|
||||
@@ -274,6 +276,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
NextInputAttributesPerPatchComponents = config.ThisInputAttributesPerPatchComponents;
|
||||
NextUsesFixedFuncAttributes = config.UsedFeatures.HasFlag(FeatureFlags.FixedFuncAttr);
|
||||
MergeOutputUserAttributes(config.UsedInputAttributes, config.UsedInputAttributesPerPatch);
|
||||
|
||||
if (config.Stage != ShaderStage.Fragment)
|
||||
{
|
||||
LastInVertexPipeline = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void MergeOutputUserAttributes(int mask, int maskPerPatch)
|
||||
|
@@ -15,7 +15,6 @@ using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types;
|
||||
using Ryujinx.HLE.HOS.Services.Sdb.Pdm.QueryService;
|
||||
using Ryujinx.HLE.HOS.Services.Sm;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
@@ -42,6 +41,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
||||
private int _notificationStorageChannelEventHandle;
|
||||
private int _healthWarningDisappearedSystemEventHandle;
|
||||
|
||||
private bool _gamePlayRecordingState;
|
||||
|
||||
private int _jitLoaded;
|
||||
|
||||
private HorizonClient _horizon;
|
||||
@@ -349,6 +350,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
||||
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+
|
||||
// InitializeGamePlayRecording(u64, handle<copy>)
|
||||
public ResultCode InitializeGamePlayRecording(ServiceCtx context)
|
||||
@@ -362,9 +372,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati
|
||||
// SetGamePlayRecordingState(u32)
|
||||
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;
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
private ControllerType[] _configuredTypes;
|
||||
private KEvent[] _styleSetUpdateEvents;
|
||||
private bool[] _supportedPlayers;
|
||||
private static HidVibrationValue _neutralVibrationValue = new HidVibrationValue
|
||||
private static VibrationValue _neutralVibrationValue = new VibrationValue
|
||||
{
|
||||
AmplitudeLow = 0f,
|
||||
FrequencyLow = 160f,
|
||||
@@ -33,8 +33,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
internal bool SixAxisActive = false; // TODO: link to hidserver when implemented
|
||||
internal ControllerType SupportedStyleSets { get; set; }
|
||||
|
||||
public Dictionary<PlayerIndex, ConcurrentQueue<(HidVibrationValue, HidVibrationValue)>> RumbleQueues = new Dictionary<PlayerIndex, ConcurrentQueue<(HidVibrationValue, HidVibrationValue)>>();
|
||||
public Dictionary<PlayerIndex, (HidVibrationValue, HidVibrationValue)> LastVibrationValues = new Dictionary<PlayerIndex, (HidVibrationValue, HidVibrationValue)>();
|
||||
public Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>> RumbleQueues = new Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>>();
|
||||
public Dictionary<PlayerIndex, (VibrationValue, VibrationValue)> LastVibrationValues = new Dictionary<PlayerIndex, (VibrationValue, VibrationValue)>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!dualVibrationValues.TryGetValue(1, out HidVibrationValue rightVibrationValue))
|
||||
if (!dualVibrationValues.TryGetValue(1, out VibrationValue rightVibrationValue))
|
||||
{
|
||||
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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -621,11 +621,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
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;
|
||||
}
|
||||
|
||||
|
@@ -35,5 +35,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer
|
||||
PlayerIndex.Unknown => NpadIdType.Unknown,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(index))
|
||||
};
|
||||
|
||||
public static bool IsValidNpadIdType(NpadIdType npadIdType)
|
||||
{
|
||||
return npadIdType <= NpadIdType.Player8 || npadIdType == NpadIdType.Handheld || npadIdType == NpadIdType.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum HidNpadJoyAssignmentMode
|
||||
{
|
||||
Dual,
|
||||
Single
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum HidNpadHandheldActivationMode
|
||||
public enum NpadHandheldActivationMode
|
||||
{
|
||||
Dual,
|
||||
Single,
|
@@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum HidNpadJoyDeviceType
|
||||
public enum NpadJoyDeviceType
|
||||
{
|
||||
Left,
|
||||
Right
|
@@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct HidAccelerometerParameters
|
||||
public struct AccelerometerParameters
|
||||
{
|
||||
public float X;
|
||||
public float Y;
|
@@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum HidGyroscopeZeroDriftMode
|
||||
public enum GyroscopeZeroDriftMode
|
||||
{
|
||||
Loose,
|
||||
Standard,
|
@@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct HidSensorFusionParameters
|
||||
public struct SensorFusionParameters
|
||||
{
|
||||
public float RevisePower;
|
||||
public float ReviseRange;
|
@@ -1,8 +0,0 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct HidVibrationDeviceValue
|
||||
{
|
||||
public HidVibrationDeviceType DeviceType;
|
||||
public HidVibrationDevicePosition Position;
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct HidVibrationDeviceHandle
|
||||
public struct VibrationDeviceHandle
|
||||
{
|
||||
public byte DeviceType;
|
||||
public byte PlayerId;
|
@@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum HidVibrationDevicePosition
|
||||
public enum VibrationDevicePosition
|
||||
{
|
||||
None,
|
||||
Left,
|
@@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public enum HidVibrationDeviceType
|
||||
public enum VibrationDeviceType
|
||||
{
|
||||
None,
|
||||
LinearResonantActuator,
|
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct VibrationDeviceValue
|
||||
{
|
||||
public VibrationDeviceType DeviceType;
|
||||
public VibrationDevicePosition Position;
|
||||
}
|
||||
}
|
@@ -1,9 +1,8 @@
|
||||
using Ryujinx.HLE.HOS.Tamper;
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
{
|
||||
public struct HidVibrationValue
|
||||
public struct VibrationValue
|
||||
{
|
||||
public float AmplitudeLow;
|
||||
public float FrequencyLow;
|
||||
@@ -12,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is HidVibrationValue value &&
|
||||
return obj is VibrationValue value &&
|
||||
AmplitudeLow == value.AmplitudeLow &&
|
||||
AmplitudeHigh == value.AmplitudeHigh;
|
||||
}
|
@@ -5,6 +5,7 @@ using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.HidServer;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types;
|
||||
using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
@@ -26,9 +27,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
private bool _isFirmwareUpdateAvailableForSixAxisSensor;
|
||||
private bool _isSixAxisSensorUnalteredPassthroughEnabled;
|
||||
|
||||
private HidNpadJoyAssignmentMode _npadJoyAssignmentMode;
|
||||
private HidNpadHandheldActivationMode _npadHandheldActivationMode;
|
||||
private HidGyroscopeZeroDriftMode _gyroscopeZeroDriftMode;
|
||||
private NpadHandheldActivationMode _npadHandheldActivationMode;
|
||||
private GyroscopeZeroDriftMode _gyroscopeZeroDriftMode;
|
||||
|
||||
private long _npadCommunicationMode;
|
||||
private uint _accelerometerPlayMode;
|
||||
@@ -37,22 +37,21 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
#pragma warning restore CS0649
|
||||
private float _sevenSixAxisSensorFusionStrength;
|
||||
|
||||
private HidSensorFusionParameters _sensorFusionParams;
|
||||
private HidAccelerometerParameters _accelerometerParams;
|
||||
private SensorFusionParameters _sensorFusionParams;
|
||||
private AccelerometerParameters _accelerometerParams;
|
||||
|
||||
public IHidServer(ServiceCtx context) : base(context.Device.System.HidServer)
|
||||
{
|
||||
_xpadIdEvent = new KEvent(context.Device.System.KernelContext);
|
||||
_palmaOperationCompleteEvent = new KEvent(context.Device.System.KernelContext);
|
||||
|
||||
_npadJoyAssignmentMode = HidNpadJoyAssignmentMode.Dual;
|
||||
_npadHandheldActivationMode = HidNpadHandheldActivationMode.Dual;
|
||||
_gyroscopeZeroDriftMode = HidGyroscopeZeroDriftMode.Standard;
|
||||
_npadHandheldActivationMode = NpadHandheldActivationMode.Dual;
|
||||
_gyroscopeZeroDriftMode = GyroscopeZeroDriftMode.Standard;
|
||||
|
||||
_isFirmwareUpdateAvailableForSixAxisSensor = false;
|
||||
|
||||
_sensorFusionParams = new HidSensorFusionParameters();
|
||||
_accelerometerParams = new HidAccelerometerParameters();
|
||||
_sensorFusionParams = new SensorFusionParameters();
|
||||
_accelerometerParams = new AccelerometerParameters();
|
||||
|
||||
// TODO: signal event at right place
|
||||
_xpadIdEvent.ReadableEvent.Signal();
|
||||
@@ -393,7 +392,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
int sixAxisSensorHandle = context.RequestData.ReadInt32();
|
||||
context.RequestData.BaseStream.Position += 4; // Padding
|
||||
|
||||
_sensorFusionParams = new HidSensorFusionParameters
|
||||
_sensorFusionParams = new SensorFusionParameters
|
||||
{
|
||||
RevisePower = context.RequestData.ReadInt32(),
|
||||
ReviseRange = context.RequestData.ReadInt32()
|
||||
@@ -445,7 +444,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
int sixAxisSensorHandle = context.RequestData.ReadInt32();
|
||||
context.RequestData.BaseStream.Position += 4; // Padding
|
||||
|
||||
_accelerometerParams = new HidAccelerometerParameters
|
||||
_accelerometerParams = new AccelerometerParameters
|
||||
{
|
||||
X = context.RequestData.ReadInt32(),
|
||||
Y = context.RequestData.ReadInt32()
|
||||
@@ -539,7 +538,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
public ResultCode SetGyroscopeZeroDriftMode(ServiceCtx context)
|
||||
{
|
||||
int sixAxisSensorHandle = context.RequestData.ReadInt32();
|
||||
_gyroscopeZeroDriftMode = (HidGyroscopeZeroDriftMode)context.RequestData.ReadInt32();
|
||||
_gyroscopeZeroDriftMode = (GyroscopeZeroDriftMode)context.RequestData.ReadInt32();
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
|
||||
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
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
|
||||
_gyroscopeZeroDriftMode = HidGyroscopeZeroDriftMode.Standard;
|
||||
_gyroscopeZeroDriftMode = GyroscopeZeroDriftMode.Standard;
|
||||
|
||||
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)
|
||||
public ResultCode SetNpadJoyAssignmentModeSingleByDefault(ServiceCtx context)
|
||||
{
|
||||
PlayerIndex hidControllerId = (PlayerIndex)context.RequestData.ReadInt32();
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32();
|
||||
context.RequestData.BaseStream.Position += 4; // Padding
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
|
||||
_npadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single;
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, hidControllerId, _npadJoyAssignmentMode });
|
||||
if (HidUtils.IsValidNpadIdType(npadIdType))
|
||||
{
|
||||
context.Device.Hid.SharedMemory.Npads[(int)HidUtils.GetIndexFromNpadIdType(npadIdType)].InternalState.JoyAssignmentMode = NpadJoyAssignmentMode.Single;
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandHipc(123)]
|
||||
// SetNpadJoyAssignmentModeSingle(uint HidControllerId, nn::applet::AppletResourceUserId, long HidNpadJoyDeviceType)
|
||||
// SetNpadJoyAssignmentModeSingle(uint npadIdType, nn::applet::AppletResourceUserId, uint npadJoyDeviceType)
|
||||
public ResultCode SetNpadJoyAssignmentModeSingle(ServiceCtx context)
|
||||
{
|
||||
PlayerIndex hidControllerId = (PlayerIndex)context.RequestData.ReadInt32();
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
HidNpadJoyDeviceType hidNpadJoyDeviceType = (HidNpadJoyDeviceType)context.RequestData.ReadInt64();
|
||||
NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32();
|
||||
context.RequestData.BaseStream.Position += 4; // Padding
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
NpadJoyDeviceType npadJoyDeviceType = (NpadJoyDeviceType)context.RequestData.ReadUInt32();
|
||||
|
||||
_npadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single;
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, hidControllerId, hidNpadJoyDeviceType, _npadJoyAssignmentMode });
|
||||
if (HidUtils.IsValidNpadIdType(npadIdType))
|
||||
{
|
||||
SetNpadJoyAssignmentModeSingleWithDestinationImpl(context, npadIdType, appletResourceUserId, npadJoyDeviceType, out _, out _);
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandHipc(124)]
|
||||
// SetNpadJoyAssignmentModeDual(uint HidControllerId, nn::applet::AppletResourceUserId)
|
||||
// SetNpadJoyAssignmentModeDual(uint npadIdType, nn::applet::AppletResourceUserId)
|
||||
public ResultCode SetNpadJoyAssignmentModeDual(ServiceCtx context)
|
||||
{
|
||||
PlayerIndex hidControllerId = HidUtils.GetIndexFromNpadIdType((NpadIdType)context.RequestData.ReadInt32());
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadUInt32();
|
||||
context.RequestData.BaseStream.Position += 4; // Padding
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
|
||||
_npadJoyAssignmentMode = HidNpadJoyAssignmentMode.Dual;
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, hidControllerId, _npadJoyAssignmentMode });
|
||||
if (HidUtils.IsValidNpadIdType(npadIdType))
|
||||
{
|
||||
context.Device.Hid.SharedMemory.Npads[(int)HidUtils.GetIndexFromNpadIdType(npadIdType)].InternalState.JoyAssignmentMode = NpadJoyAssignmentMode.Dual;
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandHipc(125)]
|
||||
// MergeSingleJoyAsDualJoy(uint SingleJoyId0, uint SingleJoyId1, nn::applet::AppletResourceUserId)
|
||||
// MergeSingleJoyAsDualJoy(uint npadIdType0, uint npadIdType1, nn::applet::AppletResourceUserId)
|
||||
public ResultCode MergeSingleJoyAsDualJoy(ServiceCtx context)
|
||||
{
|
||||
long singleJoyId0 = context.RequestData.ReadInt32();
|
||||
long singleJoyId1 = context.RequestData.ReadInt32();
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
NpadIdType npadIdType0 = (NpadIdType)context.RequestData.ReadUInt32();
|
||||
NpadIdType npadIdType1 = (NpadIdType)context.RequestData.ReadUInt32();
|
||||
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;
|
||||
}
|
||||
@@ -988,7 +996,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
public ResultCode SetNpadHandheldActivationMode(ServiceCtx context)
|
||||
{
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
_npadHandheldActivationMode = (HidNpadHandheldActivationMode)context.RequestData.ReadInt64();
|
||||
_npadHandheldActivationMode = (NpadHandheldActivationMode)context.RequestData.ReadInt64();
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, _npadHandheldActivationMode });
|
||||
|
||||
@@ -1049,37 +1057,47 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
}
|
||||
|
||||
[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)
|
||||
{
|
||||
PlayerIndex hidControllerId = (PlayerIndex)context.RequestData.ReadInt32();
|
||||
HidNpadJoyDeviceType hidNpadJoyDeviceType = (HidNpadJoyDeviceType)context.RequestData.ReadInt64();
|
||||
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||
NpadIdType npadIdType = (NpadIdType)context.RequestData.ReadInt32();
|
||||
NpadJoyDeviceType npadJoyDeviceType = (NpadJoyDeviceType)context.RequestData.ReadInt32();
|
||||
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
|
||||
context.ResponseData.Write(0); //Unknown1
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new {
|
||||
appletResourceUserId,
|
||||
hidControllerId,
|
||||
hidNpadJoyDeviceType,
|
||||
_npadJoyAssignmentMode,
|
||||
Unknown0 = 0,
|
||||
Unknown1 = 0
|
||||
});
|
||||
if (npadIdTypeIsSet)
|
||||
{
|
||||
context.ResponseData.Write(npadIdTypeIsSet);
|
||||
context.ResponseData.Write((uint)npadIdTypeSet);
|
||||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
// GetVibrationDeviceInfo(nn::hid::VibrationDeviceHandle) -> nn::hid::VibrationDeviceInfo
|
||||
public ResultCode GetVibrationDeviceInfo(ServiceCtx context)
|
||||
{
|
||||
HidVibrationDeviceHandle deviceHandle = context.RequestData.ReadStruct<HidVibrationDeviceHandle>();
|
||||
NpadStyleIndex deviceType = (NpadStyleIndex)deviceHandle.DeviceType;
|
||||
NpadIdType npadIdType = (NpadIdType)deviceHandle.PlayerId;
|
||||
VibrationDeviceHandle deviceHandle = context.RequestData.ReadStruct<VibrationDeviceHandle>();
|
||||
NpadStyleIndex deviceType = (NpadStyleIndex)deviceHandle.DeviceType;
|
||||
NpadIdType npadIdType = (NpadIdType)deviceHandle.PlayerId;
|
||||
|
||||
if (deviceType < NpadStyleIndex.System || deviceType >= NpadStyleIndex.FullKey)
|
||||
{
|
||||
@@ -1093,28 +1111,28 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
vibrationDevicePosition = HidVibrationDevicePosition.Left;
|
||||
vibrationDevicePosition = VibrationDevicePosition.Left;
|
||||
}
|
||||
else if (deviceHandle.Position == 1)
|
||||
{
|
||||
vibrationDevicePosition = HidVibrationDevicePosition.Right;
|
||||
vibrationDevicePosition = VibrationDevicePosition.Right;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1122,7 +1140,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
}
|
||||
}
|
||||
|
||||
HidVibrationDeviceValue deviceInfo = new HidVibrationDeviceValue
|
||||
VibrationDeviceValue deviceInfo = new VibrationDeviceValue
|
||||
{
|
||||
DeviceType = vibrationDeviceType,
|
||||
Position = vibrationDevicePosition
|
||||
@@ -1140,7 +1158,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
// SendVibrationValue(nn::hid::VibrationDeviceHandle, nn::hid::VibrationValue, nn::applet::AppletResourceUserId)
|
||||
public ResultCode SendVibrationValue(ServiceCtx context)
|
||||
{
|
||||
HidVibrationDeviceHandle deviceHandle = new HidVibrationDeviceHandle
|
||||
VibrationDeviceHandle deviceHandle = new VibrationDeviceHandle
|
||||
{
|
||||
DeviceType = context.RequestData.ReadByte(),
|
||||
PlayerId = context.RequestData.ReadByte(),
|
||||
@@ -1148,7 +1166,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
Reserved = context.RequestData.ReadByte()
|
||||
};
|
||||
|
||||
HidVibrationValue vibrationValue = new HidVibrationValue
|
||||
VibrationValue vibrationValue = new VibrationValue
|
||||
{
|
||||
AmplitudeLow = context.RequestData.ReadSingle(),
|
||||
FrequencyLow = context.RequestData.ReadSingle(),
|
||||
@@ -1158,7 +1176,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
|
||||
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;
|
||||
|
||||
@@ -1171,7 +1189,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
// GetActualVibrationValue(nn::hid::VibrationDeviceHandle, nn::applet::AppletResourceUserId) -> nn::hid::VibrationValue
|
||||
public ResultCode GetActualVibrationValue(ServiceCtx context)
|
||||
{
|
||||
HidVibrationDeviceHandle deviceHandle = new HidVibrationDeviceHandle
|
||||
VibrationDeviceHandle deviceHandle = new VibrationDeviceHandle
|
||||
{
|
||||
DeviceType = context.RequestData.ReadByte(),
|
||||
PlayerId = context.RequestData.ReadByte(),
|
||||
@@ -1181,7 +1199,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
|
||||
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.FrequencyLow);
|
||||
@@ -1234,12 +1252,12 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
|
||||
context.Memory.Read(context.Request.PtrBuff[1].Position, vibrationValueBuffer);
|
||||
|
||||
Span<HidVibrationDeviceHandle> deviceHandles = MemoryMarshal.Cast<byte, HidVibrationDeviceHandle>(vibrationDeviceHandleBuffer);
|
||||
Span<HidVibrationValue> vibrationValues = MemoryMarshal.Cast<byte, HidVibrationValue>(vibrationValueBuffer);
|
||||
Span<VibrationDeviceHandle> deviceHandles = MemoryMarshal.Cast<byte, VibrationDeviceHandle>(vibrationDeviceHandleBuffer);
|
||||
Span<VibrationValue> vibrationValues = MemoryMarshal.Cast<byte, VibrationValue>(vibrationValueBuffer);
|
||||
|
||||
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;
|
||||
|
||||
for (int deviceCounter = 0; deviceCounter < deviceHandles.Length; deviceCounter++)
|
||||
@@ -1250,7 +1268,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||
if (index != currentIndex || dualVibrationValues.Count == 2)
|
||||
{
|
||||
context.Device.Hid.Npads.UpdateRumbleQueue(currentIndex, dualVibrationValues);
|
||||
dualVibrationValues = new Dictionary<byte, HidVibrationValue>();
|
||||
dualVibrationValues = new Dictionary<byte, VibrationValue>();
|
||||
}
|
||||
|
||||
dualVibrationValues[position] = vibrationValues[deviceCounter];
|
||||
|
@@ -543,14 +543,14 @@ namespace Ryujinx.Input.HLE
|
||||
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)
|
||||
{
|
||||
HidVibrationValue leftVibrationValue = dualVibrationValue.Item1;
|
||||
HidVibrationValue rightVibrationValue = dualVibrationValue.Item2;
|
||||
VibrationValue leftVibrationValue = dualVibrationValue.Item1;
|
||||
VibrationValue rightVibrationValue = dualVibrationValue.Item2;
|
||||
|
||||
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));
|
||||
|
Reference in New Issue
Block a user