Просмотр исходного кода

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
gdkchan 4 лет назад
Родитель
Сommit
fbb4019ed5
44 измененных файлов с 780 добавлено и 481 удалено
  1. 7 5
      Ryujinx.Graphics.Gpu/Engine/Compute.cs
  2. 3 3
      Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs
  3. 7 6
      Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs
  4. 6 1
      Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs
  5. 10 9
      Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs
  6. 19 15
      Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs
  7. 24 10
      Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs
  8. 16 2
      Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs
  9. 3 5
      Ryujinx.Graphics.Gpu/Engine/MethodReport.cs
  10. 1 1
      Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs
  11. 11 8
      Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs
  12. 17 29
      Ryujinx.Graphics.Gpu/Engine/Methods.cs
  13. 37 4
      Ryujinx.Graphics.Gpu/GpuChannel.cs
  14. 95 44
      Ryujinx.Graphics.Gpu/GpuContext.cs
  15. 14 5
      Ryujinx.Graphics.Gpu/Image/Pool.cs
  16. 4 1
      Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
  17. 37 19
      Ryujinx.Graphics.Gpu/Image/Texture.cs
  18. 8 9
      Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
  19. 58 28
      Ryujinx.Graphics.Gpu/Image/TextureCache.cs
  20. 8 4
      Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
  21. 9 4
      Ryujinx.Graphics.Gpu/Image/TexturePool.cs
  22. 6 21
      Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs
  23. 14 11
      Ryujinx.Graphics.Gpu/Memory/Buffer.cs
  24. 25 18
      Ryujinx.Graphics.Gpu/Memory/BufferCache.cs
  25. 25 33
      Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
  26. 22 9
      Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
  27. 62 8
      Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
  28. 2 2
      Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs
  29. 30 22
      Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
  30. 25 2
      Ryujinx.Graphics.Gpu/Window.cs
  31. 11 3
      Ryujinx.HLE/HOS/ArmProcessContext.cs
  32. 11 3
      Ryujinx.HLE/HOS/ArmProcessContextFactory.cs
  33. 1 1
      Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs
  34. 15 22
      Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs
  35. 1 1
      Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs
  36. 2 2
      Ryujinx.HLE/HOS/ProgramLoader.cs
  37. 47 0
      Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs
  38. 9 9
      Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs
  39. 32 41
      Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs
  40. 4 4
      Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs
  41. 38 22
      Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs
  42. 1 1
      Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs
  43. 3 2
      Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs
  44. 0 32
      Ryujinx.HLE/Switch.cs

+ 7 - 5
Ryujinx.Graphics.Gpu/Engine/Compute.cs

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

+ 3 - 3
Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs

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

+ 7 - 6
Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs

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

+ 6 - 1
Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs

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

+ 10 - 9
Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs

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

+ 19 - 15
Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs

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

+ 24 - 10
Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs

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

+ 16 - 2
Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs

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

+ 3 - 5
Ryujinx.Graphics.Gpu/Engine/MethodReport.cs

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

+ 1 - 1
Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs

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

+ 11 - 8
Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs

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

+ 17 - 29
Ryujinx.Graphics.Gpu/Engine/Methods.cs

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

+ 37 - 4
Ryujinx.Graphics.Gpu/GpuChannel.cs

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

+ 95 - 44
Ryujinx.Graphics.Gpu/GpuContext.cs

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

+ 14 - 5
Ryujinx.Graphics.Gpu/Image/Pool.cs

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

+ 4 - 1
Ryujinx.Graphics.Gpu/Image/SamplerPool.cs

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

+ 37 - 19
Ryujinx.Graphics.Gpu/Image/Texture.cs

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

+ 8 - 9
Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs

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

+ 58 - 28
Ryujinx.Graphics.Gpu/Image/TextureCache.cs

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

+ 8 - 4
Ryujinx.Graphics.Gpu/Image/TextureGroup.cs

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

+ 9 - 4
Ryujinx.Graphics.Gpu/Image/TexturePool.cs

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

+ 6 - 21
Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs

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

+ 14 - 11
Ryujinx.Graphics.Gpu/Memory/Buffer.cs

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

+ 25 - 18
Ryujinx.Graphics.Gpu/Memory/BufferCache.cs

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

+ 25 - 33
Ryujinx.Graphics.Gpu/Memory/BufferManager.cs

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

+ 22 - 9
Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs

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

+ 62 - 8
Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs

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

+ 2 - 2
Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs

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

+ 30 - 22
Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs

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

+ 25 - 2
Ryujinx.Graphics.Gpu/Window.cs

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

+ 11 - 3
Ryujinx.HLE/HOS/ArmProcessContext.cs

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

+ 11 - 3
Ryujinx.HLE/HOS/ArmProcessContextFactory.cs

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

+ 1 - 1
Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs

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

+ 15 - 22
Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs

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

+ 1 - 1
Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs

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

+ 2 - 2
Ryujinx.HLE/HOS/ProgramLoader.cs

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

+ 47 - 0
Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs

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

+ 9 - 9
Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs

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

+ 32 - 41
Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs

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

+ 4 - 4
Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs

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

+ 38 - 22
Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs

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

+ 1 - 1
Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs

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

+ 3 - 2
Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs

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

+ 0 - 32
Ryujinx.HLE/Switch.cs

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