Compare commits

..

12 Commits

Author SHA1 Message Date
d987cacfb7 Fix VIC out of bounds copy (#3386)
* Fix VIC out of bounds copy

* Update the assert
2022-06-17 12:01:52 -03:00
851f56b08a Support Array/3D depth-stencil render target, and single layer clears (#3400)
* Support Array/3D depth-stencil render target, and single layer clears

* Alignment
2022-06-14 13:30:39 -03:00
b1bd6a50b5 Less invasive fix for EventFd blocking operations (#3394) 2022-06-12 09:29:12 +02:00
70895bdb04 Allow concurrent BSD EventFd read/write (#3385) 2022-06-11 14:58:30 -03:00
830cbf91bb Ignore ClipControl on draw texture fallback (#3388) 2022-06-11 14:31:17 -03:00
9a9349f0f4 Fix instanced indexed inline draw index count (#3389) 2022-06-10 23:44:49 -03:00
46cc7b55f0 Fix instanced indexed inline draws (#3383) 2022-06-05 21:24:28 -03:00
dd8f97ab9e Remove freed memory range from tree on memory block disposal (#3347)
* Remove freed memory range from tree on memory block disposal

* PR feedback
2022-06-05 15:12:42 -03:00
633c5ec330 Extend uses count from ushort to uint on Operand Data structure (#3374) 2022-06-05 14:15:27 -03:00
a3e7bb8eb4 Copy dependency for multisample and non-multisample textures (#3382)
* Use copy dependency for textures that differs in multisample but are otherwise compatible

* Remove allowMs flag as it's no longer required for correctness, it's just an optimization now

* Dispose intermmediate pool
2022-06-05 14:06:47 -03:00
2073ba2919 Fix a potential GPFIFO submission race (#3378)
The syncpoint maximum value represents the maximum possible syncpt value at a given time, however due to PBs being submitted before max was incremented, for a brief moment of time this is not the case which could lead to invalid behaviour if a game waits on the fence at that specific time.
2022-06-04 21:36:36 +02:00
d03124a992 Fix 3D semaphore counter type 0 handling (#3380)
Counter type 0 actually releases the semaphore payload rather than a constant zero as was previously thought. This is required by Skyrim.
2022-06-02 19:51:36 -03:00
31 changed files with 635 additions and 118 deletions

View File

@ -14,10 +14,11 @@ namespace ARMeilleure.IntermediateRepresentation
public byte Kind;
public byte Type;
public byte SymbolType;
public byte Padding; // Unused space.
public ushort AssignmentsCount;
public ushort AssignmentsCapacity;
public ushort UsesCount;
public ushort UsesCapacity;
public uint UsesCount;
public uint UsesCapacity;
public Operation* Assignments;
public Operation* Uses;
public ulong Value;
@ -84,11 +85,11 @@ namespace ARMeilleure.IntermediateRepresentation
{
Debug.Assert(Kind != OperandKind.Memory);
return new ReadOnlySpan<Operation>(_data->Uses, _data->UsesCount);
return new ReadOnlySpan<Operation>(_data->Uses, (int)_data->UsesCount);
}
}
public int UsesCount => _data->UsesCount;
public int UsesCount => (int)_data->UsesCount;
public int AssignmentsCount => _data->AssignmentsCount;
public bool Relocatable => Symbol.Type != SymbolType.None;
@ -178,7 +179,7 @@ namespace ARMeilleure.IntermediateRepresentation
{
Add(operation, ref addr._data->Assignments, ref addr._data->AssignmentsCount, ref addr._data->AssignmentsCapacity);
}
if (index != default)
{
Add(operation, ref index._data->Assignments, ref index._data->AssignmentsCount, ref index._data->AssignmentsCapacity);
@ -265,6 +266,13 @@ namespace ARMeilleure.IntermediateRepresentation
data = Allocators.References.Allocate<T>(initialCapacity);
}
private static void New<T>(ref T* data, ref uint count, ref uint capacity, uint initialCapacity) where T : unmanaged
{
count = 0;
capacity = initialCapacity;
data = Allocators.References.Allocate<T>(initialCapacity);
}
private static void Add<T>(T item, ref T* data, ref ushort count, ref ushort capacity) where T : unmanaged
{
if (count < capacity)
@ -294,6 +302,40 @@ namespace ARMeilleure.IntermediateRepresentation
}
}
private static void Add<T>(T item, ref T* data, ref uint count, ref uint capacity) where T : unmanaged
{
if (count < capacity)
{
data[count++] = item;
return;
}
// Could not add item in the fast path, fallback onto the slow path.
ExpandAdd(item, ref data, ref count, ref capacity);
static void ExpandAdd(T item, ref T* data, ref uint count, ref uint capacity)
{
uint newCount = checked(count + 1);
uint newCapacity = (uint)Math.Min(capacity * 2, int.MaxValue);
if (newCapacity <= capacity)
{
throw new OverflowException();
}
var oldSpan = new Span<T>(data, (int)count);
capacity = newCapacity;
data = Allocators.References.Allocate<T>(capacity);
oldSpan.CopyTo(new Span<T>(data, (int)count));
data[count] = item;
count = newCount;
}
}
private static void Remove<T>(in T item, ref T* data, ref ushort count) where T : unmanaged
{
var span = new Span<T>(data, count);
@ -314,6 +356,26 @@ namespace ARMeilleure.IntermediateRepresentation
}
}
private static void Remove<T>(in T item, ref T* data, ref uint count) where T : unmanaged
{
var span = new Span<T>(data, (int)count);
for (int i = 0; i < span.Length; i++)
{
if (EqualityComparer<T>.Default.Equals(span[i], item))
{
if (i + 1 < count)
{
span.Slice(i + 1).CopyTo(span.Slice(i));
}
count--;
return;
}
}
}
public override int GetHashCode()
{
if (Kind == OperandKind.LocalVariable)

View File

@ -10,9 +10,10 @@ namespace Ryujinx.Graphics.GAL
void ClearBuffer(BufferHandle destination, int offset, int size, uint value);
void ClearRenderTargetColor(int index, uint componentMask, ColorF color);
void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color);
void ClearRenderTargetDepthStencil(
int layer,
float depthValue,
bool depthMask,
int stencilValue,

View File

@ -4,19 +4,21 @@
{
public CommandType CommandType => CommandType.ClearRenderTargetColor;
private int _index;
private int _layer;
private uint _componentMask;
private ColorF _color;
public void Set(int index, uint componentMask, ColorF color)
public void Set(int index, int layer, uint componentMask, ColorF color)
{
_index = index;
_layer = layer;
_componentMask = componentMask;
_color = color;
}
public static void Run(ref ClearRenderTargetColorCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
renderer.Pipeline.ClearRenderTargetColor(command._index, command._componentMask, command._color);
renderer.Pipeline.ClearRenderTargetColor(command._index, command._layer, command._componentMask, command._color);
}
}
}

View File

@ -3,13 +3,15 @@
struct ClearRenderTargetDepthStencilCommand : IGALCommand
{
public CommandType CommandType => CommandType.ClearRenderTargetDepthStencil;
private int _layer;
private float _depthValue;
private bool _depthMask;
private int _stencilValue;
private int _stencilMask;
public void Set(float depthValue, bool depthMask, int stencilValue, int stencilMask)
public void Set(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask)
{
_layer = layer;
_depthValue = depthValue;
_depthMask = depthMask;
_stencilValue = stencilValue;
@ -18,7 +20,7 @@
public static void Run(ref ClearRenderTargetDepthStencilCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
renderer.Pipeline.ClearRenderTargetDepthStencil(command._depthValue, command._depthMask, command._stencilValue, command._stencilMask);
renderer.Pipeline.ClearRenderTargetDepthStencil(command._layer, command._depthValue, command._depthMask, command._stencilValue, command._stencilMask);
}
}
}

View File

@ -40,15 +40,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand();
}
public void ClearRenderTargetColor(int index, uint componentMask, ColorF color)
public void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color)
{
_renderer.New<ClearRenderTargetColorCommand>().Set(index, componentMask, color);
_renderer.New<ClearRenderTargetColorCommand>().Set(index, layer, componentMask, color);
_renderer.QueueCommand();
}
public void ClearRenderTargetDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask)
public void ClearRenderTargetDepthStencil(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask)
{
_renderer.New<ClearRenderTargetDepthStencilCommand>().Set(depthValue, depthMask, stencilValue, stencilMask);
_renderer.New<ClearRenderTargetDepthStencilCommand>().Set(layer, depthValue, depthMask, stencilValue, stencilMask);
_renderer.QueueCommand();
}

View File

@ -13,4 +13,12 @@ namespace Ryujinx.Graphics.GAL
CubemapArray,
TextureBuffer
}
public static class TargetExtensions
{
public static bool IsMultisample(this Target target)
{
return target == Target.Texture2DMultisample || target == Target.Texture2DMultisampleArray;
}
}
}

View File

@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private bool _instancedDrawPending;
private bool _instancedIndexed;
private bool _instancedIndexedInline;
private int _instancedFirstIndex;
private int _instancedFirstVertex;
@ -134,13 +135,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
_instancedDrawPending = true;
int ibCount = _drawState.IbStreamer.InlineIndexCount;
_instancedIndexed = _drawState.DrawIndexed;
_instancedIndexedInline = ibCount != 0;
_instancedFirstIndex = firstIndex;
_instancedFirstVertex = (int)_state.State.FirstVertex;
_instancedFirstInstance = (int)_state.State.FirstInstance;
_instancedIndexCount = indexCount;
_instancedIndexCount = ibCount != 0 ? ibCount : indexCount;
var drawState = _state.State.VertexBufferDrawState;
@ -451,8 +455,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
_instancedDrawPending = false;
if (_instancedIndexed)
bool indexedInline = _instancedIndexedInline;
if (_instancedIndexed || indexedInline)
{
if (indexedInline)
{
int inlineIndexCount = _drawState.IbStreamer.GetAndResetInlineIndexCount();
BufferRange br = new BufferRange(_drawState.IbStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4);
_channel.BufferManager.SetIndexBuffer(br, IndexType.UInt);
}
_context.Renderer.Pipeline.DrawIndexed(
_instancedIndexCount,
_instanceIndex + 1,
@ -491,8 +505,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
int index = (argument >> 6) & 0xf;
int layer = (argument >> 10) & 0x3ff;
engine.UpdateRenderTargetState(useControl: false, singleUse: index);
engine.UpdateRenderTargetState(useControl: false, layered: layer != 0, singleUse: index);
// If there is a mismatch on the host clip region and the one explicitly defined by the guest
// on the screen scissor state, then we need to force only one texture to be bound to avoid
@ -567,7 +582,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
ColorF color = new ColorF(clearColor.Red, clearColor.Green, clearColor.Blue, clearColor.Alpha);
_context.Renderer.Pipeline.ClearRenderTargetColor(index, componentMask, color);
_context.Renderer.Pipeline.ClearRenderTargetColor(index, layer, componentMask, color);
}
if (clearDepth || clearStencil)
@ -588,6 +603,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
_context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
layer,
depthValue,
clearDepth,
stencilValue,

View File

@ -20,6 +20,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary>
public bool HasInlineIndexData => _inlineIndexCount != 0;
/// <summary>
/// Total numbers of indices that have been pushed.
/// </summary>
public int InlineIndexCount => _inlineIndexCount;
/// <summary>
/// Gets the handle for the host buffer currently holding the inline index buffer data.
/// </summary>

View File

@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary>
private enum ReportCounterType
{
Zero = 0,
Payload = 0,
InputVertices = 1,
InputPrimitives = 3,
VertexShaderInvocations = 5,
@ -169,8 +169,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
switch (type)
{
case ReportCounterType.Zero:
resultHandler(null, 0);
case ReportCounterType.Payload:
resultHandler(null, (ulong)_state.State.SemaphorePayload);
break;
case ReportCounterType.SamplesPassed:
counter = _context.Renderer.ReportCounter(CounterType.SamplesPassed, resultHandler, false);

View File

@ -362,8 +362,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
/// </summary>
/// <param name="useControl">Use draw buffers information from render target control register</param>
/// <param name="layered">Indicates if the texture is layered</param>
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
public void UpdateRenderTargetState(bool useControl, int singleUse = -1)
public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1)
{
var memoryManager = _channel.MemoryManager;
var rtControl = _state.State.RtControl;
@ -399,7 +400,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
Image.Texture color = memoryManager.Physical.TextureCache.FindOrCreateTexture(
memoryManager,
colorState,
_vtgWritesRtLayer,
_vtgWritesRtLayer || layered,
samplesInX,
samplesInY,
sizeHint);
@ -433,6 +434,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
memoryManager,
dsState,
dsSize,
_vtgWritesRtLayer || layered,
samplesInX,
samplesInY,
sizeHint);

View File

@ -131,10 +131,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
/// </summary>
/// <param name="useControl">Use draw buffers information from render target control register</param>
/// <param name="layered">Indicates if the texture is layered</param>
/// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param>
public void UpdateRenderTargetState(bool useControl, int singleUse = -1)
public void UpdateRenderTargetState(bool useControl, bool layered = false, int singleUse = -1)
{
_stateUpdater.UpdateRenderTargetState(useControl, singleUse);
_stateUpdater.UpdateRenderTargetState(useControl, layered, singleUse);
}
/// <summary>

View File

@ -1136,32 +1136,22 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="range">Texture view physical memory ranges</param>
/// <param name="layerSize">Layer size on the given texture</param>
/// <param name="caps">Host GPU capabilities</param>
/// <param name="allowMs">Indicates that multisample textures are allowed to match non-multisample requested textures</param>
/// <param name="firstLayer">Texture view initial layer on this texture</param>
/// <param name="firstLevel">Texture view first mipmap level on this texture</param>
/// <returns>The level of compatiblilty a view with the given parameters created from this texture has</returns>
public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, Capabilities caps, bool allowMs, out int firstLayer, out int firstLevel)
public TextureViewCompatibility IsViewCompatible(TextureInfo info, MultiRange range, int layerSize, Capabilities caps, out int firstLayer, out int firstLevel)
{
TextureViewCompatibility result = TextureViewCompatibility.Full;
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps));
if (result != TextureViewCompatibility.Incompatible)
{
bool msTargetCompatible = false;
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info));
if (allowMs)
bool bothMs = Info.Target.IsMultisample() && info.Target.IsMultisample();
if (bothMs && (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY))
{
msTargetCompatible = Info.Target == Target.Texture2DMultisample && info.Target == Target.Texture2D;
}
if (!msTargetCompatible)
{
result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info));
if (Info.SamplesInX != info.SamplesInX || Info.SamplesInY != info.SamplesInY)
{
result = TextureViewCompatibility.Incompatible;
}
result = TextureViewCompatibility.Incompatible;
}
if (result == TextureViewCompatibility.Full && Info.FormatInfo.Format != info.FormatInfo.Format && !_context.Capabilities.SupportsMismatchingViewFormat)

View File

@ -349,6 +349,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="memoryManager">GPU memory manager where the texture is mapped</param>
/// <param name="dsState">Depth-stencil buffer texture to find or create</param>
/// <param name="size">Size of the depth-stencil texture</param>
/// <param name="layered">Indicates if the texture might be accessed with a non-zero layer index</param>
/// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
/// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
/// <param name="sizeHint">A hint indicating the minimum used size for the texture</param>
@ -357,6 +358,7 @@ namespace Ryujinx.Graphics.Gpu.Image
MemoryManager memoryManager,
RtDepthStencilState dsState,
Size3D size,
bool layered,
int samplesInX,
int samplesInY,
Size sizeHint)
@ -364,9 +366,24 @@ namespace Ryujinx.Graphics.Gpu.Image
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
Target target = (samplesInX | samplesInY) != 1
? Target.Texture2DMultisample
: Target.Texture2D;
Target target;
if (dsState.MemoryLayout.UnpackIsTarget3D())
{
target = Target.Texture3D;
}
else if ((samplesInX | samplesInY) != 1)
{
target = size.Depth > 1 && layered
? Target.Texture2DMultisampleArray
: Target.Texture2DMultisample;
}
else
{
target = size.Depth > 1 && layered
? Target.Texture2DArray
: Target.Texture2D;
}
FormatInfo formatInfo = dsState.Format.Convert();
@ -547,7 +564,6 @@ namespace Ryujinx.Graphics.Gpu.Image
range.Value,
sizeInfo.LayerSize,
_context.Capabilities,
flags.HasFlag(TextureSearchFlags.ForCopy),
out int firstLayer,
out int firstLevel);
@ -662,7 +678,6 @@ namespace Ryujinx.Graphics.Gpu.Image
overlap.Range,
overlap.LayerSize,
_context.Capabilities,
false,
out int firstLayer,
out int firstLevel);

