diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs index 0784fdca8..f1df333b7 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Compute/ComputeClass.cs @@ -98,7 +98,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute // Make sure all pending uniform buffer data is written to memory. _3dEngine.FlushUboDirty(); - + uint qmdAddress = _state.State.SendPcasA; ComputeQmd qmd = _channel.MemoryManager.Read((ulong)qmdAddress << 8); @@ -106,6 +106,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute ulong shaderGpuVa = ((ulong)_state.State.SetProgramRegionAAddressUpper << 32) | _state.State.SetProgramRegionB; shaderGpuVa += (uint)qmd.ProgramOffset; + + ShaderCache shaderCache = memoryManager.GetBackingMemory(shaderGpuVa).ShaderCache; int localMemorySize = qmd.ShaderLocalMemoryLowSize + qmd.ShaderLocalMemoryHighSize; @@ -142,7 +144,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute sharedMemorySize, _channel.BufferManager.HasUnalignedStorageBuffers); - CachedShaderProgram cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa); + CachedShaderProgram cs = shaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa); _context.Renderer.Pipeline.SetProgram(cs.HostProgram); @@ -156,10 +158,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute { BufferDescriptor sb = info.SBuffers[index]; - ulong sbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(sb.SbCbSlot); + (PhysicalMemory physical, ulong sbDescAddress) = _channel.BufferManager.GetComputeUniformBufferAddress(sb.SbCbSlot); sbDescAddress += (ulong)sb.SbCbOffset * 4; - SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read(sbDescAddress); + SbDescriptor sbDescriptor = physical.Read(sbDescAddress); uint size; if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer) @@ -187,7 +189,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute sharedMemorySize, _channel.BufferManager.HasUnalignedStorageBuffers); - cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa); + cs = shaderCache.GetComputeShader(_channel, samplerPoolMaximumId, poolState, computeState, shaderGpuVa); _context.Renderer.Pipeline.SetProgram(cs.HostProgram); } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs index 19b90c59a..031ff0d12 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs @@ -215,7 +215,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma _channel.TextureManager.RefreshModifiedTextures(); _3dEngine.CreatePendingSyncs(); _3dEngine.FlushUboDirty(); - + + PhysicalMemory srcPhysical = memoryManager.GetBackingMemory(srcGpuVa); + PhysicalMemory dstPhysical = memoryManager.GetBackingMemory(dstGpuVa); + if (copy2D) { // Buffer to texture copy. @@ -293,7 +296,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma if (completeSource && completeDest && !srcLinear && isIdentityRemap) { - Image.Texture source = memoryManager.Physical.TextureCache.FindTexture( + Image.Texture source = srcPhysical.TextureCache.FindTexture( memoryManager, srcGpuVa, srcBpp, @@ -309,7 +312,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma { source.SynchronizeMemory(); - Image.Texture target = memoryManager.Physical.TextureCache.FindOrCreateTexture( + Image.Texture target = dstPhysical.TextureCache.FindOrCreateTexture( memoryManager, source.Info.FormatInfo, dstGpuVa, @@ -339,7 +342,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma if (completeSource && completeDest && !(dstLinear && !srcLinear) && isIdentityRemap) { - Image.Texture target = memoryManager.Physical.TextureCache.FindTexture( + Image.Texture target = dstPhysical.TextureCache.FindTexture( memoryManager, dstGpuVa, dstBpp, @@ -462,6 +465,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma } else { + BufferCache bufferCache = dstPhysical.BufferCache; if (remap && _state.State.SetRemapComponentsDstX == SetRemapComponentsDst.ConstA && _state.State.SetRemapComponentsDstY == SetRemapComponentsDst.ConstA && @@ -472,7 +476,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma _state.State.SetRemapComponentsComponentSize == SetRemapComponentsComponentSize.Four) { // Fast path for clears when remap is enabled. - memoryManager.Physical.BufferCache.ClearBuffer(memoryManager, dstGpuVa, size * 4, _state.State.SetRemapConstA); + bufferCache.ClearBuffer(memoryManager, dstGpuVa, size * 4, _state.State.SetRemapConstA); } else { @@ -492,7 +496,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma } else { - memoryManager.Physical.BufferCache.CopyBuffer(memoryManager, srcGpuVa, dstGpuVa, size); + BufferCache.CopyBuffer(_context,memoryManager, srcGpuVa, dstGpuVa, size); } } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs index 37d7457fc..1e6224f2d 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs @@ -185,7 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory // Right now the copy code at the bottom assumes that it is used on both which might be incorrect. if (!_isLinear) { - Image.Texture target = memoryManager.Physical.TextureCache.FindTexture( + Image.Texture target = memoryManager.GetBackingMemory(_dstGpuVa).TextureCache.FindTexture( memoryManager, _dstGpuVa, 1, diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs index f62a4c01a..444b5a0ac 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs @@ -384,7 +384,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME ulong indirectBufferGpuVa = count.GpuVa; - BufferCache bufferCache = _processor.MemoryManager.Physical.BufferCache; + BufferCache bufferCache = _processor.MemoryManager.GetBackingMemory(indirectBufferGpuVa).BufferCache; bool useBuffer = bufferCache.CheckModified(_processor.MemoryManager, indirectBufferGpuVa, IndirectIndexedDataEntrySize, out ulong indirectBufferAddress); @@ -394,6 +394,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME _processor.ThreedClass.DrawIndirect( topology, + bufferCache, + null, new MultiRange(indirectBufferAddress, IndirectIndexedDataEntrySize), default, 1, @@ -491,22 +493,24 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME } } } - - BufferCache bufferCache = _processor.MemoryManager.Physical.BufferCache; + BufferCache indirectBufferCache = _processor.MemoryManager.GetBackingMemory(indirectBufferGpuVa).BufferCache; + BufferCache parameterBufferCache = _processor.MemoryManager.GetBackingMemory(parameterBufferGpuVa).BufferCache; ulong indirectBufferSize = (ulong)maxDrawCount * (ulong)stride; - MultiRange indirectBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize, BufferStage.Indirect); - MultiRange parameterBufferRange = bufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4, BufferStage.Indirect); + MultiRange indirectBufferRange = indirectBufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, indirectBufferGpuVa, indirectBufferSize, BufferStage.Indirect); + MultiRange parameterBufferRange = parameterBufferCache.TranslateAndCreateMultiBuffers(_processor.MemoryManager, parameterBufferGpuVa, 4, BufferStage.Indirect); _processor.ThreedClass.DrawIndirect( topology, + indirectBufferCache, + parameterBufferCache, indirectBufferRange, parameterBufferRange, maxDrawCount, stride, indexCount, - Threed.IndirectDrawType.DrawIndexedIndirectCount); + IndirectDrawType.DrawIndexedIndirectCount); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs index 15f1a4a33..eba1b6030 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeContext.cs @@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw /// class VtgAsComputeContext : IDisposable { + private const int DummyBufferSize = 16; + private readonly GpuContext _context; /// @@ -46,7 +48,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw 1, 1, 1, - format.GetBytesPerElement(), + 1, format, DepthStencilMode.Depth, Target.TextureBuffer, @@ -519,6 +521,21 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw return new BufferRange(_geometryIndexDataBuffer.Handle, offset, size, write); } + /// + /// Gets the range for a dummy 16 bytes buffer, filled with zeros. + /// + /// Dummy buffer range + public BufferRange GetDummyBufferRange() + { + if (_dummyBuffer == BufferHandle.Null) + { + _dummyBuffer = _context.Renderer.CreateBuffer(DummyBufferSize, BufferAccess.DeviceMemory); + _context.Renderer.Pipeline.ClearBuffer(_dummyBuffer, 0, DummyBufferSize, 0); + } + + return new BufferRange(_dummyBuffer, 0, DummyBufferSize); + } + /// /// Gets the range for a sequential index buffer, with ever incrementing index values. /// diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs index 16a19debe..9363290ca 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs @@ -147,6 +147,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw { _vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount); _vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0); + SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format); continue; } @@ -162,12 +163,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw { _vacContext.VertexInfoBufferUpdater.SetVertexStride(index, 0, componentsCount); _vacContext.VertexInfoBufferUpdater.SetVertexOffset(index, 0, 0); + SetDummyBufferTexture(_vertexAsCompute.Reservations, index, format); continue; } int vbStride = vertexBuffer.UnpackStride(); ulong vbSize = GetVertexBufferSize(address, endAddress.Pack(), vbStride, _indexed, instanced, _firstVertex, _count); + ulong oldVbSize = vbSize; + ulong attributeOffset = (ulong)vertexAttrib.UnpackOffset(); int componentSize = format.GetScalarSize(); @@ -196,11 +200,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding; BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize); - _context.Renderer.Pipeline.SetUniformBuffers([new BufferAssignment(vertexInfoBinding, vertexInfoRange)]); + _context.Renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(vertexInfoBinding, vertexInfoRange) }); int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding; BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: true); - _context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexDataRange)]); + _context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexDataRange) }); _vacContext.VertexInfoBufferUpdater.Commit(); @@ -228,7 +232,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw int vertexInfoBinding = _vertexAsCompute.Reservations.VertexInfoConstantBufferBinding; BufferRange vertexInfoRange = new(_vacContext.VertexInfoBufferUpdater.Handle, 0, VertexInfoBuffer.RequiredSize); - _context.Renderer.Pipeline.SetUniformBuffers([new BufferAssignment(vertexInfoBinding, vertexInfoRange)]); + _context.Renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(vertexInfoBinding, vertexInfoRange) }); int vertexDataBinding = _vertexAsCompute.Reservations.VertexOutputStorageBufferBinding; @@ -246,11 +250,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw BufferRange vertexBuffer = _vacContext.GetGeometryVertexDataBufferRange(_geometryVertexDataOffset, _geometryVertexDataSize, write: true); BufferRange indexBuffer = _vacContext.GetGeometryIndexDataBufferRange(_geometryIndexDataOffset, _geometryIndexDataSize, write: true); - _context.Renderer.Pipeline.SetStorageBuffers([ + _context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] + { new BufferAssignment(vertexDataBinding, vertexDataRange), new BufferAssignment(geometryVbBinding, vertexBuffer), - new BufferAssignment(geometryIbBinding, indexBuffer) - ]); + new BufferAssignment(geometryIbBinding, indexBuffer), + }); _context.Renderer.Pipeline.DispatchCompute( BitUtils.DivRoundUp(primitivesCount, ComputeLocalSize), @@ -294,7 +299,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw _context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram); _context.Renderer.Pipeline.SetIndexBuffer(indexBuffer, IndexType.UInt); - _context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexBuffer)]); + _context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexBuffer) }); _context.Renderer.Pipeline.SetPrimitiveRestart(true, -1); _context.Renderer.Pipeline.SetPrimitiveTopology(GetGeometryOutputTopology(_geometryAsCompute.Info.GeometryVerticesPerPrimitive)); @@ -309,7 +314,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw BufferRange vertexDataRange = _vacContext.GetVertexDataBufferRange(_vertexDataOffset, _vertexDataSize, write: false); _context.Renderer.Pipeline.SetProgram(_vertexPassthroughProgram); - _context.Renderer.Pipeline.SetStorageBuffers([new BufferAssignment(vertexDataBinding, vertexDataRange)]); + _context.Renderer.Pipeline.SetStorageBuffers(stackalloc[] { new BufferAssignment(vertexDataBinding, vertexDataRange) }); _context.Renderer.Pipeline.Draw(_count, _instanceCount, 0, 0); } } @@ -340,6 +345,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw return maxOutputVertices / verticesPerPrimitive; } + /// + /// Binds a dummy buffer as vertex buffer into a buffer texture. + /// + /// Shader resource binding reservations + /// Buffer texture index + /// Buffer texture format + private readonly void SetDummyBufferTexture(ResourceReservations reservations, int index, Format format) + { + ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format); + bufferTexture.SetStorage(_vacContext.GetDummyBufferRange()); + + _context.Renderer.Pipeline.SetTextureAndSampler(ShaderStage.Compute, reservations.GetVertexBufferTextureBinding(index), bufferTexture, null); + } + /// /// Binds a vertex buffer into a buffer texture. /// @@ -352,7 +371,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw { MemoryManager memoryManager = _channel.MemoryManager; - BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange(memoryManager.GetPhysicalRegions(address, size), BufferStage.VertexBuffer); + BufferRange range = memoryManager.GetBackingMemory(address).BufferCache.GetBufferRange(memoryManager.GetPhysicalRegions(address, size), BufferStage.VertexBuffer); ITexture bufferTexture = _vacContext.EnsureBufferTexture(index + 2, format); bufferTexture.SetStorage(range); @@ -394,7 +413,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw MemoryManager memoryManager = _channel.MemoryManager; ulong misalign = address & ((ulong)_context.Capabilities.TextureBufferOffsetAlignment - 1); - BufferRange range = memoryManager.Physical.BufferCache.GetBufferRange( + BufferRange range = memoryManager.GetBackingMemory(address).BufferCache.GetBufferRange( memoryManager.GetPhysicalRegions(address + indexOffset - misalign, size + misalign), BufferStage.IndexBuffer); misalignedOffset = (int)misalign >> shift; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs index 6fc49fc8d..30369dcb7 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ConstantBufferUpdater.cs @@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed // State associated with direct uniform buffer updates. // This state is used to attempt to batch together consecutive updates. + private ulong _ubBeginGpuAddress = 0; private ulong _ubBeginCpuAddress = 0; private ulong _ubFollowUpAddress = 0; private ulong _ubByteCount = 0; @@ -113,12 +114,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed if (_ubFollowUpAddress != 0) { MemoryManager memoryManager = _channel.MemoryManager; + PhysicalMemory physicalMemory = memoryManager.GetBackingMemory(_ubBeginGpuAddress); Span data = MemoryMarshal.Cast(_ubData.AsSpan(0, (int)(_ubByteCount / 4))); - if (memoryManager.Physical.WriteWithRedundancyCheck(_ubBeginCpuAddress, data)) + if (physicalMemory.WriteWithRedundancyCheck(_ubBeginCpuAddress, data)) { - memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount); + physicalMemory.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount); } _ubFollowUpAddress = 0; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index e3b981f40..4dc077e1e 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -641,6 +641,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed public void DrawIndirect( ThreedClass engine, PrimitiveTopology topology, + BufferCache indirectBufferCache, + BufferCache parameterBufferCache, MultiRange indirectBufferRange, MultiRange parameterBufferRange, int maxDrawCount, @@ -662,8 +664,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed return; } - PhysicalMemory memory = _channel.MemoryManager.Physical; - bool hasCount = (drawType & IndirectDrawType.Count) != 0; bool indexed = (drawType & IndirectDrawType.Indexed) != 0; @@ -684,8 +684,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed if (hasCount) { - BufferRange indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect); - BufferRange parameterBuffer = memory.BufferCache.GetBufferRange(parameterBufferRange, BufferStage.Indirect); + BufferRange indirectBuffer = indirectBufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect); + BufferRange parameterBuffer = parameterBufferCache.GetBufferRange(parameterBufferRange, BufferStage.Indirect); if (indexed) { @@ -698,7 +698,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } else { - BufferRange indirectBuffer = memory.BufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect); + BufferRange indirectBuffer = indirectBufferCache.GetBufferRange(indirectBufferRange, BufferStage.Indirect); if (indexed) { diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index f50ec852e..c9b9e3b28 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -381,10 +381,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { BufferDescriptor sb = info.SBuffers[index]; - ulong sbDescAddress = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, sb.SbCbSlot); + (PhysicalMemory physical, ulong sbDescAddress) = _channel.BufferManager.GetGraphicsUniformBufferAddress(stage, sb.SbCbSlot); sbDescAddress += (ulong)sb.SbCbOffset * 4; - SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read(sbDescAddress); + SbDescriptor sbDescriptor = physical.Read(sbDescAddress); uint size; if (sb.SbCbSlot == Constants.DriverReservedUniformBuffer) @@ -505,7 +505,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed rtNoAlphaMask |= 1u << index; } - Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture( + TextureCache colorTextureCache = memoryManager.GetBackingMemory(colorState.Address.Pack()).TextureCache; + + Image.Texture color = colorTextureCache.FindOrCreateTexture( memoryManager, colorState, _vtgWritesRtLayer || layered, @@ -513,7 +515,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed samplesInX, samplesInY, sizeHint); - + changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color); if (color != null) @@ -543,8 +545,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { RtDepthStencilState dsState = _state.State.RtDepthStencilState; Size3D dsSize = _state.State.RtDepthStencilSize; - - depthStencil = memoryManager.Physical.TextureCache.FindOrCreateTexture( + TextureCache dsTextureCache = memoryManager.GetBackingMemory(dsState.Address.Pack()).TextureCache; + + depthStencil = dsTextureCache.FindOrCreateTexture( memoryManager, dsState, dsSize, @@ -1409,8 +1412,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// private void UpdateShaderState() { - ShaderCache shaderCache = _channel.MemoryManager.Physical.ShaderCache; - _vtgWritesRtLayer = false; ShaderAddresses addresses = new(); @@ -1433,6 +1434,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed ? _state.State.TexturePoolState.MaximumId : _state.State.SamplerPoolState.MaximumId; + // Shader stages on different address spaces are not supported right now, + // but it should never happen in practice anyway. + ShaderCache shaderCache = _channel.MemoryManager.GetBackingMemory(addresses.VertexB).ShaderCache; CachedShaderProgram gs = shaderCache.GetGraphicsShader( ref _state.State, ref _pipeline, diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs index a96979cb2..2783131b5 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs @@ -5,6 +5,7 @@ using Ryujinx.Graphics.Gpu.Engine.GPFifo; using Ryujinx.Graphics.Gpu.Engine.InlineToMemory; using Ryujinx.Graphics.Gpu.Engine.Threed.Blender; using Ryujinx.Graphics.Gpu.Engine.Types; +using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Synchronization; using Ryujinx.Memory.Range; using System; @@ -804,6 +805,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Performs a indirect draw, with parameters from a GPU buffer. /// /// Primitive topology + /// Buffer cache owning the buffer with the draw parameters + /// Buffer cache owning the buffer with the draw count /// Memory range of the buffer with the draw parameters, such as count, first index, etc /// Memory range of the buffer with the draw count /// Maximum number of draws that can be made @@ -812,6 +815,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Type of the indirect draw, which can be indexed or non-indexed, with or without a draw count public void DrawIndirect( PrimitiveTopology topology, + BufferCache indirectBufferCache, + BufferCache parameterBufferCache, MultiRange indirectBufferRange, MultiRange parameterBufferRange, int maxDrawCount, @@ -819,7 +824,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed int indexCount, IndirectDrawType drawType) { - _drawManager.DrawIndirect(this, topology, indirectBufferRange, parameterBufferRange, maxDrawCount, stride, indexCount, drawType); + _drawManager.DrawIndirect(this, topology, indirectBufferCache, parameterBufferCache, indirectBufferRange, parameterBufferRange, maxDrawCount, stride, indexCount, drawType); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs index 5ab58d7d1..cba3ac854 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Twod/TwodClass.cs @@ -233,6 +233,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod TwodTexture dstCopyTexture = Unsafe.As(ref _state.State.SetDstFormat); TwodTexture srcCopyTexture = Unsafe.As(ref _state.State.SetSrcFormat); + + TextureCache srcTextureCache = memoryManager.GetBackingMemory(srcCopyTexture.Address.Pack()).TextureCache; + TextureCache dstTextureCache = memoryManager.GetBackingMemory(dstCopyTexture.Address.Pack()).TextureCache; long srcX = ((long)_state.State.SetPixelsFromMemorySrcX0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcX0Frac; long srcY = ((long)_state.State.PixelsFromMemorySrcY0Int << 32) | (long)(ulong)_state.State.SetPixelsFromMemorySrcY0Frac; @@ -305,7 +308,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod // are the same, as we can't blit between different depth formats. bool srcDepthAlias = srcCopyTexture.Format == dstCopyTexture.Format; - Image.Texture srcTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture( + Image.Texture srcTexture = srcTextureCache.FindOrCreateTexture( memoryManager, srcCopyTexture, offset, @@ -326,7 +329,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod return; } - memoryManager.Physical.TextureCache.Lift(srcTexture); + srcTextureCache.Lift(srcTexture); // When the source texture that was found has a depth format, // we must enforce the target texture also has a depth format, @@ -342,7 +345,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod dstCopyTextureFormat = dstCopyTexture.Format.Convert(); } - Image.Texture dstTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture( + Image.Texture dstTexture = dstTextureCache.FindOrCreateTexture( memoryManager, dstCopyTexture, 0, diff --git a/src/Ryujinx.Graphics.Gpu/GpuChannel.cs b/src/Ryujinx.Graphics.Gpu/GpuChannel.cs index 047cbcca6..e8f5dd720 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuChannel.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuChannel.cs @@ -58,22 +58,24 @@ namespace Ryujinx.Graphics.Gpu public void BindMemory(MemoryManager memoryManager) { MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, memoryManager ?? throw new ArgumentNullException(nameof(memoryManager))); + if (oldMemoryManager == memoryManager) + { + return; + } - memoryManager.Physical.IncrementReferenceCount(); + memoryManager.AttachToChannel(BufferManager.Rebind); if (oldMemoryManager != null) { - oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind; - oldMemoryManager.Physical.DecrementReferenceCount(); + oldMemoryManager.DetachFromChannel(BufferManager.Rebind); oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler; } - memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind; memoryManager.MemoryUnmapped += MemoryUnmappedHandler; // Since the memory manager changed, make sure we will get pools from addresses of the new memory manager. TextureManager.ReloadPools(); - memoryManager.Physical.BufferCache.QueuePrune(); + memoryManager.QueuePrune(); } /// @@ -86,7 +88,7 @@ namespace Ryujinx.Graphics.Gpu TextureManager.ReloadPools(); MemoryManager memoryManager = Volatile.Read(ref _memoryManager); - memoryManager?.Physical.BufferCache.QueuePrune(); + memoryManager?.QueuePrune(); } /// @@ -141,8 +143,7 @@ namespace Ryujinx.Graphics.Gpu MemoryManager oldMemoryManager = Interlocked.Exchange(ref _memoryManager, null); if (oldMemoryManager != null) { - oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind; - oldMemoryManager.Physical.DecrementReferenceCount(); + oldMemoryManager.DetachFromChannel(BufferManager.Rebind); oldMemoryManager.MemoryUnmapped -= MemoryUnmappedHandler; } } diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs index d1c608dd6..a56015929 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -6,6 +6,7 @@ using Ryujinx.Graphics.Gpu.Engine.GPFifo; using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Gpu.Synchronization; +using Ryujinx.Memory; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -172,7 +173,7 @@ namespace Ryujinx.Graphics.Gpu throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid)); } - return new MemoryManager(physicalMemory, cpuMemorySize); + return new MemoryManager(this, physicalMemory, cpuMemorySize); } /// @@ -197,7 +198,7 @@ namespace Ryujinx.Graphics.Gpu /// ID of the process that owns /// Virtual memory owned by the process /// Thrown if was already registered - public void RegisterProcess(ulong pid, Cpu.IVirtualMemoryManagerTracked cpuMemory) + public void RegisterProcess(ulong pid, IVirtualMemoryManagerTracked cpuMemory) { PhysicalMemory physicalMemory = new(this, cpuMemory); if (!PhysicalMemoryRegistry.TryAdd(pid, physicalMemory)) diff --git a/src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs b/src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs index b9aa37fed..0cbbcb958 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/PoolCache.cs @@ -1,3 +1,4 @@ +using Ryujinx.Graphics.Gpu.Memory; using System; using System.Collections.Generic; @@ -64,7 +65,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Maximum ID of the texture pool /// Cache of texture array bindings /// The found or newly created texture pool - public T FindOrCreate(GpuChannel channel, ulong address, int maximumId, TextureBindingsArrayCache bindingsArrayCache) + public T FindOrCreate(GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId, TextureBindingsArrayCache bindingsArrayCache) { // Remove old entries from the cache, if possible. while (_pools.Count > MaxCapacity && (_currentTimestamp - _pools.First.Value.CacheTimestamp) >= MinDeltaForRemoval) @@ -99,7 +100,7 @@ namespace Ryujinx.Graphics.Gpu.Image } // If not found, create a new one. - pool = CreatePool(_context, channel, address, maximumId); + pool = CreatePool(_context, channel, physicalMemory, address, maximumId); pool.CacheNode = _pools.AddLast(pool); pool.CacheTimestamp = _currentTimestamp; @@ -112,9 +113,10 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// GPU context that the pool belongs to /// GPU channel that the pool belongs to + /// GPU backing memory of the pool /// Address of the pool in guest memory /// Maximum ID of the pool (equal to maximum minus one) - protected abstract T CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId); + protected abstract T CreatePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId); public void Dispose() { diff --git a/src/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs b/src/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs index 881c37af4..7d8e0ba55 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/SamplerPoolCache.cs @@ -1,3 +1,5 @@ +using Ryujinx.Graphics.Gpu.Memory; + namespace Ryujinx.Graphics.Gpu.Image { /// @@ -20,11 +22,12 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// GPU context that the sampler pool belongs to /// GPU channel that the texture pool belongs to + /// GPU backing memory of the pool /// Address of the sampler pool in guest memory /// Maximum sampler ID of the sampler pool (equal to maximum samplers minus one) - protected override SamplerPool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) + protected override SamplerPool CreatePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId) { - return new SamplerPool(context, channel.MemoryManager.Physical, address, maximumId); + return new SamplerPool(context, physicalMemory, address, maximumId); } } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs index fc3b64c03..f6f44e880 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs @@ -660,6 +660,7 @@ namespace Ryujinx.Graphics.Gpu.Image ISampler[] samplers = isImage ? null : new ISampler[bindingInfo.ArrayLength]; ITexture[] textures = new ITexture[bindingInfo.ArrayLength]; + BufferCache bufferCache = null; for (int index = 0; index < length; index++) { @@ -673,7 +674,7 @@ namespace Ryujinx.Graphics.Gpu.Image else { ref readonly TextureDescriptor descriptor = ref texturePool.GetForBinding(index, bindingInfo.FormatInfo, out texture); - + bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache; if (texture != null) { entry.Textures[texture] = texture.InvalidatedSequence; @@ -702,11 +703,10 @@ namespace Ryujinx.Graphics.Gpu.Image // to ensure we're not using a old buffer that was already deleted. if (isImage) { - _channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index); - } + _channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, bufferCache, texture.Range, bindingInfo, index); } else { - _channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index); + _channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, bufferCache, texture.Range, bindingInfo, index); } } else if (isImage) @@ -797,11 +797,11 @@ namespace Ryujinx.Graphics.Gpu.Image return; } - cachedTextureBuffer = MemoryMarshal.Cast(_channel.MemoryManager.Physical.GetSpan(textureBufferBounds.Range)); - + cachedTextureBuffer = MemoryMarshal.Cast(textureBufferBounds.Physical.GetSpan(textureBufferBounds.Range)); + if (separateSamplerBuffer) { - cachedSamplerBuffer = MemoryMarshal.Cast(_channel.MemoryManager.Physical.GetSpan(samplerBufferBounds.Range)); + cachedSamplerBuffer = MemoryMarshal.Cast(samplerBufferBounds.Physical.GetSpan(samplerBufferBounds.Range)); } else { @@ -828,11 +828,10 @@ namespace Ryujinx.Graphics.Gpu.Image } else { - cachedTextureBuffer = MemoryMarshal.Cast(_channel.MemoryManager.Physical.GetSpan(textureBufferBounds.Range)); - + cachedTextureBuffer = MemoryMarshal.Cast(textureBufferBounds.Physical.GetSpan(textureBufferBounds.Range)); if (separateSamplerBuffer) { - cachedSamplerBuffer = MemoryMarshal.Cast(_channel.MemoryManager.Physical.GetSpan(samplerBufferBounds.Range)); + cachedSamplerBuffer = MemoryMarshal.Cast(samplerBufferBounds.Physical.GetSpan(samplerBufferBounds.Range)); } else { @@ -901,16 +900,18 @@ namespace Ryujinx.Graphics.Gpu.Image if (hostTexture != null && texture.Target == Target.TextureBuffer) { + BufferCache bufferCache = textureBufferBounds.BufferCache; + // Ensure that the buffer texture is using the correct buffer as storage. // Buffers are frequently re-created to accommodate larger data, so we need to re-bind // to ensure we're not using a old buffer that was already deleted. if (isImage) { - _channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, texture.Range, bindingInfo, index); + _channel.BufferManager.SetBufferTextureStorage(stage, entry.ImageArray, hostTexture, bufferCache, texture.Range, bindingInfo, index); } else { - _channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, texture.Range, bindingInfo, index); + _channel.BufferManager.SetBufferTextureStorage(stage, entry.TextureArray, hostTexture, bufferCache, texture.Range, bindingInfo, index); } } else if (isImage) diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs index aa1ac1d57..ae4c9a3f8 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs @@ -396,7 +396,7 @@ namespace Ryujinx.Graphics.Gpu.Image { ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, textureBufferIndex); - cachedTextureBuffer = MemoryMarshal.Cast(_channel.MemoryManager.Physical.GetSpan(bounds.Range)); + cachedTextureBuffer = MemoryMarshal.Cast(bounds.Physical.GetSpan(bounds.Range)); cachedTextureBufferIndex = textureBufferIndex; if (samplerBufferIndex == textureBufferIndex) @@ -410,7 +410,7 @@ namespace Ryujinx.Graphics.Gpu.Image { ref BufferBounds bounds = ref _channel.BufferManager.GetUniformBufferBounds(_isCompute, stageIndex, samplerBufferIndex); - cachedSamplerBuffer = MemoryMarshal.Cast(_channel.MemoryManager.Physical.GetSpan(bounds.Range)); + cachedSamplerBuffer = MemoryMarshal.Cast(bounds.Physical.GetSpan(bounds.Range)); cachedSamplerBufferIndex = samplerBufferIndex; } } @@ -524,7 +524,8 @@ namespace Ryujinx.Graphics.Gpu.Image // Ensure that the buffer texture is using the correct buffer as storage. // Buffers are frequently re-created to accommodate larger data, so we need to re-bind // to ensure we're not using a old buffer that was already deleted. - _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, false); + BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache; + _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, bufferCache, texture.Range, bindingInfo, false); // Cache is not used for buffer texture, it must always rebind. state.CachedTexture = null; @@ -659,7 +660,8 @@ namespace Ryujinx.Graphics.Gpu.Image // Buffers are frequently re-created to accommodate larger data, so we need to re-bind // to ensure we're not using a old buffer that was already deleted. - _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, true); + BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(descriptor.UnpackAddress()).BufferCache; + _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, bufferCache, texture.Range, bindingInfo, true); // Cache is not used for buffer texture, it must always rebind. state.CachedTexture = null; @@ -715,9 +717,10 @@ namespace Ryujinx.Graphics.Gpu.Image int packedId = ReadPackedId(stageIndex, handle, textureBufferIndex, samplerBufferIndex); int textureId = TextureHandle.UnpackTextureId(packedId); + PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(poolGpuVa); ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa); - TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache); + TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, maximumId, _bindingsArrayCache); TextureDescriptor descriptor; @@ -751,12 +754,12 @@ namespace Ryujinx.Graphics.Gpu.Image { (int textureWordOffset, int samplerWordOffset, TextureHandleType handleType) = TextureHandle.UnpackOffsets(wordOffset); - ulong textureBufferAddress = _isCompute + (PhysicalMemory texturePhysicalMemory, ulong textureBufferAddress) = _isCompute ? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex) : _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex); int handle = textureBufferAddress != MemoryManager.PteUnmapped - ? _channel.MemoryManager.Physical.Read(textureBufferAddress + (uint)textureWordOffset * 4) + ? texturePhysicalMemory.Read(textureBufferAddress + (uint)textureWordOffset * 4) : 0; // The "wordOffset" (which is really the immediate value used on texture instructions on the shader) @@ -771,12 +774,12 @@ namespace Ryujinx.Graphics.Gpu.Image if (handleType != TextureHandleType.SeparateConstantSamplerHandle) { - ulong samplerBufferAddress = _isCompute + (PhysicalMemory samplerPhysicalMemory, ulong samplerBufferAddress) = _isCompute ? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex) : _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex); samplerHandle = samplerBufferAddress != MemoryManager.PteUnmapped - ? _channel.MemoryManager.Physical.Read(samplerBufferAddress + (uint)samplerWordOffset * 4) + ? samplerPhysicalMemory.Read(samplerBufferAddress + (uint)samplerWordOffset * 4) : 0; } else @@ -813,7 +816,8 @@ namespace Ryujinx.Graphics.Gpu.Image if (poolAddress != MemoryManager.PteUnmapped) { - texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, _texturePoolMaximumId, _bindingsArrayCache); + PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(_texturePoolGpuVa); + texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, _texturePoolMaximumId, _bindingsArrayCache); _texturePool = texturePool; } } @@ -824,7 +828,8 @@ namespace Ryujinx.Graphics.Gpu.Image if (poolAddress != MemoryManager.PteUnmapped) { - samplerPool = _samplerPoolCache.FindOrCreate(_channel, poolAddress, _samplerPoolMaximumId, _bindingsArrayCache); + PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(_samplerPoolGpuVa); + samplerPool = _samplerPoolCache.FindOrCreate(_channel, physical, poolAddress, _samplerPoolMaximumId, _bindingsArrayCache); _samplerPool = samplerPool; } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index db2921468..65e243c1f 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Engine.Types; +using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Shader; using System; @@ -385,8 +386,9 @@ namespace Ryujinx.Graphics.Gpu.Image public TexturePool GetTexturePool(ulong poolGpuVa, int maximumId) { ulong poolAddress = _channel.MemoryManager.Translate(poolGpuVa); + PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(poolAddress); - TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, maximumId, _bindingsArrayCache); + TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, physical, poolAddress, maximumId, _bindingsArrayCache); return texturePool; } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs index 4a16fa90a..9854ea531 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -160,9 +160,10 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// GPU context that the texture pool belongs to /// GPU channel that the texture pool belongs to + /// Backing memory of the pool /// 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, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId) + public TexturePool(GpuContext context, GpuChannel channel, PhysicalMemory physicalMemory, ulong address, int maximumId) : base(context, physicalMemory, address, maximumId) { _channel = channel; _aliasLists = new Dictionary(); @@ -193,7 +194,9 @@ namespace Ryujinx.Graphics.Gpu.Image } TextureInfo info = GetInfo(descriptor, out int layerSize); - texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize); + MemoryManager memoryManager = _channel.MemoryManager; + TextureCache textureCache = memoryManager.GetBackingMemory(descriptor.UnpackAddress()).TextureCache; + texture = textureCache.FindOrCreateTexture(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) @@ -421,7 +424,8 @@ namespace Ryujinx.Graphics.Gpu.Image continue; } - MultiRange range = _channel.MemoryManager.Physical.TextureCache.UpdatePartiallyMapped(_channel.MemoryManager, address, texture); + TextureCache textureCache = _channel.MemoryManager.GetBackingMemory(address).TextureCache; + MultiRange range = textureCache.UpdatePartiallyMapped(_channel.MemoryManager, address, texture); // If the texture is not mapped at all, delete its reference. @@ -446,7 +450,7 @@ namespace Ryujinx.Graphics.Gpu.Image if (!range.Equals(texture.Range)) { // Part of the texture was mapped or unmapped. Replace the range and regenerate tracking handles. - if (!_channel.MemoryManager.Physical.TextureCache.UpdateMapping(texture, range)) + if (!textureCache.UpdateMapping(texture, range)) { // Texture could not be remapped due to a collision, just delete it. if (Interlocked.Exchange(ref Items[request.ID], null) != null) @@ -481,6 +485,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Size of the range being invalidated protected override void InvalidateRangeImpl(ulong address, ulong size) { + MemoryManager memoryManager = _channel.MemoryManager; ProcessDereferenceQueue(); ulong endAddress = address + size; @@ -505,7 +510,8 @@ namespace Ryujinx.Graphics.Gpu.Image if (texture.HasOneReference()) { - _channel.MemoryManager.Physical.TextureCache.AddShortCache(texture, ref cachedDescriptor); + TextureCache textureCache = memoryManager.GetBackingMemory(descriptor.UnpackAddress()).TextureCache; + textureCache.AddShortCache(texture, ref cachedDescriptor); } if (Interlocked.Exchange(ref Items[id], null) != null) diff --git a/src/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs index 5da2c439c..e1a9c64d8 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs @@ -1,3 +1,5 @@ +using Ryujinx.Graphics.Gpu.Memory; + namespace Ryujinx.Graphics.Gpu.Image { /// @@ -20,11 +22,17 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// GPU context that the texture pool belongs to /// GPU channel that the texture pool belongs to + /// Backing memory of the pool /// Address of the texture pool in guest memory /// Maximum texture ID of the texture pool (equal to maximum textures minus one) - protected override TexturePool CreatePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) + protected override TexturePool CreatePool( + GpuContext context, + GpuChannel channel, + PhysicalMemory physicalMemory, + ulong address, + int maximumId) { - return new TexturePool(context, channel, address, maximumId); + return new TexturePool(context, channel, physicalMemory, address, maximumId); } } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs index cf783ef2f..13d2b7637 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs @@ -9,6 +9,16 @@ namespace Ryujinx.Graphics.Gpu.Memory /// readonly struct BufferBounds : IEquatable { + /// + /// Physical memory backing the buffer. + /// + public PhysicalMemory Physical { get; } + + /// + /// Buffer cache that owns the buffer. + /// + public BufferCache BufferCache => Physical.BufferCache; + /// /// Physical memory ranges where the buffer is mapped. /// @@ -29,8 +39,9 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Physical memory ranges where the buffer is mapped /// Buffer usage flags - public BufferBounds(MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None) + public BufferBounds(PhysicalMemory physical, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None) { + Physical = physical; Range = range; Flags = flags; } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs index d02efcb29..20e634a75 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferCache.cs @@ -735,18 +735,22 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// This does a GPU side copy. /// + /// GPU context /// 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(MemoryManager memoryManager, ulong srcVa, ulong dstVa, ulong size) + public static void CopyBuffer(GpuContext context, MemoryManager memoryManager, ulong srcVa, ulong dstVa, ulong size) { - MultiRange srcRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, srcVa, size, BufferStage.Copy); - MultiRange dstRange = TranslateAndCreateMultiBuffersPhysicalOnly(memoryManager, dstVa, size, BufferStage.Copy); + PhysicalMemory srcPhysical = memoryManager.GetBackingMemory(srcVa); + PhysicalMemory dstPhysical = memoryManager.GetBackingMemory(dstVa); + + MultiRange srcRange = srcPhysical.BufferCache.TranslateAndCreateBuffer(memoryManager, srcVa, size, BufferStage.Copy); + MultiRange dstRange = dstPhysical.BufferCache.TranslateAndCreateBuffer(memoryManager, dstVa, size, BufferStage.Copy); if (srcRange.Count == 1 && dstRange.Count == 1) { - CopyBufferSingleRange(memoryManager, srcRange.GetSubRange(0).Address, dstRange.GetSubRange(0).Address, size); + CopyBufferSingleRange(context, srcPhysical, dstPhysical, srcRange.GetSubRange(0).Address, dstRange.GetSubRange(0).Address, size); } else { @@ -777,7 +781,7 @@ namespace Ryujinx.Graphics.Gpu.Memory ulong dstSize = dstSubRange.Size - dstOffset; ulong copySize = Math.Min(srcSize, dstSize); - CopyBufferSingleRange(memoryManager, srcSubRange.Address + srcOffset, dstSubRange.Address + dstOffset, copySize); + CopyBufferSingleRange(context, srcPhysical, dstPhysical, srcSubRange.Address + srcOffset, dstSubRange.Address + dstOffset, copySize); srcOffset += copySize; dstOffset += copySize; @@ -793,18 +797,26 @@ namespace Ryujinx.Graphics.Gpu.Memory /// This does a GPU side copy. /// /// GPU memory manager where the buffer is mapped + /// Physical memory backing the source buffer. + /// Physical memory backing the destination buffer. /// Physical address of the copy source /// Physical address of the copy destination /// Size in bytes of the copy - private void CopyBufferSingleRange(MemoryManager memoryManager, ulong srcAddress, ulong dstAddress, ulong size) + private static void CopyBufferSingleRange( + GpuContext context, + PhysicalMemory srcPhysical, + PhysicalMemory dstPhysical, + ulong srcAddress, + ulong dstAddress, + ulong size) { - Buffer srcBuffer = GetBuffer(srcAddress, size, BufferStage.Copy); - Buffer dstBuffer = GetBuffer(dstAddress, size, BufferStage.Copy); + Buffer srcBuffer = srcPhysical.BufferCache.GetBuffer(srcAddress, size, BufferStage.Copy); + Buffer dstBuffer = dstPhysical.BufferCache.GetBuffer(dstAddress, size, BufferStage.Copy); int srcOffset = (int)(srcAddress - srcBuffer.Address); int dstOffset = (int)(dstAddress - dstBuffer.Address); - _context.Renderer.Pipeline.CopyBuffer( + context.Renderer.Pipeline.CopyBuffer( srcBuffer.Handle, dstBuffer.Handle, srcOffset, @@ -820,7 +832,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); - memoryManager.Physical.WriteTrackedResource(dstAddress, memoryManager.Physical.GetSpan(srcAddress, (int)size), ResourceKind.Buffer); + dstPhysical.WriteTrackedResource(dstAddress, srcPhysical.GetSpan(srcAddress, (int)size), ResourceKind.Buffer); } dstBuffer.CopyToDependantVirtualBuffers(dstAddress, size); @@ -849,7 +861,7 @@ namespace Ryujinx.Graphics.Gpu.Memory _context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)subRange.Size, value); - memoryManager.Physical.FillTrackedResource(subRange.Address, subRange.Size, value, ResourceKind.Buffer); + memoryManager.GetBackingMemory(gpuVa).FillTrackedResource(subRange.Address, subRange.Size, value, ResourceKind.Buffer); buffer.CopyToDependantVirtualBuffers(subRange.Address, subRange.Size); } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs index cb99b455b..48e10800b 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -66,18 +66,19 @@ namespace Ryujinx.Graphics.Gpu.Memory Buffers = new BufferBounds[count]; Unaligned = new bool[count]; - Buffers.AsSpan().Fill(new BufferBounds(new MultiRange(MemoryManager.PteUnmapped, 0UL))); + Buffers.AsSpan().Fill(new BufferBounds(null, new MultiRange(MemoryManager.PteUnmapped, 0UL))); } /// /// Sets the region of a buffer at a given slot. /// /// Buffer slot + /// Physical memory backing the buffer /// Physical memory regions where the buffer is mapped /// Buffer usage flags - public void SetBounds(int index, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None) + public void SetBounds(int index, PhysicalMemory physical, MultiRange range, BufferUsageFlags flags = BufferUsageFlags.None) { - Buffers[index] = new BufferBounds(range, flags); + Buffers[index] = new BufferBounds(physical, range, flags); } /// @@ -156,8 +157,10 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Type of each index buffer element public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type) { - MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.IndexBuffer); + BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(gpuVa).BufferCache; + MultiRange range = bufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.IndexBuffer); + _indexBuffer.BufferCache = bufferCache; _indexBuffer.Range = range; _indexBuffer.Type = type; @@ -186,11 +189,15 @@ 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) { - MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.VertexBuffer); + BufferCache bufferCache = _channel.MemoryManager.GetBackingMemory(gpuVa).BufferCache; + MultiRange range = bufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.VertexBuffer); - _vertexBuffers[index].Range = range; - _vertexBuffers[index].Stride = stride; - _vertexBuffers[index].Divisor = divisor; + ref VertexBuffer vb = ref _vertexBuffers[index]; + + vb.BufferCache = bufferCache; + vb.Range = range; + vb.Stride = stride; + vb.Divisor = divisor; _vertexBuffersDirty = true; @@ -213,9 +220,10 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Size in bytes of the transform feedback buffer public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size) { - MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStage.TransformFeedback); + PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa); + MultiRange range = physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStage.TransformFeedback); - _transformFeedbackBuffers[index] = new BufferBounds(range); + _transformFeedbackBuffers[index] = new BufferBounds(physical, range); _transformFeedbackBuffersDirty = true; } @@ -258,11 +266,12 @@ namespace Ryujinx.Graphics.Gpu.Memory RecordStorageAlignment(_cpStorageBuffers, index, gpuVa); - gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment); + gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment); - MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.ComputeStorage(flags)); + PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa); + MultiRange range = physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.ComputeStorage(flags)); - _cpStorageBuffers.SetBounds(index, range, flags); + _cpStorageBuffers.SetBounds(index, physical, range, flags); } /// @@ -282,16 +291,17 @@ namespace Ryujinx.Graphics.Gpu.Memory RecordStorageAlignment(buffers, index, gpuVa); - gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment); + gpuVa = BitUtils.AlignDown(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment); - MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.GraphicsStorage(stage, flags)); + PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa); + MultiRange range = physical.BufferCache.TranslateAndCreateMultiBuffers(_channel.MemoryManager, gpuVa, size, BufferStageUtils.GraphicsStorage(stage, flags)); if (!buffers.Buffers[index].Range.Equals(range)) { _gpStorageBuffersDirty = true; } - buffers.SetBounds(index, range, flags); + buffers.SetBounds(index, physical, range, flags); } /// @@ -303,9 +313,10 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Size in bytes of the storage buffer public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size) { - MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.Compute); + PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa); + MultiRange range = physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStage.Compute); - _cpUniformBuffers.SetBounds(index, range); + _cpUniformBuffers.SetBounds(index, physical, range); } /// @@ -318,9 +329,10 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Size in bytes of the storage buffer public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size) { - MultiRange range = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStageUtils.FromShaderStage(stage)); + PhysicalMemory physical = _channel.MemoryManager.GetBackingMemory(gpuVa); + MultiRange range = _channel.MemoryManager.GetBackingMemory(gpuVa).BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size, BufferStageUtils.FromShaderStage(stage)); - _gpUniformBuffers[stage].SetBounds(index, range); + _gpUniformBuffers[stage].SetBounds(index, physical, range); _gpUniformBuffersDirty = true; } @@ -416,9 +428,10 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Index of the uniform buffer binding /// The uniform buffer address, or an undefined value if the buffer is not currently bound - public ulong GetComputeUniformBufferAddress(int index) + public (PhysicalMemory, ulong) GetComputeUniformBufferAddress(int index) { - return _cpUniformBuffers.Buffers[index].Range.GetSubRange(0).Address; + ref BufferBounds buffer = ref _cpUniformBuffers.Buffers[index]; + return (buffer.Physical, buffer.Range.GetSubRange(0).Address); } /// @@ -437,9 +450,10 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Index of the shader stage /// Index of the uniform buffer binding /// The uniform buffer address, or an undefined value if the buffer is not currently bound - public ulong GetGraphicsUniformBufferAddress(int stage, int index) + public (PhysicalMemory, ulong) GetGraphicsUniformBufferAddress(int stage, int index) { - return _gpUniformBuffers[stage].Buffers[index].Range.GetSubRange(0).Address; + ref BufferBounds buffer = ref _gpUniformBuffers[stage].Buffers[index]; + return (buffer.Physical, buffer.Range.GetSubRange(0).Address); } /// @@ -478,12 +492,10 @@ namespace Ryujinx.Graphics.Gpu.Memory /// public void CommitComputeBindings() { - BufferCache bufferCache = _channel.MemoryManager.Physical.BufferCache; + BindBuffers(_cpStorageBuffers, isStorage: true); + BindBuffers(_cpUniformBuffers, isStorage: false); - BindBuffers(bufferCache, _cpStorageBuffers, isStorage: true); - BindBuffers(bufferCache, _cpUniformBuffers, isStorage: false); - - CommitBufferTextureBindings(bufferCache); + CommitBufferTextureBindings(); // Force rebind after doing compute work. Rebind(); @@ -495,14 +507,14 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Commit any queued buffer texture bindings. /// /// Buffer cache - private void CommitBufferTextureBindings(BufferCache bufferCache) + private void CommitBufferTextureBindings() { if (_bufferTextures.Count > 0) { foreach (BufferTextureBinding binding in _bufferTextures) { bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore); - BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStageUtils.TextureBuffer(binding.Stage, binding.BindingInfo.Flags), isStore); + BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStageUtils.TextureBuffer(binding.Stage, binding.BindingInfo.Flags), isStore); binding.Texture.SetStorage(range); // The texture must be rebound to use the new storage if it was updated. @@ -526,7 +538,7 @@ namespace Ryujinx.Graphics.Gpu.Memory foreach (BufferTextureArrayBinding binding in _bufferTextureArrays) { - BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStage.None); + BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStage.None); binding.Texture.SetStorage(range); textureArray[0] = binding.Texture; @@ -536,7 +548,7 @@ namespace Ryujinx.Graphics.Gpu.Memory foreach (BufferTextureArrayBinding binding in _bufferImageArrays) { bool isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore); - BufferRange range = bufferCache.GetBufferRange(binding.Range, BufferStage.None, isStore); + BufferRange range = binding.BufferCache.GetBufferRange(binding.Range, BufferStage.None, isStore); binding.Texture.SetStorage(range); textureArray[0] = binding.Texture; @@ -555,8 +567,6 @@ namespace Ryujinx.Graphics.Gpu.Memory /// True if the index buffer is in use public void CommitGraphicsBindings(bool indexed) { - BufferCache bufferCache = _channel.MemoryManager.Physical.BufferCache; - if (indexed) { if (_indexBufferDirty || _rebind) @@ -565,14 +575,14 @@ namespace Ryujinx.Graphics.Gpu.Memory if (!_indexBuffer.Range.IsUnmapped) { - BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Range, BufferStage.IndexBuffer); + BufferRange buffer = _indexBuffer.BufferCache.GetBufferRange(_indexBuffer.Range, BufferStage.IndexBuffer); _context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type); } } else if (!_indexBuffer.Range.IsUnmapped) { - bufferCache.SynchronizeBufferRange(_indexBuffer.Range); + _indexBuffer.BufferCache.SynchronizeBufferRange(_indexBuffer.Range); } } else if (_rebind) @@ -597,7 +607,7 @@ namespace Ryujinx.Graphics.Gpu.Memory continue; } - BufferRange buffer = bufferCache.GetBufferRange(vb.Range, BufferStage.VertexBuffer); + BufferRange buffer = vb.BufferCache.GetBufferRange(vb.Range, BufferStage.VertexBuffer); vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor); } @@ -615,7 +625,7 @@ namespace Ryujinx.Graphics.Gpu.Memory continue; } - bufferCache.SynchronizeBufferRange(vb.Range); + vb.BufferCache.SynchronizeBufferRange(vb.Range); } } @@ -637,7 +647,7 @@ namespace Ryujinx.Graphics.Gpu.Memory continue; } - tfbs[index] = bufferCache.GetBufferRange(tfb.Range, BufferStage.TransformFeedback, write: true); + tfbs[index] = tfb.BufferCache.GetBufferRange(tfb.Range, BufferStage.TransformFeedback, write: true); } _context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs); @@ -684,7 +694,7 @@ namespace Ryujinx.Graphics.Gpu.Memory _context.SupportBufferUpdater.SetTfeOffset(index, tfeOffset); - buffers[index] = new BufferAssignment(index, bufferCache.GetBufferRange(range, BufferStage.TransformFeedback, write: true)); + buffers[index] = new BufferAssignment(index, tfb.BufferCache.GetBufferRange(range, BufferStage.TransformFeedback, write: true)); } } @@ -702,7 +712,7 @@ namespace Ryujinx.Graphics.Gpu.Memory continue; } - bufferCache.SynchronizeBufferRange(tfb.Range); + tfb.BufferCache.SynchronizeBufferRange(tfb.Range); } } @@ -710,7 +720,7 @@ namespace Ryujinx.Graphics.Gpu.Memory { _gpStorageBuffersDirty = false; - BindBuffers(bufferCache, _gpStorageBuffers, isStorage: true); + BindBuffers(_gpStorageBuffers, isStorage: true); } else { @@ -721,14 +731,14 @@ namespace Ryujinx.Graphics.Gpu.Memory { _gpUniformBuffersDirty = false; - BindBuffers(bufferCache, _gpUniformBuffers, isStorage: false); + BindBuffers(_gpUniformBuffers, isStorage: false); } else { UpdateBuffers(_gpUniformBuffers); } - CommitBufferTextureBindings(bufferCache); + CommitBufferTextureBindings(); _rebind = false; @@ -742,7 +752,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Buffer memory ranges to bind /// True to bind as storage buffer, false to bind as uniform buffer [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void BindBuffers(BufferCache bufferCache, BuffersPerStage[] bindings, bool isStorage) + private void BindBuffers(BuffersPerStage[] bindings, bool isStorage) { int rangesCount = 0; @@ -763,8 +773,8 @@ namespace Ryujinx.Graphics.Gpu.Memory { bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write); BufferRange range = isStorage - ? bufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite) - : bufferCache.GetBufferRange(bounds.Range, bufferStage); + ? bounds.BufferCache.GetBufferRangeAligned(bounds.Range, bufferStage | BufferStageUtils.FromUsage(bounds.Flags), isWrite) + : bounds.BufferCache.GetBufferRange(bounds.Range, bufferStage); ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range); } @@ -780,11 +790,10 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Bind respective buffer bindings on the host API. /// - /// Buffer cache holding the buffers for the specified ranges /// Buffer memory ranges to bind /// True to bind as storage buffer, false to bind as uniform buffer [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void BindBuffers(BufferCache bufferCache, BuffersPerStage buffers, bool isStorage) + private void BindBuffers(BuffersPerStage buffers, bool isStorage) { int rangesCount = 0; @@ -800,8 +809,8 @@ namespace Ryujinx.Graphics.Gpu.Memory { bool isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write); BufferRange range = isStorage - ? bufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite) - : bufferCache.GetBufferRange(bounds.Range, BufferStage.Compute); + ? bounds.BufferCache.GetBufferRangeAligned(bounds.Range, BufferStageUtils.ComputeStorage(bounds.Flags), isWrite) + : bounds.BufferCache.GetBufferRange(bounds.Range, BufferStage.Compute); ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range); } @@ -854,7 +863,7 @@ namespace Ryujinx.Graphics.Gpu.Memory continue; } - _channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(bounds.Range); + bounds.BufferCache.SynchronizeBufferRange(bounds.Range); } } } @@ -871,13 +880,14 @@ namespace Ryujinx.Graphics.Gpu.Memory public void SetBufferTextureStorage( ShaderStage stage, ITexture texture, + BufferCache bufferCache, MultiRange range, TextureBindingInfo bindingInfo, bool isImage) { - _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); + bufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); - _bufferTextures.Add(new BufferTextureBinding(stage, texture, range, bindingInfo, isImage)); + _bufferTextures.Add(new BufferTextureBinding(stage, texture, bufferCache, range, bindingInfo, isImage)); } /// @@ -894,13 +904,14 @@ namespace Ryujinx.Graphics.Gpu.Memory ShaderStage stage, ITextureArray array, ITexture texture, + BufferCache bufferCache, MultiRange range, TextureBindingInfo bindingInfo, int index) { - _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); + bufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); - _bufferTextureArrays.Add(new BufferTextureArrayBinding(array, texture, range, bindingInfo, index)); + _bufferTextureArrays.Add(new BufferTextureArrayBinding(array, texture, bufferCache, range, bindingInfo, index)); } /// @@ -917,13 +928,14 @@ namespace Ryujinx.Graphics.Gpu.Memory ShaderStage stage, IImageArray array, ITexture texture, + BufferCache bufferCache, MultiRange range, TextureBindingInfo bindingInfo, int index) { - _channel.MemoryManager.Physical.BufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); + bufferCache.CreateBuffer(range, BufferStageUtils.TextureBuffer(stage, bindingInfo.Flags)); - _bufferImageArrays.Add(new BufferTextureArrayBinding(array, texture, range, bindingInfo, index)); + _bufferImageArrays.Add(new BufferTextureArrayBinding(array, texture, bufferCache, range, bindingInfo, index)); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureArrayBinding.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureArrayBinding.cs index a5338fa55..1781c4747 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureArrayBinding.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureArrayBinding.cs @@ -19,6 +19,11 @@ namespace Ryujinx.Graphics.Gpu.Memory /// public ITexture Texture { get; } + /// + /// Buffer cache that owns the buffer. + /// + public BufferCache BufferCache { get; } + /// /// Physical ranges of memory where the buffer texture data is located. /// @@ -39,18 +44,21 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Array /// Buffer texture + /// Buffer cache that owns the buffer /// Physical ranges of memory where the buffer texture data is located /// Binding info /// Index of the binding on the array public BufferTextureArrayBinding( T array, ITexture texture, + BufferCache bufferCache, MultiRange range, TextureBindingInfo bindingInfo, int index) { Array = array; Texture = texture; + BufferCache = bufferCache; Range = range; BindingInfo = bindingInfo; Index = index; diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs index 1a3fde5b6..775fc6fb1 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferTextureBinding.cs @@ -20,6 +20,11 @@ namespace Ryujinx.Graphics.Gpu.Memory /// public ITexture Texture { get; } + /// + /// Buffer cache that owns the buffer. + /// + public BufferCache BufferCache { get; } + /// /// Physical ranges of memory where the buffer texture data is located. /// @@ -40,18 +45,21 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Shader stage accessing the texture /// Buffer texture + /// Buffer cache that owns the buffer /// Physical ranges of memory where the buffer texture data is located /// Binding info /// Whether the binding is for an image or a sampler public BufferTextureBinding( ShaderStage stage, ITexture texture, + BufferCache bufferCache, MultiRange range, TextureBindingInfo bindingInfo, bool isImage) { Stage = stage; Texture = texture; + BufferCache = bufferCache; Range = range; BindingInfo = bindingInfo; IsImage = isImage; diff --git a/src/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs index 04114a955..1021fffa0 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs @@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// struct IndexBuffer { + public BufferCache BufferCache; public MultiRange Range; public IndexType Type; } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs index 6efb7f334..cfbeb503e 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Memory; +using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Memory; using Ryujinx.Memory.Range; using System; @@ -35,10 +36,9 @@ namespace Ryujinx.Graphics.Gpu.Memory public event EventHandler MemoryUnmapped; - /// - /// Physical memory where the virtual memory is mapped into. - /// - internal PhysicalMemory Physical { get; } + private readonly GpuContext _context; + private readonly List _physicalMemoryList; + private readonly Dictionary _physicalMemoryMap; /// /// Virtual range cache. @@ -53,19 +53,65 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Creates a new instance of the GPU memory manager. /// + /// GPU context /// Physical memory that this memory manager will map into /// The amount of physical CPU Memory Avaiable on the device. - internal MemoryManager(PhysicalMemory physicalMemory, ulong cpuMemorySize) + + internal MemoryManager(GpuContext context, PhysicalMemory physicalMemory, ulong cpuMemorySize) { - Physical = physicalMemory; + _context = context; + + _physicalMemoryList = new List() + { + physicalMemory + }; + + _physicalMemoryMap = new Dictionary + { + { physicalMemory, 0 } + }; + VirtualRangeCache = new VirtualRangeCache(this); CounterCache = new CounterCache(); _pageTable = new ulong[PtLvl0Size][]; - MemoryUnmapped += Physical.TextureCache.MemoryUnmappedHandler; - MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler; + MemoryUnmapped += physicalMemory.TextureCache.MemoryUnmappedHandler; + MemoryUnmapped += physicalMemory.BufferCache.MemoryUnmappedHandler; MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler; MemoryUnmapped += CounterCache.MemoryUnmappedHandler; - Physical.TextureCache.Initialize(cpuMemorySize); + physicalMemory.TextureCache.Initialize(cpuMemorySize); + } + + /// + /// Attaches the memory manager to a new GPU channel. + /// + /// Action to be performed when the buffer cache changes + internal void AttachToChannel(Action rebind) + { + PhysicalMemory physicalMemory = GetOwnPhysicalMemory(); + + physicalMemory.IncrementReferenceCount(); + physicalMemory.BufferCache.NotifyBuffersModified += rebind; + physicalMemory.BufferCache.QueuePrune(); + } + + /// + /// Attaches the memory manager to a new GPU channel. + /// + /// Action that was performed when the buffer cache changed + internal void DetachFromChannel(Action rebind) + { + PhysicalMemory physicalMemory = GetOwnPhysicalMemory(); + + physicalMemory.BufferCache.NotifyBuffersModified -= rebind; + physicalMemory.DecrementReferenceCount(); + } + + /// + /// Queues a prune of invalid entries on the buffer cache. + /// + internal void QueuePrune() + { + GetOwnPhysicalMemory().BufferCache.QueuePrune(); } /// @@ -81,15 +127,15 @@ namespace Ryujinx.Graphics.Gpu.Memory if (IsContiguous(va, size)) { - ulong address = Translate(va); + (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); if (tracked) { - return Physical.ReadTracked(address); + return physicalMemory.ReadTracked(address); } else { - return Physical.Read(address); + return physicalMemory.Read(address); } } else @@ -113,7 +159,9 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (IsContiguous(va, size)) { - return Physical.GetSpan(Translate(va), size, tracked); + (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); + + return physicalMemory.GetSpan(address, size, tracked); } else { @@ -138,7 +186,7 @@ namespace Ryujinx.Graphics.Gpu.Memory bool isContiguous = true; int mappedSize; - if (ValidateAddress(va) && GetPte(va) != PteUnmapped && Physical.IsMapped(Translate(va))) + if (ValidateAddress(va) && IsMappedOnGpuAndPhysical(va)) { ulong endVa = va + (ulong)size; ulong endVaAligned = (endVa + PageMask) & ~PageMask; @@ -151,7 +199,7 @@ namespace Ryujinx.Graphics.Gpu.Memory ulong nextVa = currentVa + PageSize; ulong nextPa = Translate(nextVa); - if (!ValidateAddress(nextVa) || GetPte(nextVa) == PteUnmapped || !Physical.IsMapped(nextPa)) + if (!ValidateAddress(nextVa) || !IsMappedOnGpuAndPhysical(nextVa)) { break; } @@ -180,7 +228,9 @@ namespace Ryujinx.Graphics.Gpu.Memory if (isContiguous) { - return Physical.GetSpan(Translate(va), mappedSize, tracked); + (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); + + return physicalMemory.GetSpan(address, mappedSize, tracked); } else { @@ -192,6 +242,23 @@ namespace Ryujinx.Graphics.Gpu.Memory } } + /// + /// Checks if a page of memory is mapped on the GPU and its backing memory. + /// + /// GPU virtual address of the page + /// True if mapped, false otherwise + private bool IsMappedOnGpuAndPhysical(ulong va) + { + (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); + + if (address == PteUnmapped) + { + return false; + } + + return physicalMemory.IsMapped(address); + } + /// /// Reads data from a possibly non-contiguous region of GPU mapped memory. /// @@ -209,22 +276,22 @@ namespace Ryujinx.Graphics.Gpu.Memory if ((va & PageMask) != 0) { - ulong pa = Translate(va); + (PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va); size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask)); - Physical.GetSpan(pa, size, tracked).CopyTo(data[..size]); + physicalMemory.GetSpan(pa, size, tracked).CopyTo(data[..size]); offset += size; } for (; offset < data.Length; offset += size) { - ulong pa = Translate(va + (ulong)offset); + (PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset); size = Math.Min(data.Length - offset, (int)PageSize); - Physical.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size)); + physicalMemory.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size)); } } @@ -239,15 +306,17 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (IsContiguous(va, size)) { - return Physical.GetWritableRegion(Translate(va), size, tracked); + (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); + + return physicalMemory.GetWritableRegion(address, size, tracked); } else { - MemoryOwner memoryOwner = MemoryOwner.Rent(size); + Memory memory = new byte[size]; - ReadImpl(va, memoryOwner.Span, tracked); + GetSpan(va, size).CopyTo(memory.Span); - return new WritableRegion(this, va, memoryOwner, tracked); + return new WritableRegion(this, va, memory, tracked); } } @@ -269,7 +338,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The data to be written public void Write(ulong va, ReadOnlySpan data) { - WriteImpl(va, data, Physical.Write); + WriteImpl(va, data, (physical, va, data) => physical.Write(va, data)); } /// @@ -279,7 +348,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The data to be written public void WriteTrackedResource(ulong va, ReadOnlySpan data) { - WriteImpl(va, data, Physical.WriteTrackedResource); + WriteImpl(va, data, (physical, va, data) => physical.WriteTrackedResource(va, data)); } /// @@ -289,10 +358,10 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The data to be written public void WriteUntracked(ulong va, ReadOnlySpan data) { - WriteImpl(va, data, Physical.WriteUntracked); + WriteImpl(va, data, (physical, va, data) => physical.WriteUntracked(va, data)); } - private delegate void WriteCallback(ulong address, ReadOnlySpan data); + private delegate void WriteCallback(PhysicalMemory physicalMemory, ulong address, ReadOnlySpan data); /// /// Writes data to possibly non-contiguous GPU mapped memory. @@ -304,7 +373,9 @@ namespace Ryujinx.Graphics.Gpu.Memory { if (IsContiguous(va, data.Length)) { - writeCallback(Translate(va), data); + (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); + + writeCallback(physicalMemory, address, data); } else { @@ -312,22 +383,67 @@ namespace Ryujinx.Graphics.Gpu.Memory if ((va & PageMask) != 0) { - ulong pa = Translate(va); + (PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va); size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask)); - writeCallback(pa, data[..size]); + writeCallback(physicalMemory, pa, data[..size]); offset += size; } for (; offset < data.Length; offset += size) { - ulong pa = Translate(va + (ulong)offset); + (PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset); size = Math.Min(data.Length - offset, (int)PageSize); - writeCallback(pa, data.Slice(offset, size)); + writeCallback(physicalMemory, pa, data.Slice(offset, size)); + } + } + } + + /// + /// Writes data to GPU mapped memory, stopping at the first unmapped page at the memory region, if any. + /// + /// GPU virtual address to write the data into + /// The data to be written + public void WriteMapped(ulong va, ReadOnlySpan data) + { + if (IsContiguous(va, data.Length)) + { + (PhysicalMemory physicalMemory, ulong address) = TranslateWithPhysicalMemory(va); + + physicalMemory.Write(address, data); + } + else + { + int offset = 0, size; + + if ((va & PageMask) != 0) + { + (PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va); + + size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask)); + + if (pa != PteUnmapped && physicalMemory.IsMapped(pa)) + { + physicalMemory.Write(pa, data[..size]); + } + + offset += size; + } + + for (; offset < data.Length; offset += size) + { + (PhysicalMemory physicalMemory, ulong pa) = TranslateWithPhysicalMemory(va + (ulong)offset); + + size = Math.Min(data.Length - offset, (int)PageSize); + + if (pa != PteUnmapped && physicalMemory.IsMapped(pa)) + { + physicalMemory.Write(pa, data.Slice(offset, size)); + } } } } @@ -359,15 +475,51 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Size in bytes of the mapping /// Kind of the resource located at the mapping public void Map(ulong pa, ulong va, ulong size, PteKind kind) + { + MapImpl(pa, va, size, kind); + } + + /// + /// Maps a given range of pages to the specified CPU virtual address from a different process. + /// + /// + /// All addresses and sizes must be page aligned. + /// + /// CPU virtual address to map into + /// GPU virtual address to be mapped + /// Size in bytes of the mapping + /// Kind of the resource located at the mapping + /// PID of the process that owns the mapping + public void MapForeign(ulong pa, ulong va, ulong size, PteKind kind, ulong ownedPid) + { + if (_context.PhysicalMemoryRegistry.TryGetValue(ownedPid, out PhysicalMemory physicalMemory)) + { + MapImpl(pa, va, size, kind, physicalMemory); + } + } + + /// + /// Maps a given range of pages to the specified CPU virtual address. + /// + /// + /// All addresses and sizes must be page aligned. + /// + /// CPU virtual address to map into + /// GPU virtual address to be mapped + /// Size in bytes of the mapping + /// Kind of the resource located at the mapping + /// Optional physical memory to import for the mapping + private void MapImpl(ulong pa, ulong va, ulong size, PteKind kind, PhysicalMemory physicalMemory = null) { lock (_pageTable) { UnmapEventArgs e = new(va, size); MemoryUnmapped?.Invoke(this, e); + byte pIndex = physicalMemory != null ? GetOrAddPhysicalMemory(physicalMemory) : (byte)0; for (ulong offset = 0; offset < size; offset += PageSize) { - SetPte(va + offset, PackPte(pa + offset, kind)); + SetPte(va + offset, PackPte(pa + offset, pIndex, kind)); } RunRemapActions(e); @@ -418,12 +570,14 @@ namespace Ryujinx.Graphics.Gpu.Memory for (int page = 0; page < pages - 1; page++) { - if (!ValidateAddress(va + PageSize) || GetPte(va + PageSize) == PteUnmapped) + ulong nextPte = GetPte(va + PageSize); + + if (!ValidateAddress(va + PageSize) || nextPte == PteUnmapped) { return false; } - if (Translate(va) + PageSize != Translate(va + PageSize)) + if (GetPte(va) + PageSize != nextPte) { return false; } @@ -457,7 +611,7 @@ namespace Ryujinx.Graphics.Gpu.Memory int pages = (int)((endVaRounded - va) / PageSize); - List regions = []; + List regions = new(); for (int page = 0; page < pages - 1; page++) { @@ -535,6 +689,49 @@ namespace Ryujinx.Graphics.Gpu.Memory return true; } + /// + /// Gets the backing memory for a given GPU virtual address. + /// + /// GPU virtual address to get the backing memory from + /// The backing memory for the specified GPU virtual address + internal PhysicalMemory GetBackingMemory(ulong va) + { + ulong pte = GetPte(va); + + if (pte == PteUnmapped) + { + return GetOwnPhysicalMemory(); + } + + return _physicalMemoryList[UnpackPIndexFromPte(pte)]; + } + + /// + /// Gets the backing memory that is owned by this GPU memory manager. + /// + /// The backing memory owned by this memory manager + private PhysicalMemory GetOwnPhysicalMemory() + { + return _physicalMemoryList[0]; + } + + /// + /// Gets the index for a given physical memory on the list, adding it to the list if needed. + /// + /// Physical memory to get the index from + /// The index of the physical memory on the list + private byte GetOrAddPhysicalMemory(PhysicalMemory physicalMemory) + { + if (!_physicalMemoryMap.TryGetValue(physicalMemory, out byte pIndex)) + { + pIndex = checked((byte)_physicalMemoryList.Count); + _physicalMemoryList.Add(physicalMemory); + _physicalMemoryMap.Add(physicalMemory, pIndex); + } + + return pIndex; + } + /// /// Validates a GPU virtual address. /// @@ -636,6 +833,28 @@ namespace Ryujinx.Graphics.Gpu.Memory return Math.Min(maxSize, va - startVa); } + /// + /// Translates a GPU virtual address to a CPU virtual address and the associated physical memory. + /// + /// GPU virtual address to be translated + /// CPU virtual address with the physical memory, or if unmapped + private (PhysicalMemory, ulong) TranslateWithPhysicalMemory(ulong va) + { + if (!ValidateAddress(va)) + { + return (GetOwnPhysicalMemory(), PteUnmapped); + } + + ulong pte = GetPte(va); + + if (pte == PteUnmapped) + { + return (GetOwnPhysicalMemory(), PteUnmapped); + } + + return (_physicalMemoryList[UnpackPIndexFromPte(pte)], UnpackPaFromPte(pte) + (va & PageMask)); + } + /// /// Gets the kind of a given memory page. /// This might indicate the type of resource that can be allocated on the page, and also texture tiling. @@ -659,6 +878,18 @@ namespace Ryujinx.Graphics.Gpu.Memory return UnpackKindFromPte(pte); } + public bool IsForeignMapping(ulong va) + { + ulong pte = GetPte(va); + + if (pte == PteUnmapped) + { + return false; + } + + return UnpackPIndexFromPte(pte) != 0; + } + /// /// Gets the Page Table entry for a given GPU virtual address. /// @@ -704,11 +935,12 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Creates a page table entry from a physical address and kind. /// /// Physical address + /// Index of the physical memory on the list /// Kind /// Page table entry - private static ulong PackPte(ulong pa, PteKind kind) + private static ulong PackPte(ulong pa, byte pIndex, PteKind kind) { - return pa | ((ulong)kind << 56); + return pa | ((ulong)pIndex << 48) | ((ulong)kind << 56); } /// @@ -721,6 +953,16 @@ namespace Ryujinx.Graphics.Gpu.Memory return (PteKind)(pte >> 56); } + /// + /// Unpacks the physical memory index in the list from a page table entry. + /// + /// Page table entry + /// Physical memory index + private static byte UnpackPIndexFromPte(ulong pte) + { + return (byte)(pte >> 48); + } + /// /// Unpacks physical address from a page table entry. /// @@ -728,7 +970,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Physical address private static ulong UnpackPaFromPte(ulong pte) { - return pte & 0xffffffffffffffUL; + return pte & 0xffffffffffffUL; } } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs b/src/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs index 206e1b48c..5006be89c 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs @@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// struct VertexBuffer { + public BufferCache BufferCache; public MultiRange Range; public int Stride; public int Divisor; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index c7d86a47e..2f1563032 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gpu.Image; +using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader.Translation; using System; @@ -66,11 +67,11 @@ namespace Ryujinx.Graphics.Gpu.Shader /// public uint ConstantBuffer1Read(int offset) { - ulong baseAddress = _compute + (PhysicalMemory physical, ulong baseAddress) = _compute ? _channel.BufferManager.GetComputeUniformBufferAddress(1) : _channel.BufferManager.GetGraphicsUniformBufferAddress(_stageIndex, 1); - return _channel.MemoryManager.Physical.Read(baseAddress + (ulong)offset); + return physical.Read(baseAddress + (ulong)offset); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 476ae3d82..0285d5517 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -733,15 +733,15 @@ namespace Ryujinx.Graphics.Gpu.Shader byte[] codeB, bool asCompute) { - ulong cb1DataAddress = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1); - + (PhysicalMemory physical, ulong cb1DataAddress) = channel.BufferManager.GetGraphicsUniformBufferAddress(0, 1); + MemoryManager memoryManager = channel.MemoryManager; codeA ??= memoryManager.GetSpan(vertexA.Address, vertexA.Size).ToArray(); codeB ??= memoryManager.GetSpan(currentStage.Address, currentStage.Size).ToArray(); - byte[] cb1DataA = ReadArray(memoryManager, cb1DataAddress, vertexA.Cb1DataSize); - byte[] cb1DataB = ReadArray(memoryManager, cb1DataAddress, currentStage.Cb1DataSize); - + byte[] cb1DataA = ReadArray(physical, cb1DataAddress, vertexA.Cb1DataSize); + byte[] cb1DataB = ReadArray(physical, cb1DataAddress, currentStage.Cb1DataSize); + ShaderDumpPaths pathsA = default; ShaderDumpPaths pathsB = default; @@ -775,11 +775,11 @@ namespace Ryujinx.Graphics.Gpu.Shader { MemoryManager memoryManager = channel.MemoryManager; - ulong cb1DataAddress = context.Stage == ShaderStage.Compute + (PhysicalMemory physical, ulong cb1DataAddress) = context.Stage == ShaderStage.Compute ? channel.BufferManager.GetComputeUniformBufferAddress(1) : channel.BufferManager.GetGraphicsUniformBufferAddress(StageToStageIndex(context.Stage), 1); - byte[] cb1Data = ReadArray(memoryManager, cb1DataAddress, context.Cb1DataSize); + byte[] cb1Data = ReadArray(physical, cb1DataAddress, context.Cb1DataSize); code ??= memoryManager.GetSpan(context.Address, context.Size).ToArray(); ShaderDumpPaths paths = dumper?.Dump(code, context.Stage == ShaderStage.Compute) ?? default; @@ -793,18 +793,18 @@ namespace Ryujinx.Graphics.Gpu.Shader /// /// Reads data from physical memory, returns an empty array if the memory is unmapped or size is 0. /// - /// Memory manager with the physical memory to read from + /// Physical memory to read the data from, might be null /// Physical address of the region to read /// Size in bytes of the data /// An array with the data at the specified memory location - private static byte[] ReadArray(MemoryManager memoryManager, ulong address, int size) + private static byte[] ReadArray(PhysicalMemory physicalMemory, ulong address, int size) { - if (address == MemoryManager.PteUnmapped || size == 0) + if (address == MemoryManager.PteUnmapped || size == 0 || physicalMemory == null) { return []; } - return memoryManager.Physical.GetSpan(address, size).ToArray(); + return physicalMemory.GetSpan(address, size).ToArray(); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index 4bb4392e1..dd20fa9d8 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -696,7 +696,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, textureBufferIndex); - cachedTextureBuffer = MemoryMarshal.Cast(channel.MemoryManager.Physical.GetSpan(bounds.Range)); + cachedTextureBuffer = MemoryMarshal.Cast(bounds.Physical.GetSpan(bounds.Range)); cachedTextureBufferIndex = textureBufferIndex; if (samplerBufferIndex == textureBufferIndex) @@ -710,7 +710,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { ref BufferBounds bounds = ref channel.BufferManager.GetUniformBufferBounds(isCompute, stageIndex, samplerBufferIndex); - cachedSamplerBuffer = MemoryMarshal.Cast(channel.MemoryManager.Physical.GetSpan(bounds.Range)); + cachedSamplerBuffer = MemoryMarshal.Cast(bounds.Physical.GetSpan(bounds.Range)); cachedSamplerBufferIndex = samplerBufferIndex; } diff --git a/src/Ryujinx.Graphics.Gpu/Window.cs b/src/Ryujinx.Graphics.Gpu/Window.cs index 5c3463f2a..80b3b18b5 100644 --- a/src/Ryujinx.Graphics.Gpu/Window.cs +++ b/src/Ryujinx.Graphics.Gpu/Window.cs @@ -9,11 +9,18 @@ using System.Threading; namespace Ryujinx.Graphics.Gpu { + using Texture = Image.Texture; + + public record TextureData(int Width, int Height, byte[] Data); + /// /// GPU image presentation window. /// public class Window { + private const int CaptureTextureWidth = 1280; + private const int CaptureTextureHeight = 720; + private readonly GpuContext _context; /// @@ -85,7 +92,21 @@ namespace Ryujinx.Graphics.Gpu } } + private class PresentedTexture + { + public readonly Texture Texture; + public readonly ImageCrop Crop; + + public PresentedTexture(Texture texture, ImageCrop crop) + { + Texture = texture; + Crop = crop; + } + } + private readonly ConcurrentQueue _frameQueue; + private PresentedTexture _lastPresentedTexture; + private ITexture _captureTexture; private int _framesAvailable; @@ -188,6 +209,51 @@ namespace Ryujinx.Graphics.Gpu return true; } + public TextureData GetLastPresentedData() + { + PresentedTexture pt = Volatile.Read(ref _lastPresentedTexture); + + if (pt != null) + { + byte[] inputData = CaptureLastFrame(pt.Texture.HostTexture, pt.Crop); + + int size = SizeCalculator.GetBlockLinearTextureSize( + CaptureTextureWidth, + CaptureTextureHeight, + 1, + 1, + 1, + 1, + 1, + 4, + 16, + 1, + 1).TotalSize; + + byte[] data = new byte[size]; + + LayoutConverter.ConvertLinearToBlockLinear(data, CaptureTextureWidth, CaptureTextureHeight, CaptureTextureWidth * 4, 4, 16, inputData); + + return new TextureData(CaptureTextureWidth, CaptureTextureHeight, data); + } + + return new TextureData(0, 0, Array.Empty()); + } + + public TextureData GetLastPresentedDataLinear() + { + PresentedTexture pt = Volatile.Read(ref _lastPresentedTexture); + + if (pt != null) + { + byte[] inputData = CaptureLastFrame(pt.Texture.HostTexture, new ImageCrop()); + + return new TextureData(pt.Texture.Info.Width, pt.Texture.Info.Height, inputData); + } + + return new TextureData(0, 0, Array.Empty()); + } + /// /// Presents a texture on the queue. /// If the queue is empty, then no texture is presented. @@ -205,6 +271,10 @@ namespace Ryujinx.Graphics.Gpu pt.Cache.Tick(); + EnsureCaptureTexture(); + + Volatile.Write(ref _lastPresentedTexture, new PresentedTexture(texture, pt.Crop)); + texture.SynchronizeMemory(); ImageCrop crop = new( @@ -244,6 +314,96 @@ namespace Ryujinx.Graphics.Gpu } } + private void EnsureCaptureTexture() + { + if (_captureTexture == null) + { + _captureTexture = _context.Renderer.CreateTexture(new TextureCreateInfo( + 1280, + 720, + 1, + 1, + 1, + 1, + 1, + 4, + Format.R8G8B8A8Unorm, + DepthStencilMode.Depth, + Target.Texture2D, + SwizzleComponent.Red, + SwizzleComponent.Green, + SwizzleComponent.Blue, + SwizzleComponent.Alpha)); + } + } + + private byte[] CaptureLastFrame(ITexture lastFrame, ImageCrop crop) + { + int cropLeft, cropRight, cropTop, cropBottom; + + if (crop.Left == 0 && crop.Right == 0) + { + cropLeft = 0; + cropRight = lastFrame.Width; + } + else + { + cropLeft = crop.Left; + cropRight = crop.Right; + } + + if (crop.Top == 0 && crop.Bottom == 0) + { + cropTop = 0; + cropBottom = lastFrame.Height; + } + else + { + cropTop = crop.Top; + cropBottom = crop.Bottom; + } + + int x1, y1, x2, y2; + + if (crop.FlipX) + { + x1 = cropRight; + x2 = cropLeft; + } + else + { + x1 = cropLeft; + x2 = cropRight; + } + + if (crop.FlipY) + { + y1 = cropBottom; + y2 = cropTop; + } + else + { + y1 = cropTop; + y2 = cropBottom; + } + + Extents2D srcRegion = new(x1, y1, x2, y2); + Extents2D dstRegion = new(0, 0, CaptureTextureWidth, CaptureTextureHeight); + + byte[] outputData = null; + + _context.Renderer.BackgroundContextAction(() => + { + lastFrame.CopyTo(_captureTexture, srcRegion, dstRegion, true); + + using var data = _captureTexture.GetData(); + + outputData = data.Get().ToArray(); + }); + + return outputData; + } + /// /// Indicate that a frame on the queue is ready to be acquired. /// diff --git a/src/Ryujinx.HLE/HOS/ArmProcessContext.cs b/src/Ryujinx.HLE/HOS/ArmProcessContext.cs index 09a721644..e474a7a24 100644 --- a/src/Ryujinx.HLE/HOS/ArmProcessContext.cs +++ b/src/Ryujinx.HLE/HOS/ArmProcessContext.cs @@ -24,7 +24,7 @@ namespace Ryujinx.HLE.HOS private readonly ICpuContext _cpuContext; private T _memoryManager; - public IVirtualMemoryManager AddressSpace => _memoryManager; + public IVirtualMemoryManagerTracked AddressSpace => _memoryManager; public ulong AddressSpaceSize { get; } diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index 7af8711c7..6df4df91b 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -75,7 +75,7 @@ namespace Ryujinx.HLE.HOS internal ServerBase TimeServer { get; private set; } internal ServerBase ViServer { get; private set; } internal ServerBase ViServerM { get; private set; } - internal ServerBase ViServerS { get; private set; } + internal ViServer ViServerS { get; private set; } internal ServerBase LdnServer { get; private set; } internal KSharedMemory HidSharedMem { get; private set; } @@ -254,7 +254,7 @@ namespace Ryujinx.HLE.HOS TimeServer = new ServerBase(KernelContext, "TimeServer"); ViServer = new ServerBase(KernelContext, "ViServerU"); ViServerM = new ServerBase(KernelContext, "ViServerM"); - ViServerS = new ServerBase(KernelContext, "ViServerS"); + ViServerS = new ViServer(KernelContext, "ViServerS"); LdnServer = new ServerBase(KernelContext, "LdnServer"); StartNewServices(); diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs index ac36b781b..9b2bb7f92 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/IProcessContext.cs @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { interface IProcessContext : IDisposable { - IVirtualMemoryManager AddressSpace { get; } + IVirtualMemoryManagerTracked AddressSpace { get; } ulong AddressSpaceSize { get; } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs index 32dca1512..bf0f1542f 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/KProcess.cs @@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process private IProcessContextFactory _contextFactory; public IProcessContext Context { get; private set; } - public IVirtualMemoryManager CpuMemory => Context.AddressSpace; + public IVirtualMemoryManagerTracked CpuMemory => Context.AddressSpace; public HleProcessDebugger Debugger { get; private set; } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs index b4ae6ec4e..b96aa37d3 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Process/ProcessContext.cs @@ -6,11 +6,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process { class ProcessContext : IProcessContext { - public IVirtualMemoryManager AddressSpace { get; } + public IVirtualMemoryManagerTracked AddressSpace { get; } public ulong AddressSpaceSize { get; } - public ProcessContext(IVirtualMemoryManager asManager, ulong addressSpaceSize) + public ProcessContext(IVirtualMemoryManagerTracked asManager, ulong addressSpaceSize) { AddressSpace = asManager; AddressSpaceSize = addressSpaceSize; diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/IOverlayAppletProxy.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/IOverlayAppletProxy.cs new file mode 100644 index 000000000..fd05ae783 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/IOverlayAppletProxy.cs @@ -0,0 +1,105 @@ +using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.OverlayAppletProxy; +using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService +{ + class IOverlayAppletProxy : IpcService + { + private readonly ulong _pid; + + public IOverlayAppletProxy(ulong pid) + { + _pid = pid; + } + + [CommandCmif(0)] + // GetCommonStateGetter() -> object + public ResultCode GetCommonStateGetter(ServiceCtx context) + { + MakeObject(context, new ICommonStateGetter(context)); + + return ResultCode.Success; + } + + [CommandCmif(1)] + // GetSelfController() -> object + public ResultCode GetSelfController(ServiceCtx context) + { + MakeObject(context, new ISelfController(context, _pid)); + + return ResultCode.Success; + } + + [CommandCmif(2)] + // GetWindowController() -> object + public ResultCode GetWindowController(ServiceCtx context) + { + MakeObject(context, new IWindowController(_pid)); + + return ResultCode.Success; + } + + [CommandCmif(3)] + // GetAudioController() -> object + public ResultCode GetAudioController(ServiceCtx context) + { + MakeObject(context, new IAudioController()); + + return ResultCode.Success; + } + + [CommandCmif(4)] + // GetDisplayController() -> object + public ResultCode GetDisplayController(ServiceCtx context) + { + MakeObject(context, new IDisplayController(context)); + + return ResultCode.Success; + } + + [CommandCmif(11)] + // GetLibraryAppletCreator() -> object + public ResultCode GetLibraryAppletCreator(ServiceCtx context) + { + MakeObject(context, new ILibraryAppletCreator()); + + return ResultCode.Success; + } + + [CommandCmif(20)] + // GetOverlayFunctions() -> object + public ResultCode GetOverlayFunctions(ServiceCtx context) + { + MakeObject(context, new IOverlayFunctions(context.Device.System)); + + return ResultCode.Success; + } + + [CommandCmif(21)] + // GetAppletCommonFunctions() -> object + public ResultCode GetAppletCommonFunctions(ServiceCtx context) + { + MakeObject(context, new IAppletCommonFunctions()); + + return ResultCode.Success; + } + + [CommandCmif(23)] + // GetGlobalStateController() -> object + public ResultCode GetGlobalStateController(ServiceCtx context) + { + MakeObject(context, new IGlobalStateController()); + + return ResultCode.Success; + } + + [CommandCmif(1000)] + // GetDebugFunctions() -> object + public ResultCode GetDebugFunctions(ServiceCtx context) + { + MakeObject(context, new IDebugFunctions()); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/OverlayAppletProxy/IOverlayFunctions.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/OverlayAppletProxy/IOverlayFunctions.cs new file mode 100644 index 000000000..fce952ab3 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/OverlayAppletProxy/IOverlayFunctions.cs @@ -0,0 +1,155 @@ +using LibHac.Util; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.OverlayAppletProxy +{ + class IOverlayFunctions : IpcService + { + + public IOverlayFunctions(Horizon system) + { + } + + [CommandCmif(0)] + // BeginToWatchShortHomeButtonMessage() + public ResultCode BeginToWatchShortHomeButtonMessage(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(1)] + // EndToWatchShortHomeButtonMessage() + public ResultCode EndToWatchShortHomeButtonMessage(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(2)] + // GetApplicationIdForLogo() -> nn::ncm::ApplicationId + public ResultCode GetApplicationIdForLogo(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + context.ResponseData.Write(0L); + + return ResultCode.Success; + } + + [CommandCmif(3)] + // SetGpuTimeSliceBoost(u64) + public ResultCode SetGpuTimeSliceBoost(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(4)] + // SetAutoSleepTimeAndDimmingTimeEnabled(u8) + public ResultCode SetAutoSleepTimeAndDimmingTimeEnabled(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(5)] + // TerminateApplicationAndSetReason(u32) + public ResultCode TerminateApplicationAndSetReason(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(6)] + // SetScreenShotPermissionGlobally(u8) + public ResultCode SetScreenShotPermissionGlobally(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(10)] + // StartShutdownSequenceForOverlay() + public ResultCode StartShutdownSequenceForOverlay(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(11)] + // StartRebootSequenceForOverlay() + public ResultCode StartRebootSequenceForOverlay(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(20)] + // SetHandlingHomeButtonShortPressedEnabled(u8) + public ResultCode SetHandlingHomeButtonShortPressedEnabled(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(21)] + // SetHandlingTouchScreenInputEnabled(u8) + public ResultCode SetHandlingTouchScreenInputEnabled(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(30)] + // SetHealthWarningShowingState(u8) + public ResultCode SetHealthWarningShowingState(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(31)] + // IsHealthWarningRequired() -> bool + public ResultCode IsHealthWarningRequired(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + context.ResponseData.Write(false); + + return ResultCode.Success; + } + + [CommandCmif(90)] + // SetRequiresGpuResourceUse(u8) + public ResultCode SetRequiresGpuResourceUse(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(101)] + // BeginToObserveHidInputForDevelop() + public ResultCode BeginToObserveHidInputForDevelop(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs index 6bd35a779..0c8da8339 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IDisplayController.cs @@ -102,5 +102,34 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + + [CommandCmif(22)] + // AcquireLastApplicationCaptureSharedBuffer() -> (b8, u32) + public ResultCode AcquireLastApplicationCaptureSharedBuffer(ServiceCtx context) + { + context.ResponseData.Write(1); + context.ResponseData.Write(context.Device.System.ViServerS.GetApplicationLastPresentedFrameHandle(context.Device.Gpu)); + + return ResultCode.Success; + } + + [CommandCmif(26)] + + // AcquireCallerAppletCaptureSharedBuffer() -> (b8, u32) + public ResultCode AcquireCallerAppletCaptureSharedBuffer(ServiceCtx context) + { + // TODO: How does the handling for applets differ from the one for applications? + context.ResponseData.Write(1); + context.ResponseData.Write(context.Device.System.ViServerS.GetApplicationLastPresentedFrameHandle(context.Device.Gpu)); + + return ResultCode.Success; + } + + [CommandCmif(27)] + public ResultCode ReleaseCallerAppletCaptureSharedBuffer(ServiceCtx context) + { + context.ResponseData.Write(2); + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs index 8e0f515ba..71b4b482a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs @@ -225,7 +225,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys public ResultCode CreateManagedDisplayLayer(ServiceCtx context) { context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, _pid); - context.Device.System.SurfaceFlinger.SetRenderLayer(layerId); context.ResponseData.Write(layerId); @@ -236,18 +235,27 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // IsSystemBufferSharingEnabled() public ResultCode IsSystemBufferSharingEnabled(ServiceCtx context) { - // NOTE: Service checks a private field and return an error if the SystemBufferSharing is disabled. - - return ResultCode.NotImplemented; + // TODO: Implement this once we have a way to check if we're not an AppletId.Application + return ResultCode.Success; } + [CommandCmif(42)] // 4.0.0+ + // GetSystemSharedLayerHandle() -> (nn::vi::fbshare::SharedBufferHandle, nn::vi::fbshare::SharedLayerHandle) + public ResultCode GetSystemSharedLayerHandle(ServiceCtx context) + { + context.ResponseData.Write((ulong)context.Device.System.ViServerS.GetSharedBufferNvMapId()); + context.ResponseData.Write(context.Device.System.ViServerS.GetSharedLayerId()); + + return ResultCode.Success; + } + [CommandCmif(44)] // 10.0.0+ // CreateManagedDisplaySeparableLayer() -> (u64, u64) public ResultCode CreateManagedDisplaySeparableLayer(ServiceCtx context) { context.Device.System.SurfaceFlinger.CreateLayer(out long displayLayerId, _pid); context.Device.System.SurfaceFlinger.CreateLayer(out long recordingLayerId, _pid); - context.Device.System.SurfaceFlinger.SetRenderLayer(displayLayerId); + //context.Device.System.SurfaceFlinger.SetRenderLayer(displayLayerId); context.ResponseData.Write(displayLayerId); context.ResponseData.Write(recordingLayerId); diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs index b8741b22b..b5ec6a6ee 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs @@ -26,6 +26,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE return ResultCode.Success; } + + [CommandCmif(300)] + // OpenOverlayAppletProxy(pid, handle) -> object + public ResultCode OpenOverlayAppletProxy(ServiceCtx context) + { + MakeObject(context, new IOverlayAppletProxy(context.Request.HandleDesc.PId)); + + return ResultCode.Success; + } [CommandCmif(350)] // OpenSystemApplicationProxy(u64, pid, handle) -> object diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs index 834bee6f0..a9f651125 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs @@ -35,6 +35,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid public Dictionary> RumbleQueues = new(); public Dictionary LastVibrationValues = new(); + + internal PlayerIndex LastActiveNpad { get; set; } public NpadDevices(Switch device, bool active = true) : base(device, active) { @@ -384,6 +386,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid return; } + LastActiveNpad = state.PlayerId; + ref RingLifo lifo = ref GetCommonStateLifo(ref currentNpad); NpadCommonState newState = new() @@ -639,5 +643,20 @@ namespace Ryujinx.HLE.HOS.Services.Hid return rumbleQueue; } + + public NpadIdType GetLastActiveNpadId() + { + return LastActiveNpad switch { + PlayerIndex.Player1 => NpadIdType.Player1, + PlayerIndex.Player2 => NpadIdType.Player2, + PlayerIndex.Player3 => NpadIdType.Player3, + PlayerIndex.Player4 => NpadIdType.Player4, + PlayerIndex.Player5 => NpadIdType.Player5, + PlayerIndex.Player6 => NpadIdType.Player6, + PlayerIndex.Player7 => NpadIdType.Player7, + PlayerIndex.Player8 => NpadIdType.Player8, + _ => NpadIdType.Handheld, + }; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs b/src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs index 0b4eba948..5307292a5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/IHidSystemServer.cs @@ -24,15 +24,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid // GetLastActiveNpad(u32) -> u8, u8 public ResultCode GetLastActiveNpad(ServiceCtx context) { - // TODO: RequestData seems to have garbage data, reading an extra uint seems to fix the issue. - context.RequestData.ReadUInt32(); + context.ResponseData.Write((byte)context.Device.Hid.Npads.GetLastActiveNpadId()); - ResultCode resultCode = GetAppletFooterUiTypeImpl(context, out AppletFooterUiType appletFooterUiType); - - context.ResponseData.Write((byte)appletFooterUiType); - context.ResponseData.Write((byte)0); - - return resultCode; + return ResultCode.Success; } [CommandCmif(307)] diff --git a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs index 577d03822..17a4311a1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nifm/StaticService/IRequest.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common; using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Threading; @@ -105,6 +106,26 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } + [CommandCmif(6)] + // SetRequirementPreset(u32) + public ResultCode SetRequirementPreset(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceNifm); + + return ResultCode.Success; + } + + [CommandCmif(9)] + // SetNetworkProfileId(nn::util::Uuid) + public ResultCode SetNetworkProfileId(ServiceCtx context) + { + UInt128 uuid = context.RequestData.ReadStruct(); + + Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { uuid }); + + return ResultCode.Success; + } + [CommandCmif(11)] // SetConnectionConfirmationOption(i8) public ResultCode SetConnectionConfirmationOption(ServiceCtx context) @@ -142,5 +163,38 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService return ResultCode.Success; } + + [CommandCmif(23)] + // SetKeptInSleep(bool) + public ResultCode SetKeptInSleep(ServiceCtx context) + { + bool keptInSleep = context.RequestData.ReadBoolean(); + + Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { keptInSleep }); + + return ResultCode.Success; + } + + [CommandCmif(24)] + // RegisterSocketDescriptor(s32) + public ResultCode RegisterSocketDescriptor(ServiceCtx context) + { + int socketDescriptor = context.RequestData.ReadInt32(); + + Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { socketDescriptor }); + + return ResultCode.Success; + } + + [CommandCmif(25)] + // UnregisterSocketDescriptor(s32) + public ResultCode UnregisterSocketDescriptor(ServiceCtx context) + { + int socketDescriptor = context.RequestData.ReadInt32(); + + Logger.Stub?.PrintStub(LogClass.ServiceNifm, new { socketDescriptor }); + + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs index 9d0104a01..97154c9fb 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs @@ -245,8 +245,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu } } - NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, arguments.NvMapHandle); - + NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(arguments.NvMapHandle); + if (map == null) { Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{arguments.NvMapHandle:x8}!"); @@ -282,7 +282,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu { if (_asContext.ValidateFixedBuffer(arguments.Offset, size, pageSize)) { - _asContext.Gmm.Map(physicalAddress, arguments.Offset, size, (PteKind)arguments.Kind); + Map(physicalAddress, arguments.Offset, size, (PteKind)arguments.Kind, map.OwnerPid); } else { @@ -301,7 +301,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu _memoryAllocator.AllocateRange(va, size, freeAddressStartPosition); } - _asContext.Gmm.Map(physicalAddress, va, size, (PteKind)arguments.Kind); + Map(physicalAddress, va, size, (PteKind)arguments.Kind, map.OwnerPid); arguments.Offset = va; } @@ -380,8 +380,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu ulong mapOffs = (ulong)argument.MapOffset << 16; PteKind kind = (PteKind)argument.Kind; - NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, nvmapHandle); - + NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(nvmapHandle); + if (map == null) { Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{nvmapHandle:x8}!"); @@ -389,13 +389,25 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu return NvInternalResult.InvalidInput; } - gmm.Map(mapOffs + map.Address, gpuVa, size, kind); + Map(mapOffs + map.Address, gpuVa, size, kind, map.OwnerPid); } } return NvInternalResult.Success; } + private void Map(ulong pa, ulong va, ulong size, PteKind kind, ulong ownerPid) + { + if (Owner == ownerPid) + { + _asContext.Gmm.Map(pa, va, size, kind); + } + else + { + _asContext.Gmm.MapForeign(pa, va, size, kind, ownerPid); + } + } + public override void Close() { } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs index f81c2edef..3a60964e1 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs @@ -167,8 +167,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel foreach (CommandBuffer commandBuffer in commandBuffers) { - NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBuffer.Mem); - + NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(commandBuffer.Mem); + ReadOnlySpan data = _memory.GetSpan(map.Address + commandBuffer.Offset, commandBuffer.WordsCount * 4); _host1xContext.Host1x.Submit(MemoryMarshal.Cast(data), _contextId); @@ -237,8 +237,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries) { - NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle); - + NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(commandBufferEntry.MapHandle); + if (map == null) { Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!"); @@ -279,8 +279,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries) { - NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, commandBufferEntry.MapHandle); - + NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(commandBufferEntry.MapHandle); + if (map == null) { Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{commandBufferEntry.MapHandle:x8}!"); diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs index 6a0ac58bd..9cc6063da 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs @@ -71,8 +71,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap uint size = BitUtils.AlignUp(arguments.Size, (uint)MemoryManager.PageSize); - arguments.Handle = CreateHandleFromMap(new NvMapHandle(size)); - + arguments.Handle = CreateHandleFromMap(new NvMapHandle(size) { OwnerPid = Owner }); + Logger.Debug?.Print(LogClass.ServiceNv, $"Created map {arguments.Handle} with size 0x{size:x8}!"); return NvInternalResult.Success; @@ -80,8 +80,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap private NvInternalResult FromId(ref NvMapFromId arguments) { - NvMapHandle map = GetMapFromHandle(Owner, arguments.Id); - + NvMapHandle map = GetMapFromHandle(arguments.Id); + if (map == null) { Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!"); @@ -98,8 +98,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap private NvInternalResult Alloc(ref NvMapAlloc arguments) { - NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle); - + NvMapHandle map = GetMapFromHandle(arguments.Handle); + if (map == null) { Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!"); @@ -152,8 +152,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap private NvInternalResult Free(ref NvMapFree arguments) { - NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle); - + NvMapHandle map = GetMapFromHandle(arguments.Handle); + if (map == null) { Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!"); @@ -179,8 +179,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap private NvInternalResult Param(ref NvMapParam arguments) { - NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle); - + NvMapHandle map = GetMapFromHandle(arguments.Handle); + if (map == null) { Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!"); @@ -217,8 +217,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap private NvInternalResult GetId(ref NvMapGetId arguments) { - NvMapHandle map = GetMapFromHandle(Owner, arguments.Handle); - + NvMapHandle map = GetMapFromHandle(arguments.Handle); + if (map == null) { Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid handle 0x{arguments.Handle:x8}!"); @@ -237,25 +237,30 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap // _maps.TryRemove(GetOwner(), out _); } - private int CreateHandleFromMap(NvMapHandle map) + public static int CreateMap(ulong pid, ulong address, uint size) + { + return CreateHandleFromMap(new NvMapHandle(size) { Address = address, OwnerPid = pid }); + } + + private static int CreateHandleFromMap(NvMapHandle map) { return _maps.Add(map); } - private static bool DeleteMapWithHandle(ulong pid, int handle) + private static bool DeleteMapWithHandle(int handle) { return _maps.Delete(handle) != null; } public static void IncrementMapRefCount(ulong pid, int handle) { - GetMapFromHandle(pid, handle)?.IncrementRefCount(); + GetMapFromHandle(handle)?.IncrementRefCount(); } public static bool DecrementMapRefCount(ulong pid, int handle) { - NvMapHandle map = GetMapFromHandle(pid, handle); - + NvMapHandle map = GetMapFromHandle(handle); + if (map == null) { return false; @@ -263,8 +268,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap if (map.DecrementRefCount() <= 0) { - DeleteMapWithHandle(pid, handle); - + DeleteMapWithHandle(handle); + Logger.Debug?.Print(LogClass.ServiceNv, $"Deleted map {handle}!"); return true; @@ -275,7 +280,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap } } - public static NvMapHandle GetMapFromHandle(ulong pid, int handle) + public static NvMapHandle GetMapFromHandle(int handle) { return _maps.Get(handle); } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandle.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandle.cs index e821b571d..d115702a6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandle.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/Types/NvMapHandle.cs @@ -14,7 +14,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap public ulong Address; public bool Allocated; public ulong DmaMapAddress; - + public ulong OwnerPid; + private long _dupes; public NvMapHandle() diff --git a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs index 0fd5ab670..93fef364d 100644 --- a/src/Ryujinx.HLE/HOS/Services/ServerBase.cs +++ b/src/Ryujinx.HLE/HOS/Services/ServerBase.cs @@ -172,6 +172,15 @@ namespace Ryujinx.HLE.HOS.Services { ServerLoop(); } + + protected virtual ulong CalculateRequiredHeapSize() + { + return 0UL; + } + + protected virtual void CustomInit(KernelContext context, ulong pid, ulong heapAddress) + { + } private void ServerLoop() { @@ -196,10 +205,14 @@ namespace Ryujinx.HLE.HOS.Services Result result = _selfProcess.HandleTable.GenerateHandle(_wakeEvent.ReadableEvent, out _wakeHandle); InitDone.Set(); + + ulong heapSize = CalculateRequiredHeapSize() + PointerBufferSize; ulong messagePtr = _selfThread.TlsAddress; - _context.Syscall.SetHeapSize(out ulong heapAddr, 0x200000); - + _context.Syscall.SetHeapSize(out ulong heapAddr, BitUtils.AlignUp(heapSize, 0x200000UL)); + + CustomInit(_context, _selfProcess.Pid, heapAddr + PointerBufferSize); + _selfProcess.CpuMemory.Write(messagePtr + 0x0, 0); _selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10); _selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48)); diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index 935e9895e..78b286d7a 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs @@ -4,6 +4,7 @@ using Ryujinx.Common.Logging; using Ryujinx.Common.PreciseSleep; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu; +using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; using System; using System.Collections.Generic; @@ -38,8 +39,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger private readonly Lock _lock = new(); - public long RenderLayerId { get; private set; } - private class Layer { public int ProducerBinderId; @@ -60,7 +59,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { _device = device; _layers = new Dictionary(); - RenderLayerId = 0; _composerThread = new Thread(HandleComposition) { @@ -239,34 +237,12 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger private void CloseLayer(long layerId, Layer layer) { - // If the layer was removed and the current in use, we need to change the current layer in use. - if (RenderLayerId == layerId) - { - // If no layer is availaible, reset to default value. - if (_layers.Count == 0) - { - SetRenderLayer(0); - } - else - { - SetRenderLayer(_layers.Last().Key); - } - } - if (layer.State == LayerState.ManagedOpened) { layer.State = LayerState.ManagedClosed; } } - public void SetRenderLayer(long layerId) - { - lock (_lock) - { - RenderLayerId = layerId; - } - } - private Layer GetLayerByIdLocked(long layerId) { foreach (KeyValuePair pair in _layers) @@ -360,41 +336,55 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { lock (_lock) { - // TODO: support multilayers (& multidisplay ?) - if (RenderLayerId == 0) + foreach (var (layerId, layer) in _layers) { - return; - } - - Layer layer = GetLayerByIdLocked(RenderLayerId); - - Status acquireStatus = layer.Consumer.AcquireBuffer(out BufferItem item, 0); - - if (acquireStatus == Status.Success) - { - if (_device.VSyncMode == VSyncMode.Unbounded) + if (layer.State == LayerState.NotInitialized || layer.State == LayerState.ManagedClosed) + continue; + + if (_device.System.KernelContext.Processes.TryGetValue(layer.Owner, out var process)) { - if (_swapInterval != 0) + if (process.State == ProcessState.Exiting || process.State == ProcessState.Exited) { - UpdateSwapInterval(0); - _vSyncMode = _device.VSyncMode; + HOSBinderDriverServer.UnregisterBinderObject(layer.ProducerBinderId); + + if (_layers.Remove(layerId)) + { + CloseLayer(layerId, layer); + } + + continue; } } - else if (_device.VSyncMode != _vSyncMode) + + Status acquireStatus = layer.Consumer.AcquireBuffer(out BufferItem item, 0); + + if (acquireStatus == Status.Success) { - UpdateSwapInterval(_device.VSyncMode == VSyncMode.Unbounded ? 0 : item.SwapInterval); - _vSyncMode = _device.VSyncMode; - } - else if (item.SwapInterval != _swapInterval || _device.TargetVSyncInterval != _targetVSyncInterval) - { - UpdateSwapInterval(item.SwapInterval); - } + if (_device.VSyncMode == VSyncMode.Unbounded) + { + if (_swapInterval != 0) + { + UpdateSwapInterval(0); + _vSyncMode = _device.VSyncMode; + } + } + else if (_device.VSyncMode != _vSyncMode) + { + UpdateSwapInterval(_device.VSyncMode == VSyncMode.Unbounded ? 0 : item.SwapInterval); + _vSyncMode = _device.VSyncMode; + } + else if (item.SwapInterval != _swapInterval || _device.TargetVSyncInterval != _targetVSyncInterval) + { + UpdateSwapInterval(item.SwapInterval); + } - PostFrameBuffer(layer, item); - } - else if (acquireStatus != Status.NoBufferAvailaible && acquireStatus != Status.InvalidOperation) - { - throw new InvalidOperationException(); + PostFrameBuffer(layer, item); + } + else if (acquireStatus != Status.NoBufferAvailaible && acquireStatus != Status.InvalidOperation) + { + Logger.Warning?.Print(LogClass.SurfaceFlinger, $"Failed to acquire buffer for layer {layerId} (status: {acquireStatus})"); + continue; + } } } } @@ -413,8 +403,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger ulong bufferOffset = (ulong)item.GraphicBuffer.Object.Buffer.Surfaces[0].Offset; - NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(layer.Owner, nvMapHandle); - + NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(nvMapHandle); + ulong frameBufferAddress = map.Address + bufferOffset; Format format = ConvertColorFormat(item.GraphicBuffer.Object.Buffer.Surfaces[0].ColorFormat); diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBuffer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBuffer.cs index 51daac818..15e9918a8 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBuffer.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/NvGraphicBuffer.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Memory; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger @@ -36,6 +37,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public int PlanesCount; [FieldOffset(0x34)] - public NvGraphicBufferSurfaceArray Surfaces; + public Array3 Surfaces; } } diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs index 3a08cdd78..837d6230e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs @@ -40,7 +40,6 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService ulong pid = context.Device.System.AppletState.AppletResourceUserIds.GetData((int)appletResourceUserId); context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, pid); - context.Device.System.SurfaceFlinger.SetRenderLayer(layerId); context.ResponseData.Write(layerId); diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs index 12ac2cf4a..fa6e77298 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs @@ -1,4 +1,11 @@ +using Ryujinx.Common; using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.SurfaceFlinger; +using Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService.Types.Fbshare; +using Ryujinx.Horizon.Common; +using System; namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService { @@ -7,6 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService #pragma warning disable IDE0052 // Remove unread private member private readonly IApplicationDisplayService _applicationDisplayService; #pragma warning restore IDE0052 + + private KEvent _sharedFramebufferAcquirableEvent; + private int _sharedFramebufferAcquirableEventHandle; public ISystemDisplayService(IApplicationDisplayService applicationDisplayService) { @@ -57,5 +67,138 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService return ResultCode.Success; } + + [CommandCmif(8225)] // 4.0.0+ + // GetSharedBufferMemoryHandleId() + public ResultCode GetSharedBufferMemoryHandleId(ServiceCtx context) + { + context.ResponseData.Write((ulong)context.Device.System.ViServerS.GetSharedBufferNvMapId()); + context.ResponseData.Write(context.Device.System.ViServerS.GetSharedBufferSize()); + + (ulong mapAddress, ulong mapSize) = context.Request.GetBufferType0x22(); + + context.Memory.Write(mapAddress, context.Device.System.ViServerS.GetSharedBufferMap()); + + return ResultCode.Success; + } + + [CommandCmif(8250)] // 4.0.0+ + // OpenSharedLayer(nn::vi::fbshare::SharedLayerHandle, nn::applet::AppletResourceUserId, pid) + public ResultCode OpenSharedLayer(ServiceCtx context) + { + long sharedLayerHandle = context.RequestData.ReadInt64(); + long appletResourceUserId = context.RequestData.ReadInt64(); + + context.Device.System.ViServerS.OpenSharedLayer(sharedLayerHandle); + + return ResultCode.Success; + } + + [CommandCmif(8251)] // 4.0.0+ + // CloseSharedLayer(nn::vi::fbshare::SharedLayerHandle) + public ResultCode CloseSharedLayer(ServiceCtx context) + { + long sharedLayerHandle = context.RequestData.ReadInt64(); + + context.Device.System.ViServerS.CloseSharedLayer(sharedLayerHandle); + + return ResultCode.Success; + } + + [CommandCmif(8252)] // 4.0.0+ + // ConnectSharedLayer(nn::vi::fbshare::SharedLayerHandle) + public ResultCode ConnectSharedLayer(ServiceCtx context) + { + long sharedLayerHandle = context.RequestData.ReadInt64(); + + context.Device.System.ViServerS.ConnectSharedLayer(sharedLayerHandle); + + return ResultCode.Success; + } + + [CommandCmif(8253)] // 4.0.0+ + // DisconnectSharedLayer(nn::vi::fbshare::SharedLayerHandle) + public ResultCode DisconnectSharedLayer(ServiceCtx context) + { + long sharedLayerHandle = context.RequestData.ReadInt64(); + + context.Device.System.ViServerS.DisconnectSharedLayer(sharedLayerHandle); + + return ResultCode.Success; + } + + [CommandCmif(8254)] // 4.0.0+ + // AcquireSharedFrameBuffer(nn::vi::fbshare::SharedLayerHandle) -> (nn::vi::native::NativeSync, nn::vi::fbshare::SharedLayerTextureIndexList, u64) + public ResultCode AcquireSharedFrameBuffer(ServiceCtx context) + { + long sharedLayerHandle = context.RequestData.ReadInt64(); + + int slot = context.Device.System.ViServerS.DequeueFrameBuffer(sharedLayerHandle, out AndroidFence fence); + + var indexList = new SharedLayerTextureIndexList(); + + for (int i = 0; i < indexList.Indices.Length; i++) + { + indexList.Indices[i] = context.Device.System.ViServerS.GetFrameBufferMapIndex(i); + } + + context.ResponseData.WriteStruct(fence); + context.ResponseData.WriteStruct(indexList); + context.ResponseData.Write(0); // Padding + context.ResponseData.Write((ulong)slot); + + return ResultCode.Success; + } + + [CommandCmif(8255)] // 4.0.0+ + // PresentSharedFrameBuffer(nn::vi::native::NativeSync, nn::vi::CropRegion, u32, u32, nn::vi::fbshare::SharedLayerHandle, u64) + public ResultCode PresentSharedFrameBuffer(ServiceCtx context) + { + AndroidFence nativeSync = context.RequestData.ReadStruct(); + Rect cropRegion = context.RequestData.ReadStruct(); + + NativeWindowTransform transform = (NativeWindowTransform)context.RequestData.ReadUInt32(); + int swapInterval = context.RequestData.ReadInt32(); + int padding = context.RequestData.ReadInt32(); + + long sharedLayerHandle = context.RequestData.ReadInt64(); + ulong slot = context.RequestData.ReadUInt64(); + + context.Device.System.ViServerS.QueueFrameBuffer(sharedLayerHandle, (int)slot, cropRegion, transform, swapInterval, nativeSync); + + return ResultCode.Success; + } + + [CommandCmif(8256)] // 4.0.0+ + // GetSharedFrameBufferAcquirableEvent(nn::vi::fbshare::SharedLayerHandle) -> handle + public ResultCode GetSharedFrameBufferAcquirableEvent(ServiceCtx context) + { + if (_sharedFramebufferAcquirableEventHandle == 0) + { + _sharedFramebufferAcquirableEvent = new KEvent(context.Device.System.KernelContext); + _sharedFramebufferAcquirableEvent.WritableEvent.Signal(); + + if (context.Process.HandleTable.GenerateHandle(_sharedFramebufferAcquirableEvent.ReadableEvent, out _sharedFramebufferAcquirableEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_sharedFramebufferAcquirableEventHandle); + + return ResultCode.Success; + } + + [CommandCmif(8258)] // 5.0.0+ + // CancelSharedFrameBuffer(nn::vi::fbshare::SharedLayerHandle, u64) + public ResultCode CancelSharedFrameBuffer(ServiceCtx context) + { + long sharedLayerHandle = context.RequestData.ReadInt64(); + ulong slot = context.RequestData.ReadUInt64(); + + context.Device.System.ViServerS.CancelFrameBuffer(sharedLayerHandle, (int)slot); + + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/Fbshare/SharedLayerTextureIndexList.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/Fbshare/SharedLayerTextureIndexList.cs new file mode 100644 index 000000000..fd1f29c59 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/Fbshare/SharedLayerTextureIndexList.cs @@ -0,0 +1,11 @@ +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService.Types.Fbshare +{ + [StructLayout(LayoutKind.Sequential, Size = 0x10, Pack = 0x4)] + struct SharedLayerTextureIndexList + { + public Array4 Indices; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs index 9fe247e8a..56bcae1ee 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs @@ -248,8 +248,6 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return result; } - context.Device.System.SurfaceFlinger.SetRenderLayer(layerId); - using Parcel parcel = new(0x28, 0x4); parcel.WriteObject(producer, "dispdrv\0"); @@ -285,9 +283,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService // TODO: support multi display. IBinder producer = context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, 0, LayerState.Stray); - - context.Device.System.SurfaceFlinger.SetRenderLayer(layerId); - + using Parcel parcel = new(0x28, 0x4); parcel.WriteObject(producer, "dispdrv\0"); diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/Types/SharedBufferMap.cs b/src/Ryujinx.HLE/HOS/Services/Vi/Types/SharedBufferMap.cs new file mode 100644 index 000000000..cfa8f5a38 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Vi/Types/SharedBufferMap.cs @@ -0,0 +1,19 @@ +using Ryujinx.Common.Memory; + +namespace Ryujinx.HLE.HOS.Services.Vi.Types +{ + struct SharedBufferMap + { + public struct Entry + { + public ulong Offset; + public ulong Size; + public uint Width; + public uint Height; + } + + public int Count; + public int Padding; + public Array16 SharedBuffers; + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/ViServer.cs b/src/Ryujinx.HLE/HOS/Services/Vi/ViServer.cs new file mode 100644 index 000000000..416f72458 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Vi/ViServer.cs @@ -0,0 +1,235 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.Gpu; +using Ryujinx.HLE.HOS.Kernel; +using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; +using Ryujinx.HLE.HOS.Services.SurfaceFlinger; +using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types; +using Ryujinx.HLE.HOS.Services.Vi.Types; +using Ryujinx.Memory; +using System.Runtime.CompilerServices; + +namespace Ryujinx.HLE.HOS.Services +{ + class ViServer : ServerBase + { + private const int TotalFramebuffers = 16; + + private SurfaceFlinger.SurfaceFlinger _surfaceFlinger; + + private readonly uint _fbWidth; + private readonly uint _fbHeight; + private readonly PixelFormat _fbFormat; + private readonly int _fbUsage; + private readonly uint _fbCount; + private uint _fbSlotsRequested; + + private ulong _pid; + private ulong _fbsBaseAddress; + private int _bufferNvMapId; + private SharedBufferMap _bufferMap; + private long _sharedLayerId; + + public ViServer(KernelContext context, string name) : base(context, name) + { + _fbWidth = 1280; + _fbHeight = 720; + _fbFormat = PixelFormat.Rgba8888; + _fbUsage = 0x100 | 0x200 | 0x800; + _fbCount = 4; + } + + protected override ulong CalculateRequiredHeapSize() + { + return GetSharedBufferSize(); + } + + protected override void CustomInit(KernelContext context, ulong pid, ulong heapAddress) + { + _pid = pid; + _fbsBaseAddress = heapAddress; + + context.Device.Gpu.RegisterProcess(pid, KernelStatic.GetCurrentProcess().CpuMemory); + + ulong bufferSize = CalculateFramebufferSize(); + ulong totalSize = bufferSize * TotalFramebuffers; + + KernelStatic.GetCurrentProcess().CpuMemory.Fill(heapAddress, totalSize, 0xff); + + int mapId = NvMapDeviceFile.CreateMap(pid, heapAddress, (uint)totalSize); + + _bufferNvMapId = mapId; + _bufferMap.Count = TotalFramebuffers; + + ulong offset = 0; + + for (int i = 0; i < TotalFramebuffers; i++) + { + _bufferMap.SharedBuffers[i].Offset = offset; + _bufferMap.SharedBuffers[i].Size = bufferSize; + _bufferMap.SharedBuffers[i].Width = _fbWidth; + _bufferMap.SharedBuffers[i].Height = _fbHeight; + + offset += bufferSize; + } + + _surfaceFlinger = context.Device.System.SurfaceFlinger; + _surfaceFlinger.CreateLayer(out _sharedLayerId, pid); + } + + public void OpenSharedLayer(long layerId) + { + _surfaceFlinger.OpenLayer(_pid, layerId, out _); + } + + public void CloseSharedLayer(long layerId) + { + _surfaceFlinger.CloseLayer(layerId); + } + + public void ConnectSharedLayer(long layerId) + { + IGraphicBufferProducer producer = _surfaceFlinger.GetProducerByLayerId(layerId); + + producer.Connect(null, NativeWindowApi.NVN, false, out IGraphicBufferProducer.QueueBufferOutput output); + + GraphicBuffer graphicBuffer = new GraphicBuffer(); + + int gobHeightLog2 = 4; + int blockHeight = 8 * (1 << gobHeightLog2); + uint widthAlignedBytes = BitUtils.AlignUp(_fbWidth * 4, 64u); + uint widthAligned = widthAlignedBytes / 4; + uint heightAligned = BitUtils.AlignUp(_fbHeight, (uint)blockHeight); + uint totalSize = widthAlignedBytes * heightAligned; + + graphicBuffer.Header.Magic = 0x47424652; + graphicBuffer.Header.Width = (int)_fbWidth; + graphicBuffer.Header.Height = (int)_fbHeight; + graphicBuffer.Header.Stride = (int)widthAligned; + graphicBuffer.Header.Format = _fbFormat; + graphicBuffer.Header.Usage = _fbUsage; + graphicBuffer.Header.IntsCount = (Unsafe.SizeOf() - Unsafe.SizeOf()) / sizeof(int); + graphicBuffer.Buffer.NvMapId = _bufferNvMapId; + graphicBuffer.Buffer.Magic = unchecked((int)0xDAFFCAFF); + graphicBuffer.Buffer.Pid = 42; + graphicBuffer.Buffer.Usage = _fbUsage; + graphicBuffer.Buffer.PixelFormat = (int)_fbFormat; + graphicBuffer.Buffer.ExternalPixelFormat = (int)_fbFormat; + graphicBuffer.Buffer.Stride = (int)widthAligned; + graphicBuffer.Buffer.FrameBufferSize = (int)totalSize; + graphicBuffer.Buffer.PlanesCount = 1; + graphicBuffer.Buffer.Surfaces[0].Width = _fbWidth; + graphicBuffer.Buffer.Surfaces[0].Height = _fbHeight; + graphicBuffer.Buffer.Surfaces[0].ColorFormat = ColorFormat.A8B8G8R8; + graphicBuffer.Buffer.Surfaces[0].Layout = 3; // Block linear + graphicBuffer.Buffer.Surfaces[0].Pitch = (int)widthAlignedBytes; + graphicBuffer.Buffer.Surfaces[0].Kind = 0xfe; // Generic 16Bx2 + graphicBuffer.Buffer.Surfaces[0].BlockHeightLog2 = gobHeightLog2; + graphicBuffer.Buffer.Surfaces[0].Size = (int)totalSize; + + for (int slot = 0; slot < _fbCount; slot++) + { + graphicBuffer.Buffer.Surfaces[0].Offset = slot * (int)totalSize; + + producer.SetPreallocatedBuffer(slot, new AndroidStrongPointer(graphicBuffer)); + } + + _fbSlotsRequested = 0; + } + + public void DisconnectSharedLayer(long layerId) + { + IGraphicBufferProducer producer = _surfaceFlinger.GetProducerByLayerId(layerId); + + producer.Disconnect(NativeWindowApi.NVN); + } + + public int DequeueFrameBuffer(long layerId, out AndroidFence fence) + { + IGraphicBufferProducer producer = _surfaceFlinger.GetProducerByLayerId(layerId); + + Status status = producer.DequeueBuffer(out int slot, out fence, false, _fbWidth, _fbHeight, _fbFormat, (uint)_fbUsage); + + if (status == Status.Success) + { + if ((_fbSlotsRequested & (1u << slot)) == 0) + { + status = producer.RequestBuffer(slot, out _); + + if (status != Status.Success) + { + producer.CancelBuffer(slot, ref fence); + } + + _fbSlotsRequested |= 1u << slot; + } + } + + return slot; + } + + public void QueueFrameBuffer(long layerId, int slot, Rect crop, NativeWindowTransform transform, int swapInterval, AndroidFence fence) + { + IGraphicBufferProducer producer = _surfaceFlinger.GetProducerByLayerId(layerId); + + IGraphicBufferProducer.QueueBufferInput input = new(); + + input.Crop = crop; + input.Transform = transform; + input.SwapInterval = swapInterval; + input.Fence = fence; + + Status status = producer.QueueBuffer(slot, ref input, out _); + } + + public void CancelFrameBuffer(long layerId, int slot) + { + IGraphicBufferProducer producer = _surfaceFlinger.GetProducerByLayerId(layerId); + AndroidFence fence = default; + + producer.CancelBuffer(slot, ref fence); + } + + public int GetFrameBufferMapIndex(int index) + { + return (uint)index < _fbCount ? index : 0; + } + + public int GetSharedBufferNvMapId() + { + return _bufferNvMapId; + } + + public ulong GetSharedBufferSize() + { + return CalculateFramebufferSize() * TotalFramebuffers; + } + + public long GetSharedLayerId() + { + return _sharedLayerId; + } + + private ulong CalculateFramebufferSize() + { + // Each GOB dimension is 512 bytes x 8 lines. + // Assume 16 GOBs, for a total of 16 x 8 = 128 lines. + return BitUtils.AlignUp(_fbWidth * 4, 512u) * BitUtils.AlignUp(_fbHeight, 128u); + } + + public SharedBufferMap GetSharedBufferMap() + { + return _bufferMap; + } + + public int GetApplicationLastPresentedFrameHandle(GpuContext gpuContext) + { + TextureData texture = gpuContext.Window.GetLastPresentedData(); + IVirtualMemoryManagerTracked selfAs = KernelStatic.GetProcessByPid(_pid).CpuMemory; + int fbIndex = (int)_fbCount; // Place it after all our frame buffers. + + selfAs.Write(_fbsBaseAddress + _bufferMap.SharedBuffers[fbIndex].Offset, texture.Data); + + return fbIndex; + } + } +} diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index 86b04061e..fed18566d 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -1,10 +1,13 @@ using LibHac.Common; +using LibHac.Ncm; using LibHac.Ns; +using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Audio.Backends.CompatLayer; using Ryujinx.Audio.Integration; using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Graphics.Gpu; +using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Apm; @@ -158,5 +161,18 @@ namespace Ryujinx.HLE Shared = null; } } + + public bool LoadSystemProgramId(ulong programId) + { + string contentPath = System.ContentManager.GetInstalledContentPath(programId, StorageId.BuiltInSystem, NcaContentType.Program); + string filePath = VirtualFileSystem.SwitchPathToSystemPath(contentPath); + + if (string.IsNullOrWhiteSpace(filePath)) + { + throw new InvalidSystemResourceException("Specified title ID is not installed on the system."); + } + + return Processes.LoadNca(filePath); + } } } diff --git a/src/Ryujinx.Memory/AddressSpaceManager.cs b/src/Ryujinx.Memory/AddressSpaceManager.cs index f9d514b55..94b27f1b9 100644 --- a/src/Ryujinx.Memory/AddressSpaceManager.cs +++ b/src/Ryujinx.Memory/AddressSpaceManager.cs @@ -1,4 +1,5 @@ using Ryujinx.Memory.Range; +using Ryujinx.Memory.Tracking; using System; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -9,7 +10,7 @@ namespace Ryujinx.Memory /// Represents a address space manager. /// Supports virtual memory region mapping, address translation and read/write access to mapped regions. /// - public sealed class AddressSpaceManager : VirtualMemoryManagerBase, IVirtualMemoryManager + public sealed class AddressSpaceManager : VirtualMemoryManagerBase, IVirtualMemoryManagerTracked { /// public bool UsesPrivateAllocations => false; @@ -21,6 +22,8 @@ namespace Ryujinx.Memory private readonly MemoryBlock _backingMemory; private readonly PageTable _pageTable; + private readonly MemoryTracking _tracking; + private bool _writeTracked; protected override ulong AddressSpaceSize { get; } @@ -44,6 +47,7 @@ namespace Ryujinx.Memory AddressSpaceSize = asSize; _backingMemory = backingMemory; _pageTable = new PageTable(); + _tracking = new MemoryTracking(this, 0x1000); } /// @@ -227,7 +231,7 @@ namespace Ryujinx.Memory /// public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection, bool guest = false) { - throw new NotImplementedException(); + _writeTracked = true; } protected unsafe override Memory GetPhysicalAddressMemory(nuint pa, int size) @@ -240,5 +244,29 @@ namespace Ryujinx.Memory protected override nuint TranslateVirtualAddressUnchecked(ulong va) => GetHostAddress(va); + + /// + public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null) + { + if (_writeTracked) + { + _tracking.VirtualMemoryEvent(va, size, write, precise); + } + } + /// + public RegionHandle BeginTracking(ulong address, ulong size, int id, RegionFlags flags = RegionFlags.None) + { + return _tracking.BeginTracking(address, size, id, flags); + } + /// + public MultiRegionHandle BeginGranularTracking(ulong address, ulong size, IEnumerable handles, ulong granularity, int id, RegionFlags flags = RegionFlags.None) + { + return _tracking.BeginGranularTracking(address, size, handles, granularity, id, flags); + } + /// + public SmartMultiRegionHandle BeginSmartGranularTracking(ulong address, ulong size, ulong granularity, int id) + { + return _tracking.BeginSmartGranularTracking(address, size, granularity, id); + } } } diff --git a/src/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs b/src/Ryujinx.Memory/IVirtualMemoryManagerTracked.cs similarity index 99% rename from src/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs rename to src/Ryujinx.Memory/IVirtualMemoryManagerTracked.cs index e8d11ede5..1ee09641b 100644 --- a/src/Ryujinx.Cpu/IVirtualMemoryManagerTracked.cs +++ b/src/Ryujinx.Memory/IVirtualMemoryManagerTracked.cs @@ -3,7 +3,7 @@ using Ryujinx.Memory.Tracking; using System; using System.Collections.Generic; -namespace Ryujinx.Cpu +namespace Ryujinx.Memory { public interface IVirtualMemoryManagerTracked : IVirtualMemoryManager { diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 4147b8a5e..f96dfcff6 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -41,6 +41,7 @@ using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.HLE.Loaders.Processes; using Ryujinx.Input; using Ryujinx.Input.HLE; using SkiaSharp; @@ -672,6 +673,7 @@ namespace Ryujinx.Ava DiscordIntegrationModule.GuestAppStartedAt = Timestamps.Now; InitEmulatedSwitch(); + Device.LoadSystemProgramId(0x010000000000100C); MainWindow.UpdateGraphicsConfig(); SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();