Initial support for separate GPU address spaces (#2394)
* Make GPU memory manager a member of GPU channel * Move physical memory instance to the memory manager, and the caches to the physical memory * PR feedback
This commit is contained in:
parent
8cc872fb60
commit
fbb4019ed5
@ -16,11 +16,13 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
/// <param name="argument">Method call argument</param>
|
/// <param name="argument">Method call argument</param>
|
||||||
public void Dispatch(GpuState state, int argument)
|
public void Dispatch(GpuState state, int argument)
|
||||||
{
|
{
|
||||||
FlushUboDirty();
|
var memoryManager = state.Channel.MemoryManager;
|
||||||
|
|
||||||
|
FlushUboDirty(memoryManager);
|
||||||
|
|
||||||
uint qmdAddress = (uint)state.Get<int>(MethodOffset.DispatchParamsAddress);
|
uint qmdAddress = (uint)state.Get<int>(MethodOffset.DispatchParamsAddress);
|
||||||
|
|
||||||
var qmd = _context.MemoryManager.Read<ComputeQmd>((ulong)qmdAddress << 8);
|
var qmd = state.Channel.MemoryManager.Read<ComputeQmd>((ulong)qmdAddress << 8);
|
||||||
|
|
||||||
GpuVa shaderBaseAddress = state.Get<GpuVa>(MethodOffset.ShaderBaseAddress);
|
GpuVa shaderBaseAddress = state.Get<GpuVa>(MethodOffset.ShaderBaseAddress);
|
||||||
|
|
||||||
@ -43,7 +45,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
state.Channel.BufferManager.SetComputeUniformBuffer(index, gpuVa, size);
|
state.Channel.BufferManager.SetComputeUniformBuffer(index, gpuVa, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderBundle cs = ShaderCache.GetComputeShader(
|
ShaderBundle cs = memoryManager.Physical.ShaderCache.GetComputeShader(
|
||||||
state,
|
state,
|
||||||
shaderGpuVa,
|
shaderGpuVa,
|
||||||
qmd.CtaThreadDimension0,
|
qmd.CtaThreadDimension0,
|
||||||
@ -82,7 +84,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
|
|
||||||
cbDescAddress += (ulong)cbDescOffset;
|
cbDescAddress += (ulong)cbDescOffset;
|
||||||
|
|
||||||
SbDescriptor cbDescriptor = _context.PhysicalMemory.Read<SbDescriptor>(cbDescAddress);
|
SbDescriptor cbDescriptor = state.Channel.MemoryManager.Physical.Read<SbDescriptor>(cbDescAddress);
|
||||||
|
|
||||||
state.Channel.BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size);
|
state.Channel.BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size);
|
||||||
}
|
}
|
||||||
@ -97,7 +99,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
|
|
||||||
sbDescAddress += (ulong)sbDescOffset;
|
sbDescAddress += (ulong)sbDescOffset;
|
||||||
|
|
||||||
SbDescriptor sbDescriptor = _context.PhysicalMemory.Read<SbDescriptor>(sbDescAddress);
|
SbDescriptor sbDescriptor = state.Channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
|
||||||
|
|
||||||
state.Channel.BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags);
|
state.Channel.BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags);
|
||||||
}
|
}
|
||||||
|
@ -79,13 +79,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
|||||||
// TODO: Acquire operations (Wait), interrupts for invalid combinations.
|
// TODO: Acquire operations (Wait), interrupts for invalid combinations.
|
||||||
if (operation == SemaphoredOperation.Release)
|
if (operation == SemaphoredOperation.Release)
|
||||||
{
|
{
|
||||||
_context.MemoryManager.Write(address, value);
|
_parent.MemoryManager.Write(address, value);
|
||||||
}
|
}
|
||||||
else if (operation == SemaphoredOperation.Reduction)
|
else if (operation == SemaphoredOperation.Reduction)
|
||||||
{
|
{
|
||||||
bool signed = _state.State.SemaphoredFormat == SemaphoredFormat.Signed;
|
bool signed = _state.State.SemaphoredFormat == SemaphoredFormat.Signed;
|
||||||
|
|
||||||
int mem = _context.MemoryManager.Read<int>(address);
|
int mem = _parent.MemoryManager.Read<int>(address);
|
||||||
|
|
||||||
switch (_state.State.SemaphoredReduction)
|
switch (_state.State.SemaphoredReduction)
|
||||||
{
|
{
|
||||||
@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.MemoryManager.Write(address, value);
|
_parent.MemoryManager.Write(address, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
@ -53,11 +54,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fetch the command buffer.
|
/// Fetch the command buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Fetch(GpuContext context)
|
public void Fetch(MemoryManager memoryManager)
|
||||||
{
|
{
|
||||||
if (Words == null)
|
if (Words == null)
|
||||||
{
|
{
|
||||||
Words = MemoryMarshal.Cast<byte, int>(context.MemoryManager.GetSpan(EntryAddress, (int)EntryCount * 4, true)).ToArray();
|
Words = MemoryMarshal.Cast<byte, int>(memoryManager.GetSpan(EntryAddress, (int)EntryCount * 4, true)).ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,7 +156,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
|||||||
|
|
||||||
if (beforeBarrier && commandBuffer.Type == CommandBufferType.Prefetch)
|
if (beforeBarrier && commandBuffer.Type == CommandBufferType.Prefetch)
|
||||||
{
|
{
|
||||||
commandBuffer.Fetch(_context);
|
commandBuffer.Fetch(processor.MemoryManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commandBuffer.Type == CommandBufferType.NoPrefetch)
|
if (commandBuffer.Type == CommandBufferType.NoPrefetch)
|
||||||
@ -182,13 +183,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
|||||||
public void DispatchCalls()
|
public void DispatchCalls()
|
||||||
{
|
{
|
||||||
// Use this opportunity to also dispose any pending channels that were closed.
|
// Use this opportunity to also dispose any pending channels that were closed.
|
||||||
_context.DisposePendingChannels();
|
_context.RunDeferredActions();
|
||||||
|
|
||||||
// Process command buffers.
|
// Process command buffers.
|
||||||
while (_ibEnable && !_interrupt && _commandBufferQueue.TryDequeue(out CommandBuffer entry))
|
while (_ibEnable && !_interrupt && _commandBufferQueue.TryDequeue(out CommandBuffer entry))
|
||||||
{
|
{
|
||||||
_currentCommandBuffer = entry;
|
_currentCommandBuffer = entry;
|
||||||
_currentCommandBuffer.Fetch(_context);
|
_currentCommandBuffer.Fetch(entry.Processor.MemoryManager);
|
||||||
|
|
||||||
// If we are changing the current channel,
|
// If we are changing the current channel,
|
||||||
// we need to force all the host state to be updated.
|
// we need to force all the host state to be updated.
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Graphics.Gpu.State;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
@ -13,6 +14,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
|||||||
private const int MacroIndexMask = MacrosCount - 1;
|
private const int MacroIndexMask = MacrosCount - 1;
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
|
private readonly GpuChannel _channel;
|
||||||
|
|
||||||
|
public MemoryManager MemoryManager => _channel.MemoryManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Internal GPFIFO state.
|
/// Internal GPFIFO state.
|
||||||
@ -39,6 +43,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
|||||||
public GPFifoProcessor(GpuContext context, GpuChannel channel)
|
public GPFifoProcessor(GpuContext context, GpuChannel channel)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
|
_channel = channel;
|
||||||
|
|
||||||
_fifoClass = new GPFifoClass(context, this);
|
_fifoClass = new GPFifoClass(context, this);
|
||||||
_subChannels = new GpuState[8];
|
_subChannels = new GpuState[8];
|
||||||
|
@ -40,10 +40,10 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
_buffer = new int[count];
|
_buffer = new int[count];
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong dstBaseAddress = _context.MemoryManager.Translate(_params.DstAddress.Pack());
|
ulong dstBaseAddress = state.Channel.MemoryManager.Translate(_params.DstAddress.Pack());
|
||||||
|
|
||||||
// Trigger read tracking, to flush any managed resources in the destination region.
|
// Trigger read tracking, to flush any managed resources in the destination region.
|
||||||
_context.PhysicalMemory.GetSpan(dstBaseAddress, _size, true);
|
state.Channel.MemoryManager.Physical.GetSpan(dstBaseAddress, _size, true);
|
||||||
|
|
||||||
_finished = false;
|
_finished = false;
|
||||||
}
|
}
|
||||||
@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
|
|
||||||
if (_offset * 4 >= _size)
|
if (_offset * 4 >= _size)
|
||||||
{
|
{
|
||||||
FinishTransfer();
|
FinishTransfer(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,15 +69,16 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs actual copy of the inline data after the transfer is finished.
|
/// Performs actual copy of the inline data after the transfer is finished.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void FinishTransfer()
|
/// <param name="state">Current GPU state</param>
|
||||||
|
private void FinishTransfer(GpuState state)
|
||||||
{
|
{
|
||||||
Span<byte> data = MemoryMarshal.Cast<int, byte>(_buffer).Slice(0, _size);
|
Span<byte> data = MemoryMarshal.Cast<int, byte>(_buffer).Slice(0, _size);
|
||||||
|
|
||||||
if (_isLinear && _params.LineCount == 1)
|
if (_isLinear && _params.LineCount == 1)
|
||||||
{
|
{
|
||||||
ulong address = _context.MemoryManager.Translate(_params.DstAddress.Pack());
|
ulong address = state.Channel.MemoryManager.Translate(_params.DstAddress.Pack());
|
||||||
|
|
||||||
_context.PhysicalMemory.Write(address, data);
|
state.Channel.MemoryManager.Physical.Write(address, data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -91,7 +92,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
|
|
||||||
int srcOffset = 0;
|
int srcOffset = 0;
|
||||||
|
|
||||||
ulong dstBaseAddress = _context.MemoryManager.Translate(_params.DstAddress.Pack());
|
ulong dstBaseAddress = state.Channel.MemoryManager.Translate(_params.DstAddress.Pack());
|
||||||
|
|
||||||
for (int y = _params.DstY; y < _params.DstY + _params.LineCount; y++)
|
for (int y = _params.DstY; y < _params.DstY + _params.LineCount; y++)
|
||||||
{
|
{
|
||||||
@ -109,7 +110,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
|
|
||||||
Span<byte> pixel = data.Slice(srcOffset, 16);
|
Span<byte> pixel = data.Slice(srcOffset, 16);
|
||||||
|
|
||||||
_context.PhysicalMemory.Write(dstAddress, pixel);
|
state.Channel.MemoryManager.Physical.Write(dstAddress, pixel);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; x < x2; x++, srcOffset++)
|
for (; x < x2; x++, srcOffset++)
|
||||||
@ -120,7 +121,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
|
|
||||||
Span<byte> pixel = data.Slice(srcOffset, 1);
|
Span<byte> pixel = data.Slice(srcOffset, 1);
|
||||||
|
|
||||||
_context.PhysicalMemory.Write(dstAddress, pixel);
|
state.Channel.MemoryManager.Physical.Write(dstAddress, pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Engine
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
@ -23,11 +24,11 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
case Condition.Never:
|
case Condition.Never:
|
||||||
return ConditionalRenderEnabled.False;
|
return ConditionalRenderEnabled.False;
|
||||||
case Condition.ResultNonZero:
|
case Condition.ResultNonZero:
|
||||||
return CounterNonZero(condState.Address.Pack());
|
return CounterNonZero(state, condState.Address.Pack());
|
||||||
case Condition.Equal:
|
case Condition.Equal:
|
||||||
return CounterCompare(condState.Address.Pack(), true);
|
return CounterCompare(state, condState.Address.Pack(), true);
|
||||||
case Condition.NotEqual:
|
case Condition.NotEqual:
|
||||||
return CounterCompare(condState.Address.Pack(), false);
|
return CounterCompare(state, condState.Address.Pack(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Warning?.Print(LogClass.Gpu, $"Invalid conditional render condition \"{condState.Condition}\".");
|
Logger.Warning?.Print(LogClass.Gpu, $"Invalid conditional render condition \"{condState.Condition}\".");
|
||||||
@ -38,11 +39,12 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the counter value at a given GPU memory address is non-zero.
|
/// Checks if the counter value at a given GPU memory address is non-zero.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="state">GPU state</param>
|
||||||
/// <param name="gpuVa">GPU virtual address of the counter value</param>
|
/// <param name="gpuVa">GPU virtual address of the counter value</param>
|
||||||
/// <returns>True if the value is not zero, false otherwise. Returns host if handling with host conditional rendering</returns>
|
/// <returns>True if the value is not zero, false otherwise. Returns host if handling with host conditional rendering</returns>
|
||||||
private ConditionalRenderEnabled CounterNonZero(ulong gpuVa)
|
private ConditionalRenderEnabled CounterNonZero(GpuState state, ulong gpuVa)
|
||||||
{
|
{
|
||||||
ICounterEvent evt = _counterCache.FindEvent(gpuVa);
|
ICounterEvent evt = state.Channel.MemoryManager.CounterCache.FindEvent(gpuVa);
|
||||||
|
|
||||||
if (evt == null)
|
if (evt == null)
|
||||||
{
|
{
|
||||||
@ -56,30 +58,31 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
evt.Flush();
|
evt.Flush();
|
||||||
return (_context.MemoryManager.Read<ulong>(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
|
return (state.Channel.MemoryManager.Read<ulong>(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the counter at a given GPU memory address passes a specified equality comparison.
|
/// Checks if the counter at a given GPU memory address passes a specified equality comparison.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="state">GPU state</param>
|
||||||
/// <param name="gpuVa">GPU virtual address</param>
|
/// <param name="gpuVa">GPU virtual address</param>
|
||||||
/// <param name="isEqual">True to check if the values are equal, false to check if they are not equal</param>
|
/// <param name="isEqual">True to check if the values are equal, false to check if they are not equal</param>
|
||||||
/// <returns>True if the condition is met, false otherwise. Returns host if handling with host conditional rendering</returns>
|
/// <returns>True if the condition is met, false otherwise. Returns host if handling with host conditional rendering</returns>
|
||||||
private ConditionalRenderEnabled CounterCompare(ulong gpuVa, bool isEqual)
|
private ConditionalRenderEnabled CounterCompare(GpuState state, ulong gpuVa, bool isEqual)
|
||||||
{
|
{
|
||||||
ICounterEvent evt = FindEvent(gpuVa);
|
ICounterEvent evt = FindEvent(state.Channel.MemoryManager.CounterCache, gpuVa);
|
||||||
ICounterEvent evt2 = FindEvent(gpuVa + 16);
|
ICounterEvent evt2 = FindEvent(state.Channel.MemoryManager.CounterCache, gpuVa + 16);
|
||||||
|
|
||||||
bool useHost;
|
bool useHost;
|
||||||
|
|
||||||
if (evt != null && evt2 == null)
|
if (evt != null && evt2 == null)
|
||||||
{
|
{
|
||||||
useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, _context.MemoryManager.Read<ulong>(gpuVa + 16), isEqual);
|
useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, state.Channel.MemoryManager.Read<ulong>(gpuVa + 16), isEqual);
|
||||||
}
|
}
|
||||||
else if (evt == null && evt2 != null)
|
else if (evt == null && evt2 != null)
|
||||||
{
|
{
|
||||||
useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt2, _context.MemoryManager.Read<ulong>(gpuVa), isEqual);
|
useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt2, state.Channel.MemoryManager.Read<ulong>(gpuVa), isEqual);
|
||||||
}
|
}
|
||||||
else if (evt != null && evt2 != null)
|
else if (evt != null && evt2 != null)
|
||||||
{
|
{
|
||||||
@ -99,8 +102,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
evt?.Flush();
|
evt?.Flush();
|
||||||
evt2?.Flush();
|
evt2?.Flush();
|
||||||
|
|
||||||
ulong x = _context.MemoryManager.Read<ulong>(gpuVa);
|
ulong x = state.Channel.MemoryManager.Read<ulong>(gpuVa);
|
||||||
ulong y = _context.MemoryManager.Read<ulong>(gpuVa + 16);
|
ulong y = state.Channel.MemoryManager.Read<ulong>(gpuVa + 16);
|
||||||
|
|
||||||
return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
|
return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False;
|
||||||
}
|
}
|
||||||
@ -110,11 +113,12 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
/// Tries to find a counter that is supposed to be written at the specified address,
|
/// Tries to find a counter that is supposed to be written at the specified address,
|
||||||
/// returning the related event.
|
/// returning the related event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="counterCache">GPU counter cache to search on</param>
|
||||||
/// <param name="gpuVa">GPU virtual address where the counter is supposed to be written</param>
|
/// <param name="gpuVa">GPU virtual address where the counter is supposed to be written</param>
|
||||||
/// <returns>The counter event, or null if not present</returns>
|
/// <returns>The counter event, or null if not present</returns>
|
||||||
private ICounterEvent FindEvent(ulong gpuVa)
|
private static ICounterEvent FindEvent(CounterCache counterCache, ulong gpuVa)
|
||||||
{
|
{
|
||||||
return _counterCache.FindEvent(gpuVa);
|
return counterCache.FindEvent(gpuVa);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FlushUboDirty();
|
FlushUboDirty(state.Channel.MemoryManager);
|
||||||
|
|
||||||
if (copy2D)
|
if (copy2D)
|
||||||
{
|
{
|
||||||
@ -98,21 +98,27 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
dst.MemoryLayout.UnpackGobBlocksInZ(),
|
dst.MemoryLayout.UnpackGobBlocksInZ(),
|
||||||
dstBpp);
|
dstBpp);
|
||||||
|
|
||||||
ulong srcBaseAddress = _context.MemoryManager.Translate(cbp.SrcAddress.Pack());
|
ulong srcBaseAddress = state.Channel.MemoryManager.Translate(cbp.SrcAddress.Pack());
|
||||||
ulong dstBaseAddress = _context.MemoryManager.Translate(cbp.DstAddress.Pack());
|
ulong dstBaseAddress = state.Channel.MemoryManager.Translate(cbp.DstAddress.Pack());
|
||||||
|
|
||||||
(int srcBaseOffset, int srcSize) = srcCalculator.GetRectangleRange(src.RegionX, src.RegionY, cbp.XCount, cbp.YCount);
|
(int srcBaseOffset, int srcSize) = srcCalculator.GetRectangleRange(src.RegionX, src.RegionY, cbp.XCount, cbp.YCount);
|
||||||
(int dstBaseOffset, int dstSize) = dstCalculator.GetRectangleRange(dst.RegionX, dst.RegionY, cbp.XCount, cbp.YCount);
|
(int dstBaseOffset, int dstSize) = dstCalculator.GetRectangleRange(dst.RegionX, dst.RegionY, cbp.XCount, cbp.YCount);
|
||||||
|
|
||||||
ReadOnlySpan<byte> srcSpan = _context.PhysicalMemory.GetSpan(srcBaseAddress + (ulong)srcBaseOffset, srcSize, true);
|
ReadOnlySpan<byte> srcSpan = state.Channel.MemoryManager.Physical.GetSpan(srcBaseAddress + (ulong)srcBaseOffset, srcSize, true);
|
||||||
Span<byte> dstSpan = _context.PhysicalMemory.GetSpan(dstBaseAddress + (ulong)dstBaseOffset, dstSize).ToArray();
|
Span<byte> dstSpan = state.Channel.MemoryManager.Physical.GetSpan(dstBaseAddress + (ulong)dstBaseOffset, dstSize).ToArray();
|
||||||
|
|
||||||
bool completeSource = IsTextureCopyComplete(cbp, src, srcLinear, srcBpp, cbp.SrcStride);
|
bool completeSource = IsTextureCopyComplete(cbp, src, srcLinear, srcBpp, cbp.SrcStride);
|
||||||
bool completeDest = IsTextureCopyComplete(cbp, dst, dstLinear, dstBpp, cbp.DstStride);
|
bool completeDest = IsTextureCopyComplete(cbp, dst, dstLinear, dstBpp, cbp.DstStride);
|
||||||
|
|
||||||
if (completeSource && completeDest)
|
if (completeSource && completeDest)
|
||||||
{
|
{
|
||||||
Image.Texture target = TextureCache.FindTexture(dst, cbp, swizzle, dstLinear);
|
Image.Texture target = state.Channel.MemoryManager.Physical.TextureCache.FindTexture(
|
||||||
|
state.Channel.MemoryManager,
|
||||||
|
dst,
|
||||||
|
cbp,
|
||||||
|
swizzle,
|
||||||
|
dstLinear);
|
||||||
|
|
||||||
if (target != null)
|
if (target != null)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> data;
|
ReadOnlySpan<byte> data;
|
||||||
@ -154,7 +160,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
{
|
{
|
||||||
srcSpan.CopyTo(dstSpan); // No layout conversion has to be performed, just copy the data entirely.
|
srcSpan.CopyTo(dstSpan); // No layout conversion has to be performed, just copy the data entirely.
|
||||||
|
|
||||||
_context.PhysicalMemory.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan);
|
state.Channel.MemoryManager.Physical.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -195,7 +201,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
_ => throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format.")
|
_ => throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format.")
|
||||||
};
|
};
|
||||||
|
|
||||||
_context.PhysicalMemory.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan);
|
state.Channel.MemoryManager.Physical.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -209,13 +215,21 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
swizzle.UnpackComponentSize() == 4)
|
swizzle.UnpackComponentSize() == 4)
|
||||||
{
|
{
|
||||||
// Fast path for clears when remap is enabled.
|
// Fast path for clears when remap is enabled.
|
||||||
BufferCache.ClearBuffer(cbp.DstAddress, (uint)size * 4, state.Get<uint>(MethodOffset.CopyBufferConstA));
|
state.Channel.MemoryManager.Physical.BufferCache.ClearBuffer(
|
||||||
|
state.Channel.MemoryManager,
|
||||||
|
cbp.DstAddress,
|
||||||
|
(uint)size * 4,
|
||||||
|
state.Get<uint>(MethodOffset.CopyBufferConstA));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO: Implement remap functionality.
|
// TODO: Implement remap functionality.
|
||||||
// Buffer to buffer copy.
|
// Buffer to buffer copy.
|
||||||
BufferCache.CopyBuffer(cbp.SrcAddress, cbp.DstAddress, (uint)size);
|
state.Channel.MemoryManager.Physical.BufferCache.CopyBuffer(
|
||||||
|
state.Channel.MemoryManager,
|
||||||
|
cbp.SrcAddress,
|
||||||
|
cbp.DstAddress,
|
||||||
|
(uint)size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
/// <param name="argument">Method call argument</param>
|
/// <param name="argument">Method call argument</param>
|
||||||
private void CopyTexture(GpuState state, int argument)
|
private void CopyTexture(GpuState state, int argument)
|
||||||
{
|
{
|
||||||
|
var memoryManager = state.Channel.MemoryManager;
|
||||||
|
|
||||||
var dstCopyTexture = state.Get<CopyTexture>(MethodOffset.CopyDstTexture);
|
var dstCopyTexture = state.Get<CopyTexture>(MethodOffset.CopyDstTexture);
|
||||||
var srcCopyTexture = state.Get<CopyTexture>(MethodOffset.CopySrcTexture);
|
var srcCopyTexture = state.Get<CopyTexture>(MethodOffset.CopySrcTexture);
|
||||||
|
|
||||||
@ -80,7 +82,13 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
srcX1 = 0;
|
srcX1 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture srcTexture = TextureCache.FindOrCreateTexture(srcCopyTexture, offset, srcCopyTextureFormat, true, srcHint);
|
Texture srcTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
||||||
|
memoryManager,
|
||||||
|
srcCopyTexture,
|
||||||
|
offset,
|
||||||
|
srcCopyTextureFormat,
|
||||||
|
true,
|
||||||
|
srcHint);
|
||||||
|
|
||||||
if (srcTexture == null)
|
if (srcTexture == null)
|
||||||
{
|
{
|
||||||
@ -101,7 +109,13 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
dstCopyTextureFormat = dstCopyTexture.Format.Convert();
|
dstCopyTextureFormat = dstCopyTexture.Format.Convert();
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture dstTexture = TextureCache.FindOrCreateTexture(dstCopyTexture, 0, dstCopyTextureFormat, srcTexture.ScaleMode == TextureScaleMode.Scaled, dstHint);
|
Texture dstTexture = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
||||||
|
memoryManager,
|
||||||
|
dstCopyTexture,
|
||||||
|
0,
|
||||||
|
dstCopyTextureFormat,
|
||||||
|
srcTexture.ScaleMode == TextureScaleMode.Scaled,
|
||||||
|
dstHint);
|
||||||
|
|
||||||
if (dstTexture == null)
|
if (dstTexture == null)
|
||||||
{
|
{
|
||||||
|
@ -12,8 +12,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
private const int NsToTicksFractionNumerator = 384;
|
private const int NsToTicksFractionNumerator = 384;
|
||||||
private const int NsToTicksFractionDenominator = 625;
|
private const int NsToTicksFractionDenominator = 625;
|
||||||
|
|
||||||
private readonly CounterCache _counterCache = new CounterCache();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes a GPU counter to guest memory.
|
/// Writes a GPU counter to guest memory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -39,7 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
{
|
{
|
||||||
var rs = state.Get<SemaphoreState>(MethodOffset.ReportState);
|
var rs = state.Get<SemaphoreState>(MethodOffset.ReportState);
|
||||||
|
|
||||||
_context.MemoryManager.Write(rs.Address.Pack(), rs.Payload);
|
state.Channel.MemoryManager.Write(rs.Address.Pack(), rs.Payload);
|
||||||
|
|
||||||
_context.AdvanceSequence();
|
_context.AdvanceSequence();
|
||||||
}
|
}
|
||||||
@ -85,7 +83,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
|
|
||||||
if (counter?.Invalid != true)
|
if (counter?.Invalid != true)
|
||||||
{
|
{
|
||||||
_context.MemoryManager.Write(gpuVa, counterData);
|
state.Channel.MemoryManager.Write(gpuVa, counterData);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -105,7 +103,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
_counterCache.AddOrUpdate(gpuVa, counter);
|
state.Channel.MemoryManager.CounterCache.AddOrUpdate(gpuVa, counter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
|
|
||||||
int index = (argument >> 4) & 0x1f;
|
int index = (argument >> 4) & 0x1f;
|
||||||
|
|
||||||
FlushUboDirty();
|
FlushUboDirty(state.Channel.MemoryManager);
|
||||||
|
|
||||||
if (enable)
|
if (enable)
|
||||||
{
|
{
|
||||||
|
@ -16,11 +16,12 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Flushes any queued ubo updates.
|
/// Flushes any queued ubo updates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void FlushUboDirty()
|
/// <param name="memoryManager">GPU memory manager where the uniform buffer is mapped</param>
|
||||||
|
private void FlushUboDirty(MemoryManager memoryManager)
|
||||||
{
|
{
|
||||||
if (_ubFollowUpAddress != 0)
|
if (_ubFollowUpAddress != 0)
|
||||||
{
|
{
|
||||||
BufferCache.ForceDirty(_ubFollowUpAddress - _ubByteCount, _ubByteCount);
|
memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
|
||||||
|
|
||||||
_ubFollowUpAddress = 0;
|
_ubFollowUpAddress = 0;
|
||||||
}
|
}
|
||||||
@ -39,13 +40,14 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
|
|
||||||
if (_ubFollowUpAddress != address)
|
if (_ubFollowUpAddress != address)
|
||||||
{
|
{
|
||||||
FlushUboDirty();
|
FlushUboDirty(state.Channel.MemoryManager);
|
||||||
|
|
||||||
_ubByteCount = 0;
|
_ubByteCount = 0;
|
||||||
_ubBeginCpuAddress = _context.MemoryManager.Translate(address);
|
_ubBeginCpuAddress = state.Channel.MemoryManager.Translate(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.PhysicalMemory.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, MemoryMarshal.Cast<int, byte>(MemoryMarshal.CreateSpan(ref argument, 1)));
|
var byteData = MemoryMarshal.Cast<int, byte>(MemoryMarshal.CreateSpan(ref argument, 1));
|
||||||
|
state.Channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
|
||||||
|
|
||||||
_ubFollowUpAddress = address + 4;
|
_ubFollowUpAddress = address + 4;
|
||||||
_ubByteCount += 4;
|
_ubByteCount += 4;
|
||||||
@ -68,13 +70,14 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
|
|
||||||
if (_ubFollowUpAddress != address)
|
if (_ubFollowUpAddress != address)
|
||||||
{
|
{
|
||||||
FlushUboDirty();
|
FlushUboDirty(state.Channel.MemoryManager);
|
||||||
|
|
||||||
_ubByteCount = 0;
|
_ubByteCount = 0;
|
||||||
_ubBeginCpuAddress = _context.MemoryManager.Translate(address);
|
_ubBeginCpuAddress = state.Channel.MemoryManager.Translate(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.PhysicalMemory.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, MemoryMarshal.Cast<int, byte>(data));
|
var byteData = MemoryMarshal.Cast<int, byte>(data);
|
||||||
|
state.Channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
|
||||||
|
|
||||||
_ubFollowUpAddress = address + size;
|
_ubFollowUpAddress = address + size;
|
||||||
_ubByteCount += size;
|
_ubByteCount += size;
|
||||||
|
@ -22,21 +22,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
private readonly ShaderProgramInfo[] _currentProgramInfo;
|
private readonly ShaderProgramInfo[] _currentProgramInfo;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// In-memory shader cache.
|
|
||||||
/// </summary>
|
|
||||||
public ShaderCache ShaderCache { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GPU buffer manager.
|
|
||||||
/// </summary>
|
|
||||||
public BufferCache BufferCache { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GPU texture manager.
|
|
||||||
/// </summary>
|
|
||||||
public TextureCache TextureCache { get; }
|
|
||||||
|
|
||||||
private bool _isAnyVbInstanced;
|
private bool _isAnyVbInstanced;
|
||||||
private bool _vsUsesInstanceId;
|
private bool _vsUsesInstanceId;
|
||||||
private byte _vsClipDistancesWritten;
|
private byte _vsClipDistancesWritten;
|
||||||
@ -53,16 +38,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
|
|
||||||
ShaderCache = new ShaderCache(_context);
|
|
||||||
|
|
||||||
_currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages];
|
_currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages];
|
||||||
|
|
||||||
BufferCache = new BufferCache(context);
|
|
||||||
TextureCache = new TextureCache(context);
|
|
||||||
|
|
||||||
context.MemoryManager.MemoryUnmapped += _counterCache.MemoryUnmappedHandler;
|
|
||||||
context.MemoryManager.MemoryUnmapped += TextureCache.MemoryUnmappedHandler;
|
|
||||||
context.MemoryManager.MemoryUnmapped += BufferCache.MemoryUnmappedHandler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -130,7 +106,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
_prevTfEnable = false;
|
_prevTfEnable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
FlushUboDirty();
|
FlushUboDirty(state.Channel.MemoryManager);
|
||||||
|
|
||||||
// Shaders must be the first one to be updated if modified, because
|
// Shaders must be the first one to be updated if modified, because
|
||||||
// some of the other state depends on information from the currently
|
// some of the other state depends on information from the currently
|
||||||
@ -342,7 +318,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
|
|
||||||
sbDescAddress += (ulong)sbDescOffset;
|
sbDescAddress += (ulong)sbDescOffset;
|
||||||
|
|
||||||
SbDescriptor sbDescriptor = _context.PhysicalMemory.Read<SbDescriptor>(sbDescAddress);
|
SbDescriptor sbDescriptor = state.Channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
|
||||||
|
|
||||||
state.Channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags);
|
state.Channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags);
|
||||||
}
|
}
|
||||||
@ -357,6 +333,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
|
||||||
private void UpdateRenderTargetState(GpuState state, bool useControl, int singleUse = -1)
|
private void UpdateRenderTargetState(GpuState state, bool useControl, int singleUse = -1)
|
||||||
{
|
{
|
||||||
|
var memoryManager = state.Channel.MemoryManager;
|
||||||
var rtControl = state.Get<RtControl>(MethodOffset.RtControl);
|
var rtControl = state.Get<RtControl>(MethodOffset.RtControl);
|
||||||
|
|
||||||
int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
|
int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
|
||||||
@ -384,7 +361,12 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture color = TextureCache.FindOrCreateTexture(colorState, samplesInX, samplesInY, sizeHint);
|
Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
||||||
|
memoryManager,
|
||||||
|
colorState,
|
||||||
|
samplesInX,
|
||||||
|
samplesInY,
|
||||||
|
sizeHint);
|
||||||
|
|
||||||
changedScale |= state.Channel.TextureManager.SetRenderTargetColor(index, color);
|
changedScale |= state.Channel.TextureManager.SetRenderTargetColor(index, color);
|
||||||
}
|
}
|
||||||
@ -398,7 +380,13 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
var dsState = state.Get<RtDepthStencilState>(MethodOffset.RtDepthStencilState);
|
var dsState = state.Get<RtDepthStencilState>(MethodOffset.RtDepthStencilState);
|
||||||
var dsSize = state.Get<Size3D>(MethodOffset.RtDepthStencilSize);
|
var dsSize = state.Get<Size3D>(MethodOffset.RtDepthStencilSize);
|
||||||
|
|
||||||
depthStencil = TextureCache.FindOrCreateTexture(dsState, dsSize, samplesInX, samplesInY, sizeHint);
|
depthStencil = memoryManager.Physical.TextureCache.FindOrCreateTexture(
|
||||||
|
memoryManager,
|
||||||
|
dsState,
|
||||||
|
dsSize,
|
||||||
|
samplesInX,
|
||||||
|
samplesInY,
|
||||||
|
sizeHint);
|
||||||
}
|
}
|
||||||
|
|
||||||
changedScale |= state.Channel.TextureManager.SetRenderTargetDepthStencil(depthStencil);
|
changedScale |= state.Channel.TextureManager.SetRenderTargetDepthStencil(depthStencil);
|
||||||
@ -1012,7 +1000,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
|||||||
addressesArray[index] = baseAddress + shader.Offset;
|
addressesArray[index] = baseAddress + shader.Offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
ShaderBundle gs = ShaderCache.GetGraphicsShader(state, addresses);
|
ShaderBundle gs = state.Channel.MemoryManager.Physical.ShaderCache.GetGraphicsShader(state, addresses);
|
||||||
|
|
||||||
byte oldVsClipDistancesWritten = _vsClipDistancesWritten;
|
byte oldVsClipDistancesWritten = _vsClipDistancesWritten;
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using Ryujinx.Graphics.Gpu.Image;
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu
|
namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
private readonly GPFifoDevice _device;
|
private readonly GPFifoDevice _device;
|
||||||
private readonly GPFifoProcessor _processor;
|
private readonly GPFifoProcessor _processor;
|
||||||
|
private MemoryManager _memoryManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Channel buffer bindings manager.
|
/// Channel buffer bindings manager.
|
||||||
@ -24,6 +26,11 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal TextureManager TextureManager { get; }
|
internal TextureManager TextureManager { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current channel memory manager.
|
||||||
|
/// </summary>
|
||||||
|
internal MemoryManager MemoryManager => _memoryManager;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of a GPU channel.
|
/// Creates a new instance of a GPU channel.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -33,10 +40,30 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
_context = context;
|
_context = context;
|
||||||
_device = context.GPFifo;
|
_device = context.GPFifo;
|
||||||
_processor = new GPFifoProcessor(context, this);
|
_processor = new GPFifoProcessor(context, this);
|
||||||
BufferManager = new BufferManager(context);
|
BufferManager = new BufferManager(context, this);
|
||||||
TextureManager = new TextureManager(context, this);
|
TextureManager = new TextureManager(context, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binds a memory manager to the channel.
|
||||||
|
/// All submitted and in-flight commands will use the specified memory manager for any memory operations.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">The new memory manager to be bound</param>
|
||||||
|
public void BindMemory(MemoryManager memoryManager)
|
||||||
|
{
|
||||||
|
var oldMemoryManager = Interlocked.Exchange(ref _memoryManager, memoryManager ?? throw new ArgumentNullException(nameof(memoryManager)));
|
||||||
|
|
||||||
|
memoryManager.Physical.IncrementReferenceCount();
|
||||||
|
|
||||||
|
if (oldMemoryManager != null)
|
||||||
|
{
|
||||||
|
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
|
||||||
|
oldMemoryManager.Physical.DecrementReferenceCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryManager.Physical.BufferCache.NotifyBuffersModified += BufferManager.Rebind;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Push a GPFIFO entry in the form of a prefetched command buffer.
|
/// Push a GPFIFO entry in the form of a prefetched command buffer.
|
||||||
/// It is intended to be used by nvservices to handle special cases.
|
/// It is intended to be used by nvservices to handle special cases.
|
||||||
@ -62,17 +89,23 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_context.DisposedChannels.Enqueue(this);
|
_context.DeferredActions.Enqueue(Destroy);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs disposal of the host GPU resources used by this channel, that are not shared.
|
/// Performs disposal of the host GPU resources used by this channel, that are not shared.
|
||||||
/// This must only be called from the render thread.
|
/// This must only be called from the render thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal void Destroy()
|
private void Destroy()
|
||||||
{
|
{
|
||||||
BufferManager.Dispose();
|
|
||||||
TextureManager.Dispose();
|
TextureManager.Dispose();
|
||||||
|
|
||||||
|
var oldMemoryManager = Interlocked.Exchange(ref _memoryManager, null);
|
||||||
|
if (oldMemoryManager != null)
|
||||||
|
{
|
||||||
|
oldMemoryManager.Physical.BufferCache.NotifyBuffersModified -= BufferManager.Rebind;
|
||||||
|
oldMemoryManager.Physical.DecrementReferenceCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,10 @@ using Ryujinx.Graphics.GAL;
|
|||||||
using Ryujinx.Graphics.Gpu.Engine;
|
using Ryujinx.Graphics.Gpu.Engine;
|
||||||
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
using Ryujinx.Graphics.Gpu.Shader;
|
||||||
using Ryujinx.Graphics.Gpu.Synchronization;
|
using Ryujinx.Graphics.Gpu.Synchronization;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
@ -24,16 +26,6 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IRenderer Renderer { get; }
|
public IRenderer Renderer { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Physical memory access (it actually accesses the process memory, not actual physical memory).
|
|
||||||
/// </summary>
|
|
||||||
internal PhysicalMemory PhysicalMemory { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GPU memory manager.
|
|
||||||
/// </summary>
|
|
||||||
public MemoryManager MemoryManager { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// GPU engine methods processing.
|
/// GPU engine methods processing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -73,11 +65,14 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
internal List<Action> SyncActions { get; }
|
internal List<Action> SyncActions { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queue with closed channels for deferred disposal from the render thread.
|
/// Queue with deferred actions that must run on the render thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal Queue<GpuChannel> DisposedChannels { get; }
|
internal Queue<Action> DeferredActions { get; }
|
||||||
|
|
||||||
private readonly Lazy<Capabilities> _caps;
|
/// <summary>
|
||||||
|
/// Registry with physical memories that can be used with this GPU context, keyed by owner process ID.
|
||||||
|
/// </summary>
|
||||||
|
internal ConcurrentDictionary<long, PhysicalMemory> PhysicalMemoryRegistry { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Host hardware capabilities.
|
/// Host hardware capabilities.
|
||||||
@ -87,11 +82,9 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event for signalling shader cache loading progress.
|
/// Event for signalling shader cache loading progress.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<Shader.ShaderCacheState, int, int> ShaderCacheStateChanged
|
public event Action<ShaderCacheState, int, int> ShaderCacheStateChanged;
|
||||||
{
|
|
||||||
add => Methods.ShaderCache.ShaderCacheStateChanged += value;
|
private readonly Lazy<Capabilities> _caps;
|
||||||
remove => Methods.ShaderCache.ShaderCacheStateChanged -= value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the GPU emulation context.
|
/// Creates a new instance of the GPU emulation context.
|
||||||
@ -101,8 +94,6 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
{
|
{
|
||||||
Renderer = renderer;
|
Renderer = renderer;
|
||||||
|
|
||||||
MemoryManager = new MemoryManager(this);
|
|
||||||
|
|
||||||
Methods = new Methods(this);
|
Methods = new Methods(this);
|
||||||
|
|
||||||
GPFifo = new GPFifoDevice(this);
|
GPFifo = new GPFifoDevice(this);
|
||||||
@ -111,20 +102,83 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
|
|
||||||
Window = new Window(this);
|
Window = new Window(this);
|
||||||
|
|
||||||
_caps = new Lazy<Capabilities>(Renderer.GetCapabilities);
|
|
||||||
|
|
||||||
HostInitalized = new ManualResetEvent(false);
|
HostInitalized = new ManualResetEvent(false);
|
||||||
|
|
||||||
SyncActions = new List<Action>();
|
SyncActions = new List<Action>();
|
||||||
|
|
||||||
DisposedChannels = new Queue<GpuChannel>();
|
DeferredActions = new Queue<Action>();
|
||||||
|
|
||||||
|
PhysicalMemoryRegistry = new ConcurrentDictionary<long, PhysicalMemory>();
|
||||||
|
|
||||||
|
_caps = new Lazy<Capabilities>(Renderer.GetCapabilities);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new GPU channel.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The GPU channel</returns>
|
||||||
public GpuChannel CreateChannel()
|
public GpuChannel CreateChannel()
|
||||||
{
|
{
|
||||||
return new GpuChannel(this);
|
return new GpuChannel(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new GPU memory manager.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pid">ID of the process that owns the memory manager</param>
|
||||||
|
/// <returns>The memory manager</returns>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when <paramref name="pid"/> is invalid</exception>
|
||||||
|
public MemoryManager CreateMemoryManager(long pid)
|
||||||
|
{
|
||||||
|
if (!PhysicalMemoryRegistry.TryGetValue(pid, out var physicalMemory))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MemoryManager(physicalMemory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers virtual memory used by a process for GPU memory access, caching and read/write tracking.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pid">ID of the process that owns <paramref name="cpuMemory"/></param>
|
||||||
|
/// <param name="cpuMemory">Virtual memory owned by the process</param>
|
||||||
|
/// <exception cref="ArgumentException">Thrown if <paramref name="pid"/> was already registered</exception>
|
||||||
|
public void RegisterProcess(long pid, Cpu.IVirtualMemoryManagerTracked cpuMemory)
|
||||||
|
{
|
||||||
|
var physicalMemory = new PhysicalMemory(this, cpuMemory);
|
||||||
|
if (!PhysicalMemoryRegistry.TryAdd(pid, physicalMemory))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The PID was already registered", nameof(pid));
|
||||||
|
}
|
||||||
|
|
||||||
|
physicalMemory.ShaderCache.ShaderCacheStateChanged += ShaderCacheStateUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unregisters a process, indicating that its memory will no longer be used, and that caches can be freed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pid">ID of the process</param>
|
||||||
|
public void UnregisterProcess(long pid)
|
||||||
|
{
|
||||||
|
if (PhysicalMemoryRegistry.TryRemove(pid, out var physicalMemory))
|
||||||
|
{
|
||||||
|
physicalMemory.ShaderCache.ShaderCacheStateChanged -= ShaderCacheStateUpdate;
|
||||||
|
physicalMemory.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shader cache state update handler.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">Current state of the shader cache load process</param>
|
||||||
|
/// <param name="current">Number of the current shader being processed</param>
|
||||||
|
/// <param name="total">Total number of shaders to process</param>
|
||||||
|
private void ShaderCacheStateUpdate(ShaderCacheState state, int current, int total)
|
||||||
|
{
|
||||||
|
ShaderCacheStateChanged?.Invoke(state, current, total);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initialize the GPU shader cache.
|
/// Initialize the GPU shader cache.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -132,7 +186,10 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
{
|
{
|
||||||
HostInitalized.WaitOne();
|
HostInitalized.WaitOne();
|
||||||
|
|
||||||
Methods.ShaderCache.Initialize();
|
foreach (var physicalMemory in PhysicalMemoryRegistry.Values)
|
||||||
|
{
|
||||||
|
physicalMemory.ShaderCache.Initialize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -144,16 +201,6 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
SequenceNumber++;
|
SequenceNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the process memory manager, after the application process is initialized.
|
|
||||||
/// This is required for any GPU memory access.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cpuMemory">CPU memory manager</param>
|
|
||||||
public void SetVmm(Cpu.IVirtualMemoryManagerTracked cpuMemory)
|
|
||||||
{
|
|
||||||
PhysicalMemory = new PhysicalMemory(cpuMemory);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers an action to be performed the next time a syncpoint is incremented.
|
/// Registers an action to be performed the next time a syncpoint is incremented.
|
||||||
/// This will also ensure a host sync object is created, and <see cref="SyncNumber"/> is incremented.
|
/// This will also ensure a host sync object is created, and <see cref="SyncNumber"/> is incremented.
|
||||||
@ -186,14 +233,14 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs deferred disposal of closed channels.
|
/// Performs deferred actions.
|
||||||
/// This must only be called from the render thread.
|
/// This is useful for actions that must run on the render thread, such as resource disposal.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal void DisposePendingChannels()
|
internal void RunDeferredActions()
|
||||||
{
|
{
|
||||||
while (DisposedChannels.TryDequeue(out GpuChannel channel))
|
while (DeferredActions.TryDequeue(out Action action))
|
||||||
{
|
{
|
||||||
channel.Destroy();
|
action();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,15 +252,19 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
DisposePendingChannels();
|
|
||||||
Methods.ShaderCache.Dispose();
|
|
||||||
Methods.BufferCache.Dispose();
|
|
||||||
Methods.TextureCache.Dispose();
|
|
||||||
Renderer.Dispose();
|
Renderer.Dispose();
|
||||||
GPFifo.Dispose();
|
GPFifo.Dispose();
|
||||||
HostInitalized.Dispose();
|
HostInitalized.Dispose();
|
||||||
|
|
||||||
PhysicalMemory.Dispose();
|
// Has to be disposed before processing deferred actions, as it will produce some.
|
||||||
|
foreach (var physicalMemory in PhysicalMemoryRegistry.Values)
|
||||||
|
{
|
||||||
|
physicalMemory.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
PhysicalMemoryRegistry.Clear();
|
||||||
|
|
||||||
|
RunDeferredActions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Cpu.Tracking;
|
using Ryujinx.Cpu.Tracking;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
protected const int DescriptorSize = 0x20;
|
protected const int DescriptorSize = 0x20;
|
||||||
|
|
||||||
protected GpuContext Context;
|
protected GpuContext Context;
|
||||||
|
protected PhysicalMemory PhysicalMemory;
|
||||||
|
|
||||||
protected T1[] Items;
|
protected T1[] Items;
|
||||||
protected T2[] DescriptorCache;
|
protected T2[] DescriptorCache;
|
||||||
@ -38,9 +40,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private readonly CpuMultiRegionHandle _memoryTracking;
|
private readonly CpuMultiRegionHandle _memoryTracking;
|
||||||
private readonly Action<ulong, ulong> _modifiedDelegate;
|
private readonly Action<ulong, ulong> _modifiedDelegate;
|
||||||
|
|
||||||
public Pool(GpuContext context, ulong address, int maximumId)
|
/// <summary>
|
||||||
|
/// Creates a new instance of the GPU resource pool.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">GPU context that the pool belongs to</param>
|
||||||
|
/// <param name="physicalMemory">Physical memory where the resource descriptors are mapped</param>
|
||||||
|
/// <param name="address">Address of the pool in physical memory</param>
|
||||||
|
/// <param name="maximumId">Maximum index of an item on the pool (inclusive)</param>
|
||||||
|
public Pool(GpuContext context, PhysicalMemory physicalMemory, ulong address, int maximumId)
|
||||||
{
|
{
|
||||||
Context = context;
|
Context = context;
|
||||||
|
PhysicalMemory = physicalMemory;
|
||||||
MaximumId = maximumId;
|
MaximumId = maximumId;
|
||||||
|
|
||||||
int count = maximumId + 1;
|
int count = maximumId + 1;
|
||||||
@ -53,11 +63,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
Address = address;
|
Address = address;
|
||||||
Size = size;
|
Size = size;
|
||||||
|
|
||||||
_memoryTracking = context.PhysicalMemory.BeginGranularTracking(address, size);
|
_memoryTracking = physicalMemory.BeginGranularTracking(address, size);
|
||||||
_modifiedDelegate = RegionModified;
|
_modifiedDelegate = RegionModified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the descriptor for a given ID.
|
/// Gets the descriptor for a given ID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -65,7 +74,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <returns>The descriptor</returns>
|
/// <returns>The descriptor</returns>
|
||||||
public T2 GetDescriptor(int id)
|
public T2 GetDescriptor(int id)
|
||||||
{
|
{
|
||||||
return Context.PhysicalMemory.Read<T2>(Address + (ulong)id * DescriptorSize);
|
return PhysicalMemory.Read<T2>(Address + (ulong)id * DescriptorSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Image
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -11,9 +13,10 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Constructs a new instance of the sampler pool.
|
/// Constructs a new instance of the sampler pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the sampler pool belongs to</param>
|
/// <param name="context">GPU context that the sampler pool belongs to</param>
|
||||||
|
/// <param name="physicalMemory">Physical memory where the sampler descriptors are mapped</param>
|
||||||
/// <param name="address">Address of the sampler pool in guest memory</param>
|
/// <param name="address">Address of the sampler pool in guest memory</param>
|
||||||
/// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param>
|
/// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param>
|
||||||
public SamplerPool(GpuContext context, ulong address, int maximumId) : base(context, address, maximumId) { }
|
public SamplerPool(GpuContext context, PhysicalMemory physicalMemory, ulong address, int maximumId) : base(context, physicalMemory, address, maximumId) { }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the sampler with the given ID.
|
/// Gets the sampler with the given ID.
|
||||||
|
@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
private GpuContext _context;
|
private GpuContext _context;
|
||||||
|
private PhysicalMemory _physicalMemory;
|
||||||
|
|
||||||
private SizeInfo _sizeInfo;
|
private SizeInfo _sizeInfo;
|
||||||
|
|
||||||
@ -139,6 +140,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Constructs a new instance of the cached GPU texture.
|
/// Constructs a new instance of the cached GPU texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the texture belongs to</param>
|
/// <param name="context">GPU context that the texture belongs to</param>
|
||||||
|
/// <param name="physicalMemory">Physical memory where the texture is mapped</param>
|
||||||
/// <param name="info">Texture information</param>
|
/// <param name="info">Texture information</param>
|
||||||
/// <param name="sizeInfo">Size information of the texture</param>
|
/// <param name="sizeInfo">Size information of the texture</param>
|
||||||
/// <param name="range">Physical memory ranges where the texture data is located</param>
|
/// <param name="range">Physical memory ranges where the texture data is located</param>
|
||||||
@ -148,6 +150,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="scaleMode">The scale mode to initialize with</param>
|
/// <param name="scaleMode">The scale mode to initialize with</param>
|
||||||
private Texture(
|
private Texture(
|
||||||
GpuContext context,
|
GpuContext context,
|
||||||
|
PhysicalMemory physicalMemory,
|
||||||
TextureInfo info,
|
TextureInfo info,
|
||||||
SizeInfo sizeInfo,
|
SizeInfo sizeInfo,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
@ -156,7 +159,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
float scaleFactor,
|
float scaleFactor,
|
||||||
TextureScaleMode scaleMode)
|
TextureScaleMode scaleMode)
|
||||||
{
|
{
|
||||||
InitializeTexture(context, info, sizeInfo, range);
|
InitializeTexture(context, physicalMemory, info, sizeInfo, range);
|
||||||
|
|
||||||
FirstLayer = firstLayer;
|
FirstLayer = firstLayer;
|
||||||
FirstLevel = firstLevel;
|
FirstLevel = firstLevel;
|
||||||
@ -171,16 +174,23 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Constructs a new instance of the cached GPU texture.
|
/// Constructs a new instance of the cached GPU texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the texture belongs to</param>
|
/// <param name="context">GPU context that the texture belongs to</param>
|
||||||
|
/// <param name="physicalMemory">Physical memory where the texture is mapped</param>
|
||||||
/// <param name="info">Texture information</param>
|
/// <param name="info">Texture information</param>
|
||||||
/// <param name="sizeInfo">Size information of the texture</param>
|
/// <param name="sizeInfo">Size information of the texture</param>
|
||||||
/// <param name="range">Physical memory ranges where the texture data is located</param>
|
/// <param name="range">Physical memory ranges where the texture data is located</param>
|
||||||
/// <param name="scaleMode">The scale mode to initialize with. If scaled, the texture's data is loaded immediately and scaled up</param>
|
/// <param name="scaleMode">The scale mode to initialize with. If scaled, the texture's data is loaded immediately and scaled up</param>
|
||||||
public Texture(GpuContext context, TextureInfo info, SizeInfo sizeInfo, MultiRange range, TextureScaleMode scaleMode)
|
public Texture(
|
||||||
|
GpuContext context,
|
||||||
|
PhysicalMemory physicalMemory,
|
||||||
|
TextureInfo info,
|
||||||
|
SizeInfo sizeInfo,
|
||||||
|
MultiRange range,
|
||||||
|
TextureScaleMode scaleMode)
|
||||||
{
|
{
|
||||||
ScaleFactor = 1f; // Texture is first loaded at scale 1x.
|
ScaleFactor = 1f; // Texture is first loaded at scale 1x.
|
||||||
ScaleMode = scaleMode;
|
ScaleMode = scaleMode;
|
||||||
|
|
||||||
InitializeTexture(context, info, sizeInfo, range);
|
InitializeTexture(context, physicalMemory, info, sizeInfo, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -189,12 +199,19 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Other fields are initialized with their default values.
|
/// Other fields are initialized with their default values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the texture belongs to</param>
|
/// <param name="context">GPU context that the texture belongs to</param>
|
||||||
|
/// <param name="physicalMemory">Physical memory where the texture is mapped</param>
|
||||||
/// <param name="info">Texture information</param>
|
/// <param name="info">Texture information</param>
|
||||||
/// <param name="sizeInfo">Size information of the texture</param>
|
/// <param name="sizeInfo">Size information of the texture</param>
|
||||||
/// <param name="range">Physical memory ranges where the texture data is located</param>
|
/// <param name="range">Physical memory ranges where the texture data is located</param>
|
||||||
private void InitializeTexture(GpuContext context, TextureInfo info, SizeInfo sizeInfo, MultiRange range)
|
private void InitializeTexture(
|
||||||
|
GpuContext context,
|
||||||
|
PhysicalMemory physicalMemory,
|
||||||
|
TextureInfo info,
|
||||||
|
SizeInfo sizeInfo,
|
||||||
|
MultiRange range)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
|
_physicalMemory = physicalMemory;
|
||||||
_sizeInfo = sizeInfo;
|
_sizeInfo = sizeInfo;
|
||||||
Range = range;
|
Range = range;
|
||||||
|
|
||||||
@ -255,7 +272,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="hasMipViews">True if the texture will have mip views</param>
|
/// <param name="hasMipViews">True if the texture will have mip views</param>
|
||||||
public void InitializeGroup(bool hasLayerViews, bool hasMipViews)
|
public void InitializeGroup(bool hasLayerViews, bool hasMipViews)
|
||||||
{
|
{
|
||||||
Group = new TextureGroup(_context, this);
|
Group = new TextureGroup(_context, _physicalMemory, this);
|
||||||
|
|
||||||
Group.Initialize(ref _sizeInfo, hasLayerViews, hasMipViews);
|
Group.Initialize(ref _sizeInfo, hasLayerViews, hasMipViews);
|
||||||
}
|
}
|
||||||
@ -276,6 +293,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
Texture texture = new Texture(
|
Texture texture = new Texture(
|
||||||
_context,
|
_context,
|
||||||
|
_physicalMemory,
|
||||||
info,
|
info,
|
||||||
sizeInfo,
|
sizeInfo,
|
||||||
range,
|
range,
|
||||||
@ -638,7 +656,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
BlacklistScale();
|
BlacklistScale();
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<byte> data = _context.PhysicalMemory.GetSpan(Range);
|
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Range);
|
||||||
|
|
||||||
IsModified = false;
|
IsModified = false;
|
||||||
|
|
||||||
@ -805,11 +823,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (tracked)
|
if (tracked)
|
||||||
{
|
{
|
||||||
_context.PhysicalMemory.Write(Range, GetTextureDataFromGpu(tracked));
|
_physicalMemory.Write(Range, GetTextureDataFromGpu(tracked));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_context.PhysicalMemory.WriteUntracked(Range, GetTextureDataFromGpu(tracked));
|
_physicalMemory.WriteUntracked(Range, GetTextureDataFromGpu(tracked));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -846,7 +864,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
texture = _flushHostTexture = GetScaledHostTexture(1f, _flushHostTexture);
|
texture = _flushHostTexture = GetScaledHostTexture(1f, _flushHostTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.PhysicalMemory.WriteUntracked(Range, GetTextureDataFromGpu(false, texture));
|
_physicalMemory.WriteUntracked(Range, GetTextureDataFromGpu(false, texture));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1280,7 +1298,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_viewStorage.RemoveView(this);
|
_viewStorage.RemoveView(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.Methods.TextureCache.RemoveTextureFromCache(this);
|
_physicalMemory.TextureCache.RemoveTextureFromCache(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.Assert(newRefCount >= 0);
|
Debug.Assert(newRefCount >= 0);
|
||||||
|
@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="samplerIndex">Type of the sampler pool indexing used for bound samplers</param>
|
/// <param name="samplerIndex">Type of the sampler pool indexing used for bound samplers</param>
|
||||||
public void SetSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
|
public void SetSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
|
||||||
{
|
{
|
||||||
ulong address = _context.MemoryManager.Translate(gpuVa);
|
ulong address = _channel.MemoryManager.Translate(gpuVa);
|
||||||
|
|
||||||
if (_samplerPool != null)
|
if (_samplerPool != null)
|
||||||
{
|
{
|
||||||
@ -131,7 +131,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
_samplerPool.Dispose();
|
_samplerPool.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
_samplerPool = new SamplerPool(_context, address, maximumId);
|
_samplerPool = new SamplerPool(_context, _channel.MemoryManager.Physical, address, maximumId);
|
||||||
_samplerIndex = samplerIndex;
|
_samplerIndex = samplerIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <param name="maximumId">Maximum ID of the pool (total count minus one)</param>
|
/// <param name="maximumId">Maximum ID of the pool (total count minus one)</param>
|
||||||
public void SetTexturePool(ulong gpuVa, int maximumId)
|
public void SetTexturePool(ulong gpuVa, int maximumId)
|
||||||
{
|
{
|
||||||
ulong address = _context.MemoryManager.Translate(gpuVa);
|
ulong address = _channel.MemoryManager.Translate(gpuVa);
|
||||||
|
|
||||||
_texturePoolAddress = address;
|
_texturePoolAddress = address;
|
||||||
_texturePoolMaximumId = maximumId;
|
_texturePoolMaximumId = maximumId;
|
||||||
@ -228,6 +228,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
public void CommitBindings()
|
public void CommitBindings()
|
||||||
{
|
{
|
||||||
TexturePool texturePool = _texturePoolCache.FindOrCreate(
|
TexturePool texturePool = _texturePoolCache.FindOrCreate(
|
||||||
|
_channel,
|
||||||
_texturePoolAddress,
|
_texturePoolAddress,
|
||||||
_texturePoolMaximumId);
|
_texturePoolMaximumId);
|
||||||
|
|
||||||
@ -437,9 +438,9 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
var poolState = state.Get<PoolState>(MethodOffset.TexturePoolState);
|
var poolState = state.Get<PoolState>(MethodOffset.TexturePoolState);
|
||||||
|
|
||||||
ulong poolAddress = _context.MemoryManager.Translate(poolState.Address.Pack());
|
ulong poolAddress = _channel.MemoryManager.Translate(poolState.Address.Pack());
|
||||||
|
|
||||||
TexturePool texturePool = _texturePoolCache.FindOrCreate(poolAddress, poolState.MaximumId);
|
TexturePool texturePool = _texturePoolCache.FindOrCreate(_channel, poolAddress, poolState.MaximumId);
|
||||||
|
|
||||||
return texturePool.GetDescriptor(textureId);
|
return texturePool.GetDescriptor(textureId);
|
||||||
}
|
}
|
||||||
@ -455,12 +456,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <returns>The packed texture and sampler ID (the real texture handle)</returns>
|
/// <returns>The packed texture and sampler ID (the real texture handle)</returns>
|
||||||
private int ReadPackedId(int stageIndex, int wordOffset, int textureBufferIndex, int samplerBufferIndex)
|
private int ReadPackedId(int stageIndex, int wordOffset, int textureBufferIndex, int samplerBufferIndex)
|
||||||
{
|
{
|
||||||
var bufferManager = _context.Methods.BufferCache;
|
|
||||||
ulong textureBufferAddress = _isCompute
|
ulong textureBufferAddress = _isCompute
|
||||||
? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex)
|
? _channel.BufferManager.GetComputeUniformBufferAddress(textureBufferIndex)
|
||||||
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex);
|
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, textureBufferIndex);
|
||||||
|
|
||||||
int handle = _context.PhysicalMemory.Read<int>(textureBufferAddress + (ulong)(wordOffset & HandleMask) * 4);
|
int handle = _channel.MemoryManager.Physical.Read<int>(textureBufferAddress + (ulong)(wordOffset & HandleMask) * 4);
|
||||||
|
|
||||||
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
|
// The "wordOffset" (which is really the immediate value used on texture instructions on the shader)
|
||||||
// is a 13-bit value. However, in order to also support separate samplers and textures (which uses
|
// is a 13-bit value. However, in order to also support separate samplers and textures (which uses
|
||||||
@ -474,7 +474,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
|
? _channel.BufferManager.GetComputeUniformBufferAddress(samplerBufferIndex)
|
||||||
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
|
: _channel.BufferManager.GetGraphicsUniformBufferAddress(stageIndex, samplerBufferIndex);
|
||||||
|
|
||||||
handle |= _context.PhysicalMemory.Read<int>(samplerBufferAddress + (ulong)((wordOffset >> HandleHigh) - 1) * 4);
|
handle |= _channel.MemoryManager.Physical.Read<int>(samplerBufferAddress + (ulong)((wordOffset >> HandleHigh) - 1) * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
@ -514,7 +514,6 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_samplerPool?.Dispose();
|
_samplerPool?.Dispose();
|
||||||
_texturePoolCache.Dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -32,6 +32,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
private const int OverlapsBufferMaxCapacity = 10000;
|
private const int OverlapsBufferMaxCapacity = 10000;
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
|
private readonly PhysicalMemory _physicalMemory;
|
||||||
|
|
||||||
private readonly MultiRangeList<Texture> _textures;
|
private readonly MultiRangeList<Texture> _textures;
|
||||||
|
|
||||||
@ -44,9 +45,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Constructs a new instance of the texture manager.
|
/// Constructs a new instance of the texture manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The GPU context that the texture manager belongs to</param>
|
/// <param name="context">The GPU context that the texture manager belongs to</param>
|
||||||
public TextureCache(GpuContext context)
|
/// <param name="physicalMemory">Physical memory where the textures managed by this cache are mapped</param>
|
||||||
|
public TextureCache(GpuContext context, PhysicalMemory physicalMemory)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
|
_physicalMemory = physicalMemory;
|
||||||
|
|
||||||
_textures = new MultiRangeList<Texture>();
|
_textures = new MultiRangeList<Texture>();
|
||||||
|
|
||||||
@ -68,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
lock (_textures)
|
lock (_textures)
|
||||||
{
|
{
|
||||||
overlapCount = _textures.FindOverlaps(_context.MemoryManager.Translate(e.Address), e.Size, ref overlaps);
|
overlapCount = _textures.FindOverlaps(((MemoryManager)sender).Translate(e.Address), e.Size, ref overlaps);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < overlapCount; i++)
|
for (int i = 0; i < overlapCount; i++)
|
||||||
@ -139,13 +142,20 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find an existing texture, or create a new one if not found.
|
/// Tries to find an existing texture, or create a new one if not found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
|
||||||
/// <param name="copyTexture">Copy texture to find or create</param>
|
/// <param name="copyTexture">Copy texture to find or create</param>
|
||||||
/// <param name="offset">Offset to be added to the physical texture address</param>
|
/// <param name="offset">Offset to be added to the physical texture address</param>
|
||||||
/// <param name="formatInfo">Format information of the copy texture</param>
|
/// <param name="formatInfo">Format information of the copy texture</param>
|
||||||
/// <param name="preferScaling">Indicates if the texture should be scaled from the start</param>
|
/// <param name="preferScaling">Indicates if the texture should be scaled from the start</param>
|
||||||
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
||||||
/// <returns>The texture</returns>
|
/// <returns>The texture</returns>
|
||||||
public Texture FindOrCreateTexture(CopyTexture copyTexture, ulong offset, FormatInfo formatInfo, bool preferScaling = true, Size? sizeHint = null)
|
public Texture FindOrCreateTexture(
|
||||||
|
MemoryManager memoryManager,
|
||||||
|
CopyTexture copyTexture,
|
||||||
|
ulong offset,
|
||||||
|
FormatInfo formatInfo,
|
||||||
|
bool preferScaling = true,
|
||||||
|
Size? sizeHint = null)
|
||||||
{
|
{
|
||||||
int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
|
int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
|
||||||
int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
|
int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
|
||||||
@ -184,7 +194,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
flags |= TextureSearchFlags.WithUpscale;
|
flags |= TextureSearchFlags.WithUpscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture texture = FindOrCreateTexture(flags, info, 0, sizeHint);
|
Texture texture = FindOrCreateTexture(memoryManager, flags, info, 0, sizeHint);
|
||||||
|
|
||||||
texture?.SynchronizeMemory();
|
texture?.SynchronizeMemory();
|
||||||
|
|
||||||
@ -194,12 +204,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find an existing texture, or create a new one if not found.
|
/// Tries to find an existing texture, or create a new one if not found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
|
||||||
/// <param name="colorState">Color buffer texture to find or create</param>
|
/// <param name="colorState">Color buffer texture to find or create</param>
|
||||||
/// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
|
/// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
|
||||||
/// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
|
/// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
|
||||||
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
||||||
/// <returns>The texture</returns>
|
/// <returns>The texture</returns>
|
||||||
public Texture FindOrCreateTexture(RtColorState colorState, int samplesInX, int samplesInY, Size sizeHint)
|
public Texture FindOrCreateTexture(MemoryManager memoryManager, RtColorState colorState, int samplesInX, int samplesInY, Size sizeHint)
|
||||||
{
|
{
|
||||||
bool isLinear = colorState.MemoryLayout.UnpackIsLinear();
|
bool isLinear = colorState.MemoryLayout.UnpackIsLinear();
|
||||||
|
|
||||||
@ -263,7 +274,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
int layerSize = !isLinear ? colorState.LayerSize * 4 : 0;
|
int layerSize = !isLinear ? colorState.LayerSize * 4 : 0;
|
||||||
|
|
||||||
Texture texture = FindOrCreateTexture(TextureSearchFlags.WithUpscale, info, layerSize, sizeHint);
|
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, layerSize, sizeHint);
|
||||||
|
|
||||||
texture?.SynchronizeMemory();
|
texture?.SynchronizeMemory();
|
||||||
|
|
||||||
@ -273,13 +284,20 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find an existing texture, or create a new one if not found.
|
/// Tries to find an existing texture, or create a new one if not found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
|
||||||
/// <param name="dsState">Depth-stencil buffer texture to find or create</param>
|
/// <param name="dsState">Depth-stencil buffer texture to find or create</param>
|
||||||
/// <param name="size">Size of the depth-stencil texture</param>
|
/// <param name="size">Size of the depth-stencil texture</param>
|
||||||
/// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
|
/// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
|
||||||
/// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
|
/// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
|
||||||
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
||||||
/// <returns>The texture</returns>
|
/// <returns>The texture</returns>
|
||||||
public Texture FindOrCreateTexture(RtDepthStencilState dsState, Size3D size, int samplesInX, int samplesInY, Size sizeHint)
|
public Texture FindOrCreateTexture(
|
||||||
|
MemoryManager memoryManager,
|
||||||
|
RtDepthStencilState dsState,
|
||||||
|
Size3D size,
|
||||||
|
int samplesInX,
|
||||||
|
int samplesInY,
|
||||||
|
Size sizeHint)
|
||||||
{
|
{
|
||||||
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
|
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
|
||||||
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
|
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
|
||||||
@ -306,7 +324,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
target,
|
target,
|
||||||
formatInfo);
|
formatInfo);
|
||||||
|
|
||||||
Texture texture = FindOrCreateTexture(TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4, sizeHint);
|
Texture texture = FindOrCreateTexture(memoryManager, TextureSearchFlags.WithUpscale, info, dsState.LayerSize * 4, sizeHint);
|
||||||
|
|
||||||
texture?.SynchronizeMemory();
|
texture?.SynchronizeMemory();
|
||||||
|
|
||||||
@ -316,13 +334,20 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find an existing texture, or create a new one if not found.
|
/// Tries to find an existing texture, or create a new one if not found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
|
||||||
/// <param name="flags">The texture search flags, defines texture comparison rules</param>
|
/// <param name="flags">The texture search flags, defines texture comparison rules</param>
|
||||||
/// <param name="info">Texture information of the texture to be found or created</param>
|
/// <param name="info">Texture information of the texture to be found or created</param>
|
||||||
/// <param name="layerSize">Size in bytes of a single texture layer</param>
|
/// <param name="layerSize">Size in bytes of a single texture layer</param>
|
||||||
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
|
||||||
/// <param name="range">Optional ranges of physical memory where the texture data is located</param>
|
/// <param name="range">Optional ranges of physical memory where the texture data is located</param>
|
||||||
/// <returns>The texture</returns>
|
/// <returns>The texture</returns>
|
||||||
public Texture FindOrCreateTexture(TextureSearchFlags flags, TextureInfo info, int layerSize = 0, Size? sizeHint = null, MultiRange? range = null)
|
public Texture FindOrCreateTexture(
|
||||||
|
MemoryManager memoryManager,
|
||||||
|
TextureSearchFlags flags,
|
||||||
|
TextureInfo info,
|
||||||
|
int layerSize = 0,
|
||||||
|
Size? sizeHint = null,
|
||||||
|
MultiRange? range = null)
|
||||||
{
|
{
|
||||||
bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
|
bool isSamplerTexture = (flags & TextureSearchFlags.ForSampler) != 0;
|
||||||
|
|
||||||
@ -342,7 +367,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
address = _context.MemoryManager.Translate(info.GpuAddress);
|
address = memoryManager.Translate(info.GpuAddress);
|
||||||
|
|
||||||
if (address == MemoryManager.PteUnmapped)
|
if (address == MemoryManager.PteUnmapped)
|
||||||
{
|
{
|
||||||
@ -371,24 +396,28 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
if (matchQuality != TextureMatchQuality.NoMatch)
|
if (matchQuality != TextureMatchQuality.NoMatch)
|
||||||
{
|
{
|
||||||
// If the parameters match, we need to make sure the texture is mapped to the same memory regions.
|
// If the parameters match, we need to make sure the texture is mapped to the same memory regions.
|
||||||
|
if (range != null)
|
||||||
|
{
|
||||||
// If a range of memory was supplied, just check if the ranges match.
|
// If a range of memory was supplied, just check if the ranges match.
|
||||||
if (range != null && !overlap.Range.Equals(range.Value))
|
if (!overlap.Range.Equals(range.Value))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// If no range was supplied, we can check if the GPU virtual address match. If they do,
|
// If no range was supplied, we can check if the GPU virtual address match. If they do,
|
||||||
// we know the textures are located at the same memory region.
|
// we know the textures are located at the same memory region.
|
||||||
// If they don't, it may still be mapped to the same physical region, so we
|
// If they don't, it may still be mapped to the same physical region, so we
|
||||||
// do a more expensive check to tell if they are mapped into the same physical regions.
|
// do a more expensive check to tell if they are mapped into the same physical regions.
|
||||||
// If the GPU VA for the texture has ever been unmapped, then the range must be checked regardless.
|
// If the GPU VA for the texture has ever been unmapped, then the range must be checked regardless.
|
||||||
if ((overlap.Info.GpuAddress != info.GpuAddress || overlap.ChangedMapping) &&
|
if ((overlap.Info.GpuAddress != info.GpuAddress || overlap.ChangedMapping) &&
|
||||||
!_context.MemoryManager.CompareRange(overlap.Range, info.GpuAddress))
|
!memoryManager.CompareRange(overlap.Range, info.GpuAddress))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (matchQuality == TextureMatchQuality.Perfect)
|
if (matchQuality == TextureMatchQuality.Perfect)
|
||||||
{
|
{
|
||||||
@ -426,7 +455,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (range == null)
|
if (range == null)
|
||||||
{
|
{
|
||||||
range = _context.MemoryManager.GetPhysicalRegions(info.GpuAddress, size);
|
range = memoryManager.GetPhysicalRegions(info.GpuAddress, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find view compatible matches.
|
// Find view compatible matches.
|
||||||
@ -495,7 +524,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
{
|
{
|
||||||
// Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead.
|
// Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead.
|
||||||
|
|
||||||
texture = new Texture(_context, info, sizeInfo, range.Value, scaleMode);
|
texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);
|
||||||
texture.InitializeGroup(true, true);
|
texture.InitializeGroup(true, true);
|
||||||
texture.InitializeData(false, false);
|
texture.InitializeData(false, false);
|
||||||
|
|
||||||
@ -531,7 +560,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
// No match, create a new texture.
|
// No match, create a new texture.
|
||||||
if (texture == null)
|
if (texture == null)
|
||||||
{
|
{
|
||||||
texture = new Texture(_context, info, sizeInfo, range.Value, scaleMode);
|
texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode);
|
||||||
|
|
||||||
// Step 1: Find textures that are view compatible with the new texture.
|
// Step 1: Find textures that are view compatible with the new texture.
|
||||||
// Any textures that are incompatible will contain garbage data, so they should be removed where possible.
|
// Any textures that are incompatible will contain garbage data, so they should be removed where possible.
|
||||||
@ -722,14 +751,15 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null.
|
/// Tries to find an existing texture matching the given buffer copy destination. If none is found, returns null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
|
||||||
/// <param name="tex">The texture information</param>
|
/// <param name="tex">The texture information</param>
|
||||||
/// <param name="cbp">The copy buffer parameters</param>
|
/// <param name="cbp">The copy buffer parameters</param>
|
||||||
/// <param name="swizzle">The copy buffer swizzle</param>
|
/// <param name="swizzle">The copy buffer swizzle</param>
|
||||||
/// <param name="linear">True if the texture has a linear layout, false otherwise</param>
|
/// <param name="linear">True if the texture has a linear layout, false otherwise</param>
|
||||||
/// <returns>A matching texture, or null if there is no match</returns>
|
/// <returns>A matching texture, or null if there is no match</returns>
|
||||||
public Texture FindTexture(CopyBufferTexture tex, CopyBufferParams cbp, CopyBufferSwizzle swizzle, bool linear)
|
public Texture FindTexture(MemoryManager memoryManager, CopyBufferTexture tex, CopyBufferParams cbp, CopyBufferSwizzle swizzle, bool linear)
|
||||||
{
|
{
|
||||||
ulong address = _context.MemoryManager.Translate(cbp.DstAddress.Pack());
|
ulong address = memoryManager.Translate(cbp.DstAddress.Pack());
|
||||||
|
|
||||||
if (address == MemoryManager.PteUnmapped)
|
if (address == MemoryManager.PteUnmapped)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Cpu.Tracking;
|
using Ryujinx.Cpu.Tracking;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using System;
|
using System;
|
||||||
@ -28,7 +29,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasCopyDependencies { get; set; }
|
public bool HasCopyDependencies { get; set; }
|
||||||
|
|
||||||
private GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
|
private readonly PhysicalMemory _physicalMemory;
|
||||||
|
|
||||||
private int[] _allOffsets;
|
private int[] _allOffsets;
|
||||||
private int[] _sliceSizes;
|
private int[] _sliceSizes;
|
||||||
@ -51,11 +53,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Create a new texture group.
|
/// Create a new texture group.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the texture group belongs to</param>
|
/// <param name="context">GPU context that the texture group belongs to</param>
|
||||||
|
/// <param name="physicalMemory">Physical memory where the <paramref name="storage"/> texture is mapped</param>
|
||||||
/// <param name="storage">The storage texture for this group</param>
|
/// <param name="storage">The storage texture for this group</param>
|
||||||
public TextureGroup(GpuContext context, Texture storage)
|
public TextureGroup(GpuContext context, PhysicalMemory physicalMemory, Texture storage)
|
||||||
{
|
{
|
||||||
Storage = storage;
|
Storage = storage;
|
||||||
_context = context;
|
_context = context;
|
||||||
|
_physicalMemory = physicalMemory;
|
||||||
|
|
||||||
_is3D = storage.Info.Target == Target.Texture3D;
|
_is3D = storage.Info.Target == Target.Texture3D;
|
||||||
_layers = storage.Info.GetSlices();
|
_layers = storage.Info.GetSlices();
|
||||||
@ -211,7 +215,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
int endOffset = (offsetIndex + 1 == _allOffsets.Length) ? (int)Storage.Size : _allOffsets[offsetIndex + 1];
|
int endOffset = (offsetIndex + 1 == _allOffsets.Length) ? (int)Storage.Size : _allOffsets[offsetIndex + 1];
|
||||||
int size = endOffset - offset;
|
int size = endOffset - offset;
|
||||||
|
|
||||||
ReadOnlySpan<byte> data = _context.PhysicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
|
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
|
||||||
|
|
||||||
data = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true);
|
data = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true);
|
||||||
|
|
||||||
@ -561,7 +565,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// <returns>A CpuRegionHandle covering the given range</returns>
|
/// <returns>A CpuRegionHandle covering the given range</returns>
|
||||||
private CpuRegionHandle GenerateHandle(ulong address, ulong size)
|
private CpuRegionHandle GenerateHandle(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
return _context.PhysicalMemory.BeginTracking(address, size);
|
return _physicalMemory.BeginTracking(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
class TexturePool : Pool<Texture, TextureDescriptor>
|
class TexturePool : Pool<Texture, TextureDescriptor>
|
||||||
{
|
{
|
||||||
private int _sequenceNumber;
|
private int _sequenceNumber;
|
||||||
|
private readonly GpuChannel _channel;
|
||||||
private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>();
|
private readonly ConcurrentQueue<Texture> _dereferenceQueue = new ConcurrentQueue<Texture>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -24,9 +25,13 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// Constructs a new instance of the texture pool.
|
/// Constructs a new instance of the texture pool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the texture pool belongs to</param>
|
/// <param name="context">GPU context that the texture pool belongs to</param>
|
||||||
|
/// <param name="channel">GPU channel that the texture pool belongs to</param>
|
||||||
/// <param name="address">Address of the texture pool in guest memory</param>
|
/// <param name="address">Address of the texture pool in guest memory</param>
|
||||||
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
|
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
|
||||||
public TexturePool(GpuContext context, ulong address, int maximumId) : base(context, address, maximumId) { }
|
public TexturePool(GpuContext context, GpuChannel channel, ulong address, int maximumId) : base(context, channel.MemoryManager.Physical, address, maximumId)
|
||||||
|
{
|
||||||
|
_channel = channel;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the texture with the given ID.
|
/// Gets the texture with the given ID.
|
||||||
@ -57,7 +62,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
ProcessDereferenceQueue();
|
ProcessDereferenceQueue();
|
||||||
|
|
||||||
texture = Context.Methods.TextureCache.FindOrCreateTexture(TextureSearchFlags.ForSampler, info, layerSize);
|
texture = PhysicalMemory.TextureCache.FindOrCreateTexture(_channel.MemoryManager, TextureSearchFlags.ForSampler, info, layerSize);
|
||||||
|
|
||||||
// If this happens, then the texture address is invalid, we can't add it to the cache.
|
// If this happens, then the texture address is invalid, we can't add it to the cache.
|
||||||
if (texture == null)
|
if (texture == null)
|
||||||
@ -148,7 +153,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (texture != null)
|
if (texture != null)
|
||||||
{
|
{
|
||||||
TextureDescriptor descriptor = Context.PhysicalMemory.Read<TextureDescriptor>(address);
|
TextureDescriptor descriptor = PhysicalMemory.Read<TextureDescriptor>(address);
|
||||||
|
|
||||||
// If the descriptors are the same, the texture is the same,
|
// If the descriptors are the same, the texture is the same,
|
||||||
// we don't need to remove as it was not modified. Just continue.
|
// we don't need to remove as it was not modified. Just continue.
|
||||||
@ -214,7 +219,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
|
if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
|
||||||
{
|
{
|
||||||
if (Context.MemoryManager.IsMapped(gpuVa) && (int)format > 0)
|
if (gpuVa != 0 && (int)format > 0)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb}).");
|
Logger.Error?.Print(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb}).");
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,12 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
/// This can keep multiple texture pools, and return the current one as needed.
|
/// This can keep multiple texture pools, and return the current one as needed.
|
||||||
/// It is useful for applications that uses multiple texture pools.
|
/// It is useful for applications that uses multiple texture pools.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class TexturePoolCache : IDisposable
|
class TexturePoolCache
|
||||||
{
|
{
|
||||||
private const int MaxCapacity = 4;
|
private const int MaxCapacity = 4;
|
||||||
|
|
||||||
private GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
|
private readonly LinkedList<TexturePool> _pools;
|
||||||
private LinkedList<TexturePool> _pools;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a new instance of the texture pool.
|
/// Constructs a new instance of the texture pool.
|
||||||
@ -23,17 +22,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
public TexturePoolCache(GpuContext context)
|
public TexturePoolCache(GpuContext context)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
|
|
||||||
_pools = new LinkedList<TexturePool>();
|
_pools = new LinkedList<TexturePool>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds a cache texture pool, or creates a new one if not found.
|
/// Finds a cache texture pool, or creates a new one if not found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="channel">GPU channel that the texture pool cache belongs to</param>
|
||||||
/// <param name="address">Start address of the texture pool</param>
|
/// <param name="address">Start address of the texture pool</param>
|
||||||
/// <param name="maximumId">Maximum ID of the texture pool</param>
|
/// <param name="maximumId">Maximum ID of the texture pool</param>
|
||||||
/// <returns>The found or newly created texture pool</returns>
|
/// <returns>The found or newly created texture pool</returns>
|
||||||
public TexturePool FindOrCreate(ulong address, int maximumId)
|
public TexturePool FindOrCreate(GpuChannel channel, ulong address, int maximumId)
|
||||||
{
|
{
|
||||||
TexturePool pool;
|
TexturePool pool;
|
||||||
|
|
||||||
@ -56,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If not found, create a new one.
|
// If not found, create a new one.
|
||||||
pool = new TexturePool(_context, address, maximumId);
|
pool = new TexturePool(_context, channel, address, maximumId);
|
||||||
|
|
||||||
pool.CacheNode = _pools.AddLast(pool);
|
pool.CacheNode = _pools.AddLast(pool);
|
||||||
|
|
||||||
@ -73,19 +72,5 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
|
|
||||||
return pool;
|
return pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disposes the texture pool cache.
|
|
||||||
/// It's an error to use the texture pool cache after disposal.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
foreach (TexturePool pool in _pools)
|
|
||||||
{
|
|
||||||
pool.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_pools.Clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,9 +13,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class Buffer : IRange, IDisposable
|
class Buffer : IRange, IDisposable
|
||||||
{
|
{
|
||||||
private static ulong GranularBufferThreshold = 4096;
|
private const ulong GranularBufferThreshold = 4096;
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
|
private readonly PhysicalMemory _physicalMemory;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Host buffer handle.
|
/// Host buffer handle.
|
||||||
@ -68,12 +69,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// Creates a new instance of the buffer.
|
/// Creates a new instance of the buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the buffer belongs to</param>
|
/// <param name="context">GPU context that the buffer belongs to</param>
|
||||||
|
/// <param name="physicalMemory">Physical memory where the buffer is mapped</param>
|
||||||
/// <param name="address">Start address of the buffer</param>
|
/// <param name="address">Start address of the buffer</param>
|
||||||
/// <param name="size">Size of the buffer in bytes</param>
|
/// <param name="size">Size of the buffer in bytes</param>
|
||||||
/// <param name="baseBuffers">Buffers which this buffer contains, and will inherit tracking handles from</param>
|
/// <param name="baseBuffers">Buffers which this buffer contains, and will inherit tracking handles from</param>
|
||||||
public Buffer(GpuContext context, ulong address, ulong size, IEnumerable<Buffer> baseBuffers = null)
|
public Buffer(GpuContext context, PhysicalMemory physicalMemory, ulong address, ulong size, IEnumerable<Buffer> baseBuffers = null)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
|
_physicalMemory = physicalMemory;
|
||||||
Address = address;
|
Address = address;
|
||||||
Size = size;
|
Size = size;
|
||||||
|
|
||||||
@ -100,11 +103,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (_useGranular)
|
if (_useGranular)
|
||||||
{
|
{
|
||||||
_memoryTrackingGranular = context.PhysicalMemory.BeginGranularTracking(address, size, baseHandles);
|
_memoryTrackingGranular = physicalMemory.BeginGranularTracking(address, size, baseHandles);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_memoryTracking = context.PhysicalMemory.BeginTracking(address, size);
|
_memoryTracking = physicalMemory.BeginTracking(address, size);
|
||||||
|
|
||||||
if (baseHandles != null)
|
if (baseHandles != null)
|
||||||
{
|
{
|
||||||
@ -207,7 +210,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_context.Renderer.SetBufferData(Handle, 0, _context.PhysicalMemory.GetSpan(Address, (int)Size));
|
_context.Renderer.SetBufferData(Handle, 0, _physicalMemory.GetSpan(Address, (int)Size));
|
||||||
}
|
}
|
||||||
|
|
||||||
_sequenceNumber = _context.SequenceNumber;
|
_sequenceNumber = _context.SequenceNumber;
|
||||||
@ -363,7 +366,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
int offset = (int)(mAddress - Address);
|
int offset = (int)(mAddress - Address);
|
||||||
|
|
||||||
_context.Renderer.SetBufferData(Handle, offset, _context.PhysicalMemory.GetSpan(mAddress, (int)mSize));
|
_context.Renderer.SetBufferData(Handle, offset, _physicalMemory.GetSpan(mAddress, (int)mSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -412,7 +415,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
byte[] data = _context.Renderer.GetBufferData(Handle, offset, (int)size);
|
byte[] data = _context.Renderer.GetBufferData(Handle, offset, (int)size);
|
||||||
|
|
||||||
// TODO: When write tracking shaders, they will need to be aware of changes in overlapping buffers.
|
// TODO: When write tracking shaders, they will need to be aware of changes in overlapping buffers.
|
||||||
_context.PhysicalMemory.WriteUntracked(address, data);
|
_physicalMemory.WriteUntracked(address, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -18,7 +18,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
private const ulong BufferAlignmentSize = 0x1000;
|
private const ulong BufferAlignmentSize = 0x1000;
|
||||||
private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
|
private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
|
||||||
|
|
||||||
private GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
|
private readonly PhysicalMemory _physicalMemory;
|
||||||
|
|
||||||
private readonly RangeList<Buffer> _buffers;
|
private readonly RangeList<Buffer> _buffers;
|
||||||
|
|
||||||
@ -32,9 +33,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// Creates a new instance of the buffer manager.
|
/// Creates a new instance of the buffer manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">The GPU context that the buffer manager belongs to</param>
|
/// <param name="context">The GPU context that the buffer manager belongs to</param>
|
||||||
public BufferCache(GpuContext context)
|
/// <param name="physicalMemory">Physical memory where the cached buffers are mapped</param>
|
||||||
|
public BufferCache(GpuContext context, PhysicalMemory physicalMemory)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
|
_physicalMemory = physicalMemory;
|
||||||
|
|
||||||
_buffers = new RangeList<Buffer>();
|
_buffers = new RangeList<Buffer>();
|
||||||
|
|
||||||
@ -53,7 +56,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
Buffer[] overlaps = new Buffer[10];
|
Buffer[] overlaps = new Buffer[10];
|
||||||
int overlapCount;
|
int overlapCount;
|
||||||
|
|
||||||
ulong address = _context.MemoryManager.Translate(e.Address);
|
ulong address = ((MemoryManager)sender).Translate(e.Address);
|
||||||
ulong size = e.Size;
|
ulong size = e.Size;
|
||||||
|
|
||||||
lock (_buffers)
|
lock (_buffers)
|
||||||
@ -71,17 +74,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// Performs address translation of the GPU virtual address, and creates a
|
/// Performs address translation of the GPU virtual address, and creates a
|
||||||
/// new buffer, if needed, for the specified range.
|
/// new buffer, if needed, for the specified range.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
||||||
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
||||||
/// <param name="size">Size in bytes of the buffer</param>
|
/// <param name="size">Size in bytes of the buffer</param>
|
||||||
/// <returns>CPU virtual address of the buffer, after address translation</returns>
|
/// <returns>CPU virtual address of the buffer, after address translation</returns>
|
||||||
public ulong TranslateAndCreateBuffer(ulong gpuVa, ulong size)
|
public ulong TranslateAndCreateBuffer(MemoryManager memoryManager, ulong gpuVa, ulong size)
|
||||||
{
|
{
|
||||||
if (gpuVa == 0)
|
if (gpuVa == 0)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ulong address = _context.MemoryManager.Translate(gpuVa);
|
ulong address = memoryManager.Translate(gpuVa);
|
||||||
|
|
||||||
if (address == MemoryManager.PteUnmapped)
|
if (address == MemoryManager.PteUnmapped)
|
||||||
{
|
{
|
||||||
@ -122,15 +126,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// The buffer lookup for this function is cached in a dictionary for quick access, which
|
/// The buffer lookup for this function is cached in a dictionary for quick access, which
|
||||||
/// accelerates common UBO updates.
|
/// accelerates common UBO updates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
||||||
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
||||||
/// <param name="size">Size in bytes of the buffer</param>
|
/// <param name="size">Size in bytes of the buffer</param>
|
||||||
public void ForceDirty(ulong gpuVa, ulong size)
|
public void ForceDirty(MemoryManager memoryManager, ulong gpuVa, ulong size)
|
||||||
{
|
{
|
||||||
BufferCacheEntry result;
|
if (!_dirtyCache.TryGetValue(gpuVa, out BufferCacheEntry result) ||
|
||||||
|
result.EndGpuAddress < gpuVa + size ||
|
||||||
if (!_dirtyCache.TryGetValue(gpuVa, out result) || result.EndGpuAddress < gpuVa + size || result.UnmappedSequence != result.Buffer.UnmappedSequence)
|
result.UnmappedSequence != result.Buffer.UnmappedSequence)
|
||||||
{
|
{
|
||||||
ulong address = TranslateAndCreateBuffer(gpuVa, size);
|
ulong address = TranslateAndCreateBuffer(memoryManager, gpuVa, size);
|
||||||
result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size));
|
result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size));
|
||||||
|
|
||||||
_dirtyCache[gpuVa] = result;
|
_dirtyCache[gpuVa] = result;
|
||||||
@ -179,7 +184,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Buffer newBuffer = new Buffer(_context, address, endAddress - address, _bufferOverlaps.Take(overlapsCount));
|
Buffer newBuffer = new Buffer(_context, _physicalMemory, address, endAddress - address, _bufferOverlaps.Take(overlapsCount));
|
||||||
|
|
||||||
lock (_buffers)
|
lock (_buffers)
|
||||||
{
|
{
|
||||||
@ -207,7 +212,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// No overlap, just create a new buffer.
|
// No overlap, just create a new buffer.
|
||||||
Buffer buffer = new Buffer(_context, address, size);
|
Buffer buffer = new Buffer(_context, _physicalMemory, address, size);
|
||||||
|
|
||||||
lock (_buffers)
|
lock (_buffers)
|
||||||
{
|
{
|
||||||
@ -235,13 +240,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// This does a GPU side copy.
|
/// This does a GPU side copy.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
||||||
/// <param name="srcVa">GPU virtual address of the copy source</param>
|
/// <param name="srcVa">GPU virtual address of the copy source</param>
|
||||||
/// <param name="dstVa">GPU virtual address of the copy destination</param>
|
/// <param name="dstVa">GPU virtual address of the copy destination</param>
|
||||||
/// <param name="size">Size in bytes of the copy</param>
|
/// <param name="size">Size in bytes of the copy</param>
|
||||||
public void CopyBuffer(GpuVa srcVa, GpuVa dstVa, ulong size)
|
public void CopyBuffer(MemoryManager memoryManager, GpuVa srcVa, GpuVa dstVa, ulong size)
|
||||||
{
|
{
|
||||||
ulong srcAddress = TranslateAndCreateBuffer(srcVa.Pack(), size);
|
ulong srcAddress = TranslateAndCreateBuffer(memoryManager, srcVa.Pack(), size);
|
||||||
ulong dstAddress = TranslateAndCreateBuffer(dstVa.Pack(), size);
|
ulong dstAddress = TranslateAndCreateBuffer(memoryManager, dstVa.Pack(), size);
|
||||||
|
|
||||||
Buffer srcBuffer = GetBuffer(srcAddress, size);
|
Buffer srcBuffer = GetBuffer(srcAddress, size);
|
||||||
Buffer dstBuffer = GetBuffer(dstAddress, size);
|
Buffer dstBuffer = GetBuffer(dstAddress, size);
|
||||||
@ -265,7 +271,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
// Optimization: If the data being copied is already in memory, then copy it directly instead of flushing from GPU.
|
// Optimization: If the data being copied is already in memory, then copy it directly instead of flushing from GPU.
|
||||||
|
|
||||||
dstBuffer.ClearModified(dstAddress, size);
|
dstBuffer.ClearModified(dstAddress, size);
|
||||||
_context.PhysicalMemory.WriteUntracked(dstAddress, _context.PhysicalMemory.GetSpan(srcAddress, (int)size));
|
memoryManager.Physical.WriteUntracked(dstAddress, memoryManager.Physical.GetSpan(srcAddress, (int)size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,12 +281,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Both the address and size must be aligned to 4 bytes.
|
/// Both the address and size must be aligned to 4 bytes.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
/// <param name="memoryManager">GPU memory manager where the buffer is mapped</param>
|
||||||
/// <param name="gpuVa">GPU virtual address of the region to clear</param>
|
/// <param name="gpuVa">GPU virtual address of the region to clear</param>
|
||||||
/// <param name="size">Number of bytes to clear</param>
|
/// <param name="size">Number of bytes to clear</param>
|
||||||
/// <param name="value">Value to be written into the buffer</param>
|
/// <param name="value">Value to be written into the buffer</param>
|
||||||
public void ClearBuffer(GpuVa gpuVa, ulong size, uint value)
|
public void ClearBuffer(MemoryManager memoryManager, GpuVa gpuVa, ulong size, uint value)
|
||||||
{
|
{
|
||||||
ulong address = TranslateAndCreateBuffer(gpuVa.Pack(), size);
|
ulong address = TranslateAndCreateBuffer(memoryManager, gpuVa.Pack(), size);
|
||||||
|
|
||||||
Buffer buffer = GetBuffer(address, size);
|
Buffer buffer = GetBuffer(address, size);
|
||||||
|
|
||||||
|
@ -12,11 +12,12 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Buffer manager.
|
/// Buffer manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class BufferManager : IDisposable
|
class BufferManager
|
||||||
{
|
{
|
||||||
private const int StackToHeapThreshold = 16;
|
private const int StackToHeapThreshold = 16;
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
|
private readonly GpuChannel _channel;
|
||||||
|
|
||||||
private IndexBuffer _indexBuffer;
|
private IndexBuffer _indexBuffer;
|
||||||
private readonly VertexBuffer[] _vertexBuffers;
|
private readonly VertexBuffer[] _vertexBuffers;
|
||||||
@ -106,9 +107,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// Creates a new instance of the buffer manager.
|
/// Creates a new instance of the buffer manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="context">GPU context that the buffer manager belongs to</param>
|
/// <param name="context">GPU context that the buffer manager belongs to</param>
|
||||||
public BufferManager(GpuContext context)
|
/// <param name="channel">GPU channel that the buffer manager belongs to</param>
|
||||||
|
public BufferManager(GpuContext context, GpuChannel channel)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
|
_channel = channel;
|
||||||
|
|
||||||
_vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
|
_vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
|
||||||
|
|
||||||
@ -127,8 +130,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
|
|
||||||
_bufferTextures = new List<BufferTextureBinding>();
|
_bufferTextures = new List<BufferTextureBinding>();
|
||||||
|
|
||||||
context.Methods.BufferCache.NotifyBuffersModified += Rebind;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -140,7 +141,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="type">Type of each index buffer element</param>
|
/// <param name="type">Type of each index buffer element</param>
|
||||||
public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
|
public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
|
||||||
{
|
{
|
||||||
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
|
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
|
||||||
|
|
||||||
_indexBuffer.Address = address;
|
_indexBuffer.Address = address;
|
||||||
_indexBuffer.Size = size;
|
_indexBuffer.Size = size;
|
||||||
@ -171,7 +172,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
|
/// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
|
||||||
public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
|
public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
|
||||||
{
|
{
|
||||||
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
|
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
|
||||||
|
|
||||||
_vertexBuffers[index].Address = address;
|
_vertexBuffers[index].Address = address;
|
||||||
_vertexBuffers[index].Size = size;
|
_vertexBuffers[index].Size = size;
|
||||||
@ -199,7 +200,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="size">Size in bytes of the transform feedback buffer</param>
|
/// <param name="size">Size in bytes of the transform feedback buffer</param>
|
||||||
public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
|
public void SetTransformFeedbackBuffer(int index, ulong gpuVa, ulong size)
|
||||||
{
|
{
|
||||||
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
|
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
|
||||||
|
|
||||||
_transformFeedbackBuffers[index] = new BufferBounds(address, size);
|
_transformFeedbackBuffers[index] = new BufferBounds(address, size);
|
||||||
_transformFeedbackBuffersDirty = true;
|
_transformFeedbackBuffersDirty = true;
|
||||||
@ -219,7 +220,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
|
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
|
||||||
|
|
||||||
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
|
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
|
||||||
|
|
||||||
_cpStorageBuffers.SetBounds(index, address, size, flags);
|
_cpStorageBuffers.SetBounds(index, address, size, flags);
|
||||||
}
|
}
|
||||||
@ -239,7 +240,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
|
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
|
||||||
|
|
||||||
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
|
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
|
||||||
|
|
||||||
if (_gpStorageBuffers[stage].Buffers[index].Address != address ||
|
if (_gpStorageBuffers[stage].Buffers[index].Address != address ||
|
||||||
_gpStorageBuffers[stage].Buffers[index].Size != size)
|
_gpStorageBuffers[stage].Buffers[index].Size != size)
|
||||||
@ -259,7 +260,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="size">Size in bytes of the storage buffer</param>
|
/// <param name="size">Size in bytes of the storage buffer</param>
|
||||||
public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
|
public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
|
||||||
{
|
{
|
||||||
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
|
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
|
||||||
|
|
||||||
_cpUniformBuffers.SetBounds(index, address, size);
|
_cpUniformBuffers.SetBounds(index, address, size);
|
||||||
}
|
}
|
||||||
@ -274,7 +275,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="size">Size in bytes of the storage buffer</param>
|
/// <param name="size">Size in bytes of the storage buffer</param>
|
||||||
public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
|
public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
|
||||||
{
|
{
|
||||||
ulong address = _context.Methods.BufferCache.TranslateAndCreateBuffer(gpuVa, size);
|
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
|
||||||
|
|
||||||
_gpUniformBuffers[stage].SetBounds(index, address, size);
|
_gpUniformBuffers[stage].SetBounds(index, address, size);
|
||||||
_gpUniformBuffersDirty = true;
|
_gpUniformBuffersDirty = true;
|
||||||
@ -422,7 +423,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
// The storage buffer size is not reliable (it might be lower than the actual size),
|
// The storage buffer size is not reliable (it might be lower than the actual size),
|
||||||
// so we bind the entire buffer to allow otherwise out of range accesses to work.
|
// so we bind the entire buffer to allow otherwise out of range accesses to work.
|
||||||
sRanges[bindingInfo.Binding] = _context.Methods.BufferCache.GetBufferRangeTillEnd(
|
sRanges[bindingInfo.Binding] = _channel.MemoryManager.Physical.BufferCache.GetBufferRangeTillEnd(
|
||||||
bounds.Address,
|
bounds.Address,
|
||||||
bounds.Size,
|
bounds.Size,
|
||||||
bounds.Flags.HasFlag(BufferUsageFlags.Write));
|
bounds.Flags.HasFlag(BufferUsageFlags.Write));
|
||||||
@ -443,7 +444,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (bounds.Address != 0)
|
if (bounds.Address != 0)
|
||||||
{
|
{
|
||||||
uRanges[bindingInfo.Binding] = _context.Methods.BufferCache.GetBufferRange(bounds.Address, bounds.Size);
|
uRanges[bindingInfo.Binding] = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(bounds.Address, bounds.Size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,7 +466,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
foreach (var binding in _bufferTextures)
|
foreach (var binding in _bufferTextures)
|
||||||
{
|
{
|
||||||
var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
|
var isStore = binding.BindingInfo.Flags.HasFlag(TextureUsageFlags.ImageStore);
|
||||||
var range = _context.Methods.BufferCache.GetBufferRange(binding.Address, binding.Size, isStore);
|
var range = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(binding.Address, binding.Size, isStore);
|
||||||
binding.Texture.SetStorage(range);
|
binding.Texture.SetStorage(range);
|
||||||
|
|
||||||
// The texture must be rebound to use the new storage if it was updated.
|
// The texture must be rebound to use the new storage if it was updated.
|
||||||
@ -496,14 +497,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
if (_indexBuffer.Address != 0)
|
if (_indexBuffer.Address != 0)
|
||||||
{
|
{
|
||||||
BufferRange buffer = _context.Methods.BufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
|
BufferRange buffer = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
|
_context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_indexBuffer.Address != 0)
|
else if (_indexBuffer.Address != 0)
|
||||||
{
|
{
|
||||||
_context.Methods.BufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
|
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint vbEnableMask = _vertexBuffersEnableMask;
|
uint vbEnableMask = _vertexBuffersEnableMask;
|
||||||
@ -523,7 +524,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferRange buffer = _context.Methods.BufferCache.GetBufferRange(vb.Address, vb.Size);
|
BufferRange buffer = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(vb.Address, vb.Size);
|
||||||
|
|
||||||
vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
|
vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
|
||||||
}
|
}
|
||||||
@ -541,7 +542,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.Methods.BufferCache.SynchronizeBufferRange(vb.Address, vb.Size);
|
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(vb.Address, vb.Size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,7 +562,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
tfbs[index] = _context.Methods.BufferCache.GetBufferRange(tfb.Address, tfb.Size);
|
tfbs[index] = _channel.MemoryManager.Physical.BufferCache.GetBufferRange(tfb.Address, tfb.Size);
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
|
_context.Renderer.Pipeline.SetTransformFeedbackBuffers(tfbs);
|
||||||
@ -577,7 +578,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.Methods.BufferCache.SynchronizeBufferRange(tfb.Address, tfb.Size);
|
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(tfb.Address, tfb.Size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -633,8 +634,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
|
var isWrite = bounds.Flags.HasFlag(BufferUsageFlags.Write);
|
||||||
ranges[bindingInfo.Binding] = isStorage
|
ranges[bindingInfo.Binding] = isStorage
|
||||||
? _context.Methods.BufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
|
? _channel.MemoryManager.Physical.BufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
|
||||||
: _context.Methods.BufferCache.GetBufferRange(bounds.Address, bounds.Size, isWrite);
|
: _channel.MemoryManager.Physical.BufferCache.GetBufferRange(bounds.Address, bounds.Size, isWrite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -670,7 +671,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.Methods.BufferCache.SynchronizeBufferRange(bounds.Address, bounds.Size);
|
_channel.MemoryManager.Physical.BufferCache.SynchronizeBufferRange(bounds.Address, bounds.Size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -686,7 +687,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
|
/// <param name="isImage">Whether the binding is for an image or a sampler</param>
|
||||||
public void SetBufferTextureStorage(ITexture texture, ulong address, ulong size, TextureBindingInfo bindingInfo, Format format, bool isImage)
|
public void SetBufferTextureStorage(ITexture texture, ulong address, ulong size, TextureBindingInfo bindingInfo, Format format, bool isImage)
|
||||||
{
|
{
|
||||||
_context.Methods.BufferCache.CreateBuffer(address, size);
|
_channel.MemoryManager.Physical.BufferCache.CreateBuffer(address, size);
|
||||||
|
|
||||||
_bufferTextures.Add(new BufferTextureBinding(texture, address, size, bindingInfo, format, isImage));
|
_bufferTextures.Add(new BufferTextureBinding(texture, address, size, bindingInfo, format, isImage));
|
||||||
}
|
}
|
||||||
@ -698,14 +699,5 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
_rebind = true;
|
_rebind = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disposes the buffer manager.
|
|
||||||
/// It is an error to use the buffer manager after disposal.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_context.Methods.BufferCache.NotifyBuffersModified -= Rebind;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,15 +34,28 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
public event EventHandler<UnmapEventArgs> MemoryUnmapped;
|
public event EventHandler<UnmapEventArgs> MemoryUnmapped;
|
||||||
|
|
||||||
private GpuContext _context;
|
/// <summary>
|
||||||
|
/// Physical memory where the virtual memory is mapped into.
|
||||||
|
/// </summary>
|
||||||
|
internal PhysicalMemory Physical { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cache of GPU counters.
|
||||||
|
/// </summary>
|
||||||
|
internal CounterCache CounterCache { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the GPU memory manager.
|
/// Creates a new instance of the GPU memory manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MemoryManager(GpuContext context)
|
/// <param name="physicalMemory">Physical memory that this memory manager will map into</param>
|
||||||
|
internal MemoryManager(PhysicalMemory physicalMemory)
|
||||||
{
|
{
|
||||||
_context = context;
|
Physical = physicalMemory;
|
||||||
|
CounterCache = new CounterCache();
|
||||||
_pageTable = new ulong[PtLvl0Size][];
|
_pageTable = new ulong[PtLvl0Size][];
|
||||||
|
MemoryUnmapped += Physical.TextureCache.MemoryUnmappedHandler;
|
||||||
|
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
|
||||||
|
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -67,7 +80,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
if (IsContiguous(va, size))
|
if (IsContiguous(va, size))
|
||||||
{
|
{
|
||||||
return _context.PhysicalMemory.GetSpan(Translate(va), size, tracked);
|
return Physical.GetSpan(Translate(va), size, tracked);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -100,7 +113,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
|
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
|
||||||
|
|
||||||
_context.PhysicalMemory.GetSpan(pa, size, tracked).CopyTo(data.Slice(0, size));
|
Physical.GetSpan(pa, size, tracked).CopyTo(data.Slice(0, size));
|
||||||
|
|
||||||
offset += size;
|
offset += size;
|
||||||
}
|
}
|
||||||
@ -111,7 +124,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
size = Math.Min(data.Length - offset, (int)PageSize);
|
size = Math.Min(data.Length - offset, (int)PageSize);
|
||||||
|
|
||||||
_context.PhysicalMemory.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size));
|
Physical.GetSpan(pa, size, tracked).CopyTo(data.Slice(offset, size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +138,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
if (IsContiguous(va, size))
|
if (IsContiguous(va, size))
|
||||||
{
|
{
|
||||||
return _context.PhysicalMemory.GetWritableRegion(Translate(va), size);
|
return Physical.GetWritableRegion(Translate(va), size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -155,7 +168,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="data">The data to be written</param>
|
/// <param name="data">The data to be written</param>
|
||||||
public void Write(ulong va, ReadOnlySpan<byte> data)
|
public void Write(ulong va, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
WriteImpl(va, data, _context.PhysicalMemory.Write);
|
WriteImpl(va, data, Physical.Write);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -165,7 +178,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="data">The data to be written</param>
|
/// <param name="data">The data to be written</param>
|
||||||
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
|
public void WriteUntracked(ulong va, ReadOnlySpan<byte> data)
|
||||||
{
|
{
|
||||||
WriteImpl(va, data, _context.PhysicalMemory.WriteUntracked);
|
WriteImpl(va, data, Physical.WriteUntracked);
|
||||||
}
|
}
|
||||||
|
|
||||||
private delegate void WriteCallback(ulong address, ReadOnlySpan<byte> data);
|
private delegate void WriteCallback(ulong address, ReadOnlySpan<byte> data);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.Cpu.Tracking;
|
using Ryujinx.Cpu.Tracking;
|
||||||
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
|
using Ryujinx.Graphics.Gpu.Shader;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using Ryujinx.Memory.Range;
|
using Ryujinx.Memory.Range;
|
||||||
using Ryujinx.Memory.Tracking;
|
using Ryujinx.Memory.Tracking;
|
||||||
@ -7,6 +9,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Gpu.Memory
|
namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
{
|
{
|
||||||
@ -18,20 +21,63 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
public const int PageSize = 0x1000;
|
public const int PageSize = 0x1000;
|
||||||
|
|
||||||
|
private readonly GpuContext _context;
|
||||||
private IVirtualMemoryManagerTracked _cpuMemory;
|
private IVirtualMemoryManagerTracked _cpuMemory;
|
||||||
|
private int _referenceCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// In-memory shader cache.
|
||||||
|
/// </summary>
|
||||||
|
public ShaderCache ShaderCache { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// GPU buffer manager.
|
||||||
|
/// </summary>
|
||||||
|
public BufferCache BufferCache { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// GPU texture manager.
|
||||||
|
/// </summary>
|
||||||
|
public TextureCache TextureCache { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the physical memory.
|
/// Creates a new instance of the physical memory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="context">GPU context that the physical memory belongs to</param>
|
||||||
/// <param name="cpuMemory">CPU memory manager of the application process</param>
|
/// <param name="cpuMemory">CPU memory manager of the application process</param>
|
||||||
public PhysicalMemory(IVirtualMemoryManagerTracked cpuMemory)
|
public PhysicalMemory(GpuContext context, IVirtualMemoryManagerTracked cpuMemory)
|
||||||
{
|
{
|
||||||
|
_context = context;
|
||||||
_cpuMemory = cpuMemory;
|
_cpuMemory = cpuMemory;
|
||||||
|
ShaderCache = new ShaderCache(context);
|
||||||
|
BufferCache = new BufferCache(context, this);
|
||||||
|
TextureCache = new TextureCache(context, this);
|
||||||
|
|
||||||
if (_cpuMemory is IRefCounted rc)
|
if (cpuMemory is IRefCounted rc)
|
||||||
{
|
{
|
||||||
rc.IncrementReferenceCount();
|
rc.IncrementReferenceCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_referenceCount = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Increments the memory reference count.
|
||||||
|
/// </summary>
|
||||||
|
public void IncrementReferenceCount()
|
||||||
|
{
|
||||||
|
Interlocked.Increment(ref _referenceCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decrements the memory reference count.
|
||||||
|
/// </summary>
|
||||||
|
public void DecrementReferenceCount()
|
||||||
|
{
|
||||||
|
if (Interlocked.Decrement(ref _referenceCount) == 0 && _cpuMemory is IRefCounted rc)
|
||||||
|
{
|
||||||
|
rc.DecrementReferenceCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -147,7 +193,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="range">Ranges of physical memory where the data is located</param>
|
/// <param name="range">Ranges of physical memory where the data is located</param>
|
||||||
/// <param name="data">Data to be written</param>
|
/// <param name="data">Data to be written</param>
|
||||||
/// <param name="writeCallback">Callback method that will perform the write</param>
|
/// <param name="writeCallback">Callback method that will perform the write</param>
|
||||||
private void WriteImpl(MultiRange range, ReadOnlySpan<byte> data, WriteCallback writeCallback)
|
private static void WriteImpl(MultiRange range, ReadOnlySpan<byte> data, WriteCallback writeCallback)
|
||||||
{
|
{
|
||||||
if (range.Count == 1)
|
if (range.Count == 1)
|
||||||
{
|
{
|
||||||
@ -227,12 +273,20 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_cpuMemory is IRefCounted rc)
|
_context.DeferredActions.Enqueue(Destroy);
|
||||||
{
|
|
||||||
rc.DecrementReferenceCount();
|
|
||||||
|
|
||||||
_cpuMemory = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs disposal of the host GPU caches with resources mapped on this physical memory.
|
||||||
|
/// This must only be called from the render thread.
|
||||||
|
/// </summary>
|
||||||
|
private void Destroy()
|
||||||
|
{
|
||||||
|
ShaderCache.Dispose();
|
||||||
|
BufferCache.Dispose();
|
||||||
|
TextureCache.Dispose();
|
||||||
|
|
||||||
|
DecrementReferenceCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <returns>Data at the memory location</returns>
|
/// <returns>Data at the memory location</returns>
|
||||||
public override T MemoryRead<T>(ulong address)
|
public override T MemoryRead<T>(ulong address)
|
||||||
{
|
{
|
||||||
return _context.MemoryManager.Read<T>(address);
|
return _state.Channel.MemoryManager.Read<T>(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <returns>True if the address is mapped, false otherwise</returns>
|
/// <returns>True if the address is mapped, false otherwise</returns>
|
||||||
public bool MemoryMapped(ulong address)
|
public bool MemoryMapped(ulong address)
|
||||||
{
|
{
|
||||||
return _context.MemoryManager.IsMapped(address);
|
return _state.Channel.MemoryManager.IsMapped(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Graphics.Gpu.Shader.Cache;
|
using Ryujinx.Graphics.Gpu.Shader.Cache;
|
||||||
using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
|
using Ryujinx.Graphics.Gpu.Shader.Cache.Definition;
|
||||||
using Ryujinx.Graphics.Gpu.State;
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
@ -492,7 +493,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
foreach (ShaderBundle cachedCpShader in list)
|
foreach (ShaderBundle cachedCpShader in list)
|
||||||
{
|
{
|
||||||
if (IsShaderEqual(cachedCpShader, gpuVa))
|
if (IsShaderEqual(state.Channel.MemoryManager, cachedCpShader, gpuVa))
|
||||||
{
|
{
|
||||||
return cachedCpShader;
|
return cachedCpShader;
|
||||||
}
|
}
|
||||||
@ -527,7 +528,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
isShaderCacheReadOnly = _cacheManager.IsReadOnly;
|
isShaderCacheReadOnly = _cacheManager.IsReadOnly;
|
||||||
|
|
||||||
// Compute hash and prepare data for shader disk cache comparison.
|
// Compute hash and prepare data for shader disk cache comparison.
|
||||||
shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(_context.MemoryManager, shaderContexts);
|
shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(state.Channel.MemoryManager, shaderContexts);
|
||||||
programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries);
|
programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,7 +543,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The shader isn't currently cached, translate it and compile it.
|
// The shader isn't currently cached, translate it and compile it.
|
||||||
ShaderCodeHolder shader = TranslateShader(shaderContexts[0]);
|
ShaderCodeHolder shader = TranslateShader(state.Channel.MemoryManager, shaderContexts[0]);
|
||||||
|
|
||||||
shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, shader.Program.Code);
|
shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, shader.Program.Code);
|
||||||
|
|
||||||
@ -595,7 +596,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
foreach (ShaderBundle cachedGpShaders in list)
|
foreach (ShaderBundle cachedGpShaders in list)
|
||||||
{
|
{
|
||||||
if (IsShaderEqual(cachedGpShaders, addresses))
|
if (IsShaderEqual(state.Channel.MemoryManager, cachedGpShaders, addresses))
|
||||||
{
|
{
|
||||||
return cachedGpShaders;
|
return cachedGpShaders;
|
||||||
}
|
}
|
||||||
@ -647,7 +648,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
isShaderCacheReadOnly = _cacheManager.IsReadOnly;
|
isShaderCacheReadOnly = _cacheManager.IsReadOnly;
|
||||||
|
|
||||||
// Compute hash and prepare data for shader disk cache comparison.
|
// Compute hash and prepare data for shader disk cache comparison.
|
||||||
shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(_context.MemoryManager, shaderContexts);
|
shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(state.Channel.MemoryManager, shaderContexts);
|
||||||
programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries, tfd);
|
programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries, tfd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -664,11 +665,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
// The shader isn't currently cached, translate it and compile it.
|
// The shader isn't currently cached, translate it and compile it.
|
||||||
ShaderCodeHolder[] shaders = new ShaderCodeHolder[Constants.ShaderStages];
|
ShaderCodeHolder[] shaders = new ShaderCodeHolder[Constants.ShaderStages];
|
||||||
|
|
||||||
shaders[0] = TranslateShader(shaderContexts[1], shaderContexts[0]);
|
shaders[0] = TranslateShader(state.Channel.MemoryManager, shaderContexts[1], shaderContexts[0]);
|
||||||
shaders[1] = TranslateShader(shaderContexts[2]);
|
shaders[1] = TranslateShader(state.Channel.MemoryManager, shaderContexts[2]);
|
||||||
shaders[2] = TranslateShader(shaderContexts[3]);
|
shaders[2] = TranslateShader(state.Channel.MemoryManager, shaderContexts[3]);
|
||||||
shaders[3] = TranslateShader(shaderContexts[4]);
|
shaders[3] = TranslateShader(state.Channel.MemoryManager, shaderContexts[4]);
|
||||||
shaders[4] = TranslateShader(shaderContexts[5]);
|
shaders[4] = TranslateShader(state.Channel.MemoryManager, shaderContexts[5]);
|
||||||
|
|
||||||
List<IShader> hostShaders = new List<IShader>();
|
List<IShader> hostShaders = new List<IShader>();
|
||||||
|
|
||||||
@ -724,7 +725,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">Current GPU state</param>
|
/// <param name="state">Current GPU state</param>
|
||||||
/// <returns>Four transform feedback descriptors for the enabled TFBs, or null if TFB is disabled</returns>
|
/// <returns>Four transform feedback descriptors for the enabled TFBs, or null if TFB is disabled</returns>
|
||||||
private TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(GpuState state)
|
private static TransformFeedbackDescriptor[] GetTransformFeedbackDescriptors(GpuState state)
|
||||||
{
|
{
|
||||||
bool tfEnable = state.Get<Boolean32>(MethodOffset.TfEnable);
|
bool tfEnable = state.Get<Boolean32>(MethodOffset.TfEnable);
|
||||||
|
|
||||||
@ -752,21 +753,23 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if compute shader code in memory is equal to the cached shader.
|
/// Checks if compute shader code in memory is equal to the cached shader.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">Memory manager used to access the GPU memory where the shader is located</param>
|
||||||
/// <param name="cpShader">Cached compute shader</param>
|
/// <param name="cpShader">Cached compute shader</param>
|
||||||
/// <param name="gpuVa">GPU virtual address of the shader code in memory</param>
|
/// <param name="gpuVa">GPU virtual address of the shader code in memory</param>
|
||||||
/// <returns>True if the code is different, false otherwise</returns>
|
/// <returns>True if the code is different, false otherwise</returns>
|
||||||
private bool IsShaderEqual(ShaderBundle cpShader, ulong gpuVa)
|
private static bool IsShaderEqual(MemoryManager memoryManager, ShaderBundle cpShader, ulong gpuVa)
|
||||||
{
|
{
|
||||||
return IsShaderEqual(cpShader.Shaders[0], gpuVa);
|
return IsShaderEqual(memoryManager, cpShader.Shaders[0], gpuVa);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if graphics shader code from all stages in memory are equal to the cached shaders.
|
/// Checks if graphics shader code from all stages in memory are equal to the cached shaders.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">Memory manager used to access the GPU memory where the shader is located</param>
|
||||||
/// <param name="gpShaders">Cached graphics shaders</param>
|
/// <param name="gpShaders">Cached graphics shaders</param>
|
||||||
/// <param name="addresses">GPU virtual addresses of all enabled shader stages</param>
|
/// <param name="addresses">GPU virtual addresses of all enabled shader stages</param>
|
||||||
/// <returns>True if the code is different, false otherwise</returns>
|
/// <returns>True if the code is different, false otherwise</returns>
|
||||||
private bool IsShaderEqual(ShaderBundle gpShaders, ShaderAddresses addresses)
|
private static bool IsShaderEqual(MemoryManager memoryManager, ShaderBundle gpShaders, ShaderAddresses addresses)
|
||||||
{
|
{
|
||||||
for (int stage = 0; stage < gpShaders.Shaders.Length; stage++)
|
for (int stage = 0; stage < gpShaders.Shaders.Length; stage++)
|
||||||
{
|
{
|
||||||
@ -783,7 +786,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
case 4: gpuVa = addresses.Fragment; break;
|
case 4: gpuVa = addresses.Fragment; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsShaderEqual(shader, gpuVa, addresses.VertexA))
|
if (!IsShaderEqual(memoryManager, shader, gpuVa, addresses.VertexA))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -795,24 +798,25 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the code of the specified cached shader is different from the code in memory.
|
/// Checks if the code of the specified cached shader is different from the code in memory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">Memory manager used to access the GPU memory where the shader is located</param>
|
||||||
/// <param name="shader">Cached shader to compare with</param>
|
/// <param name="shader">Cached shader to compare with</param>
|
||||||
/// <param name="gpuVa">GPU virtual address of the binary shader code</param>
|
/// <param name="gpuVa">GPU virtual address of the binary shader code</param>
|
||||||
/// <param name="gpuVaA">Optional GPU virtual address of the "Vertex A" binary shader code</param>
|
/// <param name="gpuVaA">Optional GPU virtual address of the "Vertex A" binary shader code</param>
|
||||||
/// <returns>True if the code is different, false otherwise</returns>
|
/// <returns>True if the code is different, false otherwise</returns>
|
||||||
private bool IsShaderEqual(ShaderCodeHolder shader, ulong gpuVa, ulong gpuVaA = 0)
|
private static bool IsShaderEqual(MemoryManager memoryManager, ShaderCodeHolder shader, ulong gpuVa, ulong gpuVaA = 0)
|
||||||
{
|
{
|
||||||
if (shader == null)
|
if (shader == null)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadOnlySpan<byte> memoryCode = _context.MemoryManager.GetSpan(gpuVa, shader.Code.Length);
|
ReadOnlySpan<byte> memoryCode = memoryManager.GetSpan(gpuVa, shader.Code.Length);
|
||||||
|
|
||||||
bool equals = memoryCode.SequenceEqual(shader.Code);
|
bool equals = memoryCode.SequenceEqual(shader.Code);
|
||||||
|
|
||||||
if (equals && shader.Code2 != null)
|
if (equals && shader.Code2 != null)
|
||||||
{
|
{
|
||||||
memoryCode = _context.MemoryManager.GetSpan(gpuVaA, shader.Code2.Length);
|
memoryCode = memoryManager.GetSpan(gpuVaA, shader.Code2.Length);
|
||||||
|
|
||||||
equals = memoryCode.SequenceEqual(shader.Code2);
|
equals = memoryCode.SequenceEqual(shader.Code2);
|
||||||
}
|
}
|
||||||
@ -882,10 +886,14 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Translates a previously generated translator context to something that the host API accepts.
|
/// Translates a previously generated translator context to something that the host API accepts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">Memory manager used to access the GPU memory where the shader is located</param>
|
||||||
/// <param name="translatorContext">Current translator context to translate</param>
|
/// <param name="translatorContext">Current translator context to translate</param>
|
||||||
/// <param name="translatorContext2">Optional translator context of the shader that should be combined</param>
|
/// <param name="translatorContext2">Optional translator context of the shader that should be combined</param>
|
||||||
/// <returns>Compiled graphics shader code</returns>
|
/// <returns>Compiled graphics shader code</returns>
|
||||||
private ShaderCodeHolder TranslateShader(TranslatorContext translatorContext, TranslatorContext translatorContext2 = null)
|
private ShaderCodeHolder TranslateShader(
|
||||||
|
MemoryManager memoryManager,
|
||||||
|
TranslatorContext translatorContext,
|
||||||
|
TranslatorContext translatorContext2 = null)
|
||||||
{
|
{
|
||||||
if (translatorContext == null)
|
if (translatorContext == null)
|
||||||
{
|
{
|
||||||
@ -894,8 +902,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
if (translatorContext2 != null)
|
if (translatorContext2 != null)
|
||||||
{
|
{
|
||||||
byte[] codeA = _context.MemoryManager.GetSpan(translatorContext2.Address, translatorContext2.Size).ToArray();
|
byte[] codeA = memoryManager.GetSpan(translatorContext2.Address, translatorContext2.Size).ToArray();
|
||||||
byte[] codeB = _context.MemoryManager.GetSpan(translatorContext.Address, translatorContext.Size).ToArray();
|
byte[] codeB = memoryManager.GetSpan(translatorContext.Address, translatorContext.Size).ToArray();
|
||||||
|
|
||||||
_dumper.Dump(codeA, compute: false, out string fullPathA, out string codePathA);
|
_dumper.Dump(codeA, compute: false, out string fullPathA, out string codePathA);
|
||||||
_dumper.Dump(codeB, compute: false, out string fullPathB, out string codePathB);
|
_dumper.Dump(codeB, compute: false, out string fullPathB, out string codePathB);
|
||||||
@ -914,7 +922,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
byte[] code = _context.MemoryManager.GetSpan(translatorContext.Address, translatorContext.Size).ToArray();
|
byte[] code = memoryManager.GetSpan(translatorContext.Address, translatorContext.Size).ToArray();
|
||||||
|
|
||||||
_dumper.Dump(code, translatorContext.Stage == ShaderStage.Compute, out string fullPath, out string codePath);
|
_dumper.Dump(code, translatorContext.Stage == ShaderStage.Compute, out string fullPath, out string codePath);
|
||||||
|
|
||||||
|
@ -22,6 +22,11 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private struct PresentationTexture
|
private struct PresentationTexture
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Texture cache where the texture might be located.
|
||||||
|
/// </summary>
|
||||||
|
public TextureCache Cache { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Texture information.
|
/// Texture information.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -55,6 +60,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the presentation texture.
|
/// Creates a new instance of the presentation texture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="cache">Texture cache used to look for the texture to be presented</param>
|
||||||
/// <param name="info">Information of the texture to be presented</param>
|
/// <param name="info">Information of the texture to be presented</param>
|
||||||
/// <param name="range">Physical memory locations where the texture data is located</param>
|
/// <param name="range">Physical memory locations where the texture data is located</param>
|
||||||
/// <param name="crop">Texture crop region</param>
|
/// <param name="crop">Texture crop region</param>
|
||||||
@ -62,6 +68,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// <param name="releaseCallback">Texture release callback</param>
|
/// <param name="releaseCallback">Texture release callback</param>
|
||||||
/// <param name="userObj">User defined object passed to the release callback, can be used to identify the texture</param>
|
/// <param name="userObj">User defined object passed to the release callback, can be used to identify the texture</param>
|
||||||
public PresentationTexture(
|
public PresentationTexture(
|
||||||
|
TextureCache cache,
|
||||||
TextureInfo info,
|
TextureInfo info,
|
||||||
MultiRange range,
|
MultiRange range,
|
||||||
ImageCrop crop,
|
ImageCrop crop,
|
||||||
@ -69,6 +76,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
Action<object> releaseCallback,
|
Action<object> releaseCallback,
|
||||||
object userObj)
|
object userObj)
|
||||||
{
|
{
|
||||||
|
Cache = cache;
|
||||||
Info = info;
|
Info = info;
|
||||||
Range = range;
|
Range = range;
|
||||||
Crop = crop;
|
Crop = crop;
|
||||||
@ -99,6 +107,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// When the texture is presented and not needed anymore, the release callback is called.
|
/// When the texture is presented and not needed anymore, the release callback is called.
|
||||||
/// It's an error to modify the texture after calling this method, before the release callback is called.
|
/// It's an error to modify the texture after calling this method, before the release callback is called.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="pid">Process ID of the process that owns the texture pointed to by <paramref name="address"/></param>
|
||||||
/// <param name="address">CPU virtual address of the texture data</param>
|
/// <param name="address">CPU virtual address of the texture data</param>
|
||||||
/// <param name="width">Texture width</param>
|
/// <param name="width">Texture width</param>
|
||||||
/// <param name="height">Texture height</param>
|
/// <param name="height">Texture height</param>
|
||||||
@ -111,7 +120,9 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// <param name="acquireCallback">Texture acquire callback</param>
|
/// <param name="acquireCallback">Texture acquire callback</param>
|
||||||
/// <param name="releaseCallback">Texture release callback</param>
|
/// <param name="releaseCallback">Texture release callback</param>
|
||||||
/// <param name="userObj">User defined object passed to the release callback</param>
|
/// <param name="userObj">User defined object passed to the release callback</param>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when <paramref name="pid"/> is invalid</exception>
|
||||||
public void EnqueueFrameThreadSafe(
|
public void EnqueueFrameThreadSafe(
|
||||||
|
long pid,
|
||||||
ulong address,
|
ulong address,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
@ -125,6 +136,11 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
Action<object> releaseCallback,
|
Action<object> releaseCallback,
|
||||||
object userObj)
|
object userObj)
|
||||||
{
|
{
|
||||||
|
if (!_context.PhysicalMemoryRegistry.TryGetValue(pid, out var physicalMemory))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The PID is invalid or the process was not registered", nameof(pid));
|
||||||
|
}
|
||||||
|
|
||||||
FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel, 4);
|
FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel, 4);
|
||||||
|
|
||||||
TextureInfo info = new TextureInfo(
|
TextureInfo info = new TextureInfo(
|
||||||
@ -158,7 +174,14 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
|
|
||||||
MultiRange range = new MultiRange(address, (ulong)size);
|
MultiRange range = new MultiRange(address, (ulong)size);
|
||||||
|
|
||||||
_frameQueue.Enqueue(new PresentationTexture(info, range, crop, acquireCallback, releaseCallback, userObj));
|
_frameQueue.Enqueue(new PresentationTexture(
|
||||||
|
physicalMemory.TextureCache,
|
||||||
|
info,
|
||||||
|
range,
|
||||||
|
crop,
|
||||||
|
acquireCallback,
|
||||||
|
releaseCallback,
|
||||||
|
userObj));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -174,7 +197,7 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
{
|
{
|
||||||
pt.AcquireCallback(_context, pt.UserObj);
|
pt.AcquireCallback(_context, pt.UserObj);
|
||||||
|
|
||||||
Texture texture = _context.Methods.TextureCache.FindOrCreateTexture(TextureSearchFlags.WithUpscale, pt.Info, 0, null, pt.Range);
|
Texture texture = pt.Cache.FindOrCreateTexture(null, TextureSearchFlags.WithUpscale, pt.Info, 0, null, pt.Range);
|
||||||
|
|
||||||
texture.SynchronizeMemory();
|
texture.SynchronizeMemory();
|
||||||
|
|
||||||
|
@ -1,27 +1,34 @@
|
|||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
|
using Ryujinx.Graphics.Gpu;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS
|
namespace Ryujinx.HLE.HOS
|
||||||
{
|
{
|
||||||
class ArmProcessContext<T> : IProcessContext where T : class, IVirtualMemoryManager, IMemoryManager
|
class ArmProcessContext<T> : IProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager
|
||||||
{
|
{
|
||||||
|
private readonly long _pid;
|
||||||
|
private readonly GpuContext _gpuContext;
|
||||||
private readonly CpuContext _cpuContext;
|
private readonly CpuContext _cpuContext;
|
||||||
private T _memoryManager;
|
private T _memoryManager;
|
||||||
|
|
||||||
public IVirtualMemoryManager AddressSpace => _memoryManager;
|
public IVirtualMemoryManager AddressSpace => _memoryManager;
|
||||||
|
|
||||||
public ArmProcessContext(T memoryManager, bool for64Bit)
|
public ArmProcessContext(long pid, GpuContext gpuContext, T memoryManager, bool for64Bit)
|
||||||
{
|
{
|
||||||
if (memoryManager is IRefCounted rc)
|
if (memoryManager is IRefCounted rc)
|
||||||
{
|
{
|
||||||
rc.IncrementReferenceCount();
|
rc.IncrementReferenceCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
_memoryManager = memoryManager;
|
gpuContext.RegisterProcess(pid, memoryManager);
|
||||||
|
|
||||||
|
_pid = pid;
|
||||||
|
_gpuContext = gpuContext;
|
||||||
_cpuContext = new CpuContext(memoryManager, for64Bit);
|
_cpuContext = new CpuContext(memoryManager, for64Bit);
|
||||||
|
_memoryManager = memoryManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute(ExecutionContext context, ulong codeAddress)
|
public void Execute(ExecutionContext context, ulong codeAddress)
|
||||||
@ -36,6 +43,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
rc.DecrementReferenceCount();
|
rc.DecrementReferenceCount();
|
||||||
|
|
||||||
_memoryManager = null;
|
_memoryManager = null;
|
||||||
|
_gpuContext.UnregisterProcess(_pid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
|
using Ryujinx.Graphics.Gpu;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
@ -9,19 +10,26 @@ namespace Ryujinx.HLE.HOS
|
|||||||
{
|
{
|
||||||
class ArmProcessContextFactory : IProcessContextFactory
|
class ArmProcessContextFactory : IProcessContextFactory
|
||||||
{
|
{
|
||||||
public IProcessContext Create(KernelContext context, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
|
private readonly GpuContext _gpu;
|
||||||
|
|
||||||
|
public ArmProcessContextFactory(GpuContext gpu)
|
||||||
|
{
|
||||||
|
_gpu = gpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IProcessContext Create(KernelContext context, long pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
|
||||||
{
|
{
|
||||||
MemoryManagerMode mode = context.Device.Configuration.MemoryManagerMode;
|
MemoryManagerMode mode = context.Device.Configuration.MemoryManagerMode;
|
||||||
|
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case MemoryManagerMode.SoftwarePageTable:
|
case MemoryManagerMode.SoftwarePageTable:
|
||||||
return new ArmProcessContext<MemoryManager>(new MemoryManager(addressSpaceSize, invalidAccessHandler), for64Bit);
|
return new ArmProcessContext<MemoryManager>(pid, _gpu, new MemoryManager(addressSpaceSize, invalidAccessHandler), for64Bit);
|
||||||
|
|
||||||
case MemoryManagerMode.HostMapped:
|
case MemoryManagerMode.HostMapped:
|
||||||
case MemoryManagerMode.HostMappedUnsafe:
|
case MemoryManagerMode.HostMappedUnsafe:
|
||||||
bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe;
|
bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe;
|
||||||
return new ArmProcessContext<MemoryManagerHostMapped>(new MemoryManagerHostMapped(addressSpaceSize, unsafeMode, invalidAccessHandler), for64Bit);
|
return new ArmProcessContext<MemoryManagerHostMapped>(pid, _gpu, new MemoryManagerHostMapped(addressSpaceSize, unsafeMode, invalidAccessHandler), for64Bit);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
|
@ -4,6 +4,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
interface IProcessContextFactory
|
interface IProcessContextFactory
|
||||||
{
|
{
|
||||||
IProcessContext Create(KernelContext context, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit);
|
IProcessContext Create(KernelContext context, long pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +126,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
|
AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
|
||||||
|
|
||||||
|
Pid = KernelContext.NewKipId();
|
||||||
|
|
||||||
|
if (Pid == 0 || (ulong)Pid >= KernelConstants.InitialProcessId)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Invalid KIP Id {Pid}.");
|
||||||
|
}
|
||||||
|
|
||||||
InitializeMemoryManager(creationInfo.Flags);
|
InitializeMemoryManager(creationInfo.Flags);
|
||||||
|
|
||||||
bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
|
bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
|
||||||
@ -171,13 +178,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pid = KernelContext.NewKipId();
|
|
||||||
|
|
||||||
if (Pid == 0 || (ulong)Pid >= KernelConstants.InitialProcessId)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"Invalid KIP Id {Pid}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ParseProcessInfo(creationInfo);
|
return ParseProcessInfo(creationInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,6 +233,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
|
AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
|
||||||
|
|
||||||
|
Pid = KernelContext.NewProcessId();
|
||||||
|
|
||||||
|
if (Pid == -1 || (ulong)Pid < KernelConstants.InitialProcessId)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Invalid Process Id {Pid}.");
|
||||||
|
}
|
||||||
|
|
||||||
InitializeMemoryManager(creationInfo.Flags);
|
InitializeMemoryManager(creationInfo.Flags);
|
||||||
|
|
||||||
bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
|
bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
|
||||||
@ -286,13 +293,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pid = KernelContext.NewProcessId();
|
|
||||||
|
|
||||||
if (Pid == -1 || (ulong)Pid < KernelConstants.InitialProcessId)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"Invalid Process Id {Pid}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
result = ParseProcessInfo(creationInfo);
|
result = ParseProcessInfo(creationInfo);
|
||||||
|
|
||||||
if (result != KernelResult.Success)
|
if (result != KernelResult.Success)
|
||||||
@ -1051,14 +1051,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
|
|
||||||
bool for64Bit = flags.HasFlag(ProcessCreationFlags.Is64Bit);
|
bool for64Bit = flags.HasFlag(ProcessCreationFlags.Is64Bit);
|
||||||
|
|
||||||
Context = _contextFactory.Create(KernelContext, 1UL << addrSpaceBits, InvalidAccessHandler, for64Bit);
|
Context = _contextFactory.Create(KernelContext, Pid, 1UL << addrSpaceBits, InvalidAccessHandler, for64Bit);
|
||||||
|
|
||||||
// TODO: This should eventually be removed.
|
|
||||||
// The GPU shouldn't depend on the CPU memory manager at all.
|
|
||||||
if (flags.HasFlag(ProcessCreationFlags.IsApplication))
|
|
||||||
{
|
|
||||||
KernelContext.Device.Gpu.SetVmm((IVirtualMemoryManagerTracked)CpuMemory);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Context.AddressSpace is MemoryManagerHostMapped)
|
if (Context.AddressSpace is MemoryManagerHostMapped)
|
||||||
{
|
{
|
||||||
|
@ -4,7 +4,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
class ProcessContextFactory : IProcessContextFactory
|
class ProcessContextFactory : IProcessContextFactory
|
||||||
{
|
{
|
||||||
public IProcessContext Create(KernelContext context, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
|
public IProcessContext Create(KernelContext context, long pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
|
||||||
{
|
{
|
||||||
return new ProcessContext(new AddressSpaceManager(addressSpaceSize));
|
return new ProcessContext(new AddressSpaceManager(addressSpaceSize));
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
KProcess process = new KProcess(context);
|
KProcess process = new KProcess(context);
|
||||||
|
|
||||||
var processContextFactory = new ArmProcessContextFactory();
|
var processContextFactory = new ArmProcessContextFactory(context.Device.Gpu);
|
||||||
|
|
||||||
result = process.InitializeKip(
|
result = process.InitializeKip(
|
||||||
creationInfo,
|
creationInfo,
|
||||||
@ -228,7 +228,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var processContextFactory = new ArmProcessContextFactory();
|
var processContextFactory = new ArmProcessContextFactory(context.Device.Gpu);
|
||||||
|
|
||||||
result = process.Initialize(
|
result = process.Initialize(
|
||||||
creationInfo,
|
creationInfo,
|
||||||
|
47
Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs
Normal file
47
Ryujinx.HLE/HOS/Services/Nv/Host1xContext.cs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
using Ryujinx.Graphics.Gpu;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
using Ryujinx.Graphics.Host1x;
|
||||||
|
using Ryujinx.Graphics.Nvdec;
|
||||||
|
using Ryujinx.Graphics.Vic;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
|
{
|
||||||
|
class Host1xContext : IDisposable
|
||||||
|
{
|
||||||
|
public MemoryManager Smmu { get; }
|
||||||
|
public NvMemoryAllocator MemoryAllocator { get; }
|
||||||
|
public Host1xDevice Host1x { get;}
|
||||||
|
|
||||||
|
public Host1xContext(GpuContext gpu, long pid)
|
||||||
|
{
|
||||||
|
MemoryAllocator = new NvMemoryAllocator();
|
||||||
|
Host1x = new Host1xDevice(gpu.Synchronization);
|
||||||
|
Smmu = gpu.CreateMemoryManager(pid);
|
||||||
|
var nvdec = new NvdecDevice(Smmu);
|
||||||
|
var vic = new VicDevice(Smmu);
|
||||||
|
Host1x.RegisterDevice(ClassId.Nvdec, nvdec);
|
||||||
|
Host1x.RegisterDevice(ClassId.Vic, vic);
|
||||||
|
|
||||||
|
nvdec.FrameDecoded += (FrameDecodedEventArgs e) =>
|
||||||
|
{
|
||||||
|
// FIXME:
|
||||||
|
// Figure out what is causing frame ordering issues on H264.
|
||||||
|
// For now this is needed as workaround.
|
||||||
|
if (e.CodecId == CodecId.H264)
|
||||||
|
{
|
||||||
|
vic.SetSurfaceOverride(e.LumaOffset, e.ChromaOffset, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vic.DisableSurfaceOverride();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Host1x.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,6 @@ using Ryujinx.Common.Logging;
|
|||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel;
|
||||||
@ -24,8 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||||||
[Service("nvdrv:t")]
|
[Service("nvdrv:t")]
|
||||||
class INvDrvServices : IpcService
|
class INvDrvServices : IpcService
|
||||||
{
|
{
|
||||||
private static Dictionary<string, Type> _deviceFileRegistry =
|
private static Dictionary<string, Type> _deviceFileRegistry = new Dictionary<string, Type>()
|
||||||
new Dictionary<string, Type>()
|
|
||||||
{
|
{
|
||||||
{ "/dev/nvmap", typeof(NvMapDeviceFile) },
|
{ "/dev/nvmap", typeof(NvMapDeviceFile) },
|
||||||
{ "/dev/nvhost-ctrl", typeof(NvHostCtrlDeviceFile) },
|
{ "/dev/nvhost-ctrl", typeof(NvHostCtrlDeviceFile) },
|
||||||
@ -39,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||||||
//{ "/dev/nvhost-display", typeof(NvHostChannelDeviceFile) },
|
//{ "/dev/nvhost-display", typeof(NvHostChannelDeviceFile) },
|
||||||
};
|
};
|
||||||
|
|
||||||
private static IdDictionary _deviceFileIdRegistry = new IdDictionary();
|
public static IdDictionary DeviceFileIdRegistry = new IdDictionary();
|
||||||
|
|
||||||
private IVirtualMemoryManager _clientMemory;
|
private IVirtualMemoryManager _clientMemory;
|
||||||
private long _owner;
|
private long _owner;
|
||||||
@ -61,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||||||
|
|
||||||
deviceFile.Path = path;
|
deviceFile.Path = path;
|
||||||
|
|
||||||
return _deviceFileIdRegistry.Add(deviceFile);
|
return DeviceFileIdRegistry.Add(deviceFile);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -139,7 +137,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||||||
return NvResult.InvalidParameter;
|
return NvResult.InvalidParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceFile = _deviceFileIdRegistry.GetData<NvDeviceFile>(fd);
|
deviceFile = DeviceFileIdRegistry.GetData<NvDeviceFile>(fd);
|
||||||
|
|
||||||
if (deviceFile == null)
|
if (deviceFile == null)
|
||||||
{
|
{
|
||||||
@ -302,7 +300,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||||||
{
|
{
|
||||||
deviceFile.Close();
|
deviceFile.Close();
|
||||||
|
|
||||||
_deviceFileIdRegistry.Delete(fd);
|
DeviceFileIdRegistry.Delete(fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,14 +567,16 @@ namespace Ryujinx.HLE.HOS.Services.Nv
|
|||||||
|
|
||||||
public static void Destroy()
|
public static void Destroy()
|
||||||
{
|
{
|
||||||
foreach (object entry in _deviceFileIdRegistry.Values)
|
NvHostChannelDeviceFile.Destroy();
|
||||||
|
|
||||||
|
foreach (object entry in DeviceFileIdRegistry.Values)
|
||||||
{
|
{
|
||||||
NvDeviceFile deviceFile = (NvDeviceFile)entry;
|
NvDeviceFile deviceFile = (NvDeviceFile)entry;
|
||||||
|
|
||||||
deviceFile.Close();
|
deviceFile.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
_deviceFileIdRegistry.Clear();
|
DeviceFileIdRegistry.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,22 +1,22 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
||||||
{
|
{
|
||||||
class NvHostAsGpuDeviceFile : NvDeviceFile
|
class NvHostAsGpuDeviceFile : NvDeviceFile
|
||||||
{
|
{
|
||||||
private static ConcurrentDictionary<KProcess, AddressSpaceContext> _addressSpaceContextRegistry = new ConcurrentDictionary<KProcess, AddressSpaceContext>();
|
private readonly AddressSpaceContext _asContext;
|
||||||
private NvMemoryAllocator _memoryAllocator;
|
private readonly NvMemoryAllocator _memoryAllocator;
|
||||||
|
|
||||||
public NvHostAsGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner)
|
public NvHostAsGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, long owner) : base(context, owner)
|
||||||
{
|
{
|
||||||
_memoryAllocator = context.Device.MemoryAllocator;
|
_asContext = new AddressSpaceContext(context.Device.Gpu.CreateMemoryManager(owner));
|
||||||
|
_memoryAllocator = new NvMemoryAllocator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments)
|
public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments)
|
||||||
@ -77,20 +77,24 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
|
|
||||||
private NvInternalResult BindChannel(ref BindChannelArguments arguments)
|
private NvInternalResult BindChannel(ref BindChannelArguments arguments)
|
||||||
{
|
{
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceNv);
|
var channelDeviceFile = INvDrvServices.DeviceFileIdRegistry.GetData<NvHostChannelDeviceFile>(arguments.Fd);
|
||||||
|
if (channelDeviceFile == null)
|
||||||
|
{
|
||||||
|
// TODO: Return invalid Fd error.
|
||||||
|
}
|
||||||
|
|
||||||
|
channelDeviceFile.Channel.BindMemory(_asContext.Gmm);
|
||||||
|
|
||||||
return NvInternalResult.Success;
|
return NvInternalResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
private NvInternalResult AllocSpace(ref AllocSpaceArguments arguments)
|
private NvInternalResult AllocSpace(ref AllocSpaceArguments arguments)
|
||||||
{
|
{
|
||||||
AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context);
|
|
||||||
|
|
||||||
ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize;
|
ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize;
|
||||||
|
|
||||||
NvInternalResult result = NvInternalResult.Success;
|
NvInternalResult result = NvInternalResult.Success;
|
||||||
|
|
||||||
lock (addressSpaceContext)
|
lock (_asContext)
|
||||||
{
|
{
|
||||||
// Note: When the fixed offset flag is not set,
|
// Note: When the fixed offset flag is not set,
|
||||||
// the Offset field holds the alignment size instead.
|
// the Offset field holds the alignment size instead.
|
||||||
@ -132,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
addressSpaceContext.AddReservation(arguments.Offset, size);
|
_asContext.AddReservation(arguments.Offset, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,18 +145,16 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
|
|
||||||
private NvInternalResult FreeSpace(ref FreeSpaceArguments arguments)
|
private NvInternalResult FreeSpace(ref FreeSpaceArguments arguments)
|
||||||
{
|
{
|
||||||
AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context);
|
ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize;
|
||||||
|
|
||||||
NvInternalResult result = NvInternalResult.Success;
|
NvInternalResult result = NvInternalResult.Success;
|
||||||
|
|
||||||
lock (addressSpaceContext)
|
lock (_asContext)
|
||||||
{
|
{
|
||||||
ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize;
|
if (_asContext.RemoveReservation(arguments.Offset))
|
||||||
|
|
||||||
if (addressSpaceContext.RemoveReservation(arguments.Offset))
|
|
||||||
{
|
{
|
||||||
_memoryAllocator.DeallocateRange(arguments.Offset, size);
|
_memoryAllocator.DeallocateRange(arguments.Offset, size);
|
||||||
addressSpaceContext.Gmm.Unmap(arguments.Offset, size);
|
_asContext.Gmm.Unmap(arguments.Offset, size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -168,16 +170,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
|
|
||||||
private NvInternalResult UnmapBuffer(ref UnmapBufferArguments arguments)
|
private NvInternalResult UnmapBuffer(ref UnmapBufferArguments arguments)
|
||||||
{
|
{
|
||||||
AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context);
|
lock (_asContext)
|
||||||
|
|
||||||
lock (addressSpaceContext)
|
|
||||||
{
|
{
|
||||||
if (addressSpaceContext.RemoveMap(arguments.Offset, out ulong size))
|
if (_asContext.RemoveMap(arguments.Offset, out ulong size))
|
||||||
{
|
{
|
||||||
if (size != 0)
|
if (size != 0)
|
||||||
{
|
{
|
||||||
_memoryAllocator.DeallocateRange(arguments.Offset, size);
|
_memoryAllocator.DeallocateRange(arguments.Offset, size);
|
||||||
addressSpaceContext.Gmm.Unmap(arguments.Offset, size);
|
_asContext.Gmm.Unmap(arguments.Offset, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -191,22 +191,20 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
|
|
||||||
private NvInternalResult MapBufferEx(ref MapBufferExArguments arguments)
|
private NvInternalResult MapBufferEx(ref MapBufferExArguments arguments)
|
||||||
{
|
{
|
||||||
const string mapErrorMsg = "Failed to map fixed buffer with offset 0x{0:x16}, size 0x{1:x16} and alignment 0x{2:x16}!";
|
const string MapErrorMsg = "Failed to map fixed buffer with offset 0x{0:x16}, size 0x{1:x16} and alignment 0x{2:x16}!";
|
||||||
|
|
||||||
AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context);
|
|
||||||
|
|
||||||
ulong physicalAddress;
|
ulong physicalAddress;
|
||||||
|
|
||||||
if ((arguments.Flags & AddressSpaceFlags.RemapSubRange) != 0)
|
if ((arguments.Flags & AddressSpaceFlags.RemapSubRange) != 0)
|
||||||
{
|
{
|
||||||
lock (addressSpaceContext)
|
lock (_asContext)
|
||||||
{
|
{
|
||||||
if (addressSpaceContext.TryGetMapPhysicalAddress(arguments.Offset, out physicalAddress))
|
if (_asContext.TryGetMapPhysicalAddress(arguments.Offset, out physicalAddress))
|
||||||
{
|
{
|
||||||
ulong virtualAddress = arguments.Offset + arguments.BufferOffset;
|
ulong virtualAddress = arguments.Offset + arguments.BufferOffset;
|
||||||
|
|
||||||
physicalAddress += arguments.BufferOffset;
|
physicalAddress += arguments.BufferOffset;
|
||||||
addressSpaceContext.Gmm.Map(physicalAddress, virtualAddress, arguments.MappingSize);
|
_asContext.Gmm.Map(physicalAddress, virtualAddress, arguments.MappingSize);
|
||||||
|
|
||||||
return NvInternalResult.Success;
|
return NvInternalResult.Success;
|
||||||
}
|
}
|
||||||
@ -246,7 +244,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
|
|
||||||
NvInternalResult result = NvInternalResult.Success;
|
NvInternalResult result = NvInternalResult.Success;
|
||||||
|
|
||||||
lock (addressSpaceContext)
|
lock (_asContext)
|
||||||
{
|
{
|
||||||
// Note: When the fixed offset flag is not set,
|
// Note: When the fixed offset flag is not set,
|
||||||
// the Offset field holds the alignment size instead.
|
// the Offset field holds the alignment size instead.
|
||||||
@ -254,13 +252,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
|
|
||||||
if (!virtualAddressAllocated)
|
if (!virtualAddressAllocated)
|
||||||
{
|
{
|
||||||
if (addressSpaceContext.ValidateFixedBuffer(arguments.Offset, size, pageSize))
|
if (_asContext.ValidateFixedBuffer(arguments.Offset, size, pageSize))
|
||||||
{
|
{
|
||||||
addressSpaceContext.Gmm.Map(physicalAddress, arguments.Offset, size);
|
_asContext.Gmm.Map(physicalAddress, arguments.Offset, size);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string message = string.Format(mapErrorMsg, arguments.Offset, size, pageSize);
|
string message = string.Format(MapErrorMsg, arguments.Offset, size, pageSize);
|
||||||
|
|
||||||
Logger.Warning?.Print(LogClass.ServiceNv, message);
|
Logger.Warning?.Print(LogClass.ServiceNv, message);
|
||||||
|
|
||||||
@ -275,7 +273,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
_memoryAllocator.AllocateRange(va, size, freeAddressStartPosition);
|
_memoryAllocator.AllocateRange(va, size, freeAddressStartPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
addressSpaceContext.Gmm.Map(physicalAddress, va, size);
|
_asContext.Gmm.Map(physicalAddress, va, size);
|
||||||
arguments.Offset = va;
|
arguments.Offset = va;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +287,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
addressSpaceContext.AddMap(arguments.Offset, size, physicalAddress, virtualAddressAllocated);
|
_asContext.AddMap(arguments.Offset, size, physicalAddress, virtualAddressAllocated);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,12 +310,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
|
|
||||||
private NvInternalResult Remap(Span<RemapArguments> arguments)
|
private NvInternalResult Remap(Span<RemapArguments> arguments)
|
||||||
{
|
{
|
||||||
AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context);
|
MemoryManager gmm = _asContext.Gmm;
|
||||||
|
|
||||||
for (int index = 0; index < arguments.Length; index++)
|
for (int index = 0; index < arguments.Length; index++)
|
||||||
{
|
{
|
||||||
MemoryManager gmm = GetAddressSpaceContext(Context).Gmm;
|
|
||||||
|
|
||||||
ulong mapOffs = (ulong)arguments[index].MapOffset << 16;
|
ulong mapOffs = (ulong)arguments[index].MapOffset << 16;
|
||||||
ulong gpuVa = (ulong)arguments[index].GpuOffset << 16;
|
ulong gpuVa = (ulong)arguments[index].GpuOffset << 16;
|
||||||
ulong size = (ulong)arguments[index].Pages << 16;
|
ulong size = (ulong)arguments[index].Pages << 16;
|
||||||
@ -345,10 +341,5 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override void Close() { }
|
public override void Close() { }
|
||||||
|
|
||||||
public static AddressSpaceContext GetAddressSpaceContext(ServiceCtx context)
|
|
||||||
{
|
|
||||||
return _addressSpaceContextRegistry.GetOrAdd(context.Process, (key) => new AddressSpaceContext(context));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types
|
|||||||
private readonly SortedList<ulong, Range> _maps;
|
private readonly SortedList<ulong, Range> _maps;
|
||||||
private readonly SortedList<ulong, Range> _reservations;
|
private readonly SortedList<ulong, Range> _reservations;
|
||||||
|
|
||||||
public AddressSpaceContext(ServiceCtx context)
|
public AddressSpaceContext(MemoryManager gmm)
|
||||||
{
|
{
|
||||||
Gmm = context.Device.Gpu.MemoryManager;
|
Gmm = gmm;
|
||||||
|
|
||||||
_maps = new SortedList<ulong, Range>();
|
_maps = new SortedList<ulong, Range>();
|
||||||
_reservations = new SortedList<ulong, Range>();
|
_reservations = new SortedList<ulong, Range>();
|
||||||
@ -123,7 +123,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types
|
|||||||
return _reservations.Remove(gpuVa);
|
return _reservations.Remove(gpuVa);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Range BinarySearch(SortedList<ulong, Range> list, ulong address)
|
private static Range BinarySearch(SortedList<ulong, Range> list, ulong address)
|
||||||
{
|
{
|
||||||
int left = 0;
|
int left = 0;
|
||||||
int right = list.Count - 1;
|
int right = list.Count - 1;
|
||||||
@ -154,7 +154,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Range BinarySearchLt(SortedList<ulong, Range> list, ulong address)
|
private static Range BinarySearchLt(SortedList<ulong, Range> list, ulong address)
|
||||||
{
|
{
|
||||||
Range ltRg = null;
|
Range ltRg = null;
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Gpu;
|
using Ryujinx.Graphics.Gpu;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.Types;
|
using Ryujinx.HLE.HOS.Services.Nv.Types;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
@ -15,6 +15,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
{
|
{
|
||||||
class NvHostChannelDeviceFile : NvDeviceFile
|
class NvHostChannelDeviceFile : NvDeviceFile
|
||||||
{
|
{
|
||||||
|
private static readonly ConcurrentDictionary<long, Host1xContext> _host1xContextRegistry = new();
|
||||||
|
|
||||||
private const uint MaxModuleSyncpoint = 16;
|
private const uint MaxModuleSyncpoint = 16;
|
||||||
|
|
||||||
private uint _timeout;
|
private uint _timeout;
|
||||||
@ -24,8 +26,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
private readonly Switch _device;
|
private readonly Switch _device;
|
||||||
|
|
||||||
private readonly IVirtualMemoryManager _memory;
|
private readonly IVirtualMemoryManager _memory;
|
||||||
private readonly NvMemoryAllocator _memoryAllocator;
|
private readonly Host1xContext _host1xContext;
|
||||||
private readonly GpuChannel _channel;
|
|
||||||
|
public GpuChannel Channel { get; }
|
||||||
|
|
||||||
public enum ResourcePolicy
|
public enum ResourcePolicy
|
||||||
{
|
{
|
||||||
@ -48,8 +51,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
_timeout = 3000;
|
_timeout = 3000;
|
||||||
_submitTimeout = 0;
|
_submitTimeout = 0;
|
||||||
_timeslice = 0;
|
_timeslice = 0;
|
||||||
_memoryAllocator = _device.MemoryAllocator;
|
_host1xContext = GetHost1XContext(context.Device.Gpu, owner);
|
||||||
_channel = _device.Gpu.CreateChannel();
|
Channel = _device.Gpu.CreateChannel();
|
||||||
|
|
||||||
ChannelSyncpoints = new uint[MaxModuleSyncpoint];
|
ChannelSyncpoints = new uint[MaxModuleSyncpoint];
|
||||||
|
|
||||||
@ -162,7 +165,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
|
|
||||||
var data = _memory.GetSpan(map.Address + commandBuffer.Offset, commandBuffer.WordsCount * 4);
|
var data = _memory.GetSpan(map.Address + commandBuffer.Offset, commandBuffer.WordsCount * 4);
|
||||||
|
|
||||||
_device.Host1x.Submit(MemoryMarshal.Cast<byte, int>(data));
|
_host1xContext.Host1x.Submit(MemoryMarshal.Cast<byte, int>(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +175,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
|
|
||||||
tmpCmdBuff[0] = (4 << 28) | (int)fences[0].Id;
|
tmpCmdBuff[0] = (4 << 28) | (int)fences[0].Id;
|
||||||
|
|
||||||
_device.Host1x.Submit(tmpCmdBuff);
|
_host1xContext.Host1x.Submit(tmpCmdBuff);
|
||||||
|
|
||||||
return NvInternalResult.Success;
|
return NvInternalResult.Success;
|
||||||
}
|
}
|
||||||
@ -233,7 +236,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
|
int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
|
||||||
MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
|
MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
|
||||||
Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries);
|
Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries);
|
||||||
MemoryManager gmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Context).Gmm;
|
|
||||||
|
|
||||||
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
|
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
|
||||||
{
|
{
|
||||||
@ -250,12 +252,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
{
|
{
|
||||||
if (map.DmaMapAddress == 0)
|
if (map.DmaMapAddress == 0)
|
||||||
{
|
{
|
||||||
ulong va = _memoryAllocator.GetFreeAddress((ulong) map.Size, out ulong freeAddressStartPosition, 1, MemoryManager.PageSize);
|
ulong va = _host1xContext.MemoryAllocator.GetFreeAddress((ulong)map.Size, out ulong freeAddressStartPosition, 1, MemoryManager.PageSize);
|
||||||
|
|
||||||
if (va != NvMemoryAllocator.PteUnmapped && va <= uint.MaxValue && (va + (uint)map.Size) <= uint.MaxValue)
|
if (va != NvMemoryAllocator.PteUnmapped && va <= uint.MaxValue && (va + (uint)map.Size) <= uint.MaxValue)
|
||||||
{
|
{
|
||||||
_memoryAllocator.AllocateRange(va, (uint)map.Size, freeAddressStartPosition);
|
_host1xContext.MemoryAllocator.AllocateRange(va, (uint)map.Size, freeAddressStartPosition);
|
||||||
gmm.Map(map.Address, va, (uint)map.Size);
|
_host1xContext.Smmu.Map(map.Address, va, (uint)map.Size);
|
||||||
map.DmaMapAddress = va;
|
map.DmaMapAddress = va;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -276,7 +278,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
|
int headerSize = Unsafe.SizeOf<MapCommandBufferArguments>();
|
||||||
MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
|
MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast<byte, MapCommandBufferArguments>(arguments)[0];
|
||||||
Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries);
|
Span<CommandBufferHandle> commandBufferEntries = MemoryMarshal.Cast<byte, CommandBufferHandle>(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries);
|
||||||
MemoryManager gmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Context).Gmm;
|
|
||||||
|
|
||||||
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
|
foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries)
|
||||||
{
|
{
|
||||||
@ -297,7 +298,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
// To make unmapping work, we need separate address space per channel.
|
// To make unmapping work, we need separate address space per channel.
|
||||||
// Right now NVDEC and VIC share the GPU address space which is not correct at all.
|
// Right now NVDEC and VIC share the GPU address space which is not correct at all.
|
||||||
|
|
||||||
// gmm.Free((ulong)map.DmaMapAddress, (uint)map.Size);
|
// _host1xContext.MemoryAllocator.Free((ulong)map.DmaMapAddress, (uint)map.Size);
|
||||||
|
|
||||||
// map.DmaMapAddress = 0;
|
// map.DmaMapAddress = 0;
|
||||||
}
|
}
|
||||||
@ -431,10 +432,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
|
|
||||||
if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceWait) && !_device.System.HostSyncpoint.IsSyncpointExpired(header.Fence.Id, header.Fence.Value))
|
if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceWait) && !_device.System.HostSyncpoint.IsSyncpointExpired(header.Fence.Id, header.Fence.Value))
|
||||||
{
|
{
|
||||||
_channel.PushHostCommandBuffer(CreateWaitCommandBuffer(header.Fence));
|
Channel.PushHostCommandBuffer(CreateWaitCommandBuffer(header.Fence));
|
||||||
}
|
}
|
||||||
|
|
||||||
_channel.PushEntries(entries);
|
Channel.PushEntries(entries);
|
||||||
|
|
||||||
header.Fence.Id = _channelSyncpoint.Id;
|
header.Fence.Id = _channelSyncpoint.Id;
|
||||||
|
|
||||||
@ -456,7 +457,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
|
|
||||||
if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement))
|
if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement))
|
||||||
{
|
{
|
||||||
_channel.PushHostCommandBuffer(CreateIncrementCommandBuffer(ref header.Fence, header.Flags));
|
Channel.PushHostCommandBuffer(CreateIncrementCommandBuffer(ref header.Fence, header.Flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
header.Flags = SubmitGpfifoFlags.None;
|
header.Flags = SubmitGpfifoFlags.None;
|
||||||
@ -545,7 +546,22 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
|
|||||||
|
|
||||||
public override void Close()
|
public override void Close()
|
||||||
{
|
{
|
||||||
_channel.Dispose();
|
Channel.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Host1xContext GetHost1XContext(GpuContext gpu, long pid)
|
||||||
|
{
|
||||||
|
return _host1xContextRegistry.GetOrAdd(pid, (long key) => new Host1xContext(gpu, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Destroy()
|
||||||
|
{
|
||||||
|
foreach (Host1xContext host1xContext in _host1xContextRegistry.Values)
|
||||||
|
{
|
||||||
|
host1xContext.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_host1xContextRegistry.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ using System;
|
|||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices
|
namespace Ryujinx.HLE.HOS.Services.Nv
|
||||||
{
|
{
|
||||||
class NvMemoryAllocator
|
class NvMemoryAllocator
|
||||||
{
|
{
|
||||||
|
@ -385,6 +385,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
}
|
}
|
||||||
|
|
||||||
_device.Gpu.Window.EnqueueFrameThreadSafe(
|
_device.Gpu.Window.EnqueueFrameThreadSafe(
|
||||||
|
layer.Owner,
|
||||||
frameBufferAddress,
|
frameBufferAddress,
|
||||||
frameBufferWidth,
|
frameBufferWidth,
|
||||||
frameBufferHeight,
|
frameBufferHeight,
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
using Ryujinx.Audio.Backends.CompatLayer;
|
using Ryujinx.Audio.Backends.CompatLayer;
|
||||||
using Ryujinx.Audio.Integration;
|
using Ryujinx.Audio.Integration;
|
||||||
using Ryujinx.Graphics.Gpu;
|
using Ryujinx.Graphics.Gpu;
|
||||||
using Ryujinx.Graphics.Host1x;
|
|
||||||
using Ryujinx.Graphics.Nvdec;
|
|
||||||
using Ryujinx.Graphics.Vic;
|
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.Services.Apm;
|
using Ryujinx.HLE.HOS.Services.Apm;
|
||||||
using Ryujinx.HLE.HOS.Services.Hid;
|
using Ryujinx.HLE.HOS.Services.Hid;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices;
|
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@ -24,10 +20,6 @@ namespace Ryujinx.HLE
|
|||||||
|
|
||||||
public GpuContext Gpu { get; }
|
public GpuContext Gpu { get; }
|
||||||
|
|
||||||
internal NvMemoryAllocator MemoryAllocator { get; }
|
|
||||||
|
|
||||||
internal Host1xDevice Host1x { get; }
|
|
||||||
|
|
||||||
public VirtualFileSystem FileSystem => Configuration.VirtualFileSystem;
|
public VirtualFileSystem FileSystem => Configuration.VirtualFileSystem;
|
||||||
|
|
||||||
public Horizon System { get; }
|
public Horizon System { get; }
|
||||||
@ -71,29 +63,6 @@ namespace Ryujinx.HLE
|
|||||||
|
|
||||||
Gpu = new GpuContext(configuration.GpuRenderer);
|
Gpu = new GpuContext(configuration.GpuRenderer);
|
||||||
|
|
||||||
MemoryAllocator = new NvMemoryAllocator();
|
|
||||||
|
|
||||||
Host1x = new Host1xDevice(Gpu.Synchronization);
|
|
||||||
var nvdec = new NvdecDevice(Gpu.MemoryManager);
|
|
||||||
var vic = new VicDevice(Gpu.MemoryManager);
|
|
||||||
Host1x.RegisterDevice(ClassId.Nvdec, nvdec);
|
|
||||||
Host1x.RegisterDevice(ClassId.Vic, vic);
|
|
||||||
|
|
||||||
nvdec.FrameDecoded += (FrameDecodedEventArgs e) =>
|
|
||||||
{
|
|
||||||
// FIXME:
|
|
||||||
// Figure out what is causing frame ordering issues on H264.
|
|
||||||
// For now this is needed as workaround.
|
|
||||||
if (e.CodecId == CodecId.H264)
|
|
||||||
{
|
|
||||||
vic.SetSurfaceOverride(e.LumaOffset, e.ChromaOffset, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
vic.DisableSurfaceOverride();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
System = new Horizon(this);
|
System = new Horizon(this);
|
||||||
System.InitializeServices();
|
System.InitializeServices();
|
||||||
|
|
||||||
@ -190,7 +159,6 @@ namespace Ryujinx.HLE
|
|||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
System.Dispose();
|
System.Dispose();
|
||||||
Host1x.Dispose();
|
|
||||||
AudioDeviceDriver.Dispose();
|
AudioDeviceDriver.Dispose();
|
||||||
FileSystem.Unload();
|
FileSystem.Unload();
|
||||||
Memory.Dispose();
|
Memory.Dispose();
|
||||||
|
Loading…
Reference in New Issue
Block a user