View File

@ -2,7 +2,6 @@ using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Texture;
using System;
using System.Numerics;
namespace Ryujinx.Graphics.Gpu.Image
{
@ -657,6 +656,11 @@ namespace Ryujinx.Graphics.Gpu.Image
case Target.Texture2DMultisample:
case Target.Texture2DMultisampleArray:
if (rhs.Target == Target.Texture2D || rhs.Target == Target.Texture2DArray)
{
return TextureViewCompatibility.CopyOnly;
}
result = rhs.Target == Target.Texture2DMultisample ||
rhs.Target == Target.Texture2DMultisampleArray;
break;

View File

@ -9,10 +9,13 @@ namespace Ryujinx.Graphics.OpenGL
class Framebuffer : IDisposable
{
public int Handle { get; private set; }
private int _clearFbHandle;
private bool _clearFbInitialized;
private FramebufferAttachment _lastDsAttachment;
private readonly TextureView[] _colors;
private TextureView _depthStencil;
private int _colorsCount;
private bool _dualSourceBlend;
@ -20,6 +23,7 @@ namespace Ryujinx.Graphics.OpenGL
public Framebuffer()
{
Handle = GL.GenFramebuffer();
_clearFbHandle = GL.GenFramebuffer();
_colors = new TextureView[8];
}
@ -55,20 +59,7 @@ namespace Ryujinx.Graphics.OpenGL
if (depthStencil != null)
{
FramebufferAttachment attachment;
if (IsPackedDepthStencilFormat(depthStencil.Format))
{
attachment = FramebufferAttachment.DepthStencilAttachment;
}
else if (IsDepthOnlyFormat(depthStencil.Format))
{
attachment = FramebufferAttachment.DepthAttachment;
}
else
{
attachment = FramebufferAttachment.StencilAttachment;
}
FramebufferAttachment attachment = GetAttachment(depthStencil.Format);
GL.FramebufferTexture(
FramebufferTarget.Framebuffer,
@ -82,6 +73,8 @@ namespace Ryujinx.Graphics.OpenGL
{
_lastDsAttachment = 0;
}
_depthStencil = depthStencil;
}
public void SetDualSourceBlend(bool enable)
@ -124,6 +117,22 @@ namespace Ryujinx.Graphics.OpenGL
GL.DrawBuffers(colorsCount, drawBuffers);
}
private static FramebufferAttachment GetAttachment(Format format)
{
if (IsPackedDepthStencilFormat(format))
{
return FramebufferAttachment.DepthStencilAttachment;
}
else if (IsDepthOnlyFormat(format))
{
return FramebufferAttachment.DepthAttachment;
}
else
{
return FramebufferAttachment.StencilAttachment;
}
}
private static bool IsPackedDepthStencilFormat(Format format)
{
return format == Format.D24UnormS8Uint ||
@ -136,6 +145,78 @@ namespace Ryujinx.Graphics.OpenGL
return format == Format.D16Unorm || format == Format.D32Float;
}
public void AttachColorLayerForClear(int index, int layer)
{
TextureView color = _colors[index];
if (!IsLayered(color))
{
return;
}
BindClearFb();
GL.FramebufferTextureLayer(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0 + index, color.Handle, 0, layer);
}
public void DetachColorLayerForClear(int index)
{
TextureView color = _colors[index];
if (!IsLayered(color))
{
return;
}
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0 + index, 0, 0);
Bind();
}
public void AttachDepthStencilLayerForClear(int layer)
{
TextureView depthStencil = _depthStencil;
if (!IsLayered(depthStencil))
{
return;
}
BindClearFb();
GL.FramebufferTextureLayer(FramebufferTarget.Framebuffer, GetAttachment(depthStencil.Format), depthStencil.Handle, 0, layer);
}
public void DetachDepthStencilLayerForClear()
{
TextureView depthStencil = _depthStencil;
if (!IsLayered(depthStencil))
{
return;
}
GL.FramebufferTexture(FramebufferTarget.Framebuffer, GetAttachment(depthStencil.Format), 0, 0);
Bind();
}
private void BindClearFb()
{
GL.BindFramebuffer(FramebufferTarget.Framebuffer, _clearFbHandle);
if (!_clearFbInitialized)
{
SetDrawBuffersImpl(Constants.MaxRenderTargets);
_clearFbInitialized = true;
}
}
private static bool IsLayered(TextureView view)
{
return view != null &&
view.Target != Target.Texture1D &&
view.Target != Target.Texture2D &&
view.Target != Target.Texture2DMultisample &&
view.Target != Target.TextureBuffer;
}
public void Dispose()
{
if (Handle != 0)
@ -144,6 +225,13 @@ namespace Ryujinx.Graphics.OpenGL
Handle = 0;
}
if (_clearFbHandle != 0)
{
GL.DeleteFramebuffer(_clearFbHandle);
_clearFbHandle = 0;
}
}
}
}

