Initial support for separate GPU address spaces (#2394)

* Make GPU memory manager a member of GPU channel

* Move physical memory instance to the memory manager, and the caches to the physical memory

* PR feedback
This commit is contained in:
gdkchan 2021-06-29 14:32:02 -03:00 committed by GitHub
parent 8cc872fb60
commit fbb4019ed5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
44 changed files with 780 additions and 481 deletions

View file

@ -16,11 +16,13 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <param name="argument">Method call argument</param>
public void Dispatch(GpuState state, int argument)
{
FlushUboDirty();
var memoryManager = state.Channel.MemoryManager;
FlushUboDirty(memoryManager);
uint qmdAddress = (uint)state.Get<int>(MethodOffset.DispatchParamsAddress);
var qmd = _context.MemoryManager.Read<ComputeQmd>((ulong)qmdAddress << 8);
var qmd = state.Channel.MemoryManager.Read<ComputeQmd>((ulong)qmdAddress << 8);
GpuVa shaderBaseAddress = state.Get<GpuVa>(MethodOffset.ShaderBaseAddress);
@ -43,7 +45,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
state.Channel.BufferManager.SetComputeUniformBuffer(index, gpuVa, size);
}
ShaderBundle cs = ShaderCache.GetComputeShader(
ShaderBundle cs = memoryManager.Physical.ShaderCache.GetComputeShader(
state,
shaderGpuVa,
qmd.CtaThreadDimension0,
@ -82,7 +84,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
cbDescAddress += (ulong)cbDescOffset;
SbDescriptor cbDescriptor = _context.PhysicalMemory.Read<SbDescriptor>(cbDescAddress);
SbDescriptor cbDescriptor = state.Channel.MemoryManager.Physical.Read<SbDescriptor>(cbDescAddress);
state.Channel.BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size);
}
@ -97,7 +99,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
sbDescAddress += (ulong)sbDescOffset;
SbDescriptor sbDescriptor = _context.PhysicalMemory.Read<SbDescriptor>(sbDescAddress);
SbDescriptor sbDescriptor = state.Channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
state.Channel.BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags);
}

View file

@ -79,13 +79,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
// TODO: Acquire operations (Wait), interrupts for invalid combinations.
if (operation == SemaphoredOperation.Release)
{
_context.MemoryManager.Write(address, value);
_parent.MemoryManager.Write(address, value);
}
else if (operation == SemaphoredOperation.Reduction)
{
bool signed = _state.State.SemaphoredFormat == SemaphoredFormat.Signed;
int mem = _context.MemoryManager.Read<int>(address);
int mem = _parent.MemoryManager.Read<int>(address);
switch (_state.State.SemaphoredReduction)
{
@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
break;
}
_context.MemoryManager.Write(address, value);
_parent.MemoryManager.Write(address, value);
}
}

View file

@ -1,4 +1,5 @@
using System;
using Ryujinx.Graphics.Gpu.Memory;
using System;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -53,11 +54,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
/// <summary>
/// Fetch the command buffer.
/// </summary>
public void Fetch(GpuContext context)
public void Fetch(MemoryManager memoryManager)
{
if (Words == null)
{
Words = MemoryMarshal.Cast<byte, int>(context.MemoryManager.GetSpan(EntryAddress, (int)EntryCount * 4, true)).ToArray();
Words = MemoryMarshal.Cast<byte, int>(memoryManager.GetSpan(EntryAddress, (int)EntryCount * 4, true)).ToArray();
}
}
}
@ -155,7 +156,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
if (beforeBarrier && commandBuffer.Type == CommandBufferType.Prefetch)
{
commandBuffer.Fetch(_context);
commandBuffer.Fetch(processor.MemoryManager);
}
if (commandBuffer.Type == CommandBufferType.NoPrefetch)
@ -182,13 +183,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
public void DispatchCalls()
{
// Use this opportunity to also dispose any pending channels that were closed.
_context.DisposePendingChannels();
_context.RunDeferredActions();
// Process command buffers.
while (_ibEnable && !_interrupt && _commandBufferQueue.TryDequeue(out CommandBuffer entry))
{
_currentCommandBuffer = entry;
_currentCommandBuffer.Fetch(_context);
_currentCommandBuffer.Fetch(entry.Processor.MemoryManager);
// If we are changing the current channel,
// we need to force all the host state to be updated.

View file

@ -1,4 +1,5 @@
using Ryujinx.Graphics.Gpu.State;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.State;
using System;
using System.Runtime.CompilerServices;
@ -13,6 +14,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
private const int MacroIndexMask = MacrosCount - 1;
private readonly GpuContext _context;
private readonly GpuChannel _channel;
public MemoryManager MemoryManager => _channel.MemoryManager;
/// <summary>
/// Internal GPFIFO state.
@ -39,6 +43,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
public GPFifoProcessor(GpuContext context, GpuChannel channel)
{
_context = context;
_channel = channel;
_fifoClass = new GPFifoClass(context, this);
_subChannels = new GpuState[8];

View file

@ -40,10 +40,10 @@ namespace Ryujinx.Graphics.Gpu.Engine
_buffer = new int[count];
}
ulong dstBaseAddress = _context.MemoryManager.Translate(_params.DstAddress.Pack());
ulong dstBaseAddress = state.Channel.MemoryManager.Translate(_params.DstAddress.Pack());
// Trigger read tracking, to flush any managed resources in the destination region.
_context.PhysicalMemory.GetSpan(dstBaseAddress, _size, true);
state.Channel.MemoryManager.Physical.GetSpan(dstBaseAddress, _size, true);
_finished = false;
}
@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
if (_offset * 4 >= _size)
{
FinishTransfer();
FinishTransfer(state);
}
}
}
@ -69,15 +69,16 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <summary>
/// Performs actual copy of the inline data after the transfer is finished.
/// </summary>
private void FinishTransfer()
/// <param name="state">Current GPU state</param>
private void FinishTransfer(GpuState state)
{
Span<byte> data = MemoryMarshal.Cast<int, byte>(_buffer).Slice(0, _size);
if (_isLinear && _params.LineCount == 1)
{
ulong address = _context.MemoryManager.Translate(_params.DstAddress.Pack());
ulong address = state.Channel.MemoryManager.Translate(_params.DstAddress.Pack());
_context.PhysicalMemory.Write(address, data);
state.Channel.MemoryManager.Physical.Write(address, data);
}
else
{
@ -91,7 +92,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
int srcOffset = 0;
ulong dstBaseAddress = _context.MemoryManager.Translate(_params.DstAddress.Pack());
ulong dstBaseAddress = state.Channel.MemoryManager.Translate(_params.DstAddress.Pack());
for (int y = _params.DstY; y < _params.DstY + _params.LineCount; y++)
{
@ -109,7 +110,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
Span<byte> pixel = data.Slice(srcOffset, 16);
_context.PhysicalMemory.Write(dstAddress, pixel);
state.Channel.MemoryManager.Physical.Write(dstAddress, pixel);
}
for (; x < x2; x++, srcOffset++)
@ -120,7 +121,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
Span<byte> pixel = data.Slice(srcOffset, 1);
_context.PhysicalMemory.Write(dstAddress, pixel);
state.Channel.MemoryManager.Physical.Write(dstAddress, pixel);
}
}
}

View file

@ -1,5 +1,6 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.State;
namespace Ryujinx.Graphics.Gpu.Engine
@ -23,11 +24,11 @@ namespace Ryujinx.Graphics.Gpu.Engine
case Condition.Never:
return ConditionalRenderEnabled.False;
case Condition.ResultNonZero:
return CounterNonZero(condState.Address.Pack());
return CounterNonZero(state, condState.Address.Pack());
case Condition.Equal:
return CounterCompare(condState.Address.Pack(), true);
return CounterCompare(state, condState.Address.Pack(), true);
case Condition.NotEqual:
return CounterCompare(condState.Address.Pack(), false);
return CounterCompare(state, condState.Address.Pack(), false);
}
Logger.Warning?.Print(LogClass.Gpu, $"Invalid conditional render condition \"{condState.Condition}\".");
@ -38,11 +39,12 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <summary>
/// Checks if the counter value at a given GPU memory address is non-zero.
/// </summary>
/// <param name="state">GPU state</param>
/// <param name="gpuVa">GPU virtual address of the counter value</param>
/// <returns>True if the value is not zero, false otherwise. Returns host if handling with host conditional rendering</returns>
private ConditionalRenderEnabled CounterNonZero(ulong gpuVa)
private ConditionalRenderEnabled CounterNonZero(GpuState state, ulong gpuVa)
{
ICounterEvent evt = _counterCache.FindEvent(gpuVa);
ICounterEvent evt = state.Channel.MemoryManager.CounterCache.FindEvent(gpuVa);
if (evt == null)
{
@ -56,30 +58,31 @@ namespace Ryujinx.Graphics.Gpu.Engine
else
{
evt.Flush();
return (_context.MemoryManager.Read<ulong>(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
return (state.Channel.MemoryManager.Read<ulong>(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
}
}
/// <summary>
/// Checks if the counter at a given GPU memory address passes a specified equality comparison.
/// </summary>
/// <param name="state">GPU state</param>
/// <param name="gpuVa">GPU virtual address</param>
/// <param name="isEqual">True to check if the values are equal, false to check if they are not equal</param>
/// <returns>True if the condition is met, false otherwise. Returns host if handling with host conditional rendering</returns>
private ConditionalRenderEnabled CounterCompare(ulong gpuVa, bool isEqual)
private ConditionalRenderEnabled CounterCompare(GpuState state, ulong gpuVa, bool isEqual)
{
ICounterEvent evt = FindEvent(gpuVa);
ICounterEvent evt2 = FindEvent(gpuVa + 16);
ICounterEvent evt = FindEvent(state.Channel.MemoryManager.CounterCache, gpuVa);
ICounterEvent evt2 = FindEvent(state.Channel.MemoryManager.CounterCache, gpuVa + 16);
bool useHost;
if (evt != null && evt2 == null)
{
useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, _context.MemoryManager.Read<ulong>(gpuVa + 16), isEqual);
useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, state.Channel.MemoryManager.Read<ulong>(gpuVa + 16), isEqual);
}
else if (evt == null && evt2 != null)
{
useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt2, _context.MemoryManager.Read<ulong>(gpuVa), isEqual);
useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt2, state.Channel.MemoryManager.Read<ulong>(gpuVa), isEqual);
}
else if (evt != null && evt2 != null)
{
@ -99,8 +102,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
evt?.Flush();
evt2?.Flush();
ulong x = _context.MemoryManager.Read<ulong>(gpuVa);
ulong y = _context.MemoryManager.Read<ulong>(gpuVa + 16);
ulong x = state.Channel.MemoryManager.Read<ulong>(gpuVa);
ulong y = state.Channel.MemoryManager.Read<ulong>(gpuVa + 16);
return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
}
@ -110,11 +113,12 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// Tries to find a counter that is supposed to be written at the specified address,
/// returning the related event.
/// </summary>
/// <param name="counterCache">GPU counter cache to search on</param>
/// <param name="gpuVa">GPU virtual address where the counter is supposed to be written</param>
/// <returns>The counter event, or null if not present</returns>
private ICounterEvent FindEvent(ulong gpuVa)
private static ICounterEvent FindEvent(CounterCache counterCache, ulong gpuVa)
{
return _counterCache.FindEvent(gpuVa);
return counterCache.FindEvent(gpuVa);
}
}
}

View file

@ -69,7 +69,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
return;
}
FlushUboDirty();
FlushUboDirty(state.Channel.MemoryManager);
if (copy2D)
{
@ -98,21 +98,27 @@ namespace Ryujinx.Graphics.Gpu.Engine
dst.MemoryLayout.UnpackGobBlocksInZ(),
dstBpp);
ulong srcBaseAddress = _context.MemoryManager.Translate(cbp.SrcAddress.Pack());
ulong dstBaseAddress = _context.MemoryManager.Translate(cbp.DstAddress.Pack());
ulong srcBaseAddress = state.Channel.MemoryManager.Translate(cbp.SrcAddress.Pack());
ulong dstBaseAddress = state.Channel.MemoryManager.Translate(cbp.DstAddress.Pack());
(int srcBaseOffset, int srcSize) = srcCalculator.GetRectangleRange(src.RegionX, src.RegionY, cbp.XCount, cbp.YCount);
(int dstBaseOffset, int dstSize) = dstCalculator.GetRectangleRange(dst.RegionX, dst.RegionY, cbp.XCount, cbp.YCount);
ReadOnlySpan<byte> srcSpan = _context.PhysicalMemory.GetSpan(srcBaseAddress + (ulong)srcBaseOffset, srcSize, true);
Span<byte> dstSpan = _context.PhysicalMemory.GetSpan(dstBaseAddress + (ulong)dstBaseOffset, dstSize).ToArray();
ReadOnlySpan<byte> srcSpan = state.Channel.MemoryManager.Physical.GetSpan(srcBaseAddress + (ulong)srcBaseOffset, srcSize, true);
Span<byte> dstSpan = state.Channel.MemoryManager.Physical.GetSpan(dstBaseAddress + (ulong)dstBaseOffset, dstSize).ToArray();
bool completeSource = IsTextureCopyComplete(cbp, src, srcLinear, srcBpp, cbp.SrcStride);
bool completeDest = IsTextureCopyComplete(cbp, dst, dstLinear, dstBpp, cbp.DstStride);
if (completeSource && completeDest)
{
Image.Texture target = TextureCache.FindTexture(dst, cbp, swizzle, dstLinear);
Image.Texture target = state.Channel.MemoryManager.Physical.TextureCache.FindTexture(
state.Channel.MemoryManager,
dst,
cbp,
swizzle,
dstLinear);
if (target != null)
{
ReadOnlySpan<byte> data;
@ -154,7 +160,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
{
srcSpan.CopyTo(dstSpan); // No layout conversion has to be performed, just copy the data entirely.
_context.PhysicalMemory.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan);
state.Channel.MemoryManager.Physical.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan);
return;
}
@ -195,7 +201,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
_ => throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format.")
};
_context.PhysicalMemory.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan);
state.Channel.MemoryManager.Physical.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan);
}
else
{
@ -209,13 +215,21 @@ namespace Ryujinx.Graphics.Gpu.Engine
swizzle.UnpackComponentSize() == 4)
{
// Fast path for clears when remap is enabled.
BufferCache.ClearBuffer(cbp.DstAddress, (uint)size * 4, state.Get<uint>(MethodOffset.CopyBufferConstA));
state.Channel.MemoryManager.Physical.BufferCache.ClearBuffer(
state.Channel.MemoryManager,
cbp.DstAddress,
(uint)size * 4,
state.Get<uint>(MethodOffset.CopyBufferConstA));
}
else
{
// TODO: Implement remap functionality.
// Buffer to buffer copy.
BufferCache.CopyBuffer(cbp.SrcAddress, cbp.DstAddress, (uint)size);
state.Channel.MemoryManager.Physical.BufferCache.CopyBuffer(
state.Channel.MemoryManager,
cbp.SrcAddress,
cbp.DstAddress,
(uint)size);
}
}
}

