diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute.cs b/Ryujinx.Graphics.Gpu/Engine/Compute.cs index a7f6ec06..26041ecc 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Compute.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Compute.cs @@ -16,11 +16,13 @@ namespace Ryujinx.Graphics.Gpu.Engine /// Method call argument public void Dispatch(GpuState state, int argument) { - FlushUboDirty(); + var memoryManager = state.Channel.MemoryManager; + + FlushUboDirty(memoryManager); uint qmdAddress = (uint)state.Get(MethodOffset.DispatchParamsAddress); - var qmd = _context.MemoryManager.Read((ulong)qmdAddress << 8); + var qmd = state.Channel.MemoryManager.Read((ulong)qmdAddress << 8); GpuVa shaderBaseAddress = state.Get(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(cbDescAddress); + SbDescriptor cbDescriptor = state.Channel.MemoryManager.Physical.Read(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(sbDescAddress); + SbDescriptor sbDescriptor = state.Channel.MemoryManager.Physical.Read(sbDescAddress); state.Channel.BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags); } diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs index 84d35350..75b19c37 100644 --- a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs +++ b/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(address); + int mem = _parent.MemoryManager.Read(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); } } diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs index 0e284ac5..ada3bc4b 100644 --- a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs +++ b/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 /// /// Fetch the command buffer. /// - public void Fetch(GpuContext context) + public void Fetch(MemoryManager memoryManager) { if (Words == null) { - Words = MemoryMarshal.Cast(context.MemoryManager.GetSpan(EntryAddress, (int)EntryCount * 4, true)).ToArray(); + Words = MemoryMarshal.Cast(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. diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs index dc8a1c75..c683d179 100644 --- a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs +++ b/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; /// /// 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]; diff --git a/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs b/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs index a31ec72a..3d23b785 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs +++ b/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 /// /// Performs actual copy of the inline data after the transfer is finished. /// - private void FinishTransfer() + /// Current GPU state + private void FinishTransfer(GpuState state) { Span data = MemoryMarshal.Cast(_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 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 pixel = data.Slice(srcOffset, 1); - _context.PhysicalMemory.Write(dstAddress, pixel); + state.Channel.MemoryManager.Physical.Write(dstAddress, pixel); } } } diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs b/Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs index 0d7c272c..039ed78e 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs +++ b/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 /// /// Checks if the counter value at a given GPU memory address is non-zero. /// + /// GPU state /// GPU virtual address of the counter value /// True if the value is not zero, false otherwise. Returns host if handling with host conditional rendering - 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(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False; + return (state.Channel.MemoryManager.Read(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False; } } /// /// Checks if the counter at a given GPU memory address passes a specified equality comparison. /// + /// GPU state /// GPU virtual address /// True to check if the values are equal, false to check if they are not equal /// True if the condition is met, false otherwise. Returns host if handling with host conditional rendering - 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(gpuVa + 16), isEqual); + useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, state.Channel.MemoryManager.Read(gpuVa + 16), isEqual); } else if (evt == null && evt2 != null) { - useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt2, _context.MemoryManager.Read(gpuVa), isEqual); + useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt2, state.Channel.MemoryManager.Read(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(gpuVa); - ulong y = _context.MemoryManager.Read(gpuVa + 16); + ulong x = state.Channel.MemoryManager.Read(gpuVa); + ulong y = state.Channel.MemoryManager.Read(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. /// + /// GPU counter cache to search on /// GPU virtual address where the counter is supposed to be written /// The counter event, or null if not present - private ICounterEvent FindEvent(ulong gpuVa) + private static ICounterEvent FindEvent(CounterCache counterCache, ulong gpuVa) { - return _counterCache.FindEvent(gpuVa); + return counterCache.FindEvent(gpuVa); } } } diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs b/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs index c4d8a83d..9064051a 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs +++ b/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 srcSpan = _context.PhysicalMemory.GetSpan(srcBaseAddress + (ulong)srcBaseOffset, srcSize, true); - Span dstSpan = _context.PhysicalMemory.GetSpan(dstBaseAddress + (ulong)dstBaseOffset, dstSize).ToArray(); + ReadOnlySpan srcSpan = state.Channel.MemoryManager.Physical.GetSpan(srcBaseAddress + (ulong)srcBaseOffset, srcSize, true); + Span 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 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(MethodOffset.CopyBufferConstA)); + state.Channel.MemoryManager.Physical.BufferCache.ClearBuffer( + state.Channel.MemoryManager, + cbp.DstAddress, + (uint)size * 4, + state.Get(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); } } } diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs b/Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs index d0570262..946d0dd5 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs @@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Engine /// Method call argument private void CopyTexture(GpuState state, int argument) { + var memoryManager = state.Channel.MemoryManager; + var dstCopyTexture = state.Get(MethodOffset.CopyDstTexture); var srcCopyTexture = state.Get(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) { diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs b/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs index 8320ba65..2dd0bbfa 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs +++ b/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(); - /// /// Writes a GPU counter to guest memory. /// @@ -39,7 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Engine { var rs = state.Get(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); } /// diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs index 33533e8b..0746efa5 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs +++ b/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) { diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs index 981d2e94..1343dbe7 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs @@ -16,11 +16,12 @@ namespace Ryujinx.Graphics.Gpu.Engine /// /// Flushes any queued ubo updates. /// - private void FlushUboDirty() + /// GPU memory manager where the uniform buffer is mapped + 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(MemoryMarshal.CreateSpan(ref argument, 1))); + var byteData = MemoryMarshal.Cast(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(data)); + var byteData = MemoryMarshal.Cast(data); + state.Channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData); _ubFollowUpAddress = address + size; _ubByteCount += size; diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index 96741cd6..aaac9441 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs @@ -22,21 +22,6 @@ namespace Ryujinx.Graphics.Gpu.Engine private readonly GpuContext _context; private readonly ShaderProgramInfo[] _currentProgramInfo; - /// - /// In-memory shader cache. - /// - public ShaderCache ShaderCache { get; } - - /// - /// GPU buffer manager. - /// - public BufferCache BufferCache { get; } - - /// - /// GPU texture manager. - /// - 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; } /// @@ -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(sbDescAddress); + SbDescriptor sbDescriptor = state.Channel.MemoryManager.Physical.Read(sbDescAddress); state.Channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags); } @@ -357,6 +333,7 @@ namespace Ryujinx.Graphics.Gpu.Engine /// If this is not -1, it indicates that only the given indexed target will be used. private void UpdateRenderTargetState(GpuState state, bool useControl, int singleUse = -1) { + var memoryManager = state.Channel.MemoryManager; var rtControl = state.Get(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(MethodOffset.RtDepthStencilState); var dsSize = state.Get(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; diff --git a/Ryujinx.Graphics.Gpu/GpuChannel.cs b/Ryujinx.Graphics.Gpu/GpuChannel.cs index 79143449..e9f08eb8 100644 --- a/Ryujinx.Graphics.Gpu/GpuChannel.cs +++ b/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; /// /// Channel buffer bindings manager. @@ -24,6 +26,11 @@ namespace Ryujinx.Graphics.Gpu /// internal TextureManager TextureManager { get; } + /// + /// Current channel memory manager. + /// + internal MemoryManager MemoryManager => _memoryManager; + /// /// Creates a new instance of a GPU channel. /// @@ -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); } + /// + /// Binds a memory manager to the channel. + /// All submitted and in-flight commands will use the specified memory manager for any memory operations. + /// + /// The new memory manager to be bound + 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; + } + /// /// 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 /// public void Dispose() { - _context.DisposedChannels.Enqueue(this); + _context.DeferredActions.Enqueue(Destroy); } /// /// Performs disposal of the host GPU resources used by this channel, that are not shared. /// This must only be called from the render thread. /// - 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(); + } } } } diff --git a/Ryujinx.Graphics.Gpu/GpuContext.cs b/Ryujinx.Graphics.Gpu/GpuContext.cs index 2ba832bb..7fae249e 100644 --- a/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/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 /// public IRenderer Renderer { get; } - /// - /// Physical memory access (it actually accesses the process memory, not actual physical memory). - /// - internal PhysicalMemory PhysicalMemory { get; private set; } - - /// - /// GPU memory manager. - /// - public MemoryManager MemoryManager { get; } - /// /// GPU engine methods processing. /// @@ -73,11 +65,14 @@ namespace Ryujinx.Graphics.Gpu internal List SyncActions { get; } /// - /// Queue with closed channels for deferred disposal from the render thread. + /// Queue with deferred actions that must run on the render thread. /// - internal Queue DisposedChannels { get; } + internal Queue DeferredActions { get; } - private readonly Lazy _caps; + /// + /// Registry with physical memories that can be used with this GPU context, keyed by owner process ID. + /// + internal ConcurrentDictionary PhysicalMemoryRegistry { get; } /// /// Host hardware capabilities. @@ -87,11 +82,9 @@ namespace Ryujinx.Graphics.Gpu /// /// Event for signalling shader cache loading progress. /// - public event Action ShaderCacheStateChanged - { - add => Methods.ShaderCache.ShaderCacheStateChanged += value; - remove => Methods.ShaderCache.ShaderCacheStateChanged -= value; - } + public event Action ShaderCacheStateChanged; + + private readonly Lazy _caps; /// /// 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(Renderer.GetCapabilities); - HostInitalized = new ManualResetEvent(false); SyncActions = new List(); - DisposedChannels = new Queue(); + DeferredActions = new Queue(); + + PhysicalMemoryRegistry = new ConcurrentDictionary(); + + _caps = new Lazy(Renderer.GetCapabilities); } + /// + /// Creates a new GPU channel. + /// + /// The GPU channel public GpuChannel CreateChannel() { return new GpuChannel(this); } + /// + /// Creates a new GPU memory manager. + /// + /// ID of the process that owns the memory manager + /// The memory manager + /// Thrown when is invalid + 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); + } + + /// + /// Registers virtual memory used by a process for GPU memory access, caching and read/write tracking. + /// + /// ID of the process that owns + /// Virtual memory owned by the process + /// Thrown if was already registered + 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; + } + + /// + /// Unregisters a process, indicating that its memory will no longer be used, and that caches can be freed. + /// + /// ID of the process + public void UnregisterProcess(long pid) + { + if (PhysicalMemoryRegistry.TryRemove(pid, out var physicalMemory)) + { + physicalMemory.ShaderCache.ShaderCacheStateChanged -= ShaderCacheStateUpdate; + physicalMemory.Dispose(); + } + } + + /// + /// Shader cache state update handler. + /// + /// Current state of the shader cache load process + /// Number of the current shader being processed + /// Total number of shaders to process + private void ShaderCacheStateUpdate(ShaderCacheState state, int current, int total) + { + ShaderCacheStateChanged?.Invoke(state, current, total); + } + /// /// Initialize the GPU shader cache. /// @@ -132,7 +186,10 @@ namespace Ryujinx.Graphics.Gpu { HostInitalized.WaitOne(); - Methods.ShaderCache.Initialize(); + foreach (var physicalMemory in PhysicalMemoryRegistry.Values) + { + physicalMemory.ShaderCache.Initialize(); + } } /// @@ -144,16 +201,6 @@ namespace Ryujinx.Graphics.Gpu SequenceNumber++; } - /// - /// Sets the process memory manager, after the application process is initialized. - /// This is required for any GPU memory access. - /// - /// CPU memory manager - public void SetVmm(Cpu.IVirtualMemoryManagerTracked cpuMemory) - { - PhysicalMemory = new PhysicalMemory(cpuMemory); - } - /// /// Registers an action to be performed the next time a syncpoint is incremented. /// This will also ensure a host sync object is created, and is incremented. @@ -186,14 +233,14 @@ namespace Ryujinx.Graphics.Gpu } /// - /// 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. /// - 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 /// 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(); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/Pool.cs b/Ryujinx.Graphics.Gpu/Image/Pool.cs index 0b4c2993..a06a7ccf 100644 --- a/Ryujinx.Graphics.Gpu/Image/Pool.cs +++ b/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 _modifiedDelegate; - public Pool(GpuContext context, ulong address, int maximumId) + /// + /// Creates a new instance of the GPU resource pool. + /// + /// GPU context that the pool belongs to + /// Physical memory where the resource descriptors are mapped + /// Address of the pool in physical memory + /// Maximum index of an item on the pool (inclusive) + 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; } - /// /// Gets the descriptor for a given ID. /// @@ -65,7 +74,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// The descriptor public T2 GetDescriptor(int id) { - return Context.PhysicalMemory.Read(Address + (ulong)id * DescriptorSize); + return PhysicalMemory.Read(Address + (ulong)id * DescriptorSize); } /// diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs index 1395aea2..aed6cb9c 100644 --- a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs +++ b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs @@ -1,3 +1,5 @@ +using Ryujinx.Graphics.Gpu.Memory; + namespace Ryujinx.Graphics.Gpu.Image { /// @@ -11,9 +13,10 @@ namespace Ryujinx.Graphics.Gpu.Image /// Constructs a new instance of the sampler pool. /// /// GPU context that the sampler pool belongs to + /// Physical memory where the sampler descriptors are mapped /// Address of the sampler pool in guest memory /// Maximum sampler ID of the sampler pool (equal to maximum samplers minus one) - 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) { } /// /// Gets the sampler with the given ID. diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 0948c494..6ca15ea1 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/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. /// /// GPU context that the texture belongs to + /// Physical memory where the texture is mapped /// Texture information /// Size information of the texture /// Physical memory ranges where the texture data is located @@ -147,16 +149,17 @@ namespace Ryujinx.Graphics.Gpu.Image /// The floating point scale factor to initialize with /// The scale mode to initialize with 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. /// /// GPU context that the texture belongs to + /// Physical memory where the texture is mapped /// Texture information /// Size information of the texture /// Physical memory ranges where the texture data is located /// The scale mode to initialize with. If scaled, the texture's data is loaded immediately and scaled up - 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); } /// @@ -189,14 +199,21 @@ namespace Ryujinx.Graphics.Gpu.Image /// Other fields are initialized with their default values. /// /// GPU context that the texture belongs to + /// Physical memory where the texture is mapped /// Texture information /// Size information of the texture /// Physical memory ranges where the texture data is located - 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 /// True if the texture will have mip views 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 data = _context.PhysicalMemory.GetSpan(Range); + ReadOnlySpan 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); diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index 3689975d..14bc27a9 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Type of the sampler pool indexing used for bound samplers 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 /// Maximum ID of the pool (total count minus one) 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(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 /// The packed texture and sampler ID (the real texture handle) 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(textureBufferAddress + (ulong)(wordOffset & HandleMask) * 4); + int handle = _channel.MemoryManager.Physical.Read(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(samplerBufferAddress + (ulong)((wordOffset >> HandleHigh) - 1) * 4); + handle |= _channel.MemoryManager.Physical.Read(samplerBufferAddress + (ulong)((wordOffset >> HandleHigh) - 1) * 4); } return handle; @@ -514,7 +514,6 @@ namespace Ryujinx.Graphics.Gpu.Image public void Dispose() { _samplerPool?.Dispose(); - _texturePoolCache.Dispose(); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 24fa723a..37a2219f 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/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 _textures; @@ -44,9 +45,11 @@ namespace Ryujinx.Graphics.Gpu.Image /// Constructs a new instance of the texture manager. /// /// The GPU context that the texture manager belongs to - public TextureCache(GpuContext context) + /// Physical memory where the textures managed by this cache are mapped + public TextureCache(GpuContext context, PhysicalMemory physicalMemory) { _context = context; + _physicalMemory = physicalMemory; _textures = new MultiRangeList(); @@ -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 /// /// Tries to find an existing texture, or create a new one if not found. /// + /// GPU memory manager where the texture is mapped /// Copy texture to find or create /// Offset to be added to the physical texture address /// Format information of the copy texture /// Indicates if the texture should be scaled from the start /// A hint indicating the minimum used size for the texture /// The texture - 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 /// /// Tries to find an existing texture, or create a new one if not found. /// + /// GPU memory manager where the texture is mapped /// Color buffer texture to find or create /// Number of samples in the X direction, for MSAA /// Number of samples in the Y direction, for MSAA /// A hint indicating the minimum used size for the texture /// The texture - 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 /// /// Tries to find an existing texture, or create a new one if not found. /// + /// GPU memory manager where the texture is mapped /// Depth-stencil buffer texture to find or create /// Size of the depth-stencil texture /// Number of samples in the X direction, for MSAA /// Number of samples in the Y direction, for MSAA /// A hint indicating the minimum used size for the texture /// The texture - 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 /// /// Tries to find an existing texture, or create a new one if not found. /// + /// GPU memory manager where the texture is mapped /// The texture search flags, defines texture comparison rules /// Texture information of the texture to be found or created /// Size in bytes of a single texture layer /// A hint indicating the minimum used size for the texture /// Optional ranges of physical memory where the texture data is located /// The texture - 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 /// /// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null. /// + /// GPU memory manager where the texture is mapped /// The texture information /// The copy buffer parameters /// The copy buffer swizzle /// True if the texture has a linear layout, false otherwise /// A matching texture, or null if there is no match - 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) { diff --git a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 30ca59d4..a43eccd6 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/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 /// 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. /// /// GPU context that the texture group belongs to + /// Physical memory where the texture is mapped /// The storage texture for this group - 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 data = _context.PhysicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size)); + ReadOnlySpan 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 /// A CpuRegionHandle covering the given range private CpuRegionHandle GenerateHandle(ulong address, ulong size) { - return _context.PhysicalMemory.BeginTracking(address, size); + return _physicalMemory.BeginTracking(address, size); } /// diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index 128dd89e..bcce443c 100644 --- a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Gpu.Image class TexturePool : Pool { private int _sequenceNumber; + private readonly GpuChannel _channel; private readonly ConcurrentQueue _dereferenceQueue = new ConcurrentQueue(); /// @@ -24,9 +25,13 @@ namespace Ryujinx.Graphics.Gpu.Image /// Constructs a new instance of the texture pool. /// /// GPU context that the texture pool belongs to + /// GPU channel that the texture pool belongs to /// Address of the texture pool in guest memory /// Maximum texture ID of the texture pool (equal to maximum textures minus one) - 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; + } /// /// 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(address); + TextureDescriptor descriptor = PhysicalMemory.Read(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})."); } diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs b/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs index c9eebf8b..99c5a88b 100644 --- a/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs +++ b/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. /// - class TexturePoolCache : IDisposable + class TexturePoolCache { private const int MaxCapacity = 4; - private GpuContext _context; - - private LinkedList _pools; + private readonly GpuContext _context; + private readonly LinkedList _pools; /// /// 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(); } /// /// Finds a cache texture pool, or creates a new one if not found. /// + /// GPU channel that the texture pool cache belongs to /// Start address of the texture pool /// Maximum ID of the texture pool /// The found or newly created texture pool - 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; } - - /// - /// Disposes the texture pool cache. - /// It's an error to use the texture pool cache after disposal. - /// - public void Dispose() - { - foreach (TexturePool pool in _pools) - { - pool.Dispose(); - } - - _pools.Clear(); - } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs index b4854d81..96e10e77 100644 --- a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs +++ b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -13,9 +13,10 @@ namespace Ryujinx.Graphics.Gpu.Memory /// class Buffer : IRange, IDisposable { - private static ulong GranularBufferThreshold = 4096; + private const ulong GranularBufferThreshold = 4096; private readonly GpuContext _context; + private readonly PhysicalMemory _physicalMemory; /// /// Host buffer handle. @@ -68,14 +69,16 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Creates a new instance of the buffer. /// /// GPU context that the buffer belongs to + /// Physical memory where the buffer is mapped /// Start address of the buffer /// Size of the buffer in bytes /// Buffers which this buffer contains, and will inherit tracking handles from - public Buffer(GpuContext context, ulong address, ulong size, IEnumerable baseBuffers = null) + public Buffer(GpuContext context, PhysicalMemory physicalMemory, ulong address, ulong size, IEnumerable 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)); } /// @@ -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); } /// diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs index eb2d08ae..b78cbdaa 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs +++ b/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 _buffers; @@ -32,9 +33,11 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Creates a new instance of the buffer manager. /// /// The GPU context that the buffer manager belongs to - public BufferCache(GpuContext context) + /// Physical memory where the cached buffers are mapped + public BufferCache(GpuContext context, PhysicalMemory physicalMemory) { _context = context; + _physicalMemory = physicalMemory; _buffers = new RangeList(); @@ -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. /// + /// GPU memory manager where the buffer is mapped /// Start GPU virtual address of the buffer /// Size in bytes of the buffer /// CPU virtual address of the buffer, after address translation - 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. /// + /// GPU memory manager where the buffer is mapped /// Start GPU virtual address of the buffer /// Size in bytes of the buffer - 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 /// /// This does a GPU side copy. /// + /// GPU memory manager where the buffer is mapped /// GPU virtual address of the copy source /// GPU virtual address of the copy destination /// Size in bytes of the copy - 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 /// /// Both the address and size must be aligned to 4 bytes. /// + /// GPU memory manager where the buffer is mapped /// GPU virtual address of the region to clear /// Number of bytes to clear /// Value to be written into the buffer - 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); diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index e43cb3b3..eccc2ca3 100644 --- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -12,11 +12,12 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Buffer manager. /// - 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. /// /// GPU context that the buffer manager belongs to - public BufferManager(GpuContext context) + /// GPU channel that the buffer manager belongs to + 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(); - - context.Methods.BufferCache.NotifyBuffersModified += Rebind; } @@ -140,7 +141,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Type of each index buffer element 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 /// Vertex divisor of the buffer, for instanced draws 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 /// Size in bytes of the transform feedback buffer 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 /// Size in bytes of the storage buffer 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 /// Size in bytes of the storage buffer 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 /// Whether the binding is for an image or a sampler 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; } - - /// - /// Disposes the buffer manager. - /// It is an error to use the buffer manager after disposal. - /// - public void Dispose() - { - _context.Methods.BufferCache.NotifyBuffersModified -= Rebind; - } } } diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs index 5776836c..b747b558 100644 --- a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -34,15 +34,28 @@ namespace Ryujinx.Graphics.Gpu.Memory public event EventHandler MemoryUnmapped; - private GpuContext _context; + /// + /// Physical memory where the virtual memory is mapped into. + /// + internal PhysicalMemory Physical { get; } + + /// + /// Cache of GPU counters. + /// + internal CounterCache CounterCache { get; } /// /// Creates a new instance of the GPU memory manager. /// - public MemoryManager(GpuContext context) + /// Physical memory that this memory manager will map into + 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; } /// @@ -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 /// The data to be written public void Write(ulong va, ReadOnlySpan data) { - WriteImpl(va, data, _context.PhysicalMemory.Write); + WriteImpl(va, data, Physical.Write); } /// @@ -165,7 +178,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The data to be written public void WriteUntracked(ulong va, ReadOnlySpan data) { - WriteImpl(va, data, _context.PhysicalMemory.WriteUntracked); + WriteImpl(va, data, Physical.WriteUntracked); } private delegate void WriteCallback(ulong address, ReadOnlySpan data); diff --git a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs index 6463b932..289e7c1b 100644 --- a/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs +++ b/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; + + /// + /// In-memory shader cache. + /// + public ShaderCache ShaderCache { get; } + + /// + /// GPU buffer manager. + /// + public BufferCache BufferCache { get; } + + /// + /// GPU texture manager. + /// + public TextureCache TextureCache { get; } /// /// Creates a new instance of the physical memory. /// + /// GPU context that the physical memory belongs to /// CPU memory manager of the application process - 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; + } + + /// + /// Increments the memory reference count. + /// + public void IncrementReferenceCount() + { + Interlocked.Increment(ref _referenceCount); + } + + /// + /// Decrements the memory reference count. + /// + public void DecrementReferenceCount() + { + if (Interlocked.Decrement(ref _referenceCount) == 0 && _cpuMemory is IRefCounted rc) + { + rc.DecrementReferenceCount(); + } } /// @@ -147,7 +193,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Ranges of physical memory where the data is located /// Data to be written /// Callback method that will perform the write - private void WriteImpl(MultiRange range, ReadOnlySpan data, WriteCallback writeCallback) + private static void WriteImpl(MultiRange range, ReadOnlySpan data, WriteCallback writeCallback) { if (range.Count == 1) { @@ -227,12 +273,20 @@ namespace Ryujinx.Graphics.Gpu.Memory /// public void Dispose() { - if (_cpuMemory is IRefCounted rc) - { - rc.DecrementReferenceCount(); + _context.DeferredActions.Enqueue(Destroy); + } - _cpuMemory = null; - } + /// + /// Performs disposal of the host GPU caches with resources mapped on this physical memory. + /// This must only be called from the render thread. + /// + private void Destroy() + { + ShaderCache.Dispose(); + BufferCache.Dispose(); + TextureCache.Dispose(); + + DecrementReferenceCount(); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index 4381301b..62368c1c 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Data at the memory location public override T MemoryRead(ulong address) { - return _context.MemoryManager.Read(address); + return _state.Channel.MemoryManager.Read(address); } /// @@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// True if the address is mapped, false otherwise public bool MemoryMapped(ulong address) { - return _context.MemoryManager.IsMapped(address); + return _state.Channel.MemoryManager.IsMapped(address); } /// diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 72ae0b10..efb0503c 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/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 hostShaders = new List(); @@ -724,7 +725,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Current GPU state /// Four transform feedback descriptors for the enabled TFBs, or null if TFB is disabled - private TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(GpuState state) + private static TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(GpuState state) { bool tfEnable = state.Get(MethodOffset.TfEnable); @@ -752,21 +753,23 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Checks if compute shader code in memory is equal to the cached shader. /// + /// Memory manager used to access the GPU memory where the shader is located /// Cached compute shader /// GPU virtual address of the shader code in memory /// True if the code is different, false otherwise - 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); } /// /// Checks if graphics shader code from all stages in memory are equal to the cached shaders. /// + /// Memory manager used to access the GPU memory where the shader is located /// Cached graphics shaders /// GPU virtual addresses of all enabled shader stages /// True if the code is different, false otherwise - 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 /// /// Checks if the code of the specified cached shader is different from the code in memory. /// + /// Memory manager used to access the GPU memory where the shader is located /// Cached shader to compare with /// GPU virtual address of the binary shader code /// Optional GPU virtual address of the "Vertex A" binary shader code /// True if the code is different, false otherwise - 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 memoryCode = _context.MemoryManager.GetSpan(gpuVa, shader.Code.Length); + ReadOnlySpan 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 /// /// Translates a previously generated translator context to something that the host API accepts. /// + /// Memory manager used to access the GPU memory where the shader is located /// Current translator context to translate /// Optional translator context of the shader that should be combined /// Compiled graphics shader code - 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); diff --git a/Ryujinx.Graphics.Gpu/Window.cs b/Ryujinx.Graphics.Gpu/Window.cs index 28cc17ed..541a80cc 100644 --- a/Ryujinx.Graphics.Gpu/Window.cs +++ b/Ryujinx.Graphics.Gpu/Window.cs @@ -22,6 +22,11 @@ namespace Ryujinx.Graphics.Gpu /// private struct PresentationTexture { + /// + /// Texture cache where the texture might be located. + /// + public TextureCache Cache { get; } + /// /// Texture information. /// @@ -55,6 +60,7 @@ namespace Ryujinx.Graphics.Gpu /// /// Creates a new instance of the presentation texture. /// + /// Texture cache used to look for the texture to be presented /// Information of the texture to be presented /// Physical memory locations where the texture data is located /// Texture crop region @@ -62,6 +68,7 @@ namespace Ryujinx.Graphics.Gpu /// Texture release callback /// User defined object passed to the release callback, can be used to identify the texture public PresentationTexture( + TextureCache cache, TextureInfo info, MultiRange range, ImageCrop crop, @@ -69,6 +76,7 @@ namespace Ryujinx.Graphics.Gpu Action 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. /// + /// Process ID of the process that owns the texture pointed to by /// CPU virtual address of the texture data /// Texture width /// Texture height @@ -111,7 +120,9 @@ namespace Ryujinx.Graphics.Gpu /// Texture acquire callback /// Texture release callback /// User defined object passed to the release callback + /// Thrown when is invalid public void EnqueueFrameThreadSafe( + long pid, ulong address, int width, int height, @@ -125,6 +136,11 @@ namespace Ryujinx.Graphics.Gpu Action 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)); } /// @@ -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(); diff --git a/Ryujinx.HLE/HOS/ArmProcessContext.cs b/Ryujinx.HLE/HOS/ArmProcessContext.cs index 457d1218..89ce0eb3 100644 --- a/Ryujinx.HLE/HOS/ArmProcessContext.cs +++ b/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 : IProcessContext where T : class, IVirtualMemoryManager, IMemoryManager + class ArmProcessContext : 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); } } } diff --git a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs b/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs index 04d06e1f..50dbd843 100644 --- a/Ryujinx.HLE/HOS/ArmProcessContextFactory.cs +++ b/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(new MemoryManager(addressSpaceSize, invalidAccessHandler), for64Bit); + return new ArmProcessContext(pid, _gpu, new MemoryManager(addressSpaceSize, invalidAccessHandler), for64Bit); case MemoryManagerMode.HostMapped: case MemoryManagerMode.HostMappedUnsafe: bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe; - return new ArmProcessContext(new MemoryManagerHostMapped(addressSpaceSize, unsafeMode, invalidAccessHandler), for64Bit); + return new ArmProcessContext(pid, _gpu, new MemoryManagerHostMapped(addressSpaceSize, unsafeMode, invalidAccessHandler), for64Bit); default: throw new ArgumentOutOfRangeException(); diff --git a/Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs b/Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs index fbd6c139..10df43a8 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/IProcessContextFactory.cs +++ b/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); } } diff --git a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 90cd01f0..fbdd812e 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/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) { diff --git a/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs b/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs index 5920fe44..ff250e88 100644 --- a/Ryujinx.HLE/HOS/Kernel/Process/ProcessContextFactory.cs +++ b/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)); } diff --git a/Ryujinx.HLE/HOS/ProgramLoader.cs b/Ryujinx.HLE/HOS/ProgramLoader.cs index cc1c76c2..93ddd7ee 100644 --- a/Ryujinx.HLE/HOS/ProgramLoader.cs +++ b/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, diff --git a/Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs b/Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs new file mode 100644 index 00000000..1fcc12b5 --- /dev/null +++ b/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(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index 25279af3..24c784c3 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/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 _deviceFileRegistry = - new Dictionary() + private static Dictionary _deviceFileRegistry = new Dictionary() { { "/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(fd); + deviceFile = DeviceFileIdRegistry.GetData(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(); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs index ac0c4ab1..b3be6fba 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs +++ b/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 _addressSpaceContextRegistry = new ConcurrentDictionary(); - 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 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(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 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)); - } } } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs index 4e7f82ef..ab9d798e 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs +++ b/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 _maps; private readonly SortedList _reservations; - public AddressSpaceContext(ServiceCtx context) + public AddressSpaceContext(MemoryManager gmm) { - Gmm = context.Device.Gpu.MemoryManager; + Gmm = gmm; _maps = new SortedList(); _reservations = new SortedList(); @@ -123,7 +123,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types return _reservations.Remove(gpuVa); } - private Range BinarySearch(SortedList list, ulong address) + private static Range BinarySearch(SortedList 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 list, ulong address) + private static Range BinarySearchLt(SortedList list, ulong address) { Range ltRg = null; diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs index 644fc835..ebaab0c9 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs +++ b/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 _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(data)); + _host1xContext.Host1x.Submit(MemoryMarshal.Cast(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 commandBufferHeader = MemoryMarshal.Cast(arguments)[0]; Span commandBufferEntries = MemoryMarshal.Cast(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 commandBufferHeader = MemoryMarshal.Cast(arguments)[0]; Span commandBufferEntries = MemoryMarshal.Cast(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(); } } } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs b/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs index 44746db6..7369bee5 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvMemoryAllocator.cs +++ b/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 { diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index 9d8e526f..6e56aefa 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/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, diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index e05968cc..77596243 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/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();