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;
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>
@ -147,16 +149,17 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="scaleFactor">The floating point scale factor to initialize with</param>
/// <param name="scaleMode">The scale mode to initialize with</param>
private Texture(
GpuContext context,
TextureInfo info,
SizeInfo sizeInfo,
MultiRange range,
int firstLayer,
int firstLevel,
float scaleFactor,
GpuContext context,
PhysicalMemory physicalMemory,
TextureInfo info,
SizeInfo sizeInfo,
MultiRange range,
int firstLayer,
int firstLevel,
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,14 +199,21 @@ 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;
_context = context;
_physicalMemory = physicalMemory;
_sizeInfo = sizeInfo;
Range = range;
Range = range;
SetInfo(info);
@ -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,22 +396,26 @@ 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 a range of memory was supplied, just check if the ranges match.
if (range != null && !overlap.Range.Equals(range.Value))
if (range != null)
{
continue;
// If a range of memory was supplied, just check if the ranges match.
if (!overlap.Range.Equals(range.Value))
{
continue;
}
}
// 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))
else
{
continue;
// 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) &&
!memoryManager.CompareRange(overlap.Range, info.GpuAddress))
{
continue;
}
}
}
@ -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,14 +69,16 @@ 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;
Address = address;
Size = size;
_context = context;
_physicalMemory = physicalMemory;
Address = address;
Size = size;
Handle = context.Renderer.CreateBuffer((int)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,9 +210,9 @@ 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();
_context.DeferredActions.Enqueue(Destroy);
}
_cpuMemory = null;
}
/// <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
{
@ -43,13 +46,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
public NvHostChannelDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner)
{
_device = context.Device;
_memory = memory;
_timeout = 3000;
_submitTimeout = 0;
_timeslice = 0;
_memoryAllocator = _device.MemoryAllocator;
_channel = _device.Gpu.CreateChannel();
_device = context.Device;
_memory = memory;
_timeout = 3000;
_submitTimeout = 0;
_timeslice = 0;
_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

@ -49,8 +49,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
private class TextureCallbackInformation
{
public Layer Layer;
public BufferItem Item;
public Layer Layer;
public BufferItem Item;
}
public SurfaceFlinger(Switch device)
@ -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();