View File

@ -0,0 +1,98 @@
using Ryujinx.Graphics.GAL;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.OpenGL.Image
{
class IntermmediatePool : IDisposable
{
private readonly Renderer _renderer;
private readonly List<TextureView> _entries;
public IntermmediatePool(Renderer renderer)
{
_renderer = renderer;
_entries = new List<TextureView>();
}
public TextureView GetOrCreateWithAtLeast(
Target target,
int blockWidth,
int blockHeight,
int bytesPerPixel,
Format format,
int width,
int height,
int depth,
int levels)
{
TextureView entry;
for (int i = 0; i < _entries.Count; i++)
{
entry = _entries[i];
if (entry.Target == target && entry.Format == format)
{
if (entry.Width < width || entry.Height < height || entry.Info.Depth < depth || entry.Info.Levels < levels)
{
width = Math.Max(width, entry.Width);
height = Math.Max(height, entry.Height);
depth = Math.Max(depth, entry.Info.Depth);
levels = Math.Max(levels, entry.Info.Levels);
entry.Dispose();
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
_entries[i] = entry;
}
return entry;
}
}
entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
_entries.Add(entry);
return entry;
}
private TextureView CreateNew(
Target target,
int blockWidth,
int blockHeight,
int bytesPerPixel,
Format format,
int width,
int height,
int depth,
int levels)
{
return (TextureView)_renderer.CreateTexture(new TextureCreateInfo(
width,
height,
depth,
levels,
1,
blockWidth,
blockHeight,
bytesPerPixel,
format,
DepthStencilMode.Depth,
target,
SwizzleComponent.Red,
SwizzleComponent.Green,
SwizzleComponent.Blue,
SwizzleComponent.Alpha), 1f);
}
public void Dispose()
{
foreach (TextureView entry in _entries)
{
entry.Dispose();
}
_entries.Clear();
}
}
}

