Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
79408b68c3 | |||
d461d4f68b | |||
b45d30acf8 | |||
df70442c46 | |||
e2ffa5a125 | |||
73feac5819 | |||
e5ad1dfa48 | |||
79becc4b78 | |||
223172ac0b | |||
8c9633d72f | |||
1f93fd52d9 | |||
aac7bbd378 | |||
bed516bfda |
@ -130,11 +130,6 @@ namespace ARMeilleure.Instructions
|
|||||||
bool ordered = (accType & AccessType.Ordered) != 0;
|
bool ordered = (accType & AccessType.Ordered) != 0;
|
||||||
bool exclusive = (accType & AccessType.Exclusive) != 0;
|
bool exclusive = (accType & AccessType.Exclusive) != 0;
|
||||||
|
|
||||||
if (ordered)
|
|
||||||
{
|
|
||||||
EmitBarrier(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand address = context.Copy(GetIntOrSP(context, op.Rn));
|
Operand address = context.Copy(GetIntOrSP(context, op.Rn));
|
||||||
|
|
||||||
Operand t = GetIntOrZR(context, op.Rt);
|
Operand t = GetIntOrZR(context, op.Rt);
|
||||||
@ -163,6 +158,11 @@ namespace ARMeilleure.Instructions
|
|||||||
{
|
{
|
||||||
EmitStoreExclusive(context, address, t, exclusive, op.Size, op.Rs, a32: false);
|
EmitStoreExclusive(context, address, t, exclusive, op.Size, op.Rs, a32: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ordered)
|
||||||
|
{
|
||||||
|
EmitBarrier(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void EmitBarrier(ArmEmitterContext context)
|
private static void EmitBarrier(ArmEmitterContext context)
|
||||||
|
@ -146,13 +146,13 @@ namespace ARMeilleure.Instructions
|
|||||||
var exclusive = (accType & AccessType.Exclusive) != 0;
|
var exclusive = (accType & AccessType.Exclusive) != 0;
|
||||||
var ordered = (accType & AccessType.Ordered) != 0;
|
var ordered = (accType & AccessType.Ordered) != 0;
|
||||||
|
|
||||||
if (ordered)
|
|
||||||
{
|
|
||||||
EmitBarrier(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((accType & AccessType.Load) != 0)
|
if ((accType & AccessType.Load) != 0)
|
||||||
{
|
{
|
||||||
|
if (ordered)
|
||||||
|
{
|
||||||
|
EmitBarrier(context);
|
||||||
|
}
|
||||||
|
|
||||||
if (size == DWordSizeLog2)
|
if (size == DWordSizeLog2)
|
||||||
{
|
{
|
||||||
// Keep loads atomic - make the call to get the whole region and then decompose it into parts
|
// Keep loads atomic - make the call to get the whole region and then decompose it into parts
|
||||||
@ -219,6 +219,11 @@ namespace ARMeilleure.Instructions
|
|||||||
Operand value = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt));
|
Operand value = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt));
|
||||||
EmitStoreExclusive(context, address, value, exclusive, size, op.Rd, a32: true);
|
EmitStoreExclusive(context, address, value, exclusive, size, op.Rd, a32: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ordered)
|
||||||
|
{
|
||||||
|
EmitBarrier(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
private const string OuterHeaderMagicString = "PTCohd\0\0";
|
||||||
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
private const string InnerHeaderMagicString = "PTCihd\0\0";
|
||||||
|
|
||||||
private const uint InternalVersion = 3179; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 3193; //! To be incremented manually for each change to the ARMeilleure project.
|
||||||
|
|
||||||
private const string ActualDir = "0";
|
private const string ActualDir = "0";
|
||||||
private const string BackupDir = "1";
|
private const string BackupDir = "1";
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
public Stick Joystick { get; set; }
|
public Stick Joystick { get; set; }
|
||||||
public bool InvertStickX { get; set; }
|
public bool InvertStickX { get; set; }
|
||||||
public bool InvertStickY { get; set; }
|
public bool InvertStickY { get; set; }
|
||||||
|
public bool Rotate90CW { get; set; }
|
||||||
public Button StickButton { get; set; }
|
public Button StickButton { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
ServiceNim,
|
ServiceNim,
|
||||||
ServiceNs,
|
ServiceNs,
|
||||||
ServiceNsd,
|
ServiceNsd,
|
||||||
|
ServiceNtc,
|
||||||
ServiceNv,
|
ServiceNv,
|
||||||
ServiceOlsc,
|
ServiceOlsc,
|
||||||
ServicePctl,
|
ServicePctl,
|
||||||
|
@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
R32G32B32A32Sint,
|
R32G32B32A32Sint,
|
||||||
S8Uint,
|
S8Uint,
|
||||||
D16Unorm,
|
D16Unorm,
|
||||||
D24X8Unorm,
|
S8UintD24Unorm,
|
||||||
D32Float,
|
D32Float,
|
||||||
D24UnormS8Uint,
|
D24UnormS8Uint,
|
||||||
D32FloatS8Uint,
|
D32FloatS8Uint,
|
||||||
@ -266,7 +266,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
{
|
{
|
||||||
case Format.D16Unorm:
|
case Format.D16Unorm:
|
||||||
case Format.D24UnormS8Uint:
|
case Format.D24UnormS8Uint:
|
||||||
case Format.D24X8Unorm:
|
case Format.S8UintD24Unorm:
|
||||||
case Format.D32Float:
|
case Format.D32Float:
|
||||||
case Format.D32FloatS8Uint:
|
case Format.D32FloatS8Uint:
|
||||||
case Format.S8Uint:
|
case Format.S8Uint:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
|
||||||
using Ryujinx.Graphics.Device;
|
using Ryujinx.Graphics.Device;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -330,11 +330,95 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
{
|
{
|
||||||
// TODO: Implement remap functionality.
|
// TODO: Implement remap functionality.
|
||||||
// Buffer to buffer copy.
|
// Buffer to buffer copy.
|
||||||
memoryManager.Physical.BufferCache.CopyBuffer(memoryManager, srcGpuVa, dstGpuVa, size);
|
|
||||||
|
bool srcIsPitchKind = memoryManager.GetKind(srcGpuVa).IsPitch();
|
||||||
|
bool dstIsPitchKind = memoryManager.GetKind(dstGpuVa).IsPitch();
|
||||||
|
|
||||||
|
if (!srcIsPitchKind && dstIsPitchKind)
|
||||||
|
{
|
||||||
|
CopyGobBlockLinearToLinear(memoryManager, srcGpuVa, dstGpuVa, size);
|
||||||
|
}
|
||||||
|
else if (srcIsPitchKind && !dstIsPitchKind)
|
||||||
|
{
|
||||||
|
CopyGobLinearToBlockLinear(memoryManager, srcGpuVa, dstGpuVa, size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memoryManager.Physical.BufferCache.CopyBuffer(memoryManager, srcGpuVa, dstGpuVa, size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copies block linear data with block linear GOBs to a block linear destination with linear GOBs.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">GPU memory manager</param>
|
||||||
|
/// <param name="srcGpuVa">Source GPU virtual address</param>
|
||||||
|
/// <param name="dstGpuVa">Destination GPU virtual address</param>
|
||||||
|
/// <param name="size">Size in bytes of the copy</param>
|
||||||
|
private static void CopyGobBlockLinearToLinear(MemoryManager memoryManager, ulong srcGpuVa, ulong dstGpuVa, ulong size)
|
||||||
|
{
|
||||||
|
if (((srcGpuVa | dstGpuVa | size) & 0xf) == 0)
|
||||||
|
{
|
||||||
|
for (ulong offset = 0; offset < size; offset += 16)
|
||||||
|
{
|
||||||
|
Vector128<byte> data = memoryManager.Read<Vector128<byte>>(ConvertGobLinearToBlockLinearAddress(srcGpuVa + offset), true);
|
||||||
|
memoryManager.Write(dstGpuVa + offset, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (ulong offset = 0; offset < size; offset++)
|
||||||
|
{
|
||||||
|
byte data = memoryManager.Read<byte>(ConvertGobLinearToBlockLinearAddress(srcGpuVa + offset), true);
|
||||||
|
memoryManager.Write(dstGpuVa + offset, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copies block linear data with linear GOBs to a block linear destination with block linear GOBs.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">GPU memory manager</param>
|
||||||
|
/// <param name="srcGpuVa">Source GPU virtual address</param>
|
||||||
|
/// <param name="dstGpuVa">Destination GPU virtual address</param>
|
||||||
|
/// <param name="size">Size in bytes of the copy</param>
|
||||||
|
private static void CopyGobLinearToBlockLinear(MemoryManager memoryManager, ulong srcGpuVa, ulong dstGpuVa, ulong size)
|
||||||
|
{
|
||||||
|
if (((srcGpuVa | dstGpuVa | size) & 0xf) == 0)
|
||||||
|
{
|
||||||
|
for (ulong offset = 0; offset < size; offset += 16)
|
||||||
|
{
|
||||||
|
Vector128<byte> data = memoryManager.Read<Vector128<byte>>(srcGpuVa + offset, true);
|
||||||
|
memoryManager.Write(ConvertGobLinearToBlockLinearAddress(dstGpuVa + offset), data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (ulong offset = 0; offset < size; offset++)
|
||||||
|
{
|
||||||
|
byte data = memoryManager.Read<byte>(srcGpuVa + offset, true);
|
||||||
|
memoryManager.Write(ConvertGobLinearToBlockLinearAddress(dstGpuVa + offset), data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the GOB block linear address from a linear address.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Linear address</param>
|
||||||
|
/// <returns>Block linear address</returns>
|
||||||
|
private static ulong ConvertGobLinearToBlockLinearAddress(ulong address)
|
||||||
|
{
|
||||||
|
// y2 y1 y0 x5 x4 x3 x2 x1 x0 -> x5 y2 y1 x4 y0 x3 x2 x1 x0
|
||||||
|
return (address & ~0x1f0UL) |
|
||||||
|
((address & 0x40) >> 2) |
|
||||||
|
((address & 0x10) << 1) |
|
||||||
|
((address & 0x180) >> 1) |
|
||||||
|
((address & 0x20) << 3);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs a buffer to buffer, or buffer to texture copy, then optionally releases a semaphore.
|
/// Performs a buffer to buffer, or buffer to texture copy, then optionally releases a semaphore.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -28,13 +28,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types
|
|||||||
{
|
{
|
||||||
return format switch
|
return format switch
|
||||||
{
|
{
|
||||||
ZetaFormat.D32Float => new FormatInfo(Format.D32Float, 1, 1, 4, 1),
|
ZetaFormat.D32Float => new FormatInfo(Format.D32Float, 1, 1, 4, 1),
|
||||||
ZetaFormat.D16Unorm => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1),
|
ZetaFormat.D16Unorm => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1),
|
||||||
ZetaFormat.D24UnormS8Uint => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
|
ZetaFormat.D24UnormS8Uint => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
|
||||||
ZetaFormat.D24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 1),
|
ZetaFormat.D24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 1),
|
||||||
ZetaFormat.S8UintD24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
|
ZetaFormat.S8UintD24Unorm => new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2),
|
||||||
ZetaFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1),
|
ZetaFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1),
|
||||||
ZetaFormat.D32FloatS8Uint => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2),
|
ZetaFormat.D32FloatS8Uint => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2),
|
||||||
_ => FormatInfo.Default
|
_ => FormatInfo.Default
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{ 0x24a0e, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
|
{ 0x24a0e, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
|
||||||
{ 0x24a29, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
|
{ 0x24a29, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
|
||||||
{ 0x48a29, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
|
{ 0x48a29, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
|
||||||
|
{ 0x4912b, new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2) },
|
||||||
{ 0x25385, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
|
{ 0x25385, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
|
||||||
{ 0x253b0, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
|
{ 0x253b0, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
|
||||||
{ 0xa4908, new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4) },
|
{ 0xa4908, new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4) },
|
||||||
|
@ -7,7 +7,6 @@ using Ryujinx.Graphics.Gpu.Engine.Types;
|
|||||||
using Ryujinx.Graphics.Gpu.Image;
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
using Ryujinx.Memory;
|
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -203,7 +203,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((lhs.FormatInfo.Format == Format.D24UnormS8Uint ||
|
if ((lhs.FormatInfo.Format == Format.D24UnormS8Uint ||
|
||||||
lhs.FormatInfo.Format == Format.D24X8Unorm) && rhs.FormatInfo.Format == Format.B8G8R8A8Unorm)
|
lhs.FormatInfo.Format == Format.S8UintD24Unorm) && rhs.FormatInfo.Format == Format.B8G8R8A8Unorm)
|
||||||
{
|
{
|
||||||
return TextureMatchQuality.FormatAlias;
|
return TextureMatchQuality.FormatAlias;
|
||||||
}
|
}
|
||||||
|
@ -362,7 +362,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return DepthStencilMode.Depth;
|
return DepthStencilMode.Depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format == Format.D24X8Unorm || format == Format.D24UnormS8Uint)
|
if (format == Format.D24UnormS8Uint)
|
||||||
{
|
{
|
||||||
return component == SwizzleComponent.Red
|
return component == SwizzleComponent.Red
|
||||||
? DepthStencilMode.Stencil
|
? DepthStencilMode.Stencil
|
||||||
|
@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
private const ulong BufferAlignmentSize = 0x1000;
|
private const ulong BufferAlignmentSize = 0x1000;
|
||||||
private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
|
private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
|
||||||
|
|
||||||
|
private const ulong MaxDynamicGrowthSize = 0x100000;
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
private readonly PhysicalMemory _physicalMemory;
|
private readonly PhysicalMemory _physicalMemory;
|
||||||
|
|
||||||
@ -166,10 +168,35 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
// Otherwise, we must delete the overlapping buffers and create a bigger buffer
|
// Otherwise, we must delete the overlapping buffers and create a bigger buffer
|
||||||
// that fits all the data we need. We also need to copy the contents from the
|
// that fits all the data we need. We also need to copy the contents from the
|
||||||
// old buffer(s) to the new buffer.
|
// old buffer(s) to the new buffer.
|
||||||
|
|
||||||
ulong endAddress = address + size;
|
ulong endAddress = address + size;
|
||||||
|
|
||||||
if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress)
|
if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress)
|
||||||
{
|
{
|
||||||
|
// Check if the following conditions are met:
|
||||||
|
// - We have a single overlap.
|
||||||
|
// - The overlap starts at or before the requested range. That is, the overlap happens at the end.
|
||||||
|
// - The size delta between the new, merged buffer and the old one is of at most 2 pages.
|
||||||
|
// In this case, we attempt to extend the buffer further than the requested range,
|
||||||
|
// this can potentially avoid future resizes if the application keeps using overlapping
|
||||||
|
// sequential memory.
|
||||||
|
// Allowing for 2 pages (rather than just one) is necessary to catch cases where the
|
||||||
|
// range crosses a page, and after alignment, ends having a size of 2 pages.
|
||||||
|
if (overlapsCount == 1 &&
|
||||||
|
address >= _bufferOverlaps[0].Address &&
|
||||||
|
endAddress - _bufferOverlaps[0].EndAddress <= BufferAlignmentSize * 2)
|
||||||
|
{
|
||||||
|
// Try to grow the buffer by 1.5x of its current size.
|
||||||
|
// This improves performance in the cases where the buffer is resized often by small amounts.
|
||||||
|
ulong existingSize = _bufferOverlaps[0].Size;
|
||||||
|
ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask;
|
||||||
|
|
||||||
|
size = Math.Max(size, growthSize);
|
||||||
|
endAddress = address + size;
|
||||||
|
|
||||||
|
overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps);
|
||||||
|
}
|
||||||
|
|
||||||
for (int index = 0; index < overlapsCount; index++)
|
for (int index = 0; index < overlapsCount; index++)
|
||||||
{
|
{
|
||||||
Buffer buffer = _bufferOverlaps[index];
|
Buffer buffer = _bufferOverlaps[index];
|
||||||
@ -183,7 +210,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer newBuffer = new Buffer(_context, _physicalMemory, address, endAddress - address, _bufferOverlaps.Take(overlapsCount));
|
ulong newSize = endAddress - address;
|
||||||
|
|
||||||
|
Buffer newBuffer = new Buffer(_context, _physicalMemory, address, newSize, _bufferOverlaps.Take(overlapsCount));
|
||||||
|
|
||||||
lock (_buffers)
|
lock (_buffers)
|
||||||
{
|
{
|
||||||
@ -202,7 +231,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
buffer.DisposeData();
|
buffer.DisposeData();
|
||||||
}
|
}
|
||||||
|
|
||||||
newBuffer.SynchronizeMemory(address, endAddress - address);
|
newBuffer.SynchronizeMemory(address, newSize);
|
||||||
|
|
||||||
// Existing buffers were modified, we need to rebind everything.
|
// Existing buffers were modified, we need to rebind everything.
|
||||||
NotifyBuffersModified?.Invoke();
|
NotifyBuffersModified?.Invoke();
|
||||||
|
@ -264,7 +264,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="pa">CPU virtual address to map into</param>
|
/// <param name="pa">CPU virtual address to map into</param>
|
||||||
/// <param name="va">GPU virtual address to be mapped</param>
|
/// <param name="va">GPU virtual address to be mapped</param>
|
||||||
/// <param name="size">Size in bytes of the mapping</param>
|
/// <param name="size">Size in bytes of the mapping</param>
|
||||||
public void Map(ulong pa, ulong va, ulong size)
|
/// <param name="kind">Kind of the resource located at the mapping</param>
|
||||||
|
public void Map(ulong pa, ulong va, ulong size, PteKind kind)
|
||||||
{
|
{
|
||||||
lock (_pageTable)
|
lock (_pageTable)
|
||||||
{
|
{
|
||||||
@ -272,7 +273,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
for (ulong offset = 0; offset < size; offset += PageSize)
|
for (ulong offset = 0; offset < size; offset += PageSize)
|
||||||
{
|
{
|
||||||
SetPte(va + offset, pa + offset);
|
SetPte(va + offset, PackPte(pa + offset, kind));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -462,14 +463,37 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return PteUnmapped;
|
return PteUnmapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong baseAddress = GetPte(va);
|
ulong pte = GetPte(va);
|
||||||
|
|
||||||
if (baseAddress == PteUnmapped)
|
if (pte == PteUnmapped)
|
||||||
{
|
{
|
||||||
return PteUnmapped;
|
return PteUnmapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
return baseAddress + (va & PageMask);
|
return UnpackPaFromPte(pte) + (va & PageMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the kind of a given memory page.
|
||||||
|
/// This might indicate the type of resource that can be allocated on the page, and also texture tiling.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="va">GPU virtual address</param>
|
||||||
|
/// <returns>Kind of the memory page</returns>
|
||||||
|
public PteKind GetKind(ulong va)
|
||||||
|
{
|
||||||
|
if (!ValidateAddress(va))
|
||||||
|
{
|
||||||
|
return PteKind.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong pte = GetPte(va);
|
||||||
|
|
||||||
|
if (pte == PteUnmapped)
|
||||||
|
{
|
||||||
|
return PteKind.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UnpackKindFromPte(pte);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -512,5 +536,36 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
_pageTable[l0][l1] = pte;
|
_pageTable[l0][l1] = pte;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a page table entry from a physical address and kind.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pa">Physical address</param>
|
||||||
|
/// <param name="kind">Kind</param>
|
||||||
|
/// <returns>Page table entry</returns>
|
||||||
|
private static ulong PackPte(ulong pa, PteKind kind)
|
||||||
|
{
|
||||||
|
return pa | ((ulong)kind << 56);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unpacks kind from a page table entry.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pte">Page table entry</param>
|
||||||
|
/// <returns>Kind</returns>
|
||||||
|
private static PteKind UnpackKindFromPte(ulong pte)
|
||||||
|
{
|
||||||
|
return (PteKind)(pte >> 56);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unpacks physical address from a page table entry.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pte">Page table entry</param>
|
||||||
|
/// <returns>Physical address</returns>
|
||||||
|
private static ulong UnpackPaFromPte(ulong pte)
|
||||||
|
{
|
||||||
|
return pte & 0xffffffffffffffUL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
268
Ryujinx.Graphics.Gpu/Memory/PteKind.cs
Normal file
268
Ryujinx.Graphics.Gpu/Memory/PteKind.cs
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Kind of the resource at the given memory mapping.
|
||||||
|
/// </summary>
|
||||||
|
public enum PteKind : byte
|
||||||
|
{
|
||||||
|
Invalid = 0xff,
|
||||||
|
Pitch = 0x00,
|
||||||
|
Z16 = 0x01,
|
||||||
|
Z162C = 0x02,
|
||||||
|
Z16MS22C = 0x03,
|
||||||
|
Z16MS42C = 0x04,
|
||||||
|
Z16MS82C = 0x05,
|
||||||
|
Z16MS162C = 0x06,
|
||||||
|
Z162Z = 0x07,
|
||||||
|
Z16MS22Z = 0x08,
|
||||||
|
Z16MS42Z = 0x09,
|
||||||
|
Z16MS82Z = 0x0a,
|
||||||
|
Z16MS162Z = 0x0b,
|
||||||
|
Z162CZ = 0x36,
|
||||||
|
Z16MS22CZ = 0x37,
|
||||||
|
Z16MS42CZ = 0x38,
|
||||||
|
Z16MS82CZ = 0x39,
|
||||||
|
Z16MS162CZ = 0x5f,
|
||||||
|
Z164CZ = 0x0c,
|
||||||
|
Z16MS24CZ = 0x0d,
|
||||||
|
Z16MS44CZ = 0x0e,
|
||||||
|
Z16MS84CZ = 0x0f,
|
||||||
|
Z16MS164CZ = 0x10,
|
||||||
|
S8Z24 = 0x11,
|
||||||
|
S8Z241Z = 0x12,
|
||||||
|
S8Z24MS21Z = 0x13,
|
||||||
|
S8Z24MS41Z = 0x14,
|
||||||
|
S8Z24MS81Z = 0x15,
|
||||||
|
S8Z24MS161Z = 0x16,
|
||||||
|
S8Z242CZ = 0x17,
|
||||||
|
S8Z24MS22CZ = 0x18,
|
||||||
|
S8Z24MS42CZ = 0x19,
|
||||||
|
S8Z24MS82CZ = 0x1a,
|
||||||
|
S8Z24MS162CZ = 0x1b,
|
||||||
|
S8Z242CS = 0x1c,
|
||||||
|
S8Z24MS22CS = 0x1d,
|
||||||
|
S8Z24MS42CS = 0x1e,
|
||||||
|
S8Z24MS82CS = 0x1f,
|
||||||
|
S8Z24MS162CS = 0x20,
|
||||||
|
S8Z244CSZV = 0x21,
|
||||||
|
S8Z24MS24CSZV = 0x22,
|
||||||
|
S8Z24MS44CSZV = 0x23,
|
||||||
|
S8Z24MS84CSZV = 0x24,
|
||||||
|
S8Z24MS164CSZV = 0x25,
|
||||||
|
V8Z24MS4VC12 = 0x26,
|
||||||
|
V8Z24MS4VC4 = 0x27,
|
||||||
|
V8Z24MS8VC8 = 0x28,
|
||||||
|
V8Z24MS8VC24 = 0x29,
|
||||||
|
V8Z24MS4VC121ZV = 0x2e,
|
||||||
|
V8Z24MS4VC41ZV = 0x2f,
|
||||||
|
V8Z24MS8VC81ZV = 0x30,
|
||||||
|
V8Z24MS8VC241ZV = 0x31,
|
||||||
|
V8Z24MS4VC122CS = 0x32,
|
||||||
|
V8Z24MS4VC42CS = 0x33,
|
||||||
|
V8Z24MS8VC82CS = 0x34,
|
||||||
|
V8Z24MS8VC242CS = 0x35,
|
||||||
|
V8Z24MS4VC122CZV = 0x3a,
|
||||||
|
V8Z24MS4VC42CZV = 0x3b,
|
||||||
|
V8Z24MS8VC82CZV = 0x3c,
|
||||||
|
V8Z24MS8VC242CZV = 0x3d,
|
||||||
|
V8Z24MS4VC122ZV = 0x3e,
|
||||||
|
V8Z24MS4VC42ZV = 0x3f,
|
||||||
|
V8Z24MS8VC82ZV = 0x40,
|
||||||
|
V8Z24MS8VC242ZV = 0x41,
|
||||||
|
V8Z24MS4VC124CSZV = 0x42,
|
||||||
|
V8Z24MS4VC44CSZV = 0x43,
|
||||||
|
V8Z24MS8VC84CSZV = 0x44,
|
||||||
|
V8Z24MS8VC244CSZV = 0x45,
|
||||||
|
Z24S8 = 0x46,
|
||||||
|
Z24S81Z = 0x47,
|
||||||
|
Z24S8MS21Z = 0x48,
|
||||||
|
Z24S8MS41Z = 0x49,
|
||||||
|
Z24S8MS81Z = 0x4a,
|
||||||
|
Z24S8MS161Z = 0x4b,
|
||||||
|
Z24S82CS = 0x4c,
|
||||||
|
Z24S8MS22CS = 0x4d,
|
||||||
|
Z24S8MS42CS = 0x4e,
|
||||||
|
Z24S8MS82CS = 0x4f,
|
||||||
|
Z24S8MS162CS = 0x50,
|
||||||
|
Z24S82CZ = 0x51,
|
||||||
|
Z24S8MS22CZ = 0x52,
|
||||||
|
Z24S8MS42CZ = 0x53,
|
||||||
|
Z24S8MS82CZ = 0x54,
|
||||||
|
Z24S8MS162CZ = 0x55,
|
||||||
|
Z24S84CSZV = 0x56,
|
||||||
|
Z24S8MS24CSZV = 0x57,
|
||||||
|
Z24S8MS44CSZV = 0x58,
|
||||||
|
Z24S8MS84CSZV = 0x59,
|
||||||
|
Z24S8MS164CSZV = 0x5a,
|
||||||
|
Z24V8MS4VC12 = 0x5b,
|
||||||
|
Z24V8MS4VC4 = 0x5c,
|
||||||
|
Z24V8MS8VC8 = 0x5d,
|
||||||
|
Z24V8MS8VC24 = 0x5e,
|
||||||
|
YUVB8C12Y = 0x60,
|
||||||
|
YUVB8C22Y = 0x61,
|
||||||
|
YUVB10C12Y = 0x62,
|
||||||
|
YUVB10C22Y = 0x6b,
|
||||||
|
YUVB12C12Y = 0x6c,
|
||||||
|
YUVB12C22Y = 0x6d,
|
||||||
|
Z24V8MS4VC121ZV = 0x63,
|
||||||
|
Z24V8MS4VC41ZV = 0x64,
|
||||||
|
Z24V8MS8VC81ZV = 0x65,
|
||||||
|
Z24V8MS8VC241ZV = 0x66,
|
||||||
|
Z24V8MS4VC122CS = 0x67,
|
||||||
|
Z24V8MS4VC42CS = 0x68,
|
||||||
|
Z24V8MS8VC82CS = 0x69,
|
||||||
|
Z24V8MS8VC242CS = 0x6a,
|
||||||
|
Z24V8MS4VC122CZV = 0x6f,
|
||||||
|
Z24V8MS4VC42CZV = 0x70,
|
||||||
|
Z24V8MS8VC82CZV = 0x71,
|
||||||
|
Z24V8MS8VC242CZV = 0x72,
|
||||||
|
Z24V8MS4VC122ZV = 0x73,
|
||||||
|
Z24V8MS4VC42ZV = 0x74,
|
||||||
|
Z24V8MS8VC82ZV = 0x75,
|
||||||
|
Z24V8MS8VC242ZV = 0x76,
|
||||||
|
Z24V8MS4VC124CSZV = 0x77,
|
||||||
|
Z24V8MS4VC44CSZV = 0x78,
|
||||||
|
Z24V8MS8VC84CSZV = 0x79,
|
||||||
|
Z24V8MS8VC244CSZV = 0x7a,
|
||||||
|
ZF32 = 0x7b,
|
||||||
|
ZF321Z = 0x7c,
|
||||||
|
ZF32MS21Z = 0x7d,
|
||||||
|
ZF32MS41Z = 0x7e,
|
||||||
|
ZF32MS81Z = 0x7f,
|
||||||
|
ZF32MS161Z = 0x80,
|
||||||
|
ZF322CS = 0x81,
|
||||||
|
ZF32MS22CS = 0x82,
|
||||||
|
ZF32MS42CS = 0x83,
|
||||||
|
ZF32MS82CS = 0x84,
|
||||||
|
ZF32MS162CS = 0x85,
|
||||||
|
ZF322CZ = 0x86,
|
||||||
|
ZF32MS22CZ = 0x87,
|
||||||
|
ZF32MS42CZ = 0x88,
|
||||||
|
ZF32MS82CZ = 0x89,
|
||||||
|
ZF32MS162CZ = 0x8a,
|
||||||
|
X8Z24X16V8S8MS4VC12 = 0x8b,
|
||||||
|
X8Z24X16V8S8MS4VC4 = 0x8c,
|
||||||
|
X8Z24X16V8S8MS8VC8 = 0x8d,
|
||||||
|
X8Z24X16V8S8MS8VC24 = 0x8e,
|
||||||
|
X8Z24X16V8S8MS4VC121CS = 0x8f,
|
||||||
|
X8Z24X16V8S8MS4VC41CS = 0x90,
|
||||||
|
X8Z24X16V8S8MS8VC81CS = 0x91,
|
||||||
|
X8Z24X16V8S8MS8VC241CS = 0x92,
|
||||||
|
X8Z24X16V8S8MS4VC121ZV = 0x97,
|
||||||
|
X8Z24X16V8S8MS4VC41ZV = 0x98,
|
||||||
|
X8Z24X16V8S8MS8VC81ZV = 0x99,
|
||||||
|
X8Z24X16V8S8MS8VC241ZV = 0x9a,
|
||||||
|
X8Z24X16V8S8MS4VC121CZV = 0x9b,
|
||||||
|
X8Z24X16V8S8MS4VC41CZV = 0x9c,
|
||||||
|
X8Z24X16V8S8MS8VC81CZV = 0x9d,
|
||||||
|
X8Z24X16V8S8MS8VC241CZV = 0x9e,
|
||||||
|
X8Z24X16V8S8MS4VC122CS = 0x9f,
|
||||||
|
X8Z24X16V8S8MS4VC42CS = 0xa0,
|
||||||
|
X8Z24X16V8S8MS8VC82CS = 0xa1,
|
||||||
|
X8Z24X16V8S8MS8VC242CS = 0xa2,
|
||||||
|
X8Z24X16V8S8MS4VC122CSZV = 0xa3,
|
||||||
|
X8Z24X16V8S8MS4VC42CSZV = 0xa4,
|
||||||
|
X8Z24X16V8S8MS8VC82CSZV = 0xa5,
|
||||||
|
X8Z24X16V8S8MS8VC242CSZV = 0xa6,
|
||||||
|
ZF32X16V8S8MS4VC12 = 0xa7,
|
||||||
|
ZF32X16V8S8MS4VC4 = 0xa8,
|
||||||
|
ZF32X16V8S8MS8VC8 = 0xa9,
|
||||||
|
ZF32X16V8S8MS8VC24 = 0xaa,
|
||||||
|
ZF32X16V8S8MS4VC121CS = 0xab,
|
||||||
|
ZF32X16V8S8MS4VC41CS = 0xac,
|
||||||
|
ZF32X16V8S8MS8VC81CS = 0xad,
|
||||||
|
ZF32X16V8S8MS8VC241CS = 0xae,
|
||||||
|
ZF32X16V8S8MS4VC121ZV = 0xb3,
|
||||||
|
ZF32X16V8S8MS4VC41ZV = 0xb4,
|
||||||
|
ZF32X16V8S8MS8VC81ZV = 0xb5,
|
||||||
|
ZF32X16V8S8MS8VC241ZV = 0xb6,
|
||||||
|
ZF32X16V8S8MS4VC121CZV = 0xb7,
|
||||||
|
ZF32X16V8S8MS4VC41CZV = 0xb8,
|
||||||
|
ZF32X16V8S8MS8VC81CZV = 0xb9,
|
||||||
|
ZF32X16V8S8MS8VC241CZV = 0xba,
|
||||||
|
ZF32X16V8S8MS4VC122CS = 0xbb,
|
||||||
|
ZF32X16V8S8MS4VC42CS = 0xbc,
|
||||||
|
ZF32X16V8S8MS8VC82CS = 0xbd,
|
||||||
|
ZF32X16V8S8MS8VC242CS = 0xbe,
|
||||||
|
ZF32X16V8S8MS4VC122CSZV = 0xbf,
|
||||||
|
ZF32X16V8S8MS4VC42CSZV = 0xc0,
|
||||||
|
ZF32X16V8S8MS8VC82CSZV = 0xc1,
|
||||||
|
ZF32X16V8S8MS8VC242CSZV = 0xc2,
|
||||||
|
ZF32X24S8 = 0xc3,
|
||||||
|
ZF32X24S81CS = 0xc4,
|
||||||
|
ZF32X24S8MS21CS = 0xc5,
|
||||||
|
ZF32X24S8MS41CS = 0xc6,
|
||||||
|
ZF32X24S8MS81CS = 0xc7,
|
||||||
|
ZF32X24S8MS161CS = 0xc8,
|
||||||
|
ZF32X24S82CSZV = 0xce,
|
||||||
|
ZF32X24S8MS22CSZV = 0xcf,
|
||||||
|
ZF32X24S8MS42CSZV = 0xd0,
|
||||||
|
ZF32X24S8MS82CSZV = 0xd1,
|
||||||
|
ZF32X24S8MS162CSZV = 0xd2,
|
||||||
|
ZF32X24S82CS = 0xd3,
|
||||||
|
ZF32X24S8MS22CS = 0xd4,
|
||||||
|
ZF32X24S8MS42CS = 0xd5,
|
||||||
|
ZF32X24S8MS82CS = 0xd6,
|
||||||
|
ZF32X24S8MS162CS = 0xd7,
|
||||||
|
S8 = 0x2a,
|
||||||
|
S82S = 0x2b,
|
||||||
|
Generic16Bx2 = 0xfe,
|
||||||
|
C322C = 0xd8,
|
||||||
|
C322CBR = 0xd9,
|
||||||
|
C322CBA = 0xda,
|
||||||
|
C322CRA = 0xdb,
|
||||||
|
C322BRA = 0xdc,
|
||||||
|
C32MS22C = 0xdd,
|
||||||
|
C32MS22CBR = 0xde,
|
||||||
|
C32MS24CBRA = 0xcc,
|
||||||
|
C32MS42C = 0xdf,
|
||||||
|
C32MS42CBR = 0xe0,
|
||||||
|
C32MS42CBA = 0xe1,
|
||||||
|
C32MS42CRA = 0xe2,
|
||||||
|
C32MS42BRA = 0xe3,
|
||||||
|
C32MS44CBRA = 0x2c,
|
||||||
|
C32MS8MS162C = 0xe4,
|
||||||
|
C32MS8MS162CRA = 0xe5,
|
||||||
|
C642C = 0xe6,
|
||||||
|
C642CBR = 0xe7,
|
||||||
|
C642CBA = 0xe8,
|
||||||
|
C642CRA = 0xe9,
|
||||||
|
C642BRA = 0xea,
|
||||||
|
C64MS22C = 0xeb,
|
||||||
|
C64MS22CBR = 0xec,
|
||||||
|
C64MS24CBRA = 0xcd,
|
||||||
|
C64MS42C = 0xed,
|
||||||
|
C64MS42CBR = 0xee,
|
||||||
|
C64MS42CBA = 0xef,
|
||||||
|
C64MS42CRA = 0xf0,
|
||||||
|
C64MS42BRA = 0xf1,
|
||||||
|
C64MS44CBRA = 0x2d,
|
||||||
|
C64MS8MS162C = 0xf2,
|
||||||
|
C64MS8MS162CRA = 0xf3,
|
||||||
|
C1282C = 0xf4,
|
||||||
|
C1282CR = 0xf5,
|
||||||
|
C128MS22C = 0xf6,
|
||||||
|
C128MS22CR = 0xf7,
|
||||||
|
C128MS42C = 0xf8,
|
||||||
|
C128MS42CR = 0xf9,
|
||||||
|
C128MS8MS162C = 0xfa,
|
||||||
|
C128MS8MS162CR = 0xfb,
|
||||||
|
X8C24 = 0xfc,
|
||||||
|
PitchNoSwizzle = 0xfd,
|
||||||
|
SmSkedMessage = 0xca,
|
||||||
|
SmHostMessage = 0xcb
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PteKindExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the kind is pitch.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="kind">Kind to check</param>
|
||||||
|
/// <returns>True if pitch, false otherwise</returns>
|
||||||
|
public static bool IsPitch(this PteKind kind)
|
||||||
|
{
|
||||||
|
return kind == PteKind.Pitch || kind == PteKind.PitchNoSwizzle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +0,0 @@
|
|||||||
namespace Ryujinx.Graphics.Gpu.Memory
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Name of a GPU resource.
|
|
||||||
/// </summary>
|
|
||||||
public enum ResourceName
|
|
||||||
{
|
|
||||||
Buffer,
|
|
||||||
Texture,
|
|
||||||
TexturePool,
|
|
||||||
SamplerPool
|
|
||||||
}
|
|
||||||
}
|
|
@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Nvdec.Image
|
|||||||
int width = surface.Width;
|
int width = surface.Width;
|
||||||
int height = surface.Height;
|
int height = surface.Height;
|
||||||
int stride = surface.Stride;
|
int stride = surface.Stride;
|
||||||
|
|
||||||
ReadOnlySpan<byte> luma = gmm.DeviceGetSpan(lumaOffset, GetBlockLinearSize(width, height, 1));
|
ReadOnlySpan<byte> luma = gmm.DeviceGetSpan(lumaOffset, GetBlockLinearSize(width, height, 1));
|
||||||
|
|
||||||
ReadLuma(surface.YPlane.AsSpan(), luma, stride, width, height);
|
ReadLuma(surface.YPlane.AsSpan(), luma, stride, width, height);
|
||||||
|
@ -6,15 +6,15 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
{
|
{
|
||||||
struct FormatTable
|
struct FormatTable
|
||||||
{
|
{
|
||||||
private static FormatInfo[] Table;
|
private static FormatInfo[] _table;
|
||||||
private static SizedInternalFormat[] TableImage;
|
private static SizedInternalFormat[] _tableImage;
|
||||||
|
|
||||||
static FormatTable()
|
static FormatTable()
|
||||||
{
|
{
|
||||||
int tableSize = Enum.GetNames<Format>().Length;
|
int tableSize = Enum.GetNames<Format>().Length;
|
||||||
|
|
||||||
Table = new FormatInfo[tableSize];
|
_table = new FormatInfo[tableSize];
|
||||||
TableImage = new SizedInternalFormat[tableSize];
|
_tableImage = new SizedInternalFormat[tableSize];
|
||||||
|
|
||||||
Add(Format.R8Unorm, new FormatInfo(1, true, false, All.R8, PixelFormat.Red, PixelType.UnsignedByte));
|
Add(Format.R8Unorm, new FormatInfo(1, true, false, All.R8, PixelFormat.Red, PixelType.UnsignedByte));
|
||||||
Add(Format.R8Snorm, new FormatInfo(1, true, false, All.R8Snorm, PixelFormat.Red, PixelType.Byte));
|
Add(Format.R8Snorm, new FormatInfo(1, true, false, All.R8Snorm, PixelFormat.Red, PixelType.Byte));
|
||||||
@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
Add(Format.R32G32B32A32Sint, new FormatInfo(4, false, false, All.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int));
|
Add(Format.R32G32B32A32Sint, new FormatInfo(4, false, false, All.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int));
|
||||||
Add(Format.S8Uint, new FormatInfo(1, false, false, All.StencilIndex8, PixelFormat.StencilIndex, PixelType.UnsignedByte));
|
Add(Format.S8Uint, new FormatInfo(1, false, false, All.StencilIndex8, PixelFormat.StencilIndex, PixelType.UnsignedByte));
|
||||||
Add(Format.D16Unorm, new FormatInfo(1, false, false, All.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort));
|
Add(Format.D16Unorm, new FormatInfo(1, false, false, All.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort));
|
||||||
Add(Format.D24X8Unorm, new FormatInfo(1, false, false, All.DepthComponent24, PixelFormat.DepthComponent, PixelType.UnsignedInt));
|
Add(Format.S8UintD24Unorm, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248));
|
||||||
Add(Format.D32Float, new FormatInfo(1, false, false, All.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float));
|
Add(Format.D32Float, new FormatInfo(1, false, false, All.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float));
|
||||||
Add(Format.D24UnormS8Uint, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248));
|
Add(Format.D24UnormS8Uint, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248));
|
||||||
Add(Format.D32FloatS8Uint, new FormatInfo(1, false, false, All.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev));
|
Add(Format.D32FloatS8Uint, new FormatInfo(1, false, false, All.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev));
|
||||||
@ -218,22 +218,22 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
|
|
||||||
private static void Add(Format format, FormatInfo info)
|
private static void Add(Format format, FormatInfo info)
|
||||||
{
|
{
|
||||||
Table[(int)format] = info;
|
_table[(int)format] = info;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Add(Format format, SizedInternalFormat sif)
|
private static void Add(Format format, SizedInternalFormat sif)
|
||||||
{
|
{
|
||||||
TableImage[(int)format] = sif;
|
_tableImage[(int)format] = sif;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FormatInfo GetFormatInfo(Format format)
|
public static FormatInfo GetFormatInfo(Format format)
|
||||||
{
|
{
|
||||||
return Table[(int)format];
|
return _table[(int)format];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SizedInternalFormat GetImageFormat(Format format)
|
public static SizedInternalFormat GetImageFormat(Format format)
|
||||||
{
|
{
|
||||||
return TableImage[(int)format];
|
return _tableImage[(int)format];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,14 +127,13 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
private static bool IsPackedDepthStencilFormat(Format format)
|
private static bool IsPackedDepthStencilFormat(Format format)
|
||||||
{
|
{
|
||||||
return format == Format.D24UnormS8Uint ||
|
return format == Format.D24UnormS8Uint ||
|
||||||
format == Format.D32FloatS8Uint;
|
format == Format.D32FloatS8Uint ||
|
||||||
|
format == Format.S8UintD24Unorm;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsDepthOnlyFormat(Format format)
|
private static bool IsDepthOnlyFormat(Format format)
|
||||||
{
|
{
|
||||||
return format == Format.D16Unorm ||
|
return format == Format.D16Unorm || format == Format.D32Float;
|
||||||
format == Format.D24X8Unorm ||
|
|
||||||
format == Format.D32Float;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
149
Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs
Normal file
149
Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Intrinsics;
|
||||||
|
using System.Runtime.Intrinsics.X86;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.OpenGL.Image
|
||||||
|
{
|
||||||
|
static class FormatConverter
|
||||||
|
{
|
||||||
|
public unsafe static byte[] ConvertS8D24ToD24S8(ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
byte[] output = new byte[data.Length];
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
|
||||||
|
if (Avx2.IsSupported)
|
||||||
|
{
|
||||||
|
var mask = Vector256.Create(
|
||||||
|
(byte)3, (byte)0, (byte)1, (byte)2,
|
||||||
|
(byte)7, (byte)4, (byte)5, (byte)6,
|
||||||
|
(byte)11, (byte)8, (byte)9, (byte)10,
|
||||||
|
(byte)15, (byte)12, (byte)13, (byte)14,
|
||||||
|
(byte)19, (byte)16, (byte)17, (byte)18,
|
||||||
|
(byte)23, (byte)20, (byte)21, (byte)22,
|
||||||
|
(byte)27, (byte)24, (byte)25, (byte)26,
|
||||||
|
(byte)31, (byte)28, (byte)29, (byte)30);
|
||||||
|
|
||||||
|
int sizeAligned = data.Length & ~31;
|
||||||
|
|
||||||
|
fixed (byte* pInput = data, pOutput = output)
|
||||||
|
{
|
||||||
|
for (uint i = 0; i < sizeAligned; i += 32)
|
||||||
|
{
|
||||||
|
var dataVec = Avx.LoadVector256(pInput + i);
|
||||||
|
|
||||||
|
dataVec = Avx2.Shuffle(dataVec, mask);
|
||||||
|
|
||||||
|
Avx.Store(pOutput + i, dataVec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start = sizeAligned;
|
||||||
|
}
|
||||||
|
else if (Ssse3.IsSupported)
|
||||||
|
{
|
||||||
|
var mask = Vector128.Create(
|
||||||
|
(byte)3, (byte)0, (byte)1, (byte)2,
|
||||||
|
(byte)7, (byte)4, (byte)5, (byte)6,
|
||||||
|
(byte)11, (byte)8, (byte)9, (byte)10,
|
||||||
|
(byte)15, (byte)12, (byte)13, (byte)14);
|
||||||
|
|
||||||
|
int sizeAligned = data.Length & ~15;
|
||||||
|
|
||||||
|
fixed (byte* pInput = data, pOutput = output)
|
||||||
|
{
|
||||||
|
for (uint i = 0; i < sizeAligned; i += 16)
|
||||||
|
{
|
||||||
|
var dataVec = Sse2.LoadVector128(pInput + i);
|
||||||
|
|
||||||
|
dataVec = Ssse3.Shuffle(dataVec, mask);
|
||||||
|
|
||||||
|
Sse2.Store(pOutput + i, dataVec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start = sizeAligned;
|
||||||
|
}
|
||||||
|
|
||||||
|
var outSpan = MemoryMarshal.Cast<byte, uint>(output);
|
||||||
|
var dataSpan = MemoryMarshal.Cast<byte, uint>(data);
|
||||||
|
for (int i = start / sizeof(uint); i < dataSpan.Length; i++)
|
||||||
|
{
|
||||||
|
outSpan[i] = BitOperations.RotateLeft(dataSpan[i], 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe static byte[] ConvertD24S8ToS8D24(ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
byte[] output = new byte[data.Length];
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
|
||||||
|
if (Avx2.IsSupported)
|
||||||
|
{
|
||||||
|
var mask = Vector256.Create(
|
||||||
|
(byte)1, (byte)2, (byte)3, (byte)0,
|
||||||
|
(byte)5, (byte)6, (byte)7, (byte)4,
|
||||||
|
(byte)9, (byte)10, (byte)11, (byte)8,
|
||||||
|
(byte)13, (byte)14, (byte)15, (byte)12,
|
||||||
|
(byte)17, (byte)18, (byte)19, (byte)16,
|
||||||
|
(byte)21, (byte)22, (byte)23, (byte)20,
|
||||||
|
(byte)25, (byte)26, (byte)27, (byte)24,
|
||||||
|
(byte)29, (byte)30, (byte)31, (byte)28);
|
||||||
|
|
||||||
|
int sizeAligned = data.Length & ~31;
|
||||||
|
|
||||||
|
fixed (byte* pInput = data, pOutput = output)
|
||||||
|
{
|
||||||
|
for (uint i = 0; i < sizeAligned; i += 32)
|
||||||
|
{
|
||||||
|
var dataVec = Avx.LoadVector256(pInput + i);
|
||||||
|
|
||||||
|
dataVec = Avx2.Shuffle(dataVec, mask);
|
||||||
|
|
||||||
|
Avx.Store(pOutput + i, dataVec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start = sizeAligned;
|
||||||
|
}
|
||||||
|
else if (Ssse3.IsSupported)
|
||||||
|
{
|
||||||
|
var mask = Vector128.Create(
|
||||||
|
(byte)1, (byte)2, (byte)3, (byte)0,
|
||||||
|
(byte)5, (byte)6, (byte)7, (byte)4,
|
||||||
|
(byte)9, (byte)10, (byte)11, (byte)8,
|
||||||
|
(byte)13, (byte)14, (byte)15, (byte)12);
|
||||||
|
|
||||||
|
int sizeAligned = data.Length & ~15;
|
||||||
|
|
||||||
|
fixed (byte* pInput = data, pOutput = output)
|
||||||
|
{
|
||||||
|
for (uint i = 0; i < sizeAligned; i += 16)
|
||||||
|
{
|
||||||
|
var dataVec = Sse2.LoadVector128(pInput + i);
|
||||||
|
|
||||||
|
dataVec = Ssse3.Shuffle(dataVec, mask);
|
||||||
|
|
||||||
|
Sse2.Store(pOutput + i, dataVec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start = sizeAligned;
|
||||||
|
}
|
||||||
|
|
||||||
|
var outSpan = MemoryMarshal.Cast<byte, uint>(output);
|
||||||
|
var dataSpan = MemoryMarshal.Cast<byte, uint>(data);
|
||||||
|
for (int i = start / sizeof(uint); i < dataSpan.Length; i++)
|
||||||
|
{
|
||||||
|
outSpan[i] = BitOperations.RotateRight(dataSpan[i], 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -291,7 +291,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
|
|
||||||
private static ClearBufferMask GetMask(Format format)
|
private static ClearBufferMask GetMask(Format format)
|
||||||
{
|
{
|
||||||
if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint)
|
if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint || format == Format.S8UintD24Unorm)
|
||||||
{
|
{
|
||||||
return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit;
|
return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit;
|
||||||
}
|
}
|
||||||
@ -311,9 +311,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
|
|
||||||
private static bool IsDepthOnly(Format format)
|
private static bool IsDepthOnly(Format format)
|
||||||
{
|
{
|
||||||
return format == Format.D16Unorm ||
|
return format == Format.D16Unorm || format == Format.D32Float;
|
||||||
format == Format.D24X8Unorm ||
|
|
||||||
format == Format.D32Float;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextureView BgraSwap(TextureView from)
|
public TextureView BgraSwap(TextureView from)
|
||||||
|
@ -140,9 +140,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
size += Info.GetMipSize(level);
|
size += Info.GetMipSize(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> data;
|
||||||
|
|
||||||
if (HwCapabilities.UsePersistentBufferForFlush)
|
if (HwCapabilities.UsePersistentBufferForFlush)
|
||||||
{
|
{
|
||||||
return _renderer.PersistentBuffers.Default.GetTextureData(this, size);
|
data = _renderer.PersistentBuffers.Default.GetTextureData(this, size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -150,8 +152,15 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
|
|
||||||
WriteTo(target);
|
WriteTo(target);
|
||||||
|
|
||||||
return new ReadOnlySpan<byte>(target.ToPointer(), size);
|
data = new ReadOnlySpan<byte>(target.ToPointer(), size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Format == Format.S8UintD24Unorm)
|
||||||
|
{
|
||||||
|
data = FormatConverter.ConvertD24S8ToS8D24(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe ReadOnlySpan<byte> GetData(int layer, int level)
|
public unsafe ReadOnlySpan<byte> GetData(int layer, int level)
|
||||||
@ -285,6 +294,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data)
|
public void SetData(ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
|
if (Format == Format.S8UintD24Unorm)
|
||||||
|
{
|
||||||
|
data = FormatConverter.ConvertS8D24ToD24S8(data);
|
||||||
|
}
|
||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
fixed (byte* ptr = data)
|
fixed (byte* ptr = data)
|
||||||
@ -296,6 +310,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
|||||||
|
|
||||||
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
|
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
|
||||||
{
|
{
|
||||||
|
if (Format == Format.S8UintD24Unorm)
|
||||||
|
{
|
||||||
|
data = FormatConverter.ConvertS8D24ToD24S8(data);
|
||||||
|
}
|
||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
fixed (byte* ptr = data)
|
fixed (byte* ptr = data)
|
||||||
|
@ -1478,6 +1478,11 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
_currentComponentMasks |= componentMaskAtIndex;
|
_currentComponentMasks |= componentMaskAtIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RestoreClipControl()
|
||||||
|
{
|
||||||
|
GL.ClipControl(_clipOrigin, _clipDepthMode);
|
||||||
|
}
|
||||||
|
|
||||||
public void RestoreScissor0Enable()
|
public void RestoreScissor0Enable()
|
||||||
{
|
{
|
||||||
if ((_scissorEnables & 1u) != 0)
|
if ((_scissorEnables & 1u) != 0)
|
||||||
@ -1494,6 +1499,11 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RestoreViewport0()
|
||||||
|
{
|
||||||
|
GL.ViewportArray(0, 1, _viewportArray);
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
|
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
|
||||||
{
|
{
|
||||||
if (value is CounterQueueEvent)
|
if (value is CounterQueueEvent)
|
||||||
|
@ -27,11 +27,12 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
{
|
{
|
||||||
GL.Disable(EnableCap.FramebufferSrgb);
|
GL.Disable(EnableCap.FramebufferSrgb);
|
||||||
|
|
||||||
CopyTextureToFrameBufferRGB(0, GetCopyFramebufferHandleLazy(), (TextureView)texture, crop);
|
CopyTextureToFrameBufferRGB(0, GetCopyFramebufferHandleLazy(), (TextureView)texture, crop, swapBuffersCallback);
|
||||||
|
|
||||||
GL.Enable(EnableCap.FramebufferSrgb);
|
GL.Enable(EnableCap.FramebufferSrgb);
|
||||||
|
|
||||||
swapBuffersCallback();
|
// Restore unpack alignment to 4, as performance overlays such as RTSS may change this to load their resources.
|
||||||
|
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSize(int width, int height)
|
public void SetSize(int width, int height)
|
||||||
@ -40,7 +41,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
_height = height;
|
_height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop)
|
private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop, Action swapBuffersCallback)
|
||||||
{
|
{
|
||||||
(int oldDrawFramebufferHandle, int oldReadFramebufferHandle) = ((Pipeline)_renderer.Pipeline).GetBoundFramebuffers();
|
(int oldDrawFramebufferHandle, int oldReadFramebufferHandle) = ((Pipeline)_renderer.Pipeline).GetBoundFramebuffers();
|
||||||
|
|
||||||
@ -139,11 +140,20 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
((Pipeline)_renderer.Pipeline).RestoreComponentMask(i);
|
((Pipeline)_renderer.Pipeline).RestoreComponentMask(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set clip control, viewport and the framebuffer to the output to placate overlays and OBS capture.
|
||||||
|
GL.ClipControl(ClipOrigin.LowerLeft, ClipDepthMode.NegativeOneToOne);
|
||||||
|
GL.Viewport(0, 0, _width, _height);
|
||||||
|
GL.BindFramebuffer(FramebufferTarget.Framebuffer, drawFramebuffer);
|
||||||
|
|
||||||
|
swapBuffersCallback();
|
||||||
|
|
||||||
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle);
|
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle);
|
||||||
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle);
|
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle);
|
||||||
|
|
||||||
|
((Pipeline)_renderer.Pipeline).RestoreClipControl();
|
||||||
((Pipeline)_renderer.Pipeline).RestoreScissor0Enable();
|
((Pipeline)_renderer.Pipeline).RestoreScissor0Enable();
|
||||||
((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard();
|
((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard();
|
||||||
|
((Pipeline)_renderer.Pipeline).RestoreViewport0();
|
||||||
|
|
||||||
if (viewConverted != view)
|
if (viewConverted != view)
|
||||||
{
|
{
|
||||||
|
@ -328,6 +328,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
public void SetAllInputUserAttributes()
|
public void SetAllInputUserAttributes()
|
||||||
{
|
{
|
||||||
UsedInputAttributes |= Constants.AllAttributesMask;
|
UsedInputAttributes |= Constants.AllAttributesMask;
|
||||||
|
ThisInputAttributesComponents |= ~UInt128.Zero >> (128 - Constants.MaxAttributes * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetAllOutputUserAttributes()
|
public void SetAllOutputUserAttributes()
|
||||||
|
@ -218,10 +218,17 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
while (usedAttributes != UInt128.Zero)
|
while (usedAttributes != UInt128.Zero)
|
||||||
{
|
{
|
||||||
int index = usedAttributes.TrailingZeroCount();
|
int index = usedAttributes.TrailingZeroCount();
|
||||||
|
int vecIndex = index / 4;
|
||||||
InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: false);
|
|
||||||
|
|
||||||
usedAttributes &= ~UInt128.Pow2(index);
|
usedAttributes &= ~UInt128.Pow2(index);
|
||||||
|
|
||||||
|
// We don't need to initialize passthrough attributes.
|
||||||
|
if ((context.Config.PassthroughAttributes & (1 << vecIndex)) != 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt128 usedAttributesPerPatch = context.Config.NextInputAttributesPerPatchComponents;
|
UInt128 usedAttributesPerPatch = context.Config.NextInputAttributesPerPatchComponents;
|
||||||
|
@ -10,6 +10,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
private ulong _v0;
|
private ulong _v0;
|
||||||
private ulong _v1;
|
private ulong _v1;
|
||||||
|
|
||||||
|
public UInt128(ulong low, ulong high)
|
||||||
|
{
|
||||||
|
_v0 = low;
|
||||||
|
_v1 = high;
|
||||||
|
}
|
||||||
|
|
||||||
public int TrailingZeroCount()
|
public int TrailingZeroCount()
|
||||||
{
|
{
|
||||||
int count = BitOperations.TrailingZeroCount(_v0);
|
int count = BitOperations.TrailingZeroCount(_v0);
|
||||||
@ -25,25 +31,57 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
{
|
{
|
||||||
if (x >= 64)
|
if (x >= 64)
|
||||||
{
|
{
|
||||||
return new UInt128() { _v0 = 0, _v1 = 1UL << (x - 64 ) };
|
return new UInt128(0, 1UL << (x - 64));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new UInt128() { _v0 = 1UL << x, _v1 = 0 };
|
return new UInt128(1UL << x, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UInt128 operator ~(UInt128 x)
|
public static UInt128 operator ~(UInt128 x)
|
||||||
{
|
{
|
||||||
return new UInt128() { _v0 = ~x._v0, _v1 = ~x._v1 };
|
return new UInt128(~x._v0, ~x._v1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UInt128 operator &(UInt128 x, UInt128 y)
|
public static UInt128 operator &(UInt128 x, UInt128 y)
|
||||||
{
|
{
|
||||||
return new UInt128() { _v0 = x._v0 & y._v0, _v1 = x._v1 & y._v1 };
|
return new UInt128(x._v0 & y._v0, x._v1 & y._v1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UInt128 operator |(UInt128 x, UInt128 y)
|
public static UInt128 operator |(UInt128 x, UInt128 y)
|
||||||
{
|
{
|
||||||
return new UInt128() { _v0 = x._v0 | y._v0, _v1 = x._v1 | y._v1 };
|
return new UInt128(x._v0 | y._v0, x._v1 | y._v1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UInt128 operator <<(UInt128 x, int shift)
|
||||||
|
{
|
||||||
|
if (shift == 0)
|
||||||
|
{
|
||||||
|
return new UInt128(x._v0, x._v1);
|
||||||
|
}
|
||||||
|
else if (shift >= 64)
|
||||||
|
{
|
||||||
|
return new UInt128(0, x._v0 << (shift - 64));
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong shiftOut = x._v0 >> (64 - shift);
|
||||||
|
|
||||||
|
return new UInt128(x._v0 << shift, (x._v1 << shift) | shiftOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UInt128 operator >>(UInt128 x, int shift)
|
||||||
|
{
|
||||||
|
if (shift == 0)
|
||||||
|
{
|
||||||
|
return new UInt128(x._v0, x._v1);
|
||||||
|
}
|
||||||
|
else if (shift >= 64)
|
||||||
|
{
|
||||||
|
return new UInt128(x._v1 >> (shift - 64), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong shiftOut = x._v1 & ((1UL << shift) - 1);
|
||||||
|
|
||||||
|
return new UInt128((x._v0 >> shift) | (shiftOut << (64 - shift)), x._v1 >> shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator ==(UInt128 x, UInt128 y)
|
public static bool operator ==(UInt128 x, UInt128 y)
|
||||||
|
@ -14,6 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
|||||||
{
|
{
|
||||||
private GeneralServiceDetail _generalServiceDetail;
|
private GeneralServiceDetail _generalServiceDetail;
|
||||||
|
|
||||||
|
private IPInterfaceProperties _targetPropertiesCache = null;
|
||||||
|
private UnicastIPAddressInformation _targetAddressInfoCache = null;
|
||||||
|
|
||||||
public IGeneralService()
|
public IGeneralService()
|
||||||
{
|
{
|
||||||
_generalServiceDetail = new GeneralServiceDetail
|
_generalServiceDetail = new GeneralServiceDetail
|
||||||
@ -22,6 +25,8 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
|||||||
IsAnyInternetRequestAccepted = true // NOTE: Why not accept any internet request?
|
IsAnyInternetRequestAccepted = true // NOTE: Why not accept any internet request?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(LocalInterfaceCacheHandler);
|
||||||
|
|
||||||
GeneralServiceManager.Add(_generalServiceDetail);
|
GeneralServiceManager.Add(_generalServiceDetail);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,6 +170,11 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
|||||||
return (null, null);
|
return (null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_targetPropertiesCache != null && _targetAddressInfoCache != null)
|
||||||
|
{
|
||||||
|
return (_targetPropertiesCache, _targetAddressInfoCache);
|
||||||
|
}
|
||||||
|
|
||||||
IPInterfaceProperties targetProperties = null;
|
IPInterfaceProperties targetProperties = null;
|
||||||
UnicastIPAddressInformation targetAddressInfo = null;
|
UnicastIPAddressInformation targetAddressInfo = null;
|
||||||
|
|
||||||
@ -194,13 +204,26 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_targetPropertiesCache = targetProperties;
|
||||||
|
_targetAddressInfoCache = targetAddressInfo;
|
||||||
|
|
||||||
return (targetProperties, targetAddressInfo);
|
return (targetProperties, targetAddressInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void LocalInterfaceCacheHandler(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Logger.Info?.Print(LogClass.ServiceNifm, $"NetworkAddress changed, invalidating cached data.");
|
||||||
|
|
||||||
|
_targetPropertiesCache = null;
|
||||||
|
_targetAddressInfoCache = null;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
protected override void Dispose(bool isDisposing)
|
||||||
{
|
{
|
||||||
if (isDisposing)
|
if (isDisposing)
|
||||||
{
|
{
|
||||||
|
NetworkChange.NetworkAddressChanged -= LocalInterfaceCacheHandler;
|
||||||
|
|
||||||
GeneralServiceManager.Remove(_generalServiceDetail.ClientId);
|
GeneralServiceManager.Remove(_generalServiceDetail.ClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,24 @@
|
|||||||
namespace Ryujinx.HLE.HOS.Services.Nim.Ntc
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Nim.Ntc.StaticService;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nim.Ntc
|
||||||
{
|
{
|
||||||
[Service("ntc")]
|
[Service("ntc")]
|
||||||
class IStaticService : IpcService
|
class IStaticService : IpcService
|
||||||
{
|
{
|
||||||
public IStaticService(ServiceCtx context) { }
|
public IStaticService(ServiceCtx context) { }
|
||||||
|
|
||||||
|
[CommandHipc(0)]
|
||||||
|
// OpenEnsureNetworkClockAvailabilityService(u64) -> object<nn::ntc::detail::service::IEnsureNetworkClockAvailabilityService>
|
||||||
|
public ResultCode CreateAsyncInterface(ServiceCtx context)
|
||||||
|
{
|
||||||
|
ulong unknown = context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
|
MakeObject(context, new IEnsureNetworkClockAvailabilityService(context));
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceNtc, new { unknown });
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nim.Ntc.StaticService
|
||||||
|
{
|
||||||
|
class IEnsureNetworkClockAvailabilityService : IpcService
|
||||||
|
{
|
||||||
|
private KEvent _finishNotificationEvent;
|
||||||
|
private ResultCode _taskResultCode;
|
||||||
|
|
||||||
|
public IEnsureNetworkClockAvailabilityService(ServiceCtx context)
|
||||||
|
{
|
||||||
|
_finishNotificationEvent = new KEvent(context.Device.System.KernelContext);
|
||||||
|
_taskResultCode = ResultCode.Success;
|
||||||
|
|
||||||
|
// NOTE: The service starts a thread that polls Nintendo NTP server and syncs the time with it.
|
||||||
|
// Additionnally it gets and uses some settings too:
|
||||||
|
// autonomic_correction_interval_seconds, autonomic_correction_failed_retry_interval_seconds,
|
||||||
|
// autonomic_correction_immediate_try_count_max, autonomic_correction_immediate_try_interval_milliseconds
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandHipc(0)]
|
||||||
|
// StartTask()
|
||||||
|
public ResultCode StartTask(ServiceCtx context)
|
||||||
|
{
|
||||||
|
if (!context.Device.Configuration.EnableInternetAccess)
|
||||||
|
{
|
||||||
|
return (ResultCode)Time.ResultCode.NetworkTimeNotAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Since we don't support the Nintendo NTP server, we can signal the event now to confirm the update task is done.
|
||||||
|
_finishNotificationEvent.ReadableEvent.Signal();
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceNtc);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandHipc(1)]
|
||||||
|
// GetFinishNotificationEvent() -> handle<copy>
|
||||||
|
public ResultCode GetFinishNotificationEvent(ServiceCtx context)
|
||||||
|
{
|
||||||
|
if (context.Process.HandleTable.GenerateHandle(_finishNotificationEvent.ReadableEvent, out int finishNotificationEventHandle) != KernelResult.Success)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Out of handles!");
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(finishNotificationEventHandle);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandHipc(2)]
|
||||||
|
// GetResult()
|
||||||
|
public ResultCode GetResult(ServiceCtx context)
|
||||||
|
{
|
||||||
|
return _taskResultCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandHipc(3)]
|
||||||
|
// Cancel()
|
||||||
|
public ResultCode Cancel(ServiceCtx context)
|
||||||
|
{
|
||||||
|
// NOTE: The update task should be canceled here.
|
||||||
|
_finishNotificationEvent.ReadableEvent.Signal();
|
||||||
|
|
||||||
|
_taskResultCode = (ResultCode)Time.ResultCode.NetworkTimeTaskCanceled;
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceNtc);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -232,7 +232,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
ulong virtualAddress = arguments.Offset + arguments.BufferOffset;
|
ulong virtualAddress = arguments.Offset + arguments.BufferOffset;
|
||||||
|
|
||||||
physicalAddress += arguments.BufferOffset;
|
physicalAddress += arguments.BufferOffset;
|
||||||
_asContext.Gmm.Map(physicalAddress, virtualAddress, arguments.MappingSize);
|
_asContext.Gmm.Map(physicalAddress, virtualAddress, arguments.MappingSize, (PteKind)arguments.Kind);
|
||||||
|
|
||||||
return NvInternalResult.Success;
|
return NvInternalResult.Success;
|
||||||
}
|
}
|
||||||
@ -282,7 +282,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
{
|
{
|
||||||
if (_asContext.ValidateFixedBuffer(arguments.Offset, size, pageSize))
|
if (_asContext.ValidateFixedBuffer(arguments.Offset, size, pageSize))
|
||||||
{
|
{
|
||||||
_asContext.Gmm.Map(physicalAddress, arguments.Offset, size);
|
_asContext.Gmm.Map(physicalAddress, arguments.Offset, size, (PteKind)arguments.Kind);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -301,7 +301,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
_memoryAllocator.AllocateRange(va, size, freeAddressStartPosition);
|
_memoryAllocator.AllocateRange(va, size, freeAddressStartPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
_asContext.Gmm.Map(physicalAddress, va, size);
|
_asContext.Gmm.Map(physicalAddress, va, size, (PteKind)arguments.Kind);
|
||||||
arguments.Offset = va;
|
arguments.Offset = va;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,26 +366,30 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
|
|
||||||
for (int index = 0; index < arguments.Length; index++)
|
for (int index = 0; index < arguments.Length; index++)
|
||||||
{
|
{
|
||||||
ulong mapOffs = (ulong)arguments[index].MapOffset << 16;
|
ref RemapArguments argument = ref arguments[index];
|
||||||
ulong gpuVa = (ulong)arguments[index].GpuOffset << 16;
|
ulong gpuVa = (ulong)argument.GpuOffset << 16;
|
||||||
ulong size = (ulong)arguments[index].Pages << 16;
|
ulong size = (ulong)argument.Pages << 16;
|
||||||
|
int nvmapHandle = argument.NvMapHandle;
|
||||||
|
|
||||||
if (arguments[index].NvMapHandle == 0)
|
if (nvmapHandle == 0)
|
||||||
{
|
{
|
||||||
gmm.Unmap(gpuVa, size);
|
gmm.Unmap(gpuVa, size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, arguments[index].NvMapHandle);
|
ulong mapOffs = (ulong)argument.MapOffset << 16;
|
||||||
|
PteKind kind = (PteKind)argument.Kind;
|
||||||
|
|
||||||
|
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, nvmapHandle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{arguments[index].NvMapHandle:x8}!");
|
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{nvmapHandle:x8}!");
|
||||||
|
|
||||||
return NvInternalResult.InvalidInput;
|
return NvInternalResult.InvalidInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
gmm.Map(mapOffs + map.Address, gpuVa, size);
|
gmm.Map(mapOffs + map.Address, gpuVa, size, kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
if (va != NvMemoryAllocator.PteUnmapped && va <= uint.MaxValue && (va + (uint)map.Size) <= uint.MaxValue)
|
if (va != NvMemoryAllocator.PteUnmapped && va <= uint.MaxValue && (va + (uint)map.Size) <= uint.MaxValue)
|
||||||
{
|
{
|
||||||
_host1xContext.MemoryAllocator.AllocateRange(va, (uint)map.Size, freeAddressStartPosition);
|
_host1xContext.MemoryAllocator.AllocateRange(va, (uint)map.Size, freeAddressStartPosition);
|
||||||
_host1xContext.Smmu.Map(map.Address, va, (uint)map.Size);
|
_host1xContext.Smmu.Map(map.Address, va, (uint)map.Size, PteKind.Pitch); // FIXME: This should not use the GMMU.
|
||||||
map.DmaMapAddress = va;
|
map.DmaMapAddress = va;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Olsc
|
namespace Ryujinx.HLE.HOS.Services.Olsc
|
||||||
{
|
{
|
||||||
[Service("olsc:u")] // 10.0.0+
|
[Service("olsc:u")] // 10.0.0+
|
||||||
class IOlscServiceForApplication : IpcService
|
class IOlscServiceForApplication : IpcService
|
||||||
{
|
{
|
||||||
private bool _initialized;
|
private bool _initialized;
|
||||||
|
private Dictionary<UserId, bool> _saveDataBackupSettingDatabase;
|
||||||
|
|
||||||
public IOlscServiceForApplication(ServiceCtx context) { }
|
public IOlscServiceForApplication(ServiceCtx context) { }
|
||||||
|
|
||||||
@ -16,7 +18,9 @@ namespace Ryujinx.HLE.HOS.Services.Olsc
|
|||||||
public ResultCode Initialize(ServiceCtx context)
|
public ResultCode Initialize(ServiceCtx context)
|
||||||
{
|
{
|
||||||
// NOTE: Service call arp:r GetApplicationInstanceUnregistrationNotifier with the pid and initialize some internal struct.
|
// NOTE: Service call arp:r GetApplicationInstanceUnregistrationNotifier with the pid and initialize some internal struct.
|
||||||
// Since we will not support online savedata backup. It's fine to stub it for now.
|
// Since we will not support online savedata backup, it's fine to stub it for now.
|
||||||
|
|
||||||
|
_saveDataBackupSettingDatabase = new Dictionary<UserId, bool>();
|
||||||
|
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
|
|
||||||
@ -25,12 +29,11 @@ namespace Ryujinx.HLE.HOS.Services.Olsc
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandHipc(14)]
|
[CommandHipc(13)]
|
||||||
// SetSaveDataBackupSettingEnabled(nn::account::Uid, bool)
|
// GetSaveDataBackupSetting(nn::account::Uid) -> u8
|
||||||
public ResultCode SetSaveDataBackupSettingEnabled(ServiceCtx context)
|
public ResultCode GetSaveDataBackupSetting(ServiceCtx context)
|
||||||
{
|
{
|
||||||
UserId userId = context.RequestData.ReadStruct<UserId>();
|
UserId userId = context.RequestData.ReadStruct<UserId>();
|
||||||
ulong saveDataBackupSettingEnabled = context.RequestData.ReadUInt64();
|
|
||||||
|
|
||||||
if (!_initialized)
|
if (!_initialized)
|
||||||
{
|
{
|
||||||
@ -42,8 +45,42 @@ namespace Ryujinx.HLE.HOS.Services.Olsc
|
|||||||
return ResultCode.NullArgument;
|
return ResultCode.NullArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Service store the UserId and the boolean in an internal SaveDataBackupSettingDatabase object.
|
if (_saveDataBackupSettingDatabase.TryGetValue(userId, out bool enabled) && enabled)
|
||||||
// Since we will not support online savedata backup. It's fine to stub it for now.
|
{
|
||||||
|
context.ResponseData.Write((byte)1); // TODO: Determine value.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.ResponseData.Write((byte)2); // TODO: Determine value.
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Since we will not support online savedata backup, it's fine to stub it for now.
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceOlsc, new { userId });
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandHipc(14)]
|
||||||
|
// SetSaveDataBackupSettingEnabled(nn::account::Uid, bool)
|
||||||
|
public ResultCode SetSaveDataBackupSettingEnabled(ServiceCtx context)
|
||||||
|
{
|
||||||
|
bool saveDataBackupSettingEnabled = context.RequestData.ReadUInt64() != 0;
|
||||||
|
UserId userId = context.RequestData.ReadStruct<UserId>();
|
||||||
|
|
||||||
|
if (!_initialized)
|
||||||
|
{
|
||||||
|
return ResultCode.NotInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userId.IsNull)
|
||||||
|
{
|
||||||
|
return ResultCode.NullArgument;
|
||||||
|
}
|
||||||
|
|
||||||
|
_saveDataBackupSettingDatabase[userId] = saveDataBackupSettingEnabled;
|
||||||
|
|
||||||
|
// NOTE: Since we will not support online savedata backup, it's fine to stub it for now.
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceOlsc, new { userId, saveDataBackupSettingEnabled });
|
Logger.Stub?.PrintStub(LogClass.ServiceOlsc, new { userId, saveDataBackupSettingEnabled });
|
||||||
|
|
||||||
|
@ -7,16 +7,18 @@
|
|||||||
|
|
||||||
Success = 0,
|
Success = 0,
|
||||||
|
|
||||||
TimeServiceNotInitialized = (0 << ErrorCodeShift) | ModuleId,
|
TimeServiceNotInitialized = (0 << ErrorCodeShift) | ModuleId,
|
||||||
PermissionDenied = (1 << ErrorCodeShift) | ModuleId,
|
PermissionDenied = (1 << ErrorCodeShift) | ModuleId,
|
||||||
TimeMismatch = (102 << ErrorCodeShift) | ModuleId,
|
TimeMismatch = (102 << ErrorCodeShift) | ModuleId,
|
||||||
UninitializedClock = (103 << ErrorCodeShift) | ModuleId,
|
UninitializedClock = (103 << ErrorCodeShift) | ModuleId,
|
||||||
TimeNotFound = (200 << ErrorCodeShift) | ModuleId,
|
TimeNotFound = (200 << ErrorCodeShift) | ModuleId,
|
||||||
Overflow = (201 << ErrorCodeShift) | ModuleId,
|
Overflow = (201 << ErrorCodeShift) | ModuleId,
|
||||||
LocationNameTooLong = (801 << ErrorCodeShift) | ModuleId,
|
LocationNameTooLong = (801 << ErrorCodeShift) | ModuleId,
|
||||||
OutOfRange = (902 << ErrorCodeShift) | ModuleId,
|
OutOfRange = (902 << ErrorCodeShift) | ModuleId,
|
||||||
TimeZoneConversionFailed = (903 << ErrorCodeShift) | ModuleId,
|
TimeZoneConversionFailed = (903 << ErrorCodeShift) | ModuleId,
|
||||||
TimeZoneNotFound = (989 << ErrorCodeShift) | ModuleId,
|
TimeZoneNotFound = (989 << ErrorCodeShift) | ModuleId,
|
||||||
NotImplemented = (990 << ErrorCodeShift) | ModuleId,
|
NotImplemented = (990 << ErrorCodeShift) | ModuleId,
|
||||||
|
NetworkTimeNotAvailable = (1000 << ErrorCodeShift) | ModuleId,
|
||||||
|
NetworkTimeTaskCanceled = (1003 << ErrorCodeShift) | ModuleId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -220,6 +220,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
StickButton = ConfigGamepadInputId.LeftStick,
|
StickButton = ConfigGamepadInputId.LeftStick,
|
||||||
InvertStickX = false,
|
InvertStickX = false,
|
||||||
InvertStickY = false,
|
InvertStickY = false,
|
||||||
|
Rotate90CW = false,
|
||||||
},
|
},
|
||||||
|
|
||||||
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
|
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
|
||||||
@ -241,6 +242,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
StickButton = ConfigGamepadInputId.RightStick,
|
StickButton = ConfigGamepadInputId.RightStick,
|
||||||
InvertStickX = false,
|
InvertStickX = false,
|
||||||
InvertStickY = false,
|
InvertStickY = false,
|
||||||
|
Rotate90CW = false,
|
||||||
},
|
},
|
||||||
|
|
||||||
Motion = new StandardMotionConfigController
|
Motion = new StandardMotionConfigController
|
||||||
|
@ -350,6 +350,14 @@ namespace Ryujinx.Input.SDL2
|
|||||||
{
|
{
|
||||||
resultY = -resultY;
|
resultY = -resultY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((inputId == StickInputId.Left && _configuration.LeftJoyconStick.Rotate90CW) ||
|
||||||
|
(inputId == StickInputId.Right && _configuration.RightJoyconStick.Rotate90CW))
|
||||||
|
{
|
||||||
|
float temp = resultX;
|
||||||
|
resultX = resultY;
|
||||||
|
resultY = -temp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (resultX, resultY);
|
return (resultX, resultY);
|
||||||
|
@ -14,7 +14,7 @@ namespace Ryujinx.Configuration
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current version of the file format
|
/// The current version of the file format
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int CurrentVersion = 36;
|
public const int CurrentVersion = 37;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of the configuration file format
|
/// Version of the configuration file format
|
||||||
@ -236,6 +236,11 @@ namespace Ryujinx.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool StartFullscreen { get; set; }
|
public bool StartFullscreen { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Show console window
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowConsole { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enable or disable keyboard support (Independent from controllers binding)
|
/// Enable or disable keyboard support (Independent from controllers binding)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -6,6 +6,7 @@ using Ryujinx.Common.Configuration.Hid.Keyboard;
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Configuration.System;
|
using Ryujinx.Configuration.System;
|
||||||
using Ryujinx.Configuration.Ui;
|
using Ryujinx.Configuration.Ui;
|
||||||
|
using Ryujinx.Ui.Helper;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
@ -88,6 +89,11 @@ namespace Ryujinx.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ReactiveObject<bool> StartFullscreen { get; private set; }
|
public ReactiveObject<bool> StartFullscreen { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hide / Show Console Window
|
||||||
|
/// </summary>
|
||||||
|
public ReactiveObject<bool> ShowConsole { get; private set; }
|
||||||
|
|
||||||
public UiSection()
|
public UiSection()
|
||||||
{
|
{
|
||||||
GuiColumns = new Columns();
|
GuiColumns = new Columns();
|
||||||
@ -96,6 +102,8 @@ namespace Ryujinx.Configuration
|
|||||||
EnableCustomTheme = new ReactiveObject<bool>();
|
EnableCustomTheme = new ReactiveObject<bool>();
|
||||||
CustomThemePath = new ReactiveObject<string>();
|
CustomThemePath = new ReactiveObject<string>();
|
||||||
StartFullscreen = new ReactiveObject<bool>();
|
StartFullscreen = new ReactiveObject<bool>();
|
||||||
|
ShowConsole = new ReactiveObject<bool>();
|
||||||
|
ShowConsole.Event += static (s, e) => { ConsoleHelper.SetConsoleWindowState(e.NewValue); };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,6 +516,7 @@ namespace Ryujinx.Configuration
|
|||||||
EnableCustomTheme = Ui.EnableCustomTheme,
|
EnableCustomTheme = Ui.EnableCustomTheme,
|
||||||
CustomThemePath = Ui.CustomThemePath,
|
CustomThemePath = Ui.CustomThemePath,
|
||||||
StartFullscreen = Ui.StartFullscreen,
|
StartFullscreen = Ui.StartFullscreen,
|
||||||
|
ShowConsole = Ui.ShowConsole,
|
||||||
EnableKeyboard = Hid.EnableKeyboard,
|
EnableKeyboard = Hid.EnableKeyboard,
|
||||||
EnableMouse = Hid.EnableMouse,
|
EnableMouse = Hid.EnableMouse,
|
||||||
Hotkeys = Hid.Hotkeys,
|
Hotkeys = Hid.Hotkeys,
|
||||||
@ -574,6 +583,7 @@ namespace Ryujinx.Configuration
|
|||||||
Ui.EnableCustomTheme.Value = false;
|
Ui.EnableCustomTheme.Value = false;
|
||||||
Ui.CustomThemePath.Value = "";
|
Ui.CustomThemePath.Value = "";
|
||||||
Ui.StartFullscreen.Value = false;
|
Ui.StartFullscreen.Value = false;
|
||||||
|
Ui.ShowConsole.Value = true;
|
||||||
Hid.EnableKeyboard.Value = false;
|
Hid.EnableKeyboard.Value = false;
|
||||||
Hid.EnableMouse.Value = false;
|
Hid.EnableMouse.Value = false;
|
||||||
Hid.Hotkeys.Value = new KeyboardHotkeys
|
Hid.Hotkeys.Value = new KeyboardHotkeys
|
||||||
@ -995,7 +1005,7 @@ namespace Ryujinx.Configuration
|
|||||||
controllerConfig.RangeRight = 1.0f;
|
controllerConfig.RangeRight = 1.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configurationFileUpdated = true;
|
configurationFileUpdated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1007,7 +1017,16 @@ namespace Ryujinx.Configuration
|
|||||||
|
|
||||||
configurationFileUpdated = true;
|
configurationFileUpdated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (configurationFileFormat.Version < 37)
|
||||||
|
{
|
||||||
|
Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 37.");
|
||||||
|
|
||||||
|
configurationFileFormat.ShowConsole = true;
|
||||||
|
|
||||||
|
configurationFileUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
|
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
|
||||||
Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading;
|
Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading;
|
||||||
Graphics.ResScale.Value = configurationFileFormat.ResScale;
|
Graphics.ResScale.Value = configurationFileFormat.ResScale;
|
||||||
@ -1061,6 +1080,7 @@ namespace Ryujinx.Configuration
|
|||||||
Ui.EnableCustomTheme.Value = configurationFileFormat.EnableCustomTheme;
|
Ui.EnableCustomTheme.Value = configurationFileFormat.EnableCustomTheme;
|
||||||
Ui.CustomThemePath.Value = configurationFileFormat.CustomThemePath;
|
Ui.CustomThemePath.Value = configurationFileFormat.CustomThemePath;
|
||||||
Ui.StartFullscreen.Value = configurationFileFormat.StartFullscreen;
|
Ui.StartFullscreen.Value = configurationFileFormat.StartFullscreen;
|
||||||
|
Ui.ShowConsole.Value = configurationFileFormat.ShowConsole;
|
||||||
Hid.EnableKeyboard.Value = configurationFileFormat.EnableKeyboard;
|
Hid.EnableKeyboard.Value = configurationFileFormat.EnableKeyboard;
|
||||||
Hid.EnableMouse.Value = configurationFileFormat.EnableMouse;
|
Hid.EnableMouse.Value = configurationFileFormat.EnableMouse;
|
||||||
Hid.Hotkeys.Value = configurationFileFormat.Hotkeys;
|
Hid.Hotkeys.Value = configurationFileFormat.Hotkeys;
|
||||||
|
49
Ryujinx/Ui/Helper/ConsoleHelper.cs
Normal file
49
Ryujinx/Ui/Helper/ConsoleHelper.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ui.Helper
|
||||||
|
{
|
||||||
|
public static class ConsoleHelper
|
||||||
|
{
|
||||||
|
public static bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows();
|
||||||
|
|
||||||
|
public static void SetConsoleWindowState(bool show)
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
SetConsoleWindowStateWindows(show);
|
||||||
|
}
|
||||||
|
else if (show == false)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, "OS doesn't support hiding console window");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
private static void SetConsoleWindowStateWindows(bool show)
|
||||||
|
{
|
||||||
|
const int SW_HIDE = 0;
|
||||||
|
const int SW_SHOW = 5;
|
||||||
|
|
||||||
|
IntPtr hWnd = GetConsoleWindow();
|
||||||
|
|
||||||
|
if (hWnd == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
Logger.Warning?.Print(LogClass.Application, "Attempted to show/hide console window but console window does not exist");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
[DllImport("kernel32")]
|
||||||
|
static extern IntPtr GetConsoleWindow();
|
||||||
|
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
[DllImport("user32")]
|
||||||
|
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
||||||
|
}
|
||||||
|
}
|
@ -107,6 +107,7 @@ namespace Ryujinx.Ui
|
|||||||
[GUI] MenuItem _hideUi;
|
[GUI] MenuItem _hideUi;
|
||||||
[GUI] MenuItem _fullScreen;
|
[GUI] MenuItem _fullScreen;
|
||||||
[GUI] CheckMenuItem _startFullScreen;
|
[GUI] CheckMenuItem _startFullScreen;
|
||||||
|
[GUI] CheckMenuItem _showConsole;
|
||||||
[GUI] CheckMenuItem _favToggle;
|
[GUI] CheckMenuItem _favToggle;
|
||||||
[GUI] MenuItem _firmwareInstallDirectory;
|
[GUI] MenuItem _firmwareInstallDirectory;
|
||||||
[GUI] MenuItem _firmwareInstallFile;
|
[GUI] MenuItem _firmwareInstallFile;
|
||||||
@ -213,6 +214,9 @@ namespace Ryujinx.Ui
|
|||||||
_startFullScreen.Active = true;
|
_startFullScreen.Active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_showConsole.Active = ConfigurationState.Instance.Ui.ShowConsole.Value;
|
||||||
|
_showConsole.Visible = ConsoleHelper.SetConsoleWindowStateSupported;
|
||||||
|
|
||||||
_actionMenu.Sensitive = false;
|
_actionMenu.Sensitive = false;
|
||||||
_pauseEmulation.Sensitive = false;
|
_pauseEmulation.Sensitive = false;
|
||||||
_resumeEmulation.Sensitive = false;
|
_resumeEmulation.Sensitive = false;
|
||||||
@ -1535,6 +1539,13 @@ namespace Ryujinx.Ui
|
|||||||
SaveConfig();
|
SaveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ShowConsole_Toggled(object sender, EventArgs args)
|
||||||
|
{
|
||||||
|
ConfigurationState.Instance.Ui.ShowConsole.Value = _showConsole.Active;
|
||||||
|
|
||||||
|
SaveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
private void OptionMenu_StateChanged(object o, StateChangedArgs args)
|
private void OptionMenu_StateChanged(object o, StateChangedArgs args)
|
||||||
{
|
{
|
||||||
_manageUserProfiles.Sensitive = _emulationContext == null;
|
_manageUserProfiles.Sensitive = _emulationContext == null;
|
||||||
|
@ -142,6 +142,15 @@
|
|||||||
<signal name="toggled" handler="StartFullScreen_Toggled" swapped="no"/>
|
<signal name="toggled" handler="StartFullScreen_Toggled" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckMenuItem" id="_showConsole">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label" translatable="yes">Show Log Console</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<signal name="toggled" handler="ShowConsole_Toggled" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkSeparatorMenuItem">
|
<object class="GtkSeparatorMenuItem">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
@ -73,6 +73,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
[GUI] ToggleButton _lStick;
|
[GUI] ToggleButton _lStick;
|
||||||
[GUI] CheckButton _invertLStickX;
|
[GUI] CheckButton _invertLStickX;
|
||||||
[GUI] CheckButton _invertLStickY;
|
[GUI] CheckButton _invertLStickY;
|
||||||
|
[GUI] CheckButton _rotateL90CW;
|
||||||
[GUI] ToggleButton _lStickUp;
|
[GUI] ToggleButton _lStickUp;
|
||||||
[GUI] ToggleButton _lStickDown;
|
[GUI] ToggleButton _lStickDown;
|
||||||
[GUI] ToggleButton _lStickLeft;
|
[GUI] ToggleButton _lStickLeft;
|
||||||
@ -88,6 +89,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
[GUI] ToggleButton _rStick;
|
[GUI] ToggleButton _rStick;
|
||||||
[GUI] CheckButton _invertRStickX;
|
[GUI] CheckButton _invertRStickX;
|
||||||
[GUI] CheckButton _invertRStickY;
|
[GUI] CheckButton _invertRStickY;
|
||||||
|
[GUI] CheckButton _rotateR90CW;
|
||||||
[GUI] ToggleButton _rStickUp;
|
[GUI] ToggleButton _rStickUp;
|
||||||
[GUI] ToggleButton _rStickDown;
|
[GUI] ToggleButton _rStickDown;
|
||||||
[GUI] ToggleButton _rStickLeft;
|
[GUI] ToggleButton _rStickLeft;
|
||||||
@ -490,6 +492,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
_lStick.Label = controllerConfig.LeftJoyconStick.Joystick.ToString();
|
_lStick.Label = controllerConfig.LeftJoyconStick.Joystick.ToString();
|
||||||
_invertLStickX.Active = controllerConfig.LeftJoyconStick.InvertStickX;
|
_invertLStickX.Active = controllerConfig.LeftJoyconStick.InvertStickX;
|
||||||
_invertLStickY.Active = controllerConfig.LeftJoyconStick.InvertStickY;
|
_invertLStickY.Active = controllerConfig.LeftJoyconStick.InvertStickY;
|
||||||
|
_rotateL90CW.Active = controllerConfig.LeftJoyconStick.Rotate90CW;
|
||||||
_lStickButton.Label = controllerConfig.LeftJoyconStick.StickButton.ToString();
|
_lStickButton.Label = controllerConfig.LeftJoyconStick.StickButton.ToString();
|
||||||
_dpadUp.Label = controllerConfig.LeftJoycon.DpadUp.ToString();
|
_dpadUp.Label = controllerConfig.LeftJoycon.DpadUp.ToString();
|
||||||
_dpadDown.Label = controllerConfig.LeftJoycon.DpadDown.ToString();
|
_dpadDown.Label = controllerConfig.LeftJoycon.DpadDown.ToString();
|
||||||
@ -503,6 +506,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
_rStick.Label = controllerConfig.RightJoyconStick.Joystick.ToString();
|
_rStick.Label = controllerConfig.RightJoyconStick.Joystick.ToString();
|
||||||
_invertRStickX.Active = controllerConfig.RightJoyconStick.InvertStickX;
|
_invertRStickX.Active = controllerConfig.RightJoyconStick.InvertStickX;
|
||||||
_invertRStickY.Active = controllerConfig.RightJoyconStick.InvertStickY;
|
_invertRStickY.Active = controllerConfig.RightJoyconStick.InvertStickY;
|
||||||
|
_rotateR90CW.Active = controllerConfig.RightJoyconStick.Rotate90CW;
|
||||||
_rStickButton.Label = controllerConfig.RightJoyconStick.StickButton.ToString();
|
_rStickButton.Label = controllerConfig.RightJoyconStick.StickButton.ToString();
|
||||||
_a.Label = controllerConfig.RightJoycon.ButtonA.ToString();
|
_a.Label = controllerConfig.RightJoycon.ButtonA.ToString();
|
||||||
_b.Label = controllerConfig.RightJoycon.ButtonB.ToString();
|
_b.Label = controllerConfig.RightJoycon.ButtonB.ToString();
|
||||||
@ -718,6 +722,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
Joystick = lStick,
|
Joystick = lStick,
|
||||||
InvertStickY = _invertLStickY.Active,
|
InvertStickY = _invertLStickY.Active,
|
||||||
StickButton = lStickButton,
|
StickButton = lStickButton,
|
||||||
|
Rotate90CW = _rotateL90CW.Active,
|
||||||
},
|
},
|
||||||
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
|
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
|
||||||
{
|
{
|
||||||
@ -737,6 +742,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
Joystick = rStick,
|
Joystick = rStick,
|
||||||
InvertStickY = _invertRStickY.Active,
|
InvertStickY = _invertRStickY.Active,
|
||||||
StickButton = rStickButton,
|
StickButton = rStickButton,
|
||||||
|
Rotate90CW = _rotateR90CW.Active,
|
||||||
},
|
},
|
||||||
Motion = motionConfig,
|
Motion = motionConfig,
|
||||||
Rumble = new RumbleConfigController
|
Rumble = new RumbleConfigController
|
||||||
@ -1056,6 +1062,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
StickButton = ConfigGamepadInputId.LeftStick,
|
StickButton = ConfigGamepadInputId.LeftStick,
|
||||||
InvertStickX = false,
|
InvertStickX = false,
|
||||||
InvertStickY = false,
|
InvertStickY = false,
|
||||||
|
Rotate90CW = false,
|
||||||
},
|
},
|
||||||
|
|
||||||
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
|
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
|
||||||
@ -1077,6 +1084,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
StickButton = ConfigGamepadInputId.RightStick,
|
StickButton = ConfigGamepadInputId.RightStick,
|
||||||
InvertStickX = false,
|
InvertStickX = false,
|
||||||
InvertStickY = false,
|
InvertStickY = false,
|
||||||
|
Rotate90CW = false,
|
||||||
},
|
},
|
||||||
|
|
||||||
Motion = new StandardMotionConfigController
|
Motion = new StandardMotionConfigController
|
||||||
|
@ -740,6 +740,19 @@
|
|||||||
<property name="top_attach">1</property>
|
<property name="top_attach">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckButton" id="_rotateL90CW">
|
||||||
|
<property name="label" translatable="yes">Rotate 90° Clockwise</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">2</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
@ -1697,6 +1710,19 @@
|
|||||||
<property name="top_attach">1</property>
|
<property name="top_attach">1</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckButton" id="_rotateR90CW">
|
||||||
|
<property name="label" translatable="yes">Rotate 90° Clockwise</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">2</property>
|
||||||
|
<property name="top_attach">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
|
Reference in New Issue
Block a user