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>
@ -148,6 +150,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <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,
PhysicalMemory physicalMemory,
TextureInfo info, TextureInfo info,
SizeInfo sizeInfo, SizeInfo sizeInfo,
MultiRange range, MultiRange range,
@ -156,7 +159,7 @@ namespace Ryujinx.Graphics.Gpu.Image
float scaleFactor, 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,12 +199,19 @@ 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;
@ -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,24 +396,28 @@ 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 a range of memory was supplied, just check if the ranges match.
if (range != null && !overlap.Range.Equals(range.Value)) if (!overlap.Range.Equals(range.Value))
{ {
continue; continue;
} }
}
else
{
// If no range was supplied, we can check if the GPU virtual address match. If they do, // 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. // 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 // 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. // 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 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) && if ((overlap.Info.GpuAddress != info.GpuAddress || overlap.ChangedMapping) &&
!_context.MemoryManager.CompareRange(overlap.Range, info.GpuAddress)) !memoryManager.CompareRange(overlap.Range, info.GpuAddress))
{ {
continue; continue;
} }
} }
}
if (matchQuality == TextureMatchQuality.Perfect) if (matchQuality == TextureMatchQuality.Perfect)
{ {
@ -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,12 +69,14 @@ 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;
_physicalMemory = physicalMemory;
Address = address; Address = address;
Size = size; Size = 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,7 +210,7 @@ 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
{ {
@ -48,8 +51,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
_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

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