View file

@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <param name="argument">Method call argument</param>
private void CopyTexture(GpuState state, int argument)
{
var memoryManager = state.Channel.MemoryManager;
var dstCopyTexture = state.Get<CopyTexture>(MethodOffset.CopyDstTexture);
var srcCopyTexture = state.Get<CopyTexture>(MethodOffset.CopySrcTexture);
@ -80,7 +82,13 @@ namespace Ryujinx.Graphics.Gpu.Engine
srcX1 = 0;
}
Texture srcTexture = TextureCache.FindOrCreateTexture(srcCopyTexture, offset, srcCopyTextureFormat, true, srcHint);
Texture srcTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture(
memoryManager,
srcCopyTexture,
offset,
srcCopyTextureFormat,
true,
srcHint);
if (srcTexture == null)
{
@ -101,7 +109,13 @@ namespace Ryujinx.Graphics.Gpu.Engine
dstCopyTextureFormat = dstCopyTexture.Format.Convert();
}
Texture dstTexture = TextureCache.FindOrCreateTexture(dstCopyTexture, 0, dstCopyTextureFormat, srcTexture.ScaleMode == TextureScaleMode.Scaled, dstHint);
Texture dstTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture(
memoryManager,
dstCopyTexture,
0,
dstCopyTextureFormat,
srcTexture.ScaleMode == TextureScaleMode.Scaled,
dstHint);
if (dstTexture == null)
{

View file

@ -12,8 +12,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
private const int NsToTicksFractionNumerator = 384;
private const int NsToTicksFractionDenominator = 625;
private readonly CounterCache _counterCache = new CounterCache();
/// <summary>
/// Writes a GPU counter to guest memory.
/// </summary>
@ -39,7 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
{
var rs = state.Get<SemaphoreState>(MethodOffset.ReportState);
_context.MemoryManager.Write(rs.Address.Pack(), rs.Payload);
state.Channel.MemoryManager.Write(rs.Address.Pack(), rs.Payload);
_context.AdvanceSequence();
}
@ -85,7 +83,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
if (counter?.Invalid != true)
{
_context.MemoryManager.Write(gpuVa, counterData);
state.Channel.MemoryManager.Write(gpuVa, counterData);
}
};
@ -105,7 +103,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
break;
}
_counterCache.AddOrUpdate(gpuVa, counter);
state.Channel.MemoryManager.CounterCache.AddOrUpdate(gpuVa, counter);
}
/// <summary>

View file

@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
int index = (argument >> 4) & 0x1f;
FlushUboDirty();
FlushUboDirty(state.Channel.MemoryManager);
if (enable)
{

View file

@ -16,11 +16,12 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <summary>
/// Flushes any queued ubo updates.
/// </summary>
private void FlushUboDirty()
/// <param name="memoryManager">GPU memory manager where the uniform buffer is mapped</param>
private void FlushUboDirty(MemoryManager memoryManager)
{
if (_ubFollowUpAddress != 0)
{
BufferCache.ForceDirty(_ubFollowUpAddress - _ubByteCount, _ubByteCount);
memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
_ubFollowUpAddress = 0;
}
@ -39,13 +40,14 @@ namespace Ryujinx.Graphics.Gpu.Engine
if (_ubFollowUpAddress != address)
{
FlushUboDirty();
FlushUboDirty(state.Channel.MemoryManager);
_ubByteCount = 0;
_ubBeginCpuAddress = _context.MemoryManager.Translate(address);
_ubBeginCpuAddress = state.Channel.MemoryManager.Translate(address);
}
_context.PhysicalMemory.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, MemoryMarshal.Cast<int, byte>(MemoryMarshal.CreateSpan(ref argument, 1)));
var byteData = MemoryMarshal.Cast<int, byte>(MemoryMarshal.CreateSpan(ref argument, 1));
state.Channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
_ubFollowUpAddress = address + 4;
_ubByteCount += 4;
@ -68,13 +70,14 @@ namespace Ryujinx.Graphics.Gpu.Engine
if (_ubFollowUpAddress != address)
{
FlushUboDirty();
FlushUboDirty(state.Channel.MemoryManager);
_ubByteCount = 0;
_ubBeginCpuAddress = _context.MemoryManager.Translate(address);
_ubBeginCpuAddress = state.Channel.MemoryManager.Translate(address);
}
_context.PhysicalMemory.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, MemoryMarshal.Cast<int, byte>(data));
var byteData = MemoryMarshal.Cast<int, byte>(data);
state.Channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
_ubFollowUpAddress = address + size;
_ubByteCount += size;

View file

@ -22,21 +22,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
private readonly GpuContext _context;
private readonly ShaderProgramInfo[] _currentProgramInfo;
/// <summary>
/// In-memory shader cache.
/// </summary>
public ShaderCache ShaderCache { get; }
/// <summary>
/// GPU buffer manager.
/// </summary>
public BufferCache BufferCache { get; }
/// <summary>
/// GPU texture manager.
/// </summary>
public TextureCache TextureCache { get; }
private bool _isAnyVbInstanced;
private bool _vsUsesInstanceId;
private byte _vsClipDistancesWritten;
@ -53,16 +38,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
{
_context = context;
ShaderCache = new ShaderCache(_context);
_currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages];
BufferCache = new BufferCache(context);
TextureCache = new TextureCache(context);
context.MemoryManager.MemoryUnmapped += _counterCache.MemoryUnmappedHandler;
context.MemoryManager.MemoryUnmapped += TextureCache.MemoryUnmappedHandler;
context.MemoryManager.MemoryUnmapped += BufferCache.MemoryUnmappedHandler;
}
/// <summary>
@ -130,7 +106,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
_prevTfEnable = false;
}
FlushUboDirty();
FlushUboDirty(state.Channel.MemoryManager);
// Shaders must be the first one to be updated if modified, because
// some of the other state depends on information from the currently
@ -342,7 +318,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
sbDescAddress += (ulong)sbDescOffset;
SbDescriptor sbDescriptor = _context.PhysicalMemory.Read<SbDescriptor>(sbDescAddress);
SbDescriptor sbDescriptor = state.Channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
state.Channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags);
}
@ -357,6 +333,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
private void UpdateRenderTargetState(GpuState state, bool useControl, int singleUse = -1)
{
var memoryManager = state.Channel.MemoryManager;
var rtControl = state.Get<RtControl>(MethodOffset.RtControl);
int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
@ -384,7 +361,12 @@ namespace Ryujinx.Graphics.Gpu.Engine
continue;
}
Texture color = TextureCache.FindOrCreateTexture(colorState, samplesInX, samplesInY, sizeHint);
Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
memoryManager,
colorState,
samplesInX,
samplesInY,
sizeHint);
changedScale |= state.Channel.TextureManager.SetRenderTargetColor(index, color);
}
@ -398,7 +380,13 @@ namespace Ryujinx.Graphics.Gpu.Engine
var dsState = state.Get<RtDepthStencilState>(MethodOffset.RtDepthStencilState);
var dsSize = state.Get<Size3D>(MethodOffset.RtDepthStencilSize);
depthStencil = TextureCache.FindOrCreateTexture(dsState, dsSize, samplesInX, samplesInY, sizeHint);
depthStencil = memoryManager.Physical.TextureCache.FindOrCreateTexture(
memoryManager,
dsState,
dsSize,
samplesInX,
samplesInY,
sizeHint);
}
changedScale |= state.Channel.TextureManager.SetRenderTargetDepthStencil(depthStencil);
@ -1012,7 +1000,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
addressesArray[index] = baseAddress + shader.Offset;
}
ShaderBundle gs = ShaderCache.GetGraphicsShader(state, addresses);
ShaderBundle gs = state.Channel.MemoryManager.Physical.ShaderCache.GetGraphicsShader(state, addresses);
byte oldVsClipDistancesWritten = _vsClipDistancesWritten;

View file

@ -2,6 +2,7 @@
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
using System;
using System.Threading;
namespace Ryujinx.Graphics.Gpu
{
@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Gpu
private readonly GpuContext _context;
private readonly GPFifoDevice _device;
private readonly GPFifoProcessor _processor;
private MemoryManager _memoryManager;
/// <summary>
/// Channel buffer bindings manager.
@ -24,6 +26,11 @@ namespace Ryujinx.Graphics.Gpu
/// </summary>
internal TextureManager TextureManager { get; }
/// <summary>
/// Current channel memory manager.
/// </summary>
internal MemoryManager MemoryManager => _memoryManager;
/// <summary>
/// Creates a new instance of a GPU channel.
/// </summary>
@ -33,10 +40,30 @@ namespace Ryujinx.Graphics.Gpu
_context = context;
_device = context.GPFifo;
_processor = new GPFifoProcessor(context, this);
BufferManager = new BufferManager(context);
BufferManager = new BufferManager(context, this);
TextureManager = new TextureManager(context, this);
}
/// <summary>
/// Binds a memory manager to the channel.
/// All submitted and in-flight commands will use the specified memory manager for any memory operations.
/// </summary>
/// <param name="memoryManager">The new memory manager to be bound</param>
public void BindMemory(MemoryManager memoryManager)
{
var oldMemoryManager = Interlocked.Exchange(ref _memoryManager, memoryManager ?? throw new ArgumentNullException(nameof(memoryManager)));
memoryManager.Physical.IncrementReferenceCount();
if (oldMemoryManager != null)
{
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
oldMemoryManager.Physical.DecrementReferenceCount();
}
memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind;
}
/// <summary>
/// Push a GPFIFO entry in the form of a prefetched command buffer.
/// It is intended to be used by nvservices to handle special cases.
@ -62,17 +89,23 @@ namespace Ryujinx.Graphics.Gpu
/// </summary>
public void Dispose()
{
_context.DisposedChannels.Enqueue(this);
_context.DeferredActions.Enqueue(Destroy);
}
/// <summary>
/// Performs disposal of the host GPU resources used by this channel, that are not shared.
/// This must only be called from the render thread.
/// </summary>
internal void Destroy()
private void Destroy()
{
BufferManager.Dispose();
TextureManager.Dispose();
var oldMemoryManager = Interlocked.Exchange(ref _memoryManager, null);
if (oldMemoryManager != null)
{
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
oldMemoryManager.Physical.DecrementReferenceCount();
}
}
}
}

View file