View File

@ -9,6 +9,8 @@ namespace Ryujinx.Graphics.OpenGL.Image
{
private readonly Renderer _renderer;
public IntermmediatePool IntermmediatePool { get; }
private int _srcFramebuffer;
private int _dstFramebuffer;
@ -18,6 +20,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
public TextureCopy(Renderer renderer)
{
_renderer = renderer;
IntermmediatePool = new IntermmediatePool(renderer);
}
public void Copy(
@ -25,7 +28,30 @@ namespace Ryujinx.Graphics.OpenGL.Image
TextureView dst,
Extents2D srcRegion,
Extents2D dstRegion,
bool linearFilter)
bool linearFilter,
int srcLayer = 0,
int dstLayer = 0,
int srcLevel = 0,
int dstLevel = 0)
{
int levels = Math.Min(src.Info.Levels - srcLevel, dst.Info.Levels - dstLevel);
int layers = Math.Min(src.Info.GetLayers() - srcLayer, dst.Info.GetLayers() - dstLayer);
Copy(src, dst, srcRegion, dstRegion, linearFilter, srcLayer, dstLayer, srcLevel, dstLevel, layers, levels);
}
public void Copy(
TextureView src,
TextureView dst,
Extents2D srcRegion,
Extents2D dstRegion,
bool linearFilter,
int srcLayer,
int dstLayer,
int srcLevel,
int dstLevel,
int layers,
int levels)
{
TextureView srcConverted = src.Format.IsBgr() != dst.Format.IsBgr() ? BgraSwap(src) : src;
@ -34,22 +60,29 @@ namespace Ryujinx.Graphics.OpenGL.Image
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy());
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy());
int levels = Math.Min(src.Info.Levels, dst.Info.Levels);
int layers = Math.Min(src.Info.GetLayers(), dst.Info.GetLayers());
if (srcLevel != 0)
{
srcRegion = srcRegion.Reduce(srcLevel);
}
if (dstLevel != 0)
{
dstRegion = dstRegion.Reduce(dstLevel);
}
for (int level = 0; level < levels; level++)
{
for (int layer = 0; layer < layers; layer++)
{
if (layers > 1)
if ((srcLayer | dstLayer) != 0 || layers > 1)
{
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level, layer);
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level, layer);
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level, srcLayer + layer);
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level, dstLayer + layer);
}
else
{
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, level);
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, level);
Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle, srcLevel + level);
Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle, dstLevel + level);
}
ClearBufferMask mask = GetMask(src.Format);
@ -484,6 +517,8 @@ namespace Ryujinx.Graphics.OpenGL.Image
_copyPboHandle = 0;
}
IntermmediatePool.Dispose();
}
}
}

