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> /// <param name="argument">Method call argument</param>
public void Dispatch(GpuState state, int argument) public void Dispatch(GpuState state, int argument)
{ {
FlushUboDirty(); var memoryManager = state.Channel.MemoryManager;
FlushUboDirty(memoryManager);
uint qmdAddress = (uint)state.Get<int>(MethodOffset.DispatchParamsAddress); 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); GpuVa shaderBaseAddress = state.Get<GpuVa>(MethodOffset.ShaderBaseAddress);
@ -43,7 +45,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
state.Channel.BufferManager.SetComputeUniformBuffer(index, gpuVa, size); state.Channel.BufferManager.SetComputeUniformBuffer(index, gpuVa, size);
} }
ShaderBundle cs = ShaderCache.GetComputeShader( ShaderBundle cs = memoryManager.Physical.ShaderCache.GetComputeShader(
state, state,
shaderGpuVa, shaderGpuVa,
qmd.CtaThreadDimension0, qmd.CtaThreadDimension0,
@ -82,7 +84,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
cbDescAddress += (ulong)cbDescOffset; 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); state.Channel.BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size);
} }
@ -97,7 +99,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
sbDescAddress += (ulong)sbDescOffset; 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); 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. // TODO: Acquire operations (Wait), interrupts for invalid combinations.
if (operation == SemaphoredOperation.Release) if (operation == SemaphoredOperation.Release)
{ {
_context.MemoryManager.Write(address, value); _parent.MemoryManager.Write(address, value);
} }
else if (operation == SemaphoredOperation.Reduction) else if (operation == SemaphoredOperation.Reduction)
{ {
bool signed = _state.State.SemaphoredFormat == SemaphoredFormat.Signed; 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) switch (_state.State.SemaphoredReduction)
{ {
@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
break; 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.Collections.Concurrent;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -53,11 +54,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
/// <summary> /// <summary>
/// Fetch the command buffer. /// Fetch the command buffer.
/// </summary> /// </summary>
public void Fetch(GpuContext context) public void Fetch(MemoryManager memoryManager)
{ {
if (Words == null) 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) if (beforeBarrier && commandBuffer.Type == CommandBufferType.Prefetch)
{ {
commandBuffer.Fetch(_context); commandBuffer.Fetch(processor.MemoryManager);
} }
if (commandBuffer.Type == CommandBufferType.NoPrefetch) if (commandBuffer.Type == CommandBufferType.NoPrefetch)
@ -182,13 +183,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
public void DispatchCalls() public void DispatchCalls()
{ {
// Use this opportunity to also dispose any pending channels that were closed. // Use this opportunity to also dispose any pending channels that were closed.
_context.DisposePendingChannels(); _context.RunDeferredActions();
// Process command buffers. // Process command buffers.
while (_ibEnable && !_interrupt && _commandBufferQueue.TryDequeue(out CommandBuffer entry)) while (_ibEnable && !_interrupt && _commandBufferQueue.TryDequeue(out CommandBuffer entry))
{ {
_currentCommandBuffer = entry; _currentCommandBuffer = entry;
_currentCommandBuffer.Fetch(_context); _currentCommandBuffer.Fetch(entry.Processor.MemoryManager);
// If we are changing the current channel, // If we are changing the current channel,
// we need to force all the host state to be updated. // 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;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -13,6 +14,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
private const int MacroIndexMask = MacrosCount - 1; private const int MacroIndexMask = MacrosCount - 1;
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly GpuChannel _channel;
public MemoryManager MemoryManager => _channel.MemoryManager;
/// <summary> /// <summary>
/// Internal GPFIFO state. /// Internal GPFIFO state.
@ -39,6 +43,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
public GPFifoProcessor(GpuContext context, GpuChannel channel) public GPFifoProcessor(GpuContext context, GpuChannel channel)
{ {
_context = context; _context = context;
_channel = channel;
_fifoClass = new GPFifoClass(context, this); _fifoClass = new GPFifoClass(context, this);
_subChannels = new GpuState[8]; _subChannels = new GpuState[8];

View file

@ -40,10 +40,10 @@ namespace Ryujinx.Graphics.Gpu.Engine
_buffer = new int[count]; _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. // 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; _finished = false;
} }
@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
if (_offset * 4 >= _size) if (_offset * 4 >= _size)
{ {
FinishTransfer(); FinishTransfer(state);
} }
} }
} }
@ -69,15 +69,16 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <summary> /// <summary>
/// Performs actual copy of the inline data after the transfer is finished. /// Performs actual copy of the inline data after the transfer is finished.
/// </summary> /// </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); Span<byte> data = MemoryMarshal.Cast<int, byte>(_buffer).Slice(0, _size);
if (_isLinear && _params.LineCount == 1) 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 else
{ {
@ -91,7 +92,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
int srcOffset = 0; 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++) 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); Span<byte> pixel = data.Slice(srcOffset, 16);
_context.PhysicalMemory.Write(dstAddress, pixel); state.Channel.MemoryManager.Physical.Write(dstAddress, pixel);
} }
for (; x < x2; x++, srcOffset++) for (; x < x2; x++, srcOffset++)
@ -120,7 +121,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
Span<byte> pixel = data.Slice(srcOffset, 1); 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.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.State; using Ryujinx.Graphics.Gpu.State;
namespace Ryujinx.Graphics.Gpu.Engine namespace Ryujinx.Graphics.Gpu.Engine
@ -23,11 +24,11 @@ namespace Ryujinx.Graphics.Gpu.Engine
case Condition.Never: case Condition.Never:
return ConditionalRenderEnabled.False; return ConditionalRenderEnabled.False;
case Condition.ResultNonZero: case Condition.ResultNonZero:
return CounterNonZero(condState.Address.Pack()); return CounterNonZero(state, condState.Address.Pack());
case Condition.Equal: case Condition.Equal:
return CounterCompare(condState.Address.Pack(), true); return CounterCompare(state, condState.Address.Pack(), true);
case Condition.NotEqual: 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}\"."); Logger.Warning?.Print(LogClass.Gpu, $"Invalid conditional render condition \"{condState.Condition}\".");
@ -38,11 +39,12 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <summary> /// <summary>
/// Checks if the counter value at a given GPU memory address is non-zero. /// Checks if the counter value at a given GPU memory address is non-zero.
/// </summary> /// </summary>
/// <param name="state">GPU state</param>
/// <param name="gpuVa">GPU virtual address of the counter value</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> /// <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) if (evt == null)
{ {
@ -56,30 +58,31 @@ namespace Ryujinx.Graphics.Gpu.Engine
else else
{ {
evt.Flush(); 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> /// <summary>
/// Checks if the counter at a given GPU memory address passes a specified equality comparison. /// Checks if the counter at a given GPU memory address passes a specified equality comparison.
/// </summary> /// </summary>
/// <param name="state">GPU state</param>
/// <param name="gpuVa">GPU virtual address</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> /// <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> /// <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 evt = FindEvent(state.Channel.MemoryManager.CounterCache, gpuVa);
ICounterEvent evt2 = FindEvent(gpuVa + 16); ICounterEvent evt2 = FindEvent(state.Channel.MemoryManager.CounterCache, gpuVa + 16);
bool useHost; bool useHost;
if (evt != null && evt2 == null) 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) 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) else if (evt != null && evt2 != null)
{ {
@ -99,8 +102,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
evt?.Flush(); evt?.Flush();
evt2?.Flush(); evt2?.Flush();
ulong x = _context.MemoryManager.Read<ulong>(gpuVa); ulong x = state.Channel.MemoryManager.Read<ulong>(gpuVa);
ulong y = _context.MemoryManager.Read<ulong>(gpuVa + 16); ulong y = state.Channel.MemoryManager.Read<ulong>(gpuVa + 16);
return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False; 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, /// Tries to find a counter that is supposed to be written at the specified address,
/// returning the related event. /// returning the related event.
/// </summary> /// </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> /// <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> /// <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; return;
} }
FlushUboDirty(); FlushUboDirty(state.Channel.MemoryManager);
if (copy2D) if (copy2D)
{ {
@ -98,21 +98,27 @@ namespace Ryujinx.Graphics.Gpu.Engine
dst.MemoryLayout.UnpackGobBlocksInZ(), dst.MemoryLayout.UnpackGobBlocksInZ(),
dstBpp); dstBpp);
ulong srcBaseAddress = _context.MemoryManager.Translate(cbp.SrcAddress.Pack()); ulong srcBaseAddress = state.Channel.MemoryManager.Translate(cbp.SrcAddress.Pack());
ulong dstBaseAddress = _context.MemoryManager.Translate(cbp.DstAddress.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 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); (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); ReadOnlySpan<byte> srcSpan = state.Channel.MemoryManager.Physical.GetSpan(srcBaseAddress + (ulong)srcBaseOffset, srcSize, true);
Span<byte> dstSpan = _context.PhysicalMemory.GetSpan(dstBaseAddress + (ulong)dstBaseOffset, dstSize).ToArray(); Span<byte> dstSpan = state.Channel.MemoryManager.Physical.GetSpan(dstBaseAddress + (ulong)dstBaseOffset, dstSize).ToArray();
bool completeSource = IsTextureCopyComplete(cbp, src, srcLinear, srcBpp, cbp.SrcStride); bool completeSource = IsTextureCopyComplete(cbp, src, srcLinear, srcBpp, cbp.SrcStride);
bool completeDest = IsTextureCopyComplete(cbp, dst, dstLinear, dstBpp, cbp.DstStride); bool completeDest = IsTextureCopyComplete(cbp, dst, dstLinear, dstBpp, cbp.DstStride);
if (completeSource && completeDest) 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) if (target != null)
{ {
ReadOnlySpan<byte> data; 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. 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; return;
} }
@ -195,7 +201,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
_ => throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format.") _ => 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 else
{ {
@ -209,13 +215,21 @@ namespace Ryujinx.Graphics.Gpu.Engine
swizzle.UnpackComponentSize() == 4) swizzle.UnpackComponentSize() == 4)
{ {
// Fast path for clears when remap is enabled. // 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 else
{ {
// TODO: Implement remap functionality. // TODO: Implement remap functionality.
// Buffer to buffer copy. // 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> /// <param name="argument">Method call argument</param>
private void CopyTexture(GpuState state, int argument) private void CopyTexture(GpuState state, int argument)
{ {
var memoryManager = state.Channel.MemoryManager;
var dstCopyTexture = state.Get<CopyTexture>(MethodOffset.CopyDstTexture); var dstCopyTexture = state.Get<CopyTexture>(MethodOffset.CopyDstTexture);
var srcCopyTexture = state.Get<CopyTexture>(MethodOffset.CopySrcTexture); var srcCopyTexture = state.Get<CopyTexture>(MethodOffset.CopySrcTexture);
@ -80,7 +82,13 @@ namespace Ryujinx.Graphics.Gpu.Engine
srcX1 = 0; 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) if (srcTexture == null)
{ {
@ -101,7 +109,13 @@ namespace Ryujinx.Graphics.Gpu.Engine
dstCopyTextureFormat = dstCopyTexture.Format.Convert(); 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) if (dstTexture == null)
{ {

View file

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

View file

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

View file

@ -16,11 +16,12 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// <summary> /// <summary>
/// Flushes any queued ubo updates. /// Flushes any queued ubo updates.
/// </summary> /// </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) if (_ubFollowUpAddress != 0)
{ {
BufferCache.ForceDirty(_ubFollowUpAddress - _ubByteCount, _ubByteCount); memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
_ubFollowUpAddress = 0; _ubFollowUpAddress = 0;
} }
@ -39,13 +40,14 @@ namespace Ryujinx.Graphics.Gpu.Engine
if (_ubFollowUpAddress != address) if (_ubFollowUpAddress != address)
{ {
FlushUboDirty(); FlushUboDirty(state.Channel.MemoryManager);
_ubByteCount = 0; _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; _ubFollowUpAddress = address + 4;
_ubByteCount += 4; _ubByteCount += 4;
@ -68,13 +70,14 @@ namespace Ryujinx.Graphics.Gpu.Engine
if (_ubFollowUpAddress != address) if (_ubFollowUpAddress != address)
{ {
FlushUboDirty(); FlushUboDirty(state.Channel.MemoryManager);
_ubByteCount = 0; _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; _ubFollowUpAddress = address + size;
_ubByteCount += size; _ubByteCount += size;

View file

@ -22,21 +22,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly ShaderProgramInfo[] _currentProgramInfo; 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 _isAnyVbInstanced;
private bool _vsUsesInstanceId; private bool _vsUsesInstanceId;
private byte _vsClipDistancesWritten; private byte _vsClipDistancesWritten;
@ -53,16 +38,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
{ {
_context = context; _context = context;
ShaderCache = new ShaderCache(_context);
_currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages]; _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> /// <summary>
@ -130,7 +106,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
_prevTfEnable = false; _prevTfEnable = false;
} }
FlushUboDirty(); FlushUboDirty(state.Channel.MemoryManager);
// Shaders must be the first one to be updated if modified, because // Shaders must be the first one to be updated if modified, because
// some of the other state depends on information from the currently // some of the other state depends on information from the currently
@ -342,7 +318,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
sbDescAddress += (ulong)sbDescOffset; 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); 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> /// <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) private void UpdateRenderTargetState(GpuState state, bool useControl, int singleUse = -1)
{ {
var memoryManager = state.Channel.MemoryManager;
var rtControl = state.Get<RtControl>(MethodOffset.RtControl); var rtControl = state.Get<RtControl>(MethodOffset.RtControl);
int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets; int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
@ -384,7 +361,12 @@ namespace Ryujinx.Graphics.Gpu.Engine
continue; 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); changedScale |= state.Channel.TextureManager.SetRenderTargetColor(index, color);
} }
@ -398,7 +380,13 @@ namespace Ryujinx.Graphics.Gpu.Engine
var dsState = state.Get<RtDepthStencilState>(MethodOffset.RtDepthStencilState); var dsState = state.Get<RtDepthStencilState>(MethodOffset.RtDepthStencilState);
var dsSize = state.Get<Size3D>(MethodOffset.RtDepthStencilSize); 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); changedScale |= state.Channel.TextureManager.SetRenderTargetDepthStencil(depthStencil);
@ -1012,7 +1000,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
addressesArray[index] = baseAddress + shader.Offset; addressesArray[index] = baseAddress + shader.Offset;
} }
ShaderBundle gs = ShaderCache.GetGraphicsShader(state, addresses); ShaderBundle gs = state.Channel.MemoryManager.Physical.ShaderCache.GetGraphicsShader(state, addresses);
byte oldVsClipDistancesWritten = _vsClipDistancesWritten; byte oldVsClipDistancesWritten = _vsClipDistancesWritten;

View file

@ -2,6 +2,7 @@
using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Memory;
using System; using System;
using System.Threading;
namespace Ryujinx.Graphics.Gpu namespace Ryujinx.Graphics.Gpu
{ {
@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Gpu
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly GPFifoDevice _device; private readonly GPFifoDevice _device;
private readonly GPFifoProcessor _processor; private readonly GPFifoProcessor _processor;
private MemoryManager _memoryManager;
/// <summary> /// <summary>
/// Channel buffer bindings manager. /// Channel buffer bindings manager.
@ -24,6 +26,11 @@ namespace Ryujinx.Graphics.Gpu
/// </summary> /// </summary>
internal TextureManager TextureManager { get; } internal TextureManager TextureManager { get; }
/// <summary>
/// Current channel memory manager.
/// </summary>
internal MemoryManager MemoryManager => _memoryManager;
/// <summary> /// <summary>
/// Creates a new instance of a GPU channel. /// Creates a new instance of a GPU channel.
/// </summary> /// </summary>
@ -33,10 +40,30 @@ namespace Ryujinx.Graphics.Gpu
_context = context; _context = context;
_device = context.GPFifo; _device = context.GPFifo;
_processor = new GPFifoProcessor(context, this); _processor = new GPFifoProcessor(context, this);
BufferManager = new BufferManager(context); BufferManager = new BufferManager(context, this);
TextureManager = new TextureManager(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> /// <summary>
/// Push a GPFIFO entry in the form of a prefetched command buffer. /// Push a GPFIFO entry in the form of a prefetched command buffer.
/// It is intended to be used by nvservices to handle special cases. /// It is intended to be used by nvservices to handle special cases.
@ -62,17 +89,23 @@ namespace Ryujinx.Graphics.Gpu
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
_context.DisposedChannels.Enqueue(this); _context.DeferredActions.Enqueue(Destroy);
} }
/// <summary> /// <summary>
/// Performs disposal of the host GPU resources used by this channel, that are not shared. /// Performs disposal of the host GPU resources used by this channel, that are not shared.
/// This must only be called from the render thread. /// This must only be called from the render thread.
/// </summary> /// </summary>
internal void Destroy() private void Destroy()
{ {
BufferManager.Dispose();
TextureManager.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;
using Ryujinx.Graphics.Gpu.Engine.GPFifo; using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Gpu.Synchronization; using Ryujinx.Graphics.Gpu.Synchronization;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
@ -24,16 +26,6 @@ namespace Ryujinx.Graphics.Gpu
/// </summary> /// </summary>
public IRenderer Renderer { get; } 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> /// <summary>
/// GPU engine methods processing. /// GPU engine methods processing.
/// </summary> /// </summary>
@ -73,11 +65,14 @@ namespace Ryujinx.Graphics.Gpu
internal List<Action> SyncActions { get; } internal List<Action> SyncActions { get; }
/// <summary> /// <summary>
/// Queue with closed channels for deferred disposal from the render thread. /// Queue with deferred actions that must run on the render thread.
/// </summary> /// </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> /// <summary>
/// Host hardware capabilities. /// Host hardware capabilities.
@ -87,11 +82,9 @@ namespace Ryujinx.Graphics.Gpu
/// <summary> /// <summary>
/// Event for signalling shader cache loading progress. /// Event for signalling shader cache loading progress.
/// </summary> /// </summary>
public event Action<Shader.ShaderCacheState, int, int> ShaderCacheStateChanged public event Action<ShaderCacheState, int, int> ShaderCacheStateChanged;
{
add => Methods.ShaderCache.ShaderCacheStateChanged += value; private readonly Lazy<Capabilities> _caps;
remove => Methods.ShaderCache.ShaderCacheStateChanged -= value;
}
/// <summary> /// <summary>
/// Creates a new instance of the GPU emulation context. /// Creates a new instance of the GPU emulation context.
@ -101,8 +94,6 @@ namespace Ryujinx.Graphics.Gpu
{ {
Renderer = renderer; Renderer = renderer;
MemoryManager = new MemoryManager(this);
Methods = new Methods(this); Methods = new Methods(this);
GPFifo = new GPFifoDevice(this); GPFifo = new GPFifoDevice(this);
@ -111,20 +102,83 @@ namespace Ryujinx.Graphics.Gpu
Window = new Window(this); Window = new Window(this);
_caps = new Lazy<Capabilities>(Renderer.GetCapabilities);
HostInitalized = new ManualResetEvent(false); HostInitalized = new ManualResetEvent(false);
SyncActions = new List<Action>(); 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() public GpuChannel CreateChannel()
{ {
return new GpuChannel(this); 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> /// <summary>
/// Initialize the GPU shader cache. /// Initialize the GPU shader cache.
/// </summary> /// </summary>
@ -132,7 +186,10 @@ namespace Ryujinx.Graphics.Gpu
{ {
HostInitalized.WaitOne(); HostInitalized.WaitOne();
Methods.ShaderCache.Initialize(); foreach (var physicalMemory in PhysicalMemoryRegistry.Values)
{
physicalMemory.ShaderCache.Initialize();
}
} }
/// <summary> /// <summary>
@ -144,16 +201,6 @@ namespace Ryujinx.Graphics.Gpu
SequenceNumber++; 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> /// <summary>
/// Registers an action to be performed the next time a syncpoint is incremented. /// 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. /// 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> /// <summary>
/// Performs deferred disposal of closed channels. /// Performs deferred actions.
/// This must only be called from the render thread. /// This is useful for actions that must run on the render thread, such as resource disposal.
/// </summary> /// </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> /// </summary>
public void Dispose() public void Dispose()
{ {
DisposePendingChannels();
Methods.ShaderCache.Dispose();
Methods.BufferCache.Dispose();
Methods.TextureCache.Dispose();
Renderer.Dispose(); Renderer.Dispose();
GPFifo.Dispose(); GPFifo.Dispose();
HostInitalized.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.Cpu.Tracking;
using Ryujinx.Graphics.Gpu.Memory;
using System; using System;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Image
protected const int DescriptorSize = 0x20; protected const int DescriptorSize = 0x20;
protected GpuContext Context; protected GpuContext Context;
protected PhysicalMemory PhysicalMemory;
protected T1[] Items; protected T1[] Items;
protected T2[] DescriptorCache; protected T2[] DescriptorCache;
@ -38,9 +40,17 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly CpuMultiRegionHandle _memoryTracking; private readonly CpuMultiRegionHandle _memoryTracking;
private readonly Action<ulong, ulong> _modifiedDelegate; 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; MaximumId = maximumId;
int count = maximumId + 1; int count = maximumId + 1;
@ -53,11 +63,10 @@ namespace Ryujinx.Graphics.Gpu.Image
Address = address; Address = address;
Size = size; Size = size;
_memoryTracking = context.PhysicalMemory.BeginGranularTracking(address, size); _memoryTracking = physicalMemory.BeginGranularTracking(address, size);
_modifiedDelegate = RegionModified; _modifiedDelegate = RegionModified;
} }
/// <summary> /// <summary>
/// Gets the descriptor for a given ID. /// Gets the descriptor for a given ID.
/// </summary> /// </summary>
@ -65,7 +74,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>The descriptor</returns> /// <returns>The descriptor</returns>
public T2 GetDescriptor(int id) public T2 GetDescriptor(int id)
{ {
return Context.PhysicalMemory.Read<T2>(Address + (ulong)id * DescriptorSize); return PhysicalMemory.Read<T2>(Address + (ulong)id * DescriptorSize);
} }
/// <summary> /// <summary>

View file

@ -1,3 +1,5 @@
using Ryujinx.Graphics.Gpu.Memory;
namespace Ryujinx.Graphics.Gpu.Image namespace Ryujinx.Graphics.Gpu.Image
{ {
/// <summary> /// <summary>
@ -11,9 +13,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Constructs a new instance of the sampler pool. /// Constructs a new instance of the sampler pool.
/// </summary> /// </summary>
/// <param name="context">GPU context that the sampler pool belongs to</param> /// <param name="context">GPU context that the sampler pool belongs to</param>
/// <param name="physicalMemory">Physical memory where the sampler descriptors are mapped</param>
/// <param name="address">Address of the sampler pool in guest memory</param> /// <param name="address">Address of the sampler pool in guest memory</param>
/// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param> /// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param>
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> /// <summary>
/// Gets the sampler with the given ID. /// Gets the sampler with the given ID.

View file

@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
private GpuContext _context; private GpuContext _context;
private PhysicalMemory _physicalMemory;
private SizeInfo _sizeInfo; private SizeInfo _sizeInfo;
@ -139,6 +140,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Constructs a new instance of the cached GPU texture. /// Constructs a new instance of the cached GPU texture.
/// </summary> /// </summary>
/// <param name="context">GPU context that the texture belongs to</param> /// <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="info">Texture information</param>
/// <param name="sizeInfo">Size information of the texture</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="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="scaleFactor">The floating point scale factor to initialize with</param>
/// <param name="scaleMode">The scale mode to initialize with</param> /// <param name="scaleMode">The scale mode to initialize with</param>
private Texture( private Texture(
GpuContext context, GpuContext context,
TextureInfo info, PhysicalMemory physicalMemory,
SizeInfo sizeInfo, TextureInfo info,
MultiRange range, SizeInfo sizeInfo,
int firstLayer, MultiRange range,
int firstLevel, int firstLayer,
float scaleFactor, int firstLevel,
float scaleFactor,
TextureScaleMode scaleMode) TextureScaleMode scaleMode)
{ {
InitializeTexture(context, info, sizeInfo, range); InitializeTexture(context, physicalMemory, info, sizeInfo, range);
FirstLayer = firstLayer; FirstLayer = firstLayer;
FirstLevel = firstLevel; FirstLevel = firstLevel;
@ -171,16 +174,23 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Constructs a new instance of the cached GPU texture. /// Constructs a new instance of the cached GPU texture.
/// </summary> /// </summary>
/// <param name="context">GPU context that the texture belongs to</param> /// <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="info">Texture information</param>
/// <param name="sizeInfo">Size information of the texture</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="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> /// <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. ScaleFactor = 1f; // Texture is first loaded at scale 1x.
ScaleMode = scaleMode; ScaleMode = scaleMode;
InitializeTexture(context, info, sizeInfo, range); InitializeTexture(context, physicalMemory, info, sizeInfo, range);
} }
/// <summary> /// <summary>
@ -189,14 +199,21 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Other fields are initialized with their default values. /// Other fields are initialized with their default values.
/// </summary> /// </summary>
/// <param name="context">GPU context that the texture belongs to</param> /// <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="info">Texture information</param>
/// <param name="sizeInfo">Size information of the texture</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="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; _sizeInfo = sizeInfo;
Range = range; Range = range;
SetInfo(info); SetInfo(info);
@ -255,7 +272,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="hasMipViews">True if the texture will have mip views</param> /// <param name="hasMipViews">True if the texture will have mip views</param>
public void InitializeGroup(bool hasLayerViews, bool hasMipViews) public void InitializeGroup(bool hasLayerViews, bool hasMipViews)
{ {
Group = new TextureGroup(_context, this); Group = new TextureGroup(_context, _physicalMemory, this);
Group.Initialize(ref _sizeInfo, hasLayerViews, hasMipViews); Group.Initialize(ref _sizeInfo, hasLayerViews, hasMipViews);
} }
@ -276,6 +293,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
Texture texture = new Texture( Texture texture = new Texture(
_context, _context,
_physicalMemory,
info, info,
sizeInfo, sizeInfo,
range, range,
@ -638,7 +656,7 @@ namespace Ryujinx.Graphics.Gpu.Image
BlacklistScale(); BlacklistScale();
} }
ReadOnlySpan<byte> data = _context.PhysicalMemory.GetSpan(Range); ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Range);
IsModified = false; IsModified = false;
@ -805,11 +823,11 @@ namespace Ryujinx.Graphics.Gpu.Image
if (tracked) if (tracked)
{ {
_context.PhysicalMemory.Write(Range, GetTextureDataFromGpu(tracked)); _physicalMemory.Write(Range, GetTextureDataFromGpu(tracked));
} }
else 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); 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); _viewStorage.RemoveView(this);
} }
_context.Methods.TextureCache.RemoveTextureFromCache(this); _physicalMemory.TextureCache.RemoveTextureFromCache(this);
} }
Debug.Assert(newRefCount >= 0); 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> /// <param name="samplerIndex">Type of the sampler pool indexing used for bound samplers</param>
public void SetSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex) public void SetSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
{ {
ulong address = _context.MemoryManager.Translate(gpuVa); ulong address = _channel.MemoryManager.Translate(gpuVa);
if (_samplerPool != null) if (_samplerPool != null)
{ {
@ -131,7 +131,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_samplerPool.Dispose(); _samplerPool.Dispose();
} }
_samplerPool = new SamplerPool(_context, address, maximumId); _samplerPool = new SamplerPool(_context, _channel.MemoryManager.Physical, address, maximumId);
_samplerIndex = samplerIndex; _samplerIndex = samplerIndex;
} }
@ -142,7 +142,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="maximumId">Maximum ID of the pool (total count minus one)</param> /// <param name="maximumId">Maximum ID of the pool (total count minus one)</param>
public void SetTexturePool(ulong gpuVa, int maximumId) public void SetTexturePool(ulong gpuVa, int maximumId)
{ {
ulong address = _context.MemoryManager.Translate(gpuVa); ulong address = _channel.MemoryManager.Translate(gpuVa);
_texturePoolAddress = address; _texturePoolAddress = address;
_texturePoolMaximumId = maximumId; _texturePoolMaximumId = maximumId;
@ -228,6 +228,7 @@ namespace Ryujinx.Graphics.Gpu.Image
public void CommitBindings() public void CommitBindings()
{ {
TexturePool texturePool = _texturePoolCache.FindOrCreate( TexturePool texturePool = _texturePoolCache.FindOrCreate(
_channel,
_texturePoolAddress, _texturePoolAddress,
_texturePoolMaximumId); _texturePoolMaximumId);
@ -437,9 +438,9 @@ namespace Ryujinx.Graphics.Gpu.Image
var poolState = state.Get<PoolState>(MethodOffset.TexturePoolState); 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); 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> /// <returns>The packed texture and sampler ID (the real texture handle)</returns>
private int ReadPackedId(int stageIndex, int wordOffset, int textureBufferIndex, int samplerBufferIndex) private int ReadPackedId(int stageIndex, int wordOffset, int textureBufferIndex, int samplerBufferIndex)
{ {
var bufferManager = _context.Methods.BufferCache;
ulong textureBufferAddress = _isCompute ulong textureBufferAddress = _isCompute
? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex) ? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, 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) // 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 // 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.GetComputeUniformBufferAddress(samplerBufferIndex)
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, 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; return handle;
@ -514,7 +514,6 @@ namespace Ryujinx.Graphics.Gpu.Image
public void Dispose() public void Dispose()
{ {
_samplerPool?.Dispose(); _samplerPool?.Dispose();
_texturePoolCache.Dispose();
} }
} }
} }

View file

@ -32,6 +32,7 @@ namespace Ryujinx.Graphics.Gpu.Image
private const int OverlapsBufferMaxCapacity = 10000; private const int OverlapsBufferMaxCapacity = 10000;
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly PhysicalMemory _physicalMemory;
private readonly MultiRangeList<Texture> _textures; private readonly MultiRangeList<Texture> _textures;
@ -44,9 +45,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Constructs a new instance of the texture manager. /// Constructs a new instance of the texture manager.
/// </summary> /// </summary>
/// <param name="context">The GPU context that the texture manager belongs to</param> /// <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; _context = context;
_physicalMemory = physicalMemory;
_textures = new MultiRangeList<Texture>(); _textures = new MultiRangeList<Texture>();
@ -68,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Image
lock (_textures) 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++) for (int i = 0; i < overlapCount; i++)
@ -139,13 +142,20 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary> /// <summary>
/// Tries to find an existing texture, or create a new one if not found. /// Tries to find an existing texture, or create a new one if not found.
/// </summary> /// </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="copyTexture">Copy texture to find or create</param>
/// <param name="offset">Offset to be added to the physical texture address</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="formatInfo">Format information of the copy texture</param>
/// <param name="preferScaling">Indicates if the texture should be scaled from the start</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> /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
/// <returns>The texture</returns> /// <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 gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ(); int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
@ -184,7 +194,7 @@ namespace Ryujinx.Graphics.Gpu.Image
flags |= TextureSearchFlags.WithUpscale; flags |= TextureSearchFlags.WithUpscale;
} }
Texture texture = FindOrCreateTexture(flags, info, 0, sizeHint); Texture texture = FindOrCreateTexture(memoryManager, flags, info, 0, sizeHint);
texture?.SynchronizeMemory(); texture?.SynchronizeMemory();
@ -194,12 +204,13 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary> /// <summary>
/// Tries to find an existing texture, or create a new one if not found. /// Tries to find an existing texture, or create a new one if not found.
/// </summary> /// </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="colorState">Color buffer texture to find or create</param>
/// <param name="samplesInX">Number of samples in the X direction, for MSAA</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="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> /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
/// <returns>The texture</returns> /// <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(); bool isLinear = colorState.MemoryLayout.UnpackIsLinear();
@ -263,7 +274,7 @@ namespace Ryujinx.Graphics.Gpu.Image
int layerSize = !isLinear ? colorState.LayerSize * 4 : 0; 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(); texture?.SynchronizeMemory();
@ -273,13 +284,20 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary> /// <summary>
/// Tries to find an existing texture, or create a new one if not found. /// Tries to find an existing texture, or create a new one if not found.
/// </summary> /// </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="dsState">Depth-stencil buffer texture to find or create</param>
/// <param name="size">Size of the depth-stencil texture</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="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="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> /// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
/// <returns>The texture</returns> /// <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 gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ(); int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
@ -306,7 +324,7 @@ namespace Ryujinx.Graphics.Gpu.Image
target, target,
formatInfo); formatInfo);
Texture texture = FindOrCreateTexture(TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4, sizeHint); Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4, sizeHint);
texture?.SynchronizeMemory(); texture?.SynchronizeMemory();
@ -316,13 +334,20 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <summary> /// <summary>
/// Tries to find an existing texture, or create a new one if not found. /// Tries to find an existing texture, or create a new one if not found.
/// </summary> /// </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="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="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="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="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> /// <param name="range">Optional ranges of physical memory where the texture data is located</param>
/// <returns>The texture</returns> /// <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; bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
@ -342,7 +367,7 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
else else
{ {
address = _context.MemoryManager.Translate(info.GpuAddress); address = memoryManager.Translate(info.GpuAddress);
if (address == MemoryManager.PteUnmapped) if (address == MemoryManager.PteUnmapped)
{ {
@ -371,22 +396,26 @@ namespace Ryujinx.Graphics.Gpu.Image
if (matchQuality != TextureMatchQuality.NoMatch) if (matchQuality != TextureMatchQuality.NoMatch)
{ {
// If the parameters match, we need to make sure the texture is mapped to the same memory regions. // If the parameters match, we need to make sure the texture is mapped to the same memory regions.
if (range != null)
// If a range of memory was supplied, just check if the ranges match.
if (range != null && !overlap.Range.Equals(range.Value))
{ {
continue; // If a range of memory was supplied, just check if the ranges match.
if (!overlap.Range.Equals(range.Value))
{
continue;
}
} }
else
// If no range was supplied, we can check if the GPU virtual address match. If they do,
// we know the textures are located at the same memory region.
// If they don't, it may still be mapped to the same physical region, so we
// do a more expensive check to tell if they are mapped into the same physical regions.
// If the GPU VA for the texture has ever been unmapped, then the range must be checked regardless.
if ((overlap.Info.GpuAddress != info.GpuAddress || overlap.ChangedMapping) &&
!_context.MemoryManager.CompareRange(overlap.Range, info.GpuAddress))
{ {
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) if (range == null)
{ {
range = _context.MemoryManager.GetPhysicalRegions(info.GpuAddress, size); range = memoryManager.GetPhysicalRegions(info.GpuAddress, size);
} }
// Find view compatible matches. // 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. // 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.InitializeGroup(true, true);
texture.InitializeData(false, false); texture.InitializeData(false, false);
@ -531,7 +560,7 @@ namespace Ryujinx.Graphics.Gpu.Image
// No match, create a new texture. // No match, create a new texture.
if (texture == null) 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. // 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. // 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> /// <summary>
/// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null. /// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null.
/// </summary> /// </summary>
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
/// <param name="tex">The texture information</param> /// <param name="tex">The texture information</param>
/// <param name="cbp">The copy buffer parameters</param> /// <param name="cbp">The copy buffer parameters</param>
/// <param name="swizzle">The copy buffer swizzle</param> /// <param name="swizzle">The copy buffer swizzle</param>
/// <param name="linear">True if the texture has a linear layout, false otherwise</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> /// <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) if (address == MemoryManager.PteUnmapped)
{ {

View file

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

View file

@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Gpu.Image
class TexturePool : Pool<Texture, TextureDescriptor> class TexturePool : Pool<Texture, TextureDescriptor>
{ {
private int _sequenceNumber; private int _sequenceNumber;
private readonly GpuChannel _channel;
private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>(); private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>();
/// <summary> /// <summary>
@ -24,9 +25,13 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Constructs a new instance of the texture pool. /// Constructs a new instance of the texture pool.
/// </summary> /// </summary>
/// <param name="context">GPU context that the texture pool belongs to</param> /// <param name="context">GPU context that the texture pool belongs to</param>
/// <param name="channel">GPU channel that the texture pool belongs to</param>
/// <param name="address">Address of the texture pool in guest memory</param> /// <param name="address">Address of the texture pool in guest memory</param>
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param> /// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
public TexturePool(GpuContext context, 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> /// <summary>
/// Gets the texture with the given ID. /// Gets the texture with the given ID.
@ -57,7 +62,7 @@ namespace Ryujinx.Graphics.Gpu.Image
ProcessDereferenceQueue(); 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 this happens, then the texture address is invalid, we can't add it to the cache.
if (texture == null) if (texture == null)
@ -148,7 +153,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (texture != null) 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, // If the descriptors are the same, the texture is the same,
// we don't need to remove as it was not modified. Just continue. // 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 (!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})."); 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. /// This can keep multiple texture pools, and return the current one as needed.
/// It is useful for applications that uses multiple texture pools. /// It is useful for applications that uses multiple texture pools.
/// </summary> /// </summary>
class TexturePoolCache : IDisposable class TexturePoolCache
{ {
private const int MaxCapacity = 4; private const int MaxCapacity = 4;
private GpuContext _context; private readonly GpuContext _context;
private readonly LinkedList<TexturePool> _pools;
private LinkedList<TexturePool> _pools;
/// <summary> /// <summary>
/// Constructs a new instance of the texture pool. /// Constructs a new instance of the texture pool.
@ -23,17 +22,17 @@ namespace Ryujinx.Graphics.Gpu.Image
public TexturePoolCache(GpuContext context) public TexturePoolCache(GpuContext context)
{ {
_context = context; _context = context;
_pools = new LinkedList<TexturePool>(); _pools = new LinkedList<TexturePool>();
} }
/// <summary> /// <summary>
/// Finds a cache texture pool, or creates a new one if not found. /// Finds a cache texture pool, or creates a new one if not found.
/// </summary> /// </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="address">Start address of the texture pool</param>
/// <param name="maximumId">Maximum ID of the texture pool</param> /// <param name="maximumId">Maximum ID of the texture pool</param>
/// <returns>The found or newly created texture pool</returns> /// <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; TexturePool pool;
@ -56,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
// If not found, create a new one. // 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); pool.CacheNode = _pools.AddLast(pool);
@ -73,19 +72,5 @@ namespace Ryujinx.Graphics.Gpu.Image
return pool; 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> /// </summary>
class Buffer : IRange, IDisposable class Buffer : IRange, IDisposable
{ {
private static ulong GranularBufferThreshold = 4096; private const ulong GranularBufferThreshold = 4096;
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly PhysicalMemory _physicalMemory;
/// <summary> /// <summary>
/// Host buffer handle. /// Host buffer handle.
@ -68,14 +69,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Creates a new instance of the buffer. /// Creates a new instance of the buffer.
/// </summary> /// </summary>
/// <param name="context">GPU context that the buffer belongs to</param> /// <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="address">Start address of the buffer</param>
/// <param name="size">Size of the buffer in bytes</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> /// <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; _context = context;
Address = address; _physicalMemory = physicalMemory;
Size = size; Address = address;
Size = size;
Handle = context.Renderer.CreateBuffer((int)size); Handle = context.Renderer.CreateBuffer((int)size);
@ -100,11 +103,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (_useGranular) if (_useGranular)
{ {
_memoryTrackingGranular = context.PhysicalMemory.BeginGranularTracking(address, size, baseHandles); _memoryTrackingGranular = physicalMemory.BeginGranularTracking(address, size, baseHandles);
} }
else else
{ {
_memoryTracking = context.PhysicalMemory.BeginTracking(address, size); _memoryTracking = physicalMemory.BeginTracking(address, size);
if (baseHandles != null) if (baseHandles != null)
{ {
@ -207,9 +210,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
else 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; _sequenceNumber = _context.SequenceNumber;
} }
} }
@ -363,7 +366,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
int offset = (int)(mAddress - Address); 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> /// <summary>
@ -412,7 +415,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
byte[] data = _context.Renderer.GetBufferData(Handle, offset, (int)size); 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. // 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> /// <summary>

View file

@ -18,7 +18,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
private const ulong BufferAlignmentSize = 0x1000; private const ulong BufferAlignmentSize = 0x1000;
private const ulong BufferAlignmentMask = BufferAlignmentSize - 1; private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
private GpuContext _context; private readonly GpuContext _context;
private readonly PhysicalMemory _physicalMemory;
private readonly RangeList<Buffer> _buffers; private readonly RangeList<Buffer> _buffers;
@ -32,9 +33,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Creates a new instance of the buffer manager. /// Creates a new instance of the buffer manager.
/// </summary> /// </summary>
/// <param name="context">The GPU context that the buffer manager belongs to</param> /// <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; _context = context;
_physicalMemory = physicalMemory;
_buffers = new RangeList<Buffer>(); _buffers = new RangeList<Buffer>();
@ -53,7 +56,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
Buffer[] overlaps = new Buffer[10]; Buffer[] overlaps = new Buffer[10];
int overlapCount; int overlapCount;
ulong address = _context.MemoryManager.Translate(e.Address); ulong address = ((MemoryManager)sender).Translate(e.Address);
ulong size = e.Size; ulong size = e.Size;
lock (_buffers) lock (_buffers)
@ -71,17 +74,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Performs address translation of the GPU virtual address, and creates a /// Performs address translation of the GPU virtual address, and creates a
/// new buffer, if needed, for the specified range. /// new buffer, if needed, for the specified range.
/// </summary> /// </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="gpuVa">Start GPU virtual address of the buffer</param>
/// <param name="size">Size in bytes 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> /// <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) if (gpuVa == 0)
{ {
return 0; return 0;
} }
ulong address = _context.MemoryManager.Translate(gpuVa); ulong address = memoryManager.Translate(gpuVa);
if (address == MemoryManager.PteUnmapped) 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 /// The buffer lookup for this function is cached in a dictionary for quick access, which
/// accelerates common UBO updates. /// accelerates common UBO updates.
/// </summary> /// </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="gpuVa">Start GPU virtual address of the buffer</param>
/// <param name="size">Size in bytes 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 BufferCacheEntry result) ||
result.EndGpuAddress < gpuVa + size ||
if (!_dirtyCache.TryGetValue(gpuVa, out result) || result.EndGpuAddress < gpuVa + size || result.UnmappedSequence != result.Buffer.UnmappedSequence) result.UnmappedSequence != result.Buffer.UnmappedSequence)
{ {
ulong address = TranslateAndCreateBuffer(gpuVa, size); ulong address = TranslateAndCreateBuffer(memoryManager, gpuVa, size);
result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size)); result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size));
_dirtyCache[gpuVa] = result; _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) lock (_buffers)
{ {
@ -207,7 +212,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
else else
{ {
// No overlap, just create a new buffer. // No overlap, just create a new buffer.
Buffer buffer = new Buffer(_context, address, size); Buffer buffer = new Buffer(_context, _physicalMemory, address, size);
lock (_buffers) lock (_buffers)
{ {
@ -235,13 +240,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <remarks> /// <remarks>
/// This does a GPU side copy. /// This does a GPU side copy.
/// </remarks> /// </remarks>
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
/// <param name="srcVa">GPU virtual address of the copy source</param> /// <param name="srcVa">GPU virtual address of the copy source</param>
/// <param name="dstVa">GPU virtual address of the copy destination</param> /// <param name="dstVa">GPU virtual address of the copy destination</param>
/// <param name="size">Size in bytes of the copy</param> /// <param name="size">Size in bytes of the copy</param>
public void CopyBuffer(GpuVa srcVa, GpuVa dstVa, ulong size) public void CopyBuffer(MemoryManager memoryManager, GpuVa srcVa, GpuVa dstVa, ulong size)
{ {
ulong srcAddress = TranslateAndCreateBuffer(srcVa.Pack(), size); ulong srcAddress = TranslateAndCreateBuffer(memoryManager, srcVa.Pack(), size);
ulong dstAddress = TranslateAndCreateBuffer(dstVa.Pack(), size); ulong dstAddress = TranslateAndCreateBuffer(memoryManager, dstVa.Pack(), size);
Buffer srcBuffer = GetBuffer(srcAddress, size); Buffer srcBuffer = GetBuffer(srcAddress, size);
Buffer dstBuffer = GetBuffer(dstAddress, 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. // Optimization: If the data being copied is already in memory, then copy it directly instead of flushing from GPU.
dstBuffer.ClearModified(dstAddress, size); dstBuffer.ClearModified(dstAddress, size);
_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> /// <remarks>
/// Both the address and size must be aligned to 4 bytes. /// Both the address and size must be aligned to 4 bytes.
/// </remarks> /// </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="gpuVa">GPU virtual address of the region to clear</param>
/// <param name="size">Number of bytes to clear</param> /// <param name="size">Number of bytes to clear</param>
/// <param name="value">Value to be written into the buffer</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); Buffer buffer = GetBuffer(address, size);

View file

@ -12,11 +12,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary> /// <summary>
/// Buffer manager. /// Buffer manager.
/// </summary> /// </summary>
class BufferManager : IDisposable class BufferManager
{ {
private const int StackToHeapThreshold = 16; private const int StackToHeapThreshold = 16;
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly GpuChannel _channel;
private IndexBuffer _indexBuffer; private IndexBuffer _indexBuffer;
private readonly VertexBuffer[] _vertexBuffers; private readonly VertexBuffer[] _vertexBuffers;
@ -106,9 +107,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Creates a new instance of the buffer manager. /// Creates a new instance of the buffer manager.
/// </summary> /// </summary>
/// <param name="context">GPU context that the buffer manager belongs to</param> /// <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; _context = context;
_channel = channel;
_vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers]; _vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
@ -127,8 +130,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
_bufferTextures = new List<BufferTextureBinding>(); _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> /// <param name="type">Type of each index buffer element</param>
public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type) public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
{ {
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size); ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
_indexBuffer.Address = address; _indexBuffer.Address = address;
_indexBuffer.Size = size; _indexBuffer.Size = size;
@ -171,7 +172,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param> /// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor) public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
{ {
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].Address = address;
_vertexBuffers[index].Size = size; _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> /// <param name="size">Size in bytes of the transform feedback buffer</param>
public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size) public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
{ {
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); _transformFeedbackBuffers[index] = new BufferBounds(address, size);
_transformFeedbackBuffersDirty = true; _transformFeedbackBuffersDirty = true;
@ -219,7 +220,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment); 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); _cpStorageBuffers.SetBounds(index, address, size, flags);
} }
@ -239,7 +240,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment); 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 || if (_gpStorageBuffers[stage].Buffers[index].Address != address ||
_gpStorageBuffers[stage].Buffers[index].Size != size) _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> /// <param name="size">Size in bytes of the storage buffer</param>
public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size) public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
{ {
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size); ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
_cpUniformBuffers.SetBounds(index, address, 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> /// <param name="size">Size in bytes of the storage buffer</param>
public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size) public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
{ {
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); _gpUniformBuffers[stage].SetBounds(index, address, size);
_gpUniformBuffersDirty = true; _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), // 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. // 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.Address,
bounds.Size, bounds.Size,
bounds.Flags.HasFlag(BufferUsageFlags.Write)); bounds.Flags.HasFlag(BufferUsageFlags.Write));
@ -443,7 +444,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (bounds.Address != 0) 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) foreach (var binding in _bufferTextures)
{ {
var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore); 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); binding.Texture.SetStorage(range);
// The texture must be rebound to use the new storage if it was updated. // The texture must be rebound to use the new storage if it was updated.
@ -496,14 +497,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (_indexBuffer.Address != 0) 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); _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
} }
} }
else if (_indexBuffer.Address != 0) 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; uint vbEnableMask = _vertexBuffersEnableMask;
@ -523,7 +524,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue; 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); vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
} }
@ -541,7 +542,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue; 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; 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); _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
@ -577,7 +578,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
continue; 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); var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
ranges[bindingInfo.Binding] = isStorage ranges[bindingInfo.Binding] = isStorage
? _context.Methods.BufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite) ? _channel.MemoryManager.Physical.BufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
: _context.Methods.BufferCache.GetBufferRange(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; 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> /// <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) 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)); _bufferTextures.Add(new BufferTextureBinding(texture, address, size, bindingInfo, format, isImage));
} }
@ -698,14 +699,5 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
_rebind = true; _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; 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> /// <summary>
/// Creates a new instance of the GPU memory manager. /// Creates a new instance of the GPU memory manager.
/// </summary> /// </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][]; _pageTable = new ulong[PtLvl0Size][];
MemoryUnmapped += Physical.TextureCache.MemoryUnmappedHandler;
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
} }
/// <summary> /// <summary>
@ -67,7 +80,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
if (IsContiguous(va, size)) if (IsContiguous(va, size))
{ {
return _context.PhysicalMemory.GetSpan(Translate(va), size, tracked); return Physical.GetSpan(Translate(va), size, tracked);
} }
else else
{ {
@ -100,7 +113,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask)); 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; offset += size;
} }
@ -111,7 +124,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
size = Math.Min(data.Length - offset, (int)PageSize); 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)) if (IsContiguous(va, size))
{ {
return _context.PhysicalMemory.GetWritableRegion(Translate(va), size); return Physical.GetWritableRegion(Translate(va), size);
} }
else else
{ {
@ -155,7 +168,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param> /// <param name="data">The data to be written</param>
public void Write(ulong va, ReadOnlySpan<byte> data) public void Write(ulong va, ReadOnlySpan<byte> data)
{ {
WriteImpl(va, data, _context.PhysicalMemory.Write); WriteImpl(va, data, Physical.Write);
} }
/// <summary> /// <summary>
@ -165,7 +178,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="data">The data to be written</param> /// <param name="data">The data to be written</param>
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data) 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); private delegate void WriteCallback(ulong address, ReadOnlySpan<byte> data);