@ -2,8 +2,10 @@ using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine;
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Gpu.Synchronization;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
@ -24,16 +26,6 @@ namespace Ryujinx.Graphics.Gpu
/// </summary>
public IRenderer Renderer { get; }
/// <summary>
/// Physical memory access (it actually accesses the process memory, not actual physical memory).
/// </summary>
internal PhysicalMemory PhysicalMemory { get; private set; }
/// <summary>
/// GPU memory manager.
/// </summary>
public MemoryManager MemoryManager { get; }
/// <summary>
/// GPU engine methods processing.
/// </summary>
@ -73,11 +65,14 @@ namespace Ryujinx.Graphics.Gpu
internal List<Action> SyncActions { get; }
/// <summary>
/// Queue with closed channels for deferred disposal from the render thread.
/// Queue with deferred actions that must run on the render thread.
/// </summary>
internal Queue<GpuChannel> DisposedChannels { get; }
internal Queue<Action> DeferredActions { get; }
private readonly Lazy<Capabilities> _caps;
/// <summary>
/// Registry with physical memories that can be used with this GPU context, keyed by owner process ID.
/// </summary>
internal ConcurrentDictionary<long, PhysicalMemory> PhysicalMemoryRegistry { get; }
/// <summary>
/// Host hardware capabilities.
@ -87,11 +82,9 @@ namespace Ryujinx.Graphics.Gpu
/// <summary>
/// Event for signalling shader cache loading progress.
/// </summary>
public event Action<Shader.ShaderCacheState, int, int> ShaderCacheStateChanged
{
add => Methods.ShaderCache.ShaderCacheStateChanged += value;
remove => Methods.ShaderCache.ShaderCacheStateChanged -= value;
}
public event Action<ShaderCacheState, int, int> ShaderCacheStateChanged;
private readonly Lazy<Capabilities> _caps;
/// <summary>
/// Creates a new instance of the GPU emulation context.
@ -101,8 +94,6 @@ namespace Ryujinx.Graphics.Gpu
{
Renderer = renderer;
MemoryManager = new MemoryManager(this);
Methods = new Methods(this);
GPFifo = new GPFifoDevice(this);
@ -111,20 +102,83 @@ namespace Ryujinx.Graphics.Gpu
Window = new Window(this);
_caps = new Lazy<Capabilities>(Renderer.GetCapabilities);
HostInitalized = new ManualResetEvent(false);
SyncActions = new List<Action>();
DisposedChannels = new Queue<GpuChannel>();
DeferredActions = new Queue<Action>();
PhysicalMemoryRegistry = new ConcurrentDictionary<long, PhysicalMemory>();
_caps = new Lazy<Capabilities>(Renderer.GetCapabilities);
}
/// <summary>
/// Creates a new GPU channel.
/// </summary>
/// <returns>The GPU channel</returns>
public GpuChannel CreateChannel()
{
return new GpuChannel(this);
}
/// <summary>
/// Creates a new GPU memory manager.
/// </summary>
/// <param name="pid">ID of the process that owns the memory manager</param>
/// <returns>The memory manager</returns>
/// <exception cref="ArgumentException">Thrown when <paramref name="pid"/> is invalid</exception>
public MemoryManager CreateMemoryManager(long pid)
{
if (!PhysicalMemoryRegistry.TryGetValue(pid, out var physicalMemory))
{
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid));
}
return new MemoryManager(physicalMemory);
}
/// <summary>
/// Registers virtual memory used by a process for GPU memory access, caching and read/write tracking.
/// </summary>
/// <param name="pid">ID of the process that owns <paramref name="cpuMemory"/></param>
/// <param name="cpuMemory">Virtual memory owned by the process</param>
/// <exception cref="ArgumentException">Thrown if <paramref name="pid"/> was already registered</exception>
public void RegisterProcess(long pid, Cpu.IVirtualMemoryManagerTracked cpuMemory)
{
var physicalMemory = new PhysicalMemory(this, cpuMemory);
if (!PhysicalMemoryRegistry.TryAdd(pid, physicalMemory))
{
throw new ArgumentException("The PID was already registered", nameof(pid));
}
physicalMemory.ShaderCache.ShaderCacheStateChanged += ShaderCacheStateUpdate;
}
/// <summary>
/// Unregisters a process, indicating that its memory will no longer be used, and that caches can be freed.
/// </summary>
/// <param name="pid">ID of the process</param>
public void UnregisterProcess(long pid)
{
if (PhysicalMemoryRegistry.TryRemove(pid, out var physicalMemory))
{
physicalMemory.ShaderCache.ShaderCacheStateChanged -= ShaderCacheStateUpdate;
physicalMemory.Dispose();
}
}
/// <summary>
/// Shader cache state update handler.
/// </summary>
/// <param name="state">Current state of the shader cache load process</param>
/// <param name="current">Number of the current shader being processed</param>
/// <param name="total">Total number of shaders to process</param>
private void ShaderCacheStateUpdate(ShaderCacheState state, int current, int total)
{
ShaderCacheStateChanged?.Invoke(state, current, total);
}
/// <summary>
/// Initialize the GPU shader cache.
/// </summary>
@ -132,7 +186,10 @@ namespace Ryujinx.Graphics.Gpu
{
HostInitalized.WaitOne();
Methods.ShaderCache.Initialize();
foreach (var physicalMemory in PhysicalMemoryRegistry.Values)
{
physicalMemory.ShaderCache.Initialize();
}
}
/// <summary>
@ -144,16 +201,6 @@ namespace Ryujinx.Graphics.Gpu
SequenceNumber++;
}
/// <summary>
/// Sets the process memory manager, after the application process is initialized.
/// This is required for any GPU memory access.
/// </summary>
/// <param name="cpuMemory">CPU memory manager</param>
public void SetVmm(Cpu.IVirtualMemoryManagerTracked cpuMemory)
{
PhysicalMemory = new PhysicalMemory(cpuMemory);
}
/// <summary>
/// Registers an action to be performed the next time a syncpoint is incremented.
/// This will also ensure a host sync object is created, and <see cref="SyncNumber"/> is incremented.
@ -186,14 +233,14 @@ namespace Ryujinx.Graphics.Gpu
}
/// <summary>
/// Performs deferred disposal of closed channels.
/// This must only be called from the render thread.
/// Performs deferred actions.
/// This is useful for actions that must run on the render thread, such as resource disposal.
/// </summary>
internal void DisposePendingChannels()
internal void RunDeferredActions()
{
while (DisposedChannels.TryDequeue(out GpuChannel channel))
while (DeferredActions.TryDequeue(out Action action))
{
channel.Destroy();
action();
}
}
@ -205,15 +252,19 @@ namespace Ryujinx.Graphics.Gpu
/// </summary>
public void Dispose()
{
DisposePendingChannels();
Methods.ShaderCache.Dispose();
Methods.BufferCache.Dispose();
Methods.TextureCache.Dispose();
Renderer.Dispose();
GPFifo.Dispose();
HostInitalized.Dispose();
PhysicalMemory.Dispose();
// Has to be disposed before processing deferred actions, as it will produce some.
foreach (var physicalMemory in PhysicalMemoryRegistry.Values)
{
physicalMemory.Dispose();
}
PhysicalMemoryRegistry.Clear();
RunDeferredActions();
}
}
}

View file

@ -1,4 +1,5 @@
using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.Gpu.Memory;
using System;
namespace Ryujinx.Graphics.Gpu.Image
@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Image
protected const int DescriptorSize = 0x20;
protected GpuContext Context;
protected PhysicalMemory PhysicalMemory;
protected T1[] Items;
protected T2[] DescriptorCache;
@ -38,9 +40,17 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly CpuMultiRegionHandle _memoryTracking;
private readonly Action<ulong, ulong> _modifiedDelegate;
public Pool(GpuContext context, ulong address, int maximumId)
/// <summary>
/// Creates a new instance of the GPU resource pool.
/// </summary>
/// <param name="context">GPU context that the pool belongs to</param>
/// <param name="physicalMemory">Physical memory where the resource descriptors are mapped</param>
/// <param name="address">Address of the pool in physical memory</param>
/// <param name="maximumId">Maximum index of an item on the pool (inclusive)</param>
public Pool(GpuContext context, PhysicalMemory physicalMemory, ulong address, int maximumId)
{
Context = context;
PhysicalMemory = physicalMemory;
MaximumId = maximumId;
int count = maximumId + 1;
@ -53,11 +63,10 @@ namespace Ryujinx.Graphics.Gpu.Image
Address = address;
Size = size;
_memoryTracking = context.PhysicalMemory.BeginGranularTracking(address, size);
_memoryTracking = physicalMemory.BeginGranularTracking(address, size);
_modifiedDelegate = RegionModified;
}
/// <summary>
/// Gets the descriptor for a given ID.
/// </summary>
@ -65,7 +74,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>The descriptor</returns>
public T2 GetDescriptor(int id)
{
return Context.PhysicalMemory.Read<T2>(Address + (ulong)id * DescriptorSize);
return PhysicalMemory.Read<T2>(Address + (ulong)id * DescriptorSize);
}
/// <summary>

View file

@ -1,3 +1,5 @@
using Ryujinx.Graphics.Gpu.Memory;
namespace Ryujinx.Graphics.Gpu.Image
{
/// <summary>
@ -11,9 +13,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Constructs a new instance of the sampler pool.
/// </summary>
/// <param name="context">GPU context that the sampler pool belongs to</param>
/// <param name="physicalMemory">Physical memory where the sampler descriptors are mapped</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>
public SamplerPool(GpuContext context, ulong address, int maximumId) : base(context, address, maximumId) { }
public SamplerPool(GpuContext context, PhysicalMemory physicalMemory, ulong address, int maximumId) : base(context, physicalMemory, address, maximumId) { }
/// <summary>
/// Gets the sampler with the given ID.

View file

@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Gpu.Image
}
private GpuContext _context;
private PhysicalMemory _physicalMemory;
private SizeInfo _sizeInfo;
@ -139,6 +140,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Constructs a new instance of the cached GPU texture.
/// </summary>
/// <param name="context">GPU context that the texture belongs to</param>
/// <param name="physicalMemory">Physical memory where the texture is mapped</param>
/// <param name="info">Texture information</param>
/// <param name="sizeInfo">Size information of the texture</param>
/// <param name="range">Physical memory ranges where the texture data is located</param>
@ -148,6 +150,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="scaleMode">The scale mode to initialize with</param>
private Texture(
GpuContext context,
PhysicalMemory physicalMemory,
TextureInfo info,
SizeInfo sizeInfo,
MultiRange range,
@ -156,7 +159,7 @@ namespace Ryujinx.Graphics.Gpu.Image
float scaleFactor,
TextureScaleMode scaleMode)
{
InitializeTexture(context, info, sizeInfo, range);
InitializeTexture(context, physicalMemory, info, sizeInfo, range);
FirstLayer = firstLayer;
FirstLevel = firstLevel;
@ -171,16 +174,23 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Constructs a new instance of the cached GPU texture.
/// </summary>
/// <param name="context">GPU context that the texture belongs to</param>
/// <param name="physicalMemory">Physical memory where the texture is mapped</param>
/// <param name="info">Texture information</param>
/// <param name="sizeInfo">Size information of the texture</param>
/// <param name="range">Physical memory ranges where the texture data is located</param>
/// <param name="scaleMode">The scale mode to initialize with. If scaled, the texture's data is loaded immediately and scaled up</param>
public Texture(GpuContext context, TextureInfo info, SizeInfo sizeInfo, MultiRange range, TextureScaleMode scaleMode)
public Texture(
GpuContext context,
PhysicalMemory physicalMemory,
TextureInfo info,
SizeInfo sizeInfo,
MultiRange range,
TextureScaleMode scaleMode)
{
ScaleFactor = 1f; // Texture is first loaded at scale 1x.
ScaleMode = scaleMode;
InitializeTexture(context, info, sizeInfo, range);
InitializeTexture(context, physicalMemory, info, sizeInfo, range);
}
/// <summary>
@ -189,12 +199,19 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Other fields are initialized with their default values.
/// </summary>
/// <param name="context">GPU context that the texture belongs to</param>
/// <param name="physicalMemory">Physical memory where the texture is mapped</param>
/// <param name="info">Texture information</param>
/// <param name="sizeInfo">Size information of the texture</param>
/// <param name="range">Physical memory ranges where the texture data is located</param>
private void InitializeTexture(GpuContext context, TextureInfo info, SizeInfo sizeInfo, MultiRange range)
private void InitializeTexture(
GpuContext context,
PhysicalMemory physicalMemory,
TextureInfo info,
SizeInfo sizeInfo,
MultiRange range)
{
_context = context;
_physicalMemory = physicalMemory;
_sizeInfo = sizeInfo;
Range = range;
@ -255,7 +272,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="hasMipViews">True if the texture will have mip views</param>
public void InitializeGroup(bool hasLayerViews, bool hasMipViews)
{
Group = new TextureGroup(_context, this);
Group = new TextureGroup(_context, _physicalMemory, this);
Group.Initialize(ref _sizeInfo, hasLayerViews, hasMipViews);
}
@ -276,6 +293,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
Texture texture = new Texture(
_context,
_physicalMemory,
info,
sizeInfo,
range,
@ -638,7 +656,7 @@ namespace Ryujinx.Graphics.Gpu.Image
BlacklistScale();
}
ReadOnlySpan<byte> data = _context.PhysicalMemory.GetSpan(Range);
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Range);
IsModified = false;
@ -805,11 +823,11 @@ namespace Ryujinx.Graphics.Gpu.Image
if (tracked)
{
_context.PhysicalMemory.Write(Range, GetTextureDataFromGpu(tracked));
_physicalMemory.Write(Range, GetTextureDataFromGpu(tracked));
}
else
{
_context.PhysicalMemory.WriteUntracked(Range, GetTextureDataFromGpu(tracked));
_physicalMemory.WriteUntracked(Range, GetTextureDataFromGpu(tracked));
}
}
@ -846,7 +864,7 @@ namespace Ryujinx.Graphics.Gpu.Image
texture = _flushHostTexture = GetScaledHostTexture(1f, _flushHostTexture);
}
_context.PhysicalMemory.WriteUntracked(Range, GetTextureDataFromGpu(false, texture));
_physicalMemory.WriteUntracked(Range, GetTextureDataFromGpu(false, texture));
});
}
@ -1280,7 +1298,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_viewStorage.RemoveView(this);
}
_context.Methods.TextureCache.RemoveTextureFromCache(this);
_physicalMemory.TextureCache.RemoveTextureFromCache(this);
}
Debug.Assert(newRefCount >= 0);

View file