View File

@ -115,14 +115,77 @@ namespace Ryujinx.Graphics.OpenGL.Image
{
TextureView destinationView = (TextureView)destination;
_renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel);
if (destinationView.Target.IsMultisample() || Target.IsMultisample())
{
Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast(
GetIntermmediateTarget(Target),
Info.BlockWidth,
Info.BlockHeight,
Info.BytesPerPixel,
Format,
Width,
Height,
Info.Depth,
Info.Levels);
GL.Disable(EnableCap.FramebufferSrgb);
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true);
_renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, firstLayer, 0, firstLevel);
GL.Enable(EnableCap.FramebufferSrgb);
}
else
{
_renderer.TextureCopy.CopyUnscaled(this, destinationView, 0, firstLayer, 0, firstLevel);
}
}
public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel)
{
TextureView destinationView = (TextureView)destination;
TextureView destinationView = (TextureView)destination;
_renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
if (destinationView.Target.IsMultisample() || Target.IsMultisample())
{
Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast(
GetIntermmediateTarget(Target),
Info.BlockWidth,
Info.BlockHeight,
Info.BytesPerPixel,
Format,
Math.Max(1, Width >> srcLevel),
Math.Max(1, Height >> srcLevel),
1,
1);
GL.Disable(EnableCap.FramebufferSrgb);
_renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true, srcLayer, 0, srcLevel, 0, 1, 1);
_renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, dstLayer, 0, dstLevel, 1, 1);
GL.Enable(EnableCap.FramebufferSrgb);
}
else
{
_renderer.TextureCopy.CopyUnscaled(this, destinationView, srcLayer, dstLayer, srcLevel, dstLevel, 1, 1);
}
}
private static Target GetIntermmediateTarget(Target srcTarget)
{
return srcTarget switch
{
Target.Texture2D => Target.Texture2DMultisample,
Target.Texture2DArray => Target.Texture2DMultisampleArray,
Target.Texture2DMultisampleArray => Target.Texture2DArray,
_ => Target.Texture2D
};
}
public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)

View File

