Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
d987cacfb7 | |||
851f56b08a | |||
b1bd6a50b5 | |||
70895bdb04 | |||
830cbf91bb | |||
9a9349f0f4 | |||
46cc7b55f0 | |||
dd8f97ab9e | |||
633c5ec330 | |||
a3e7bb8eb4 | |||
2073ba2919 | |||
d03124a992 |
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
98
Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs
Normal file
98
Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
|
18
Ryujinx.Graphics.Vic/Rectangle.cs
Normal file
18
Ryujinx.Graphics.Vic/Rectangle.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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())
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
Reference in New Issue
Block a user