Applet Prep + System Buffer Sharing #634

Open
Jacobwasbeast wants to merge 6 commits from Jacobwasbeast/feature/system-buffer-sharing into master
62 changed files with 1730 additions and 341 deletions

View File

@ -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);
} }

View File

@ -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);
} }
} }
} }

View File

@ -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,

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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;

View File

@ -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)
{ {

View File

@ -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,

View File

@ -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>

View File

@ -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,

View File

@ -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;
} }
} }

View File

@ -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))

View File

@ -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()
{ {

View File

@ -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);
} }
} }
} }

View File

@ -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)

View File

@ -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;
} }
} }

View File

@ -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;
} }

View File

@ -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)

View File

@ -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);
} }
} }
} }

View File

@ -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;
} }

View File

@ -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);
} }

View File

@ -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>

View File

@ -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;

View File

@ -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;

View File

@ -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;
} }

View File

@ -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;
} }
} }
} }

View File

@ -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;

View File

@ -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/>

View File

@ -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>

View File

@ -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;
} }

View File

@ -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>

View File

@ -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; }

View File

@ -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();

View File

@ -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; }

View File

@ -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; }

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
} }
} }

View File

@ -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);

View File

@ -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>

View File

@ -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,
};
}
} }
} }

View File

@ -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)]

View File

@ -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;
}
} }
} }

View File

@ -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() { }
} }
} }

View File

@ -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}!");

View File

@ -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);
} }

View File

@ -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()

View File

@ -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));

View File

@ -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);

View File

@ -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;
} }
} }

View File

@ -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);

View File

@ -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;
}
} }
} }

View File

@ -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;
}
}

View File

@ -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");

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
} }
} }

View File

@ -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);
}
} }
} }

View File

@ -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
{ {

View File

@ -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();