@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="samplerIndex">Type of the sampler pool indexing used for bound samplers</param>
public void SetSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
{
ulong address = _context.MemoryManager.Translate(gpuVa);
ulong address = _channel.MemoryManager.Translate(gpuVa);
if (_samplerPool != null)
{
@ -131,7 +131,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_samplerPool.Dispose();
}
_samplerPool = new SamplerPool(_context, address, maximumId);
_samplerPool = new SamplerPool(_context, _channel.MemoryManager.Physical, address, maximumId);
_samplerIndex = samplerIndex;
}
@ -142,7 +142,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="maximumId">Maximum ID of the pool (total count minus one)</param>
public void SetTexturePool(ulong gpuVa, int maximumId)
{
ulong address = _context.MemoryManager.Translate(gpuVa);
ulong address = _channel.MemoryManager.Translate(gpuVa);
_texturePoolAddress = address;
_texturePoolMaximumId = maximumId;
@ -228,6 +228,7 @@ namespace Ryujinx.Graphics.Gpu.Image
public void CommitBindings()
{
TexturePool texturePool = _texturePoolCache.FindOrCreate(
_channel,
_texturePoolAddress,
_texturePoolMaximumId);
@ -437,9 +438,9 @@ namespace Ryujinx.Graphics.Gpu.Image
var poolState = state.Get<PoolState>(MethodOffset.TexturePoolState);
ulong poolAddress = _context.MemoryManager.Translate(poolState.Address.Pack());
ulong poolAddress = _channel.MemoryManager.Translate(poolState.Address.Pack());
TexturePool texturePool = _texturePoolCache.FindOrCreate(poolAddress, poolState.MaximumId);
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, poolState.MaximumId);
return texturePool.GetDescriptor(textureId);
}
@ -455,12 +456,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>The packed texture and sampler ID (the real texture handle)</returns>
private int ReadPackedId(int stageIndex, int wordOffset, int textureBufferIndex, int samplerBufferIndex)
{
var bufferManager = _context.Methods.BufferCache;
ulong textureBufferAddress = _isCompute
? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex);
int handle = _context.PhysicalMemory.Read<int>(textureBufferAddress + (ulong)(wordOffset & HandleMask) * 4);
int handle = _channel.MemoryManager.Physical.Read<int>(textureBufferAddress + (ulong)(wordOffset & HandleMask) * 4);
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
// is a 13-bit value. However, in order to also support separate samplers and textures (which uses
@ -474,7 +474,7 @@ namespace Ryujinx.Graphics.Gpu.Image
? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
handle |= _context.PhysicalMemory.Read<int>(samplerBufferAddress + (ulong)((wordOffset >> HandleHigh) - 1) * 4);
handle |= _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (ulong)((wordOffset >> HandleHigh) - 1) * 4);
}
return handle;
@ -514,7 +514,6 @@ namespace Ryujinx.Graphics.Gpu.Image
public void Dispose()
{
_samplerPool?.Dispose();
_texturePoolCache.Dispose();
}
}
}

View file