@ -110,7 +110,7 @@ namespace Ryujinx.Graphics.OpenGL
Buffer.Clear(destination, offset, size, value);
}
public void ClearRenderTargetColor(int index, uint componentMask, ColorF color)
public void ClearRenderTargetColor(int index, int layer, uint componentMask, ColorF color)
{
GL.ColorMask(
index,
@ -119,14 +119,18 @@ namespace Ryujinx.Graphics.OpenGL
(componentMask & 4) != 0,
(componentMask & 8) != 0);
_framebuffer.AttachColorLayerForClear(index, layer);
float[] colors = new float[] { color.Red, color.Green, color.Blue, color.Alpha };
GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors);
_framebuffer.DetachColorLayerForClear(index);
RestoreComponentMask(index);
}
public void ClearRenderTargetDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask)
public void ClearRenderTargetDepthStencil(int layer, float depthValue, bool depthMask, int stencilValue, int stencilMask)
{
bool stencilMaskChanged =
stencilMask != 0 &&
@ -144,6 +148,8 @@ namespace Ryujinx.Graphics.OpenGL
GL.DepthMask(depthMask);
}
_framebuffer.AttachDepthStencilLayerForClear(layer);
if (depthMask && stencilMask != 0)
{
GL.ClearBuffer(ClearBufferCombined.DepthStencil, 0, depthValue, stencilValue);
@ -157,6 +163,8 @@ namespace Ryujinx.Graphics.OpenGL
GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Stencil, 0, ref stencilValue);
}
_framebuffer.DetachDepthStencilLayerForClear();
if (stencilMaskChanged)
{
GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask);
@ -597,6 +605,8 @@ namespace Ryujinx.Graphics.OpenGL
GL.EndTransformFeedback();
}
GL.ClipControl(ClipOrigin.UpperLeft, ClipDepthMode.NegativeOneToOne);
_drawTexture.Draw(
view,
samp,
@ -627,6 +637,8 @@ namespace Ryujinx.Graphics.OpenGL
{
GL.BeginTransformFeedback(_tfTopology);
}
RestoreClipControl();
}
}
}

View File

@ -10,17 +10,22 @@ namespace Ryujinx.Graphics.Vic
{
static class Blender
{
public static void BlendOne(Surface dst, Surface src, ref SlotStruct slot)
public static void BlendOne(Surface dst, Surface src, ref SlotStruct slot, Rectangle targetRect)
{
if (Sse41.IsSupported && (dst.Width & 3) == 0)
int x1 = targetRect.X;
int y1 = targetRect.Y;
int x2 = Math.Min(src.Width, x1 + targetRect.Width);
int y2 = Math.Min(src.Height, y1 + targetRect.Height);
if (Sse41.IsSupported && ((x1 | x2) & 3) == 0)
{
BlendOneSse41(dst, src, ref slot);
BlendOneSse41(dst, src, ref slot, x1, y1, x2, y2);
return;
}
for (int y = 0; y < dst.Height; y++)
for (int y = y1; y < y2; y++)
{
for (int x = 0; x < dst.Width; x++)
for (int x = x1; x < x2; x++)
{
int inR = src.GetR(x, y);
int inG = src.GetG(x, y);
@ -40,9 +45,9 @@ namespace Ryujinx.Graphics.Vic
}
}
private unsafe static void BlendOneSse41(Surface dst, Surface src, ref SlotStruct slot)
private unsafe static void BlendOneSse41(Surface dst, Surface src, ref SlotStruct slot, int x1, int y1, int x2, int y2)
{
Debug.Assert((dst.Width & 3) == 0);
Debug.Assert(((x1 | x2) & 3) == 0);
ref MatrixStruct mtx = ref slot.ColorMatrixStruct;
@ -62,9 +67,9 @@ namespace Ryujinx.Graphics.Vic
Pixel* ip = srcPtr;
Pixel* op = dstPtr;
for (int y = 0; y < dst.Height; y++, ip += src.Width, op += dst.Width)
for (int y = y1; y < y2; y++, ip += src.Width, op += dst.Width)
{
for (int x = 0; x < dst.Width; x += 4)
for (int x = x1; x < x2; x += 4)
{
Vector128<int> pixel1 = Sse41.ConvertToVector128Int32((ushort*)(ip + (uint)x));
Vector128<int> pixel2 = Sse41.ConvertToVector128Int32((ushort*)(ip + (uint)x + 1));

View File

@ -0,0 +1,18 @@
namespace Ryujinx.Graphics.Vic
{
struct Rectangle
{
public readonly int X;
public readonly int Y;
public readonly int Width;
public readonly int Height;
public Rectangle(int x, int y, int width, int height)
{
X = x;
Y = y;
Width = width;
Height = height;
}
}
}

View File

@ -2,6 +2,7 @@
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Vic.Image;
using Ryujinx.Graphics.Vic.Types;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Vic
@ -47,7 +48,19 @@ namespace Ryujinx.Graphics.Vic
using Surface src = SurfaceReader.Read(_rm, ref slot.SlotConfig, ref slot.SlotSurfaceConfig, ref offsets);
Blender.BlendOne(output, src, ref slot);
int x1 = config.OutputConfig.TargetRectLeft;
int y1 = config.OutputConfig.TargetRectTop;
int x2 = config.OutputConfig.TargetRectRight + 1;
int y2 = config.OutputConfig.TargetRectBottom + 1;
int targetX = Math.Min(x1, x2);
int targetY = Math.Min(y1, y2);
int targetW = Math.Min(output.Width - targetX, Math.Abs(x2 - x1));
int targetH = Math.Min(output.Height - targetY, Math.Abs(y2 - y1));
Rectangle targetRect = new Rectangle(targetX, targetY, targetW, targetH);
Blender.BlendOne(output, src, ref slot, targetRect);
}
SurfaceWriter.Write(_rm, output, ref config.OutputSurfaceConfig, ref _state.State.SetOutputSurface);

View File

@ -735,11 +735,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
ulong argsPtr,
ulong stackTop,
int priority,
int cpuCore)
int cpuCore,
ThreadStart customThreadStart = null)
{
lock (_processLock)
{
return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this, ThreadType.User, null);
return thread.Initialize(entrypoint, argsPtr, stackTop, priority, cpuCore, this, ThreadType.User, customThreadStart);
}
}

View File

@ -2350,6 +2350,18 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
[PointerSized] ulong stackTop,
int priority,
int cpuCore)
{
return CreateThread(out handle, entrypoint, argsPtr, stackTop, priority, cpuCore, null);
}
public KernelResult CreateThread(
out int handle,
ulong entrypoint,
ulong argsPtr,
ulong stackTop,
int priority,
int cpuCore,
ThreadStart customThreadStart)
{
handle = 0;
@ -2386,7 +2398,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
argsPtr,
stackTop,
priority,
cpuCore);
cpuCore,
customThreadStart);
if (result == KernelResult.Success)
{

View File

@ -429,8 +429,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
Channel.PushHostCommandBuffer(CreateWaitCommandBuffer(header.Fence));
}
Channel.PushEntries(entries);
header.Fence.Id = _channelSyncpoint.Id;
if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement) || header.Flags.HasFlag(SubmitGpfifoFlags.IncrementWithValue))
@ -449,6 +447,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
header.Fence.Value = _device.System.HostSyncpoint.ReadSyncpointMaxValue(header.Fence.Id);
}
Channel.PushEntries(entries);
if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement))
{
Channel.PushHostCommandBuffer(CreateIncrementCommandBuffer(ref header.Fence, header.Flags));

View File

@ -1,3 +1,4 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Common;
@ -38,15 +39,15 @@ namespace Ryujinx.HLE.HOS.Services
private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
public ManualResetEvent InitDone { get; }
public Func<IpcService> SmObjectFactory { get; }
public string Name { get; }
public Func<IpcService> SmObjectFactory { get; }
public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
{
InitDone = new ManualResetEvent(false);
_context = context;
Name = name;
SmObjectFactory = smObjectFactory;
_context = context;
const ProcessCreationFlags flags =
ProcessCreationFlags.EnableAslr |
@ -56,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Services
ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, ServerLoop);
KernelStatic.StartInitialProcess(context, creationInfo, DefaultCapabilities, 44, Main);
}
private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
@ -80,6 +81,11 @@ namespace Ryujinx.HLE.HOS.Services
_sessions.Add(serverSessionHandle, obj);
}
private void Main()
{
ServerLoop();
}
private void ServerLoop()
{
_selfProcess = KernelStatic.GetCurrentProcess();

View File

@ -8,7 +8,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
private ulong _value;
private readonly EventFdFlags _flags;
private AutoResetEvent _event;
private object _lock = new object();
@ -19,9 +18,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
public EventFileDescriptor(ulong value, EventFdFlags flags)
{
// FIXME: We should support blocking operations.
// Right now they can't be supported because it would cause the
// service to lock up as we only have one thread processing requests.
flags |= EventFdFlags.NonBlocking;
_value = value;
_flags = flags;
_event = new AutoResetEvent(false);
WriteEvent = new ManualResetEvent(true);
ReadEvent = new ManualResetEvent(true);
@ -31,7 +34,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
public void Dispose()
{
_event.Dispose();
WriteEvent.Dispose();
ReadEvent.Dispose();
}
@ -57,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
while (_value == 0)
{
_event.WaitOne();
Monitor.Wait(_lock);
}
}
else
@ -106,7 +108,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
if (Blocking)
{
_event.WaitOne();
Monitor.Wait(_lock);
}
else
{
@ -119,7 +121,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
writeSize = sizeof(ulong);
_value += count;
_event.Set();
Monitor.Pulse(_lock);
WriteEvent.Set();

View File

@ -19,6 +19,8 @@ namespace Ryujinx.Memory
private ConcurrentDictionary<MemoryBlock, byte> _viewStorages;
private int _viewCount;
internal bool ForceWindows4KBView => _forceWindows4KBView;
/// <summary>
/// Pointer to the memory block data.
/// </summary>
@ -145,7 +147,7 @@ namespace Ryujinx.Memory
srcBlock.IncrementViewCount();
}
MemoryManagement.MapView(srcBlock._sharedMemory, srcOffset, GetPointerInternal(dstOffset, size), size, _forceWindows4KBView);
MemoryManagement.MapView(srcBlock._sharedMemory, srcOffset, GetPointerInternal(dstOffset, size), size, this);
}
/// <summary>
@ -156,7 +158,7 @@ namespace Ryujinx.Memory
/// <param name="size">Size of the range to be unmapped</param>
public void UnmapView(MemoryBlock srcBlock, ulong offset, ulong size)
{
MemoryManagement.UnmapView(srcBlock._sharedMemory, GetPointerInternal(offset, size), size, _forceWindows4KBView);
MemoryManagement.UnmapView(srcBlock._sharedMemory, GetPointerInternal(offset, size), size, this);
}
/// <summary>