View file

@ -1,5 +1,7 @@
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.Cpu.Tracking; using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Memory; using Ryujinx.Memory;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using Ryujinx.Memory.Tracking; using Ryujinx.Memory.Tracking;
@ -7,6 +9,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
namespace Ryujinx.Graphics.Gpu.Memory namespace Ryujinx.Graphics.Gpu.Memory
{ {
@ -18,20 +21,63 @@ namespace Ryujinx.Graphics.Gpu.Memory
{ {
public const int PageSize = 0x1000; public const int PageSize = 0x1000;
private readonly GpuContext _context;
private IVirtualMemoryManagerTracked _cpuMemory; 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> /// <summary>
/// Creates a new instance of the physical memory. /// Creates a new instance of the physical memory.
/// </summary> /// </summary>
/// <param name="context">GPU context that the physical memory belongs to</param>
/// <param name="cpuMemory">CPU memory manager of the application process</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; _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(); 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> /// <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="range">Ranges of physical memory where the data is located</param>
/// <param name="data">Data to be written</param> /// <param name="data">Data to be written</param>
/// <param name="writeCallback">Callback method that will perform the write</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) if (range.Count == 1)
{ {
@ -227,12 +273,20 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// </summary> /// </summary>
public void Dispose() public void Dispose()
{ {
if (_cpuMemory is IRefCounted rc) _context.DeferredActions.Enqueue(Destroy);
{ }
rc.DecrementReferenceCount();
_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> /// <returns>Data at the memory location</returns>
public override T MemoryRead<T>(ulong address) public override T MemoryRead<T>(ulong address)
{ {
return _context.MemoryManager.Read<T>(address); return _state.Channel.MemoryManager.Read<T>(address);
} }
/// <summary> /// <summary>
@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>True if the address is mapped, false otherwise</returns> /// <returns>True if the address is mapped, false otherwise</returns>
public bool MemoryMapped(ulong address) public bool MemoryMapped(ulong address)
{ {
return _context.MemoryManager.IsMapped(address); return _state.Channel.MemoryManager.IsMapped(address);
} }
/// <summary> /// <summary>

View file

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

View file

@ -22,6 +22,11 @@ namespace Ryujinx.Graphics.Gpu
/// </summary> /// </summary>
private struct PresentationTexture private struct PresentationTexture
{ {
/// <summary>
/// Texture cache where the texture might be located.
/// </summary>
public TextureCache Cache { get; }
/// <summary> /// <summary>
/// Texture information. /// Texture information.
/// </summary> /// </summary>
@ -55,6 +60,7 @@ namespace Ryujinx.Graphics.Gpu
/// <summary> /// <summary>
/// Creates a new instance of the presentation texture. /// Creates a new instance of the presentation texture.
/// </summary> /// </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="info">Information of the texture to be presented</param>
/// <param name="range">Physical memory locations where the texture data is located</param> /// <param name="range">Physical memory locations where the texture data is located</param>
/// <param name="crop">Texture crop region</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="releaseCallback">Texture release callback</param>
/// <param name="userObj">User defined object passed to the release callback, can be used to identify the texture</param> /// <param name="userObj">User defined object passed to the release callback, can be used to identify the texture</param>
public PresentationTexture( public PresentationTexture(
TextureCache cache,
TextureInfo info, TextureInfo info,
MultiRange range, MultiRange range,
ImageCrop crop, ImageCrop crop,
@ -69,6 +76,7 @@ namespace Ryujinx.Graphics.Gpu
Action<object> releaseCallback, Action<object> releaseCallback,
object userObj) object userObj)
{ {
Cache = cache;
Info = info; Info = info;
Range = range; Range = range;
Crop = crop; Crop = crop;
@ -99,6 +107,7 @@ namespace Ryujinx.Graphics.Gpu
/// When the texture is presented and not needed anymore, the release callback is called. /// 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. /// It's an error to modify the texture after calling this method, before the release callback is called.
/// </summary> /// </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="address">CPU virtual address of the texture data</param>
/// <param name="width">Texture width</param> /// <param name="width">Texture width</param>
/// <param name="height">Texture height</param> /// <param name="height">Texture height</param>
@ -111,7 +120,9 @@ namespace Ryujinx.Graphics.Gpu
/// <param name="acquireCallback">Texture acquire callback</param> /// <param name="acquireCallback">Texture acquire callback</param>
/// <param name="releaseCallback">Texture release callback</param> /// <param name="releaseCallback">Texture release callback</param>
/// <param name="userObj">User defined object passed to the 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( public void EnqueueFrameThreadSafe(
long pid,
ulong address, ulong address,
int width, int width,
int height, int height,
@ -125,6 +136,11 @@ namespace Ryujinx.Graphics.Gpu
Action<object> releaseCallback, Action<object> releaseCallback,
object userObj) 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); FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel, 4);
TextureInfo info = new TextureInfo( TextureInfo info = new TextureInfo(
@ -158,7 +174,14 @@ namespace Ryujinx.Graphics.Gpu
MultiRange range = new MultiRange(address, (ulong)size); 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> /// <summary>
@ -174,7 +197,7 @@ namespace Ryujinx.Graphics.Gpu
{ {
pt.AcquireCallback(_context, pt.UserObj); 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(); texture.SynchronizeMemory();

View file

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

View file

@ -1,5 +1,6 @@
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Cpu; using Ryujinx.Cpu;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.Memory; using Ryujinx.Memory;
@ -9,19 +10,26 @@ namespace Ryujinx.HLE.HOS
{ {
class ArmProcessContextFactory : IProcessContextFactory 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; MemoryManagerMode mode = context.Device.Configuration.MemoryManagerMode;
switch (mode) switch (mode)
{ {
case MemoryManagerMode.SoftwarePageTable: 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.HostMapped:
case MemoryManagerMode.HostMappedUnsafe: case MemoryManagerMode.HostMappedUnsafe:
bool unsafeMode = mode == 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: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();

View file

@ -4,6 +4,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{ {
interface IProcessContextFactory 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); 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); InitializeMemoryManager(creationInfo.Flags);
bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr); bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
@ -171,13 +178,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return result; return result;
} }
Pid = KernelContext.NewKipId();
if (Pid == 0 || (ulong)Pid >= KernelConstants.InitialProcessId)
{
throw new InvalidOperationException($"Invalid KIP Id {Pid}.");
}
return ParseProcessInfo(creationInfo); return ParseProcessInfo(creationInfo);
} }
@ -233,6 +233,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift); 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); InitializeMemoryManager(creationInfo.Flags);
bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr); bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
@ -286,13 +293,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return result; return result;
} }
Pid = KernelContext.NewProcessId();
if (Pid == -1 || (ulong)Pid < KernelConstants.InitialProcessId)
{
throw new InvalidOperationException($"Invalid Process Id {Pid}.");
}
result = ParseProcessInfo(creationInfo); result = ParseProcessInfo(creationInfo);
if (result != KernelResult.Success) if (result != KernelResult.Success)
@ -1051,14 +1051,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
bool for64Bit = flags.HasFlag(ProcessCreationFlags.Is64Bit); bool for64Bit = flags.HasFlag(ProcessCreationFlags.Is64Bit);
Context = _contextFactory.Create(KernelContext, 1UL << addrSpaceBits, InvalidAccessHandler, for64Bit); Context = _contextFactory.Create(KernelContext, Pid, 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);
}
if (Context.AddressSpace is MemoryManagerHostMapped) if (Context.AddressSpace is MemoryManagerHostMapped)
{ {

View file

@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{ {
class ProcessContextFactory : IProcessContextFactory 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)); return new ProcessContext(new AddressSpaceManager(addressSpaceSize));
} }

View file

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

View file

@ -1,22 +1,22 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu.Memory; 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.NvHostAsGpu.Types;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Collections.Concurrent;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
{ {
class NvHostAsGpuDeviceFile : NvDeviceFile class NvHostAsGpuDeviceFile : NvDeviceFile
{ {
private static ConcurrentDictionary<KProcess, AddressSpaceContext> _addressSpaceContextRegistry = new ConcurrentDictionary<KProcess, AddressSpaceContext>(); private readonly AddressSpaceContext _asContext;
private NvMemoryAllocator _memoryAllocator; private readonly NvMemoryAllocator _memoryAllocator;
public NvHostAsGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner) 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) 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) 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; return NvInternalResult.Success;
} }
private NvInternalResult AllocSpace(ref AllocSpaceArguments arguments) private NvInternalResult AllocSpace(ref AllocSpaceArguments arguments)
{ {
AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context);
ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize; ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize;
NvInternalResult result = NvInternalResult.Success; NvInternalResult result = NvInternalResult.Success;
lock (addressSpaceContext) lock (_asContext)
{ {
// Note: When the fixed offset flag is not set, // Note: When the fixed offset flag is not set,
// the Offset field holds the alignment size instead. // the Offset field holds the alignment size instead.
@ -132,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
} }
else 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) private NvInternalResult FreeSpace(ref FreeSpaceArguments arguments)
{ {
AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context); ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize;
NvInternalResult result = NvInternalResult.Success; NvInternalResult result = NvInternalResult.Success;
lock (addressSpaceContext) lock (_asContext)
{ {
ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize; if (_asContext.RemoveReservation(arguments.Offset))
if (addressSpaceContext.RemoveReservation(arguments.Offset))
{ {
_memoryAllocator.DeallocateRange(arguments.Offset, size); _memoryAllocator.DeallocateRange(arguments.Offset, size);
addressSpaceContext.Gmm.Unmap(arguments.Offset, size); _asContext.Gmm.Unmap(arguments.Offset, size);
} }
else else
{ {
@ -168,16 +170,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
private NvInternalResult UnmapBuffer(ref UnmapBufferArguments arguments) private NvInternalResult UnmapBuffer(ref UnmapBufferArguments arguments)
{ {
AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context); lock (_asContext)
lock (addressSpaceContext)
{ {
if (addressSpaceContext.RemoveMap(arguments.Offset, out ulong size)) if (_asContext.RemoveMap(arguments.Offset, out ulong size))
{ {
if (size != 0) if (size != 0)
{ {
_memoryAllocator.DeallocateRange(arguments.Offset, size); _memoryAllocator.DeallocateRange(arguments.Offset, size);
addressSpaceContext.Gmm.Unmap(arguments.Offset, size); _asContext.Gmm.Unmap(arguments.Offset, size);
} }
} }
else else
@ -191,22 +191,20 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
private NvInternalResult MapBufferEx(ref MapBufferExArguments arguments) 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}!"; 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);
ulong physicalAddress; ulong physicalAddress;
if ((arguments.Flags & AddressSpaceFlags.RemapSubRange) != 0) 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; ulong virtualAddress = arguments.Offset + arguments.BufferOffset;
physicalAddress += arguments.BufferOffset; physicalAddress += arguments.BufferOffset;
addressSpaceContext.Gmm.Map(physicalAddress, virtualAddress, arguments.MappingSize); _asContext.Gmm.Map(physicalAddress, virtualAddress, arguments.MappingSize);
return NvInternalResult.Success; return NvInternalResult.Success;
} }
@ -246,7 +244,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
NvInternalResult result = NvInternalResult.Success; NvInternalResult result = NvInternalResult.Success;
lock (addressSpaceContext) lock (_asContext)
{ {
// Note: When the fixed offset flag is not set, // Note: When the fixed offset flag is not set,
// the Offset field holds the alignment size instead. // the Offset field holds the alignment size instead.
@ -254,13 +252,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
if (!virtualAddressAllocated) 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 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); Logger.Warning?.Print(LogClass.ServiceNv, message);
@ -275,7 +273,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
_memoryAllocator.AllocateRange(va, size, freeAddressStartPosition); _memoryAllocator.AllocateRange(va, size, freeAddressStartPosition);
} }
addressSpaceContext.Gmm.Map(physicalAddress, va, size); _asContext.Gmm.Map(physicalAddress, va, size);
arguments.Offset = va; arguments.Offset = va;
} }
@ -289,7 +287,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
} }
else 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) private NvInternalResult Remap(Span<RemapArguments> arguments)
{ {
AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context); MemoryManager gmm = _asContext.Gmm;
for (int index = 0; index < arguments.Length; index++) for (int index = 0; index < arguments.Length; index++)
{ {
MemoryManager gmm = GetAddressSpaceContext(Context).Gmm;
ulong mapOffs = (ulong)arguments[index].MapOffset << 16; ulong mapOffs = (ulong)arguments[index].MapOffset << 16;
ulong gpuVa = (ulong)arguments[index].GpuOffset << 16; ulong gpuVa = (ulong)arguments[index].GpuOffset << 16;
ulong size = (ulong)arguments[index].Pages << 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 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> _maps;
private readonly SortedList<ulong, Range> _reservations; 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>(); _maps = new SortedList<ulong, Range>();
_reservations = 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); 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 left = 0;
int right = list.Count - 1; int right = list.Count - 1;
@ -154,7 +154,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types
return null; return null;
} }
private Range BinarySearchLt(SortedList<ulong, Range> list, ulong address) private static Range BinarySearchLt(SortedList<ulong, Range> list, ulong address)
{ {
Range ltRg = null; Range ltRg = null;

View file

@ -1,13 +1,13 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.Gpu.Memory; 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.NvHostChannel.Types;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
using Ryujinx.HLE.HOS.Services.Nv.Types; using Ryujinx.HLE.HOS.Services.Nv.Types;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Collections.Concurrent;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -15,6 +15,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
{ {
class NvHostChannelDeviceFile : NvDeviceFile class NvHostChannelDeviceFile : NvDeviceFile
{ {
private static readonly ConcurrentDictionary<long, Host1xContext> _host1xContextRegistry = new();
private const uint MaxModuleSyncpoint = 16; private const uint MaxModuleSyncpoint = 16;
private uint _timeout; private uint _timeout;
@ -24,8 +26,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
private readonly Switch _device; private readonly Switch _device;
private readonly IVirtualMemoryManager _memory; private readonly IVirtualMemoryManager _memory;
private readonly NvMemoryAllocator _memoryAllocator; private readonly Host1xContext _host1xContext;
private readonly GpuChannel _channel;
public GpuChannel Channel { get; }
public enum ResourcePolicy 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) public NvHostChannelDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner)
{ {
_device = context.Device; _device = context.Device;
_memory = memory; _memory = memory;
_timeout = 3000; _timeout = 3000;
_submitTimeout = 0; _submitTimeout = 0;
_timeslice = 0; _timeslice = 0;
_memoryAllocator = _device.MemoryAllocator; _host1xContext = GetHost1XContext(context.Device.Gpu, owner);
_channel = _device.Gpu.CreateChannel(); Channel = _device.Gpu.CreateChannel();
ChannelSyncpoints = new uint[MaxModuleSyncpoint]; 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); 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; tmpCmdBuff[0] = (4 << 28) | (int)fences[0].Id;
_device.Host1x.Submit(tmpCmdBuff); _host1xContext.Host1x.Submit(tmpCmdBuff);
return NvInternalResult.Success; return NvInternalResult.Success;
} }
@ -233,7 +236,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>(); int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0]; MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries); 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) foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
{ {
@ -250,12 +252,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
{ {
if (map.DmaMapAddress == 0) 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) if (va != NvMemoryAllocator.PteUnmapped && va <= uint.MaxValue && (va + (uint)map.Size) <= uint.MaxValue)
{ {
_memoryAllocator.AllocateRange(va, (uint)map.Size, freeAddressStartPosition); _host1xContext.MemoryAllocator.AllocateRange(va, (uint)map.Size, freeAddressStartPosition);
gmm.Map(map.Address, va, (uint)map.Size); _host1xContext.Smmu.Map(map.Address, va, (uint)map.Size);
map.DmaMapAddress = va; map.DmaMapAddress = va;
} }
else else
@ -276,7 +278,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>(); int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0]; MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries); 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) 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. // 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. // 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; // 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)) 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; header.Fence.Id = _channelSyncpoint.Id;
@ -456,7 +457,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement)) 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; header.Flags = SubmitGpfifoFlags.None;
@ -545,7 +546,22 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
public override void Close() 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.Graphics.Gpu.Memory;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices namespace Ryujinx.HLE.HOS.Services.Nv
{ {
class NvMemoryAllocator class NvMemoryAllocator
{ {

View file

@ -49,8 +49,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
private class TextureCallbackInformation private class TextureCallbackInformation
{ {
public Layer Layer; public Layer Layer;
public BufferItem Item; public BufferItem Item;
} }
public SurfaceFlinger(Switch device) public SurfaceFlinger(Switch device)
@ -385,6 +385,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
} }
_device.Gpu.Window.EnqueueFrameThreadSafe( _device.Gpu.Window.EnqueueFrameThreadSafe(
layer.Owner,
frameBufferAddress, frameBufferAddress,
frameBufferWidth, frameBufferWidth,
frameBufferHeight, frameBufferHeight,

View file

@ -1,14 +1,10 @@
using Ryujinx.Audio.Backends.CompatLayer; using Ryujinx.Audio.Backends.CompatLayer;
using Ryujinx.Audio.Integration; using Ryujinx.Audio.Integration;
using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.Host1x;
using Ryujinx.Graphics.Nvdec;
using Ryujinx.Graphics.Vic;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Apm; using Ryujinx.HLE.HOS.Services.Apm;
using Ryujinx.HLE.HOS.Services.Hid; using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
@ -24,10 +20,6 @@ namespace Ryujinx.HLE
public GpuContext Gpu { get; } public GpuContext Gpu { get; }
internal NvMemoryAllocator MemoryAllocator { get; }
internal Host1xDevice Host1x { get; }
public VirtualFileSystem FileSystem => Configuration.VirtualFileSystem; public VirtualFileSystem FileSystem => Configuration.VirtualFileSystem;
public Horizon System { get; } public Horizon System { get; }
@ -71,29 +63,6 @@ namespace Ryujinx.HLE
Gpu = new GpuContext(configuration.GpuRenderer); 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 = new Horizon(this);
System.InitializeServices(); System.InitializeServices();
@ -190,7 +159,6 @@ namespace Ryujinx.HLE
if (disposing) if (disposing)
{ {
System.Dispose(); System.Dispose();
Host1x.Dispose();
AudioDeviceDriver.Dispose(); AudioDeviceDriver.Dispose();
FileSystem.Unload(); FileSystem.Unload();
Memory.Dispose(); Memory.Dispose();