@ -32,6 +32,7 @@ namespace Ryujinx.Graphics.Gpu.Image
private const int OverlapsBufferMaxCapacity = 10000;
private readonly GpuContext _context;
private readonly PhysicalMemory _physicalMemory;
private readonly MultiRangeList<Texture> _textures;
@ -44,9 +45,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Constructs a new instance of the texture manager.
/// </summary>
/// <param name="context">The GPU context that the texture manager belongs to</param>
public TextureCache(GpuContext context)
/// <param name="physicalMemory">Physical memory where the textures managed by this cache are mapped</param>
public TextureCache(GpuContext context, PhysicalMemory physicalMemory)
{
_context = context;
_physicalMemory = physicalMemory;
_textures = new MultiRangeList<Texture>();
@ -68,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Image
lock (_textures)
{
overlapCount = _textures.FindOverlaps(_context.MemoryManager.Translate(e.Address), e.Size, ref overlaps);
overlapCount = _textures.FindOverlaps(((MemoryManager)sender).Translate(e.Address), e.Size, ref overlaps);
}
for (int i = 0; i < overlapCount; i++)
@ -139,13 +142,20 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary>
/// Tries to find an existing texture, or create a new one if not found.
/// </summary>
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
/// <param name="copyTexture">Copy texture to find or create</param>
/// <param name="offset">Offset to be added to the physical texture address</param>
/// <param name="formatInfo">Format information of the copy texture</param>
/// <param name="preferScaling">Indicates if the texture should be scaled from the start</param>
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
/// <returns>The texture</returns>
public Texture FindOrCreateTexture(CopyTexture copyTexture, ulong offset, FormatInfo formatInfo, bool preferScaling = true, Size? sizeHint = null)
public Texture FindOrCreateTexture(
MemoryManager memoryManager,
CopyTexture copyTexture,
ulong offset,
FormatInfo formatInfo,
bool preferScaling = true,
Size? sizeHint = null)
{
int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
@ -184,7 +194,7 @@ namespace Ryujinx.Graphics.Gpu.Image
flags |= TextureSearchFlags.WithUpscale;
}
Texture texture = FindOrCreateTexture(flags, info, 0, sizeHint);
Texture texture = FindOrCreateTexture(memoryManager, flags, info, 0, sizeHint);
texture?.SynchronizeMemory();
@ -194,12 +204,13 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary>
/// Tries to find an existing texture, or create a new one if not found.
/// </summary>
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
/// <param name="colorState">Color buffer texture to find or create</param>
/// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
/// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
/// <returns>The texture</returns>
public Texture FindOrCreateTexture(RtColorState colorState, int samplesInX, int samplesInY, Size sizeHint)
public Texture FindOrCreateTexture(MemoryManager memoryManager, RtColorState colorState, int samplesInX, int samplesInY, Size sizeHint)
{
bool isLinear = colorState.MemoryLayout.UnpackIsLinear();
@ -263,7 +274,7 @@ namespace Ryujinx.Graphics.Gpu.Image
int layerSize = !isLinear ? colorState.LayerSize * 4 : 0;
Texture texture = FindOrCreateTexture(TextureSearchFlags.WithUpscale, info, layerSize, sizeHint);
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, layerSize, sizeHint);
texture?.SynchronizeMemory();
@ -273,13 +284,20 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary>
/// Tries to find an existing texture, or create a new one if not found.
/// </summary>
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
/// <param name="dsState">Depth-stencil buffer texture to find or create</param>
/// <param name="size">Size of the depth-stencil texture</param>
/// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
/// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
/// <returns>The texture</returns>
public Texture FindOrCreateTexture(RtDepthStencilState dsState, Size3D size, int samplesInX, int samplesInY, Size sizeHint)
public Texture FindOrCreateTexture(
MemoryManager memoryManager,
RtDepthStencilState dsState,
Size3D size,
int samplesInX,
int samplesInY,
Size sizeHint)
{
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
@ -306,7 +324,7 @@ namespace Ryujinx.Graphics.Gpu.Image
target,
formatInfo);
Texture texture = FindOrCreateTexture(TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4, sizeHint);
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4, sizeHint);
texture?.SynchronizeMemory();
@ -316,13 +334,20 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary>
/// Tries to find an existing texture, or create a new one if not found.
/// </summary>
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
/// <param name="flags">The texture search flags, defines texture comparison rules</param>
/// <param name="info">Texture information of the texture to be found or created</param>
/// <param name="layerSize">Size in bytes of a single texture layer</param>
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
/// <param name="range">Optional ranges of physical memory where the texture data is located</param>
/// <returns>The texture</returns>
public Texture FindOrCreateTexture(TextureSearchFlags flags, TextureInfo info, int layerSize = 0, Size? sizeHint = null, MultiRange? range = null)
public Texture FindOrCreateTexture(
MemoryManager memoryManager,
TextureSearchFlags flags,
TextureInfo info,
int layerSize = 0,
Size? sizeHint = null,
MultiRange? range = null)
{
bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
@ -342,7 +367,7 @@ namespace Ryujinx.Graphics.Gpu.Image
}
else
{
address = _context.MemoryManager.Translate(info.GpuAddress);
address = memoryManager.Translate(info.GpuAddress);
if (address == MemoryManager.PteUnmapped)
{
@ -371,24 +396,28 @@ namespace Ryujinx.Graphics.Gpu.Image
if (matchQuality != TextureMatchQuality.NoMatch)
{
// If the parameters match, we need to make sure the texture is mapped to the same memory regions.
if (range != null)
{
// If a range of memory was supplied, just check if the ranges match.
if (range != null && !overlap.Range.Equals(range.Value))
if (!overlap.Range.Equals(range.Value))
{
continue;
}
}
else
{
// If no range was supplied, we can check if the GPU virtual address match. If they do,
// we know the textures are located at the same memory region.
// If they don't, it may still be mapped to the same physical region, so we
// do a more expensive check to tell if they are mapped into the same physical regions.
// If the GPU VA for the texture has ever been unmapped, then the range must be checked regardless.
if ((overlap.Info.GpuAddress != info.GpuAddress || overlap.ChangedMapping) &&
!_context.MemoryManager.CompareRange(overlap.Range, info.GpuAddress))
!memoryManager.CompareRange(overlap.Range, info.GpuAddress))
{
continue;
}
}
}
if (matchQuality == TextureMatchQuality.Perfect)
{
@ -426,7 +455,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (range == null)
{
range = _context.MemoryManager.GetPhysicalRegions(info.GpuAddress, size);
range = memoryManager.GetPhysicalRegions(info.GpuAddress, size);
}
// Find view compatible matches.
@ -495,7 +524,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{
// Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead.
texture = new Texture(_context, info, sizeInfo, range.Value, scaleMode);
texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);
texture.InitializeGroup(true, true);
texture.InitializeData(false, false);
@ -531,7 +560,7 @@ namespace Ryujinx.Graphics.Gpu.Image
// No match, create a new texture.
if (texture == null)
{
texture = new Texture(_context, info, sizeInfo, range.Value, scaleMode);
texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);
// Step 1: Find textures that are view compatible with the new texture.
// Any textures that are incompatible will contain garbage data, so they should be removed where possible.
@ -722,14 +751,15 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary>
/// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null.
/// </summary>
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
/// <param name="tex">The texture information</param>
/// <param name="cbp">The copy buffer parameters</param>
/// <param name="swizzle">The copy buffer swizzle</param>
/// <param name="linear">True if the texture has a linear layout, false otherwise</param>
/// <returns>A matching texture, or null if there is no match</returns>
public Texture FindTexture(CopyBufferTexture tex, CopyBufferParams cbp, CopyBufferSwizzle swizzle, bool linear)
public Texture FindTexture(MemoryManager memoryManager, CopyBufferTexture tex, CopyBufferParams cbp, CopyBufferSwizzle swizzle, bool linear)
{
ulong address = _context.MemoryManager.Translate(cbp.DstAddress.Pack());
ulong address = memoryManager.Translate(cbp.DstAddress.Pack());
if (address == MemoryManager.PteUnmapped)
{

View file

@ -1,5 +1,6 @@
using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Texture;
using Ryujinx.Memory.Range;
using System;
@ -28,7 +29,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
public bool HasCopyDependencies { get; set; }
private GpuContext _context;
private readonly GpuContext _context;
private readonly PhysicalMemory _physicalMemory;
private int[] _allOffsets;
private int[] _sliceSizes;
@ -51,11 +53,13 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Create a new texture group.
/// </summary>
/// <param name="context">GPU context that the texture group belongs to</param>
/// <param name="physicalMemory">Physical memory where the <paramref name="storage"/> texture is mapped</param>
/// <param name="storage">The storage texture for this group</param>
public TextureGroup(GpuContext context, Texture storage)
public TextureGroup(GpuContext context, PhysicalMemory physicalMemory, Texture storage)
{
Storage = storage;
_context = context;
_physicalMemory = physicalMemory;
_is3D = storage.Info.Target == Target.Texture3D;
_layers = storage.Info.GetSlices();
@ -211,7 +215,7 @@ namespace Ryujinx.Graphics.Gpu.Image
int endOffset = (offsetIndex + 1 == _allOffsets.Length) ? (int)Storage.Size : _allOffsets[offsetIndex + 1];
int size = endOffset - offset;
ReadOnlySpan<byte> data = _context.PhysicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
data = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true);
@ -561,7 +565,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>A CpuRegionHandle covering the given range</returns>
private CpuRegionHandle GenerateHandle(ulong address, ulong size)
{
return _context.PhysicalMemory.BeginTracking(address, size);
return _physicalMemory.BeginTracking(address, size);
}
/// <summary>

View file

@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Gpu.Image
class TexturePool : Pool<Texture, TextureDescriptor>
{
private int _sequenceNumber;
private readonly GpuChannel _channel;
private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>();
/// <summary>
@ -24,9 +25,13 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Constructs a new instance of the texture pool.
/// </summary>
/// <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="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>
public TexturePool(GpuContext context, ulong address, int maximumId) : base(context, address, maximumId) { }
public TexturePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId)
{
_channel = channel;
}
/// <summary>
/// Gets the texture with the given ID.
@ -57,7 +62,7 @@ namespace Ryujinx.Graphics.Gpu.Image
ProcessDereferenceQueue();
texture = Context.Methods.TextureCache.FindOrCreateTexture(TextureSearchFlags.ForSampler, info, layerSize);
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
// If this happens, then the texture address is invalid, we can't add it to the cache.
if (texture == null)
@ -148,7 +153,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (texture != null)
{
TextureDescriptor descriptor = Context.PhysicalMemory.Read<TextureDescriptor>(address);
TextureDescriptor descriptor = PhysicalMemory.Read<TextureDescriptor>(address);
// If the descriptors are the same, the texture is the same,
// we don't need to remove as it was not modified. Just continue.
@ -214,7 +219,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
{
if (Context.MemoryManager.IsMapped(gpuVa) && (int)format > 0)
if (gpuVa != 0 && (int)format > 0)
{
Logger.Error?.Print(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb}).");
}

View file

@ -8,13 +8,12 @@ namespace Ryujinx.Graphics.Gpu.Image
/// This can keep multiple texture pools, and return the current one as needed.
/// It is useful for applications that uses multiple texture pools.
/// </summary>
class TexturePoolCache : IDisposable
class TexturePoolCache
{
private const int MaxCapacity = 4;
private GpuContext _context;
private LinkedList<TexturePool> _pools;
private readonly GpuContext _context;
private readonly LinkedList<TexturePool> _pools;
/// <summary>
/// Constructs a new instance of the texture pool.
@ -23,17 +22,17 @@ namespace Ryujinx.Graphics.Gpu.Image
public TexturePoolCache(GpuContext context)
{
_context = context;
_pools = new LinkedList<TexturePool>();
}
/// <summary>
/// Finds a cache texture pool, or creates a new one if not found.
/// </summary>
/// <param name="channel">GPU channel that the texture pool cache belongs to</param>
/// <param name="address">Start address of the texture pool</param>
/// <param name="maximumId">Maximum ID of the texture pool</param>
/// <returns>The found or newly created texture pool</returns>
public TexturePool FindOrCreate(ulong address, int maximumId)
public TexturePool FindOrCreate(GpuChannel channel, ulong address, int maximumId)
{
TexturePool pool;
@ -56,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Image
}
// If not found, create a new one.
pool = new TexturePool(_context, address, maximumId);
pool = new TexturePool(_context, channel, address, maximumId);
pool.CacheNode = _pools.AddLast(pool);
@ -73,19 +72,5 @@ namespace Ryujinx.Graphics.Gpu.Image
return pool;
}
/// <summary>
/// Disposes the texture pool cache.
/// It's an error to use the texture pool cache after disposal.
/// </summary>
public void Dispose()
{
foreach (TexturePool pool in _pools)
{
pool.Dispose();
}
_pools.Clear();
}
}
}

View file

@ -13,9 +13,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
class Buffer : IRange, IDisposable
{
private static ulong GranularBufferThreshold = 4096;
private const ulong GranularBufferThreshold = 4096;
private readonly GpuContext _context;
private readonly PhysicalMemory _physicalMemory;
/// <summary>
/// Host buffer handle.
@ -68,12 +69,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Creates a new instance of the buffer.
/// </summary>
/// <param name="context">GPU context that the buffer belongs to</param>
/// <param name="physicalMemory">Physical memory where the buffer is mapped</param>
/// <param name="address">Start address of the buffer</param>
/// <param name="size">Size of the buffer in bytes</param>
/// <param name="baseBuffers">Buffers which this buffer contains, and will inherit tracking handles from</param>
public Buffer(GpuContext context, ulong address, ulong size, IEnumerable<Buffer> baseBuffers = null)
public Buffer(GpuContext context, PhysicalMemory physicalMemory, ulong address, ulong size, IEnumerable<Buffer> baseBuffers = null)
{
_context = context;
_physicalMemory = physicalMemory;
Address = address;
Size = size;
@ -100,11 +103,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (_useGranular)
{
_memoryTrackingGranular = context.PhysicalMemory.BeginGranularTracking(address, size, baseHandles);
_memoryTrackingGranular = physicalMemory.BeginGranularTracking(address, size, baseHandles);
}
else
{
_memoryTracking = context.PhysicalMemory.BeginTracking(address, size);
_memoryTracking = physicalMemory.BeginTracking(address, size);
if (baseHandles != null)
{
@ -207,7 +210,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
else
{
_context.Renderer.SetBufferData(Handle, 0, _context.PhysicalMemory.GetSpan(Address, (int)Size));
_context.Renderer.SetBufferData(Handle, 0, _physicalMemory.GetSpan(Address, (int)Size));
}
_sequenceNumber = _context.SequenceNumber;
@ -363,7 +366,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
int offset = (int)(mAddress - Address);
_context.Renderer.SetBufferData(Handle, offset, _context.PhysicalMemory.GetSpan(mAddress, (int)mSize));
_context.Renderer.SetBufferData(Handle, offset, _physicalMemory.GetSpan(mAddress, (int)mSize));
}
/// <summary>
@ -412,7 +415,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
byte[] data = _context.Renderer.GetBufferData(Handle, offset, (int)size);
// TODO: When write tracking shaders, they will need to be aware of changes in overlapping buffers.
_context.PhysicalMemory.WriteUntracked(address, data);
_physicalMemory.WriteUntracked(address, data);
}
/// <summary>

View file

@ -18,7 +18,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
private const ulong BufferAlignmentSize = 0x1000;
private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
private GpuContext _context;
private readonly GpuContext _context;
private readonly PhysicalMemory _physicalMemory;
private readonly RangeList<Buffer> _buffers;
@ -32,9 +33,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Creates a new instance of the buffer manager.
/// </summary>
/// <param name="context">The GPU context that the buffer manager belongs to</param>
public BufferCache(GpuContext context)
/// <param name="physicalMemory">Physical memory where the cached buffers are mapped</param>
public BufferCache(GpuContext context, PhysicalMemory physicalMemory)
{
_context = context;
_physicalMemory = physicalMemory;
_buffers = new RangeList<Buffer>();
@ -53,7 +56,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
Buffer[] overlaps = new Buffer[10];
int overlapCount;
ulong address = _context.MemoryManager.Translate(e.Address);
ulong address = ((MemoryManager)sender).Translate(e.Address);
ulong size = e.Size;
lock (_buffers)
@ -71,17 +74,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Performs address translation of the GPU virtual address, and creates a
/// new buffer, if needed, for the specified range.
/// </summary>
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
/// <param name="size">Size in bytes of the buffer</param>
/// <returns>CPU virtual address of the buffer, after address translation</returns>
public ulong TranslateAndCreateBuffer(ulong gpuVa, ulong size)
public ulong TranslateAndCreateBuffer(MemoryManager memoryManager, ulong gpuVa, ulong size)
{
if (gpuVa == 0)
{
return 0;
}
ulong address = _context.MemoryManager.Translate(gpuVa);
ulong address = memoryManager.Translate(gpuVa);
if (address == MemoryManager.PteUnmapped)
{
@ -122,15 +126,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// The buffer lookup for this function is cached in a dictionary for quick access, which
/// accelerates common UBO updates.
/// </summary>
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
/// <param name="size">Size in bytes of the buffer</param>
public void ForceDirty(ulong gpuVa, ulong size)
public void ForceDirty(MemoryManager memoryManager, ulong gpuVa, ulong size)
{
BufferCacheEntry result;
if (!_dirtyCache.TryGetValue(gpuVa, out result) || result.EndGpuAddress < gpuVa + size || result.UnmappedSequence != result.Buffer.UnmappedSequence)
if (!_dirtyCache.TryGetValue(gpuVa, out BufferCacheEntry result) ||
result.EndGpuAddress < gpuVa + size ||
result.UnmappedSequence != result.Buffer.UnmappedSequence)
{
ulong address = TranslateAndCreateBuffer(gpuVa, size);
ulong address = TranslateAndCreateBuffer(memoryManager, gpuVa, size);
result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size));
_dirtyCache[gpuVa] = result;
@ -179,7 +184,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
}
Buffer newBuffer = new Buffer(_context, address, endAddress - address, _bufferOverlaps.Take(overlapsCount));
Buffer newBuffer = new Buffer(_context, _physicalMemory, address, endAddress - address, _bufferOverlaps.Take(overlapsCount));
lock (_buffers)
{
@ -207,7 +212,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
else
{
// No overlap, just create a new buffer.
Buffer buffer = new Buffer(_context, address, size);
Buffer buffer = new Buffer(_context, _physicalMemory, address, size);
lock (_buffers)
{
@ -235,13 +240,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <remarks>
/// This does a GPU side copy.
/// </remarks>
/// <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="dstVa">GPU virtual address of the copy destination</param>
/// <param name="size">Size in bytes of the copy</param>
public void CopyBuffer(GpuVa srcVa, GpuVa dstVa, ulong size)
public void CopyBuffer(MemoryManager memoryManager, GpuVa srcVa, GpuVa dstVa, ulong size)
{
ulong srcAddress = TranslateAndCreateBuffer(srcVa.Pack(), size);
ulong dstAddress = TranslateAndCreateBuffer(dstVa.Pack(), size);
ulong srcAddress = TranslateAndCreateBuffer(memoryManager, srcVa.Pack(), size);
ulong dstAddress = TranslateAndCreateBuffer(memoryManager, dstVa.Pack(), size);
Buffer srcBuffer = GetBuffer(srcAddress, size);
Buffer dstBuffer = GetBuffer(dstAddress, size);
@ -265,7 +271,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.
dstBuffer.ClearModified(dstAddress, size);
_context.PhysicalMemory.WriteUntracked(dstAddress, _context.PhysicalMemory.GetSpan(srcAddress, (int)size));
memoryManager.Physical.WriteUntracked(dstAddress, memoryManager.Physical.GetSpan(srcAddress, (int)size));
}
}
@ -275,12 +281,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <remarks>
/// Both the address and size must be aligned to 4 bytes.
/// </remarks>
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
/// <param name="gpuVa">GPU virtual address of the region to clear</param>
/// <param name="size">Number of bytes to clear</param>
/// <param name="value">Value to be written into the buffer</param>
public void ClearBuffer(GpuVa gpuVa, ulong size, uint value)
public void ClearBuffer(MemoryManager memoryManager, GpuVa gpuVa, ulong size, uint value)
{
ulong address = TranslateAndCreateBuffer(gpuVa.Pack(), size);
ulong address = TranslateAndCreateBuffer(memoryManager, gpuVa.Pack(), size);
Buffer buffer = GetBuffer(address, size);

View file

@ -12,11 +12,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Buffer manager.
/// </summary>
class BufferManager : IDisposable
class BufferManager
{
private const int StackToHeapThreshold = 16;
private readonly GpuContext _context;
private readonly GpuChannel _channel;
private IndexBuffer _indexBuffer;
private readonly VertexBuffer[] _vertexBuffers;
@ -106,9 +107,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Creates a new instance of the buffer manager.
/// </summary>
/// <param name="context">GPU context that the buffer manager belongs to</param>
public BufferManager(GpuContext context)
/// <param name="channel">GPU channel that the buffer manager belongs to</param>
public BufferManager(GpuContext context, GpuChannel channel)
{
_context = context;
_channel = channel;
_vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
@ -127,8 +130,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
_bufferTextures = new List<BufferTextureBinding>();
context.Methods.BufferCache.NotifyBuffersModified += Rebind;
}
@ -140,7 +141,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="type">Type of each index buffer element</param>
public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
{
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
_indexBuffer.Address = address;
_indexBuffer.Size = size;
@ -171,7 +172,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <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)
{
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
_vertexBuffers[index].Address = address;
_vertexBuffers[index].Size = size;
@ -199,7 +200,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the transform feedback buffer</param>
public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
{
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
_transformFeedbackBuffers[index] = new BufferBounds(address, size);
_transformFeedbackBuffersDirty = true;
@ -219,7 +220,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
_cpStorageBuffers.SetBounds(index, address, size, flags);
}
@ -239,7 +240,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
if (_gpStorageBuffers[stage].Buffers[index].Address != address ||
_gpStorageBuffers[stage].Buffers[index].Size != size)
@ -259,7 +260,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the storage buffer</param>
public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
{
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
_cpUniformBuffers.SetBounds(index, address, size);
}
@ -274,7 +275,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="size">Size in bytes of the storage buffer</param>
public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
{
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
_gpUniformBuffers[stage].SetBounds(index, address, size);
_gpUniformBuffersDirty = true;
@ -422,7 +423,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
// The storage buffer size is not reliable (it might be lower than the actual size),
// so we bind the entire buffer to allow otherwise out of range accesses to work.
sRanges[bindingInfo.Binding] = _context.Methods.BufferCache.GetBufferRangeTillEnd(
sRanges[bindingInfo.Binding] = _channel.MemoryManager.Physical.BufferCache.GetBufferRangeTillEnd(
bounds.Address,
bounds.Size,
bounds.Flags.HasFlag(BufferUsageFlags.Write));
@ -443,7 +444,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (bounds.Address != 0)
{
uRanges[bindingInfo.Binding] = _context.Methods.BufferCache.GetBufferRange(bounds.Address, bounds.Size);
uRanges[bindingInfo.Binding] = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(bounds.Address, bounds.Size);
}
}
@ -465,7 +466,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
foreach (var binding in _bufferTextures)
{
var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
var range = _context.Methods.BufferCache.GetBufferRange(binding.Address, binding.Size, isStore);
var range = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(binding.Address, binding.Size, isStore);
binding.Texture.SetStorage(range);
// The texture must be rebound to use the new storage if it was updated.
@ -496,14 +497,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (_indexBuffer.Address != 0)
{
BufferRange buffer = _context.Methods.BufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
BufferRange buffer = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
_context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
}
}
else if (_indexBuffer.Address != 0)
{
_context.Methods.BufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
}
uint vbEnableMask = _vertexBuffersEnableMask;
@ -523,7 +524,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
BufferRange buffer = _context.Methods.BufferCache.GetBufferRange(vb.Address, vb.Size);
BufferRange buffer = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(vb.Address, vb.Size);
vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
}
@ -541,7 +542,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
_context.Methods.BufferCache.SynchronizeBufferRange(vb.Address, vb.Size);
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(vb.Address, vb.Size);
}
}
@ -561,7 +562,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
tfbs[index] = _context.Methods.BufferCache.GetBufferRange(tfb.Address, tfb.Size);
tfbs[index] = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(tfb.Address, tfb.Size);
}
_context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
@ -577,7 +578,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
_context.Methods.BufferCache.SynchronizeBufferRange(tfb.Address, tfb.Size);
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(tfb.Address, tfb.Size);
}
}
@ -633,8 +634,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
ranges[bindingInfo.Binding] = isStorage
? _context.Methods.BufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
: _context.Methods.BufferCache.GetBufferRange(bounds.Address, bounds.Size, isWrite);
? _channel.MemoryManager.Physical.BufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
: _channel.MemoryManager.Physical.BufferCache.GetBufferRange(bounds.Address, bounds.Size, isWrite);
}
}
}
@ -670,7 +671,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue;
}
_context.Methods.BufferCache.SynchronizeBufferRange(bounds.Address, bounds.Size);
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(bounds.Address, bounds.Size);
}
}
}
@ -686,7 +687,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
public void SetBufferTextureStorage(ITexture texture, ulong address, ulong size, TextureBindingInfo bindingInfo, Format format, bool isImage)
{
_context.Methods.BufferCache.CreateBuffer(address, size);
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(address, size);
_bufferTextures.Add(new BufferTextureBinding(texture, address, size, bindingInfo, format, isImage));
}
@ -698,14 +699,5 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
_rebind = true;
}
/// <summary>
/// Disposes the buffer manager.
/// It is an error to use the buffer manager after disposal.
/// </summary>
public void Dispose()
{
_context.Methods.BufferCache.NotifyBuffersModified -= Rebind;
}
}
}