View File

@ -68,17 +68,17 @@ namespace Ryujinx.Memory
}
}
public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr address, ulong size, bool force4KBMap)
public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr address, ulong size, MemoryBlock owner)
{
if (OperatingSystem.IsWindows())
{
if (force4KBMap)
if (owner.ForceWindows4KBView)
{
MemoryManagementWindows.MapView4KB(sharedMemory, srcOffset, address, (IntPtr)size);
}
else
{
MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (IntPtr)size);
MemoryManagementWindows.MapView(sharedMemory, srcOffset, address, (IntPtr)size, owner);
}
}
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
@ -91,17 +91,17 @@ namespace Ryujinx.Memory
}
}
public static void UnmapView(IntPtr sharedMemory, IntPtr address, ulong size, bool force4KBMap)
public static void UnmapView(IntPtr sharedMemory, IntPtr address, ulong size, MemoryBlock owner)
{
if (OperatingSystem.IsWindows())
{
if (force4KBMap)
if (owner.ForceWindows4KBView)
{
MemoryManagementWindows.UnmapView4KB(address, (IntPtr)size);
}
else
{
MemoryManagementWindows.UnmapView(sharedMemory, address, (IntPtr)size);
MemoryManagementWindows.UnmapView(sharedMemory, address, (IntPtr)size, owner);
}
}
else if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())

View File

