Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2f47f978a0 | |||
| 0282b216fd | |||
| f2ade5f1f8 | |||
| 965fb9dd5f | |||
| e2a5e69f4c | |||
| 7f27b791f8 | |||
| 3ca8618f5f |
@@ -42,7 +42,7 @@
|
|||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
||||||
<PackageVersion Include="Gommon" Version="2.7.1.1" />
|
<PackageVersion Include="Gommon" Version="2.7.1" />
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
<PackageVersion Include="Sep" Version="0.6.0" />
|
<PackageVersion Include="Sep" Version="0.6.0" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
|
|
||||||
// Make sure all pending uniform buffer data is written to memory.
|
// Make sure all pending uniform buffer data is written to memory.
|
||||||
_3dEngine.FlushUboDirty();
|
_3dEngine.FlushUboDirty();
|
||||||
|
|
||||||
uint qmdAddress = _state.State.SendPcasA;
|
uint qmdAddress = _state.State.SendPcasA;
|
||||||
|
|
||||||
ComputeQmd qmd = _channel.MemoryManager.Read<ComputeQmd>((ulong)qmdAddress << 8);
|
ComputeQmd qmd = _channel.MemoryManager.Read<ComputeQmd>((ulong)qmdAddress << 8);
|
||||||
@@ -106,6 +106,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
ulong shaderGpuVa = ((ulong)_state.State.SetProgramRegionAAddressUpper << 32) | _state.State.SetProgramRegionB;
|
ulong shaderGpuVa = ((ulong)_state.State.SetProgramRegionAAddressUpper << 32) | _state.State.SetProgramRegionB;
|
||||||
|
|
||||||
shaderGpuVa += (uint)qmd.ProgramOffset;
|
shaderGpuVa += (uint)qmd.ProgramOffset;
|
||||||
|
|
||||||
|
ShaderCache shaderCache = memoryManager.GetBackingMemory(shaderGpuVa).ShaderCache;
|
||||||
|
|
||||||
int localMemorySize = qmd.ShaderLocalMemoryLowSize + qmd.ShaderLocalMemoryHighSize;
|
int localMemorySize = qmd.ShaderLocalMemoryLowSize + qmd.ShaderLocalMemoryHighSize;
|
||||||
|
|
||||||
@@ -142,7 +144,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
sharedMemorySize,
|
sharedMemorySize,
|
||||||
_channel.BufferManager.HasUnalignedStorageBuffers);
|
_channel.BufferManager.HasUnalignedStorageBuffers);
|
||||||
|
|
||||||
CachedShaderProgram cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
|
CachedShaderProgram cs = shaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
|
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
|
||||||
|
|
||||||
@@ -156,10 +158,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
{
|
{
|
||||||
BufferDescriptor sb = info.SBuffers[index];
|
BufferDescriptor sb = info.SBuffers[index];
|
||||||
|
|
||||||
ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(sb.SbCbSlot);
|
(PhysicalMemory physical, ulong sbDescAddress) = _channel.BufferManager.GetComputeUniformBufferAddress(sb.SbCbSlot);
|
||||||
sbDescAddress += (ulong)sb.SbCbOffset * 4;
|
sbDescAddress += (ulong)sb.SbCbOffset * 4;
|
||||||
|
|
||||||
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
|
SbDescriptor sbDescriptor = physical.Read<SbDescriptor>(sbDescAddress);
|
||||||
|
|
||||||
uint size;
|
uint size;
|
||||||
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
|
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
|
||||||
@@ -187,7 +189,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
sharedMemorySize,
|
sharedMemorySize,
|
||||||
_channel.BufferManager.HasUnalignedStorageBuffers);
|
_channel.BufferManager.HasUnalignedStorageBuffers);
|
||||||
|
|
||||||
cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
|
cs = shaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa);
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
|
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,7 +215,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
_channel.TextureManager.RefreshModifiedTextures();
|
_channel.TextureManager.RefreshModifiedTextures();
|
||||||
_3dEngine.CreatePendingSyncs();
|
_3dEngine.CreatePendingSyncs();
|
||||||
_3dEngine.FlushUboDirty();
|
_3dEngine.FlushUboDirty();
|
||||||
|
|
||||||
|
PhysicalMemory srcPhysical = memoryManager.GetBackingMemory(srcGpuVa);
|
||||||
|
PhysicalMemory dstPhysical = memoryManager.GetBackingMemory(dstGpuVa);
|
||||||
|
|
||||||
if (copy2D)
|
if (copy2D)
|
||||||
{
|
{
|
||||||
// Buffer to texture copy.
|
// Buffer to texture copy.
|
||||||
@@ -293,7 +296,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
|
|
||||||
if (completeSource && completeDest && !srcLinear && isIdentityRemap)
|
if (completeSource && completeDest && !srcLinear && isIdentityRemap)
|
||||||
{
|
{
|
||||||
Image.Texture source = memoryManager.Physical.TextureCache.FindTexture(
|
Image.Texture source = srcPhysical.TextureCache.FindTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
srcGpuVa,
|
srcGpuVa,
|
||||||
srcBpp,
|
srcBpp,
|
||||||
@@ -309,7 +312,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
{
|
{
|
||||||
source.SynchronizeMemory();
|
source.SynchronizeMemory();
|
||||||
|
|
||||||
Image.Texture target = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
Image.Texture target = dstPhysical.TextureCache.FindOrCreateTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
source.Info.FormatInfo,
|
source.Info.FormatInfo,
|
||||||
dstGpuVa,
|
dstGpuVa,
|
||||||
@@ -339,7 +342,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
|
|
||||||
if (completeSource && completeDest && !(dstLinear && !srcLinear) && isIdentityRemap)
|
if (completeSource && completeDest && !(dstLinear && !srcLinear) && isIdentityRemap)
|
||||||
{
|
{
|
||||||
Image.Texture target = memoryManager.Physical.TextureCache.FindTexture(
|
Image.Texture target = dstPhysical.TextureCache.FindTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
dstGpuVa,
|
dstGpuVa,
|
||||||
dstBpp,
|
dstBpp,
|
||||||
@@ -462,6 +465,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
BufferCache bufferCache = dstPhysical.BufferCache;
|
||||||
if (remap &&
|
if (remap &&
|
||||||
_state.State.SetRemapComponentsDstX == SetRemapComponentsDst.ConstA &&
|
_state.State.SetRemapComponentsDstX == SetRemapComponentsDst.ConstA &&
|
||||||
_state.State.SetRemapComponentsDstY == SetRemapComponentsDst.ConstA &&
|
_state.State.SetRemapComponentsDstY == SetRemapComponentsDst.ConstA &&
|
||||||
@@ -472,7 +476,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
_state.State.SetRemapComponentsComponentSize == SetRemapComponentsComponentSize.Four)
|
_state.State.SetRemapComponentsComponentSize == SetRemapComponentsComponentSize.Four)
|
||||||
{
|
{
|
||||||
// Fast path for clears when remap is enabled.
|
// Fast path for clears when remap is enabled.
|
||||||
memoryManager.Physical.BufferCache.ClearBuffer(memoryManager, dstGpuVa, size * 4, _state.State.SetRemapConstA);
|
bufferCache.ClearBuffer(memoryManager, dstGpuVa, size * 4, _state.State.SetRemapConstA);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -492,7 +496,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
memoryManager.Physical.BufferCache.CopyBuffer(memoryManager, srcGpuVa, dstGpuVa, size);
|
BufferCache.CopyBuffer(_context,memoryManager, srcGpuVa, dstGpuVa, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
|
|||||||
// Right now the copy code at the bottom assumes that it is used on both which might be incorrect.
|
// Right now the copy code at the bottom assumes that it is used on both which might be incorrect.
|
||||||
if (!_isLinear)
|
if (!_isLinear)
|
||||||
{
|
{
|
||||||
Image.Texture target = memoryManager.Physical.TextureCache.FindTexture(
|
Image.Texture target = memoryManager.GetBackingMemory(_dstGpuVa).TextureCache.FindTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
_dstGpuVa,
|
_dstGpuVa,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
|||||||
|
|
||||||
ulong indirectBufferGpuVa = count.GpuVa;
|
ulong indirectBufferGpuVa = count.GpuVa;
|
||||||
|
|
||||||
BufferCache bufferCache = _processor.MemoryManager.Physical.BufferCache;
|
BufferCache bufferCache = _processor.MemoryManager.GetBackingMemory(indirectBufferGpuVa).BufferCache;
|
||||||
|
|
||||||
bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress);
|
bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress);
|
||||||
|
|
||||||
@@ -394,6 +394,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
|||||||
|
|
||||||
_processor.ThreedClass.DrawIndirect(
|
_processor.ThreedClass.DrawIndirect(
|
||||||
topology,
|
topology,
|
||||||
|
bufferCache,
|
||||||
|
null,
|
||||||
new MultiRange(indirectBufferAddress, IndirectIndexedDataEntrySize),
|
new MultiRange(indirectBufferAddress, IndirectIndexedDataEntrySize),
|
||||||
default,
|
default,
|
||||||
1,
|
1,
|
||||||
@@ -491,22 +493,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BufferCache indirectBufferCache = _processor.MemoryManager.GetBackingMemory(indirectBufferGpuVa).BufferCache;
|
||||||
BufferCache bufferCache = _processor.MemoryManager.Physical.BufferCache;
|
BufferCache parameterBufferCache = _processor.MemoryManager.GetBackingMemory(parameterBufferGpuVa).BufferCache;
|
||||||
|
|
||||||
ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride;
|
ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride;
|
||||||
|
|
||||||
MultiRange indirectBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize, BufferStage.Indirect);
|
MultiRange indirectBufferRange = indirectBufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize, BufferStage.Indirect);
|
||||||
MultiRange parameterBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4, BufferStage.Indirect);
|
MultiRange parameterBufferRange = parameterBufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4, BufferStage.Indirect);
|
||||||
|
|
||||||
_processor.ThreedClass.DrawIndirect(
|
_processor.ThreedClass.DrawIndirect(
|
||||||
topology,
|
topology,
|
||||||
|
indirectBufferCache,
|
||||||
|
parameterBufferCache,
|
||||||
indirectBufferRange,
|
indirectBufferRange,
|
||||||
parameterBufferRange,
|
parameterBufferRange,
|
||||||
maxDrawCount,
|
maxDrawCount,
|
||||||
stride,
|
stride,
|
||||||
indexCount,
|
indexCount,
|
||||||
Threed.IndirectDrawType.DrawIndexedIndirectCount);
|
IndirectDrawType.DrawIndexedIndirectCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class VtgAsComputeContext : IDisposable
|
class VtgAsComputeContext : IDisposable
|
||||||
{
|
{
|
||||||
|
private const int DummyBufferSize = 16;
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -46,7 +48,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
format.GetBytesPerElement(),
|
1,
|
||||||
format,
|
format,
|
||||||
DepthStencilMode.Depth,
|
DepthStencilMode.Depth,
|
||||||
Target.TextureBuffer,
|
Target.TextureBuffer,
|
||||||
@@ -519,6 +521,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
return new BufferRange(_geometryIndexDataBuffer.Handle, offset, size, write);
|
return new BufferRange(_geometryIndexDataBuffer.Handle, offset, size, write);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the range for a dummy 16 bytes buffer, filled with zeros.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Dummy buffer range</returns>
|
||||||
|
public BufferRange GetDummyBufferRange()
|
||||||
|
{
|
||||||
|
if (_dummyBuffer == BufferHandle.Null)
|
||||||
|
{
|
||||||
|
_dummyBuffer = _context.Renderer.CreateBuffer(DummyBufferSize, BufferAccess.DeviceMemory);
|
||||||
|
_context.Renderer.Pipeline.ClearBuffer(_dummyBuffer, 0, DummyBufferSize, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BufferRange(_dummyBuffer, 0, DummyBufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the range for a sequential index buffer, with ever incrementing index values.
|
/// Gets the range for a sequential index buffer, with ever incrementing index values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
{
|
{
|
||||||
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
|
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
|
||||||
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
|
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
|
||||||
|
SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,12 +163,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
{
|
{
|
||||||
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
|
_vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount);
|
||||||
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
|
_vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0);
|
||||||
|
SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int vbStride = vertexBuffer.UnpackStride();
|
int vbStride = vertexBuffer.UnpackStride();
|
||||||
ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count);
|
ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count);
|
||||||
|
|
||||||
|
ulong oldVbSize = vbSize;
|
||||||
|
|
||||||
ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset();
|
ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset();
|
||||||
int componentSize = format.GetScalarSize();
|
int componentSize = format.GetScalarSize();
|
||||||
|
|
||||||
@@ -196,11 +200,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
|
|
||||||
int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding;
|
int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding;
|
||||||
BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize);
|
BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize);
|
||||||
_context.Renderer.Pipeline.SetUniformBuffers([new BufferAssignment(vertexInfoBinding, vertexInfoRange)]);
|
_context.Renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(vertexInfoBinding, vertexInfoRange) });
|
||||||
|
|
||||||
int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding;
|
int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding;
|
||||||
BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: true);
|
BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: true);
|
||||||
_context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexDataRange)]);
|
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexDataRange) });
|
||||||
|
|
||||||
_vacContext.VertexInfoBufferUpdater.Commit();
|
_vacContext.VertexInfoBufferUpdater.Commit();
|
||||||
|
|
||||||
@@ -228,7 +232,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
|
|
||||||
int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding;
|
int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding;
|
||||||
BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize);
|
BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize);
|
||||||
_context.Renderer.Pipeline.SetUniformBuffers([new BufferAssignment(vertexInfoBinding, vertexInfoRange)]);
|
_context.Renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(vertexInfoBinding, vertexInfoRange) });
|
||||||
|
|
||||||
int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding;
|
int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding;
|
||||||
|
|
||||||
@@ -246,11 +250,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
BufferRange vertexBuffer = _vacContext.GetGeometryVertexDataBufferRange(_geometryVertexDataOffset, _geometryVertexDataSize, write: true);
|
BufferRange vertexBuffer = _vacContext.GetGeometryVertexDataBufferRange(_geometryVertexDataOffset, _geometryVertexDataSize, write: true);
|
||||||
BufferRange indexBuffer = _vacContext.GetGeometryIndexDataBufferRange(_geometryIndexDataOffset, _geometryIndexDataSize, write: true);
|
BufferRange indexBuffer = _vacContext.GetGeometryIndexDataBufferRange(_geometryIndexDataOffset, _geometryIndexDataSize, write: true);
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetStorageBuffers([
|
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[]
|
||||||
|
{
|
||||||
new BufferAssignment(vertexDataBinding, vertexDataRange),
|
new BufferAssignment(vertexDataBinding, vertexDataRange),
|
||||||
new BufferAssignment(geometryVbBinding, vertexBuffer),
|
new BufferAssignment(geometryVbBinding, vertexBuffer),
|
||||||
new BufferAssignment(geometryIbBinding, indexBuffer)
|
new BufferAssignment(geometryIbBinding, indexBuffer),
|
||||||
]);
|
});
|
||||||
|
|
||||||
_context.Renderer.Pipeline.DispatchCompute(
|
_context.Renderer.Pipeline.DispatchCompute(
|
||||||
BitUtils.DivRoundUp(primitivesCount, ComputeLocalSize),
|
BitUtils.DivRoundUp(primitivesCount, ComputeLocalSize),
|
||||||
@@ -294,7 +299,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
|
|
||||||
_context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram);
|
_context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram);
|
||||||
_context.Renderer.Pipeline.SetIndexBuffer(indexBuffer, IndexType.UInt);
|
_context.Renderer.Pipeline.SetIndexBuffer(indexBuffer, IndexType.UInt);
|
||||||
_context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexBuffer)]);
|
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexBuffer) });
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetPrimitiveRestart(true, -1);
|
_context.Renderer.Pipeline.SetPrimitiveRestart(true, -1);
|
||||||
_context.Renderer.Pipeline.SetPrimitiveTopology(GetGeometryOutputTopology(_geometryAsCompute.Info.GeometryVerticesPerPrimitive));
|
_context.Renderer.Pipeline.SetPrimitiveTopology(GetGeometryOutputTopology(_geometryAsCompute.Info.GeometryVerticesPerPrimitive));
|
||||||
@@ -309,7 +314,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: false);
|
BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: false);
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram);
|
_context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram);
|
||||||
_context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexDataRange)]);
|
_context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexDataRange) });
|
||||||
_context.Renderer.Pipeline.Draw(_count, _instanceCount, 0, 0);
|
_context.Renderer.Pipeline.Draw(_count, _instanceCount, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,6 +345,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
return maxOutputVertices / verticesPerPrimitive;
|
return maxOutputVertices / verticesPerPrimitive;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binds a dummy buffer as vertex buffer into a buffer texture.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reservations">Shader resource binding reservations</param>
|
||||||
|
/// <param name="index">Buffer texture index</param>
|
||||||
|
/// <param name="format">Buffer texture format</param>
|
||||||
|
private readonly void SetDummyBufferTexture(ResourceReservations reservations, int index, Format format)
|
||||||
|
{
|
||||||
|
ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format);
|
||||||
|
bufferTexture.SetStorage(_vacContext.GetDummyBufferRange());
|
||||||
|
|
||||||
|
_context.Renderer.Pipeline.SetTextureAndSampler(ShaderStage.Compute, reservations.GetVertexBufferTextureBinding(index), bufferTexture, null);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Binds a vertex buffer into a buffer texture.
|
/// Binds a vertex buffer into a buffer texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -352,7 +371,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
{
|
{
|
||||||
MemoryManager memoryManager = _channel.MemoryManager;
|
MemoryManager memoryManager = _channel.MemoryManager;
|
||||||
|
|
||||||
BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(memoryManager.GetPhysicalRegions(address, size), BufferStage.VertexBuffer);
|
BufferRange range = memoryManager.GetBackingMemory(address).BufferCache.GetBufferRange(memoryManager.GetPhysicalRegions(address, size), BufferStage.VertexBuffer);
|
||||||
|
|
||||||
ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format);
|
ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format);
|
||||||
bufferTexture.SetStorage(range);
|
bufferTexture.SetStorage(range);
|
||||||
@@ -394,7 +413,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw
|
|||||||
MemoryManager memoryManager = _channel.MemoryManager;
|
MemoryManager memoryManager = _channel.MemoryManager;
|
||||||
|
|
||||||
ulong misalign = address & ((ulong)_context.Capabilities.TextureBufferOffsetAlignment - 1);
|
ulong misalign = address & ((ulong)_context.Capabilities.TextureBufferOffsetAlignment - 1);
|
||||||
BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(
|
BufferRange range = memoryManager.GetBackingMemory(address).BufferCache.GetBufferRange(
|
||||||
memoryManager.GetPhysicalRegions(address + indexOffset - misalign, size + misalign),
|
memoryManager.GetPhysicalRegions(address + indexOffset - misalign, size + misalign),
|
||||||
BufferStage.IndexBuffer);
|
BufferStage.IndexBuffer);
|
||||||
misalignedOffset = (int)misalign >> shift;
|
misalignedOffset = (int)misalign >> shift;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
// State associated with direct uniform buffer updates.
|
// State associated with direct uniform buffer updates.
|
||||||
// This state is used to attempt to batch together consecutive updates.
|
// This state is used to attempt to batch together consecutive updates.
|
||||||
|
private ulong _ubBeginGpuAddress = 0;
|
||||||
private ulong _ubBeginCpuAddress = 0;
|
private ulong _ubBeginCpuAddress = 0;
|
||||||
private ulong _ubFollowUpAddress = 0;
|
private ulong _ubFollowUpAddress = 0;
|
||||||
private ulong _ubByteCount = 0;
|
private ulong _ubByteCount = 0;
|
||||||
@@ -113,12 +114,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
if (_ubFollowUpAddress != 0)
|
if (_ubFollowUpAddress != 0)
|
||||||
{
|
{
|
||||||
MemoryManager memoryManager = _channel.MemoryManager;
|
MemoryManager memoryManager = _channel.MemoryManager;
|
||||||
|
PhysicalMemory physicalMemory = memoryManager.GetBackingMemory(_ubBeginGpuAddress);
|
||||||
|
|
||||||
Span<byte> data = MemoryMarshal.Cast<int, byte>(_ubData.AsSpan(0, (int)(_ubByteCount / 4)));
|
Span<byte> data = MemoryMarshal.Cast<int, byte>(_ubData.AsSpan(0, (int)(_ubByteCount / 4)));
|
||||||
|
|
||||||
if (memoryManager.Physical.WriteWithRedundancyCheck(_ubBeginCpuAddress, data))
|
if (physicalMemory.WriteWithRedundancyCheck(_ubBeginCpuAddress, data))
|
||||||
{
|
{
|
||||||
memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
|
physicalMemory.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ubFollowUpAddress = 0;
|
_ubFollowUpAddress = 0;
|
||||||
|
|||||||
@@ -641,6 +641,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
public void DrawIndirect(
|
public void DrawIndirect(
|
||||||
ThreedClass engine,
|
ThreedClass engine,
|
||||||
PrimitiveTopology topology,
|
PrimitiveTopology topology,
|
||||||
|
BufferCache indirectBufferCache,
|
||||||
|
BufferCache parameterBufferCache,
|
||||||
MultiRange indirectBufferRange,
|
MultiRange indirectBufferRange,
|
||||||
MultiRange parameterBufferRange,
|
MultiRange parameterBufferRange,
|
||||||
int maxDrawCount,
|
int maxDrawCount,
|
||||||
@@ -662,8 +664,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicalMemory memory = _channel.MemoryManager.Physical;
|
|
||||||
|
|
||||||
bool hasCount = (drawType & IndirectDrawType.Count) != 0;
|
bool hasCount = (drawType & IndirectDrawType.Count) != 0;
|
||||||
bool indexed = (drawType & IndirectDrawType.Indexed) != 0;
|
bool indexed = (drawType & IndirectDrawType.Indexed) != 0;
|
||||||
|
|
||||||
@@ -684,8 +684,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
if (hasCount)
|
if (hasCount)
|
||||||
{
|
{
|
||||||
BufferRange indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
|
BufferRange indirectBuffer = indirectBufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
|
||||||
BufferRange parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferRange, BufferStage.Indirect);
|
BufferRange parameterBuffer = parameterBufferCache.GetBufferRange(parameterBufferRange, BufferStage.Indirect);
|
||||||
|
|
||||||
if (indexed)
|
if (indexed)
|
||||||
{
|
{
|
||||||
@@ -698,7 +698,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
BufferRange indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
|
BufferRange indirectBuffer = indirectBufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect);
|
||||||
|
|
||||||
if (indexed)
|
if (indexed)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -381,10 +381,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
{
|
{
|
||||||
BufferDescriptor sb = info.SBuffers[index];
|
BufferDescriptor sb = info.SBuffers[index];
|
||||||
|
|
||||||
ulong sbDescAddress = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, sb.SbCbSlot);
|
(PhysicalMemory physical, ulong sbDescAddress) = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, sb.SbCbSlot);
|
||||||
sbDescAddress += (ulong)sb.SbCbOffset * 4;
|
sbDescAddress += (ulong)sb.SbCbOffset * 4;
|
||||||
|
|
||||||
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
|
SbDescriptor sbDescriptor = physical.Read<SbDescriptor>(sbDescAddress);
|
||||||
|
|
||||||
uint size;
|
uint size;
|
||||||
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
|
if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer)
|
||||||
@@ -505,7 +505,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
rtNoAlphaMask |= 1u << index;
|
rtNoAlphaMask |= 1u << index;
|
||||||
}
|
}
|
||||||
|
|
||||||
Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
TextureCache colorTextureCache = memoryManager.GetBackingMemory(colorState.Address.Pack()).TextureCache;
|
||||||
|
|
||||||
|
Image.Texture color = colorTextureCache.FindOrCreateTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
colorState,
|
colorState,
|
||||||
_vtgWritesRtLayer || layered,
|
_vtgWritesRtLayer || layered,
|
||||||
@@ -513,7 +515,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
samplesInX,
|
samplesInX,
|
||||||
samplesInY,
|
samplesInY,
|
||||||
sizeHint);
|
sizeHint);
|
||||||
|
|
||||||
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color);
|
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color);
|
||||||
|
|
||||||
if (color != null)
|
if (color != null)
|
||||||
@@ -543,8 +545,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
{
|
{
|
||||||
RtDepthStencilState dsState = _state.State.RtDepthStencilState;
|
RtDepthStencilState dsState = _state.State.RtDepthStencilState;
|
||||||
Size3D dsSize = _state.State.RtDepthStencilSize;
|
Size3D dsSize = _state.State.RtDepthStencilSize;
|
||||||
|
TextureCache dsTextureCache = memoryManager.GetBackingMemory(dsState.Address.Pack()).TextureCache;
|
||||||
depthStencil = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
|
||||||
|
depthStencil = dsTextureCache.FindOrCreateTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
dsState,
|
dsState,
|
||||||
dsSize,
|
dsSize,
|
||||||
@@ -1409,8 +1412,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateShaderState()
|
private void UpdateShaderState()
|
||||||
{
|
{
|
||||||
ShaderCache shaderCache = _channel.MemoryManager.Physical.ShaderCache;
|
|
||||||
|
|
||||||
_vtgWritesRtLayer = false;
|
_vtgWritesRtLayer = false;
|
||||||
|
|
||||||
ShaderAddresses addresses = new();
|
ShaderAddresses addresses = new();
|
||||||
@@ -1433,6 +1434,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
? _state.State.TexturePoolState.MaximumId
|
? _state.State.TexturePoolState.MaximumId
|
||||||
: _state.State.SamplerPoolState.MaximumId;
|
: _state.State.SamplerPoolState.MaximumId;
|
||||||
|
|
||||||
|
// Shader stages on different address spaces are not supported right now,
|
||||||
|
// but it should never happen in practice anyway.
|
||||||
|
ShaderCache shaderCache = _channel.MemoryManager.GetBackingMemory(addresses.VertexB).ShaderCache;
|
||||||
CachedShaderProgram gs = shaderCache.GetGraphicsShader(
|
CachedShaderProgram gs = shaderCache.GetGraphicsShader(
|
||||||
ref _state.State,
|
ref _state.State,
|
||||||
ref _pipeline,
|
ref _pipeline,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
|||||||
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
|
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
|
using Ryujinx.Graphics.Gpu.Engine.Threed.Blender;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Gpu.Synchronization;
|
using Ryujinx.Graphics.Gpu.Synchronization;
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
@@ -804,6 +805,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// Performs a indirect draw, with parameters from a GPU buffer.
|
/// Performs a indirect draw, with parameters from a GPU buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="topology">Primitive topology</param>
|
/// <param name="topology">Primitive topology</param>
|
||||||
|
/// <param name="indirectBufferCache">Buffer cache owning the buffer with the draw parameters</param>
|
||||||
|
/// <param name="parameterBufferCache">Buffer cache owning the buffer with the draw count</param>
|
||||||
/// <param name="indirectBufferRange">Memory range of the buffer with the draw parameters, such as count, first index, etc</param>
|
/// <param name="indirectBufferRange">Memory range of the buffer with the draw parameters, such as count, first index, etc</param>
|
||||||
/// <param name="parameterBufferRange">Memory range of the buffer with the draw count</param>
|
/// <param name="parameterBufferRange">Memory range of the buffer with the draw count</param>
|
||||||
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
|
/// <param name="maxDrawCount">Maximum number of draws that can be made</param>
|
||||||
@@ -812,6 +815,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param>
|
/// <param name="drawType">Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count</param>
|
||||||
public void DrawIndirect(
|
public void DrawIndirect(
|
||||||
PrimitiveTopology topology,
|
PrimitiveTopology topology,
|
||||||
|
BufferCache indirectBufferCache,
|
||||||
|
BufferCache parameterBufferCache,
|
||||||
MultiRange indirectBufferRange,
|
MultiRange indirectBufferRange,
|
||||||
MultiRange parameterBufferRange,
|
MultiRange parameterBufferRange,
|
||||||
int maxDrawCount,
|
int maxDrawCount,
|
||||||
@@ -819,7 +824,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
int indexCount,
|
int indexCount,
|
||||||
IndirectDrawType drawType)
|
IndirectDrawType drawType)
|
||||||
{
|
{
|
||||||
_drawManager.DrawIndirect(this, topology, indirectBufferRange, parameterBufferRange, maxDrawCount, stride, indexCount, drawType);
|
_drawManager.DrawIndirect(this, topology, indirectBufferCache, parameterBufferCache, indirectBufferRange, parameterBufferRange, maxDrawCount, stride, indexCount, drawType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -233,6 +233,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
|
|||||||
|
|
||||||
TwodTexture dstCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetDstFormat);
|
TwodTexture dstCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetDstFormat);
|
||||||
TwodTexture srcCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetSrcFormat);
|
TwodTexture srcCopyTexture = Unsafe.As<uint, TwodTexture>(ref _state.State.SetSrcFormat);
|
||||||
|
|
||||||
|
TextureCache srcTextureCache = memoryManager.GetBackingMemory(srcCopyTexture.Address.Pack()).TextureCache;
|
||||||
|
TextureCache dstTextureCache = memoryManager.GetBackingMemory(dstCopyTexture.Address.Pack()).TextureCache;
|
||||||
|
|
||||||
long srcX = ((long)_state.State.SetPixelsFromMemorySrcX0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcX0Frac;
|
long srcX = ((long)_state.State.SetPixelsFromMemorySrcX0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcX0Frac;
|
||||||
long srcY = ((long)_state.State.PixelsFromMemorySrcY0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcY0Frac;
|
long srcY = ((long)_state.State.PixelsFromMemorySrcY0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcY0Frac;
|
||||||
@@ -305,7 +308,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
|
|||||||
// are the same, as we can't blit between different depth formats.
|
// are the same, as we can't blit between different depth formats.
|
||||||
bool srcDepthAlias = srcCopyTexture.Format == dstCopyTexture.Format;
|
bool srcDepthAlias = srcCopyTexture.Format == dstCopyTexture.Format;
|
||||||
|
|
||||||
Image.Texture srcTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
Image.Texture srcTexture = srcTextureCache.FindOrCreateTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
srcCopyTexture,
|
srcCopyTexture,
|
||||||
offset,
|
offset,
|
||||||
@@ -326,7 +329,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
memoryManager.Physical.TextureCache.Lift(srcTexture);
|
srcTextureCache.Lift(srcTexture);
|
||||||
|
|
||||||
// When the source texture that was found has a depth format,
|
// When the source texture that was found has a depth format,
|
||||||
// we must enforce the target texture also has a depth format,
|
// we must enforce the target texture also has a depth format,
|
||||||
@@ -342,7 +345,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
|
|||||||
dstCopyTextureFormat = dstCopyTexture.Format.Convert();
|
dstCopyTextureFormat = dstCopyTexture.Format.Convert();
|
||||||
}
|
}
|
||||||
|
|
||||||
Image.Texture dstTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
Image.Texture dstTexture = dstTextureCache.FindOrCreateTexture(
|
||||||
memoryManager,
|
memoryManager,
|
||||||
dstCopyTexture,
|
dstCopyTexture,
|
||||||
0,
|
0,
|
||||||
|
|||||||
@@ -58,22 +58,24 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
public void BindMemory(MemoryManager memoryManager)
|
public void BindMemory(MemoryManager memoryManager)
|
||||||
{
|
{
|
||||||
MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, memoryManager ?? throw new ArgumentNullException(nameof(memoryManager)));
|
MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, memoryManager ?? throw new ArgumentNullException(nameof(memoryManager)));
|
||||||
|
if (oldMemoryManager == memoryManager)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
memoryManager.Physical.IncrementReferenceCount();
|
memoryManager.AttachToChannel(BufferManager.Rebind);
|
||||||
|
|
||||||
if (oldMemoryManager != null)
|
if (oldMemoryManager != null)
|
||||||
{
|
{
|
||||||
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
|
oldMemoryManager.DetachFromChannel(BufferManager.Rebind);
|
||||||
oldMemoryManager.Physical.DecrementReferenceCount();
|
|
||||||
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
|
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind;
|
|
||||||
memoryManager.MemoryUnmapped += MemoryUnmappedHandler;
|
memoryManager.MemoryUnmapped += MemoryUnmappedHandler;
|
||||||
|
|
||||||
// Since the memory manager changed, make sure we will get pools from addresses of the new memory manager.
|
// Since the memory manager changed, make sure we will get pools from addresses of the new memory manager.
|
||||||
TextureManager.ReloadPools();
|
TextureManager.ReloadPools();
|
||||||
memoryManager.Physical.BufferCache.QueuePrune();
|
memoryManager.QueuePrune();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -86,7 +88,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
TextureManager.ReloadPools();
|
TextureManager.ReloadPools();
|
||||||
|
|
||||||
MemoryManager memoryManager = Volatile.Read(ref _memoryManager);
|
MemoryManager memoryManager = Volatile.Read(ref _memoryManager);
|
||||||
memoryManager?.Physical.BufferCache.QueuePrune();
|
memoryManager?.QueuePrune();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -141,8 +143,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, null);
|
MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, null);
|
||||||
if (oldMemoryManager != null)
|
if (oldMemoryManager != null)
|
||||||
{
|
{
|
||||||
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
|
oldMemoryManager.DetachFromChannel(BufferManager.Rebind);
|
||||||
oldMemoryManager.Physical.DecrementReferenceCount();
|
|
||||||
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
|
oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
|||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Gpu.Shader;
|
using Ryujinx.Graphics.Gpu.Shader;
|
||||||
using Ryujinx.Graphics.Gpu.Synchronization;
|
using Ryujinx.Graphics.Gpu.Synchronization;
|
||||||
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -172,7 +173,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid));
|
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MemoryManager(physicalMemory, cpuMemorySize);
|
return new MemoryManager(this, physicalMemory, cpuMemorySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -197,7 +198,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// <param name="pid">ID of the process that owns <paramref name="cpuMemory"/></param>
|
/// <param name="pid">ID of the process that owns <paramref name="cpuMemory"/></param>
|
||||||
/// <param name="cpuMemory">Virtual memory owned by the process</param>
|
/// <param name="cpuMemory">Virtual memory owned by the process</param>
|
||||||
/// <exception cref="ArgumentException">Thrown if <paramref name="pid"/> was already registered</exception>
|
/// <exception cref="ArgumentException">Thrown if <paramref name="pid"/> was already registered</exception>
|
||||||
public void RegisterProcess(ulong pid, Cpu.IVirtualMemoryManagerTracked cpuMemory)
|
public void RegisterProcess(ulong pid, IVirtualMemoryManagerTracked cpuMemory)
|
||||||
{
|
{
|
||||||
PhysicalMemory physicalMemory = new(this, cpuMemory);
|
PhysicalMemory physicalMemory = new(this, cpuMemory);
|
||||||
if (!PhysicalMemoryRegistry.TryAdd(pid, physicalMemory))
|
if (!PhysicalMemoryRegistry.TryAdd(pid, physicalMemory))
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
@@ -64,7 +65,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="maximumId">Maximum ID of the texture pool</param>
|
/// <param name="maximumId">Maximum ID of the texture pool</param>
|
||||||
/// <param name="bindingsArrayCache">Cache of texture array bindings</param>
|
/// <param name="bindingsArrayCache">Cache of texture array bindings</param>
|
||||||
/// <returns>The found or newly created texture pool</returns>
|
/// <returns>The found or newly created texture pool</returns>
|
||||||
public T FindOrCreate(GpuChannel channel, ulong address, int maximumId, TextureBindingsArrayCache bindingsArrayCache)
|
public T FindOrCreate(GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId, TextureBindingsArrayCache bindingsArrayCache)
|
||||||
{
|
{
|
||||||
// Remove old entries from the cache, if possible.
|
// Remove old entries from the cache, if possible.
|
||||||
while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval)
|
while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval)
|
||||||
@@ -99,7 +100,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If not found, create a new one.
|
// If not found, create a new one.
|
||||||
pool = CreatePool(_context, channel, address, maximumId);
|
pool = CreatePool(_context, channel, physicalMemory, address, maximumId);
|
||||||
|
|
||||||
pool.CacheNode = _pools.AddLast(pool);
|
pool.CacheNode = _pools.AddLast(pool);
|
||||||
pool.CacheTimestamp = _currentTimestamp;
|
pool.CacheTimestamp = _currentTimestamp;
|
||||||
@@ -112,9 +113,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the pool belongs to</param>
|
/// <param name="context">GPU context that the pool belongs to</param>
|
||||||
/// <param name="channel">GPU channel that the pool belongs to</param>
|
/// <param name="channel">GPU channel that the pool belongs to</param>
|
||||||
|
/// <param name="physicalMemory">GPU backing memory of the pool</param>
|
||||||
/// <param name="address">Address of the pool in guest memory</param>
|
/// <param name="address">Address of the pool in guest memory</param>
|
||||||
/// <param name="maximumId">Maximum ID of the pool (equal to maximum minus one)</param>
|
/// <param name="maximumId">Maximum ID of the pool (equal to maximum minus one)</param>
|
||||||
protected abstract T CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId);
|
protected abstract T CreatePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId);
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -20,11 +22,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the sampler pool belongs to</param>
|
/// <param name="context">GPU context that the sampler pool belongs to</param>
|
||||||
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
||||||
|
/// <param name="physicalMemory">GPU backing memory of the pool</param>
|
||||||
/// <param name="address">Address of the sampler pool in guest memory</param>
|
/// <param name="address">Address of the sampler pool in guest memory</param>
|
||||||
/// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param>
|
/// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param>
|
||||||
protected override SamplerPool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId)
|
protected override SamplerPool CreatePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId)
|
||||||
{
|
{
|
||||||
return new SamplerPool(context, channel.MemoryManager.Physical, address, maximumId);
|
return new SamplerPool(context, physicalMemory, address, maximumId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -660,6 +660,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
|
ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength];
|
||||||
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
|
ITexture[] textures = new ITexture[bindingInfo.ArrayLength];
|
||||||
|
BufferCache bufferCache = null;
|
||||||
|
|
||||||
for (int index = 0; index < length; index++)
|
for (int index = 0; index < length; index++)
|
||||||
{
|
{
|
||||||
@@ -673,7 +674,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, bindingInfo.FormatInfo, out texture);
|
ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, bindingInfo.FormatInfo, out texture);
|
||||||
|
bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache;
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
entry.Textures[texture] = texture.InvalidatedSequence;
|
entry.Textures[texture] = texture.InvalidatedSequence;
|
||||||
@@ -702,11 +703,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
if (isImage)
|
if (isImage)
|
||||||
{
|
{
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
|
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, bufferCache, texture.Range, bindingInfo, index); }
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index);
|
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, bufferCache, texture.Range, bindingInfo, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (isImage)
|
else if (isImage)
|
||||||
@@ -797,11 +797,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(textureBufferBounds.Range));
|
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(textureBufferBounds.Physical.GetSpan(textureBufferBounds.Range));
|
||||||
|
|
||||||
if (separateSamplerBuffer)
|
if (separateSamplerBuffer)
|
||||||
{
|
{
|
||||||
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(samplerBufferBounds.Range));
|
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(samplerBufferBounds.Physical.GetSpan(samplerBufferBounds.Range));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -828,11 +828,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(textureBufferBounds.Range));
|
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(textureBufferBounds.Physical.GetSpan(textureBufferBounds.Range));
|
||||||
|
|
||||||
if (separateSamplerBuffer)
|
if (separateSamplerBuffer)
|
||||||
{
|
{
|
||||||
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(samplerBufferBounds.Range));
|
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(samplerBufferBounds.Physical.GetSpan(samplerBufferBounds.Range));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -901,16 +900,18 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
if (hostTexture != null && texture.Target == Target.TextureBuffer)
|
||||||
{
|
{
|
||||||
|
BufferCache bufferCache = textureBufferBounds.BufferCache;
|
||||||
|
|
||||||
// Ensure that the buffer texture is using the correct buffer as storage.
|
// Ensure that the buffer texture is using the correct buffer as storage.
|
||||||
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
if (isImage)
|
if (isImage)
|
||||||
{
|
{
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index);
|
_channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, bufferCache, texture.Range, bindingInfo, index);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index);
|
_channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, bufferCache, texture.Range, bindingInfo, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (isImage)
|
else if (isImage)
|
||||||
|
|||||||
@@ -396,7 +396,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex);
|
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex);
|
||||||
|
|
||||||
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Range));
|
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
|
||||||
cachedTextureBufferIndex = textureBufferIndex;
|
cachedTextureBufferIndex = textureBufferIndex;
|
||||||
|
|
||||||
if (samplerBufferIndex == textureBufferIndex)
|
if (samplerBufferIndex == textureBufferIndex)
|
||||||
@@ -410,7 +410,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex);
|
ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex);
|
||||||
|
|
||||||
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(_channel.MemoryManager.Physical.GetSpan(bounds.Range));
|
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
|
||||||
cachedSamplerBufferIndex = samplerBufferIndex;
|
cachedSamplerBufferIndex = samplerBufferIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -524,7 +524,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
// Ensure that the buffer texture is using the correct buffer as storage.
|
// Ensure that the buffer texture is using the correct buffer as storage.
|
||||||
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, false);
|
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache;
|
||||||
|
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, bufferCache, texture.Range, bindingInfo, false);
|
||||||
|
|
||||||
// Cache is not used for buffer texture, it must always rebind.
|
// Cache is not used for buffer texture, it must always rebind.
|
||||||
state.CachedTexture = null;
|
state.CachedTexture = null;
|
||||||
@@ -659,7 +660,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind
|
||||||
// to ensure we're not using a old buffer that was already deleted.
|
// to ensure we're not using a old buffer that was already deleted.
|
||||||
|
|
||||||
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, true);
|
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache;
|
||||||
|
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, bufferCache, texture.Range, bindingInfo, true);
|
||||||
|
|
||||||
// Cache is not used for buffer texture, it must always rebind.
|
// Cache is not used for buffer texture, it must always rebind.
|
||||||
state.CachedTexture = null;
|
state.CachedTexture = null;
|
||||||
@@ -715,9 +717,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
int packedId = ReadPackedId(stageIndex, handle, textureBufferIndex, samplerBufferIndex);
|
int packedId = ReadPackedId(stageIndex, handle, textureBufferIndex, samplerBufferIndex);
|
||||||
int textureId = TextureHandle.UnpackTextureId(packedId);
|
int textureId = TextureHandle.UnpackTextureId(packedId);
|
||||||
|
|
||||||
|
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(poolGpuVa);
|
||||||
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
|
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
|
||||||
|
|
||||||
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache);
|
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, maximumId, _bindingsArrayCache);
|
||||||
|
|
||||||
TextureDescriptor descriptor;
|
TextureDescriptor descriptor;
|
||||||
|
|
||||||
@@ -751,12 +754,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
(int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = TextureHandle.UnpackOffsets(wordOffset);
|
(int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = TextureHandle.UnpackOffsets(wordOffset);
|
||||||
|
|
||||||
ulong textureBufferAddress = _isCompute
|
(PhysicalMemory texturePhysicalMemory, ulong textureBufferAddress) = _isCompute
|
||||||
? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex)
|
? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex)
|
||||||
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex);
|
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex);
|
||||||
|
|
||||||
int handle = textureBufferAddress != MemoryManager.PteUnmapped
|
int handle = textureBufferAddress != MemoryManager.PteUnmapped
|
||||||
? _channel.MemoryManager.Physical.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4)
|
? texturePhysicalMemory.Read<int>(textureBufferAddress + (uint)textureWordOffset * 4)
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
|
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
|
||||||
@@ -771,12 +774,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (handleType != TextureHandleType.SeparateConstantSamplerHandle)
|
if (handleType != TextureHandleType.SeparateConstantSamplerHandle)
|
||||||
{
|
{
|
||||||
ulong samplerBufferAddress = _isCompute
|
(PhysicalMemory samplerPhysicalMemory, ulong samplerBufferAddress) = _isCompute
|
||||||
? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
|
? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
|
||||||
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
|
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
|
||||||
|
|
||||||
samplerHandle = samplerBufferAddress != MemoryManager.PteUnmapped
|
samplerHandle = samplerBufferAddress != MemoryManager.PteUnmapped
|
||||||
? _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4)
|
? samplerPhysicalMemory.Read<int>(samplerBufferAddress + (uint)samplerWordOffset * 4)
|
||||||
: 0;
|
: 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -813,7 +816,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (poolAddress != MemoryManager.PteUnmapped)
|
if (poolAddress != MemoryManager.PteUnmapped)
|
||||||
{
|
{
|
||||||
texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, _texturePoolMaximumId, _bindingsArrayCache);
|
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(_texturePoolGpuVa);
|
||||||
|
texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, _texturePoolMaximumId, _bindingsArrayCache);
|
||||||
_texturePool = texturePool;
|
_texturePool = texturePool;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -824,7 +828,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (poolAddress != MemoryManager.PteUnmapped)
|
if (poolAddress != MemoryManager.PteUnmapped)
|
||||||
{
|
{
|
||||||
samplerPool = _samplerPoolCache.FindOrCreate(_channel, poolAddress, _samplerPoolMaximumId, _bindingsArrayCache);
|
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(_samplerPoolGpuVa);
|
||||||
|
samplerPool = _samplerPoolCache.FindOrCreate(_channel, physical, poolAddress, _samplerPoolMaximumId, _bindingsArrayCache);
|
||||||
_samplerPool = samplerPool;
|
_samplerPool = samplerPool;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.Types;
|
using Ryujinx.Graphics.Gpu.Engine.Types;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Gpu.Shader;
|
using Ryujinx.Graphics.Gpu.Shader;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@@ -385,8 +386,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
public TexturePool GetTexturePool(ulong poolGpuVa, int maximumId)
|
public TexturePool GetTexturePool(ulong poolGpuVa, int maximumId)
|
||||||
{
|
{
|
||||||
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
|
ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa);
|
||||||
|
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(poolAddress);
|
||||||
|
|
||||||
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache);
|
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, maximumId, _bindingsArrayCache);
|
||||||
|
|
||||||
return texturePool;
|
return texturePool;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,9 +160,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the texture pool belongs to</param>
|
/// <param name="context">GPU context that the texture pool belongs to</param>
|
||||||
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
||||||
|
/// <param name="physicalMemory">Backing memory of the pool</param>
|
||||||
/// <param name="address">Address of the texture pool in guest memory</param>
|
/// <param name="address">Address of the texture pool in guest memory</param>
|
||||||
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
|
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
|
||||||
public TexturePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId)
|
public TexturePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId) : base(context, physicalMemory, address, maximumId)
|
||||||
{
|
{
|
||||||
_channel = channel;
|
_channel = channel;
|
||||||
_aliasLists = new Dictionary<Texture, TextureAliasList>();
|
_aliasLists = new Dictionary<Texture, TextureAliasList>();
|
||||||
@@ -193,7 +194,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
TextureInfo info = GetInfo(descriptor, out int layerSize);
|
||||||
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
|
MemoryManager memoryManager = _channel.MemoryManager;
|
||||||
|
TextureCache textureCache = memoryManager.GetBackingMemory(descriptor.UnpackAddress()).TextureCache;
|
||||||
|
texture = textureCache.FindOrCreateTexture(memoryManager, TextureSearchFlags.ForSampler, info, layerSize);
|
||||||
|
|
||||||
// If this happens, then the texture address is invalid, we can't add it to the cache.
|
// If this happens, then the texture address is invalid, we can't add it to the cache.
|
||||||
if (texture == null)
|
if (texture == null)
|
||||||
@@ -421,7 +424,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
MultiRange range = _channel.MemoryManager.Physical.TextureCache.UpdatePartiallyMapped(_channel.MemoryManager, address, texture);
|
TextureCache textureCache = _channel.MemoryManager.GetBackingMemory(address).TextureCache;
|
||||||
|
MultiRange range = textureCache.UpdatePartiallyMapped(_channel.MemoryManager, address, texture);
|
||||||
|
|
||||||
// If the texture is not mapped at all, delete its reference.
|
// If the texture is not mapped at all, delete its reference.
|
||||||
|
|
||||||
@@ -446,7 +450,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
if (!range.Equals(texture.Range))
|
if (!range.Equals(texture.Range))
|
||||||
{
|
{
|
||||||
// Part of the texture was mapped or unmapped. Replace the range and regenerate tracking handles.
|
// Part of the texture was mapped or unmapped. Replace the range and regenerate tracking handles.
|
||||||
if (!_channel.MemoryManager.Physical.TextureCache.UpdateMapping(texture, range))
|
if (!textureCache.UpdateMapping(texture, range))
|
||||||
{
|
{
|
||||||
// Texture could not be remapped due to a collision, just delete it.
|
// Texture could not be remapped due to a collision, just delete it.
|
||||||
if (Interlocked.Exchange(ref Items[request.ID], null) != null)
|
if (Interlocked.Exchange(ref Items[request.ID], null) != null)
|
||||||
@@ -481,6 +485,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="size">Size of the range being invalidated</param>
|
/// <param name="size">Size of the range being invalidated</param>
|
||||||
protected override void InvalidateRangeImpl(ulong address, ulong size)
|
protected override void InvalidateRangeImpl(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
|
MemoryManager memoryManager = _channel.MemoryManager;
|
||||||
ProcessDereferenceQueue();
|
ProcessDereferenceQueue();
|
||||||
|
|
||||||
ulong endAddress = address + size;
|
ulong endAddress = address + size;
|
||||||
@@ -505,7 +510,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (texture.HasOneReference())
|
if (texture.HasOneReference())
|
||||||
{
|
{
|
||||||
_channel.MemoryManager.Physical.TextureCache.AddShortCache(texture, ref cachedDescriptor);
|
TextureCache textureCache = memoryManager.GetBackingMemory(descriptor.UnpackAddress()).TextureCache;
|
||||||
|
textureCache.AddShortCache(texture, ref cachedDescriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Interlocked.Exchange(ref Items[id], null) != null)
|
if (Interlocked.Exchange(ref Items[id], null) != null)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -20,11 +22,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the texture pool belongs to</param>
|
/// <param name="context">GPU context that the texture pool belongs to</param>
|
||||||
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
||||||
|
/// <param name="physicalMemory">Backing memory of the pool</param>
|
||||||
/// <param name="address">Address of the texture pool in guest memory</param>
|
/// <param name="address">Address of the texture pool in guest memory</param>
|
||||||
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
|
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
|
||||||
protected override TexturePool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId)
|
protected override TexturePool CreatePool(
|
||||||
|
GpuContext context,
|
||||||
|
GpuChannel channel,
|
||||||
|
PhysicalMemory physicalMemory,
|
||||||
|
ulong address,
|
||||||
|
int maximumId)
|
||||||
{
|
{
|
||||||
return new TexturePool(context, channel, address, maximumId);
|
return new TexturePool(context, channel, physicalMemory, address, maximumId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
readonly struct BufferBounds : IEquatable<BufferBounds>
|
readonly struct BufferBounds : IEquatable<BufferBounds>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Physical memory backing the buffer.
|
||||||
|
/// </summary>
|
||||||
|
public PhysicalMemory Physical { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Buffer cache that owns the buffer.
|
||||||
|
/// </summary>
|
||||||
|
public BufferCache BufferCache => Physical.BufferCache;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Physical memory ranges where the buffer is mapped.
|
/// Physical memory ranges where the buffer is mapped.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -29,8 +39,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="range">Physical memory ranges where the buffer is mapped</param>
|
/// <param name="range">Physical memory ranges where the buffer is mapped</param>
|
||||||
/// <param name="flags">Buffer usage flags</param>
|
/// <param name="flags">Buffer usage flags</param>
|
||||||
public BufferBounds(MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
|
public BufferBounds(PhysicalMemory physical, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
|
||||||
{
|
{
|
||||||
|
Physical = physical;
|
||||||
Range = range;
|
Range = range;
|
||||||
Flags = flags;
|
Flags = flags;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -735,18 +735,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This does a GPU side copy.
|
/// This does a GPU side copy.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
/// <param name="context">GPU context</param>
|
||||||
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
||||||
/// <param name="srcVa">GPU virtual address of the copy source</param>
|
/// <param name="srcVa">GPU virtual address of the copy source</param>
|
||||||
/// <param name="dstVa">GPU virtual address of the copy destination</param>
|
/// <param name="dstVa">GPU virtual address of the copy destination</param>
|
||||||
/// <param name="size">Size in bytes of the copy</param>
|
/// <param name="size">Size in bytes of the copy</param>
|
||||||
public void CopyBuffer(MemoryManager memoryManager, ulong srcVa, ulong dstVa, ulong size)
|
public static void CopyBuffer(GpuContext context, MemoryManager memoryManager, ulong srcVa, ulong dstVa, ulong size)
|
||||||
{
|
{
|
||||||
MultiRange srcRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, srcVa, size, BufferStage.Copy);
|
PhysicalMemory srcPhysical = memoryManager.GetBackingMemory(srcVa);
|
||||||
MultiRange dstRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, dstVa, size, BufferStage.Copy);
|
PhysicalMemory dstPhysical = memoryManager.GetBackingMemory(dstVa);
|
||||||
|
|
||||||
|
MultiRange srcRange = srcPhysical.BufferCache.TranslateAndCreateBuffer(memoryManager, srcVa, size, BufferStage.Copy);
|
||||||
|
MultiRange dstRange = dstPhysical.BufferCache.TranslateAndCreateBuffer(memoryManager, dstVa, size, BufferStage.Copy);
|
||||||
|
|
||||||
if (srcRange.Count == 1 && dstRange.Count == 1)
|
if (srcRange.Count == 1 && dstRange.Count == 1)
|
||||||
{
|
{
|
||||||
CopyBufferSingleRange(memoryManager, srcRange.GetSubRange(0).Address, dstRange.GetSubRange(0).Address, size);
|
CopyBufferSingleRange(context, srcPhysical, dstPhysical, srcRange.GetSubRange(0).Address, dstRange.GetSubRange(0).Address, size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -777,7 +781,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ulong dstSize = dstSubRange.Size - dstOffset;
|
ulong dstSize = dstSubRange.Size - dstOffset;
|
||||||
ulong copySize = Math.Min(srcSize, dstSize);
|
ulong copySize = Math.Min(srcSize, dstSize);
|
||||||
|
|
||||||
CopyBufferSingleRange(memoryManager, srcSubRange.Address + srcOffset, dstSubRange.Address + dstOffset, copySize);
|
CopyBufferSingleRange(context, srcPhysical, dstPhysical, srcSubRange.Address + srcOffset, dstSubRange.Address + dstOffset, copySize);
|
||||||
|
|
||||||
srcOffset += copySize;
|
srcOffset += copySize;
|
||||||
dstOffset += copySize;
|
dstOffset += copySize;
|
||||||
@@ -793,18 +797,26 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// This does a GPU side copy.
|
/// This does a GPU side copy.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
||||||
|
/// <param name="srcPhysical">Physical memory backing the source buffer.</param>
|
||||||
|
/// <param name="dstPhysical">Physical memory backing the destination buffer.</param>
|
||||||
/// <param name="srcAddress">Physical address of the copy source</param>
|
/// <param name="srcAddress">Physical address of the copy source</param>
|
||||||
/// <param name="dstAddress">Physical address of the copy destination</param>
|
/// <param name="dstAddress">Physical address of the copy destination</param>
|
||||||
/// <param name="size">Size in bytes of the copy</param>
|
/// <param name="size">Size in bytes of the copy</param>
|
||||||
private void CopyBufferSingleRange(MemoryManager memoryManager, ulong srcAddress, ulong dstAddress, ulong size)
|
private static void CopyBufferSingleRange(
|
||||||
|
GpuContext context,
|
||||||
|
PhysicalMemory srcPhysical,
|
||||||
|
PhysicalMemory dstPhysical,
|
||||||
|
ulong srcAddress,
|
||||||
|
ulong dstAddress,
|
||||||
|
ulong size)
|
||||||
{
|
{
|
||||||
Buffer srcBuffer = GetBuffer(srcAddress, size, BufferStage.Copy);
|
Buffer srcBuffer = srcPhysical.BufferCache.GetBuffer(srcAddress, size, BufferStage.Copy);
|
||||||
Buffer dstBuffer = GetBuffer(dstAddress, size, BufferStage.Copy);
|
Buffer dstBuffer = dstPhysical.BufferCache.GetBuffer(dstAddress, size, BufferStage.Copy);
|
||||||
|
|
||||||
int srcOffset = (int)(srcAddress - srcBuffer.Address);
|
int srcOffset = (int)(srcAddress - srcBuffer.Address);
|
||||||
int dstOffset = (int)(dstAddress - dstBuffer.Address);
|
int dstOffset = (int)(dstAddress - dstBuffer.Address);
|
||||||
|
|
||||||
_context.Renderer.Pipeline.CopyBuffer(
|
context.Renderer.Pipeline.CopyBuffer(
|
||||||
srcBuffer.Handle,
|
srcBuffer.Handle,
|
||||||
dstBuffer.Handle,
|
dstBuffer.Handle,
|
||||||
srcOffset,
|
srcOffset,
|
||||||
@@ -820,7 +832,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
// Optimization: If the data being copied is already in memory, then copy it directly instead of flushing from GPU.
|
// Optimization: If the data being copied is already in memory, then copy it directly instead of flushing from GPU.
|
||||||
|
|
||||||
dstBuffer.ClearModified(dstAddress, size);
|
dstBuffer.ClearModified(dstAddress, size);
|
||||||
memoryManager.Physical.WriteTrackedResource(dstAddress, memoryManager.Physical.GetSpan(srcAddress, (int)size), ResourceKind.Buffer);
|
dstPhysical.WriteTrackedResource(dstAddress, srcPhysical.GetSpan(srcAddress, (int)size), ResourceKind.Buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
dstBuffer.CopyToDependantVirtualBuffers(dstAddress, size);
|
dstBuffer.CopyToDependantVirtualBuffers(dstAddress, size);
|
||||||
@@ -849,7 +861,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
_context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)subRange.Size, value);
|
_context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)subRange.Size, value);
|
||||||
|
|
||||||
memoryManager.Physical.FillTrackedResource(subRange.Address, subRange.Size, value, ResourceKind.Buffer);
|
memoryManager.GetBackingMemory(gpuVa).FillTrackedResource(subRange.Address, subRange.Size, value, ResourceKind.Buffer);
|
||||||
|
|
||||||
buffer.CopyToDependantVirtualBuffers(subRange.Address, subRange.Size);
|
buffer.CopyToDependantVirtualBuffers(subRange.Address, subRange.Size);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,18 +66,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
Buffers = new BufferBounds[count];
|
Buffers = new BufferBounds[count];
|
||||||
Unaligned = new bool[count];
|
Unaligned = new bool[count];
|
||||||
|
|
||||||
Buffers.AsSpan().Fill(new BufferBounds(new MultiRange(MemoryManager.PteUnmapped, 0UL)));
|
Buffers.AsSpan().Fill(new BufferBounds(null, new MultiRange(MemoryManager.PteUnmapped, 0UL)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the region of a buffer at a given slot.
|
/// Sets the region of a buffer at a given slot.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="index">Buffer slot</param>
|
/// <param name="index">Buffer slot</param>
|
||||||
|
/// <param name="physical">Physical memory backing the buffer</param>
|
||||||
/// <param name="range">Physical memory regions where the buffer is mapped</param>
|
/// <param name="range">Physical memory regions where the buffer is mapped</param>
|
||||||
/// <param name="flags">Buffer usage flags</param>
|
/// <param name="flags">Buffer usage flags</param>
|
||||||
public void SetBounds(int index, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
|
public void SetBounds(int index, PhysicalMemory physical, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None)
|
||||||
{
|
{
|
||||||
Buffers[index] = new BufferBounds(range, flags);
|
Buffers[index] = new BufferBounds(physical, range, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -156,8 +157,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="type">Type of each index buffer element</param>
|
/// <param name="type">Type of each index buffer element</param>
|
||||||
public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
|
public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
|
||||||
{
|
{
|
||||||
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.IndexBuffer);
|
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(gpuVa).BufferCache;
|
||||||
|
MultiRange range = bufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.IndexBuffer);
|
||||||
|
|
||||||
|
_indexBuffer.BufferCache = bufferCache;
|
||||||
_indexBuffer.Range = range;
|
_indexBuffer.Range = range;
|
||||||
_indexBuffer.Type = type;
|
_indexBuffer.Type = type;
|
||||||
|
|
||||||
@@ -186,11 +189,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
|
/// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
|
||||||
public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
|
public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
|
||||||
{
|
{
|
||||||
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.VertexBuffer);
|
BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(gpuVa).BufferCache;
|
||||||
|
MultiRange range = bufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.VertexBuffer);
|
||||||
|
|
||||||
_vertexBuffers[index].Range = range;
|
ref VertexBuffer vb = ref _vertexBuffers[index];
|
||||||
_vertexBuffers[index].Stride = stride;
|
|
||||||
_vertexBuffers[index].Divisor = divisor;
|
vb.BufferCache = bufferCache;
|
||||||
|
vb.Range = range;
|
||||||
|
vb.Stride = stride;
|
||||||
|
vb.Divisor = divisor;
|
||||||
|
|
||||||
_vertexBuffersDirty = true;
|
_vertexBuffersDirty = true;
|
||||||
|
|
||||||
@@ -213,9 +220,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="size">Size in bytes of the transform feedback buffer</param>
|
/// <param name="size">Size in bytes of the transform feedback buffer</param>
|
||||||
public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
|
public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
|
||||||
{
|
{
|
||||||
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStage.TransformFeedback);
|
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa);
|
||||||
|
MultiRange range = physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStage.TransformFeedback);
|
||||||
|
|
||||||
_transformFeedbackBuffers[index] = new BufferBounds(range);
|
_transformFeedbackBuffers[index] = new BufferBounds(physical, range);
|
||||||
_transformFeedbackBuffersDirty = true;
|
_transformFeedbackBuffersDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,11 +266,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
RecordStorageAlignment(_cpStorageBuffers, index, gpuVa);
|
RecordStorageAlignment(_cpStorageBuffers, index, gpuVa);
|
||||||
|
|
||||||
gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
|
gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
|
||||||
|
|
||||||
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.ComputeStorage(flags));
|
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa);
|
||||||
|
MultiRange range = physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.ComputeStorage(flags));
|
||||||
|
|
||||||
_cpStorageBuffers.SetBounds(index, range, flags);
|
_cpStorageBuffers.SetBounds(index, physical, range, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -282,16 +291,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
RecordStorageAlignment(buffers, index, gpuVa);
|
RecordStorageAlignment(buffers, index, gpuVa);
|
||||||
|
|
||||||
gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
|
gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
|
||||||
|
|
||||||
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.GraphicsStorage(stage, flags));
|
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa);
|
||||||
|
MultiRange range = physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.GraphicsStorage(stage, flags));
|
||||||
|
|
||||||
if (!buffers.Buffers[index].Range.Equals(range))
|
if (!buffers.Buffers[index].Range.Equals(range))
|
||||||
{
|
{
|
||||||
_gpStorageBuffersDirty = true;
|
_gpStorageBuffersDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffers.SetBounds(index, range, flags);
|
buffers.SetBounds(index, physical, range, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -303,9 +313,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="size">Size in bytes of the storage buffer</param>
|
/// <param name="size">Size in bytes of the storage buffer</param>
|
||||||
public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
|
public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
|
||||||
{
|
{
|
||||||
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.Compute);
|
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa);
|
||||||
|
MultiRange range = physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.Compute);
|
||||||
|
|
||||||
_cpUniformBuffers.SetBounds(index, range);
|
_cpUniformBuffers.SetBounds(index, physical, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -318,9 +329,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="size">Size in bytes of the storage buffer</param>
|
/// <param name="size">Size in bytes of the storage buffer</param>
|
||||||
public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
|
public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
|
||||||
{
|
{
|
||||||
MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStageUtils.FromShaderStage(stage));
|
PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa);
|
||||||
|
MultiRange range = _channel.MemoryManager.GetBackingMemory(gpuVa).BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStageUtils.FromShaderStage(stage));
|
||||||
|
|
||||||
_gpUniformBuffers[stage].SetBounds(index, range);
|
_gpUniformBuffers[stage].SetBounds(index, physical, range);
|
||||||
_gpUniformBuffersDirty = true;
|
_gpUniformBuffersDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,9 +428,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="index">Index of the uniform buffer binding</param>
|
/// <param name="index">Index of the uniform buffer binding</param>
|
||||||
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
|
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
|
||||||
public ulong GetComputeUniformBufferAddress(int index)
|
public (PhysicalMemory, ulong) GetComputeUniformBufferAddress(int index)
|
||||||
{
|
{
|
||||||
return _cpUniformBuffers.Buffers[index].Range.GetSubRange(0).Address;
|
ref BufferBounds buffer = ref _cpUniformBuffers.Buffers[index];
|
||||||
|
return (buffer.Physical, buffer.Range.GetSubRange(0).Address);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -437,9 +450,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="stage">Index of the shader stage</param>
|
/// <param name="stage">Index of the shader stage</param>
|
||||||
/// <param name="index">Index of the uniform buffer binding</param>
|
/// <param name="index">Index of the uniform buffer binding</param>
|
||||||
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
|
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
|
||||||
public ulong GetGraphicsUniformBufferAddress(int stage, int index)
|
public (PhysicalMemory, ulong) GetGraphicsUniformBufferAddress(int stage, int index)
|
||||||
{
|
{
|
||||||
return _gpUniformBuffers[stage].Buffers[index].Range.GetSubRange(0).Address;
|
ref BufferBounds buffer = ref _gpUniformBuffers[stage].Buffers[index];
|
||||||
|
return (buffer.Physical, buffer.Range.GetSubRange(0).Address);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -478,12 +492,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void CommitComputeBindings()
|
public void CommitComputeBindings()
|
||||||
{
|
{
|
||||||
BufferCache bufferCache = _channel.MemoryManager.Physical.BufferCache;
|
BindBuffers(_cpStorageBuffers, isStorage: true);
|
||||||
|
BindBuffers(_cpUniformBuffers, isStorage: false);
|
||||||
|
|
||||||
BindBuffers(bufferCache, _cpStorageBuffers, isStorage: true);
|
CommitBufferTextureBindings();
|
||||||
BindBuffers(bufferCache, _cpUniformBuffers, isStorage: false);
|
|
||||||
|
|
||||||
CommitBufferTextureBindings(bufferCache);
|
|
||||||
|
|
||||||
// Force rebind after doing compute work.
|
// Force rebind after doing compute work.
|
||||||
Rebind();
|
Rebind();
|
||||||
@@ -495,14 +507,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// Commit any queued buffer texture bindings.
|
/// Commit any queued buffer texture bindings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bufferCache">Buffer cache</param>
|
/// <param name="bufferCache">Buffer cache</param>
|
||||||
private void CommitBufferTextureBindings(BufferCache bufferCache)
|
private void CommitBufferTextureBindings()
|
||||||
{
|
{
|
||||||
if (_bufferTextures.Count > 0)
|
if (_bufferTextures.Count > 0)
|
||||||
{
|
{
|
||||||
foreach (BufferTextureBinding binding in _bufferTextures)
|
foreach (BufferTextureBinding binding in _bufferTextures)
|
||||||
{
|
{
|
||||||
bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
|
bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
|
||||||
BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStageUtils.TextureBuffer(binding.Stage, binding.BindingInfo.Flags), isStore);
|
BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStageUtils.TextureBuffer(binding.Stage, binding.BindingInfo.Flags), isStore);
|
||||||
binding.Texture.SetStorage(range);
|
binding.Texture.SetStorage(range);
|
||||||
|
|
||||||
// The texture must be rebound to use the new storage if it was updated.
|
// The texture must be rebound to use the new storage if it was updated.
|
||||||
@@ -526,7 +538,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
foreach (BufferTextureArrayBinding<ITextureArray> binding in _bufferTextureArrays)
|
foreach (BufferTextureArrayBinding<ITextureArray> binding in _bufferTextureArrays)
|
||||||
{
|
{
|
||||||
BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStage.None);
|
BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStage.None);
|
||||||
binding.Texture.SetStorage(range);
|
binding.Texture.SetStorage(range);
|
||||||
|
|
||||||
textureArray[0] = binding.Texture;
|
textureArray[0] = binding.Texture;
|
||||||
@@ -536,7 +548,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
foreach (BufferTextureArrayBinding<IImageArray> binding in _bufferImageArrays)
|
foreach (BufferTextureArrayBinding<IImageArray> binding in _bufferImageArrays)
|
||||||
{
|
{
|
||||||
bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
|
bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
|
||||||
BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStage.None, isStore);
|
BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStage.None, isStore);
|
||||||
binding.Texture.SetStorage(range);
|
binding.Texture.SetStorage(range);
|
||||||
|
|
||||||
textureArray[0] = binding.Texture;
|
textureArray[0] = binding.Texture;
|
||||||
@@ -555,8 +567,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="indexed">True if the index buffer is in use</param>
|
/// <param name="indexed">True if the index buffer is in use</param>
|
||||||
public void CommitGraphicsBindings(bool indexed)
|
public void CommitGraphicsBindings(bool indexed)
|
||||||
{
|
{
|
||||||
BufferCache bufferCache = _channel.MemoryManager.Physical.BufferCache;
|
|
||||||
|
|
||||||
if (indexed)
|
if (indexed)
|
||||||
{
|
{
|
||||||
if (_indexBufferDirty || _rebind)
|
if (_indexBufferDirty || _rebind)
|
||||||
@@ -565,14 +575,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (!_indexBuffer.Range.IsUnmapped)
|
if (!_indexBuffer.Range.IsUnmapped)
|
||||||
{
|
{
|
||||||
BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Range, BufferStage.IndexBuffer);
|
BufferRange buffer = _indexBuffer.BufferCache.GetBufferRange(_indexBuffer.Range, BufferStage.IndexBuffer);
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
|
_context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!_indexBuffer.Range.IsUnmapped)
|
else if (!_indexBuffer.Range.IsUnmapped)
|
||||||
{
|
{
|
||||||
bufferCache.SynchronizeBufferRange(_indexBuffer.Range);
|
_indexBuffer.BufferCache.SynchronizeBufferRange(_indexBuffer.Range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_rebind)
|
else if (_rebind)
|
||||||
@@ -597,7 +607,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferRange buffer = bufferCache.GetBufferRange(vb.Range, BufferStage.VertexBuffer);
|
BufferRange buffer = vb.BufferCache.GetBufferRange(vb.Range, BufferStage.VertexBuffer);
|
||||||
|
|
||||||
vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
|
vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
|
||||||
}
|
}
|
||||||
@@ -615,7 +625,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferCache.SynchronizeBufferRange(vb.Range);
|
vb.BufferCache.SynchronizeBufferRange(vb.Range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -637,7 +647,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
tfbs[index] = bufferCache.GetBufferRange(tfb.Range, BufferStage.TransformFeedback, write: true);
|
tfbs[index] = tfb.BufferCache.GetBufferRange(tfb.Range, BufferStage.TransformFeedback, write: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
|
_context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
|
||||||
@@ -684,7 +694,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
_context.SupportBufferUpdater.SetTfeOffset(index, tfeOffset);
|
_context.SupportBufferUpdater.SetTfeOffset(index, tfeOffset);
|
||||||
|
|
||||||
buffers[index] = new BufferAssignment(index, bufferCache.GetBufferRange(range, BufferStage.TransformFeedback, write: true));
|
buffers[index] = new BufferAssignment(index, tfb.BufferCache.GetBufferRange(range, BufferStage.TransformFeedback, write: true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -702,7 +712,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferCache.SynchronizeBufferRange(tfb.Range);
|
tfb.BufferCache.SynchronizeBufferRange(tfb.Range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -710,7 +720,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
_gpStorageBuffersDirty = false;
|
_gpStorageBuffersDirty = false;
|
||||||
|
|
||||||
BindBuffers(bufferCache, _gpStorageBuffers, isStorage: true);
|
BindBuffers(_gpStorageBuffers, isStorage: true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -721,14 +731,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
_gpUniformBuffersDirty = false;
|
_gpUniformBuffersDirty = false;
|
||||||
|
|
||||||
BindBuffers(bufferCache, _gpUniformBuffers, isStorage: false);
|
BindBuffers(_gpUniformBuffers, isStorage: false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UpdateBuffers(_gpUniformBuffers);
|
UpdateBuffers(_gpUniformBuffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
CommitBufferTextureBindings(bufferCache);
|
CommitBufferTextureBindings();
|
||||||
|
|
||||||
_rebind = false;
|
_rebind = false;
|
||||||
|
|
||||||
@@ -742,7 +752,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="bindings">Buffer memory ranges to bind</param>
|
/// <param name="bindings">Buffer memory ranges to bind</param>
|
||||||
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
|
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void BindBuffers(BufferCache bufferCache, BuffersPerStage[] bindings, bool isStorage)
|
private void BindBuffers(BuffersPerStage[] bindings, bool isStorage)
|
||||||
{
|
{
|
||||||
int rangesCount = 0;
|
int rangesCount = 0;
|
||||||
|
|
||||||
@@ -763,8 +773,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
|
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
|
||||||
BufferRange range = isStorage
|
BufferRange range = isStorage
|
||||||
? bufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite)
|
? bounds.BufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite)
|
||||||
: bufferCache.GetBufferRange(bounds.Range, bufferStage);
|
: bounds.BufferCache.GetBufferRange(bounds.Range, bufferStage);
|
||||||
|
|
||||||
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
|
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
|
||||||
}
|
}
|
||||||
@@ -780,11 +790,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bind respective buffer bindings on the host API.
|
/// Bind respective buffer bindings on the host API.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bufferCache">Buffer cache holding the buffers for the specified ranges</param>
|
|
||||||
/// <param name="buffers">Buffer memory ranges to bind</param>
|
/// <param name="buffers">Buffer memory ranges to bind</param>
|
||||||
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
|
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void BindBuffers(BufferCache bufferCache, BuffersPerStage buffers, bool isStorage)
|
private void BindBuffers(BuffersPerStage buffers, bool isStorage)
|
||||||
{
|
{
|
||||||
int rangesCount = 0;
|
int rangesCount = 0;
|
||||||
|
|
||||||
@@ -800,8 +809,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
|
bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
|
||||||
BufferRange range = isStorage
|
BufferRange range = isStorage
|
||||||
? bufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite)
|
? bounds.BufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite)
|
||||||
: bufferCache.GetBufferRange(bounds.Range, BufferStage.Compute);
|
: bounds.BufferCache.GetBufferRange(bounds.Range, BufferStage.Compute);
|
||||||
|
|
||||||
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
|
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
|
||||||
}
|
}
|
||||||
@@ -854,7 +863,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(bounds.Range);
|
bounds.BufferCache.SynchronizeBufferRange(bounds.Range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -871,13 +880,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
public void SetBufferTextureStorage(
|
public void SetBufferTextureStorage(
|
||||||
ShaderStage stage,
|
ShaderStage stage,
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
|
BufferCache bufferCache,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
bool isImage)
|
bool isImage)
|
||||||
{
|
{
|
||||||
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
bufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
||||||
|
|
||||||
_bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, isImage));
|
_bufferTextures.Add(new BufferTextureBinding(stage, texture, bufferCache, range, bindingInfo, isImage));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -894,13 +904,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ShaderStage stage,
|
ShaderStage stage,
|
||||||
ITextureArray array,
|
ITextureArray array,
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
|
BufferCache bufferCache,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
int index)
|
int index)
|
||||||
{
|
{
|
||||||
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
bufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
||||||
|
|
||||||
_bufferTextureArrays.Add(new BufferTextureArrayBinding<ITextureArray>(array, texture, range, bindingInfo, index));
|
_bufferTextureArrays.Add(new BufferTextureArrayBinding<ITextureArray>(array, texture, bufferCache, range, bindingInfo, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -917,13 +928,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ShaderStage stage,
|
ShaderStage stage,
|
||||||
IImageArray array,
|
IImageArray array,
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
|
BufferCache bufferCache,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
int index)
|
int index)
|
||||||
{
|
{
|
||||||
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
bufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags));
|
||||||
|
|
||||||
_bufferImageArrays.Add(new BufferTextureArrayBinding<IImageArray>(array, texture, range, bindingInfo, index));
|
_bufferImageArrays.Add(new BufferTextureArrayBinding<IImageArray>(array, texture, bufferCache, range, bindingInfo, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ITexture Texture { get; }
|
public ITexture Texture { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Buffer cache that owns the buffer.
|
||||||
|
/// </summary>
|
||||||
|
public BufferCache BufferCache { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Physical ranges of memory where the buffer texture data is located.
|
/// Physical ranges of memory where the buffer texture data is located.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -39,18 +44,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="array">Array</param>
|
/// <param name="array">Array</param>
|
||||||
/// <param name="texture">Buffer texture</param>
|
/// <param name="texture">Buffer texture</param>
|
||||||
|
/// <param name="bufferCache">Buffer cache that owns the buffer</param>
|
||||||
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
||||||
/// <param name="bindingInfo">Binding info</param>
|
/// <param name="bindingInfo">Binding info</param>
|
||||||
/// <param name="index">Index of the binding on the array</param>
|
/// <param name="index">Index of the binding on the array</param>
|
||||||
public BufferTextureArrayBinding(
|
public BufferTextureArrayBinding(
|
||||||
T array,
|
T array,
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
|
BufferCache bufferCache,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
int index)
|
int index)
|
||||||
{
|
{
|
||||||
Array = array;
|
Array = array;
|
||||||
Texture = texture;
|
Texture = texture;
|
||||||
|
BufferCache = bufferCache;
|
||||||
Range = range;
|
Range = range;
|
||||||
BindingInfo = bindingInfo;
|
BindingInfo = bindingInfo;
|
||||||
Index = index;
|
Index = index;
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ITexture Texture { get; }
|
public ITexture Texture { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Buffer cache that owns the buffer.
|
||||||
|
/// </summary>
|
||||||
|
public BufferCache BufferCache { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Physical ranges of memory where the buffer texture data is located.
|
/// Physical ranges of memory where the buffer texture data is located.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -40,18 +45,21 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stage">Shader stage accessing the texture</param>
|
/// <param name="stage">Shader stage accessing the texture</param>
|
||||||
/// <param name="texture">Buffer texture</param>
|
/// <param name="texture">Buffer texture</param>
|
||||||
|
/// <param name="bufferCache">Buffer cache that owns the buffer</param>
|
||||||
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
/// <param name="range">Physical ranges of memory where the buffer texture data is located</param>
|
||||||
/// <param name="bindingInfo">Binding info</param>
|
/// <param name="bindingInfo">Binding info</param>
|
||||||
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
|
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
|
||||||
public BufferTextureBinding(
|
public BufferTextureBinding(
|
||||||
ShaderStage stage,
|
ShaderStage stage,
|
||||||
ITexture texture,
|
ITexture texture,
|
||||||
|
BufferCache bufferCache,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
TextureBindingInfo bindingInfo,
|
TextureBindingInfo bindingInfo,
|
||||||
bool isImage)
|
bool isImage)
|
||||||
{
|
{
|
||||||
Stage = stage;
|
Stage = stage;
|
||||||
Texture = texture;
|
Texture = texture;
|
||||||
|
BufferCache = bufferCache;
|
||||||
Range = range;
|
Range = range;
|
||||||
BindingInfo = bindingInfo;
|
BindingInfo = bindingInfo;
|
||||||
IsImage = isImage;
|
IsImage = isImage;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
struct IndexBuffer
|
struct IndexBuffer
|
||||||
{
|
{
|
||||||
|
public BufferCache BufferCache;
|
||||||
public MultiRange Range;
|
public MultiRange Range;
|
||||||
public IndexType Type;
|
public IndexType Type;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
@@ -35,10 +36,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
public event EventHandler<UnmapEventArgs> MemoryUnmapped;
|
public event EventHandler<UnmapEventArgs> MemoryUnmapped;
|
||||||
|
|
||||||
/// <summary>
|
private readonly GpuContext _context;
|
||||||
/// Physical memory where the virtual memory is mapped into.
|
private readonly List<PhysicalMemory> _physicalMemoryList;
|
||||||
/// </summary>
|
private readonly Dictionary<PhysicalMemory, byte> _physicalMemoryMap;
|
||||||
internal PhysicalMemory Physical { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Virtual range cache.
|
/// Virtual range cache.
|
||||||
@@ -53,19 +53,65 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the GPU memory manager.
|
/// Creates a new instance of the GPU memory manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="context">GPU context</param>
|
||||||
/// <param name="physicalMemory">Physical memory that this memory manager will map into</param>
|
/// <param name="physicalMemory">Physical memory that this memory manager will map into</param>
|
||||||
/// <param name="cpuMemorySize">The amount of physical CPU Memory Avaiable on the device.</param>
|
/// <param name="cpuMemorySize">The amount of physical CPU Memory Avaiable on the device.</param>
|
||||||
internal MemoryManager(PhysicalMemory physicalMemory, ulong cpuMemorySize)
|
|
||||||
|
internal MemoryManager(GpuContext context, PhysicalMemory physicalMemory, ulong cpuMemorySize)
|
||||||
{
|
{
|
||||||
Physical = physicalMemory;
|
_context = context;
|
||||||
|
|
||||||
|
_physicalMemoryList = new List<PhysicalMemory>()
|
||||||
|
{
|
||||||
|
physicalMemory
|
||||||
|
};
|
||||||
|
|
||||||
|
_physicalMemoryMap = new Dictionary<PhysicalMemory, byte>
|
||||||
|
{
|
||||||
|
{ physicalMemory, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
VirtualRangeCache = new VirtualRangeCache(this);
|
VirtualRangeCache = new VirtualRangeCache(this);
|
||||||
CounterCache = new CounterCache();
|
CounterCache = new CounterCache();
|
||||||
_pageTable = new ulong[PtLvl0Size][];
|
_pageTable = new ulong[PtLvl0Size][];
|
||||||
MemoryUnmapped += Physical.TextureCache.MemoryUnmappedHandler;
|
MemoryUnmapped += physicalMemory.TextureCache.MemoryUnmappedHandler;
|
||||||
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
|
MemoryUnmapped += physicalMemory.BufferCache.MemoryUnmappedHandler;
|
||||||
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
|
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
|
||||||
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
|
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
|
||||||
Physical.TextureCache.Initialize(cpuMemorySize);
|
physicalMemory.TextureCache.Initialize(cpuMemorySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attaches the memory manager to a new GPU channel.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rebind">Action to be performed when the buffer cache changes</param>
|
||||||
|
internal void AttachToChannel(Action rebind)
|
||||||
|
{
|
||||||
|
PhysicalMemory physicalMemory = GetOwnPhysicalMemory();
|
||||||
|
|
||||||
|
physicalMemory.IncrementReferenceCount();
|
||||||
|
physicalMemory.BufferCache.NotifyBuffersModified += rebind;
|
||||||
|
physicalMemory.BufferCache.QueuePrune();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attaches the memory manager to a new GPU channel.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rebind">Action that was performed when the buffer cache changed</param>
|
||||||
|
internal void DetachFromChannel(Action rebind)
|
||||||
|
{
|
||||||
|
PhysicalMemory physicalMemory = GetOwnPhysicalMemory();
|
||||||
|
|
||||||
|
physicalMemory.BufferCache.NotifyBuffersModified -= rebind;
|
||||||
|
physicalMemory.DecrementReferenceCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queues a prune of invalid entries on the buffer cache.
|
||||||
|
/// </summary>
|
||||||
|
internal void QueuePrune()
|
||||||
|
{
|
||||||
|
GetOwnPhysicalMemory().BufferCache.QueuePrune();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -81,15 +127,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (IsContiguous(va, size))
|
if (IsContiguous(va, size))
|
||||||
{
|
{
|
||||||
ulong address = Translate(va);
|
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
if (tracked)
|
if (tracked)
|
||||||
{
|
{
|
||||||
return Physical.ReadTracked<T>(address);
|
return physicalMemory.ReadTracked<T>(address);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return Physical.Read<T>(address);
|
return physicalMemory.Read<T>(address);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -113,7 +159,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
if (IsContiguous(va, size))
|
if (IsContiguous(va, size))
|
||||||
{
|
{
|
||||||
return Physical.GetSpan(Translate(va), size, tracked);
|
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
|
return physicalMemory.GetSpan(address, size, tracked);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -138,7 +186,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
bool isContiguous = true;
|
bool isContiguous = true;
|
||||||
int mappedSize;
|
int mappedSize;
|
||||||
|
|
||||||
if (ValidateAddress(va) && GetPte(va) != PteUnmapped && Physical.IsMapped(Translate(va)))
|
if (ValidateAddress(va) && IsMappedOnGpuAndPhysical(va))
|
||||||
{
|
{
|
||||||
ulong endVa = va + (ulong)size;
|
ulong endVa = va + (ulong)size;
|
||||||
ulong endVaAligned = (endVa + PageMask) & ~PageMask;
|
ulong endVaAligned = (endVa + PageMask) & ~PageMask;
|
||||||
@@ -151,7 +199,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
ulong nextVa = currentVa + PageSize;
|
ulong nextVa = currentVa + PageSize;
|
||||||
ulong nextPa = Translate(nextVa);
|
ulong nextPa = Translate(nextVa);
|
||||||
|
|
||||||
if (!ValidateAddress(nextVa) || GetPte(nextVa) == PteUnmapped || !Physical.IsMapped(nextPa))
|
if (!ValidateAddress(nextVa) || !IsMappedOnGpuAndPhysical(nextVa))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -180,7 +228,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (isContiguous)
|
if (isContiguous)
|
||||||
{
|
{
|
||||||
return Physical.GetSpan(Translate(va), mappedSize, tracked);
|
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
|
return physicalMemory.GetSpan(address, mappedSize, tracked);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -192,6 +242,23 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a page of memory is mapped on the GPU and its backing memory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="va">GPU virtual address of the page</param>
|
||||||
|
/// <returns>True if mapped, false otherwise</returns>
|
||||||
|
private bool IsMappedOnGpuAndPhysical(ulong va)
|
||||||
|
{
|
||||||
|
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
|
if (address == PteUnmapped)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return physicalMemory.IsMapped(address);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads data from a possibly non-contiguous region of GPU mapped memory.
|
/// Reads data from a possibly non-contiguous region of GPU mapped memory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -209,22 +276,22 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if ((va & PageMask) != 0)
|
if ((va & PageMask) != 0)
|
||||||
{
|
{
|
||||||
ulong pa = Translate(va);
|
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
|
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
|
||||||
|
|
||||||
Physical.GetSpan(pa, size, tracked).CopyTo(data[..size]);
|
physicalMemory.GetSpan(pa, size, tracked).CopyTo(data[..size]);
|
||||||
|
|
||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; offset < data.Length; offset += size)
|
for (; offset < data.Length; offset += size)
|
||||||
{
|
{
|
||||||
ulong pa = Translate(va + (ulong)offset);
|
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset);
|
||||||
|
|
||||||
size = Math.Min(data.Length - offset, (int)PageSize);
|
size = Math.Min(data.Length - offset, (int)PageSize);
|
||||||
|
|
||||||
Physical.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size));
|
physicalMemory.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,15 +306,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
if (IsContiguous(va, size))
|
if (IsContiguous(va, size))
|
||||||
{
|
{
|
||||||
return Physical.GetWritableRegion(Translate(va), size, tracked);
|
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
|
return physicalMemory.GetWritableRegion(address, size, tracked);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
|
Memory<byte> memory = new byte[size];
|
||||||
|
|
||||||
ReadImpl(va, memoryOwner.Span, tracked);
|
GetSpan(va, size).CopyTo(memory.Span);
|
||||||
|
|
||||||
return new WritableRegion(this, va, memoryOwner, tracked);
|
return new WritableRegion(this, va, memory, tracked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +338,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="data">The data to be written</param>
|
/// <param name="data">The data to be written</param>
|
||||||
public void Write(ulong va, ReadOnlySpan<byte> data)
|
public void Write(ulong va, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
WriteImpl(va, data, Physical.Write);
|
WriteImpl(va, data, (physical, va, data) => physical.Write(va, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -279,7 +348,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="data">The data to be written</param>
|
/// <param name="data">The data to be written</param>
|
||||||
public void WriteTrackedResource(ulong va, ReadOnlySpan<byte> data)
|
public void WriteTrackedResource(ulong va, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
WriteImpl(va, data, Physical.WriteTrackedResource);
|
WriteImpl(va, data, (physical, va, data) => physical.WriteTrackedResource(va, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -289,10 +358,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="data">The data to be written</param>
|
/// <param name="data">The data to be written</param>
|
||||||
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
|
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
WriteImpl(va, data, Physical.WriteUntracked);
|
WriteImpl(va, data, (physical, va, data) => physical.WriteUntracked(va, data));
|
||||||
}
|
}
|
||||||
|
|
||||||
private delegate void WriteCallback(ulong address, ReadOnlySpan<byte> data);
|
private delegate void WriteCallback(PhysicalMemory physicalMemory, ulong address, ReadOnlySpan<byte> data);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes data to possibly non-contiguous GPU mapped memory.
|
/// Writes data to possibly non-contiguous GPU mapped memory.
|
||||||
@@ -304,7 +373,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
if (IsContiguous(va, data.Length))
|
if (IsContiguous(va, data.Length))
|
||||||
{
|
{
|
||||||
writeCallback(Translate(va), data);
|
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
|
writeCallback(physicalMemory, address, data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -312,22 +383,67 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if ((va & PageMask) != 0)
|
if ((va & PageMask) != 0)
|
||||||
{
|
{
|
||||||
ulong pa = Translate(va);
|
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
|
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
|
||||||
|
|
||||||
writeCallback(pa, data[..size]);
|
writeCallback(physicalMemory, pa, data[..size]);
|
||||||
|
|
||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; offset < data.Length; offset += size)
|
for (; offset < data.Length; offset += size)
|
||||||
{
|
{
|
||||||
ulong pa = Translate(va + (ulong)offset);
|
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset);
|
||||||
|
|
||||||
size = Math.Min(data.Length - offset, (int)PageSize);
|
size = Math.Min(data.Length - offset, (int)PageSize);
|
||||||
|
|
||||||
writeCallback(pa, data.Slice(offset, size));
|
writeCallback(physicalMemory, pa, data.Slice(offset, size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes data to GPU mapped memory, stopping at the first unmapped page at the memory region, if any.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="va">GPU virtual address to write the data into</param>
|
||||||
|
/// <param name="data">The data to be written</param>
|
||||||
|
public void WriteMapped(ulong va, ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
if (IsContiguous(va, data.Length))
|
||||||
|
{
|
||||||
|
(PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
|
physicalMemory.Write(address, data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int offset = 0, size;
|
||||||
|
|
||||||
|
if ((va & PageMask) != 0)
|
||||||
|
{
|
||||||
|
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va);
|
||||||
|
|
||||||
|
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
|
||||||
|
|
||||||
|
if (pa != PteUnmapped && physicalMemory.IsMapped(pa))
|
||||||
|
{
|
||||||
|
physicalMemory.Write(pa, data[..size]);
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; offset < data.Length; offset += size)
|
||||||
|
{
|
||||||
|
(PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset);
|
||||||
|
|
||||||
|
size = Math.Min(data.Length - offset, (int)PageSize);
|
||||||
|
|
||||||
|
if (pa != PteUnmapped && physicalMemory.IsMapped(pa))
|
||||||
|
{
|
||||||
|
physicalMemory.Write(pa, data.Slice(offset, size));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -359,15 +475,51 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="size">Size in bytes of the mapping</param>
|
/// <param name="size">Size in bytes of the mapping</param>
|
||||||
/// <param name="kind">Kind of the resource located at the mapping</param>
|
/// <param name="kind">Kind of the resource located at the mapping</param>
|
||||||
public void Map(ulong pa, ulong va, ulong size, PteKind kind)
|
public void Map(ulong pa, ulong va, ulong size, PteKind kind)
|
||||||
|
{
|
||||||
|
MapImpl(pa, va, size, kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps a given range of pages to the specified CPU virtual address from a different process.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// All addresses and sizes must be page aligned.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="pa">CPU virtual address to map into</param>
|
||||||
|
/// <param name="va">GPU virtual address to be mapped</param>
|
||||||
|
/// <param name="size">Size in bytes of the mapping</param>
|
||||||
|
/// <param name="kind">Kind of the resource located at the mapping</param>
|
||||||
|
/// <param name="ownedPid">PID of the process that owns the mapping</param>
|
||||||
|
public void MapForeign(ulong pa, ulong va, ulong size, PteKind kind, ulong ownedPid)
|
||||||
|
{
|
||||||
|
if (_context.PhysicalMemoryRegistry.TryGetValue(ownedPid, out PhysicalMemory physicalMemory))
|
||||||
|
{
|
||||||
|
MapImpl(pa, va, size, kind, physicalMemory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maps a given range of pages to the specified CPU virtual address.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// All addresses and sizes must be page aligned.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="pa">CPU virtual address to map into</param>
|
||||||
|
/// <param name="va">GPU virtual address to be mapped</param>
|
||||||
|
/// <param name="size">Size in bytes of the mapping</param>
|
||||||
|
/// <param name="kind">Kind of the resource located at the mapping</param>
|
||||||
|
/// <param name="physicalMemory">Optional physical memory to import for the mapping</param>
|
||||||
|
private void MapImpl(ulong pa, ulong va, ulong size, PteKind kind, PhysicalMemory physicalMemory = null)
|
||||||
{
|
{
|
||||||
lock (_pageTable)
|
lock (_pageTable)
|
||||||
{
|
{
|
||||||
UnmapEventArgs e = new(va, size);
|
UnmapEventArgs e = new(va, size);
|
||||||
MemoryUnmapped?.Invoke(this, e);
|
MemoryUnmapped?.Invoke(this, e);
|
||||||
|
byte pIndex = physicalMemory != null ? GetOrAddPhysicalMemory(physicalMemory) : (byte)0;
|
||||||
|
|
||||||
for (ulong offset = 0; offset < size; offset += PageSize)
|
for (ulong offset = 0; offset < size; offset += PageSize)
|
||||||
{
|
{
|
||||||
SetPte(va + offset, PackPte(pa + offset, kind));
|
SetPte(va + offset, PackPte(pa + offset, pIndex, kind));
|
||||||
}
|
}
|
||||||
|
|
||||||
RunRemapActions(e);
|
RunRemapActions(e);
|
||||||
@@ -418,12 +570,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
for (int page = 0; page < pages - 1; page++)
|
for (int page = 0; page < pages - 1; page++)
|
||||||
{
|
{
|
||||||
if (!ValidateAddress(va + PageSize) || GetPte(va + PageSize) == PteUnmapped)
|
ulong nextPte = GetPte(va + PageSize);
|
||||||
|
|
||||||
|
if (!ValidateAddress(va + PageSize) || nextPte == PteUnmapped)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Translate(va) + PageSize != Translate(va + PageSize))
|
if (GetPte(va) + PageSize != nextPte)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -457,7 +611,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
int pages = (int)((endVaRounded - va) / PageSize);
|
int pages = (int)((endVaRounded - va) / PageSize);
|
||||||
|
|
||||||
List<MemoryRange> regions = [];
|
List<MemoryRange> regions = new();
|
||||||
|
|
||||||
for (int page = 0; page < pages - 1; page++)
|
for (int page = 0; page < pages - 1; page++)
|
||||||
{
|
{
|
||||||
@@ -535,6 +689,49 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the backing memory for a given GPU virtual address.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="va">GPU virtual address to get the backing memory from</param>
|
||||||
|
/// <returns>The backing memory for the specified GPU virtual address</returns>
|
||||||
|
internal PhysicalMemory GetBackingMemory(ulong va)
|
||||||
|
{
|
||||||
|
ulong pte = GetPte(va);
|
||||||
|
|
||||||
|
if (pte == PteUnmapped)
|
||||||
|
{
|
||||||
|
return GetOwnPhysicalMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _physicalMemoryList[UnpackPIndexFromPte(pte)];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the backing memory that is owned by this GPU memory manager.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The backing memory owned by this memory manager</returns>
|
||||||
|
private PhysicalMemory GetOwnPhysicalMemory()
|
||||||
|
{
|
||||||
|
return _physicalMemoryList[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the index for a given physical memory on the list, adding it to the list if needed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="physicalMemory">Physical memory to get the index from</param>
|
||||||
|
/// <returns>The index of the physical memory on the list</returns>
|
||||||
|
private byte GetOrAddPhysicalMemory(PhysicalMemory physicalMemory)
|
||||||
|
{
|
||||||
|
if (!_physicalMemoryMap.TryGetValue(physicalMemory, out byte pIndex))
|
||||||
|
{
|
||||||
|
pIndex = checked((byte)_physicalMemoryList.Count);
|
||||||
|
_physicalMemoryList.Add(physicalMemory);
|
||||||
|
_physicalMemoryMap.Add(physicalMemory, pIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pIndex;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates a GPU virtual address.
|
/// Validates a GPU virtual address.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -636,6 +833,28 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return Math.Min(maxSize, va - startVa);
|
return Math.Min(maxSize, va - startVa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Translates a GPU virtual address to a CPU virtual address and the associated physical memory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="va">GPU virtual address to be translated</param>
|
||||||
|
/// <returns>CPU virtual address with the physical memory, or <see cref="PteUnmapped"/> if unmapped</returns>
|
||||||
|
private (PhysicalMemory, ulong) TranslateWithPhysicalMemory(ulong va)
|
||||||
|
{
|
||||||
|
if (!ValidateAddress(va))
|
||||||
|
{
|
||||||
|
return (GetOwnPhysicalMemory(), PteUnmapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong pte = GetPte(va);
|
||||||
|
|
||||||
|
if (pte == PteUnmapped)
|
||||||
|
{
|
||||||
|
return (GetOwnPhysicalMemory(), PteUnmapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (_physicalMemoryList[UnpackPIndexFromPte(pte)], UnpackPaFromPte(pte) + (va & PageMask));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the kind of a given memory page.
|
/// 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.
|
/// This might indicate the type of resource that can be allocated on the page, and also texture tiling.
|
||||||
@@ -659,6 +878,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return UnpackKindFromPte(pte);
|
return UnpackKindFromPte(pte);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsForeignMapping(ulong va)
|
||||||
|
{
|
||||||
|
ulong pte = GetPte(va);
|
||||||
|
|
||||||
|
if (pte == PteUnmapped)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UnpackPIndexFromPte(pte) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the Page Table entry for a given GPU virtual address.
|
/// Gets the Page Table entry for a given GPU virtual address.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -704,11 +935,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// Creates a page table entry from a physical address and kind.
|
/// Creates a page table entry from a physical address and kind.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pa">Physical address</param>
|
/// <param name="pa">Physical address</param>
|
||||||
|
/// <param name="pIndex">Index of the physical memory on the list</param>
|
||||||
/// <param name="kind">Kind</param>
|
/// <param name="kind">Kind</param>
|
||||||
/// <returns>Page table entry</returns>
|
/// <returns>Page table entry</returns>
|
||||||
private static ulong PackPte(ulong pa, PteKind kind)
|
private static ulong PackPte(ulong pa, byte pIndex, PteKind kind)
|
||||||
{
|
{
|
||||||
return pa | ((ulong)kind << 56);
|
return pa | ((ulong)pIndex << 48) | ((ulong)kind << 56);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -721,6 +953,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return (PteKind)(pte >> 56);
|
return (PteKind)(pte >> 56);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unpacks the physical memory index in the list from a page table entry.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pte">Page table entry</param>
|
||||||
|
/// <returns>Physical memory index</returns>
|
||||||
|
private static byte UnpackPIndexFromPte(ulong pte)
|
||||||
|
{
|
||||||
|
return (byte)(pte >> 48);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unpacks physical address from a page table entry.
|
/// Unpacks physical address from a page table entry.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -728,7 +970,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <returns>Physical address</returns>
|
/// <returns>Physical address</returns>
|
||||||
private static ulong UnpackPaFromPte(ulong pte)
|
private static ulong UnpackPaFromPte(ulong pte)
|
||||||
{
|
{
|
||||||
return pte & 0xffffffffffffffUL;
|
return pte & 0xffffffffffffUL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
struct VertexBuffer
|
struct VertexBuffer
|
||||||
{
|
{
|
||||||
|
public BufferCache BufferCache;
|
||||||
public MultiRange Range;
|
public MultiRange Range;
|
||||||
public int Stride;
|
public int Stride;
|
||||||
public int Divisor;
|
public int Divisor;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Gpu.Image;
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
using System;
|
using System;
|
||||||
@@ -66,11 +67,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public uint ConstantBuffer1Read(int offset)
|
public uint ConstantBuffer1Read(int offset)
|
||||||
{
|
{
|
||||||
ulong baseAddress = _compute
|
(PhysicalMemory physical, ulong baseAddress) = _compute
|
||||||
? _channel.BufferManager.GetComputeUniformBufferAddress(1)
|
? _channel.BufferManager.GetComputeUniformBufferAddress(1)
|
||||||
: _channel.BufferManager.GetGraphicsUniformBufferAddress(_stageIndex, 1);
|
: _channel.BufferManager.GetGraphicsUniformBufferAddress(_stageIndex, 1);
|
||||||
|
|
||||||
return _channel.MemoryManager.Physical.Read<uint>(baseAddress + (ulong)offset);
|
return physical.Read<uint>(baseAddress + (ulong)offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|||||||
@@ -733,15 +733,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
byte[] codeB,
|
byte[] codeB,
|
||||||
bool asCompute)
|
bool asCompute)
|
||||||
{
|
{
|
||||||
ulong cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1);
|
(PhysicalMemory physical, ulong cb1DataAddress) = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1);
|
||||||
|
|
||||||
MemoryManager memoryManager = channel.MemoryManager;
|
MemoryManager memoryManager = channel.MemoryManager;
|
||||||
|
|
||||||
codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray();
|
codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray();
|
||||||
codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray();
|
codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray();
|
||||||
byte[] cb1DataA = ReadArray(memoryManager, cb1DataAddress, vertexA.Cb1DataSize);
|
byte[] cb1DataA = ReadArray(physical, cb1DataAddress, vertexA.Cb1DataSize);
|
||||||
byte[] cb1DataB = ReadArray(memoryManager, cb1DataAddress, currentStage.Cb1DataSize);
|
byte[] cb1DataB = ReadArray(physical, cb1DataAddress, currentStage.Cb1DataSize);
|
||||||
|
|
||||||
ShaderDumpPaths pathsA = default;
|
ShaderDumpPaths pathsA = default;
|
||||||
ShaderDumpPaths pathsB = default;
|
ShaderDumpPaths pathsB = default;
|
||||||
|
|
||||||
@@ -775,11 +775,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
MemoryManager memoryManager = channel.MemoryManager;
|
MemoryManager memoryManager = channel.MemoryManager;
|
||||||
|
|
||||||
ulong cb1DataAddress = context.Stage == ShaderStage.Compute
|
(PhysicalMemory physical, ulong cb1DataAddress) = context.Stage == ShaderStage.Compute
|
||||||
? channel.BufferManager.GetComputeUniformBufferAddress(1)
|
? channel.BufferManager.GetComputeUniformBufferAddress(1)
|
||||||
: channel.BufferManager.GetGraphicsUniformBufferAddress(StageToStageIndex(context.Stage), 1);
|
: channel.BufferManager.GetGraphicsUniformBufferAddress(StageToStageIndex(context.Stage), 1);
|
||||||
|
|
||||||
byte[] cb1Data = ReadArray(memoryManager, cb1DataAddress, context.Cb1DataSize);
|
byte[] cb1Data = ReadArray(physical, cb1DataAddress, context.Cb1DataSize);
|
||||||
code ??= memoryManager.GetSpan(context.Address, context.Size).ToArray();
|
code ??= memoryManager.GetSpan(context.Address, context.Size).ToArray();
|
||||||
|
|
||||||
ShaderDumpPaths paths = dumper?.Dump(code, context.Stage == ShaderStage.Compute) ?? default;
|
ShaderDumpPaths paths = dumper?.Dump(code, context.Stage == ShaderStage.Compute) ?? default;
|
||||||
@@ -793,18 +793,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads data from physical memory, returns an empty array if the memory is unmapped or size is 0.
|
/// Reads data from physical memory, returns an empty array if the memory is unmapped or size is 0.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="memoryManager">Memory manager with the physical memory to read from</param>
|
/// <param name="physicalMemory">Physical memory to read the data from, might be null</param>
|
||||||
/// <param name="address">Physical address of the region to read</param>
|
/// <param name="address">Physical address of the region to read</param>
|
||||||
/// <param name="size">Size in bytes of the data</param>
|
/// <param name="size">Size in bytes of the data</param>
|
||||||
/// <returns>An array with the data at the specified memory location</returns>
|
/// <returns>An array with the data at the specified memory location</returns>
|
||||||
private static byte[] ReadArray(MemoryManager memoryManager, ulong address, int size)
|
private static byte[] ReadArray(PhysicalMemory physicalMemory, ulong address, int size)
|
||||||
{
|
{
|
||||||
if (address == MemoryManager.PteUnmapped || size == 0)
|
if (address == MemoryManager.PteUnmapped || size == 0 || physicalMemory == null)
|
||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return memoryManager.Physical.GetSpan(address, size).ToArray();
|
return physicalMemory.GetSpan(address, size).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -696,7 +696,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex);
|
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex);
|
||||||
|
|
||||||
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Range));
|
cachedTextureBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
|
||||||
cachedTextureBufferIndex = textureBufferIndex;
|
cachedTextureBufferIndex = textureBufferIndex;
|
||||||
|
|
||||||
if (samplerBufferIndex == textureBufferIndex)
|
if (samplerBufferIndex == textureBufferIndex)
|
||||||
@@ -710,7 +710,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex);
|
ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex);
|
||||||
|
|
||||||
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(channel.MemoryManager.Physical.GetSpan(bounds.Range));
|
cachedSamplerBuffer = MemoryMarshal.Cast<byte, int>(bounds.Physical.GetSpan(bounds.Range));
|
||||||
cachedSamplerBufferIndex = samplerBufferIndex;
|
cachedSamplerBufferIndex = samplerBufferIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,18 @@ using System.Threading;
|
|||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu
|
namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
|
using Texture = Image.Texture;
|
||||||
|
|
||||||
|
public record TextureData(int Width, int Height, byte[] Data);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// GPU image presentation window.
|
/// GPU image presentation window.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Window
|
public class Window
|
||||||
{
|
{
|
||||||
|
private const int CaptureTextureWidth = 1280;
|
||||||
|
private const int CaptureTextureHeight = 720;
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -85,7 +92,21 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class PresentedTexture
|
||||||
|
{
|
||||||
|
public readonly Texture Texture;
|
||||||
|
public readonly ImageCrop Crop;
|
||||||
|
|
||||||
|
public PresentedTexture(Texture texture, ImageCrop crop)
|
||||||
|
{
|
||||||
|
Texture = texture;
|
||||||
|
Crop = crop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private readonly ConcurrentQueue<PresentationTexture> _frameQueue;
|
private readonly ConcurrentQueue<PresentationTexture> _frameQueue;
|
||||||
|
private PresentedTexture _lastPresentedTexture;
|
||||||
|
private ITexture _captureTexture;
|
||||||
|
|
||||||
private int _framesAvailable;
|
private int _framesAvailable;
|
||||||
|
|
||||||
@@ -188,6 +209,51 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TextureData GetLastPresentedData()
|
||||||
|
{
|
||||||
|
PresentedTexture pt = Volatile.Read(ref _lastPresentedTexture);
|
||||||
|
|
||||||
|
if (pt != null)
|
||||||
|
{
|
||||||
|
byte[] inputData = CaptureLastFrame(pt.Texture.HostTexture, pt.Crop);
|
||||||
|
|
||||||
|
int size = SizeCalculator.GetBlockLinearTextureSize(
|
||||||
|
CaptureTextureWidth,
|
||||||
|
CaptureTextureHeight,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
16,
|
||||||
|
1,
|
||||||
|
1).TotalSize;
|
||||||
|
|
||||||
|
byte[] data = new byte[size];
|
||||||
|
|
||||||
|
LayoutConverter.ConvertLinearToBlockLinear(data, CaptureTextureWidth, CaptureTextureHeight, CaptureTextureWidth * 4, 4, 16, inputData);
|
||||||
|
|
||||||
|
return new TextureData(CaptureTextureWidth, CaptureTextureHeight, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextureData(0, 0, Array.Empty<byte>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureData GetLastPresentedDataLinear()
|
||||||
|
{
|
||||||
|
PresentedTexture pt = Volatile.Read(ref _lastPresentedTexture);
|
||||||
|
|
||||||
|
if (pt != null)
|
||||||
|
{
|
||||||
|
byte[] inputData = CaptureLastFrame(pt.Texture.HostTexture, new ImageCrop());
|
||||||
|
|
||||||
|
return new TextureData(pt.Texture.Info.Width, pt.Texture.Info.Height, inputData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextureData(0, 0, Array.Empty<byte>());
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Presents a texture on the queue.
|
/// Presents a texture on the queue.
|
||||||
/// If the queue is empty, then no texture is presented.
|
/// If the queue is empty, then no texture is presented.
|
||||||
@@ -205,6 +271,10 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
|
|
||||||
pt.Cache.Tick();
|
pt.Cache.Tick();
|
||||||
|
|
||||||
|
EnsureCaptureTexture();
|
||||||
|
|
||||||
|
Volatile.Write(ref _lastPresentedTexture, new PresentedTexture(texture, pt.Crop));
|
||||||
|
|
||||||
texture.SynchronizeMemory();
|
texture.SynchronizeMemory();
|
||||||
|
|
||||||
ImageCrop crop = new(
|
ImageCrop crop = new(
|
||||||
@@ -244,6 +314,96 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void EnsureCaptureTexture()
|
||||||
|
{
|
||||||
|
if (_captureTexture == null)
|
||||||
|
{
|
||||||
|
_captureTexture = _context.Renderer.CreateTexture(new TextureCreateInfo(
|
||||||
|
1280,
|
||||||
|
720,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
Format.R8G8B8A8Unorm,
|
||||||
|
DepthStencilMode.Depth,
|
||||||
|
Target.Texture2D,
|
||||||
|
SwizzleComponent.Red,
|
||||||
|
SwizzleComponent.Green,
|
||||||
|
SwizzleComponent.Blue,
|
||||||
|
SwizzleComponent.Alpha));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] CaptureLastFrame(ITexture lastFrame, ImageCrop crop)
|
||||||
|
{
|
||||||
|
int cropLeft, cropRight, cropTop, cropBottom;
|
||||||
|
|
||||||
|
if (crop.Left == 0 && crop.Right == 0)
|
||||||
|
{
|
||||||
|
cropLeft = 0;
|
||||||
|
cropRight = lastFrame.Width;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cropLeft = crop.Left;
|
||||||
|
cropRight = crop.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crop.Top == 0 && crop.Bottom == 0)
|
||||||
|
{
|
||||||
|
cropTop = 0;
|
||||||
|
cropBottom = lastFrame.Height;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cropTop = crop.Top;
|
||||||
|
cropBottom = crop.Bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x1, y1, x2, y2;
|
||||||
|
|
||||||
|
if (crop.FlipX)
|
||||||
|
{
|
||||||
|
x1 = cropRight;
|
||||||
|
x2 = cropLeft;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x1 = cropLeft;
|
||||||
|
x2 = cropRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crop.FlipY)
|
||||||
|
{
|
||||||
|
y1 = cropBottom;
|
||||||
|
y2 = cropTop;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
y1 = cropTop;
|
||||||
|
y2 = cropBottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
Extents2D srcRegion = new(x1, y1, x2, y2);
|
||||||
|
Extents2D dstRegion = new(0, 0, CaptureTextureWidth, CaptureTextureHeight);
|
||||||
|
|
||||||
|
byte[] outputData = null;
|
||||||
|
|
||||||
|
_context.Renderer.BackgroundContextAction(() =>
|
||||||
|
{
|
||||||
|
lastFrame.CopyTo(_captureTexture, srcRegion, dstRegion, true);
|
||||||
|
|
||||||
|
using var data = _captureTexture.GetData();
|
||||||
|
|
||||||
|
outputData = data.Get().ToArray();
|
||||||
|
});
|
||||||
|
|
||||||
|
return outputData;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicate that a frame on the queue is ready to be acquired.
|
/// Indicate that a frame on the queue is ready to be acquired.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
private readonly ICpuContext _cpuContext;
|
private readonly ICpuContext _cpuContext;
|
||||||
private T _memoryManager;
|
private T _memoryManager;
|
||||||
|
|
||||||
public IVirtualMemoryManager AddressSpace => _memoryManager;
|
public IVirtualMemoryManagerTracked AddressSpace => _memoryManager;
|
||||||
|
|
||||||
public ulong AddressSpaceSize { get; }
|
public ulong AddressSpaceSize { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
internal ServerBase TimeServer { get; private set; }
|
internal ServerBase TimeServer { get; private set; }
|
||||||
internal ServerBase ViServer { get; private set; }
|
internal ServerBase ViServer { get; private set; }
|
||||||
internal ServerBase ViServerM { get; private set; }
|
internal ServerBase ViServerM { get; private set; }
|
||||||
internal ServerBase ViServerS { get; private set; }
|
internal ViServer ViServerS { get; private set; }
|
||||||
internal ServerBase LdnServer { get; private set; }
|
internal ServerBase LdnServer { get; private set; }
|
||||||
|
|
||||||
internal KSharedMemory HidSharedMem { get; private set; }
|
internal KSharedMemory HidSharedMem { get; private set; }
|
||||||
@@ -254,7 +254,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
TimeServer = new ServerBase(KernelContext, "TimeServer");
|
TimeServer = new ServerBase(KernelContext, "TimeServer");
|
||||||
ViServer = new ServerBase(KernelContext, "ViServerU");
|
ViServer = new ServerBase(KernelContext, "ViServerU");
|
||||||
ViServerM = new ServerBase(KernelContext, "ViServerM");
|
ViServerM = new ServerBase(KernelContext, "ViServerM");
|
||||||
ViServerS = new ServerBase(KernelContext, "ViServerS");
|
ViServerS = new ViServer(KernelContext, "ViServerS");
|
||||||
LdnServer = new ServerBase(KernelContext, "LdnServer");
|
LdnServer = new ServerBase(KernelContext, "LdnServer");
|
||||||
|
|
||||||
StartNewServices();
|
StartNewServices();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
interface IProcessContext : IDisposable
|
interface IProcessContext : IDisposable
|
||||||
{
|
{
|
||||||
IVirtualMemoryManager AddressSpace { get; }
|
IVirtualMemoryManagerTracked AddressSpace { get; }
|
||||||
|
|
||||||
ulong AddressSpaceSize { get; }
|
ulong AddressSpaceSize { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
private IProcessContextFactory _contextFactory;
|
private IProcessContextFactory _contextFactory;
|
||||||
public IProcessContext Context { get; private set; }
|
public IProcessContext Context { get; private set; }
|
||||||
public IVirtualMemoryManager CpuMemory => Context.AddressSpace;
|
public IVirtualMemoryManagerTracked CpuMemory => Context.AddressSpace;
|
||||||
|
|
||||||
public HleProcessDebugger Debugger { get; private set; }
|
public HleProcessDebugger Debugger { get; private set; }
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
class ProcessContext : IProcessContext
|
class ProcessContext : IProcessContext
|
||||||
{
|
{
|
||||||
public IVirtualMemoryManager AddressSpace { get; }
|
public IVirtualMemoryManagerTracked AddressSpace { get; }
|
||||||
|
|
||||||
public ulong AddressSpaceSize { get; }
|
public ulong AddressSpaceSize { get; }
|
||||||
|
|
||||||
public ProcessContext(IVirtualMemoryManager asManager, ulong addressSpaceSize)
|
public ProcessContext(IVirtualMemoryManagerTracked asManager, ulong addressSpaceSize)
|
||||||
{
|
{
|
||||||
AddressSpace = asManager;
|
AddressSpace = asManager;
|
||||||
AddressSpaceSize = addressSpaceSize;
|
AddressSpaceSize = addressSpaceSize;
|
||||||
|
|||||||
+105
@@ -0,0 +1,105 @@
|
|||||||
|
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.OverlayAppletProxy;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
|
||||||
|
{
|
||||||
|
class IOverlayAppletProxy : IpcService
|
||||||
|
{
|
||||||
|
private readonly ulong _pid;
|
||||||
|
|
||||||
|
public IOverlayAppletProxy(ulong pid)
|
||||||
|
{
|
||||||
|
_pid = pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(0)]
|
||||||
|
// GetCommonStateGetter() -> object<nn::am::service::ICommonStateGetter>
|
||||||
|
public ResultCode GetCommonStateGetter(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new ICommonStateGetter(context));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(1)]
|
||||||
|
// GetSelfController() -> object<nn::am::service::ISelfController>
|
||||||
|
public ResultCode GetSelfController(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new ISelfController(context, _pid));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(2)]
|
||||||
|
// GetWindowController() -> object<nn::am::service::IWindowController>
|
||||||
|
public ResultCode GetWindowController(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IWindowController(_pid));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(3)]
|
||||||
|
// GetAudioController() -> object<nn::am::service::IAudioController>
|
||||||
|
public ResultCode GetAudioController(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IAudioController());
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(4)]
|
||||||
|
// GetDisplayController() -> object<nn::am::service::IDisplayController>
|
||||||
|
public ResultCode GetDisplayController(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IDisplayController(context));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(11)]
|
||||||
|
// GetLibraryAppletCreator() -> object<nn::am::service::ILibraryAppletCreator>
|
||||||
|
public ResultCode GetLibraryAppletCreator(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new ILibraryAppletCreator());
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(20)]
|
||||||
|
// GetOverlayFunctions() -> object<nn::am::service::IOverlayFunctions>
|
||||||
|
public ResultCode GetOverlayFunctions(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IOverlayFunctions(context.Device.System));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(21)]
|
||||||
|
// GetAppletCommonFunctions() -> object<nn::am::service::IAppletCommonFunctions>
|
||||||
|
public ResultCode GetAppletCommonFunctions(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IAppletCommonFunctions());
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(23)]
|
||||||
|
// GetGlobalStateController() -> object<nn::am::service::IGlobalStateController>
|
||||||
|
public ResultCode GetGlobalStateController(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IGlobalStateController());
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(1000)]
|
||||||
|
// GetDebugFunctions() -> object<nn::am::service::IDebugFunctions>
|
||||||
|
public ResultCode GetDebugFunctions(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IDebugFunctions());
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+155
@@ -0,0 +1,155 @@
|
|||||||
|
using LibHac.Util;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.OverlayAppletProxy
|
||||||
|
{
|
||||||
|
class IOverlayFunctions : IpcService
|
||||||
|
{
|
||||||
|
|
||||||
|
public IOverlayFunctions(Horizon system)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(0)]
|
||||||
|
// BeginToWatchShortHomeButtonMessage()
|
||||||
|
public ResultCode BeginToWatchShortHomeButtonMessage(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(1)]
|
||||||
|
// EndToWatchShortHomeButtonMessage()
|
||||||
|
public ResultCode EndToWatchShortHomeButtonMessage(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(2)]
|
||||||
|
// GetApplicationIdForLogo() -> nn::ncm::ApplicationId
|
||||||
|
public ResultCode GetApplicationIdForLogo(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
context.ResponseData.Write(0L);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(3)]
|
||||||
|
// SetGpuTimeSliceBoost(u64)
|
||||||
|
public ResultCode SetGpuTimeSliceBoost(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(4)]
|
||||||
|
// SetAutoSleepTimeAndDimmingTimeEnabled(u8)
|
||||||
|
public ResultCode SetAutoSleepTimeAndDimmingTimeEnabled(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(5)]
|
||||||
|
// TerminateApplicationAndSetReason(u32)
|
||||||
|
public ResultCode TerminateApplicationAndSetReason(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(6)]
|
||||||
|
// SetScreenShotPermissionGlobally(u8)
|
||||||
|
public ResultCode SetScreenShotPermissionGlobally(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(10)]
|
||||||
|
// StartShutdownSequenceForOverlay()
|
||||||
|
public ResultCode StartShutdownSequenceForOverlay(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(11)]
|
||||||
|
// StartRebootSequenceForOverlay()
|
||||||
|
public ResultCode StartRebootSequenceForOverlay(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(20)]
|
||||||
|
// SetHandlingHomeButtonShortPressedEnabled(u8)
|
||||||
|
public ResultCode SetHandlingHomeButtonShortPressedEnabled(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(21)]
|
||||||
|
// SetHandlingTouchScreenInputEnabled(u8)
|
||||||
|
public ResultCode SetHandlingTouchScreenInputEnabled(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(30)]
|
||||||
|
// SetHealthWarningShowingState(u8)
|
||||||
|
public ResultCode SetHealthWarningShowingState(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(31)]
|
||||||
|
// IsHealthWarningRequired() -> bool
|
||||||
|
public ResultCode IsHealthWarningRequired(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
context.ResponseData.Write(false);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(90)]
|
||||||
|
// SetRequiresGpuResourceUse(u8)
|
||||||
|
public ResultCode SetRequiresGpuResourceUse(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(101)]
|
||||||
|
// BeginToObserveHidInputForDevelop()
|
||||||
|
public ResultCode BeginToObserveHidInputForDevelop(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -102,5 +102,34 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
|||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(22)]
|
||||||
|
// AcquireLastApplicationCaptureSharedBuffer() -> (b8, u32)
|
||||||
|
public ResultCode AcquireLastApplicationCaptureSharedBuffer(ServiceCtx context)
|
||||||
|
{
|
||||||
|
context.ResponseData.Write(1);
|
||||||
|
context.ResponseData.Write(context.Device.System.ViServerS.GetApplicationLastPresentedFrameHandle(context.Device.Gpu));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(26)]
|
||||||
|
|
||||||
|
// AcquireCallerAppletCaptureSharedBuffer() -> (b8, u32)
|
||||||
|
public ResultCode AcquireCallerAppletCaptureSharedBuffer(ServiceCtx context)
|
||||||
|
{
|
||||||
|
// TODO: How does the handling for applets differ from the one for applications?
|
||||||
|
context.ResponseData.Write(1);
|
||||||
|
context.ResponseData.Write(context.Device.System.ViServerS.GetApplicationLastPresentedFrameHandle(context.Device.Gpu));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(27)]
|
||||||
|
public ResultCode ReleaseCallerAppletCaptureSharedBuffer(ServiceCtx context)
|
||||||
|
{
|
||||||
|
context.ResponseData.Write(2);
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-5
@@ -225,7 +225,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
|||||||
public ResultCode CreateManagedDisplayLayer(ServiceCtx context)
|
public ResultCode CreateManagedDisplayLayer(ServiceCtx context)
|
||||||
{
|
{
|
||||||
context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, _pid);
|
context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, _pid);
|
||||||
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
|
|
||||||
|
|
||||||
context.ResponseData.Write(layerId);
|
context.ResponseData.Write(layerId);
|
||||||
|
|
||||||
@@ -236,18 +235,27 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
|||||||
// IsSystemBufferSharingEnabled()
|
// IsSystemBufferSharingEnabled()
|
||||||
public ResultCode IsSystemBufferSharingEnabled(ServiceCtx context)
|
public ResultCode IsSystemBufferSharingEnabled(ServiceCtx context)
|
||||||
{
|
{
|
||||||
// NOTE: Service checks a private field and return an error if the SystemBufferSharing is disabled.
|
// TODO: Implement this once we have a way to check if we're not an AppletId.Application
|
||||||
|
return ResultCode.Success;
|
||||||
return ResultCode.NotImplemented;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(42)] // 4.0.0+
|
||||||
|
// GetSystemSharedLayerHandle() -> (nn::vi::fbshare::SharedBufferHandle, nn::vi::fbshare::SharedLayerHandle)
|
||||||
|
public ResultCode GetSystemSharedLayerHandle(ServiceCtx context)
|
||||||
|
{
|
||||||
|
context.ResponseData.Write((ulong)context.Device.System.ViServerS.GetSharedBufferNvMapId());
|
||||||
|
context.ResponseData.Write(context.Device.System.ViServerS.GetSharedLayerId());
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandCmif(44)] // 10.0.0+
|
[CommandCmif(44)] // 10.0.0+
|
||||||
// CreateManagedDisplaySeparableLayer() -> (u64, u64)
|
// CreateManagedDisplaySeparableLayer() -> (u64, u64)
|
||||||
public ResultCode CreateManagedDisplaySeparableLayer(ServiceCtx context)
|
public ResultCode CreateManagedDisplaySeparableLayer(ServiceCtx context)
|
||||||
{
|
{
|
||||||
context.Device.System.SurfaceFlinger.CreateLayer(out long displayLayerId, _pid);
|
context.Device.System.SurfaceFlinger.CreateLayer(out long displayLayerId, _pid);
|
||||||
context.Device.System.SurfaceFlinger.CreateLayer(out long recordingLayerId, _pid);
|
context.Device.System.SurfaceFlinger.CreateLayer(out long recordingLayerId, _pid);
|
||||||
context.Device.System.SurfaceFlinger.SetRenderLayer(displayLayerId);
|
//context.Device.System.SurfaceFlinger.SetRenderLayer(displayLayerId);
|
||||||
|
|
||||||
context.ResponseData.Write(displayLayerId);
|
context.ResponseData.Write(displayLayerId);
|
||||||
context.ResponseData.Write(recordingLayerId);
|
context.ResponseData.Write(recordingLayerId);
|
||||||
|
|||||||
@@ -26,6 +26,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
|
|||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(300)]
|
||||||
|
// OpenOverlayAppletProxy(pid, handle<copy>) -> object<nn::am::service::IOverlayAppletProxy>
|
||||||
|
public ResultCode OpenOverlayAppletProxy(ServiceCtx context)
|
||||||
|
{
|
||||||
|
MakeObject(context, new IOverlayAppletProxy(context.Request.HandleDesc.PId));
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandCmif(350)]
|
[CommandCmif(350)]
|
||||||
// OpenSystemApplicationProxy(u64, pid, handle<copy>) -> object<nn::am::service::IApplicationProxy>
|
// OpenSystemApplicationProxy(u64, pid, handle<copy>) -> object<nn::am::service::IApplicationProxy>
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
|
|
||||||
public Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>> RumbleQueues = new();
|
public Dictionary<PlayerIndex, ConcurrentQueue<(VibrationValue, VibrationValue)>> RumbleQueues = new();
|
||||||
public Dictionary<PlayerIndex, (VibrationValue, VibrationValue)> LastVibrationValues = new();
|
public Dictionary<PlayerIndex, (VibrationValue, VibrationValue)> LastVibrationValues = new();
|
||||||
|
|
||||||
|
internal PlayerIndex LastActiveNpad { get; set; }
|
||||||
|
|
||||||
public NpadDevices(Switch device, bool active = true) : base(device, active)
|
public NpadDevices(Switch device, bool active = true) : base(device, active)
|
||||||
{
|
{
|
||||||
@@ -384,6 +386,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LastActiveNpad = state.PlayerId;
|
||||||
|
|
||||||
ref RingLifo<NpadCommonState> lifo = ref GetCommonStateLifo(ref currentNpad);
|
ref RingLifo<NpadCommonState> lifo = ref GetCommonStateLifo(ref currentNpad);
|
||||||
|
|
||||||
NpadCommonState newState = new()
|
NpadCommonState newState = new()
|
||||||
@@ -639,5 +643,20 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
|
|
||||||
return rumbleQueue;
|
return rumbleQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NpadIdType GetLastActiveNpadId()
|
||||||
|
{
|
||||||
|
return LastActiveNpad switch {
|
||||||
|
PlayerIndex.Player1 => NpadIdType.Player1,
|
||||||
|
PlayerIndex.Player2 => NpadIdType.Player2,
|
||||||
|
PlayerIndex.Player3 => NpadIdType.Player3,
|
||||||
|
PlayerIndex.Player4 => NpadIdType.Player4,
|
||||||
|
PlayerIndex.Player5 => NpadIdType.Player5,
|
||||||
|
PlayerIndex.Player6 => NpadIdType.Player6,
|
||||||
|
PlayerIndex.Player7 => NpadIdType.Player7,
|
||||||
|
PlayerIndex.Player8 => NpadIdType.Player8,
|
||||||
|
_ => NpadIdType.Handheld,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,15 +24,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
// GetLastActiveNpad(u32) -> u8, u8
|
// GetLastActiveNpad(u32) -> u8, u8
|
||||||
public ResultCode GetLastActiveNpad(ServiceCtx context)
|
public ResultCode GetLastActiveNpad(ServiceCtx context)
|
||||||
{
|
{
|
||||||
// TODO: RequestData seems to have garbage data, reading an extra uint seems to fix the issue.
|
context.ResponseData.Write((byte)context.Device.Hid.Npads.GetLastActiveNpadId());
|
||||||
context.RequestData.ReadUInt32();
|
|
||||||
|
|
||||||
ResultCode resultCode = GetAppletFooterUiTypeImpl(context, out AppletFooterUiType appletFooterUiType);
|
return ResultCode.Success;
|
||||||
|
|
||||||
context.ResponseData.Write((byte)appletFooterUiType);
|
|
||||||
context.ResponseData.Write((byte)0);
|
|
||||||
|
|
||||||
return resultCode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandCmif(307)]
|
[CommandCmif(307)]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
@@ -105,6 +106,26 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
|||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(6)]
|
||||||
|
// SetRequirementPreset(u32)
|
||||||
|
public ResultCode SetRequirementPreset(ServiceCtx context)
|
||||||
|
{
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceNifm);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(9)]
|
||||||
|
// SetNetworkProfileId(nn::util::Uuid)
|
||||||
|
public ResultCode SetNetworkProfileId(ServiceCtx context)
|
||||||
|
{
|
||||||
|
UInt128 uuid = context.RequestData.ReadStruct<UInt128>();
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { uuid });
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandCmif(11)]
|
[CommandCmif(11)]
|
||||||
// SetConnectionConfirmationOption(i8)
|
// SetConnectionConfirmationOption(i8)
|
||||||
public ResultCode SetConnectionConfirmationOption(ServiceCtx context)
|
public ResultCode SetConnectionConfirmationOption(ServiceCtx context)
|
||||||
@@ -142,5 +163,38 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
|
|||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(23)]
|
||||||
|
// SetKeptInSleep(bool)
|
||||||
|
public ResultCode SetKeptInSleep(ServiceCtx context)
|
||||||
|
{
|
||||||
|
bool keptInSleep = context.RequestData.ReadBoolean();
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { keptInSleep });
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(24)]
|
||||||
|
// RegisterSocketDescriptor(s32)
|
||||||
|
public ResultCode RegisterSocketDescriptor(ServiceCtx context)
|
||||||
|
{
|
||||||
|
int socketDescriptor = context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { socketDescriptor });
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(25)]
|
||||||
|
// UnregisterSocketDescriptor(s32)
|
||||||
|
public ResultCode UnregisterSocketDescriptor(ServiceCtx context)
|
||||||
|
{
|
||||||
|
int socketDescriptor = context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { socketDescriptor });
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -245,8 +245,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, arguments.NvMapHandle);
|
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(arguments.NvMapHandle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{arguments.NvMapHandle:x8}!");
|
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{arguments.NvMapHandle:x8}!");
|
||||||
@@ -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, (PteKind)arguments.Kind);
|
Map(physicalAddress, arguments.Offset, size, (PteKind)arguments.Kind, map.OwnerPid);
|
||||||
}
|
}
|
||||||
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, (PteKind)arguments.Kind);
|
Map(physicalAddress, va, size, (PteKind)arguments.Kind, map.OwnerPid);
|
||||||
arguments.Offset = va;
|
arguments.Offset = va;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,8 +380,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
ulong mapOffs = (ulong)argument.MapOffset << 16;
|
ulong mapOffs = (ulong)argument.MapOffset << 16;
|
||||||
PteKind kind = (PteKind)argument.Kind;
|
PteKind kind = (PteKind)argument.Kind;
|
||||||
|
|
||||||
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, nvmapHandle);
|
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(nvmapHandle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{nvmapHandle:x8}!");
|
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{nvmapHandle:x8}!");
|
||||||
@@ -389,13 +389,25 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
return NvInternalResult.InvalidInput;
|
return NvInternalResult.InvalidInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
gmm.Map(mapOffs + map.Address, gpuVa, size, kind);
|
Map(mapOffs + map.Address, gpuVa, size, kind, map.OwnerPid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NvInternalResult.Success;
|
return NvInternalResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Map(ulong pa, ulong va, ulong size, PteKind kind, ulong ownerPid)
|
||||||
|
{
|
||||||
|
if (Owner == ownerPid)
|
||||||
|
{
|
||||||
|
_asContext.Gmm.Map(pa, va, size, kind);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_asContext.Gmm.MapForeign(pa, va, size, kind, ownerPid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void Close() { }
|
public override void Close() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-6
@@ -167,8 +167,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
|
|
||||||
foreach (CommandBuffer commandBuffer in commandBuffers)
|
foreach (CommandBuffer commandBuffer in commandBuffers)
|
||||||
{
|
{
|
||||||
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBuffer.Mem);
|
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(commandBuffer.Mem);
|
||||||
|
|
||||||
ReadOnlySpan<byte> data = _memory.GetSpan(map.Address + commandBuffer.Offset, commandBuffer.WordsCount * 4);
|
ReadOnlySpan<byte> data = _memory.GetSpan(map.Address + commandBuffer.Offset, commandBuffer.WordsCount * 4);
|
||||||
|
|
||||||
_host1xContext.Host1x.Submit(MemoryMarshal.Cast<byte, int>(data), _contextId);
|
_host1xContext.Host1x.Submit(MemoryMarshal.Cast<byte, int>(data), _contextId);
|
||||||
@@ -237,8 +237,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
|
|
||||||
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
|
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
|
||||||
{
|
{
|
||||||
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle);
|
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(commandBufferEntry.MapHandle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!");
|
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!");
|
||||||
@@ -279,8 +279,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
|
|
||||||
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
|
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
|
||||||
{
|
{
|
||||||
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle);
|
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(commandBufferEntry.MapHandle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!");
|
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!");
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
|
|
||||||
uint size = BitUtils.AlignUp(arguments.Size, (uint)MemoryManager.PageSize);
|
uint size = BitUtils.AlignUp(arguments.Size, (uint)MemoryManager.PageSize);
|
||||||
|
|
||||||
arguments.Handle = CreateHandleFromMap(new NvMapHandle(size));
|
arguments.Handle = CreateHandleFromMap(new NvMapHandle(size) { OwnerPid = Owner });
|
||||||
|
|
||||||
Logger.Debug?.Print(LogClass.ServiceNv, $"Created map {arguments.Handle} with size 0x{size:x8}!");
|
Logger.Debug?.Print(LogClass.ServiceNv, $"Created map {arguments.Handle} with size 0x{size:x8}!");
|
||||||
|
|
||||||
return NvInternalResult.Success;
|
return NvInternalResult.Success;
|
||||||
@@ -80,8 +80,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
|
|
||||||
private NvInternalResult FromId(ref NvMapFromId arguments)
|
private NvInternalResult FromId(ref NvMapFromId arguments)
|
||||||
{
|
{
|
||||||
NvMapHandle map = GetMapFromHandle(Owner, arguments.Id);
|
NvMapHandle map = GetMapFromHandle(arguments.Id);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!");
|
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!");
|
||||||
@@ -98,8 +98,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
|
|
||||||
private NvInternalResult Alloc(ref NvMapAlloc arguments)
|
private NvInternalResult Alloc(ref NvMapAlloc arguments)
|
||||||
{
|
{
|
||||||
NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle);
|
NvMapHandle map = GetMapFromHandle(arguments.Handle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!");
|
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!");
|
||||||
@@ -152,8 +152,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
|
|
||||||
private NvInternalResult Free(ref NvMapFree arguments)
|
private NvInternalResult Free(ref NvMapFree arguments)
|
||||||
{
|
{
|
||||||
NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle);
|
NvMapHandle map = GetMapFromHandle(arguments.Handle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!");
|
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!");
|
||||||
@@ -179,8 +179,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
|
|
||||||
private NvInternalResult Param(ref NvMapParam arguments)
|
private NvInternalResult Param(ref NvMapParam arguments)
|
||||||
{
|
{
|
||||||
NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle);
|
NvMapHandle map = GetMapFromHandle(arguments.Handle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!");
|
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!");
|
||||||
@@ -217,8 +217,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
|
|
||||||
private NvInternalResult GetId(ref NvMapGetId arguments)
|
private NvInternalResult GetId(ref NvMapGetId arguments)
|
||||||
{
|
{
|
||||||
NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle);
|
NvMapHandle map = GetMapFromHandle(arguments.Handle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!");
|
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!");
|
||||||
@@ -237,25 +237,30 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
// _maps.TryRemove(GetOwner(), out _);
|
// _maps.TryRemove(GetOwner(), out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int CreateHandleFromMap(NvMapHandle map)
|
public static int CreateMap(ulong pid, ulong address, uint size)
|
||||||
|
{
|
||||||
|
return CreateHandleFromMap(new NvMapHandle(size) { Address = address, OwnerPid = pid });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int CreateHandleFromMap(NvMapHandle map)
|
||||||
{
|
{
|
||||||
return _maps.Add(map);
|
return _maps.Add(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool DeleteMapWithHandle(ulong pid, int handle)
|
private static bool DeleteMapWithHandle(int handle)
|
||||||
{
|
{
|
||||||
return _maps.Delete(handle) != null;
|
return _maps.Delete(handle) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void IncrementMapRefCount(ulong pid, int handle)
|
public static void IncrementMapRefCount(ulong pid, int handle)
|
||||||
{
|
{
|
||||||
GetMapFromHandle(pid, handle)?.IncrementRefCount();
|
GetMapFromHandle(handle)?.IncrementRefCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool DecrementMapRefCount(ulong pid, int handle)
|
public static bool DecrementMapRefCount(ulong pid, int handle)
|
||||||
{
|
{
|
||||||
NvMapHandle map = GetMapFromHandle(pid, handle);
|
NvMapHandle map = GetMapFromHandle(handle);
|
||||||
|
|
||||||
if (map == null)
|
if (map == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -263,8 +268,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
|
|
||||||
if (map.DecrementRefCount() <= 0)
|
if (map.DecrementRefCount() <= 0)
|
||||||
{
|
{
|
||||||
DeleteMapWithHandle(pid, handle);
|
DeleteMapWithHandle(handle);
|
||||||
|
|
||||||
Logger.Debug?.Print(LogClass.ServiceNv, $"Deleted map {handle}!");
|
Logger.Debug?.Print(LogClass.ServiceNv, $"Deleted map {handle}!");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -275,7 +280,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NvMapHandle GetMapFromHandle(ulong pid, int handle)
|
public static NvMapHandle GetMapFromHandle(int handle)
|
||||||
{
|
{
|
||||||
return _maps.Get(handle);
|
return _maps.Get(handle);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
|
|||||||
public ulong Address;
|
public ulong Address;
|
||||||
public bool Allocated;
|
public bool Allocated;
|
||||||
public ulong DmaMapAddress;
|
public ulong DmaMapAddress;
|
||||||
|
public ulong OwnerPid;
|
||||||
|
|
||||||
private long _dupes;
|
private long _dupes;
|
||||||
|
|
||||||
public NvMapHandle()
|
public NvMapHandle()
|
||||||
|
|||||||
@@ -172,6 +172,15 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
{
|
{
|
||||||
ServerLoop();
|
ServerLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual ulong CalculateRequiredHeapSize()
|
||||||
|
{
|
||||||
|
return 0UL;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void CustomInit(KernelContext context, ulong pid, ulong heapAddress)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
private void ServerLoop()
|
private void ServerLoop()
|
||||||
{
|
{
|
||||||
@@ -196,10 +205,14 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
Result result = _selfProcess.HandleTable.GenerateHandle(_wakeEvent.ReadableEvent, out _wakeHandle);
|
Result result = _selfProcess.HandleTable.GenerateHandle(_wakeEvent.ReadableEvent, out _wakeHandle);
|
||||||
|
|
||||||
InitDone.Set();
|
InitDone.Set();
|
||||||
|
|
||||||
|
ulong heapSize = CalculateRequiredHeapSize() + PointerBufferSize;
|
||||||
|
|
||||||
ulong messagePtr = _selfThread.TlsAddress;
|
ulong messagePtr = _selfThread.TlsAddress;
|
||||||
_context.Syscall.SetHeapSize(out ulong heapAddr, 0x200000);
|
_context.Syscall.SetHeapSize(out ulong heapAddr, BitUtils.AlignUp(heapSize, 0x200000UL));
|
||||||
|
|
||||||
|
CustomInit(_context, _selfProcess.Pid, heapAddr + PointerBufferSize);
|
||||||
|
|
||||||
_selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
|
_selfProcess.CpuMemory.Write(messagePtr + 0x0, 0);
|
||||||
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
|
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
|
||||||
_selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
|
_selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Ryujinx.Common.Logging;
|
|||||||
using Ryujinx.Common.PreciseSleep;
|
using Ryujinx.Common.PreciseSleep;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu;
|
using Ryujinx.Graphics.Gpu;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -38,8 +39,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
|
|
||||||
private readonly Lock _lock = new();
|
private readonly Lock _lock = new();
|
||||||
|
|
||||||
public long RenderLayerId { get; private set; }
|
|
||||||
|
|
||||||
private class Layer
|
private class Layer
|
||||||
{
|
{
|
||||||
public int ProducerBinderId;
|
public int ProducerBinderId;
|
||||||
@@ -60,7 +59,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
{
|
{
|
||||||
_device = device;
|
_device = device;
|
||||||
_layers = new Dictionary<long, Layer>();
|
_layers = new Dictionary<long, Layer>();
|
||||||
RenderLayerId = 0;
|
|
||||||
|
|
||||||
_composerThread = new Thread(HandleComposition)
|
_composerThread = new Thread(HandleComposition)
|
||||||
{
|
{
|
||||||
@@ -239,34 +237,12 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
|
|
||||||
private void CloseLayer(long layerId, Layer layer)
|
private void CloseLayer(long layerId, Layer layer)
|
||||||
{
|
{
|
||||||
// If the layer was removed and the current in use, we need to change the current layer in use.
|
|
||||||
if (RenderLayerId == layerId)
|
|
||||||
{
|
|
||||||
// If no layer is availaible, reset to default value.
|
|
||||||
if (_layers.Count == 0)
|
|
||||||
{
|
|
||||||
SetRenderLayer(0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SetRenderLayer(_layers.Last().Key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (layer.State == LayerState.ManagedOpened)
|
if (layer.State == LayerState.ManagedOpened)
|
||||||
{
|
{
|
||||||
layer.State = LayerState.ManagedClosed;
|
layer.State = LayerState.ManagedClosed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetRenderLayer(long layerId)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
RenderLayerId = layerId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Layer GetLayerByIdLocked(long layerId)
|
private Layer GetLayerByIdLocked(long layerId)
|
||||||
{
|
{
|
||||||
foreach (KeyValuePair<long, Layer> pair in _layers)
|
foreach (KeyValuePair<long, Layer> pair in _layers)
|
||||||
@@ -360,41 +336,55 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
// TODO: support multilayers (& multidisplay ?)
|
foreach (var (layerId, layer) in _layers)
|
||||||
if (RenderLayerId == 0)
|
|
||||||
{
|
{
|
||||||
return;
|
if (layer.State == LayerState.NotInitialized || layer.State == LayerState.ManagedClosed)
|
||||||
}
|
continue;
|
||||||
|
|
||||||
Layer layer = GetLayerByIdLocked(RenderLayerId);
|
if (_device.System.KernelContext.Processes.TryGetValue(layer.Owner, out var process))
|
||||||
|
|
||||||
Status acquireStatus = layer.Consumer.AcquireBuffer(out BufferItem item, 0);
|
|
||||||
|
|
||||||
if (acquireStatus == Status.Success)
|
|
||||||
{
|
|
||||||
if (_device.VSyncMode == VSyncMode.Unbounded)
|
|
||||||
{
|
{
|
||||||
if (_swapInterval != 0)
|
if (process.State == ProcessState.Exiting || process.State == ProcessState.Exited)
|
||||||
{
|
{
|
||||||
UpdateSwapInterval(0);
|
HOSBinderDriverServer.UnregisterBinderObject(layer.ProducerBinderId);
|
||||||
_vSyncMode = _device.VSyncMode;
|
|
||||||
|
if (_layers.Remove(layerId))
|
||||||
|
{
|
||||||
|
CloseLayer(layerId, layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_device.VSyncMode != _vSyncMode)
|
|
||||||
|
Status acquireStatus = layer.Consumer.AcquireBuffer(out BufferItem item, 0);
|
||||||
|
|
||||||
|
if (acquireStatus == Status.Success)
|
||||||
{
|
{
|
||||||
UpdateSwapInterval(_device.VSyncMode == VSyncMode.Unbounded ? 0 : item.SwapInterval);
|
if (_device.VSyncMode == VSyncMode.Unbounded)
|
||||||
_vSyncMode = _device.VSyncMode;
|
{
|
||||||
}
|
if (_swapInterval != 0)
|
||||||
else if (item.SwapInterval != _swapInterval || _device.TargetVSyncInterval != _targetVSyncInterval)
|
{
|
||||||
{
|
UpdateSwapInterval(0);
|
||||||
UpdateSwapInterval(item.SwapInterval);
|
_vSyncMode = _device.VSyncMode;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else if (_device.VSyncMode != _vSyncMode)
|
||||||
|
{
|
||||||
|
UpdateSwapInterval(_device.VSyncMode == VSyncMode.Unbounded ? 0 : item.SwapInterval);
|
||||||
|
_vSyncMode = _device.VSyncMode;
|
||||||
|
}
|
||||||
|
else if (item.SwapInterval != _swapInterval || _device.TargetVSyncInterval != _targetVSyncInterval)
|
||||||
|
{
|
||||||
|
UpdateSwapInterval(item.SwapInterval);
|
||||||
|
}
|
||||||
|
|
||||||
PostFrameBuffer(layer, item);
|
PostFrameBuffer(layer, item);
|
||||||
}
|
}
|
||||||
else if (acquireStatus != Status.NoBufferAvailaible && acquireStatus != Status.InvalidOperation)
|
else if (acquireStatus != Status.NoBufferAvailaible && acquireStatus != Status.InvalidOperation)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException();
|
Logger.Warning?.Print(LogClass.SurfaceFlinger, $"Failed to acquire buffer for layer {layerId} (status: {acquireStatus})");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -413,8 +403,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
|
|
||||||
ulong bufferOffset = (ulong)item.GraphicBuffer.Object.Buffer.Surfaces[0].Offset;
|
ulong bufferOffset = (ulong)item.GraphicBuffer.Object.Buffer.Surfaces[0].Offset;
|
||||||
|
|
||||||
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(layer.Owner, nvMapHandle);
|
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(nvMapHandle);
|
||||||
|
|
||||||
ulong frameBufferAddress = map.Address + bufferOffset;
|
ulong frameBufferAddress = map.Address + bufferOffset;
|
||||||
|
|
||||||
Format format = ConvertColorFormat(item.GraphicBuffer.Object.Buffer.Surfaces[0].ColorFormat);
|
Format format = ConvertColorFormat(item.GraphicBuffer.Object.Buffer.Surfaces[0].ColorFormat);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
@@ -36,6 +37,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
public int PlanesCount;
|
public int PlanesCount;
|
||||||
|
|
||||||
[FieldOffset(0x34)]
|
[FieldOffset(0x34)]
|
||||||
public NvGraphicBufferSurfaceArray Surfaces;
|
public Array3<NvGraphicBufferSurface> Surfaces;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -40,7 +40,6 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
|
|||||||
ulong pid = context.Device.System.AppletState.AppletResourceUserIds.GetData<ulong>((int)appletResourceUserId);
|
ulong pid = context.Device.System.AppletState.AppletResourceUserIds.GetData<ulong>((int)appletResourceUserId);
|
||||||
|
|
||||||
context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, pid);
|
context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, pid);
|
||||||
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
|
|
||||||
|
|
||||||
context.ResponseData.Write(layerId);
|
context.ResponseData.Write(layerId);
|
||||||
|
|
||||||
|
|||||||
+143
@@ -1,4 +1,11 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
using Ryujinx.HLE.HOS.Services.SurfaceFlinger;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService.Types.Fbshare;
|
||||||
|
using Ryujinx.Horizon.Common;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
|
namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
|
||||||
{
|
{
|
||||||
@@ -7,6 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
|
|||||||
#pragma warning disable IDE0052 // Remove unread private member
|
#pragma warning disable IDE0052 // Remove unread private member
|
||||||
private readonly IApplicationDisplayService _applicationDisplayService;
|
private readonly IApplicationDisplayService _applicationDisplayService;
|
||||||
#pragma warning restore IDE0052
|
#pragma warning restore IDE0052
|
||||||
|
|
||||||
|
private KEvent _sharedFramebufferAcquirableEvent;
|
||||||
|
private int _sharedFramebufferAcquirableEventHandle;
|
||||||
|
|
||||||
public ISystemDisplayService(IApplicationDisplayService applicationDisplayService)
|
public ISystemDisplayService(IApplicationDisplayService applicationDisplayService)
|
||||||
{
|
{
|
||||||
@@ -57,5 +67,138 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
|
|||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandCmif(8225)] // 4.0.0+
|
||||||
|
// GetSharedBufferMemoryHandleId()
|
||||||
|
public ResultCode GetSharedBufferMemoryHandleId(ServiceCtx context)
|
||||||
|
{
|
||||||
|
context.ResponseData.Write((ulong)context.Device.System.ViServerS.GetSharedBufferNvMapId());
|
||||||
|
context.ResponseData.Write(context.Device.System.ViServerS.GetSharedBufferSize());
|
||||||
|
|
||||||
|
(ulong mapAddress, ulong mapSize) = context.Request.GetBufferType0x22();
|
||||||
|
|
||||||
|
context.Memory.Write(mapAddress, context.Device.System.ViServerS.GetSharedBufferMap());
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(8250)] // 4.0.0+
|
||||||
|
// OpenSharedLayer(nn::vi::fbshare::SharedLayerHandle, nn::applet::AppletResourceUserId, pid)
|
||||||
|
public ResultCode OpenSharedLayer(ServiceCtx context)
|
||||||
|
{
|
||||||
|
long sharedLayerHandle = context.RequestData.ReadInt64();
|
||||||
|
long appletResourceUserId = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
context.Device.System.ViServerS.OpenSharedLayer(sharedLayerHandle);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(8251)] // 4.0.0+
|
||||||
|
// CloseSharedLayer(nn::vi::fbshare::SharedLayerHandle)
|
||||||
|
public ResultCode CloseSharedLayer(ServiceCtx context)
|
||||||
|
{
|
||||||
|
long sharedLayerHandle = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
context.Device.System.ViServerS.CloseSharedLayer(sharedLayerHandle);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(8252)] // 4.0.0+
|
||||||
|
// ConnectSharedLayer(nn::vi::fbshare::SharedLayerHandle)
|
||||||
|
public ResultCode ConnectSharedLayer(ServiceCtx context)
|
||||||
|
{
|
||||||
|
long sharedLayerHandle = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
context.Device.System.ViServerS.ConnectSharedLayer(sharedLayerHandle);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(8253)] // 4.0.0+
|
||||||
|
// DisconnectSharedLayer(nn::vi::fbshare::SharedLayerHandle)
|
||||||
|
public ResultCode DisconnectSharedLayer(ServiceCtx context)
|
||||||
|
{
|
||||||
|
long sharedLayerHandle = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
context.Device.System.ViServerS.DisconnectSharedLayer(sharedLayerHandle);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(8254)] // 4.0.0+
|
||||||
|
// AcquireSharedFrameBuffer(nn::vi::fbshare::SharedLayerHandle) -> (nn::vi::native::NativeSync, nn::vi::fbshare::SharedLayerTextureIndexList, u64)
|
||||||
|
public ResultCode AcquireSharedFrameBuffer(ServiceCtx context)
|
||||||
|
{
|
||||||
|
long sharedLayerHandle = context.RequestData.ReadInt64();
|
||||||
|
|
||||||
|
int slot = context.Device.System.ViServerS.DequeueFrameBuffer(sharedLayerHandle, out AndroidFence fence);
|
||||||
|
|
||||||
|
var indexList = new SharedLayerTextureIndexList();
|
||||||
|
|
||||||
|
for (int i = 0; i < indexList.Indices.Length; i++)
|
||||||
|
{
|
||||||
|
indexList.Indices[i] = context.Device.System.ViServerS.GetFrameBufferMapIndex(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.ResponseData.WriteStruct(fence);
|
||||||
|
context.ResponseData.WriteStruct(indexList);
|
||||||
|
context.ResponseData.Write(0); // Padding
|
||||||
|
context.ResponseData.Write((ulong)slot);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(8255)] // 4.0.0+
|
||||||
|
// PresentSharedFrameBuffer(nn::vi::native::NativeSync, nn::vi::CropRegion, u32, u32, nn::vi::fbshare::SharedLayerHandle, u64)
|
||||||
|
public ResultCode PresentSharedFrameBuffer(ServiceCtx context)
|
||||||
|
{
|
||||||
|
AndroidFence nativeSync = context.RequestData.ReadStruct<AndroidFence>();
|
||||||
|
Rect cropRegion = context.RequestData.ReadStruct<Rect>();
|
||||||
|
|
||||||
|
NativeWindowTransform transform = (NativeWindowTransform)context.RequestData.ReadUInt32();
|
||||||
|
int swapInterval = context.RequestData.ReadInt32();
|
||||||
|
int padding = context.RequestData.ReadInt32();
|
||||||
|
|
||||||
|
long sharedLayerHandle = context.RequestData.ReadInt64();
|
||||||
|
ulong slot = context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
|
context.Device.System.ViServerS.QueueFrameBuffer(sharedLayerHandle, (int)slot, cropRegion, transform, swapInterval, nativeSync);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(8256)] // 4.0.0+
|
||||||
|
// GetSharedFrameBufferAcquirableEvent(nn::vi::fbshare::SharedLayerHandle) -> handle<copy>
|
||||||
|
public ResultCode GetSharedFrameBufferAcquirableEvent(ServiceCtx context)
|
||||||
|
{
|
||||||
|
if (_sharedFramebufferAcquirableEventHandle == 0)
|
||||||
|
{
|
||||||
|
_sharedFramebufferAcquirableEvent = new KEvent(context.Device.System.KernelContext);
|
||||||
|
_sharedFramebufferAcquirableEvent.WritableEvent.Signal();
|
||||||
|
|
||||||
|
if (context.Process.HandleTable.GenerateHandle(_sharedFramebufferAcquirableEvent.ReadableEvent, out _sharedFramebufferAcquirableEventHandle) != Result.Success)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Out of handles!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_sharedFramebufferAcquirableEventHandle);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandCmif(8258)] // 5.0.0+
|
||||||
|
// CancelSharedFrameBuffer(nn::vi::fbshare::SharedLayerHandle, u64)
|
||||||
|
public ResultCode CancelSharedFrameBuffer(ServiceCtx context)
|
||||||
|
{
|
||||||
|
long sharedLayerHandle = context.RequestData.ReadInt64();
|
||||||
|
ulong slot = context.RequestData.ReadUInt64();
|
||||||
|
|
||||||
|
context.Device.System.ViServerS.CancelFrameBuffer(sharedLayerHandle, (int)slot);
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService.Types.Fbshare
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 0x4)]
|
||||||
|
struct SharedLayerTextureIndexList
|
||||||
|
{
|
||||||
|
public Array4<int> Indices;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -248,8 +248,6 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
|
|
||||||
|
|
||||||
using Parcel parcel = new(0x28, 0x4);
|
using Parcel parcel = new(0x28, 0x4);
|
||||||
|
|
||||||
parcel.WriteObject(producer, "dispdrv\0");
|
parcel.WriteObject(producer, "dispdrv\0");
|
||||||
@@ -285,9 +283,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
|
|||||||
|
|
||||||
// TODO: support multi display.
|
// TODO: support multi display.
|
||||||
IBinder producer = context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, 0, LayerState.Stray);
|
IBinder producer = context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, 0, LayerState.Stray);
|
||||||
|
|
||||||
context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
|
|
||||||
|
|
||||||
using Parcel parcel = new(0x28, 0x4);
|
using Parcel parcel = new(0x28, 0x4);
|
||||||
|
|
||||||
parcel.WriteObject(producer, "dispdrv\0");
|
parcel.WriteObject(producer, "dispdrv\0");
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Ryujinx.Common.Memory;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Vi.Types
|
||||||
|
{
|
||||||
|
struct SharedBufferMap
|
||||||
|
{
|
||||||
|
public struct Entry
|
||||||
|
{
|
||||||
|
public ulong Offset;
|
||||||
|
public ulong Size;
|
||||||
|
public uint Width;
|
||||||
|
public uint Height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count;
|
||||||
|
public int Padding;
|
||||||
|
public Array16<Entry> SharedBuffers;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.Gpu;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
||||||
|
using Ryujinx.HLE.HOS.Services.SurfaceFlinger;
|
||||||
|
using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Vi.Types;
|
||||||
|
using Ryujinx.Memory;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services
|
||||||
|
{
|
||||||
|
class ViServer : ServerBase
|
||||||
|
{
|
||||||
|
private const int TotalFramebuffers = 16;
|
||||||
|
|
||||||
|
private SurfaceFlinger.SurfaceFlinger _surfaceFlinger;
|
||||||
|
|
||||||
|
private readonly uint _fbWidth;
|
||||||
|
private readonly uint _fbHeight;
|
||||||
|
private readonly PixelFormat _fbFormat;
|
||||||
|
private readonly int _fbUsage;
|
||||||
|
private readonly uint _fbCount;
|
||||||
|
private uint _fbSlotsRequested;
|
||||||
|
|
||||||
|
private ulong _pid;
|
||||||
|
private ulong _fbsBaseAddress;
|
||||||
|
private int _bufferNvMapId;
|
||||||
|
private SharedBufferMap _bufferMap;
|
||||||
|
private long _sharedLayerId;
|
||||||
|
|
||||||
|
public ViServer(KernelContext context, string name) : base(context, name)
|
||||||
|
{
|
||||||
|
_fbWidth = 1280;
|
||||||
|
_fbHeight = 720;
|
||||||
|
_fbFormat = PixelFormat.Rgba8888;
|
||||||
|
_fbUsage = 0x100 | 0x200 | 0x800;
|
||||||
|
_fbCount = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override ulong CalculateRequiredHeapSize()
|
||||||
|
{
|
||||||
|
return GetSharedBufferSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CustomInit(KernelContext context, ulong pid, ulong heapAddress)
|
||||||
|
{
|
||||||
|
_pid = pid;
|
||||||
|
_fbsBaseAddress = heapAddress;
|
||||||
|
|
||||||
|
context.Device.Gpu.RegisterProcess(pid, KernelStatic.GetCurrentProcess().CpuMemory);
|
||||||
|
|
||||||
|
ulong bufferSize = CalculateFramebufferSize();
|
||||||
|
ulong totalSize = bufferSize * TotalFramebuffers;
|
||||||
|
|
||||||
|
KernelStatic.GetCurrentProcess().CpuMemory.Fill(heapAddress, totalSize, 0xff);
|
||||||
|
|
||||||
|
int mapId = NvMapDeviceFile.CreateMap(pid, heapAddress, (uint)totalSize);
|
||||||
|
|
||||||
|
_bufferNvMapId = mapId;
|
||||||
|
_bufferMap.Count = TotalFramebuffers;
|
||||||
|
|
||||||
|
ulong offset = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < TotalFramebuffers; i++)
|
||||||
|
{
|
||||||
|
_bufferMap.SharedBuffers[i].Offset = offset;
|
||||||
|
_bufferMap.SharedBuffers[i].Size = bufferSize;
|
||||||
|
_bufferMap.SharedBuffers[i].Width = _fbWidth;
|
||||||
|
_bufferMap.SharedBuffers[i].Height = _fbHeight;
|
||||||
|
|
||||||
|
offset += bufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
_surfaceFlinger = context.Device.System.SurfaceFlinger;
|
||||||
|
_surfaceFlinger.CreateLayer(out _sharedLayerId, pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenSharedLayer(long layerId)
|
||||||
|
{
|
||||||
|
_surfaceFlinger.OpenLayer(_pid, layerId, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CloseSharedLayer(long layerId)
|
||||||
|
{
|
||||||
|
_surfaceFlinger.CloseLayer(layerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConnectSharedLayer(long layerId)
|
||||||
|
{
|
||||||
|
IGraphicBufferProducer producer = _surfaceFlinger.GetProducerByLayerId(layerId);
|
||||||
|
|
||||||
|
producer.Connect(null, NativeWindowApi.NVN, false, out IGraphicBufferProducer.QueueBufferOutput output);
|
||||||
|
|
||||||
|
GraphicBuffer graphicBuffer = new GraphicBuffer();
|
||||||
|
|
||||||
|
int gobHeightLog2 = 4;
|
||||||
|
int blockHeight = 8 * (1 << gobHeightLog2);
|
||||||
|
uint widthAlignedBytes = BitUtils.AlignUp(_fbWidth * 4, 64u);
|
||||||
|
uint widthAligned = widthAlignedBytes / 4;
|
||||||
|
uint heightAligned = BitUtils.AlignUp(_fbHeight, (uint)blockHeight);
|
||||||
|
uint totalSize = widthAlignedBytes * heightAligned;
|
||||||
|
|
||||||
|
graphicBuffer.Header.Magic = 0x47424652;
|
||||||
|
graphicBuffer.Header.Width = (int)_fbWidth;
|
||||||
|
graphicBuffer.Header.Height = (int)_fbHeight;
|
||||||
|
graphicBuffer.Header.Stride = (int)widthAligned;
|
||||||
|
graphicBuffer.Header.Format = _fbFormat;
|
||||||
|
graphicBuffer.Header.Usage = _fbUsage;
|
||||||
|
graphicBuffer.Header.IntsCount = (Unsafe.SizeOf<GraphicBuffer>() - Unsafe.SizeOf<GraphicBufferHeader>()) / sizeof(int);
|
||||||
|
graphicBuffer.Buffer.NvMapId = _bufferNvMapId;
|
||||||
|
graphicBuffer.Buffer.Magic = unchecked((int)0xDAFFCAFF);
|
||||||
|
graphicBuffer.Buffer.Pid = 42;
|
||||||
|
graphicBuffer.Buffer.Usage = _fbUsage;
|
||||||
|
graphicBuffer.Buffer.PixelFormat = (int)_fbFormat;
|
||||||
|
graphicBuffer.Buffer.ExternalPixelFormat = (int)_fbFormat;
|
||||||
|
graphicBuffer.Buffer.Stride = (int)widthAligned;
|
||||||
|
graphicBuffer.Buffer.FrameBufferSize = (int)totalSize;
|
||||||
|
graphicBuffer.Buffer.PlanesCount = 1;
|
||||||
|
graphicBuffer.Buffer.Surfaces[0].Width = _fbWidth;
|
||||||
|
graphicBuffer.Buffer.Surfaces[0].Height = _fbHeight;
|
||||||
|
graphicBuffer.Buffer.Surfaces[0].ColorFormat = ColorFormat.A8B8G8R8;
|
||||||
|
graphicBuffer.Buffer.Surfaces[0].Layout = 3; // Block linear
|
||||||
|
graphicBuffer.Buffer.Surfaces[0].Pitch = (int)widthAlignedBytes;
|
||||||
|
graphicBuffer.Buffer.Surfaces[0].Kind = 0xfe; // Generic 16Bx2
|
||||||
|
graphicBuffer.Buffer.Surfaces[0].BlockHeightLog2 = gobHeightLog2;
|
||||||
|
graphicBuffer.Buffer.Surfaces[0].Size = (int)totalSize;
|
||||||
|
|
||||||
|
for (int slot = 0; slot < _fbCount; slot++)
|
||||||
|
{
|
||||||
|
graphicBuffer.Buffer.Surfaces[0].Offset = slot * (int)totalSize;
|
||||||
|
|
||||||
|
producer.SetPreallocatedBuffer(slot, new AndroidStrongPointer<GraphicBuffer>(graphicBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
_fbSlotsRequested = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisconnectSharedLayer(long layerId)
|
||||||
|
{
|
||||||
|
IGraphicBufferProducer producer = _surfaceFlinger.GetProducerByLayerId(layerId);
|
||||||
|
|
||||||
|
producer.Disconnect(NativeWindowApi.NVN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int DequeueFrameBuffer(long layerId, out AndroidFence fence)
|
||||||
|
{
|
||||||
|
IGraphicBufferProducer producer = _surfaceFlinger.GetProducerByLayerId(layerId);
|
||||||
|
|
||||||
|
Status status = producer.DequeueBuffer(out int slot, out fence, false, _fbWidth, _fbHeight, _fbFormat, (uint)_fbUsage);
|
||||||
|
|
||||||
|
if (status == Status.Success)
|
||||||
|
{
|
||||||
|
if ((_fbSlotsRequested & (1u << slot)) == 0)
|
||||||
|
{
|
||||||
|
status = producer.RequestBuffer(slot, out _);
|
||||||
|
|
||||||
|
if (status != Status.Success)
|
||||||
|
{
|
||||||
|
producer.CancelBuffer(slot, ref fence);
|
||||||
|
}
|
||||||
|
|
||||||
|
_fbSlotsRequested |= 1u << slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void QueueFrameBuffer(long layerId, int slot, Rect crop, NativeWindowTransform transform, int swapInterval, AndroidFence fence)
|
||||||
|
{
|
||||||
|
IGraphicBufferProducer producer = _surfaceFlinger.GetProducerByLayerId(layerId);
|
||||||
|
|
||||||
|
IGraphicBufferProducer.QueueBufferInput input = new();
|
||||||
|
|
||||||
|
input.Crop = crop;
|
||||||
|
input.Transform = transform;
|
||||||
|
input.SwapInterval = swapInterval;
|
||||||
|
input.Fence = fence;
|
||||||
|
|
||||||
|
Status status = producer.QueueBuffer(slot, ref input, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CancelFrameBuffer(long layerId, int slot)
|
||||||
|
{
|
||||||
|
IGraphicBufferProducer producer = _surfaceFlinger.GetProducerByLayerId(layerId);
|
||||||
|
AndroidFence fence = default;
|
||||||
|
|
||||||
|
producer.CancelBuffer(slot, ref fence);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetFrameBufferMapIndex(int index)
|
||||||
|
{
|
||||||
|
return (uint)index < _fbCount ? index : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetSharedBufferNvMapId()
|
||||||
|
{
|
||||||
|
return _bufferNvMapId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong GetSharedBufferSize()
|
||||||
|
{
|
||||||
|
return CalculateFramebufferSize() * TotalFramebuffers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long GetSharedLayerId()
|
||||||
|
{
|
||||||
|
return _sharedLayerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ulong CalculateFramebufferSize()
|
||||||
|
{
|
||||||
|
// Each GOB dimension is 512 bytes x 8 lines.
|
||||||
|
// Assume 16 GOBs, for a total of 16 x 8 = 128 lines.
|
||||||
|
return BitUtils.AlignUp(_fbWidth * 4, 512u) * BitUtils.AlignUp(_fbHeight, 128u);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SharedBufferMap GetSharedBufferMap()
|
||||||
|
{
|
||||||
|
return _bufferMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetApplicationLastPresentedFrameHandle(GpuContext gpuContext)
|
||||||
|
{
|
||||||
|
TextureData texture = gpuContext.Window.GetLastPresentedData();
|
||||||
|
IVirtualMemoryManagerTracked selfAs = KernelStatic.GetProcessByPid(_pid).CpuMemory;
|
||||||
|
int fbIndex = (int)_fbCount; // Place it after all our frame buffers.
|
||||||
|
|
||||||
|
selfAs.Write(_fbsBaseAddress + _bufferMap.SharedBuffers[fbIndex].Offset, texture.Data);
|
||||||
|
|
||||||
|
return fbIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
|
using LibHac.Ncm;
|
||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Audio.Backends.CompatLayer;
|
using Ryujinx.Audio.Backends.CompatLayer;
|
||||||
using Ryujinx.Audio.Integration;
|
using Ryujinx.Audio.Integration;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Graphics.Gpu;
|
using Ryujinx.Graphics.Gpu;
|
||||||
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.Services.Apm;
|
using Ryujinx.HLE.HOS.Services.Apm;
|
||||||
@@ -158,5 +161,18 @@ namespace Ryujinx.HLE
|
|||||||
Shared = null;
|
Shared = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool LoadSystemProgramId(ulong programId)
|
||||||
|
{
|
||||||
|
string contentPath = System.ContentManager.GetInstalledContentPath(programId, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||||
|
string filePath = VirtualFileSystem.SwitchPathToSystemPath(contentPath);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(filePath))
|
||||||
|
{
|
||||||
|
throw new InvalidSystemResourceException("Specified title ID is not installed on the system.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Processes.LoadNca(filePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
|
using Ryujinx.Memory.Tracking;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@@ -9,7 +10,7 @@ namespace Ryujinx.Memory
|
|||||||
/// Represents a address space manager.
|
/// Represents a address space manager.
|
||||||
/// Supports virtual memory region mapping, address translation and read/write access to mapped regions.
|
/// Supports virtual memory region mapping, address translation and read/write access to mapped regions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AddressSpaceManager : VirtualMemoryManagerBase, IVirtualMemoryManager
|
public sealed class AddressSpaceManager : VirtualMemoryManagerBase, IVirtualMemoryManagerTracked
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool UsesPrivateAllocations => false;
|
public bool UsesPrivateAllocations => false;
|
||||||
@@ -21,6 +22,8 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
private readonly MemoryBlock _backingMemory;
|
private readonly MemoryBlock _backingMemory;
|
||||||
private readonly PageTable<nuint> _pageTable;
|
private readonly PageTable<nuint> _pageTable;
|
||||||
|
private readonly MemoryTracking _tracking;
|
||||||
|
private bool _writeTracked;
|
||||||
|
|
||||||
protected override ulong AddressSpaceSize { get; }
|
protected override ulong AddressSpaceSize { get; }
|
||||||
|
|
||||||
@@ -44,6 +47,7 @@ namespace Ryujinx.Memory
|
|||||||
AddressSpaceSize = asSize;
|
AddressSpaceSize = asSize;
|
||||||
_backingMemory = backingMemory;
|
_backingMemory = backingMemory;
|
||||||
_pageTable = new PageTable<nuint>();
|
_pageTable = new PageTable<nuint>();
|
||||||
|
_tracking = new MemoryTracking(this, 0x1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -227,7 +231,7 @@ namespace Ryujinx.Memory
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection, bool guest = false)
|
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection, bool guest = false)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
_writeTracked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected unsafe override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
|
protected unsafe override Memory<byte> GetPhysicalAddressMemory(nuint pa, int size)
|
||||||
@@ -240,5 +244,29 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
protected override nuint TranslateVirtualAddressUnchecked(ulong va)
|
protected override nuint TranslateVirtualAddressUnchecked(ulong va)
|
||||||
=> GetHostAddress(va);
|
=> GetHostAddress(va);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
|
||||||
|
{
|
||||||
|
if (_writeTracked)
|
||||||
|
{
|
||||||
|
_tracking.VirtualMemoryEvent(va, size, write, precise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public RegionHandle BeginTracking(ulong address, ulong size, int id, RegionFlags flags = RegionFlags.None)
|
||||||
|
{
|
||||||
|
return _tracking.BeginTracking(address, size, id, flags);
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity, int id, RegionFlags flags = RegionFlags.None)
|
||||||
|
{
|
||||||
|
return _tracking.BeginGranularTracking(address, size, handles, granularity, id, flags);
|
||||||
|
}
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id)
|
||||||
|
{
|
||||||
|
return _tracking.BeginSmartGranularTracking(address, size, granularity, id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ using Ryujinx.Memory.Tracking;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu
|
namespace Ryujinx.Memory
|
||||||
{
|
{
|
||||||
public interface IVirtualMemoryManagerTracked : IVirtualMemoryManager
|
public interface IVirtualMemoryManagerTracked : IVirtualMemoryManager
|
||||||
{
|
{
|
||||||
@@ -41,6 +41,7 @@ using Ryujinx.HLE.FileSystem;
|
|||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
|
using Ryujinx.HLE.Loaders.Processes;
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using Ryujinx.Input.HLE;
|
using Ryujinx.Input.HLE;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
@@ -672,6 +673,7 @@ namespace Ryujinx.Ava
|
|||||||
DiscordIntegrationModule.GuestAppStartedAt = Timestamps.Now;
|
DiscordIntegrationModule.GuestAppStartedAt = Timestamps.Now;
|
||||||
|
|
||||||
InitEmulatedSwitch();
|
InitEmulatedSwitch();
|
||||||
|
Device.LoadSystemProgramId(0x010000000000100C);
|
||||||
MainWindow.UpdateGraphicsConfig();
|
MainWindow.UpdateGraphicsConfig();
|
||||||
|
|
||||||
SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
||||||
|
|||||||
@@ -463,7 +463,7 @@
|
|||||||
"no_NO": "",
|
"no_NO": "",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Abrir Pasta de Capturas de Tela",
|
"pt_BR": "Abrir Pasta de Capturas de Tela",
|
||||||
"ru_RU": "Открыть папку со скриншотами",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -613,7 +613,7 @@
|
|||||||
"no_NO": "Start Spillet med UI Gjemt",
|
"no_NO": "Start Spillet med UI Gjemt",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Iniciar Jogos Ocultando a Interface",
|
"pt_BR": "Iniciar Jogos Ocultando a Interface",
|
||||||
"ru_RU": "Запускать игры скрывая интерфейс",
|
"ru_RU": "",
|
||||||
"sv_SE": "Starta spel med dolt användargränssnitt",
|
"sv_SE": "Starta spel med dolt användargränssnitt",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -688,7 +688,7 @@
|
|||||||
"no_NO": "_Administrere Brukerprofiler",
|
"no_NO": "_Administrere Brukerprofiler",
|
||||||
"pl_PL": "_Zarządzaj profilami użytkowników",
|
"pl_PL": "_Zarządzaj profilami użytkowników",
|
||||||
"pt_BR": "_Gerenciar Perfis de Usuário",
|
"pt_BR": "_Gerenciar Perfis de Usuário",
|
||||||
"ru_RU": "_Менеджер учётных записей",
|
"ru_RU": "_Менеджер учетных записей",
|
||||||
"sv_SE": "_Hantera användarprofiler",
|
"sv_SE": "_Hantera användarprofiler",
|
||||||
"th_TH": "_จัดการโปรไฟล์ผู้ใช้งาน",
|
"th_TH": "_จัดการโปรไฟล์ผู้ใช้งาน",
|
||||||
"tr_TR": "_Kullanıcı Profillerini Yönet",
|
"tr_TR": "_Kullanıcı Profillerini Yönet",
|
||||||
@@ -1563,7 +1563,7 @@
|
|||||||
"no_NO": "Utviklet av {0}",
|
"no_NO": "Utviklet av {0}",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Desenvolvido por {0}",
|
"pt_BR": "Desenvolvido por {0}",
|
||||||
"ru_RU": "Разработана {0}",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -1863,7 +1863,7 @@
|
|||||||
"no_NO": "Kompatibilitet",
|
"no_NO": "Kompatibilitet",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Compatibilidade:",
|
"pt_BR": "Compatibilidade:",
|
||||||
"ru_RU": "Совместимость:",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -1888,7 +1888,7 @@
|
|||||||
"no_NO": "Tittel ID:",
|
"no_NO": "Tittel ID:",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "ID do Título:",
|
"pt_BR": "ID do Título:",
|
||||||
"ru_RU": "ID приложения",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -1913,7 +1913,7 @@
|
|||||||
"no_NO": "Spill som Arrangeres: {0}",
|
"no_NO": "Spill som Arrangeres: {0}",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Jogos Hospedados: {0}",
|
"pt_BR": "Jogos Hospedados: {0}",
|
||||||
"ru_RU": "Запущенно игр: {0}",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -1938,7 +1938,7 @@
|
|||||||
"no_NO": "Online-spillere: {0}",
|
"no_NO": "Online-spillere: {0}",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Jogadores Online: {0}",
|
"pt_BR": "Jogadores Online: {0}",
|
||||||
"ru_RU": "Игроков онлайн: {0}",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -2288,7 +2288,7 @@
|
|||||||
"no_NO": "Tøm PPTC-bufferen",
|
"no_NO": "Tøm PPTC-bufferen",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Limpar Cache PPTC",
|
"pt_BR": "Limpar Cache PPTC",
|
||||||
"ru_RU": "Очистить кэш PPTC",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -2313,7 +2313,7 @@
|
|||||||
"no_NO": "Sletter alle PPTC-cache-filer for applikasjonen",
|
"no_NO": "Sletter alle PPTC-cache-filer for applikasjonen",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Apaga os arquivos de cache PPTC do aplicativo",
|
"pt_BR": "Apaga os arquivos de cache PPTC do aplicativo",
|
||||||
"ru_RU": "Удаляет все файлы кэша PPTC для приложения",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -2638,7 +2638,7 @@
|
|||||||
"no_NO": "Pakk ut RomFS filene fra valgt DLC fil",
|
"no_NO": "Pakk ut RomFS filene fra valgt DLC fil",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Extraia o RomFS de um arquivo DLC selecionado",
|
"pt_BR": "Extraia o RomFS de um arquivo DLC selecionado",
|
||||||
"ru_RU": "Извлекает файлы RomFS из выбранного файла DLC",
|
"ru_RU": "",
|
||||||
"sv_SE": "Extrahera RomFS från en vald DLC-fil",
|
"sv_SE": "Extrahera RomFS från en vald DLC-fil",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -2788,7 +2788,7 @@
|
|||||||
"no_NO": "Vis kompatibilitetsoppføring",
|
"no_NO": "Vis kompatibilitetsoppføring",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Mostrar Dados de Compatibilidade",
|
"pt_BR": "Mostrar Dados de Compatibilidade",
|
||||||
"ru_RU": "Показать записи о совместимости",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -2813,7 +2813,7 @@
|
|||||||
"no_NO": "Vis det valgte spillet i kompatibilitetslisten, som du vanligvis får tilgang til via Hjelp-menyen.",
|
"no_NO": "Vis det valgte spillet i kompatibilitetslisten, som du vanligvis får tilgang til via Hjelp-menyen.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Exibe o jogo selecionado na Lista de Compatibilidade, que normalmente pode ser acessada pelo menu Ajuda.",
|
"pt_BR": "Exibe o jogo selecionado na Lista de Compatibilidade, que normalmente pode ser acessada pelo menu Ajuda.",
|
||||||
"ru_RU": "Отобразить выбранную игру в списке совместимости, доступ к которому вы обычно можете получить через меню Справки.",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -2838,7 +2838,7 @@
|
|||||||
"no_NO": "Vis Spill Info",
|
"no_NO": "Vis Spill Info",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Mostrar Informações do Jogo",
|
"pt_BR": "Mostrar Informações do Jogo",
|
||||||
"ru_RU": "Показать информацию об игре",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -2863,7 +2863,7 @@
|
|||||||
"no_NO": "Vis statistikk og detaljer om det valgte spillet.",
|
"no_NO": "Vis statistikk og detaljer om det valgte spillet.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Exibe estatísticas e detalhes sobre o jogo selecionado.",
|
"pt_BR": "Exibe estatísticas e detalhes sobre o jogo selecionado.",
|
||||||
"ru_RU": "Показывать статистику и подробную информацию о выбранной игре.",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -3388,7 +3388,7 @@
|
|||||||
"no_NO": "Se etter Oppdateringer:",
|
"no_NO": "Se etter Oppdateringer:",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Verificar Atualizações:",
|
"pt_BR": "Verificar Atualizações:",
|
||||||
"ru_RU": "Проверка наличия обновлений",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -3413,7 +3413,7 @@
|
|||||||
"no_NO": "Av",
|
"no_NO": "Av",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Desligado",
|
"pt_BR": "Desligado",
|
||||||
"ru_RU": "Отключить",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -3438,7 +3438,7 @@
|
|||||||
"no_NO": "Spør",
|
"no_NO": "Spør",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Ao Abrir",
|
"pt_BR": "Ao Abrir",
|
||||||
"ru_RU": "При запуске",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -3463,7 +3463,7 @@
|
|||||||
"no_NO": "Bakgrunn",
|
"no_NO": "Bakgrunn",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "2° Plano",
|
"pt_BR": "2° Plano",
|
||||||
"ru_RU": "В фоне",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -3488,7 +3488,7 @@
|
|||||||
"no_NO": "På Emulator Fokus Tapt:",
|
"no_NO": "På Emulator Fokus Tapt:",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Ao Perder o Foco:",
|
"pt_BR": "Ao Perder o Foco:",
|
||||||
"ru_RU": "При выходе эмулятора из фокуса",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -3513,7 +3513,7 @@
|
|||||||
"no_NO": "Gjør Ingenting",
|
"no_NO": "Gjør Ingenting",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Não Fazer Nada",
|
"pt_BR": "Não Fazer Nada",
|
||||||
"ru_RU": "Ничего не делать",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -3538,7 +3538,7 @@
|
|||||||
"no_NO": "Blokkinngang",
|
"no_NO": "Blokkinngang",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Bloquear Controles",
|
"pt_BR": "Bloquear Controles",
|
||||||
"ru_RU": "Блокировать управление",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -3563,7 +3563,7 @@
|
|||||||
"no_NO": "Demp Lyd",
|
"no_NO": "Demp Lyd",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Ficar Mudo",
|
"pt_BR": "Ficar Mudo",
|
||||||
"ru_RU": "Отключить звук",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -3588,7 +3588,7 @@
|
|||||||
"no_NO": "Blokker Inputs og demp Volumet",
|
"no_NO": "Blokker Inputs og demp Volumet",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Bloquear Controles & Ficar Mudo",
|
"pt_BR": "Bloquear Controles & Ficar Mudo",
|
||||||
"ru_RU": "Блокировать управление и отключить звук",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -3613,7 +3613,7 @@
|
|||||||
"no_NO": "Pause Emulatoren",
|
"no_NO": "Pause Emulatoren",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Pausar a Emulação",
|
"pt_BR": "Pausar a Emulação",
|
||||||
"ru_RU": "Поставить паузу",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -3688,7 +3688,7 @@
|
|||||||
"no_NO": "Deaktiver inndata når vinduet er ute av fokus",
|
"no_NO": "Deaktiver inndata når vinduet er ute av fokus",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Desativar Controles Quando Estiver Fora de Foco",
|
"pt_BR": "Desativar Controles Quando Estiver Fora de Foco",
|
||||||
"ru_RU": "Отключает управление при выходе из фокуса",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -4713,7 +4713,7 @@
|
|||||||
"no_NO": "Match systemtid",
|
"no_NO": "Match systemtid",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Sincronizar com o Sistema PC",
|
"pt_BR": "Sincronizar com o Sistema PC",
|
||||||
"ru_RU": "Соответствовать времени в системе",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -5138,7 +5138,7 @@
|
|||||||
"no_NO": "",
|
"no_NO": "",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Ignorar Applet do Controlador",
|
"pt_BR": "Ignorar Applet do Controlador",
|
||||||
"ru_RU": "Игнорировать апплет контроллера",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -5988,7 +5988,7 @@
|
|||||||
"no_NO": "Aktivere UI-logger",
|
"no_NO": "Aktivere UI-logger",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Habilitar Logs da IU",
|
"pt_BR": "Habilitar Logs da IU",
|
||||||
"ru_RU": "Включить журнал интерфейса",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -6388,7 +6388,7 @@
|
|||||||
"no_NO": "Tilbakestill innstillinger",
|
"no_NO": "Tilbakestill innstillinger",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Redefinir Configurações",
|
"pt_BR": "Redefinir Configurações",
|
||||||
"ru_RU": "Сбросить настройки",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -6413,7 +6413,7 @@
|
|||||||
"no_NO": "Jeg vil tilbakestille innstillingene mine.",
|
"no_NO": "Jeg vil tilbakestille innstillingene mine.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Quero redefinir minhas configurações.",
|
"pt_BR": "Quero redefinir minhas configurações.",
|
||||||
"ru_RU": "Я хочу сбросить свои настройки.",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -8313,7 +8313,7 @@
|
|||||||
"no_NO": "",
|
"no_NO": "",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "",
|
"pt_BR": "",
|
||||||
"ru_RU": "LED-подсветка",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -8338,7 +8338,7 @@
|
|||||||
"no_NO": "Deaktiver",
|
"no_NO": "Deaktiver",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Desabilitar",
|
"pt_BR": "Desabilitar",
|
||||||
"ru_RU": "Отключить",
|
"ru_RU": "",
|
||||||
"sv_SE": "Inaktivera",
|
"sv_SE": "Inaktivera",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -8363,7 +8363,7 @@
|
|||||||
"no_NO": "Regnbue",
|
"no_NO": "Regnbue",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Arco-íris",
|
"pt_BR": "Arco-íris",
|
||||||
"ru_RU": "Радужная",
|
"ru_RU": "",
|
||||||
"sv_SE": "Regnbåge",
|
"sv_SE": "Regnbåge",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -8388,7 +8388,7 @@
|
|||||||
"no_NO": "Regnbue Hastighet",
|
"no_NO": "Regnbue Hastighet",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Velocidade do Arco-íris",
|
"pt_BR": "Velocidade do Arco-íris",
|
||||||
"ru_RU": "Скорость переливания",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -8413,7 +8413,7 @@
|
|||||||
"no_NO": "Farge",
|
"no_NO": "Farge",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Cor",
|
"pt_BR": "Cor",
|
||||||
"ru_RU": "Цвет",
|
"ru_RU": "",
|
||||||
"sv_SE": "Färg",
|
"sv_SE": "Färg",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -9438,7 +9438,7 @@
|
|||||||
"no_NO": "Numerisk 1",
|
"no_NO": "Numerisk 1",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "",
|
"pt_BR": "",
|
||||||
"ru_RU": "Блок цифр 1",
|
"ru_RU": "1 (цифровий блок)",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -11838,7 +11838,7 @@
|
|||||||
"no_NO": "Vis/Skjul favorittstatus for spillet",
|
"no_NO": "Vis/Skjul favorittstatus for spillet",
|
||||||
"pl_PL": "Przełącz status Ulubionej Gry",
|
"pl_PL": "Przełącz status Ulubionej Gry",
|
||||||
"pt_BR": "Marca ou desmarca o jogo como favorito",
|
"pt_BR": "Marca ou desmarca o jogo como favorito",
|
||||||
"ru_RU": "Добавляет игру в избранное и помечает звёздочкой",
|
"ru_RU": "Добавляет игру в избранное и помечает звездочкой",
|
||||||
"sv_SE": "Växla favoritstatus för spelet",
|
"sv_SE": "Växla favoritstatus för spelet",
|
||||||
"th_TH": "สลับสถานะเกมที่ชื่นชอบ",
|
"th_TH": "สลับสถานะเกมที่ชื่นชอบ",
|
||||||
"tr_TR": "Oyunu Favorilere Ekle/Çıkar",
|
"tr_TR": "Oyunu Favorilere Ekle/Çıkar",
|
||||||
@@ -11913,7 +11913,7 @@
|
|||||||
"no_NO": "Mørk",
|
"no_NO": "Mørk",
|
||||||
"pl_PL": "Ciemny",
|
"pl_PL": "Ciemny",
|
||||||
"pt_BR": "Escuro",
|
"pt_BR": "Escuro",
|
||||||
"ru_RU": "Тёмная",
|
"ru_RU": "Темная",
|
||||||
"sv_SE": "Mörkt",
|
"sv_SE": "Mörkt",
|
||||||
"th_TH": "มืด",
|
"th_TH": "มืด",
|
||||||
"tr_TR": "Karanlık",
|
"tr_TR": "Karanlık",
|
||||||
@@ -12113,7 +12113,7 @@
|
|||||||
"no_NO": "{0} - Bekreftelse",
|
"no_NO": "{0} - Bekreftelse",
|
||||||
"pl_PL": "{0} - Potwierdzenie",
|
"pl_PL": "{0} - Potwierdzenie",
|
||||||
"pt_BR": "{0} - Confirmação",
|
"pt_BR": "{0} - Confirmação",
|
||||||
"ru_RU": "{0} — Подтверждение",
|
"ru_RU": "{0} - Подтверждение",
|
||||||
"sv_SE": "{0} - Bekräftelse",
|
"sv_SE": "{0} - Bekräftelse",
|
||||||
"th_TH": "{0} - ยืนยัน",
|
"th_TH": "{0} - ยืนยัน",
|
||||||
"tr_TR": "{0} - Onay",
|
"tr_TR": "{0} - Onay",
|
||||||
@@ -12138,7 +12138,7 @@
|
|||||||
"no_NO": "{0} – Oppdaterer",
|
"no_NO": "{0} – Oppdaterer",
|
||||||
"pl_PL": "{0} - Asystent aktualizacji",
|
"pl_PL": "{0} - Asystent aktualizacji",
|
||||||
"pt_BR": "{0} - Atualizador",
|
"pt_BR": "{0} - Atualizador",
|
||||||
"ru_RU": "{0} — Обновление",
|
"ru_RU": "{0} - Обновление",
|
||||||
"sv_SE": "{0} - Uppdatering",
|
"sv_SE": "{0} - Uppdatering",
|
||||||
"th_TH": "{0} - อัพเดต",
|
"th_TH": "{0} - อัพเดต",
|
||||||
"tr_TR": "{0} - Güncelleyici",
|
"tr_TR": "{0} - Güncelleyici",
|
||||||
@@ -12163,7 +12163,7 @@
|
|||||||
"no_NO": "{0} - Feil",
|
"no_NO": "{0} - Feil",
|
||||||
"pl_PL": "{0} - Błąd",
|
"pl_PL": "{0} - Błąd",
|
||||||
"pt_BR": "{0} - Erro",
|
"pt_BR": "{0} - Erro",
|
||||||
"ru_RU": "{0} — Ошибка",
|
"ru_RU": "{0} - Ошибка",
|
||||||
"sv_SE": "{0} - Fel",
|
"sv_SE": "{0} - Fel",
|
||||||
"th_TH": "{0} - ผิดพลาด",
|
"th_TH": "{0} - ผิดพลาด",
|
||||||
"tr_TR": "{0} - Hata",
|
"tr_TR": "{0} - Hata",
|
||||||
@@ -12188,7 +12188,7 @@
|
|||||||
"no_NO": "{0} - Advarsel",
|
"no_NO": "{0} - Advarsel",
|
||||||
"pl_PL": "{0} - Ostrzeżenie",
|
"pl_PL": "{0} - Ostrzeżenie",
|
||||||
"pt_BR": "{0} - Alerta",
|
"pt_BR": "{0} - Alerta",
|
||||||
"ru_RU": "{0} — Предупреждение",
|
"ru_RU": "{0} - Предупреждение",
|
||||||
"sv_SE": "{0} - Varning",
|
"sv_SE": "{0} - Varning",
|
||||||
"th_TH": "{0} - คำเตือน",
|
"th_TH": "{0} - คำเตือน",
|
||||||
"tr_TR": "{0} - Uyarı",
|
"tr_TR": "{0} - Uyarı",
|
||||||
@@ -12213,7 +12213,7 @@
|
|||||||
"no_NO": "{0} - Avslutt",
|
"no_NO": "{0} - Avslutt",
|
||||||
"pl_PL": "{0} - Wyjdź",
|
"pl_PL": "{0} - Wyjdź",
|
||||||
"pt_BR": "{0} - Sair",
|
"pt_BR": "{0} - Sair",
|
||||||
"ru_RU": "{0} — Выход",
|
"ru_RU": "{0} - Выход",
|
||||||
"sv_SE": "{0} - Avslut",
|
"sv_SE": "{0} - Avslut",
|
||||||
"th_TH": "{0} - ออก",
|
"th_TH": "{0} - ออก",
|
||||||
"tr_TR": "{0} - Çıkış",
|
"tr_TR": "{0} - Çıkış",
|
||||||
@@ -12288,7 +12288,7 @@
|
|||||||
"no_NO": "Alle ulagrede data vil gå tapt!",
|
"no_NO": "Alle ulagrede data vil gå tapt!",
|
||||||
"pl_PL": "Wszystkie niezapisane dane zostaną utracone!",
|
"pl_PL": "Wszystkie niezapisane dane zostaną utracone!",
|
||||||
"pt_BR": "Todos os dados que não foram salvos serão perdidos!",
|
"pt_BR": "Todos os dados que não foram salvos serão perdidos!",
|
||||||
"ru_RU": "Все несохранённые данные будут потеряны",
|
"ru_RU": "Все несохраненные данные будут потеряны",
|
||||||
"sv_SE": "Allt data som inte sparats kommer att förloras!",
|
"sv_SE": "Allt data som inte sparats kommer att förloras!",
|
||||||
"th_TH": "ข้อมูลทั้งหมดที่ไม่ได้บันทึกทั้งหมดจะสูญหาย!",
|
"th_TH": "ข้อมูลทั้งหมดที่ไม่ได้บันทึกทั้งหมดจะสูญหาย!",
|
||||||
"tr_TR": "Kaydedilmeyen bütün veriler kaybedilecek!",
|
"tr_TR": "Kaydedilmeyen bütün veriler kaybedilecek!",
|
||||||
@@ -13663,7 +13663,7 @@
|
|||||||
"no_NO": "Du er i ferd med å slette alle PPTC-data fra:\n\n{0}\n\n\nEr du sikker på at du vil fortsette?",
|
"no_NO": "Du er i ferd med å slette alle PPTC-data fra:\n\n{0}\n\n\nEr du sikker på at du vil fortsette?",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Você está prestes a limpar todos os dados PPTC de:\n\n{0}\n\nTem certeza de que deseja continuar?",
|
"pt_BR": "Você está prestes a limpar todos os dados PPTC de:\n\n{0}\n\nTem certeza de que deseja continuar?",
|
||||||
"ru_RU": "Вы собираетесь удалить все данные PPTC из:\n\n{0}\n\nВы уверены, что хотите продолжить?",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -14213,7 +14213,7 @@
|
|||||||
"no_NO": "Du har gjort endringer i denne brukerprofilen som ikke er lagret.",
|
"no_NO": "Du har gjort endringer i denne brukerprofilen som ikke er lagret.",
|
||||||
"pl_PL": "Wprowadziłeś zmiany dla tego profilu użytkownika, które nie zostały zapisane.",
|
"pl_PL": "Wprowadziłeś zmiany dla tego profilu użytkownika, które nie zostały zapisane.",
|
||||||
"pt_BR": "Você fez alterações para este perfil de usuário que não foram salvas.",
|
"pt_BR": "Você fez alterações para este perfil de usuário que não foram salvas.",
|
||||||
"ru_RU": "В эту учётную запись внесены изменения, которые не были сохранены.",
|
"ru_RU": "В эту учетную запись внесены изменения, которые не были сохранены.",
|
||||||
"sv_SE": "Du har gjort ändringar i denna användarprofil som inte har sparats.",
|
"sv_SE": "Du har gjort ändringar i denna användarprofil som inte har sparats.",
|
||||||
"th_TH": "คุณได้ทำการเปลี่ยนแปลงโปรไฟล์ผู้ใช้นี้โดยไม่ได้รับการบันทึก",
|
"th_TH": "คุณได้ทำการเปลี่ยนแปลงโปรไฟล์ผู้ใช้นี้โดยไม่ได้รับการบันทึก",
|
||||||
"tr_TR": "Kullanıcı profilinizde kaydedilmemiş değişiklikler var.",
|
"tr_TR": "Kullanıcı profilinizde kaydedilmemiş değişiklikler var.",
|
||||||
@@ -15763,7 +15763,7 @@
|
|||||||
"no_NO": "Velg om Ryujinx skal vises på din \"spillende\" Discord aktivitet eller ikke",
|
"no_NO": "Velg om Ryujinx skal vises på din \"spillende\" Discord aktivitet eller ikke",
|
||||||
"pl_PL": "Wybierz, czy chcesz wyświetlać Ryujinx w swojej \"aktualnie grane\" aktywności Discord",
|
"pl_PL": "Wybierz, czy chcesz wyświetlać Ryujinx w swojej \"aktualnie grane\" aktywności Discord",
|
||||||
"pt_BR": "Escolha se deseja mostrar Ryujinx ou não na sua atividade do Discord quando estiver usando-o",
|
"pt_BR": "Escolha se deseja mostrar Ryujinx ou não na sua atividade do Discord quando estiver usando-o",
|
||||||
"ru_RU": "Включает или отключает отображение статуса «Играет в игру» в Discord",
|
"ru_RU": "Включает или отключает отображение статуса \"Играет в игру\" в Discord",
|
||||||
"sv_SE": "Välj huruvida Ryujinx ska visas på din \"spelar för tillfället\" Discord-aktivitet",
|
"sv_SE": "Välj huruvida Ryujinx ska visas på din \"spelar för tillfället\" Discord-aktivitet",
|
||||||
"th_TH": "เลือกว่าจะแสดง Ryujinx ในกิจกรรม Discord \"ที่กำลังเล่นอยู่\" ของคุณหรือไม่?",
|
"th_TH": "เลือกว่าจะแสดง Ryujinx ในกิจกรรม Discord \"ที่กำลังเล่นอยู่\" ของคุณหรือไม่?",
|
||||||
"tr_TR": "Ryujinx'i \"şimdi oynanıyor\" Discord aktivitesinde göstermeyi veya göstermemeyi seçin",
|
"tr_TR": "Ryujinx'i \"şimdi oynanıyor\" Discord aktivitesinde göstermeyi veya göstermemeyi seçin",
|
||||||
@@ -16338,7 +16338,7 @@
|
|||||||
"no_NO": "Endre hvordan gjesteminne tilordnes og åpnes. Påvirker emulator CPU-ytelsen veldig mye.\n\nSett til HOST UNCHECKED hvis usikker.",
|
"no_NO": "Endre hvordan gjesteminne tilordnes og åpnes. Påvirker emulator CPU-ytelsen veldig mye.\n\nSett til HOST UNCHECKED hvis usikker.",
|
||||||
"pl_PL": "Zmień sposób mapowania i uzyskiwania dostępu do pamięci gości. Znacznie wpływa na wydajność emulowanego procesora.\n\nUstaw na HOST UNCHECKED, jeśli nie masz pewności.",
|
"pl_PL": "Zmień sposób mapowania i uzyskiwania dostępu do pamięci gości. Znacznie wpływa na wydajność emulowanego procesora.\n\nUstaw na HOST UNCHECKED, jeśli nie masz pewności.",
|
||||||
"pt_BR": "Altera como a memória do convidado é mapeada e acessada. Afeta muito o desempenho da CPU emulada.\n\nDefina como HÓSPEDE SEM VERIFICAÇÃO se não tiver certeza.",
|
"pt_BR": "Altera como a memória do convidado é mapeada e acessada. Afeta muito o desempenho da CPU emulada.\n\nDefina como HÓSPEDE SEM VERIFICAÇÃO se não tiver certeza.",
|
||||||
"ru_RU": "Меняет разметку и доступ к гостевой памяти. Значительно влияет на производительность процессора.\n\nРекомендуется оставить «Хост не установлен»",
|
"ru_RU": "Меняет разметку и доступ к гостевой памяти. Значительно влияет на производительность процессора.\n\nРекомендуется оставить \"Хост не установлен\"",
|
||||||
"sv_SE": "Ändra hur gästminne mappas och ges åtkomst till. Påverkar emulerad CPU-prestanda mycket.\n\nStäll in till \"Värd inte kontrollerad\" om du är osäker.",
|
"sv_SE": "Ändra hur gästminne mappas och ges åtkomst till. Påverkar emulerad CPU-prestanda mycket.\n\nStäll in till \"Värd inte kontrollerad\" om du är osäker.",
|
||||||
"th_TH": "เปลี่ยนวิธีการเข้าถึงหน่วยความจำของผู้เยี่ยมชม ส่งผลอย่างมากต่อประสิทธิภาพการทำงานของ CPU ที่จำลอง\n\nตั้งค่าเป็น ไม่ได้ตรวจสอบโฮสต์ หากคุณไม่แน่ใจ",
|
"th_TH": "เปลี่ยนวิธีการเข้าถึงหน่วยความจำของผู้เยี่ยมชม ส่งผลอย่างมากต่อประสิทธิภาพการทำงานของ CPU ที่จำลอง\n\nตั้งค่าเป็น ไม่ได้ตรวจสอบโฮสต์ หากคุณไม่แน่ใจ",
|
||||||
"tr_TR": "Guest hafızasının nasıl tahsis edilip erişildiğini değiştirir. Emüle edilen CPU performansını ciddi biçimde etkiler.\n\nEmin değilseniz HOST UNCHECKED seçeneğine ayarlayın.",
|
"tr_TR": "Guest hafızasının nasıl tahsis edilip erişildiğini değiştirir. Emüle edilen CPU performansını ciddi biçimde etkiler.\n\nEmin değilseniz HOST UNCHECKED seçeneğine ayarlayın.",
|
||||||
@@ -16513,7 +16513,7 @@
|
|||||||
"no_NO": "",
|
"no_NO": "",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "A caixa de diálogo do Applet do controlador não aparecerá se o controle for desconectado enquanto um aplicativo estiver em execução.\n\nDeixe a opção DESLIGADO se não tiver certeza.",
|
"pt_BR": "A caixa de diálogo do Applet do controlador não aparecerá se o controle for desconectado enquanto um aplicativo estiver em execução.\n\nDeixe a opção DESLIGADO se não tiver certeza.",
|
||||||
"ru_RU": "Диалоговое окно апплета контроллера не будет отображаться, если геймпад отключен во время работы приложения.\n\nОставьте выключенным, если не уверены.",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -16613,7 +16613,7 @@
|
|||||||
"no_NO": "Dobler spillets gjengivelse.\n\nNoen få spill fungerer kanskje ikke med dette aktivert og kan se veldig pikselert ut selv når gjengivelsen er økt; for de spillene, så kan det hende du må bruke modifikasjoner som fjerner anti-aliasing eller som øker den interne gjengivelsen. For og bruke sistnenvte, så vil du helst bruke \"Native\".\n\nHa til tanke at 4x er unødig for virituelt alle maskiner.",
|
"no_NO": "Dobler spillets gjengivelse.\n\nNoen få spill fungerer kanskje ikke med dette aktivert og kan se veldig pikselert ut selv når gjengivelsen er økt; for de spillene, så kan det hende du må bruke modifikasjoner som fjerner anti-aliasing eller som øker den interne gjengivelsen. For og bruke sistnenvte, så vil du helst bruke \"Native\".\n\nHa til tanke at 4x er unødig for virituelt alle maskiner.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Multiplica a resolução de renderização do jogo.\n\nAlguns jogos podem não funcionar bem com essa opção e apresentar uma aparência pixelada, mesmo com o aumento da resolução; para esses jogos, talvez seja necessário encontrar mods que removam o anti-aliasing ou aumentem a resolução de renderização interna. Ao usar a segunda opção, provavelmente desejará selecionar Nativa.\n\nEssa opção pode ser alterada enquanto um jogo está em execução, clicando em \"Aplicar\" abaixo; basta mover a janela de configurações para o lado e experimentar até encontrar o visual preferido para o jogo.\n\nLembre-se de que 4x é exagerado para praticamente qualquer configuração.",
|
"pt_BR": "Multiplica a resolução de renderização do jogo.\n\nAlguns jogos podem não funcionar bem com essa opção e apresentar uma aparência pixelada, mesmo com o aumento da resolução; para esses jogos, talvez seja necessário encontrar mods que removam o anti-aliasing ou aumentem a resolução de renderização interna. Ao usar a segunda opção, provavelmente desejará selecionar Nativa.\n\nEssa opção pode ser alterada enquanto um jogo está em execução, clicando em \"Aplicar\" abaixo; basta mover a janela de configurações para o lado e experimentar até encontrar o visual preferido para o jogo.\n\nLembre-se de que 4x é exagerado para praticamente qualquer configuração.",
|
||||||
"ru_RU": "Увеличивает разрешение рендера игры.\n\nНекоторые игры могут не работать с этой настройкой и выглядеть смазано даже когда разрешение увеличено. Для таких игр может потребоваться установка модов, которые убирают сглаживание или увеличивают разрешение рендеринга. \nДля использования последнего, вам нужно будет выбрать опцию «Нативное».\n\nЭта опция может быть изменена во время игры по нажатию кнопки «Применить» ниже. Вы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не подберете подходящие настройки для конкретной игры.\n\nИмейте в виду, что «4x» является излишеством.",
|
"ru_RU": "Увеличивает разрешение рендера игры.\n\nНекоторые игры могут не работать с этой настройкой и выглядеть смазано даже когда разрешение увеличено. Для таких игр может потребоваться установка модов, которые убирают сглаживание или увеличивают разрешение рендеринга. \nДля использования последнего, вам нужно будет выбрать опцию \"Нативное\".\n\nЭта опция может быть изменена во время игры по нажатию кнопки \"Применить\" ниже. Вы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не подберете подходящие настройки для конкретной игры.\n\nИмейте в виду, что \"4x\" является излишеством.",
|
||||||
"sv_SE": "Multiplicerar spelets renderingsupplösning.\n\nNågra spel kanske inte fungerar med detta och ser pixelerade ut även när upplösningen ökas; för dessa spel så kan du behöva hitta moddar som tar bort anti-aliasing eller som ökar deras interna renderingsupplösning. För att använda det senare, kommer du sannolikt vilja välja Inbyggd.\n\nDet här alternativet kan ändras medan ett spel körs genom att klicka på \"Tillämpa\" nedan. du kan helt enkelt flytta inställningsfönstret åt sidan och experimentera tills du hittar ditt föredragna utseende för ett spel.\n\nTänk på att 4x är overkill för praktiskt taget alla maskiner.",
|
"sv_SE": "Multiplicerar spelets renderingsupplösning.\n\nNågra spel kanske inte fungerar med detta och ser pixelerade ut även när upplösningen ökas; för dessa spel så kan du behöva hitta moddar som tar bort anti-aliasing eller som ökar deras interna renderingsupplösning. För att använda det senare, kommer du sannolikt vilja välja Inbyggd.\n\nDet här alternativet kan ändras medan ett spel körs genom att klicka på \"Tillämpa\" nedan. du kan helt enkelt flytta inställningsfönstret åt sidan och experimentera tills du hittar ditt föredragna utseende för ett spel.\n\nTänk på att 4x är overkill för praktiskt taget alla maskiner.",
|
||||||
"th_TH": "คูณความละเอียดการเรนเดอร์ของเกม\n\nเกมบางเกมอาจไม่สามารถใช้งานได้และดูเป็นพิกเซลแม้ว่าความละเอียดจะเพิ่มขึ้นก็ตาม สำหรับเกมเหล่านั้น คุณอาจต้องค้นหาม็อดที่ลบรอยหยักของภาพหรือเพิ่มความละเอียดในการเรนเดอร์ภายใน หากต้องการใช้อย่างหลัง คุณอาจต้องเลือก Native\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำมาใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม\n\nโปรดทราบว่า 4x นั้นเกินความจำเป็นสำหรับการตั้งค่าแทบทุกประเภท",
|
"th_TH": "คูณความละเอียดการเรนเดอร์ของเกม\n\nเกมบางเกมอาจไม่สามารถใช้งานได้และดูเป็นพิกเซลแม้ว่าความละเอียดจะเพิ่มขึ้นก็ตาม สำหรับเกมเหล่านั้น คุณอาจต้องค้นหาม็อดที่ลบรอยหยักของภาพหรือเพิ่มความละเอียดในการเรนเดอร์ภายใน หากต้องการใช้อย่างหลัง คุณอาจต้องเลือก Native\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำมาใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม\n\nโปรดทราบว่า 4x นั้นเกินความจำเป็นสำหรับการตั้งค่าแทบทุกประเภท",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -16988,7 +16988,7 @@
|
|||||||
"no_NO": "Skriver ut Avalonia (UI)-loggmeldinger i konsollen.",
|
"no_NO": "Skriver ut Avalonia (UI)-loggmeldinger i konsollen.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Imprime mensagens de log do Avalonia (UI) no console.",
|
"pt_BR": "Imprime mensagens de log do Avalonia (UI) no console.",
|
||||||
"ru_RU": "Выводит сообщения журнала Avalonia (интерфейс) в консоли.",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -17188,7 +17188,7 @@
|
|||||||
"no_NO": "",
|
"no_NO": "",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Abre a pasta de capturas de tela do Ryujinx",
|
"pt_BR": "Abre a pasta de capturas de tela do Ryujinx",
|
||||||
"ru_RU": "Открывает папку скриншотов Ryujinx",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -17213,7 +17213,7 @@
|
|||||||
"no_NO": "Åpner mappen hvor logger er lagret",
|
"no_NO": "Åpner mappen hvor logger er lagret",
|
||||||
"pl_PL": "Otwiera folder, w którym zapisywane są logi",
|
"pl_PL": "Otwiera folder, w którym zapisywane są logi",
|
||||||
"pt_BR": "Abre a pasta onde os logs são gravados",
|
"pt_BR": "Abre a pasta onde os logs são gravados",
|
||||||
"ru_RU": "Открывает папку, в которую записываются логи",
|
"ru_RU": "Открывает папку в которую записываются логи",
|
||||||
"sv_SE": "Öppnar mappen där loggarna har skrivits till",
|
"sv_SE": "Öppnar mappen där loggarna har skrivits till",
|
||||||
"th_TH": "เปิดโฟลเดอร์ ที่เก็บไฟล์ประวัติ",
|
"th_TH": "เปิดโฟลเดอร์ ที่เก็บไฟล์ประวัติ",
|
||||||
"tr_TR": "Log dosyalarının bulunduğu klasörü açar",
|
"tr_TR": "Log dosyalarının bulunduğu klasörü açar",
|
||||||
@@ -17288,7 +17288,7 @@
|
|||||||
"no_NO": "Åpne vindu for brukerprofiler",
|
"no_NO": "Åpne vindu for brukerprofiler",
|
||||||
"pl_PL": "Otwórz okno Menedżera Profili Użytkownika",
|
"pl_PL": "Otwórz okno Menedżera Profili Użytkownika",
|
||||||
"pt_BR": "Abrir Janela de Gerenciamento de Perfis",
|
"pt_BR": "Abrir Janela de Gerenciamento de Perfis",
|
||||||
"ru_RU": "Открыть менеджер учётных записей",
|
"ru_RU": "Открыть менеджер учетных записей",
|
||||||
"sv_SE": "Öppna hanterare för användarprofiler",
|
"sv_SE": "Öppna hanterare för användarprofiler",
|
||||||
"th_TH": "เปิดหน้าต่างตัวจัดการโปรไฟล์ผู้ใช้",
|
"th_TH": "เปิดหน้าต่างตัวจัดการโปรไฟล์ผู้ใช้",
|
||||||
"tr_TR": "Kullanıcı profil yöneticisi penceresini açar",
|
"tr_TR": "Kullanıcı profil yöneticisi penceresini açar",
|
||||||
@@ -17938,7 +17938,7 @@
|
|||||||
"no_NO": "Oppdatering tilgjengelig!",
|
"no_NO": "Oppdatering tilgjengelig!",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Atualização Disponível!",
|
"pt_BR": "Atualização Disponível!",
|
||||||
"ru_RU": "Доступно обновление!",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -19738,7 +19738,7 @@
|
|||||||
"no_NO": "LED-innstillinger",
|
"no_NO": "LED-innstillinger",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Configurações de LED",
|
"pt_BR": "Configurações de LED",
|
||||||
"ru_RU": "Настройки LED-подсветки",
|
"ru_RU": "",
|
||||||
"sv_SE": "LED-inställningar",
|
"sv_SE": "LED-inställningar",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -20338,7 +20338,7 @@
|
|||||||
"no_NO": "Bruker Profiler Behandler",
|
"no_NO": "Bruker Profiler Behandler",
|
||||||
"pl_PL": "Menedżer Profili Użytkowników",
|
"pl_PL": "Menedżer Profili Użytkowników",
|
||||||
"pt_BR": "Gerenciador de Perfis de Usuário",
|
"pt_BR": "Gerenciador de Perfis de Usuário",
|
||||||
"ru_RU": "Менеджер учётных записей",
|
"ru_RU": "Менеджер учетных записей",
|
||||||
"sv_SE": "Hanterare för användarprofiler",
|
"sv_SE": "Hanterare för användarprofiler",
|
||||||
"th_TH": "จัดการโปรไฟล์ผู้ใช้",
|
"th_TH": "จัดการโปรไฟล์ผู้ใช้",
|
||||||
"tr_TR": "Kullanıcı Profillerini Yönet",
|
"tr_TR": "Kullanıcı Profillerini Yönet",
|
||||||
@@ -21663,7 +21663,7 @@
|
|||||||
"no_NO": "Velg grafikkkortet som skal brukes sammen med Vulkan grafikkbackenden\n\nPåvirker ikke GPU-er som OpenGL skal bruke.\n\nSett til GPU-merket som \"dGPU\" hvis usikker. Hvis det ikke det er en, la være uberørt.",
|
"no_NO": "Velg grafikkkortet som skal brukes sammen med Vulkan grafikkbackenden\n\nPåvirker ikke GPU-er som OpenGL skal bruke.\n\nSett til GPU-merket som \"dGPU\" hvis usikker. Hvis det ikke det er en, la være uberørt.",
|
||||||
"pl_PL": "Wybierz kartę graficzną, która będzie używana z backendem graficznym Vulkan.\n\nNie wpływa na GPU używane przez OpenGL.\n\nW razie wątpliwości ustaw flagę GPU jako \"dGPU\". Jeśli żadnej nie ma, pozostaw nietknięte.",
|
"pl_PL": "Wybierz kartę graficzną, która będzie używana z backendem graficznym Vulkan.\n\nNie wpływa na GPU używane przez OpenGL.\n\nW razie wątpliwości ustaw flagę GPU jako \"dGPU\". Jeśli żadnej nie ma, pozostaw nietknięte.",
|
||||||
"pt_BR": "Selecione a placa de vídeo que será usada com o renderizador gráfico Vulkan.\n\nNão afeta a GPU que OpenGL usará.\n\nSelecione \"dGPU\" em caso de dúvida. Se não houver nenhuma, não mexa.",
|
"pt_BR": "Selecione a placa de vídeo que será usada com o renderizador gráfico Vulkan.\n\nNão afeta a GPU que OpenGL usará.\n\nSelecione \"dGPU\" em caso de dúvida. Se não houver nenhuma, não mexa.",
|
||||||
"ru_RU": "Выберает видеоадаптер, который будет использоваться графическим бэкендом Vulkan.\n\nЭта настройка не влияет на видеоадаптер, который будет использоваться с OpenGL.\n\nЕсли вы не уверены что нужно выбрать, используйте графический процессор, помеченный как «dGPU». Если его нет, оставьте выбор по умолчанию.",
|
"ru_RU": "Выберает видеоадаптер, который будет использоваться графическим бэкендом Vulkan.\n\nЭта настройка не влияет на видеоадаптер, который будет использоваться с OpenGL.\n\nЕсли вы не уверены что нужно выбрать, используйте графический процессор, помеченный как \"dGPU\". Если его нет, оставьте выбор по умолчанию.",
|
||||||
"sv_SE": "Välj grafikkortet som ska användas med Vulkan-grafikbakänden.\n\nPåverkar inte GPU:n som OpenGL använder.\n\nStäll in till den GPU som flaggats som \"dGPU\" om osäker. Om det inte finns någon, lämna orörd.",
|
"sv_SE": "Välj grafikkortet som ska användas med Vulkan-grafikbakänden.\n\nPåverkar inte GPU:n som OpenGL använder.\n\nStäll in till den GPU som flaggats som \"dGPU\" om osäker. Om det inte finns någon, lämna orörd.",
|
||||||
"th_TH": "เลือกการ์ดจอที่จะใช้กับแบ็กเอนด์กราฟิก Vulkan\n\nไม่ส่งผลต่อ GPU ที่ OpenGL จะใช้\n\nตั้งค่าเป็น GPU ที่ถูกตั้งค่าสถานะเป็น \"dGPU\" ถ้าหากคุณไม่แน่ใจ ,หากไม่มีก็ปล่อยทิ้งไว้โดยไม่ต้องแตะต้องมัน",
|
"th_TH": "เลือกการ์ดจอที่จะใช้กับแบ็กเอนด์กราฟิก Vulkan\n\nไม่ส่งผลต่อ GPU ที่ OpenGL จะใช้\n\nตั้งค่าเป็น GPU ที่ถูกตั้งค่าสถานะเป็น \"dGPU\" ถ้าหากคุณไม่แน่ใจ ,หากไม่มีก็ปล่อยทิ้งไว้โดยไม่ต้องแตะต้องมัน",
|
||||||
"tr_TR": "Vulkan Grafik Arka Ucu ile kullanılacak Ekran Kartını Seçin.\n\nOpenGL'nin kullanacağı GPU'yu etkilemez.\n\n Emin değilseniz \"dGPU\" olarak işaretlenmiş GPU'ya ayarlayın. Eğer yoksa, dokunmadan bırakın.\n",
|
"tr_TR": "Vulkan Grafik Arka Ucu ile kullanılacak Ekran Kartını Seçin.\n\nOpenGL'nin kullanacağı GPU'yu etkilemez.\n\n Emin değilseniz \"dGPU\" olarak işaretlenmiş GPU'ya ayarlayın. Eğer yoksa, dokunmadan bırakın.\n",
|
||||||
@@ -22163,7 +22163,7 @@
|
|||||||
"no_NO": "Gjenopprett tapte kontoer",
|
"no_NO": "Gjenopprett tapte kontoer",
|
||||||
"pl_PL": "Odzyskaj Utracone Konta",
|
"pl_PL": "Odzyskaj Utracone Konta",
|
||||||
"pt_BR": "Recuperar Contas Perdidas",
|
"pt_BR": "Recuperar Contas Perdidas",
|
||||||
"ru_RU": "Восстановить учётные записи",
|
"ru_RU": "Восстановить учетные записи",
|
||||||
"sv_SE": "Återskapa förlorade konton",
|
"sv_SE": "Återskapa förlorade konton",
|
||||||
"th_TH": "กู้คืนบัญชีที่สูญหาย",
|
"th_TH": "กู้คืนบัญชีที่สูญหาย",
|
||||||
"tr_TR": "Kayıp Hesapları Kurtar",
|
"tr_TR": "Kayıp Hesapları Kurtar",
|
||||||
@@ -22238,7 +22238,7 @@
|
|||||||
"no_NO": "Ingen profiler å gjenopprette",
|
"no_NO": "Ingen profiler å gjenopprette",
|
||||||
"pl_PL": "Brak profili do odzyskania",
|
"pl_PL": "Brak profili do odzyskania",
|
||||||
"pt_BR": "Nenhum perfil para recuperar",
|
"pt_BR": "Nenhum perfil para recuperar",
|
||||||
"ru_RU": "Нет учётных записей для восстановления",
|
"ru_RU": "Нет учетных записей для восстановления",
|
||||||
"sv_SE": "Inga profiler att återskapa",
|
"sv_SE": "Inga profiler att återskapa",
|
||||||
"th_TH": "ไม่มีโปรไฟล์ที่สามารถกู้คืนได้",
|
"th_TH": "ไม่มีโปรไฟล์ที่สามารถกู้คืนได้",
|
||||||
"tr_TR": "Kurtarılacak profil bulunamadı",
|
"tr_TR": "Kurtarılacak profil bulunamadı",
|
||||||
@@ -22263,7 +22263,7 @@
|
|||||||
"no_NO": "Aktiverer anti-aliasing til spill render.\n\nFXAA vil gjøre det meste av bildet, mens SMAA vil forsøke å finne berørte kanter og glatte dem ut.\n\nAnbefales ikke til bruk i forbindelse med FSR-skaleringsfilteret.\n\nDette valget kan endres mens et spill kjører ved å klikke \"Apply\" nedenfor; du kan bare flytte innstillingsvinduet til du finner det foretrukne utseendet til et spill.\n\nForlat på NONE hvis usikker.",
|
"no_NO": "Aktiverer anti-aliasing til spill render.\n\nFXAA vil gjøre det meste av bildet, mens SMAA vil forsøke å finne berørte kanter og glatte dem ut.\n\nAnbefales ikke til bruk i forbindelse med FSR-skaleringsfilteret.\n\nDette valget kan endres mens et spill kjører ved å klikke \"Apply\" nedenfor; du kan bare flytte innstillingsvinduet til du finner det foretrukne utseendet til et spill.\n\nForlat på NONE hvis usikker.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Aplica anti-aliasing à renderização do jogo.\n\nFXAA borrará a maior parte da imagem, enquanto SMAA tentará identificar e suavizar bordas serrilhadas.\n\nNão é recomendado usar em conjunto com o filtro de escala FSR.\n\nEssa opção pode ser alterada enquanto o jogo está em execução clicando em \"Aplicar\" abaixo; basta mover a janela de configurações para o lado e experimentar até encontrar o visual preferido para o jogo.\n\nDeixe em NENHUM se estiver em dúvida.",
|
"pt_BR": "Aplica anti-aliasing à renderização do jogo.\n\nFXAA borrará a maior parte da imagem, enquanto SMAA tentará identificar e suavizar bordas serrilhadas.\n\nNão é recomendado usar em conjunto com o filtro de escala FSR.\n\nEssa opção pode ser alterada enquanto o jogo está em execução clicando em \"Aplicar\" abaixo; basta mover a janela de configurações para o lado e experimentar até encontrar o visual preferido para o jogo.\n\nDeixe em NENHUM se estiver em dúvida.",
|
||||||
"ru_RU": "Применимое сглаживание для рендера.\n\nFXAA размывает большую часть изображения, SMAA попытается найти «зазубренные» края и сгладить их.\n\nНе рекомендуется использовать вместе с масштабирующим фильтром FSR.\n\nЭта опция может быть изменена во время игры по нажатию «Применить» ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не найдёте подходящую настройку игры.\n\nРекомендуется использовать «Нет».",
|
"ru_RU": "Применимое сглаживание для рендера.\n\nFXAA размывает большую часть изображения, SMAA попытается найти \"зазубренные\" края и сгладить их.\n\nНе рекомендуется использовать вместе с масштабирующим фильтром FSR.\n\nЭта опция может быть изменена во время игры по нажатию \"Применить\" ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не найдёте подходящую настройку игры.\n\nРекомендуется использовать \"Нет\".",
|
||||||
"sv_SE": "Tillämpar anti-aliasing på spelrenderaren.\n\nFXAA kommer att sudda det mesta av bilden, medan SMAA kommer att försöka hitta taggiga kanter och släta ut dem.\n\nRekommenderas inte att använda tillsammans med skalfiltret FSR.\n\nDet här alternativet kan ändras medan ett spel körs genom att klicka på \"Tillämpa\" nedan. Du kan helt enkelt flytta inställningsfönstret åt sidan och experimentera tills du hittar ditt föredragna utseende för ett spel.\n\nLämna som INGEN om du är osäker.",
|
"sv_SE": "Tillämpar anti-aliasing på spelrenderaren.\n\nFXAA kommer att sudda det mesta av bilden, medan SMAA kommer att försöka hitta taggiga kanter och släta ut dem.\n\nRekommenderas inte att använda tillsammans med skalfiltret FSR.\n\nDet här alternativet kan ändras medan ett spel körs genom att klicka på \"Tillämpa\" nedan. Du kan helt enkelt flytta inställningsfönstret åt sidan och experimentera tills du hittar ditt föredragna utseende för ett spel.\n\nLämna som INGEN om du är osäker.",
|
||||||
"th_TH": "ใช้การลดรอยหยักกับการเรนเดอร์เกม\n\nFXAA จะเบลอภาพส่วนใหญ่ ในขณะที่ SMAA จะพยายามค้นหารอยหยักและปรับให้เรียบ\n\nไม่แนะนำให้ใช้ร่วมกับตัวกรองสเกล FSR\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำไปใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม\n\nปล่อยไว้ที่ NONE หากไม่แน่ใจ",
|
"th_TH": "ใช้การลดรอยหยักกับการเรนเดอร์เกม\n\nFXAA จะเบลอภาพส่วนใหญ่ ในขณะที่ SMAA จะพยายามค้นหารอยหยักและปรับให้เรียบ\n\nไม่แนะนำให้ใช้ร่วมกับตัวกรองสเกล FSR\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำไปใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม\n\nปล่อยไว้ที่ NONE หากไม่แน่ใจ",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -22338,7 +22338,7 @@
|
|||||||
"no_NO": "Velg det skaleringsfilteret som skal brukes når du bruker oppløsningsskalaen.\n\nBilinear fungerer godt for 3D-spill og er et trygt standardalternativ.\n\nNærmeste anbefales for pixel kunst-spill.\n\nFSR 1.0 er bare et skarpere filter, ikke anbefalt for bruk med FXAA eller SMAA.\n\nOmrådeskalering anbefales når nedskalering er større enn utgangsvinduet. Den kan brukes til å oppnå en superprøvetaket anti-aliasingseffekt når en nedskalerer med mer enn 2x.\n\nDette valget kan endres mens et spill kjører ved å klikke \"Apply\" nedenfor; du kan bare flytte innstillingsvinduet til du finner det foretrukne utseendet til et spill.\n\nLa være på BILINEAR hvis usikker.",
|
"no_NO": "Velg det skaleringsfilteret som skal brukes når du bruker oppløsningsskalaen.\n\nBilinear fungerer godt for 3D-spill og er et trygt standardalternativ.\n\nNærmeste anbefales for pixel kunst-spill.\n\nFSR 1.0 er bare et skarpere filter, ikke anbefalt for bruk med FXAA eller SMAA.\n\nOmrådeskalering anbefales når nedskalering er større enn utgangsvinduet. Den kan brukes til å oppnå en superprøvetaket anti-aliasingseffekt når en nedskalerer med mer enn 2x.\n\nDette valget kan endres mens et spill kjører ved å klikke \"Apply\" nedenfor; du kan bare flytte innstillingsvinduet til du finner det foretrukne utseendet til et spill.\n\nLa være på BILINEAR hvis usikker.",
|
||||||
"pl_PL": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.",
|
"pl_PL": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.",
|
||||||
"pt_BR": "Escolha o filtro de escala que será aplicado ao usar a escala de resolução.\n\nBilinear funciona bem para jogos 3D e é uma opção padrão segura.\n\nNearest é recomendado para jogos em pixel art.\n\nFSR 1.0 é apenas um filtro de nitidez, não recomendado para uso com FXAA ou SMAA.\n\nEssa opção pode ser alterada enquanto o jogo está em execução, clicando em \"Aplicar\" abaixo; basta mover a janela de configurações para o lado e experimentar até encontrar o visual preferido para o jogo.\n\nMantenha em BILINEAR se estiver em dúvida.",
|
"pt_BR": "Escolha o filtro de escala que será aplicado ao usar a escala de resolução.\n\nBilinear funciona bem para jogos 3D e é uma opção padrão segura.\n\nNearest é recomendado para jogos em pixel art.\n\nFSR 1.0 é apenas um filtro de nitidez, não recomendado para uso com FXAA ou SMAA.\n\nEssa opção pode ser alterada enquanto o jogo está em execução, clicando em \"Aplicar\" abaixo; basta mover a janela de configurações para o lado e experimentar até encontrar o visual preferido para o jogo.\n\nMantenha em BILINEAR se estiver em dúvida.",
|
||||||
"ru_RU": "Фильтрация текстур, которая будет применяться при масштабировании.\n\nБилинейная хорошо работает для 3D-игр и является настройкой по умолчанию.\n\nСтупенчатая рекомендуется для пиксельных игр.\n\nFSR это фильтр резкости, который не рекомендуется использовать с FXAA или SMAA.\n\nЗональная рекомендуется в случае использования разрешения больше разрешения окна. Можно использовать для достижения эффекта суперсемплига (SSAA) при даунскейле более чем в 2 раза.\n\nЭта опция может быть изменена во время игры по нажатию кнопки «Применить» ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не подберете подходящие настройки для конкретной игры.\n\nРекомендуется использовать «Билинейная».",
|
"ru_RU": "Фильтрация текстур, которая будет применяться при масштабировании.\n\nБилинейная хорошо работает для 3D-игр и является настройкой по умолчанию.\n\nСтупенчатая рекомендуется для пиксельных игр.\n\nFSR это фильтр резкости, который не рекомендуется использовать с FXAA или SMAA.\n\nЭта опция может быть изменена во время игры по нажатию кнопки \"Применить\" ниже; \nВы можете просто переместить окно настроек в сторону и поэкспериментировать, пока не подберете подходящие настройки для конкретной игры.\n\nРекомендуется использовать \"Билинейная\".",
|
||||||
"sv_SE": "Välj det skalfilter som ska tillämpas vid användning av upplösningsskala.\n\nBilinjär fungerar bra för 3D-spel och är ett säkert standardalternativ.\n\nNärmast rekommenderas för pixel art-spel.\n\nFSR 1.0 är bara ett skarpningsfilter, rekommenderas inte för FXAA eller SMAA.\n\nOmrådesskalning rekommenderas vid nedskalning av upplösning som är större än utdatafönstret. Det kan användas för att uppnå en supersamplad anti-alias-effekt vid nedskalning med mer än 2x.\n\nDetta alternativ kan ändras medan ett spel körs genom att klicka på \"Tillämpa\" nedan. du kan helt enkelt flytta inställningsfönstret åt sidan och experimentera tills du hittar ditt föredragna utseende för ett spel.\n\nLämna som BILINJÄR om du är osäker.",
|
"sv_SE": "Välj det skalfilter som ska tillämpas vid användning av upplösningsskala.\n\nBilinjär fungerar bra för 3D-spel och är ett säkert standardalternativ.\n\nNärmast rekommenderas för pixel art-spel.\n\nFSR 1.0 är bara ett skarpningsfilter, rekommenderas inte för FXAA eller SMAA.\n\nOmrådesskalning rekommenderas vid nedskalning av upplösning som är större än utdatafönstret. Det kan användas för att uppnå en supersamplad anti-alias-effekt vid nedskalning med mer än 2x.\n\nDetta alternativ kan ändras medan ett spel körs genom att klicka på \"Tillämpa\" nedan. du kan helt enkelt flytta inställningsfönstret åt sidan och experimentera tills du hittar ditt föredragna utseende för ett spel.\n\nLämna som BILINJÄR om du är osäker.",
|
||||||
"th_TH": "เลือกตัวกรองสเกลที่จะใช้เมื่อใช้สเกลความละเอียด\n\nBilinear ทำงานได้ดีกับเกม 3D และเป็นตัวเลือกเริ่มต้นที่ปลอดภัย\n\nแนะนำให้ใช้เกมภาพพิกเซลที่ใกล้เคียงที่สุด\n\nFSR 1.0 เป็นเพียงตัวกรองความคมชัด ไม่แนะนำให้ใช้กับ FXAA หรือ SMAA\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำไปใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม",
|
"th_TH": "เลือกตัวกรองสเกลที่จะใช้เมื่อใช้สเกลความละเอียด\n\nBilinear ทำงานได้ดีกับเกม 3D และเป็นตัวเลือกเริ่มต้นที่ปลอดภัย\n\nแนะนำให้ใช้เกมภาพพิกเซลที่ใกล้เคียงที่สุด\n\nFSR 1.0 เป็นเพียงตัวกรองความคมชัด ไม่แนะนำให้ใช้กับ FXAA หรือ SMAA\n\nตัวเลือกนี้สามารถเปลี่ยนแปลงได้ในขณะที่เกมกำลังทำงานอยู่โดยคลิก \"นำไปใช้\" ด้านล่าง คุณสามารถย้ายหน้าต่างการตั้งค่าไปด้านข้างและทดลองจนกว่าคุณจะพบรูปลักษณ์ที่คุณต้องการสำหรับเกม",
|
||||||
"tr_TR": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.",
|
"tr_TR": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.",
|
||||||
@@ -22488,7 +22488,7 @@
|
|||||||
"no_NO": "Definer FSR 1,0 skarpere nivå. Høyere er skarpere.",
|
"no_NO": "Definer FSR 1,0 skarpere nivå. Høyere er skarpere.",
|
||||||
"pl_PL": "Ustaw poziom ostrzeżenia FSR 1.0. Wyższy jest ostrzejszy.",
|
"pl_PL": "Ustaw poziom ostrzeżenia FSR 1.0. Wyższy jest ostrzejszy.",
|
||||||
"pt_BR": "Defina o nível de nitidez do FSR 1.0. Quanto maior, mais nítido.",
|
"pt_BR": "Defina o nível de nitidez do FSR 1.0. Quanto maior, mais nítido.",
|
||||||
"ru_RU": "Выбор режима работы FSR 1.0. Выше — четче.",
|
"ru_RU": "Выбор режима работы FSR 1.0. Выше - четче.",
|
||||||
"sv_SE": "Ställ in nivå för FSR 1.0 sharpening. Högre är skarpare.",
|
"sv_SE": "Ställ in nivå för FSR 1.0 sharpening. Högre är skarpare.",
|
||||||
"th_TH": "ตั้งค่าระดับความคมชัด FSR 1.0 ยิ่งสูงกว่าจะยิ่งคมชัดกว่า",
|
"th_TH": "ตั้งค่าระดับความคมชัด FSR 1.0 ยิ่งสูงกว่าจะยิ่งคมชัดกว่า",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -22688,7 +22688,7 @@
|
|||||||
"no_NO": "Nettverksgrensesnittets grensesnitt brukt for LAN/LDN funksjoner.\n\ni konjuksjon med en VPN eller XLink Kai og ett spill med LAN støtte, kan bli brukt til og spoofe ett \"samme-nettverk\" tilkobling over nettet.\n\nLa være på DEFAULT om usikker.",
|
"no_NO": "Nettverksgrensesnittets grensesnitt brukt for LAN/LDN funksjoner.\n\ni konjuksjon med en VPN eller XLink Kai og ett spill med LAN støtte, kan bli brukt til og spoofe ett \"samme-nettverk\" tilkobling over nettet.\n\nLa være på DEFAULT om usikker.",
|
||||||
"pl_PL": "Interfejs sieciowy używany dla funkcji LAN/LDN.\n\nw połączeniu z VPN lub XLink Kai i grą z obsługą sieci LAN, może być użyty do spoofowania połączenia z tą samą siecią przez Internet.\n\nZostaw DOMYŚLNE, jeśli nie ma pewności.",
|
"pl_PL": "Interfejs sieciowy używany dla funkcji LAN/LDN.\n\nw połączeniu z VPN lub XLink Kai i grą z obsługą sieci LAN, może być użyty do spoofowania połączenia z tą samą siecią przez Internet.\n\nZostaw DOMYŚLNE, jeśli nie ma pewności.",
|
||||||
"pt_BR": "A interface de rede usada para recursos de LAN/LDN.\n\nEm conjunto com uma VPN ou XLink Kai e um jogo com suporte a LAN, pode ser usada para simular uma conexão na mesma rede pela Internet.\n\nMantenha em PADRÃO se estiver em dúvida.",
|
"pt_BR": "A interface de rede usada para recursos de LAN/LDN.\n\nEm conjunto com uma VPN ou XLink Kai e um jogo com suporte a LAN, pode ser usada para simular uma conexão na mesma rede pela Internet.\n\nMantenha em PADRÃO se estiver em dúvida.",
|
||||||
"ru_RU": "Сетевой интерфейс, используемый для функций LAN/LDN.\n\nМожет использоваться для игры через интернет в сочетании с VPN или XLink Kai и игрой с поддержкой LAN.\n\nРекомендуется использовать «По умолчанию».",
|
"ru_RU": "Сетевой интерфейс, используемый для функций LAN/LDN.\n\nМожет использоваться для игры через интернет в сочетании с VPN или XLink Kai и игрой с поддержкой LAN.\n\nРекомендуется использовать \"По умолчанию\".",
|
||||||
"sv_SE": "Nätverksgränssnittet som används för LAN/LDN-funktioner.\n\nTillsammans med en VPN eller XLink Kai och ett spel med LAN-stöd så kan detta användas för att spoofa en same-network-anslutning över internet.\n\nLämna som STANDARD om du är osäker.",
|
"sv_SE": "Nätverksgränssnittet som används för LAN/LDN-funktioner.\n\nTillsammans med en VPN eller XLink Kai och ett spel med LAN-stöd så kan detta användas för att spoofa en same-network-anslutning över internet.\n\nLämna som STANDARD om du är osäker.",
|
||||||
"th_TH": "อินเทอร์เฟซเครือข่ายที่ใช้สำหรับคุณสมบัติ LAN/LDN\n\nเมื่อใช้ร่วมกับ VPN หรือ XLink Kai และเกมที่รองรับ LAN สามารถใช้เพื่อปลอมการเชื่อมต่อเครือข่ายเดียวกันผ่านทางอินเทอร์เน็ต\n\nปล่อยให้เป็น ค่าเริ่มต้น หากคุณไม่แน่ใจ",
|
"th_TH": "อินเทอร์เฟซเครือข่ายที่ใช้สำหรับคุณสมบัติ LAN/LDN\n\nเมื่อใช้ร่วมกับ VPN หรือ XLink Kai และเกมที่รองรับ LAN สามารถใช้เพื่อปลอมการเชื่อมต่อเครือข่ายเดียวกันผ่านทางอินเทอร์เน็ต\n\nปล่อยให้เป็น ค่าเริ่มต้น หากคุณไม่แน่ใจ",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -22863,7 +22863,7 @@
|
|||||||
"no_NO": "Endre LDN flerspillermodus.\n\nLdnMitm vil endre lokal trådløst/lokal spillfunksjonalitet i spill som skal fungere som om den var LAN, noe som tillater lokal, samme nettverk forbindelser med andre Ryujinx instanser og hacket Nintendo Switch konsoller som har installert ldn_mitm-modulen.\n\nFlerspiller krever at alle spillerne er på samme versjon (dvs. Super Smash Bros. Ultimat v13.0.1 kan ikke koble til v13.0.0).\n\nForlat DEAKTIVERT hvis usikker.",
|
"no_NO": "Endre LDN flerspillermodus.\n\nLdnMitm vil endre lokal trådløst/lokal spillfunksjonalitet i spill som skal fungere som om den var LAN, noe som tillater lokal, samme nettverk forbindelser med andre Ryujinx instanser og hacket Nintendo Switch konsoller som har installert ldn_mitm-modulen.\n\nFlerspiller krever at alle spillerne er på samme versjon (dvs. Super Smash Bros. Ultimat v13.0.1 kan ikke koble til v13.0.0).\n\nForlat DEAKTIVERT hvis usikker.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Alterar o modo multiplayer LDN.\n\nLdnMitm modificará a funcionalidade de jogo sem fio/local nos jogos para funcionar como se fosse LAN, permitindo conexões locais, na mesma rede, com outras instâncias do Ryujinx e consoles Nintendo Switch hackeados que possuem o módulo ldn_mitm instalado.\n\nO multiplayer exige que todos os jogadores estejam na mesma versão do jogo (ex.: Super Smash Bros. Ultimate v13.0.1 não consegue se conectar à v13.0.0).\n\nDeixe DESATIVADO se estiver em dúvida.",
|
"pt_BR": "Alterar o modo multiplayer LDN.\n\nLdnMitm modificará a funcionalidade de jogo sem fio/local nos jogos para funcionar como se fosse LAN, permitindo conexões locais, na mesma rede, com outras instâncias do Ryujinx e consoles Nintendo Switch hackeados que possuem o módulo ldn_mitm instalado.\n\nO multiplayer exige que todos os jogadores estejam na mesma versão do jogo (ex.: Super Smash Bros. Ultimate v13.0.1 não consegue se conectar à v13.0.0).\n\nDeixe DESATIVADO se estiver em dúvida.",
|
||||||
"ru_RU": "Меняет многопользовательский режим LDN.\n\nLdnMitm модифицирует функциональность локальной беспроводной/игры на одном устройстве в играх, позволяя играть с другими пользователями Ryujinx или взломанными консолями Nintendo Switch с установленным модулем ldn_mitm, находящимися в одной локальной сети друг с другом.\n\nМногопользовательская игра требует наличия у всех игроков одной и той же версии игры (т.е. Super Smash Bros. Ultimate v13.0.1 не может подключиться к v13.0.0).\n\nРекомендуется оставить выключенным.",
|
"ru_RU": "Меняет многопользовательский режим LDN.\n\nLdnMitm модифицирует функциональность локальной беспроводной/игры на одном устройстве в играх, позволяя играть с другими пользователями Ryujinx или взломанными консолями Nintendo Switch с установленным модулем ldn_mitm, находящимися в одной локальной сети друг с другом.\n\nМногопользовательская игра требует наличия у всех игроков одной и той же версии игры (т.е. Super Smash Bros. Ultimate v13.0.1 не может подключиться к v13.0.0).\n\nРекомендуется оставить отключенным.",
|
||||||
"sv_SE": "Ändra LDN-flerspelarläge\n\nLdnMitm kommer att ändra lokal funktionalitet för trådlös/lokalt spel att fungera som om det vore ett LAN, vilket ger stöd för anslutningar med local och same-network med andra Ryujinx-instanser och hackade Nintendo Switch-konsoller som har modulen ldn_mitm installerad.\n\nFlerspelare kräver att alla spelare har samma spelversion (t.ex. Super Smash Bros. Ultimate v13.0.1 kan inte ansluta till v13.0.0).\n\nLämna INAKTIVERAD om du är osäker.",
|
"sv_SE": "Ändra LDN-flerspelarläge\n\nLdnMitm kommer att ändra lokal funktionalitet för trådlös/lokalt spel att fungera som om det vore ett LAN, vilket ger stöd för anslutningar med local och same-network med andra Ryujinx-instanser och hackade Nintendo Switch-konsoller som har modulen ldn_mitm installerad.\n\nFlerspelare kräver att alla spelare har samma spelversion (t.ex. Super Smash Bros. Ultimate v13.0.1 kan inte ansluta till v13.0.0).\n\nLämna INAKTIVERAD om du är osäker.",
|
||||||
"th_TH": "เปลี่ยนโหมดผู้เล่นหลายคนของ LDN\n\nLdnMitm จะปรับเปลี่ยนฟังก์ชันการเล่นแบบไร้สาย/ภายใน จะให้เกมทำงานเหมือนกับว่าเป็น LAN ช่วยให้สามารถเชื่อมต่อภายในเครือข่ายเดียวกันกับอินสแตนซ์ Ryujinx อื่น ๆ และคอนโซล Nintendo Switch ที่ถูกแฮ็กซึ่งมีโมดูล ldn_mitm ติดตั้งอยู่\n\nผู้เล่นหลายคนต้องการให้ผู้เล่นทุกคนอยู่ในเกมเวอร์ชันเดียวกัน (เช่น Super Smash Bros. Ultimate v13.0.1 ไม่สามารถเชื่อมต่อกับ v13.0.0)\n\nปล่อยให้ปิดการใช้งานหากไม่แน่ใจ",
|
"th_TH": "เปลี่ยนโหมดผู้เล่นหลายคนของ LDN\n\nLdnMitm จะปรับเปลี่ยนฟังก์ชันการเล่นแบบไร้สาย/ภายใน จะให้เกมทำงานเหมือนกับว่าเป็น LAN ช่วยให้สามารถเชื่อมต่อภายในเครือข่ายเดียวกันกับอินสแตนซ์ Ryujinx อื่น ๆ และคอนโซล Nintendo Switch ที่ถูกแฮ็กซึ่งมีโมดูล ldn_mitm ติดตั้งอยู่\n\nผู้เล่นหลายคนต้องการให้ผู้เล่นทุกคนอยู่ในเกมเวอร์ชันเดียวกัน (เช่น Super Smash Bros. Ultimate v13.0.1 ไม่สามารถเชื่อมต่อกับ v13.0.0)\n\nปล่อยให้ปิดการใช้งานหากไม่แน่ใจ",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23213,7 +23213,7 @@
|
|||||||
"no_NO": "Ugyldig passordfrase! Må være i formatet \"Ryujinx-<8 hex tegn>\"",
|
"no_NO": "Ugyldig passordfrase! Må være i formatet \"Ryujinx-<8 hex tegn>\"",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Frase-senha inválida! Deve estar no formato \"Ryujinx-<8 hex chars>\"",
|
"pt_BR": "Frase-senha inválida! Deve estar no formato \"Ryujinx-<8 hex chars>\"",
|
||||||
"ru_RU": "Неверный пароль! Пароль должен быть в формате «Ryujinx-<8 шестнадцатеричных символов>»",
|
"ru_RU": "Неверный пароль! Пароль должен быть в формате \"Ryujinx-<8 шестнадцатеричных символов>\"",
|
||||||
"sv_SE": "Ogiltig lösenfras! Måste vara i formatet \"Ryujinx-<8 hextecken>\"",
|
"sv_SE": "Ogiltig lösenfras! Måste vara i formatet \"Ryujinx-<8 hextecken>\"",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23363,7 +23363,7 @@
|
|||||||
"no_NO": "Emulert vertikal synkronisering. «Switch» emulerer Switchs oppdateringsfrekvens på 60 Hz. «Ubegrenset» er en ubegrenset oppdateringsfrekvens.",
|
"no_NO": "Emulert vertikal synkronisering. «Switch» emulerer Switchs oppdateringsfrekvens på 60 Hz. «Ubegrenset» er en ubegrenset oppdateringsfrekvens.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Sincronização vertical emulada. 'Switch' emula a taxa de atualização de 60 Hz do Switch. 'Ilimitada' é uma taxa de atualização sem limite.",
|
"pt_BR": "Sincronização vertical emulada. 'Switch' emula a taxa de atualização de 60 Hz do Switch. 'Ilimitada' é uma taxa de atualização sem limite.",
|
||||||
"ru_RU": "Эмулированная вертикальная синхронизация. «Консоль» эмулирует частоту обновления консоли, равную 60 Гц. «Без ограничений» — неограниченная частота кадров.",
|
"ru_RU": "Эмулированная вертикальная синхронизация. 'Консоль' эмулирует частоту обновления консоли, равную 60 Гц. 'Без ограничений' - неограниченная частота кадров.",
|
||||||
"sv_SE": "Emulerad vertikal synk. 'Switch' emulerar Switchens uppdateringsfrekvens på 60Hz. 'Obunden' är en obegränsad uppdateringsfrekvens.",
|
"sv_SE": "Emulerad vertikal synk. 'Switch' emulerar Switchens uppdateringsfrekvens på 60Hz. 'Obunden' är en obegränsad uppdateringsfrekvens.",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23388,7 +23388,7 @@
|
|||||||
"no_NO": "Emulert vertikal synkronisering. «Switch» emulerer Switchs oppdateringsfrekvens på 60 Hz. «Ubegrenset» er en ubegrenset oppdateringsfrekvens. «Egendefinert» emulerer den angitte egendefinerte oppdateringsfrekvensen.",
|
"no_NO": "Emulert vertikal synkronisering. «Switch» emulerer Switchs oppdateringsfrekvens på 60 Hz. «Ubegrenset» er en ubegrenset oppdateringsfrekvens. «Egendefinert» emulerer den angitte egendefinerte oppdateringsfrekvensen.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Sincronização Vertical Emulada. 'Switch' emula a taxa de atualização de 60 Hz do Switch. 'Ilimitada' é uma taxa de atualização sem limite. 'Taxa de atualização personalizada' emula a taxa de atualização personalizada especificada.",
|
"pt_BR": "Sincronização Vertical Emulada. 'Switch' emula a taxa de atualização de 60 Hz do Switch. 'Ilimitada' é uma taxa de atualização sem limite. 'Taxa de atualização personalizada' emula a taxa de atualização personalizada especificada.",
|
||||||
"ru_RU": "Эмулированная вертикальная синхронизация. «Консоль» эмулирует частоту обновления консоли, равную 60 Гц. «Без ограничений» — неограниченная частота кадров. «Пользовательска частота кадров» эмулирует выбранную пользователем частоту кадров.",
|
"ru_RU": "Эмулированная вертикальная синхронизация. 'Консоль' эмулирует частоту обновления консоли, равную 60 Гц. 'Без ограничений' - неограниченная частота кадров. 'Пользовательска частота кадров' эмулирует выбранную пользователем частоту кадров.",
|
||||||
"sv_SE": "Emulerad vertikal synk. 'Switch' emulerar Switchens uppdateringsfrekvens på 60Hz. 'Obunden' är en obegränsad uppdateringsfrekvens. 'Anpassad uppdateringsfrekvens' emulerar den angivna anpassade uppdateringsfrekvensen.",
|
"sv_SE": "Emulerad vertikal synk. 'Switch' emulerar Switchens uppdateringsfrekvens på 60Hz. 'Obunden' är en obegränsad uppdateringsfrekvens. 'Anpassad uppdateringsfrekvens' emulerar den angivna anpassade uppdateringsfrekvensen.",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23638,7 +23638,7 @@
|
|||||||
"no_NO": "Sist oppdatert: {0}",
|
"no_NO": "Sist oppdatert: {0}",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Última atualização: {0}",
|
"pt_BR": "Última atualização: {0}",
|
||||||
"ru_RU": "Последнее обновление: {0}",
|
"ru_RU": "",
|
||||||
"sv_SE": "Senast uppdaterad: {0}",
|
"sv_SE": "Senast uppdaterad: {0}",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23663,7 +23663,7 @@
|
|||||||
"no_NO": "Denne kompatibilitetslisten kan inneholde oppføringer som er tomme for data.\nVær ikke imot å teste spill i statusen «Ingame».",
|
"no_NO": "Denne kompatibilitetslisten kan inneholde oppføringer som er tomme for data.\nVær ikke imot å teste spill i statusen «Ingame».",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Esta lista de compatibilidade pode estar desatualizada.\nNão se oponha a testar os jogos",
|
"pt_BR": "Esta lista de compatibilidade pode estar desatualizada.\nNão se oponha a testar os jogos",
|
||||||
"ru_RU": "В списке совместимости могут содержаться устаревшие записи.\nНе стестняйтесь тестировать игр в статусе «Запускается»",
|
"ru_RU": "",
|
||||||
"sv_SE": "Denna kompatibilitetslista kan innehålla utdaterade poster.\nTesta gärna spelen som listas med \"Spelproblem\"-status.",
|
"sv_SE": "Denna kompatibilitetslista kan innehålla utdaterade poster.\nTesta gärna spelen som listas med \"Spelproblem\"-status.",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23688,7 +23688,7 @@
|
|||||||
"no_NO": "Søk i kompatibilitetsoppføringer...",
|
"no_NO": "Søk i kompatibilitetsoppføringer...",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Pesquisa de compatibilidade",
|
"pt_BR": "Pesquisa de compatibilidade",
|
||||||
"ru_RU": "Поиск записей о совместимости...",
|
"ru_RU": "",
|
||||||
"sv_SE": "Sök i kompatibilitetsposter...",
|
"sv_SE": "Sök i kompatibilitetsposter...",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23713,7 +23713,7 @@
|
|||||||
"no_NO": "Åpne kompatibilitetslisten",
|
"no_NO": "Åpne kompatibilitetslisten",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Lista de Compatibilidade",
|
"pt_BR": "Lista de Compatibilidade",
|
||||||
"ru_RU": "Открыть список совместимости",
|
"ru_RU": "",
|
||||||
"sv_SE": "Öppna kompatibilitetslistan",
|
"sv_SE": "Öppna kompatibilitetslistan",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23738,7 +23738,7 @@
|
|||||||
"no_NO": "Vis bare eide spill",
|
"no_NO": "Vis bare eide spill",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Mostrar apenas jogos disponíveis",
|
"pt_BR": "Mostrar apenas jogos disponíveis",
|
||||||
"ru_RU": "Показывать только свои игры",
|
"ru_RU": "",
|
||||||
"sv_SE": "Visa endast ägda spel",
|
"sv_SE": "Visa endast ägda spel",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23763,7 +23763,7 @@
|
|||||||
"no_NO": "Spillbar",
|
"no_NO": "Spillbar",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Jogável",
|
"pt_BR": "Jogável",
|
||||||
"ru_RU": "Играбельно",
|
"ru_RU": "",
|
||||||
"sv_SE": "Spelbart",
|
"sv_SE": "Spelbart",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23788,7 +23788,7 @@
|
|||||||
"no_NO": "",
|
"no_NO": "",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "No jogo",
|
"pt_BR": "No jogo",
|
||||||
"ru_RU": "Запускается",
|
"ru_RU": "",
|
||||||
"sv_SE": "Spelproblem",
|
"sv_SE": "Spelproblem",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23813,7 +23813,7 @@
|
|||||||
"no_NO": "Menyer",
|
"no_NO": "Menyer",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Menu",
|
"pt_BR": "Menu",
|
||||||
"ru_RU": "Меню",
|
"ru_RU": "",
|
||||||
"sv_SE": "Menyer",
|
"sv_SE": "Menyer",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23838,7 +23838,7 @@
|
|||||||
"no_NO": "Starter",
|
"no_NO": "Starter",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Inicializa",
|
"pt_BR": "Inicializa",
|
||||||
"ru_RU": "Стартует",
|
"ru_RU": "",
|
||||||
"sv_SE": "Startar",
|
"sv_SE": "Startar",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23863,7 +23863,7 @@
|
|||||||
"no_NO": "Ingenting",
|
"no_NO": "Ingenting",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Nada",
|
"pt_BR": "Nada",
|
||||||
"ru_RU": "Ничего",
|
"ru_RU": "",
|
||||||
"sv_SE": "Ingenting",
|
"sv_SE": "Ingenting",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23888,7 +23888,7 @@
|
|||||||
"no_NO": "Starter opp og spiller uten krasj eller GPU-feil av noe slag, og med en hastighet som er rask nok til å ha rimelig glede av på en gjennomsnittlig PC.",
|
"no_NO": "Starter opp og spiller uten krasj eller GPU-feil av noe slag, og med en hastighet som er rask nok til å ha rimelig glede av på en gjennomsnittlig PC.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Inicializa e roda sem travamentos ou bugs de GPU de qualquer tipo, e em uma velocidade rápida o suficiente para ser aproveitado em um PC comum.",
|
"pt_BR": "Inicializa e roda sem travamentos ou bugs de GPU de qualquer tipo, e em uma velocidade rápida o suficiente para ser aproveitado em um PC comum.",
|
||||||
"ru_RU": "Запускается и работает без любого рода сбоев или графисечких ошибок и на скорости, достаточной для работы на обычном ПК.",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23913,7 +23913,7 @@
|
|||||||
"no_NO": "Starter og går i gang i spillet, men lider av ett eller flere av følgende: krasjer, fastlåser, GPU-feil, distraherende dårlig lyd eller er rett og slett for tregt. Spillet kan fortsatt spilles helt til ende, men ikke slik det er ment å spilles.",
|
"no_NO": "Starter og går i gang i spillet, men lider av ett eller flere av følgende: krasjer, fastlåser, GPU-feil, distraherende dårlig lyd eller er rett og slett for tregt. Spillet kan fortsatt spilles helt til ende, men ikke slik det er ment å spilles.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Inicializa e entra no jogo, mas sofre de um ou mais dos seguintes: travamentos, deadlocks, bugs de GPU, áudio ruim que distrai ou é simplesmente muito lento. O jogo ainda pode ser jogado até o fim, mas não da forma como foi criado para ser jogado.",
|
"pt_BR": "Inicializa e entra no jogo, mas sofre de um ou mais dos seguintes: travamentos, deadlocks, bugs de GPU, áudio ruim que distrai ou é simplesmente muito lento. O jogo ainda pode ser jogado até o fim, mas não da forma como foi criado para ser jogado.",
|
||||||
"ru_RU": "Запускается и работает, но возникает одна или несколько из следующих проблем: сбои, взаимоблокировки, ошибки GPU, отвлекающие звуки или просто слишком медленная работа. Возможно, игру всё же удастся пройти до конца, но не так, как она задумана.",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23938,7 +23938,7 @@
|
|||||||
"no_NO": "Starter opp og går forbi tittelskjermen, men kommer ikke inn i hovedspillet.",
|
"no_NO": "Starter opp og går forbi tittelskjermen, men kommer ikke inn i hovedspillet.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Inicializa e passa da tela de título, mas não entra no jogo principal.",
|
"pt_BR": "Inicializa e passa da tela de título, mas não entra no jogo principal.",
|
||||||
"ru_RU": "Загружается титульный экран и можно перейти дальше, но сама игра не работает.",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23963,7 +23963,7 @@
|
|||||||
"no_NO": "Starter, men kommer ikke lenger enn til tittelskjermen.",
|
"no_NO": "Starter, men kommer ikke lenger enn til tittelskjermen.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Inizializa, mas não passa da tela de título.",
|
"pt_BR": "Inizializa, mas não passa da tela de título.",
|
||||||
"ru_RU": "Загружается, но не проходит дальше титульного экрана.",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -23988,7 +23988,7 @@
|
|||||||
"no_NO": "Starter ikke opp eller viser ingen tegn til aktivitet.",
|
"no_NO": "Starter ikke opp eller viser ingen tegn til aktivitet.",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Não inicializa ou não mostra sinais de atividade.",
|
"pt_BR": "Não inicializa ou não mostra sinais de atividade.",
|
||||||
"ru_RU": "Не запускается или не подаёт признаков жизни.",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -24013,7 +24013,7 @@
|
|||||||
"no_NO": "Velg en DLC og hente ut",
|
"no_NO": "Velg en DLC og hente ut",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Selecione um DLC para Extrair",
|
"pt_BR": "Selecione um DLC para Extrair",
|
||||||
"ru_RU": "Выберите DLC для извлечения",
|
"ru_RU": "",
|
||||||
"sv_SE": "Välj en DLC att extrahera",
|
"sv_SE": "Välj en DLC att extrahera",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -24038,7 +24038,7 @@
|
|||||||
"no_NO": "Rikt nærværsbilde",
|
"no_NO": "Rikt nærværsbilde",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Imagem da Presença do Discord",
|
"pt_BR": "Imagem da Presença do Discord",
|
||||||
"ru_RU": "Изображение для статуса активности",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -24063,7 +24063,7 @@
|
|||||||
"no_NO": "Dynamisk og rik tilstedeværelse",
|
"no_NO": "Dynamisk og rik tilstedeværelse",
|
||||||
"pl_PL": "",
|
"pl_PL": "",
|
||||||
"pt_BR": "Presença Dinâmica do Discord",
|
"pt_BR": "Presença Dinâmica do Discord",
|
||||||
"ru_RU": "Динамический статус активности",
|
"ru_RU": "",
|
||||||
"sv_SE": "",
|
"sv_SE": "",
|
||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
@@ -24073,4 +24073,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -793,7 +793,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task HandleFirmwareInstallation(string filename)
|
private async Task HandleFirmwareInstallation(string filename)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -139,21 +139,8 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
base.OnApplyTemplate(e);
|
base.OnApplyTemplate(e);
|
||||||
|
|
||||||
NotificationHelper.SetNotificationManager(this);
|
NotificationHelper.SetNotificationManager(this);
|
||||||
|
|
||||||
Executor.ExecuteBackgroundAsync(async () =>
|
Executor.ExecuteBackgroundAsync(ShowIntelMacWarningAsync);
|
||||||
{
|
|
||||||
await ShowIntelMacWarningAsync();
|
|
||||||
FilePath firmwarePath = CommandLineState.FirmwareToInstallPathArg;
|
|
||||||
if (firmwarePath is not null)
|
|
||||||
{
|
|
||||||
if ((firmwarePath.ExistsAsFile && firmwarePath.Extension is "xci" or "zip") ||
|
|
||||||
firmwarePath.ExistsAsDirectory)
|
|
||||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
|
||||||
ViewModel.HandleFirmwareInstallation(firmwarePath));
|
|
||||||
else
|
|
||||||
Logger.Notice.Print(LogClass.UI, "Invalid firmware type provided. Path must be a directory, or a .zip or .xci file.");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnScalingChanged(object sender, EventArgs e)
|
private void OnScalingChanged(object sender, EventArgs e)
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using Gommon;
|
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
@@ -14,7 +13,6 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
public static string OverrideBackendThreading { get; private set; }
|
public static string OverrideBackendThreading { get; private set; }
|
||||||
public static string OverrideHideCursor { get; private set; }
|
public static string OverrideHideCursor { get; private set; }
|
||||||
public static string BaseDirPathArg { get; private set; }
|
public static string BaseDirPathArg { get; private set; }
|
||||||
public static FilePath FirmwareToInstallPathArg { get; private set; }
|
|
||||||
public static string Profile { get; private set; }
|
public static string Profile { get; private set; }
|
||||||
public static string LaunchPathArg { get; private set; }
|
public static string LaunchPathArg { get; private set; }
|
||||||
public static string LaunchApplicationId { get; private set; }
|
public static string LaunchApplicationId { get; private set; }
|
||||||
@@ -43,19 +41,6 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
|
|
||||||
BaseDirPathArg = args[++i];
|
BaseDirPathArg = args[++i];
|
||||||
|
|
||||||
arguments.Add(arg);
|
|
||||||
arguments.Add(args[i]);
|
|
||||||
break;
|
|
||||||
case "--install-firmware":
|
|
||||||
if (i + 1 >= args.Length)
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.Application, $"Invalid option '{arg}'");
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
FirmwareToInstallPathArg = new FilePath(args[++i]);
|
|
||||||
|
|
||||||
arguments.Add(arg);
|
arguments.Add(arg);
|
||||||
arguments.Add(args[i]);
|
arguments.Add(args[i]);
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user