View file

@ -34,15 +34,28 @@ namespace Ryujinx.Graphics.Gpu.Memory
public event EventHandler<UnmapEventArgs> MemoryUnmapped;
private GpuContext _context;
/// <summary>
/// Physical memory where the virtual memory is mapped into.
/// </summary>
internal PhysicalMemory Physical { get; }
/// <summary>
/// Cache of GPU counters.
/// </summary>
internal CounterCache CounterCache { get; }
/// <summary>
/// Creates a new instance of the GPU memory manager.
/// </summary>
public MemoryManager(GpuContext context)
/// <param name="physicalMemory">Physical memory that this memory manager will map into</param>
internal MemoryManager(PhysicalMemory physicalMemory)
{
_context = context;
Physical = physicalMemory;
CounterCache = new CounterCache();
_pageTable = new ulong[PtLvl0Size][];
MemoryUnmapped += Physical.TextureCache.MemoryUnmappedHandler;
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
}
/// <summary>
@ -67,7 +80,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
if (IsContiguous(va, size))
{
return _context.PhysicalMemory.GetSpan(Translate(va), size, tracked);
return Physical.GetSpan(Translate(va), size, tracked);
}
else
{
@ -100,7 +113,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
_context.PhysicalMemory.GetSpan(pa, size, tracked).CopyTo(data.Slice(0, size));
Physical.GetSpan(pa, size, tracked).CopyTo(data.Slice(0, size));
offset += size;
}
@ -111,7 +124,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
size = Math.Min(data.Length - offset, (int)PageSize);
_context.PhysicalMemory.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size));
Physical.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size));
}
}
@ -125,7 +138,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
if (IsContiguous(va, size))
{
return _context.PhysicalMemory.GetWritableRegion(Translate(va), size);
return Physical.GetWritableRegion(Translate(va), size);
}
else
{
@ -155,7 +168,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param>
public void Write(ulong va, ReadOnlySpan<byte> data)
{
WriteImpl(va, data, _context.PhysicalMemory.Write);
WriteImpl(va, data, Physical.Write);
}
/// <summary>
@ -165,7 +178,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param>
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
{
WriteImpl(va, data, _context.PhysicalMemory.WriteUntracked);
WriteImpl(va, data, Physical.WriteUntracked);
}
private delegate void WriteCallback(ulong address, ReadOnlySpan<byte> data);

View file

@ -1,5 +1,7 @@
using Ryujinx.Cpu;
using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking;
@ -7,6 +9,7 @@ using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
namespace Ryujinx.Graphics.Gpu.Memory
{
@ -18,20 +21,63 @@ namespace Ryujinx.Graphics.Gpu.Memory
{
public const int PageSize = 0x1000;
private readonly GpuContext _context;
private IVirtualMemoryManagerTracked _cpuMemory;
private int _referenceCount;
/// <summary>
/// In-memory shader cache.
/// </summary>
public ShaderCache ShaderCache { get; }
/// <summary>
/// GPU buffer manager.
/// </summary>
public BufferCache BufferCache { get; }
/// <summary>
/// GPU texture manager.
/// </summary>
public TextureCache TextureCache { get; }
/// <summary>
/// Creates a new instance of the physical memory.
/// </summary>
/// <param name="context">GPU context that the physical memory belongs to</param>
/// <param name="cpuMemory">CPU memory manager of the application process</param>
public PhysicalMemory(IVirtualMemoryManagerTracked cpuMemory)
public PhysicalMemory(GpuContext context, IVirtualMemoryManagerTracked cpuMemory)
{
_context = context;
_cpuMemory = cpuMemory;
ShaderCache = new ShaderCache(context);
BufferCache = new BufferCache(context, this);
TextureCache = new TextureCache(context, this);
if (_cpuMemory is IRefCounted rc)
if (cpuMemory is IRefCounted rc)
{
rc.IncrementReferenceCount();
}
_referenceCount = 1;
}
/// <summary>
/// Increments the memory reference count.
/// </summary>
public void IncrementReferenceCount()
{
Interlocked.Increment(ref _referenceCount);
}
/// <summary>
/// Decrements the memory reference count.
/// </summary>
public void DecrementReferenceCount()
{
if (Interlocked.Decrement(ref _referenceCount) == 0 && _cpuMemory is IRefCounted rc)
{
rc.DecrementReferenceCount();
}
}
/// <summary>
@ -147,7 +193,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="range">Ranges of physical memory where the data is located</param>
/// <param name="data">Data to be written</param>
/// <param name="writeCallback">Callback method that will perform the write</param>
private void WriteImpl(MultiRange range, ReadOnlySpan<byte> data, WriteCallback writeCallback)
private static void WriteImpl(MultiRange range, ReadOnlySpan<byte> data, WriteCallback writeCallback)
{
if (range.Count == 1)
{
@ -227,12 +273,20 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary>
public void Dispose()
{
if (_cpuMemory is IRefCounted rc)
{
rc.DecrementReferenceCount();
_cpuMemory = null;
_context.DeferredActions.Enqueue(Destroy);
}
/// <summary>
/// Performs disposal of the host GPU caches with resources mapped on this physical memory.
/// This must only be called from the render thread.
/// </summary>
private void Destroy()
{
ShaderCache.Dispose();
BufferCache.Dispose();
TextureCache.Dispose();
DecrementReferenceCount();
}
}
}

View file

@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>Data at the memory location</returns>
public override T MemoryRead<T>(ulong address)
{
return _context.MemoryManager.Read<T>(address);
return _state.Channel.MemoryManager.Read<T>(address);
}
/// <summary>
@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>True if the address is mapped, false otherwise</returns>
public bool MemoryMapped(ulong address)
{
return _context.MemoryManager.IsMapped(address);
return _state.Channel.MemoryManager.IsMapped(address);
}
/// <summary>

View file

@ -1,6 +1,7 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Shader.Cache;
using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
using Ryujinx.Graphics.Gpu.State;
@ -492,7 +493,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
foreach (ShaderBundle cachedCpShader in list)
{
if (IsShaderEqual(cachedCpShader, gpuVa))
if (IsShaderEqual(state.Channel.MemoryManager, cachedCpShader, gpuVa))
{
return cachedCpShader;
}
@ -527,7 +528,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
isShaderCacheReadOnly = _cacheManager.IsReadOnly;
// Compute hash and prepare data for shader disk cache comparison.
shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(_context.MemoryManager, shaderContexts);
shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(state.Channel.MemoryManager, shaderContexts);
programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries);
}
@ -542,7 +543,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
// The shader isn't currently cached, translate it and compile it.
ShaderCodeHolder shader = TranslateShader(shaderContexts[0]);
ShaderCodeHolder shader = TranslateShader(state.Channel.MemoryManager, shaderContexts[0]);
shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, shader.Program.Code);
@ -595,7 +596,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
foreach (ShaderBundle cachedGpShaders in list)
{
if (IsShaderEqual(cachedGpShaders, addresses))
if (IsShaderEqual(state.Channel.MemoryManager, cachedGpShaders, addresses))
{
return cachedGpShaders;
}
@ -647,7 +648,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
isShaderCacheReadOnly = _cacheManager.IsReadOnly;
// Compute hash and prepare data for shader disk cache comparison.
shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(_context.MemoryManager, shaderContexts);
shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(state.Channel.MemoryManager, shaderContexts);
programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries, tfd);
}
@ -664,11 +665,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
// The shader isn't currently cached, translate it and compile it.
ShaderCodeHolder[] shaders = new ShaderCodeHolder[Constants.ShaderStages];
shaders[0] = TranslateShader(shaderContexts[1], shaderContexts[0]);
shaders[1] = TranslateShader(shaderContexts[2]);
shaders[2] = TranslateShader(shaderContexts[3]);
shaders[3] = TranslateShader(shaderContexts[4]);
shaders[4] = TranslateShader(shaderContexts[5]);
shaders[0] = TranslateShader(state.Channel.MemoryManager, shaderContexts[1], shaderContexts[0]);
shaders[1] = TranslateShader(state.Channel.MemoryManager, shaderContexts[2]);
shaders[2] = TranslateShader(state.Channel.MemoryManager, shaderContexts[3]);
shaders[3] = TranslateShader(state.Channel.MemoryManager, shaderContexts[4]);
shaders[4] = TranslateShader(state.Channel.MemoryManager, shaderContexts[5]);
List<IShader> hostShaders = new List<IShader>();
@ -724,7 +725,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary>
/// <param name="state">Current GPU state</param>
/// <returns>Four transform feedback descriptors for the enabled TFBs, or null if TFB is disabled</returns>
private TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(GpuState state)
private static TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(GpuState state)
{
bool tfEnable = state.Get<Boolean32>(MethodOffset.TfEnable);
@ -752,21 +753,23 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary>
/// Checks if compute shader code in memory is equal to the cached shader.
/// </summary>
/// <param name="memoryManager">Memory manager used to access the GPU memory where the shader is located</param>
/// <param name="cpShader">Cached compute shader</param>
/// <param name="gpuVa">GPU virtual address of the shader code in memory</param>
/// <returns>True if the code is different, false otherwise</returns>
private bool IsShaderEqual(ShaderBundle cpShader, ulong gpuVa)
private static bool IsShaderEqual(MemoryManager memoryManager, ShaderBundle cpShader, ulong gpuVa)
{
return IsShaderEqual(cpShader.Shaders[0], gpuVa);
return IsShaderEqual(memoryManager, cpShader.Shaders[0], gpuVa);
}
/// <summary>
/// Checks if graphics shader code from all stages in memory are equal to the cached shaders.
/// </summary>
/// <param name="memoryManager">Memory manager used to access the GPU memory where the shader is located</param>
/// <param name="gpShaders">Cached graphics shaders</param>
/// <param name="addresses">GPU virtual addresses of all enabled shader stages</param>
/// <returns>True if the code is different, false otherwise</returns>
private bool IsShaderEqual(ShaderBundle gpShaders, ShaderAddresses addresses)
private static bool IsShaderEqual(MemoryManager memoryManager, ShaderBundle gpShaders, ShaderAddresses addresses)
{
for (int stage = 0; stage < gpShaders.Shaders.Length; stage++)
{
@ -783,7 +786,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
case 4: gpuVa = addresses.Fragment; break;
}
if (!IsShaderEqual(shader, gpuVa, addresses.VertexA))
if (!IsShaderEqual(memoryManager, shader, gpuVa, addresses.VertexA))
{
return false;
}
@ -795,24 +798,25 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary>
/// Checks if the code of the specified cached shader is different from the code in memory.
/// </summary>
/// <param name="memoryManager">Memory manager used to access the GPU memory where the shader is located</param>
/// <param name="shader">Cached shader to compare with</param>
/// <param name="gpuVa">GPU virtual address of the binary shader code</param>
/// <param name="gpuVaA">Optional GPU virtual address of the "Vertex A" binary shader code</param>
/// <returns>True if the code is different, false otherwise</returns>
private bool IsShaderEqual(ShaderCodeHolder shader, ulong gpuVa, ulong gpuVaA = 0)
private static bool IsShaderEqual(MemoryManager memoryManager, ShaderCodeHolder shader, ulong gpuVa, ulong gpuVaA = 0)
{
if (shader == null)
{
return true;
}
ReadOnlySpan<byte> memoryCode = _context.MemoryManager.GetSpan(gpuVa, shader.Code.Length);
ReadOnlySpan<byte> memoryCode = memoryManager.GetSpan(gpuVa, shader.Code.Length);
bool equals = memoryCode.SequenceEqual(shader.Code);
if (equals && shader.Code2 != null)
{
memoryCode = _context.MemoryManager.GetSpan(gpuVaA, shader.Code2.Length);
memoryCode = memoryManager.GetSpan(gpuVaA, shader.Code2.Length);
equals = memoryCode.SequenceEqual(shader.Code2);
}
@ -882,10 +886,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <summary>
/// Translates a previously generated translator context to something that the host API accepts.
/// </summary>
/// <param name="memoryManager">Memory manager used to access the GPU memory where the shader is located</param>
/// <param name="translatorContext">Current translator context to translate</param>
/// <param name="translatorContext2">Optional translator context of the shader that should be combined</param>
/// <returns>Compiled graphics shader code</returns>
private ShaderCodeHolder TranslateShader(TranslatorContext translatorContext, TranslatorContext translatorContext2 = null)
private ShaderCodeHolder TranslateShader(
MemoryManager memoryManager,
TranslatorContext translatorContext,
TranslatorContext translatorContext2 = null)
{
if (translatorContext == null)
{
@ -894,8 +902,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
if (translatorContext2 != null)
{
byte[] codeA = _context.MemoryManager.GetSpan(translatorContext2.Address, translatorContext2.Size).ToArray();
byte[] codeB = _context.MemoryManager.GetSpan(translatorContext.Address, translatorContext.Size).ToArray();
byte[] codeA = memoryManager.GetSpan(translatorContext2.Address, translatorContext2.Size).ToArray();
byte[] codeB = memoryManager.GetSpan(translatorContext.Address, translatorContext.Size).ToArray();
_dumper.Dump(codeA, compute: false, out string fullPathA, out string codePathA);
_dumper.Dump(codeB, compute: false, out string fullPathB, out string codePathB);
@ -914,7 +922,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
}
else
{
byte[] code = _context.MemoryManager.GetSpan(translatorContext.Address, translatorContext.Size).ToArray();
byte[] code = memoryManager.GetSpan(translatorContext.Address, translatorContext.Size).ToArray();
_dumper.Dump(code, translatorContext.Stage == ShaderStage.Compute, out string fullPath, out string codePath);

View file

@ -22,6 +22,11 @@ namespace Ryujinx.Graphics.Gpu
/// </summary>
private struct PresentationTexture
{
/// <summary>
/// Texture cache where the texture might be located.
/// </summary>
public TextureCache Cache { get; }
/// <summary>
/// Texture information.
/// </summary>
@ -55,6 +60,7 @@ namespace Ryujinx.Graphics.Gpu
/// <summary>
/// Creates a new instance of the presentation texture.
/// </summary>
/// <param name="cache">Texture cache used to look for the texture to be presented</param>
/// <param name="info">Information of the texture to be presented</param>
/// <param name="range">Physical memory locations where the texture data is located</param>
/// <param name="crop">Texture crop region</param>
@ -62,6 +68,7 @@ namespace Ryujinx.Graphics.Gpu
/// <param name="releaseCallback">Texture release callback</param>
/// <param name="userObj">User defined object passed to the release callback, can be used to identify the texture</param>
public PresentationTexture(
TextureCache cache,
TextureInfo info,
MultiRange range,
ImageCrop crop,
@ -69,6 +76,7 @@ namespace Ryujinx.Graphics.Gpu
Action<object> releaseCallback,
object userObj)
{
Cache = cache;
Info = info;
Range = range;
Crop = crop;
@ -99,6 +107,7 @@ namespace Ryujinx.Graphics.Gpu
/// When the texture is presented and not needed anymore, the release callback is called.
/// It's an error to modify the texture after calling this method, before the release callback is called.
/// </summary>
/// <param name="pid">Process ID of the process that owns the texture pointed to by <paramref name="address"/></param>
/// <param name="address">CPU virtual address of the texture data</param>
/// <param name="width">Texture width</param>
/// <param name="height">Texture height</param>
@ -111,7 +120,9 @@ namespace Ryujinx.Graphics.Gpu
/// <param name="acquireCallback">Texture acquire callback</param>
/// <param name="releaseCallback">Texture release callback</param>
/// <param name="userObj">User defined object passed to the release callback</param>
/// <exception cref="ArgumentException">Thrown when <paramref name="pid"/> is invalid</exception>
public void EnqueueFrameThreadSafe(
long pid,
ulong address,
int width,
int height,
@ -125,6 +136,11 @@ namespace Ryujinx.Graphics.Gpu
Action<object> releaseCallback,
object userObj)
{
if (!_context.PhysicalMemoryRegistry.TryGetValue(pid, out var physicalMemory))
{
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid));
}
FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel, 4);
TextureInfo info = new TextureInfo(
@ -158,7 +174,14 @@ namespace Ryujinx.Graphics.Gpu
MultiRange range = new MultiRange(address, (ulong)size);
_frameQueue.Enqueue(new PresentationTexture(info, range, crop, acquireCallback, releaseCallback, userObj));
_frameQueue.Enqueue(new PresentationTexture(
physicalMemory.TextureCache,
info,
range,
crop,
acquireCallback,
releaseCallback,
userObj));
}
/// <summary>
@ -174,7 +197,7 @@ namespace Ryujinx.Graphics.Gpu
{
pt.AcquireCallback(_context, pt.UserObj);
Texture texture = _context.Methods.TextureCache.FindOrCreateTexture(TextureSearchFlags.WithUpscale, pt.Info, 0, null, pt.Range);
Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, null, pt.Range);
texture.SynchronizeMemory();

View file

@ -1,27 +1,34 @@
using ARMeilleure.Memory;
using ARMeilleure.State;
using Ryujinx.Cpu;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.Memory;
namespace Ryujinx.HLE.HOS
{
class ArmProcessContext<T> : IProcessContext where T : class, IVirtualMemoryManager, IMemoryManager
class ArmProcessContext<T> : IProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager
{
private readonly long _pid;
private readonly GpuContext _gpuContext;
private readonly CpuContext _cpuContext;
private T _memoryManager;
public IVirtualMemoryManager AddressSpace => _memoryManager;
public ArmProcessContext(T memoryManager, bool for64Bit)
public ArmProcessContext(long pid, GpuContext gpuContext, T memoryManager, bool for64Bit)
{
if (memoryManager is IRefCounted rc)
{
rc.IncrementReferenceCount();
}
_memoryManager = memoryManager;
gpuContext.RegisterProcess(pid, memoryManager);
_pid = pid;
_gpuContext = gpuContext;
_cpuContext = new CpuContext(memoryManager, for64Bit);
_memoryManager = memoryManager;
}
public void Execute(ExecutionContext context, ulong codeAddress)
@ -36,6 +43,7 @@ namespace Ryujinx.HLE.HOS
rc.DecrementReferenceCount();
_memoryManager = null;
_gpuContext.UnregisterProcess(_pid);
}
}
}

View file

@ -1,5 +1,6 @@
using Ryujinx.Common.Configuration;
using Ryujinx.Cpu;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.Memory;
@ -9,19 +10,26 @@ namespace Ryujinx.HLE.HOS
{
class ArmProcessContextFactory : IProcessContextFactory
{
public IProcessContext Create(KernelContext context, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
private readonly GpuContext _gpu;
public ArmProcessContextFactory(GpuContext gpu)
{
_gpu = gpu;
}
public IProcessContext Create(KernelContext context, long pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
{
MemoryManagerMode mode = context.Device.Configuration.MemoryManagerMode;
switch (mode)
{
case MemoryManagerMode.SoftwarePageTable:
return new ArmProcessContext<MemoryManager>(new MemoryManager(addressSpaceSize, invalidAccessHandler), for64Bit);
return new ArmProcessContext<MemoryManager>(pid, _gpu, new MemoryManager(addressSpaceSize, invalidAccessHandler), for64Bit);
case MemoryManagerMode.HostMapped:
case MemoryManagerMode.HostMappedUnsafe:
bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe;
return new ArmProcessContext<MemoryManagerHostMapped>(new MemoryManagerHostMapped(addressSpaceSize, unsafeMode, invalidAccessHandler), for64Bit);
return new ArmProcessContext<MemoryManagerHostMapped>(pid, _gpu, new MemoryManagerHostMapped(addressSpaceSize, unsafeMode, invalidAccessHandler), for64Bit);
default:
throw new ArgumentOutOfRangeException();

View file

@ -4,6 +4,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
interface IProcessContextFactory
{
IProcessContext Create(KernelContext context, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit);
IProcessContext Create(KernelContext context, long pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit);
}
}

View file

@ -126,6 +126,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
Pid = KernelContext.NewKipId();
if (Pid == 0 || (ulong)Pid >= KernelConstants.InitialProcessId)
{
throw new InvalidOperationException($"Invalid KIP Id {Pid}.");
}
InitializeMemoryManager(creationInfo.Flags);
bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
@ -171,13 +178,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return result;
}
Pid = KernelContext.NewKipId();
if (Pid == 0 || (ulong)Pid >= KernelConstants.InitialProcessId)
{
throw new InvalidOperationException($"Invalid KIP Id {Pid}.");
}
return ParseProcessInfo(creationInfo);
}
@ -233,6 +233,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
Pid = KernelContext.NewProcessId();
if (Pid == -1 || (ulong)Pid < KernelConstants.InitialProcessId)
{
throw new InvalidOperationException($"Invalid Process Id {Pid}.");
}
InitializeMemoryManager(creationInfo.Flags);
bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
@ -286,13 +293,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return result;
}
Pid = KernelContext.NewProcessId();
if (Pid == -1 || (ulong)Pid < KernelConstants.InitialProcessId)
{
throw new InvalidOperationException($"Invalid Process Id {Pid}.");
}
result = ParseProcessInfo(creationInfo);
if (result != KernelResult.Success)
@ -1051,14 +1051,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
bool for64Bit = flags.HasFlag(ProcessCreationFlags.Is64Bit);
Context = _contextFactory.Create(KernelContext, 1UL << addrSpaceBits, InvalidAccessHandler, for64Bit);
// TODO: This should eventually be removed.
// The GPU shouldn't depend on the CPU memory manager at all.
if (flags.HasFlag(ProcessCreationFlags.IsApplication))
{
KernelContext.Device.Gpu.SetVmm((IVirtualMemoryManagerTracked)CpuMemory);
}
Context = _contextFactory.Create(KernelContext, Pid, 1UL << addrSpaceBits, InvalidAccessHandler, for64Bit);
if (Context.AddressSpace is MemoryManagerHostMapped)
{

View file

@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
class ProcessContextFactory : IProcessContextFactory
{
public IProcessContext Create(KernelContext context, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
public IProcessContext Create(KernelContext context, long pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
{
return new ProcessContext(new AddressSpaceManager(addressSpaceSize));
}

View file

@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS
KProcess process = new KProcess(context);
var processContextFactory = new ArmProcessContextFactory();
var processContextFactory = new ArmProcessContextFactory(context.Device.Gpu);
result = process.InitializeKip(
creationInfo,
@ -228,7 +228,7 @@ namespace Ryujinx.HLE.HOS
return false;
}
var processContextFactory = new ArmProcessContextFactory();
var processContextFactory = new ArmProcessContextFactory(context.Device.Gpu);
result = process.Initialize(
creationInfo,

View file

@ -0,0 +1,47 @@
using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Host1x;
using Ryujinx.Graphics.Nvdec;
using Ryujinx.Graphics.Vic;
using System;
namespace Ryujinx.HLE.HOS.Services.Nv
{
class Host1xContext : IDisposable
{
public MemoryManager Smmu { get; }
public NvMemoryAllocator MemoryAllocator { get; }
public Host1xDevice Host1x { get;}
public Host1xContext(GpuContext gpu, long pid)
{
MemoryAllocator = new NvMemoryAllocator();
Host1x = new Host1xDevice(gpu.Synchronization);
Smmu = gpu.CreateMemoryManager(pid);
var nvdec = new NvdecDevice(Smmu);
var vic = new VicDevice(Smmu);
Host1x.RegisterDevice(ClassId.Nvdec, nvdec);
Host1x.RegisterDevice(ClassId.Vic, vic);
nvdec.FrameDecoded += (FrameDecodedEventArgs e) =>
{
// FIXME:
// Figure out what is causing frame ordering issues on H264.
// For now this is needed as workaround.
if (e.CodecId == CodecId.H264)
{
vic.SetSurfaceOverride(e.LumaOffset, e.ChromaOffset, 0);
}
else
{
vic.DisableSurfaceOverride();
}
};
}
public void Dispose()
{
Host1x.Dispose();
}
}
}

View file

@ -3,7 +3,6 @@ using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel;
@ -24,8 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
[Service("nvdrv:t")]
class INvDrvServices : IpcService
{
private static Dictionary<string, Type> _deviceFileRegistry =
new Dictionary<string, Type>()
private static Dictionary<string, Type> _deviceFileRegistry = new Dictionary<string, Type>()
{
{ "/dev/nvmap", typeof(NvMapDeviceFile) },
{ "/dev/nvhost-ctrl", typeof(NvHostCtrlDeviceFile) },
@ -39,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
//{ "/dev/nvhost-display", typeof(NvHostChannelDeviceFile) },
};
private static IdDictionary _deviceFileIdRegistry = new IdDictionary();
public static IdDictionary DeviceFileIdRegistry = new IdDictionary();
private IVirtualMemoryManager _clientMemory;
private long _owner;
@ -61,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
deviceFile.Path = path;
return _deviceFileIdRegistry.Add(deviceFile);
return DeviceFileIdRegistry.Add(deviceFile);
}
else
{
@ -139,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
return NvResult.InvalidParameter;
}
deviceFile = _deviceFileIdRegistry.GetData<NvDeviceFile>(fd);
deviceFile = DeviceFileIdRegistry.GetData<NvDeviceFile>(fd);
if (deviceFile == null)
{
@ -302,7 +300,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
{
deviceFile.Close();
_deviceFileIdRegistry.Delete(fd);
DeviceFileIdRegistry.Delete(fd);
}
}
@ -569,14 +567,16 @@ namespace Ryujinx.HLE.HOS.Services.Nv
public static void Destroy()
{
foreach (object entry in _deviceFileIdRegistry.Values)
NvHostChannelDeviceFile.Destroy();
foreach (object entry in DeviceFileIdRegistry.Values)
{
NvDeviceFile deviceFile = (NvDeviceFile)entry;
deviceFile.Close();
}
_deviceFileIdRegistry.Clear();
DeviceFileIdRegistry.Clear();
}
}
}

View file

@ -1,22 +1,22 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
using Ryujinx.Memory;
using System;
using System.Collections.Concurrent;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
{
class NvHostAsGpuDeviceFile : NvDeviceFile
{
private static ConcurrentDictionary<KProcess, AddressSpaceContext> _addressSpaceContextRegistry = new ConcurrentDictionary<KProcess, AddressSpaceContext>();
private NvMemoryAllocator _memoryAllocator;
private readonly AddressSpaceContext _asContext;
private readonly NvMemoryAllocator _memoryAllocator;
public NvHostAsGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner)
{
_memoryAllocator = context.Device.MemoryAllocator;
_asContext = new AddressSpaceContext(context.Device.Gpu.CreateMemoryManager(owner));
_memoryAllocator = new NvMemoryAllocator();
}
public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments)
@ -77,20 +77,24 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
private NvInternalResult BindChannel(ref BindChannelArguments arguments)
{
Logger.Stub?.PrintStub(LogClass.ServiceNv);
var channelDeviceFile = INvDrvServices.DeviceFileIdRegistry.GetData<NvHostChannelDeviceFile>(arguments.Fd);
if (channelDeviceFile == null)
{
// TODO: Return invalid Fd error.
}
channelDeviceFile.Channel.BindMemory(_asContext.Gmm);
return NvInternalResult.Success;
}
private NvInternalResult AllocSpace(ref AllocSpaceArguments arguments)
{
AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context);
ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize;
NvInternalResult result = NvInternalResult.Success;
lock (addressSpaceContext)
lock (_asContext)
{
// Note: When the fixed offset flag is not set,
// the Offset field holds the alignment size instead.
@ -132,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
}
else
{
addressSpaceContext.AddReservation(arguments.Offset, size);
_asContext.AddReservation(arguments.Offset, size);
}
}
@ -141,18 +145,16 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
private NvInternalResult FreeSpace(ref FreeSpaceArguments arguments)
{
AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context);
ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize;
NvInternalResult result = NvInternalResult.Success;
lock (addressSpaceContext)
lock (_asContext)
{
ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize;
if (addressSpaceContext.RemoveReservation(arguments.Offset))
if (_asContext.RemoveReservation(arguments.Offset))
{
_memoryAllocator.DeallocateRange(arguments.Offset, size);
addressSpaceContext.Gmm.Unmap(arguments.Offset, size);
_asContext.Gmm.Unmap(arguments.Offset, size);
}
else
{
@ -168,16 +170,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
private NvInternalResult UnmapBuffer(ref UnmapBufferArguments arguments)
{
AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context);
lock (addressSpaceContext)
lock (_asContext)
{
if (addressSpaceContext.RemoveMap(arguments.Offset, out ulong size))
if (_asContext.RemoveMap(arguments.Offset, out ulong size))
{
if (size != 0)
{
_memoryAllocator.DeallocateRange(arguments.Offset, size);
addressSpaceContext.Gmm.Unmap(arguments.Offset, size);
_asContext.Gmm.Unmap(arguments.Offset, size);
}
}
else
@ -191,22 +191,20 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
private NvInternalResult MapBufferEx(ref MapBufferExArguments arguments)
{
const string mapErrorMsg = "Failed to map fixed buffer with offset 0x{0:x16}, size 0x{1:x16} and alignment 0x{2:x16}!";
AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context);
const string MapErrorMsg = "Failed to map fixed buffer with offset 0x{0:x16}, size 0x{1:x16} and alignment 0x{2:x16}!";
ulong physicalAddress;
if ((arguments.Flags & AddressSpaceFlags.RemapSubRange) != 0)
{
lock (addressSpaceContext)
lock (_asContext)
{
if (addressSpaceContext.TryGetMapPhysicalAddress(arguments.Offset, out physicalAddress))
if (_asContext.TryGetMapPhysicalAddress(arguments.Offset, out physicalAddress))
{
ulong virtualAddress = arguments.Offset + arguments.BufferOffset;
physicalAddress += arguments.BufferOffset;
addressSpaceContext.Gmm.Map(physicalAddress, virtualAddress, arguments.MappingSize);
_asContext.Gmm.Map(physicalAddress, virtualAddress, arguments.MappingSize);
return NvInternalResult.Success;
}
@ -246,7 +244,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
NvInternalResult result = NvInternalResult.Success;
lock (addressSpaceContext)
lock (_asContext)
{
// Note: When the fixed offset flag is not set,
// the Offset field holds the alignment size instead.
@ -254,13 +252,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
if (!virtualAddressAllocated)
{
if (addressSpaceContext.ValidateFixedBuffer(arguments.Offset, size, pageSize))
if (_asContext.ValidateFixedBuffer(arguments.Offset, size, pageSize))
{
addressSpaceContext.Gmm.Map(physicalAddress, arguments.Offset, size);
_asContext.Gmm.Map(physicalAddress, arguments.Offset, size);
}
else
{
string message = string.Format(mapErrorMsg, arguments.Offset, size, pageSize);
string message = string.Format(MapErrorMsg, arguments.Offset, size, pageSize);
Logger.Warning?.Print(LogClass.ServiceNv, message);
@ -275,7 +273,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
_memoryAllocator.AllocateRange(va, size, freeAddressStartPosition);
}
addressSpaceContext.Gmm.Map(physicalAddress, va, size);
_asContext.Gmm.Map(physicalAddress, va, size);
arguments.Offset = va;
}
@ -289,7 +287,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
}
else
{
addressSpaceContext.AddMap(arguments.Offset, size, physicalAddress, virtualAddressAllocated);
_asContext.AddMap(arguments.Offset, size, physicalAddress, virtualAddressAllocated);
}
}
@ -312,12 +310,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
private NvInternalResult Remap(Span<RemapArguments> arguments)
{
AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context);
MemoryManager gmm = _asContext.Gmm;
for (int index = 0; index < arguments.Length; index++)
{
MemoryManager gmm = GetAddressSpaceContext(Context).Gmm;
ulong mapOffs = (ulong)arguments[index].MapOffset << 16;
ulong gpuVa = (ulong)arguments[index].GpuOffset << 16;
ulong size = (ulong)arguments[index].Pages << 16;
@ -345,10 +341,5 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
}
public override void Close() { }
public static AddressSpaceContext GetAddressSpaceContext(ServiceCtx context)
{
return _addressSpaceContextRegistry.GetOrAdd(context.Process, (key) => new AddressSpaceContext(context));
}
}
}

View file

@ -34,9 +34,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types
private readonly SortedList<ulong, Range> _maps;
private readonly SortedList<ulong, Range> _reservations;
public AddressSpaceContext(ServiceCtx context)
public AddressSpaceContext(MemoryManager gmm)
{
Gmm = context.Device.Gpu.MemoryManager;
Gmm = gmm;
_maps = new SortedList<ulong, Range>();
_reservations = new SortedList<ulong, Range>();
@ -123,7 +123,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types
return _reservations.Remove(gpuVa);
}
private Range BinarySearch(SortedList<ulong, Range> list, ulong address)
private static Range BinarySearch(SortedList<ulong, Range> list, ulong address)
{
int left = 0;
int right = list.Count - 1;
@ -154,7 +154,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types
return null;
}
private Range BinarySearchLt(SortedList<ulong, Range> list, ulong address)
private static Range BinarySearchLt(SortedList<ulong, Range> list, ulong address)
{
Range ltRg = null;

View file

@ -1,13 +1,13 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
using Ryujinx.HLE.HOS.Services.Nv.Types;
using Ryujinx.Memory;
using System;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -15,6 +15,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
{
class NvHostChannelDeviceFile : NvDeviceFile
{
private static readonly ConcurrentDictionary<long, Host1xContext> _host1xContextRegistry = new();
private const uint MaxModuleSyncpoint = 16;
private uint _timeout;
@ -24,8 +26,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
private readonly Switch _device;
private readonly IVirtualMemoryManager _memory;
private readonly NvMemoryAllocator _memoryAllocator;
private readonly GpuChannel _channel;
private readonly Host1xContext _host1xContext;
public GpuChannel Channel { get; }
public enum ResourcePolicy
{
@ -48,8 +51,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
_timeout = 3000;
_submitTimeout = 0;
_timeslice = 0;
_memoryAllocator = _device.MemoryAllocator;
_channel = _device.Gpu.CreateChannel();
_host1xContext = GetHost1XContext(context.Device.Gpu, owner);
Channel = _device.Gpu.CreateChannel();
ChannelSyncpoints = new uint[MaxModuleSyncpoint];
@ -162,7 +165,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
var data = _memory.GetSpan(map.Address + commandBuffer.Offset, commandBuffer.WordsCount * 4);
_device.Host1x.Submit(MemoryMarshal.Cast<byte, int>(data));
_host1xContext.Host1x.Submit(MemoryMarshal.Cast<byte, int>(data));
}
}
@ -172,7 +175,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
tmpCmdBuff[0] = (4 << 28) | (int)fences[0].Id;
_device.Host1x.Submit(tmpCmdBuff);
_host1xContext.Host1x.Submit(tmpCmdBuff);
return NvInternalResult.Success;
}
@ -233,7 +236,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries);
MemoryManager gmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Context).Gmm;
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
{
@ -250,12 +252,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
{
if (map.DmaMapAddress == 0)
{
ulong va = _memoryAllocator.GetFreeAddress((ulong) map.Size, out ulong freeAddressStartPosition, 1, MemoryManager.PageSize);
ulong va = _host1xContext.MemoryAllocator.GetFreeAddress((ulong)map.Size, out ulong freeAddressStartPosition, 1, MemoryManager.PageSize);
if (va != NvMemoryAllocator.PteUnmapped && va <= uint.MaxValue && (va + (uint)map.Size) <= uint.MaxValue)
{
_memoryAllocator.AllocateRange(va, (uint)map.Size, freeAddressStartPosition);
gmm.Map(map.Address, va, (uint)map.Size);
_host1xContext.MemoryAllocator.AllocateRange(va, (uint)map.Size, freeAddressStartPosition);
_host1xContext.Smmu.Map(map.Address, va, (uint)map.Size);
map.DmaMapAddress = va;
}
else
@ -276,7 +278,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries);
MemoryManager gmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Context).Gmm;
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
{
@ -297,7 +298,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
// To make unmapping work, we need separate address space per channel.
// Right now NVDEC and VIC share the GPU address space which is not correct at all.
// gmm.Free((ulong)map.DmaMapAddress, (uint)map.Size);
// _host1xContext.MemoryAllocator.Free((ulong)map.DmaMapAddress, (uint)map.Size);
// map.DmaMapAddress = 0;
}
@ -431,10 +432,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceWait) && !_device.System.HostSyncpoint.IsSyncpointExpired(header.Fence.Id, header.Fence.Value))
{
_channel.PushHostCommandBuffer(CreateWaitCommandBuffer(header.Fence));
Channel.PushHostCommandBuffer(CreateWaitCommandBuffer(header.Fence));
}
_channel.PushEntries(entries);
Channel.PushEntries(entries);
header.Fence.Id = _channelSyncpoint.Id;
@ -456,7 +457,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement))
{
_channel.PushHostCommandBuffer(CreateIncrementCommandBuffer(ref header.Fence, header.Flags));
Channel.PushHostCommandBuffer(CreateIncrementCommandBuffer(ref header.Fence, header.Flags));
}
header.Flags = SubmitGpfifoFlags.None;
@ -545,7 +546,22 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
public override void Close()
{
_channel.Dispose();
Channel.Dispose();
}
private static Host1xContext GetHost1XContext(GpuContext gpu, long pid)
{
return _host1xContextRegistry.GetOrAdd(pid, (long key) => new Host1xContext(gpu, key));
}
public static void Destroy()
{
foreach (Host1xContext host1xContext in _host1xContextRegistry.Values)
{
host1xContext.Dispose();
}
_host1xContextRegistry.Clear();
}
}
}

View file

@ -4,7 +4,7 @@ using System;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Common.Logging;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices
namespace Ryujinx.HLE.HOS.Services.Nv
{
class NvMemoryAllocator
{

View file

@ -385,6 +385,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
}
_device.Gpu.Window.EnqueueFrameThreadSafe(
layer.Owner,
frameBufferAddress,
frameBufferWidth,
frameBufferHeight,

View file

@ -1,14 +1,10 @@
using Ryujinx.Audio.Backends.CompatLayer;
using Ryujinx.Audio.Integration;
using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.Host1x;
using Ryujinx.Graphics.Nvdec;
using Ryujinx.Graphics.Vic;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Apm;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices;
using Ryujinx.Memory;
using System;
@ -24,10 +20,6 @@ namespace Ryujinx.HLE
public GpuContext Gpu { get; }
internal NvMemoryAllocator MemoryAllocator { get; }
internal Host1xDevice Host1x { get; }
public VirtualFileSystem FileSystem => Configuration.VirtualFileSystem;
public Horizon System { get; }
@ -71,29 +63,6 @@ namespace Ryujinx.HLE
Gpu = new GpuContext(configuration.GpuRenderer);
MemoryAllocator = new NvMemoryAllocator();
Host1x = new Host1xDevice(Gpu.Synchronization);
var nvdec = new NvdecDevice(Gpu.MemoryManager);
var vic = new VicDevice(Gpu.MemoryManager);
Host1x.RegisterDevice(ClassId.Nvdec, nvdec);
Host1x.RegisterDevice(ClassId.Vic, vic);
nvdec.FrameDecoded += (FrameDecodedEventArgs e) =>
{
// FIXME:
// Figure out what is causing frame ordering issues on H264.
// For now this is needed as workaround.
if (e.CodecId == CodecId.H264)
{
vic.SetSurfaceOverride(e.LumaOffset, e.ChromaOffset, 0);
}
else
{
vic.DisableSurfaceOverride();
}
};
System = new Horizon(this);
System.InitializeServices();
@ -190,7 +159,6 @@ namespace Ryujinx.HLE
if (disposing)
{
System.Dispose();
Host1x.Dispose();
AudioDeviceDriver.Dispose();
FileSystem.Unload();
Memory.Dispose();