@ -68,9 +68,9 @@ namespace Ryujinx.Memory
return WindowsApi.VirtualFree(location, size, AllocationType.Decommit);
}
public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size)
public static void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, MemoryBlock owner)
{
_placeholders.MapView(sharedMemory, srcOffset, location, size);
_placeholders.MapView(sharedMemory, srcOffset, location, size, owner);
}
public static void MapView4KB(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size)
@ -106,9 +106,9 @@ namespace Ryujinx.Memory
}
}
public static void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size)
public static void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner)
{
_placeholders.UnmapView(sharedMemory, location, size);
_placeholders.UnmapView(sharedMemory, location, size, owner);
}
public static void UnmapView4KB(IntPtr location, IntPtr size)
@ -154,7 +154,7 @@ namespace Ryujinx.Memory
}
else
{
_placeholders.UnmapView(IntPtr.Zero, address, size);
_placeholders.UnreserveRange((ulong)address, (ulong)size);
}
return WindowsApi.VirtualFree(address, IntPtr.Zero, AllocationType.Release);

View File

@ -44,6 +44,50 @@ namespace Ryujinx.Memory.WindowsShared
}
}
/// <summary>
/// Unreserves a range of memory that has been previously reserved with <see cref="ReserveRange"/>.
/// </summary>
/// <param name="address">Start address of the region to unreserve</param>
/// <param name="size">Size in bytes of the region to unreserve</param>
/// <exception cref="WindowsApiException">Thrown when the Windows API returns an error unreserving the memory</exception>
public void UnreserveRange(ulong address, ulong size)
{
ulong endAddress = address + size;
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
int count;
lock (_mappings)
{
count = _mappings.Get(address, endAddress, ref overlaps);
for (int index = 0; index < count; index++)
{
var overlap = overlaps[index];
if (IsMapped(overlap.Value))
{
if (!WindowsApi.UnmapViewOfFile2(WindowsApi.CurrentProcessHandle, (IntPtr)overlap.Start, 2))
{
throw new WindowsApiException("UnmapViewOfFile2");
}
}
_mappings.Remove(overlap);
}
}
if (count > 1)
{
CheckFreeResult(WindowsApi.VirtualFree(
(IntPtr)address,
(IntPtr)size,
AllocationType.Release | AllocationType.CoalescePlaceholders));
}
RemoveProtection(address, size);
}
/// <summary>
/// Maps a shared memory view on a previously reserved memory region.
/// </summary>
@ -51,13 +95,14 @@ namespace Ryujinx.Memory.WindowsShared
/// <param name="srcOffset">Offset in the shared memory to map</param>
/// <param name="location">Address to map the view into</param>
/// <param name="size">Size of the view in bytes</param>
public void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size)
/// <param name="owner">Memory block that owns the mapping</param>
public void MapView(IntPtr sharedMemory, ulong srcOffset, IntPtr location, IntPtr size, MemoryBlock owner)
{
_partialUnmapLock.AcquireReaderLock(Timeout.Infinite);
try
{
UnmapViewInternal(sharedMemory, location, size);
UnmapViewInternal(sharedMemory, location, size, owner);
MapViewInternal(sharedMemory, srcOffset, location, size);
}
finally
@ -173,13 +218,14 @@ namespace Ryujinx.Memory.WindowsShared
/// <param name="sharedMemory">Shared memory that the view being unmapped belongs to</param>
/// <param name="location">Address to unmap</param>
/// <param name="size">Size of the region to unmap in bytes</param>
public void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size)
/// <param name="owner">Memory block that owns the mapping</param>
public void UnmapView(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner)
{
_partialUnmapLock.AcquireReaderLock(Timeout.Infinite);
try
{
UnmapViewInternal(sharedMemory, location, size);
UnmapViewInternal(sharedMemory, location, size, owner);
}
finally
{
@ -197,8 +243,9 @@ namespace Ryujinx.Memory.WindowsShared
/// <param name="sharedMemory">Shared memory that the view being unmapped belongs to</param>
/// <param name="location">Address to unmap</param>
/// <param name="size">Size of the region to unmap in bytes</param>
/// <param name="owner">Memory block that owns the mapping</param>
/// <exception cref="WindowsApiException">Thrown when the Windows API returns an error unmapping or remapping the memory</exception>
private void UnmapViewInternal(IntPtr sharedMemory, IntPtr location, IntPtr size)
private void UnmapViewInternal(IntPtr sharedMemory, IntPtr location, IntPtr size, MemoryBlock owner)
{
ulong startAddress = (ulong)location;
ulong unmapSize = (ulong)size;
@ -272,7 +319,7 @@ namespace Ryujinx.Memory.WindowsShared
}
}
CoalesceForUnmap(startAddress, unmapSize);
CoalesceForUnmap(startAddress, unmapSize, owner);
RemoveProtection(startAddress, unmapSize);
}
@ -281,15 +328,21 @@ namespace Ryujinx.Memory.WindowsShared
/// </summary>
/// <param name="address">Address of the region that was unmapped</param>
/// <param name="size">Size of the region that was unmapped in bytes</param>
private void CoalesceForUnmap(ulong address, ulong size)
/// <param name="owner">Memory block that owns the mapping</param>
private void CoalesceForUnmap(ulong address, ulong size, MemoryBlock owner)
{
ulong endAddress = address + size;
ulong blockAddress = (ulong)owner.Pointer;
ulong blockEnd = blockAddress + owner.Size;
var overlaps = Array.Empty<IntervalTreeNode<ulong, ulong>>();
int unmappedCount = 0;
lock (_mappings)
{
int count = _mappings.Get(address - MinimumPageSize, endAddress + MinimumPageSize, ref overlaps);
int count = _mappings.Get(
Math.Max(address - MinimumPageSize, blockAddress),
Math.Min(endAddress + MinimumPageSize, blockEnd), ref overlaps);
if (count < 2)
{
// Nothing to coalesce if we only have 1 or no overlaps.