Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
7d8e198c33 | ||
|
3d98e1361b | ||
|
141cf61ff7 | ||
|
3fe3598d41 | ||
|
59cdf310bd | ||
|
4e34170a84 | ||
|
d540af5dc0 |
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
@@ -57,6 +57,7 @@ jobs:
|
|||||||
- name: Get git short hash
|
- name: Get git short hash
|
||||||
id: git_short_hash
|
id: git_short_hash
|
||||||
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
|
||||||
|
shell: bash
|
||||||
- name: Clear
|
- name: Clear
|
||||||
run: dotnet clean && dotnet nuget locals all --clear
|
run: dotnet clean && dotnet nuget locals all --clear
|
||||||
- name: Build
|
- name: Build
|
||||||
|
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 2;
|
private const ushort FileFormatVersionMinor = 2;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||||
private const uint CodeGenVersion = 3781;
|
private const uint CodeGenVersion = 3807;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@@ -7,7 +7,9 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
|
|||||||
{
|
{
|
||||||
unsafe class FFmpegContext : IDisposable
|
unsafe class FFmpegContext : IDisposable
|
||||||
{
|
{
|
||||||
private readonly FFCodec.AVCodec_decode _decodeFrame;
|
private unsafe delegate int AVCodec_decode(AVCodecContext* avctx, void* outdata, int* got_frame_ptr, AVPacket* avpkt);
|
||||||
|
|
||||||
|
private readonly AVCodec_decode _decodeFrame;
|
||||||
private static readonly FFmpegApi.av_log_set_callback_callback _logFunc;
|
private static readonly FFmpegApi.av_log_set_callback_callback _logFunc;
|
||||||
private readonly AVCodec* _codec;
|
private readonly AVCodec* _codec;
|
||||||
private AVPacket* _packet;
|
private AVPacket* _packet;
|
||||||
@@ -53,17 +55,17 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
|
|||||||
// libavcodec 59.24 changed AvCodec to move its private API and also move the codec function to an union.
|
// libavcodec 59.24 changed AvCodec to move its private API and also move the codec function to an union.
|
||||||
if (avCodecMajorVersion > 59 || (avCodecMajorVersion == 59 && avCodecMinorVersion > 24))
|
if (avCodecMajorVersion > 59 || (avCodecMajorVersion == 59 && avCodecMinorVersion > 24))
|
||||||
{
|
{
|
||||||
_decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodec*)_codec)->CodecCallback);
|
_decodeFrame = Marshal.GetDelegateForFunctionPointer<AVCodec_decode>(((FFCodec<AVCodec>*)_codec)->CodecCallback);
|
||||||
}
|
}
|
||||||
// libavcodec 59.x changed AvCodec private API layout.
|
// libavcodec 59.x changed AvCodec private API layout.
|
||||||
else if (avCodecMajorVersion == 59)
|
else if (avCodecMajorVersion == 59)
|
||||||
{
|
{
|
||||||
_decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodecLegacy<AVCodec>*)_codec)->Decode);
|
_decodeFrame = Marshal.GetDelegateForFunctionPointer<AVCodec_decode>(((FFCodecLegacy<AVCodec501>*)_codec)->Decode);
|
||||||
}
|
}
|
||||||
// libavcodec 58.x and lower
|
// libavcodec 58.x and lower
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodecLegacy<AVCodecLegacy>*)_codec)->Decode);
|
_decodeFrame = Marshal.GetDelegateForFunctionPointer<AVCodec_decode>(((FFCodecLegacy<AVCodec>*)_codec)->Decode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
|||||||
public unsafe IntPtr PrivClass;
|
public unsafe IntPtr PrivClass;
|
||||||
public IntPtr Profiles;
|
public IntPtr Profiles;
|
||||||
public unsafe byte* WrapperName;
|
public unsafe byte* WrapperName;
|
||||||
|
public IntPtr ChLayouts;
|
||||||
#pragma warning restore CS0649
|
#pragma warning restore CS0649
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
||||||
{
|
{
|
||||||
struct AVCodecLegacy
|
struct AVCodec501
|
||||||
{
|
{
|
||||||
#pragma warning disable CS0649
|
#pragma warning disable CS0649
|
||||||
public unsafe byte* Name;
|
public unsafe byte* Name;
|
||||||
@@ -20,7 +20,6 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
|||||||
public unsafe IntPtr PrivClass;
|
public unsafe IntPtr PrivClass;
|
||||||
public IntPtr Profiles;
|
public IntPtr Profiles;
|
||||||
public unsafe byte* WrapperName;
|
public unsafe byte* WrapperName;
|
||||||
public IntPtr ChLayouts;
|
|
||||||
#pragma warning restore CS0649
|
#pragma warning restore CS0649
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
|||||||
public unsafe IntPtr AvClass;
|
public unsafe IntPtr AvClass;
|
||||||
public int LogLevelOffset;
|
public int LogLevelOffset;
|
||||||
public int CodecType;
|
public int CodecType;
|
||||||
public unsafe AVCodecLegacy* Codec;
|
public unsafe AVCodec* Codec;
|
||||||
public AVCodecID CodecId;
|
public AVCodecID CodecId;
|
||||||
public uint CodecTag;
|
public uint CodecTag;
|
||||||
public IntPtr PrivData;
|
public IntPtr PrivData;
|
||||||
|
@@ -2,12 +2,10 @@
|
|||||||
|
|
||||||
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
|
||||||
{
|
{
|
||||||
struct FFCodec
|
struct FFCodec<T> where T: struct
|
||||||
{
|
{
|
||||||
public unsafe delegate int AVCodec_decode(AVCodecContext* avctx, void* outdata, int* got_frame_ptr, AVPacket* avpkt);
|
|
||||||
|
|
||||||
#pragma warning disable CS0649
|
#pragma warning disable CS0649
|
||||||
public AVCodec Base;
|
public T Base;
|
||||||
public int CapsInternalOrCbType;
|
public int CapsInternalOrCbType;
|
||||||
public int PrivDataSize;
|
public int PrivDataSize;
|
||||||
public IntPtr UpdateThreadContext;
|
public IntPtr UpdateThreadContext;
|
||||||
|
@@ -262,6 +262,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
Instruction ioVariable, elemIndex;
|
Instruction ioVariable, elemIndex;
|
||||||
|
|
||||||
|
Instruction invocationId = null;
|
||||||
|
|
||||||
|
if (Config.Stage == ShaderStage.TessellationControl && isOutAttr)
|
||||||
|
{
|
||||||
|
invocationId = Load(TypeS32(), Inputs[AttributeConsts.InvocationId]);
|
||||||
|
}
|
||||||
|
|
||||||
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
|
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
|
||||||
|
|
||||||
if (isUserAttr &&
|
if (isUserAttr &&
|
||||||
@@ -273,7 +280,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
|
elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
|
||||||
var vecIndex = Constant(TypeU32(), (attr - AttributeConsts.UserAttributeBase) >> 4);
|
var vecIndex = Constant(TypeU32(), (attr - AttributeConsts.UserAttributeBase) >> 4);
|
||||||
|
|
||||||
if (AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr))
|
bool isArray = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr);
|
||||||
|
|
||||||
|
if (invocationId != null && isArray)
|
||||||
|
{
|
||||||
|
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, vecIndex, elemIndex);
|
||||||
|
}
|
||||||
|
else if (invocationId != null)
|
||||||
|
{
|
||||||
|
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, vecIndex, elemIndex);
|
||||||
|
}
|
||||||
|
else if (isArray)
|
||||||
{
|
{
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex);
|
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex);
|
||||||
}
|
}
|
||||||
@@ -308,12 +325,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
if ((type & (AggregateType.Array | AggregateType.Vector)) == 0)
|
if ((type & (AggregateType.Array | AggregateType.Vector)) == 0)
|
||||||
{
|
{
|
||||||
return isIndexed ? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index) : ioVariable;
|
if (invocationId != null)
|
||||||
|
{
|
||||||
|
return isIndexed
|
||||||
|
? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index)
|
||||||
|
: AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return isIndexed ? AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index) : ioVariable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
|
elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
|
||||||
|
|
||||||
if (isIndexed)
|
if (invocationId != null && isIndexed)
|
||||||
|
{
|
||||||
|
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, elemIndex);
|
||||||
|
}
|
||||||
|
else if (invocationId != null)
|
||||||
|
{
|
||||||
|
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, elemIndex);
|
||||||
|
}
|
||||||
|
else if (isIndexed)
|
||||||
{
|
{
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, elemIndex);
|
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, elemIndex);
|
||||||
}
|
}
|
||||||
@@ -327,12 +361,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
{
|
{
|
||||||
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
|
var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
|
||||||
|
|
||||||
|
Instruction invocationId = null;
|
||||||
|
|
||||||
|
if (Config.Stage == ShaderStage.TessellationControl && isOutAttr)
|
||||||
|
{
|
||||||
|
invocationId = Load(TypeS32(), Inputs[AttributeConsts.InvocationId]);
|
||||||
|
}
|
||||||
|
|
||||||
elemType = AggregateType.FP32;
|
elemType = AggregateType.FP32;
|
||||||
var ioVariable = isOutAttr ? OutputsArray : InputsArray;
|
var ioVariable = isOutAttr ? OutputsArray : InputsArray;
|
||||||
var vecIndex = ShiftRightLogical(TypeS32(), attrIndex, Constant(TypeS32(), 2));
|
var vecIndex = ShiftRightLogical(TypeS32(), attrIndex, Constant(TypeS32(), 2));
|
||||||
var elemIndex = BitwiseAnd(TypeS32(), attrIndex, Constant(TypeS32(), 3));
|
var elemIndex = BitwiseAnd(TypeS32(), attrIndex, Constant(TypeS32(), 3));
|
||||||
|
|
||||||
if (AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr))
|
bool isArray = AttributeInfo.IsArrayAttributeSpirv(Config.Stage, isOutAttr);
|
||||||
|
|
||||||
|
if (invocationId != null && isArray)
|
||||||
|
{
|
||||||
|
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, index, vecIndex, elemIndex);
|
||||||
|
}
|
||||||
|
else if (invocationId != null)
|
||||||
|
{
|
||||||
|
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, invocationId, vecIndex, elemIndex);
|
||||||
|
}
|
||||||
|
else if (isArray)
|
||||||
{
|
{
|
||||||
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex);
|
return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, index, vecIndex, elemIndex);
|
||||||
}
|
}
|
||||||
|
@@ -473,6 +473,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
|
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
|
||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
|
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
|
||||||
|
|
||||||
|
if (context.Config.Stage == ShaderStage.TessellationControl)
|
||||||
|
{
|
||||||
|
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
|
||||||
|
}
|
||||||
|
|
||||||
var spvType = context.TypePointer(StorageClass.Output, attrType);
|
var spvType = context.TypePointer(StorageClass.Output, attrType);
|
||||||
var spvVar = context.Variable(spvType, StorageClass.Output);
|
var spvVar = context.Variable(spvType, StorageClass.Output);
|
||||||
|
|
||||||
@@ -543,6 +548,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr && !perPatch)
|
||||||
|
{
|
||||||
|
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
|
||||||
|
}
|
||||||
|
|
||||||
var spvType = context.TypePointer(storageClass, attrType);
|
var spvType = context.TypePointer(storageClass, attrType);
|
||||||
var spvVar = context.Variable(spvType, storageClass);
|
var spvVar = context.Variable(spvType, storageClass);
|
||||||
|
|
||||||
@@ -634,6 +644,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize));
|
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)arraySize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context.Config.Stage == ShaderStage.TessellationControl && isOutAttr)
|
||||||
|
{
|
||||||
|
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
|
||||||
|
}
|
||||||
|
|
||||||
var spvType = context.TypePointer(storageClass, attrType);
|
var spvType = context.TypePointer(storageClass, attrType);
|
||||||
var spvVar = context.Variable(spvType, storageClass);
|
var spvVar = context.Variable(spvType, storageClass);
|
||||||
|
|
||||||
|
@@ -37,7 +37,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
|
|
||||||
Config = config;
|
Config = config;
|
||||||
|
|
||||||
if (config.GpPassthrough)
|
if (config.Stage == ShaderStage.TessellationControl)
|
||||||
|
{
|
||||||
|
// Required to index outputs.
|
||||||
|
Info.Inputs.Add(AttributeConsts.InvocationId);
|
||||||
|
}
|
||||||
|
else if (config.GpPassthrough)
|
||||||
{
|
{
|
||||||
int passthroughAttributes = config.PassthroughAttributes;
|
int passthroughAttributes = config.PassthroughAttributes;
|
||||||
while (passthroughAttributes != 0)
|
while (passthroughAttributes != 0)
|
||||||
|
@@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
"VUID-VkSubpassDependency-srcSubpass-00867"
|
"VUID-VkSubpassDependency-srcSubpass-00867"
|
||||||
};
|
};
|
||||||
|
|
||||||
internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions, out ExtDebugReport debugReport, out DebugReportCallbackEXT debugReportCallback)
|
internal static Instance CreateInstance(Vk api, GraphicsDebugLevel logLevel, string[] requiredExtensions, out ExtDebugUtils debugUtils, out DebugUtilsMessengerEXT debugUtilsMessenger)
|
||||||
{
|
{
|
||||||
var enabledLayers = new List<string>();
|
var enabledLayers = new List<string>();
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
AddAvailableLayer("VK_LAYER_KHRONOS_validation");
|
AddAvailableLayer("VK_LAYER_KHRONOS_validation");
|
||||||
}
|
}
|
||||||
|
|
||||||
var enabledExtensions = requiredExtensions.Append(ExtDebugReport.ExtensionName).ToArray();
|
var enabledExtensions = requiredExtensions.Append(ExtDebugUtils.ExtensionName).ToArray();
|
||||||
|
|
||||||
var appName = Marshal.StringToHGlobalAnsi(AppName);
|
var appName = Marshal.StringToHGlobalAnsi(AppName);
|
||||||
|
|
||||||
@@ -139,22 +139,18 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Marshal.FreeHGlobal(ppEnabledLayers[i]);
|
Marshal.FreeHGlobal(ppEnabledLayers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateDebugCallbacks(api, logLevel, instance, out debugReport, out debugReportCallback);
|
CreateDebugMessenger(api, logLevel, instance, out debugUtils, out debugUtilsMessenger);
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe static uint DebugReport(
|
private unsafe static uint DebugMessenger(
|
||||||
uint flags,
|
DebugUtilsMessageSeverityFlagsEXT messageSeverity,
|
||||||
DebugReportObjectTypeEXT objectType,
|
DebugUtilsMessageTypeFlagsEXT messageTypes,
|
||||||
ulong @object,
|
DebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
||||||
nuint location,
|
void* pUserData)
|
||||||
int messageCode,
|
|
||||||
byte* layerPrefix,
|
|
||||||
byte* message,
|
|
||||||
void* userData)
|
|
||||||
{
|
{
|
||||||
var msg = Marshal.PtrToStringAnsi((IntPtr)message);
|
var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage);
|
||||||
|
|
||||||
foreach (string excludedMessagePart in _excludedMessages)
|
foreach (string excludedMessagePart in _excludedMessages)
|
||||||
{
|
{
|
||||||
@@ -164,26 +160,20 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugReportFlagsEXT debugFlags = (DebugReportFlagsEXT)flags;
|
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt))
|
||||||
|
|
||||||
if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportErrorBitExt))
|
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Gpu, msg);
|
Logger.Error?.Print(LogClass.Gpu, msg);
|
||||||
//throw new Exception(msg);
|
//throw new Exception(msg);
|
||||||
}
|
}
|
||||||
else if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportWarningBitExt))
|
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt))
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Gpu, msg);
|
Logger.Warning?.Print(LogClass.Gpu, msg);
|
||||||
}
|
}
|
||||||
else if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportInformationBitExt))
|
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityInfoBitExt))
|
||||||
{
|
{
|
||||||
Logger.Info?.Print(LogClass.Gpu, msg);
|
Logger.Info?.Print(LogClass.Gpu, msg);
|
||||||
}
|
}
|
||||||
else if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportPerformanceWarningBitExt))
|
else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityVerboseBitExt))
|
||||||
{
|
|
||||||
Logger.Warning?.Print(LogClass.Gpu, msg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
Logger.Debug?.Print(LogClass.Gpu, msg);
|
Logger.Debug?.Print(LogClass.Gpu, msg);
|
||||||
}
|
}
|
||||||
@@ -551,46 +541,59 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return new CommandBufferPool(api, device, queue, queueLock, queueFamilyIndex);
|
return new CommandBufferPool(api, device, queue, queueLock, queueFamilyIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal unsafe static void CreateDebugCallbacks(
|
internal unsafe static void CreateDebugMessenger(
|
||||||
Vk api,
|
Vk api,
|
||||||
GraphicsDebugLevel logLevel,
|
GraphicsDebugLevel logLevel,
|
||||||
Instance instance,
|
Instance instance,
|
||||||
out ExtDebugReport debugReport,
|
out ExtDebugUtils debugUtils,
|
||||||
out DebugReportCallbackEXT debugReportCallback)
|
out DebugUtilsMessengerEXT debugUtilsMessenger)
|
||||||
{
|
{
|
||||||
debugReport = default;
|
debugUtils = default;
|
||||||
|
|
||||||
if (logLevel != GraphicsDebugLevel.None)
|
if (logLevel != GraphicsDebugLevel.None)
|
||||||
{
|
{
|
||||||
if (!api.TryGetInstanceExtension(instance, out debugReport))
|
if (!api.TryGetInstanceExtension(instance, out debugUtils))
|
||||||
{
|
{
|
||||||
debugReportCallback = default;
|
debugUtilsMessenger = default;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var flags = logLevel switch
|
var filterLogType = logLevel switch
|
||||||
{
|
{
|
||||||
GraphicsDebugLevel.Error => DebugReportFlagsEXT.DebugReportErrorBitExt,
|
GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt,
|
||||||
GraphicsDebugLevel.Slowdowns => DebugReportFlagsEXT.DebugReportErrorBitExt | DebugReportFlagsEXT.DebugReportPerformanceWarningBitExt,
|
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt |
|
||||||
GraphicsDebugLevel.All => DebugReportFlagsEXT.DebugReportInformationBitExt |
|
DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypePerformanceBitExt,
|
||||||
DebugReportFlagsEXT.DebugReportWarningBitExt |
|
GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeGeneralBitExt |
|
||||||
DebugReportFlagsEXT.DebugReportPerformanceWarningBitExt |
|
DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt |
|
||||||
DebugReportFlagsEXT.DebugReportErrorBitExt |
|
DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypePerformanceBitExt,
|
||||||
DebugReportFlagsEXT.DebugReportDebugBitExt,
|
|
||||||
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
|
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
|
||||||
};
|
};
|
||||||
var debugReportCallbackCreateInfo = new DebugReportCallbackCreateInfoEXT()
|
|
||||||
|
var filterLogSeverity = logLevel switch
|
||||||
{
|
{
|
||||||
SType = StructureType.DebugReportCallbackCreateInfoExt,
|
GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt,
|
||||||
Flags = flags,
|
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt |
|
||||||
PfnCallback = new PfnDebugReportCallbackEXT(DebugReport)
|
DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt,
|
||||||
|
GraphicsDebugLevel.All => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityInfoBitExt |
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt |
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityVerboseBitExt |
|
||||||
|
DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt,
|
||||||
|
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
|
||||||
};
|
};
|
||||||
|
|
||||||
debugReport.CreateDebugReportCallback(instance, in debugReportCallbackCreateInfo, null, out debugReportCallback).ThrowOnError();
|
var debugUtilsMessengerCreateInfo = new DebugUtilsMessengerCreateInfoEXT()
|
||||||
|
{
|
||||||
|
SType = StructureType.DebugUtilsMessengerCreateInfoExt,
|
||||||
|
MessageType = filterLogType,
|
||||||
|
MessageSeverity = filterLogSeverity,
|
||||||
|
PfnUserCallback = new PfnDebugUtilsMessengerCallbackEXT(DebugMessenger)
|
||||||
|
};
|
||||||
|
|
||||||
|
debugUtils.CreateDebugUtilsMessenger(instance, in debugUtilsMessengerCreateInfo, null, out debugUtilsMessenger).ThrowOnError();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
debugReportCallback = default;
|
debugUtilsMessenger = default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
internal KhrPushDescriptor PushDescriptorApi { get; private set; }
|
internal KhrPushDescriptor PushDescriptorApi { get; private set; }
|
||||||
internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
|
internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
|
||||||
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
|
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
|
||||||
internal ExtDebugReport DebugReportApi { get; private set; }
|
internal ExtDebugUtils DebugUtilsApi { get; private set; }
|
||||||
|
|
||||||
internal uint QueueFamilyIndex { get; private set; }
|
internal uint QueueFamilyIndex { get; private set; }
|
||||||
internal Queue Queue { get; private set; }
|
internal Queue Queue { get; private set; }
|
||||||
@@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private SyncManager _syncManager;
|
private SyncManager _syncManager;
|
||||||
|
|
||||||
private PipelineFull _pipeline;
|
private PipelineFull _pipeline;
|
||||||
private DebugReportCallbackEXT _debugReportCallback;
|
private DebugUtilsMessengerEXT _debugUtilsMessenger;
|
||||||
|
|
||||||
internal HelperShader HelperShader { get; private set; }
|
internal HelperShader HelperShader { get; private set; }
|
||||||
internal PipelineFull PipelineInternal => _pipeline;
|
internal PipelineFull PipelineInternal => _pipeline;
|
||||||
@@ -237,9 +237,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
Api = api;
|
Api = api;
|
||||||
|
|
||||||
_instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions(), out ExtDebugReport debugReport, out _debugReportCallback);
|
_instance = VulkanInitialization.CreateInstance(api, logLevel, _getRequiredExtensions(), out ExtDebugUtils debugUtils, out _debugUtilsMessenger);
|
||||||
|
|
||||||
DebugReportApi = debugReport;
|
DebugUtilsApi = debugUtils;
|
||||||
|
|
||||||
if (api.TryGetInstanceExtension(_instance, out KhrSurface surfaceApi))
|
if (api.TryGetInstanceExtension(_instance, out KhrSurface surfaceApi))
|
||||||
{
|
{
|
||||||
@@ -584,9 +584,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
MemoryAllocator.Dispose();
|
MemoryAllocator.Dispose();
|
||||||
|
|
||||||
if (_debugReportCallback.Handle != 0)
|
if (_debugUtilsMessenger.Handle != 0)
|
||||||
{
|
{
|
||||||
DebugReportApi.DestroyDebugReportCallback(_instance, _debugReportCallback, null);
|
DebugUtilsApi.DestroyDebugUtilsMessenger(_instance, _debugUtilsMessenger, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var shader in Shaders)
|
foreach (var shader in Shaders)
|
||||||
|
@@ -32,6 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
|||||||
private bool _handlesRequestToDisplay = false;
|
private bool _handlesRequestToDisplay = false;
|
||||||
private bool _autoSleepDisabled = false;
|
private bool _autoSleepDisabled = false;
|
||||||
private bool _albumImageTakenNotificationEnabled = false;
|
private bool _albumImageTakenNotificationEnabled = false;
|
||||||
|
private bool _recordVolumeMuted = false;
|
||||||
|
|
||||||
private uint _screenShotImageOrientation = 0;
|
private uint _screenShotImageOrientation = 0;
|
||||||
private uint _idleTimeDetectionExtension = 0;
|
private uint _idleTimeDetectionExtension = 0;
|
||||||
@@ -389,5 +390,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
|||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandHipc(130)] // 13.0.0+
|
||||||
|
// SetRecordVolumeMuted(b8)
|
||||||
|
public ResultCode SetRecordVolumeMuted(ServiceCtx context)
|
||||||
|
{
|
||||||
|
bool recordVolumeMuted = context.RequestData.ReadBoolean();
|
||||||
|
|
||||||
|
Logger.Stub?.PrintStub(LogClass.ServiceAm, new { recordVolumeMuted });
|
||||||
|
|
||||||
|
_recordVolumeMuted = recordVolumeMuted;
|
||||||
|
|
||||||
|
return ResultCode.Success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -26,7 +26,7 @@
|
|||||||
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.15.0" />
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.25.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Due to Concentus. -->
|
<!-- Due to Concentus. -->
|
||||||
|
199
Ryujinx.Memory/Tracking/BitMap.cs
Normal file
199
Ryujinx.Memory/Tracking/BitMap.cs
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Memory.Tracking
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A bitmap that can check or set large ranges of true/false values at once.
|
||||||
|
/// </summary>
|
||||||
|
struct BitMap
|
||||||
|
{
|
||||||
|
public const int IntSize = 64;
|
||||||
|
|
||||||
|
private const int IntShift = 6;
|
||||||
|
private const int IntMask = IntSize - 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Masks representing the bitmap. Least significant bit first, 64-bits per mask.
|
||||||
|
/// </summary>
|
||||||
|
public readonly long[] Masks;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new bitmap.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="count">The number of bits to reserve</param>
|
||||||
|
public BitMap(int count)
|
||||||
|
{
|
||||||
|
Masks = new long[(count + IntMask) / IntSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if any bit in the bitmap is set.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if any bits are set, false otherwise</returns>
|
||||||
|
public bool AnySet()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Masks.Length; i++)
|
||||||
|
{
|
||||||
|
if (Masks[i] != 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if a bit in the bitmap is set.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bit">The bit index to check</param>
|
||||||
|
/// <returns>True if the bit is set, false otherwise</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public bool IsSet(int bit)
|
||||||
|
{
|
||||||
|
int wordIndex = bit >> IntShift;
|
||||||
|
int wordBit = bit & IntMask;
|
||||||
|
|
||||||
|
long wordMask = 1L << wordBit;
|
||||||
|
|
||||||
|
return (Masks[wordIndex] & wordMask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if any bit in a range of bits in the bitmap are set. (inclusive)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="start">The first bit index to check</param>
|
||||||
|
/// <param name="end">The last bit index to check</param>
|
||||||
|
/// <returns>True if a bit is set, false otherwise</returns>
|
||||||
|
public bool IsSet(int start, int end)
|
||||||
|
{
|
||||||
|
if (start == end)
|
||||||
|
{
|
||||||
|
return IsSet(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
int startIndex = start >> IntShift;
|
||||||
|
int startBit = start & IntMask;
|
||||||
|
long startMask = -1L << startBit;
|
||||||
|
|
||||||
|
int endIndex = end >> IntShift;
|
||||||
|
int endBit = end & IntMask;
|
||||||
|
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
|
||||||
|
|
||||||
|
if (startIndex == endIndex)
|
||||||
|
{
|
||||||
|
return (Masks[startIndex] & startMask & endMask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((Masks[startIndex] & startMask) != 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = startIndex + 1; i < endIndex; i++)
|
||||||
|
{
|
||||||
|
if (Masks[i] != 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((Masks[endIndex] & endMask) != 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set a bit at a specific index to 1.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bit">The bit index to set</param>
|
||||||
|
/// <returns>True if the bit is set, false if it was already set</returns>
|
||||||
|
public bool Set(int bit)
|
||||||
|
{
|
||||||
|
int wordIndex = bit >> IntShift;
|
||||||
|
int wordBit = bit & IntMask;
|
||||||
|
|
||||||
|
long wordMask = 1L << wordBit;
|
||||||
|
|
||||||
|
if ((Masks[wordIndex] & wordMask) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Masks[wordIndex] |= wordMask;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set a range of bits in the bitmap to 1.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="start">The first bit index to set</param>
|
||||||
|
/// <param name="end">The last bit index to set</param>
|
||||||
|
public void SetRange(int start, int end)
|
||||||
|
{
|
||||||
|
if (start == end)
|
||||||
|
{
|
||||||
|
Set(start);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int startIndex = start >> IntShift;
|
||||||
|
int startBit = start & IntMask;
|
||||||
|
long startMask = -1L << startBit;
|
||||||
|
|
||||||
|
int endIndex = end >> IntShift;
|
||||||
|
int endBit = end & IntMask;
|
||||||
|
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
|
||||||
|
|
||||||
|
if (startIndex == endIndex)
|
||||||
|
{
|
||||||
|
Masks[startIndex] |= startMask & endMask;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Masks[startIndex] |= startMask;
|
||||||
|
|
||||||
|
for (int i = startIndex + 1; i < endIndex; i++)
|
||||||
|
{
|
||||||
|
Masks[i] |= -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Masks[endIndex] |= endMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clear a bit at a specific index to 0.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bit">The bit index to clear</param>
|
||||||
|
/// <returns>True if the bit was set, false if it was not</returns>
|
||||||
|
public bool Clear(int bit)
|
||||||
|
{
|
||||||
|
int wordIndex = bit >> IntShift;
|
||||||
|
int wordBit = bit & IntMask;
|
||||||
|
|
||||||
|
long wordMask = 1L << wordBit;
|
||||||
|
|
||||||
|
bool wasSet = (Masks[wordIndex] & wordMask) != 0;
|
||||||
|
|
||||||
|
Masks[wordIndex] &= ~wordMask;
|
||||||
|
|
||||||
|
return wasSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clear the bitmap entirely, setting all bits to 0.
|
||||||
|
/// </summary>
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Masks.Length; i++)
|
||||||
|
{
|
||||||
|
Masks[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
161
Ryujinx.Memory/Tracking/ConcurrentBitmap.cs
Normal file
161
Ryujinx.Memory/Tracking/ConcurrentBitmap.cs
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Ryujinx.Memory.Tracking
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A bitmap that can be safely modified from multiple threads.
|
||||||
|
/// </summary>
|
||||||
|
internal class ConcurrentBitmap
|
||||||
|
{
|
||||||
|
public const int IntSize = 64;
|
||||||
|
|
||||||
|
public const int IntShift = 6;
|
||||||
|
public const int IntMask = IntSize - 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Masks representing the bitmap. Least significant bit first, 64-bits per mask.
|
||||||
|
/// </summary>
|
||||||
|
public readonly long[] Masks;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new multithreaded bitmap.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="count">The number of bits to reserve</param>
|
||||||
|
/// <param name="set">Whether the bits should be initially set or not</param>
|
||||||
|
public ConcurrentBitmap(int count, bool set)
|
||||||
|
{
|
||||||
|
Masks = new long[(count + IntMask) / IntSize];
|
||||||
|
|
||||||
|
if (set)
|
||||||
|
{
|
||||||
|
Array.Fill(Masks, -1L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if any bit in the bitmap is set.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if any bits are set, false otherwise</returns>
|
||||||
|
public bool AnySet()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Masks.Length; i++)
|
||||||
|
{
|
||||||
|
if (Volatile.Read(ref Masks[i]) != 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if a bit in the bitmap is set.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bit">The bit index to check</param>
|
||||||
|
/// <returns>True if the bit is set, false otherwise</returns>
|
||||||
|
public bool IsSet(int bit)
|
||||||
|
{
|
||||||
|
int wordIndex = bit >> IntShift;
|
||||||
|
int wordBit = bit & IntMask;
|
||||||
|
|
||||||
|
long wordMask = 1L << wordBit;
|
||||||
|
|
||||||
|
return (Volatile.Read(ref Masks[wordIndex]) & wordMask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if any bit in a range of bits in the bitmap are set. (inclusive)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="start">The first bit index to check</param>
|
||||||
|
/// <param name="end">The last bit index to check</param>
|
||||||
|
/// <returns>True if a bit is set, false otherwise</returns>
|
||||||
|
public bool IsSet(int start, int end)
|
||||||
|
{
|
||||||
|
if (start == end)
|
||||||
|
{
|
||||||
|
return IsSet(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
int startIndex = start >> IntShift;
|
||||||
|
int startBit = start & IntMask;
|
||||||
|
long startMask = -1L << startBit;
|
||||||
|
|
||||||
|
int endIndex = end >> IntShift;
|
||||||
|
int endBit = end & IntMask;
|
||||||
|
long endMask = (long)(ulong.MaxValue >> (IntMask - endBit));
|
||||||
|
|
||||||
|
long startValue = Volatile.Read(ref Masks[startIndex]);
|
||||||
|
|
||||||
|
if (startIndex == endIndex)
|
||||||
|
{
|
||||||
|
return (startValue & startMask & endMask) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((startValue & startMask) != 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = startIndex + 1; i < endIndex; i++)
|
||||||
|
{
|
||||||
|
if (Volatile.Read(ref Masks[i]) != 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long endValue = Volatile.Read(ref Masks[endIndex]);
|
||||||
|
|
||||||
|
if ((endValue & endMask) != 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set a bit at a specific index to either true or false.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="bit">The bit index to set</param>
|
||||||
|
/// <param name="value">Whether the bit should be set or not</param>
|
||||||
|
public void Set(int bit, bool value)
|
||||||
|
{
|
||||||
|
int wordIndex = bit >> IntShift;
|
||||||
|
int wordBit = bit & IntMask;
|
||||||
|
|
||||||
|
long wordMask = 1L << wordBit;
|
||||||
|
|
||||||
|
long existing;
|
||||||
|
long newValue;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
existing = Volatile.Read(ref Masks[wordIndex]);
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
newValue = existing | wordMask;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newValue = existing & ~wordMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (Interlocked.CompareExchange(ref Masks[wordIndex], newValue, existing) != existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clear the bitmap entirely, setting all bits to 0.
|
||||||
|
/// </summary>
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Masks.Length; i++)
|
||||||
|
{
|
||||||
|
Volatile.Write(ref Masks[i], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -176,6 +176,26 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Obtains a memory tracking handle for the given virtual region. This should be disposed when finished with.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">CPU virtual address of the region</param>
|
||||||
|
/// <param name="size">Size of the region</param>
|
||||||
|
/// <param name="bitmap">The bitmap owning the dirty flag for this handle</param>
|
||||||
|
/// <param name="bit">The bit of this handle within the dirty flag</param>
|
||||||
|
/// <returns>The memory tracking handle</returns>
|
||||||
|
internal RegionHandle BeginTrackingBitmap(ulong address, ulong size, ConcurrentBitmap bitmap, int bit)
|
||||||
|
{
|
||||||
|
(address, size) = PageAlign(address, size);
|
||||||
|
|
||||||
|
lock (TrackingLock)
|
||||||
|
{
|
||||||
|
RegionHandle handle = new RegionHandle(this, address, size, bitmap, bit, _memoryManager.IsRangeMapped(address, size));
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Signal that a virtual memory event happened at the given location (one byte).
|
/// Signal that a virtual memory event happened at the given location (one byte).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -1,11 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Memory.Tracking
|
namespace Ryujinx.Memory.Tracking
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A region handle that tracks a large region using many smaller handles, to provide
|
/// A region handle that tracks a large region using many smaller handles, to provide
|
||||||
/// granular tracking that can be used to track partial updates.
|
/// granular tracking that can be used to track partial updates. Backed by a bitmap
|
||||||
|
/// to improve performance when scanning large regions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MultiRegionHandle : IMultiRegionHandle
|
public class MultiRegionHandle : IMultiRegionHandle
|
||||||
{
|
{
|
||||||
@@ -17,6 +21,12 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
private readonly ulong Granularity;
|
private readonly ulong Granularity;
|
||||||
private readonly ulong Size;
|
private readonly ulong Size;
|
||||||
|
|
||||||
|
private ConcurrentBitmap _dirtyBitmap;
|
||||||
|
|
||||||
|
private int _sequenceNumber;
|
||||||
|
private BitMap _sequenceNumberBitmap;
|
||||||
|
private int _uncheckedHandles;
|
||||||
|
|
||||||
public bool Dirty { get; private set; } = true;
|
public bool Dirty { get; private set; } = true;
|
||||||
|
|
||||||
internal MultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity)
|
internal MultiRegionHandle(MemoryTracking tracking, ulong address, ulong size, IEnumerable<IRegionHandle> handles, ulong granularity)
|
||||||
@@ -24,6 +34,9 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
_handles = new RegionHandle[size / granularity];
|
_handles = new RegionHandle[size / granularity];
|
||||||
Granularity = granularity;
|
Granularity = granularity;
|
||||||
|
|
||||||
|
_dirtyBitmap = new ConcurrentBitmap(_handles.Length, true);
|
||||||
|
_sequenceNumberBitmap = new BitMap(_handles.Length);
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
if (handles != null)
|
if (handles != null)
|
||||||
@@ -40,37 +53,43 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
// Fill any gap left before this handle.
|
// Fill any gap left before this handle.
|
||||||
while (i < startIndex)
|
while (i < startIndex)
|
||||||
{
|
{
|
||||||
RegionHandle fillHandle = tracking.BeginTracking(address + (ulong)i * granularity, granularity);
|
RegionHandle fillHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i);
|
||||||
fillHandle.Parent = this;
|
fillHandle.Parent = this;
|
||||||
_handles[i++] = fillHandle;
|
_handles[i++] = fillHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handle.Size == granularity)
|
lock (tracking.TrackingLock)
|
||||||
{
|
{
|
||||||
handle.Parent = this;
|
if (handle is RegionHandle bitHandle && handle.Size == granularity)
|
||||||
_handles[i++] = handle;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int endIndex = (int)((handle.EndAddress - address) / granularity);
|
|
||||||
|
|
||||||
while (i < endIndex)
|
|
||||||
{
|
{
|
||||||
RegionHandle splitHandle = tracking.BeginTracking(address + (ulong)i * granularity, granularity);
|
handle.Parent = this;
|
||||||
splitHandle.Parent = this;
|
|
||||||
|
|
||||||
splitHandle.Reprotect(handle.Dirty);
|
bitHandle.ReplaceBitmap(_dirtyBitmap, i);
|
||||||
|
|
||||||
RegionSignal signal = handle.PreAction;
|
_handles[i++] = bitHandle;
|
||||||
if (signal != null)
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int endIndex = (int)((handle.EndAddress - address) / granularity);
|
||||||
|
|
||||||
|
while (i < endIndex)
|
||||||
{
|
{
|
||||||
splitHandle.RegisterAction(signal);
|
RegionHandle splitHandle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i);
|
||||||
|
splitHandle.Parent = this;
|
||||||
|
|
||||||
|
splitHandle.Reprotect(handle.Dirty);
|
||||||
|
|
||||||
|
RegionSignal signal = handle.PreAction;
|
||||||
|
if (signal != null)
|
||||||
|
{
|
||||||
|
splitHandle.RegisterAction(signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
_handles[i++] = splitHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
_handles[i++] = splitHandle;
|
handle.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
handle.Dispose();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,15 +97,27 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
// Fill any remaining space with new handles.
|
// Fill any remaining space with new handles.
|
||||||
while (i < _handles.Length)
|
while (i < _handles.Length)
|
||||||
{
|
{
|
||||||
RegionHandle handle = tracking.BeginTracking(address + (ulong)i * granularity, granularity);
|
RegionHandle handle = tracking.BeginTrackingBitmap(address + (ulong)i * granularity, granularity, _dirtyBitmap, i);
|
||||||
handle.Parent = this;
|
handle.Parent = this;
|
||||||
_handles[i++] = handle;
|
_handles[i++] = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_uncheckedHandles = _handles.Length;
|
||||||
|
|
||||||
Address = address;
|
Address = address;
|
||||||
Size = size;
|
Size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SignalWrite()
|
||||||
|
{
|
||||||
|
Dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<RegionHandle> GetHandles()
|
||||||
|
{
|
||||||
|
return _handles;
|
||||||
|
}
|
||||||
|
|
||||||
public void ForceDirty(ulong address, ulong size)
|
public void ForceDirty(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
Dirty = true;
|
Dirty = true;
|
||||||
@@ -96,21 +127,15 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
|
|
||||||
for (int i = startHandle; i <= lastHandle; i++)
|
for (int i = startHandle; i <= lastHandle; i++)
|
||||||
{
|
{
|
||||||
_handles[i].SequenceNumber--;
|
if (_sequenceNumberBitmap.Clear(i))
|
||||||
|
{
|
||||||
|
_uncheckedHandles++;
|
||||||
|
}
|
||||||
|
|
||||||
_handles[i].ForceDirty();
|
_handles[i].ForceDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<RegionHandle> GetHandles()
|
|
||||||
{
|
|
||||||
return _handles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SignalWrite()
|
|
||||||
{
|
|
||||||
Dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void QueryModified(Action<ulong, ulong> modifiedAction)
|
public void QueryModified(Action<ulong, ulong> modifiedAction)
|
||||||
{
|
{
|
||||||
if (!Dirty)
|
if (!Dirty)
|
||||||
@@ -123,33 +148,95 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
QueryModified(Address, Size, modifiedAction);
|
QueryModified(Address, Size, modifiedAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void ParseDirtyBits(long dirtyBits, ref int baseBit, ref int prevHandle, ref ulong rgStart, ref ulong rgSize, Action<ulong, ulong> modifiedAction)
|
||||||
{
|
{
|
||||||
int startHandle = (int)((address - Address) / Granularity);
|
while (dirtyBits != 0)
|
||||||
int lastHandle = (int)((address + (size - 1) - Address) / Granularity);
|
|
||||||
|
|
||||||
ulong rgStart = _handles[startHandle].Address;
|
|
||||||
ulong rgSize = 0;
|
|
||||||
|
|
||||||
for (int i = startHandle; i <= lastHandle; i++)
|
|
||||||
{
|
{
|
||||||
RegionHandle handle = _handles[i];
|
int bit = BitOperations.TrailingZeroCount(dirtyBits);
|
||||||
|
|
||||||
|
dirtyBits &= ~(1L << bit);
|
||||||
|
|
||||||
|
int handleIndex = baseBit + bit;
|
||||||
|
|
||||||
|
RegionHandle handle = _handles[handleIndex];
|
||||||
|
|
||||||
|
if (handleIndex != prevHandle + 1)
|
||||||
|
{
|
||||||
|
// Submit handles scanned until the gap as dirty
|
||||||
|
if (rgSize != 0)
|
||||||
|
{
|
||||||
|
modifiedAction(rgStart, rgSize);
|
||||||
|
rgSize = 0;
|
||||||
|
}
|
||||||
|
rgStart = handle.Address;
|
||||||
|
}
|
||||||
|
|
||||||
if (handle.Dirty)
|
if (handle.Dirty)
|
||||||
{
|
{
|
||||||
rgSize += handle.Size;
|
rgSize += handle.Size;
|
||||||
handle.Reprotect();
|
handle.Reprotect();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
prevHandle = handleIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
baseBit += ConcurrentBitmap.IntSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction)
|
||||||
|
{
|
||||||
|
int startHandle = (int)((address - Address) / Granularity);
|
||||||
|
int lastHandle = (int)((address + (size - 1) - Address) / Granularity);
|
||||||
|
|
||||||
|
ulong rgStart = _handles[startHandle].Address;
|
||||||
|
|
||||||
|
if (startHandle == lastHandle)
|
||||||
|
{
|
||||||
|
RegionHandle handle = _handles[startHandle];
|
||||||
|
|
||||||
|
if (handle.Dirty)
|
||||||
{
|
{
|
||||||
// Submit the region scanned so far as dirty
|
handle.Reprotect();
|
||||||
if (rgSize != 0)
|
modifiedAction(rgStart, handle.Size);
|
||||||
{
|
|
||||||
modifiedAction(rgStart, rgSize);
|
|
||||||
rgSize = 0;
|
|
||||||
}
|
|
||||||
rgStart = handle.EndAddress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong rgSize = 0;
|
||||||
|
|
||||||
|
long[] masks = _dirtyBitmap.Masks;
|
||||||
|
|
||||||
|
int startIndex = startHandle >> ConcurrentBitmap.IntShift;
|
||||||
|
int startBit = startHandle & ConcurrentBitmap.IntMask;
|
||||||
|
long startMask = -1L << startBit;
|
||||||
|
|
||||||
|
int endIndex = lastHandle >> ConcurrentBitmap.IntShift;
|
||||||
|
int endBit = lastHandle & ConcurrentBitmap.IntMask;
|
||||||
|
long endMask = (long)(ulong.MaxValue >> (ConcurrentBitmap.IntMask - endBit));
|
||||||
|
|
||||||
|
long startValue = Volatile.Read(ref masks[startIndex]);
|
||||||
|
|
||||||
|
int baseBit = startIndex << ConcurrentBitmap.IntShift;
|
||||||
|
int prevHandle = startHandle - 1;
|
||||||
|
|
||||||
|
if (startIndex == endIndex)
|
||||||
|
{
|
||||||
|
ParseDirtyBits(startValue & startMask & endMask, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ParseDirtyBits(startValue & startMask, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||||
|
|
||||||
|
for (int i = startIndex + 1; i < endIndex; i++)
|
||||||
|
{
|
||||||
|
ParseDirtyBits(Volatile.Read(ref masks[i]), ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
long endValue = Volatile.Read(ref masks[endIndex]);
|
||||||
|
|
||||||
|
ParseDirtyBits(endValue & endMask, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rgSize != 0)
|
if (rgSize != 0)
|
||||||
@@ -158,35 +245,120 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void ParseDirtyBits(long dirtyBits, long mask, int index, long[] seqMasks, ref int baseBit, ref int prevHandle, ref ulong rgStart, ref ulong rgSize, Action<ulong, ulong> modifiedAction)
|
||||||
{
|
{
|
||||||
int startHandle = (int)((address - Address) / Granularity);
|
long seqMask = mask & ~seqMasks[index];
|
||||||
int lastHandle = (int)((address + (size - 1) - Address) / Granularity);
|
dirtyBits &= seqMask;
|
||||||
|
|
||||||
ulong rgStart = _handles[startHandle].Address;
|
while (dirtyBits != 0)
|
||||||
ulong rgSize = 0;
|
|
||||||
|
|
||||||
for (int i = startHandle; i <= lastHandle; i++)
|
|
||||||
{
|
{
|
||||||
RegionHandle handle = _handles[i];
|
int bit = BitOperations.TrailingZeroCount(dirtyBits);
|
||||||
|
|
||||||
if (sequenceNumber != handle.SequenceNumber && handle.DirtyOrVolatile())
|
dirtyBits &= ~(1L << bit);
|
||||||
|
|
||||||
|
int handleIndex = baseBit + bit;
|
||||||
|
|
||||||
|
RegionHandle handle = _handles[handleIndex];
|
||||||
|
|
||||||
|
if (handleIndex != prevHandle + 1)
|
||||||
{
|
{
|
||||||
rgSize += handle.Size;
|
// Submit handles scanned until the gap as dirty
|
||||||
handle.Reprotect();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Submit the region scanned so far as dirty
|
|
||||||
if (rgSize != 0)
|
if (rgSize != 0)
|
||||||
{
|
{
|
||||||
modifiedAction(rgStart, rgSize);
|
modifiedAction(rgStart, rgSize);
|
||||||
rgSize = 0;
|
rgSize = 0;
|
||||||
}
|
}
|
||||||
rgStart = handle.EndAddress;
|
rgStart = handle.Address;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle.SequenceNumber = sequenceNumber;
|
rgSize += handle.Size;
|
||||||
|
handle.Reprotect();
|
||||||
|
|
||||||
|
prevHandle = handleIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
seqMasks[index] |= mask;
|
||||||
|
_uncheckedHandles -= BitOperations.PopCount((ulong)seqMask);
|
||||||
|
|
||||||
|
baseBit += ConcurrentBitmap.IntSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void QueryModified(ulong address, ulong size, Action<ulong, ulong> modifiedAction, int sequenceNumber)
|
||||||
|
{
|
||||||
|
int startHandle = (int)((address - Address) / Granularity);
|
||||||
|
int lastHandle = (int)((address + (size - 1) - Address) / Granularity);
|
||||||
|
|
||||||
|
ulong rgStart = Address + (ulong)startHandle * Granularity;
|
||||||
|
|
||||||
|
if (sequenceNumber != _sequenceNumber)
|
||||||
|
{
|
||||||
|
if (_uncheckedHandles != _handles.Length)
|
||||||
|
{
|
||||||
|
_sequenceNumberBitmap.Clear();
|
||||||
|
_uncheckedHandles = _handles.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
_sequenceNumber = sequenceNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startHandle == lastHandle)
|
||||||
|
{
|
||||||
|
var handle = _handles[startHandle];
|
||||||
|
if (_sequenceNumberBitmap.Set(startHandle))
|
||||||
|
{
|
||||||
|
_uncheckedHandles--;
|
||||||
|
|
||||||
|
if (handle.DirtyOrVolatile())
|
||||||
|
{
|
||||||
|
handle.Reprotect();
|
||||||
|
|
||||||
|
modifiedAction(rgStart, handle.Size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_uncheckedHandles == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong rgSize = 0;
|
||||||
|
|
||||||
|
long[] seqMasks = _sequenceNumberBitmap.Masks;
|
||||||
|
long[] masks = _dirtyBitmap.Masks;
|
||||||
|
|
||||||
|
int startIndex = startHandle >> ConcurrentBitmap.IntShift;
|
||||||
|
int startBit = startHandle & ConcurrentBitmap.IntMask;
|
||||||
|
long startMask = -1L << startBit;
|
||||||
|
|
||||||
|
int endIndex = lastHandle >> ConcurrentBitmap.IntShift;
|
||||||
|
int endBit = lastHandle & ConcurrentBitmap.IntMask;
|
||||||
|
long endMask = (long)(ulong.MaxValue >> (ConcurrentBitmap.IntMask - endBit));
|
||||||
|
|
||||||
|
long startValue = Volatile.Read(ref masks[startIndex]);
|
||||||
|
|
||||||
|
int baseBit = startIndex << ConcurrentBitmap.IntShift;
|
||||||
|
int prevHandle = startHandle - 1;
|
||||||
|
|
||||||
|
if (startIndex == endIndex)
|
||||||
|
{
|
||||||
|
ParseDirtyBits(startValue, startMask & endMask, startIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ParseDirtyBits(startValue, startMask, startIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||||
|
|
||||||
|
for (int i = startIndex + 1; i < endIndex; i++)
|
||||||
|
{
|
||||||
|
ParseDirtyBits(Volatile.Read(ref masks[i]), -1L, i, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
long endValue = Volatile.Read(ref masks[endIndex]);
|
||||||
|
|
||||||
|
ParseDirtyBits(endValue, endMask, endIndex, seqMasks, ref baseBit, ref prevHandle, ref rgStart, ref rgSize, modifiedAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rgSize != 0)
|
if (rgSize != 0)
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
using Ryujinx.Memory.Range;
|
using System;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -10,7 +9,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
/// A tracking handle for a given region of virtual memory. The Dirty flag is updated whenever any changes are made,
|
/// A tracking handle for a given region of virtual memory. The Dirty flag is updated whenever any changes are made,
|
||||||
/// and an action can be performed when the region is read to or written from.
|
/// and an action can be performed when the region is read to or written from.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RegionHandle : IRegionHandle, IRange
|
public class RegionHandle : IRegionHandle
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If more than this number of checks have been performed on a dirty flag since its last reprotect,
|
/// If more than this number of checks have been performed on a dirty flag since its last reprotect,
|
||||||
@@ -23,7 +22,20 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private static int VolatileThreshold = 5;
|
private static int VolatileThreshold = 5;
|
||||||
|
|
||||||
public bool Dirty { get; private set; }
|
public bool Dirty
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Bitmap.IsSet(DirtyBit);
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
Bitmap.Set(DirtyBit, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal int SequenceNumber { get; set; }
|
||||||
|
|
||||||
public bool Unmapped { get; private set; }
|
public bool Unmapped { get; private set; }
|
||||||
|
|
||||||
public ulong Address { get; }
|
public ulong Address { get; }
|
||||||
@@ -31,7 +43,6 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
public ulong EndAddress { get; }
|
public ulong EndAddress { get; }
|
||||||
|
|
||||||
internal IMultiRegionHandle Parent { get; set; }
|
internal IMultiRegionHandle Parent { get; set; }
|
||||||
internal int SequenceNumber { get; set; }
|
|
||||||
|
|
||||||
private event Action _onDirty;
|
private event Action _onDirty;
|
||||||
|
|
||||||
@@ -68,17 +79,26 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
|
|
||||||
internal RegionSignal PreAction => _preAction;
|
internal RegionSignal PreAction => _preAction;
|
||||||
|
|
||||||
|
internal ConcurrentBitmap Bitmap;
|
||||||
|
internal int DirtyBit;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a new region handle. The handle is registered with the given tracking object,
|
/// Create a new bitmap backed region handle. The handle is registered with the given tracking object,
|
||||||
/// and will be notified of any changes to the specified region.
|
/// and will be notified of any changes to the specified region.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="tracking">Tracking object for the target memory block</param>
|
/// <param name="tracking">Tracking object for the target memory block</param>
|
||||||
/// <param name="address">Virtual address of the region to track</param>
|
/// <param name="address">Virtual address of the region to track</param>
|
||||||
/// <param name="size">Size of the region to track</param>
|
/// <param name="size">Size of the region to track</param>
|
||||||
|
/// <param name="bitmap">The bitmap the dirty flag for this handle is stored in</param>
|
||||||
|
/// <param name="bit">The bit index representing the dirty flag for this handle</param>
|
||||||
/// <param name="mapped">True if the region handle starts mapped</param>
|
/// <param name="mapped">True if the region handle starts mapped</param>
|
||||||
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, bool mapped = true)
|
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, ConcurrentBitmap bitmap, int bit, bool mapped = true)
|
||||||
{
|
{
|
||||||
|
Bitmap = bitmap;
|
||||||
|
DirtyBit = bit;
|
||||||
|
|
||||||
Dirty = mapped;
|
Dirty = mapped;
|
||||||
|
|
||||||
Unmapped = !mapped;
|
Unmapped = !mapped;
|
||||||
Address = address;
|
Address = address;
|
||||||
Size = size;
|
Size = size;
|
||||||
@@ -92,6 +112,54 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new region handle. The handle is registered with the given tracking object,
|
||||||
|
/// and will be notified of any changes to the specified region.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tracking">Tracking object for the target memory block</param>
|
||||||
|
/// <param name="address">Virtual address of the region to track</param>
|
||||||
|
/// <param name="size">Size of the region to track</param>
|
||||||
|
/// <param name="mapped">True if the region handle starts mapped</param>
|
||||||
|
internal RegionHandle(MemoryTracking tracking, ulong address, ulong size, bool mapped = true)
|
||||||
|
{
|
||||||
|
Bitmap = new ConcurrentBitmap(1, mapped);
|
||||||
|
|
||||||
|
Unmapped = !mapped;
|
||||||
|
Address = address;
|
||||||
|
Size = size;
|
||||||
|
EndAddress = address + size;
|
||||||
|
|
||||||
|
_tracking = tracking;
|
||||||
|
_regions = tracking.GetVirtualRegionsForHandle(address, size);
|
||||||
|
foreach (var region in _regions)
|
||||||
|
{
|
||||||
|
region.Handles.Add(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replace the bitmap and bit index used to track dirty state.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The tracking lock should be held when this is called, to ensure neither bitmap is modified.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="bitmap">The bitmap the dirty flag for this handle is stored in</param>
|
||||||
|
/// <param name="bit">The bit index representing the dirty flag for this handle</param>
|
||||||
|
internal void ReplaceBitmap(ConcurrentBitmap bitmap, int bit)
|
||||||
|
{
|
||||||
|
// Assumes the tracking lock is held, so nothing else can signal right now.
|
||||||
|
|
||||||
|
var oldBitmap = Bitmap;
|
||||||
|
var oldBit = DirtyBit;
|
||||||
|
|
||||||
|
bitmap.Set(bit, Dirty);
|
||||||
|
|
||||||
|
Bitmap = bitmap;
|
||||||
|
DirtyBit = bit;
|
||||||
|
|
||||||
|
Dirty |= oldBitmap.IsSet(oldBit);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Clear the volatile state of this handle.
|
/// Clear the volatile state of this handle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -108,7 +176,7 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
public bool DirtyOrVolatile()
|
public bool DirtyOrVolatile()
|
||||||
{
|
{
|
||||||
_checkCount++;
|
_checkCount++;
|
||||||
return Dirty || _volatile;
|
return _volatile || Dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -195,6 +263,8 @@ namespace Ryujinx.Memory.Tracking
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void ForceDirty()
|
public void ForceDirty()
|
||||||
{
|
{
|
||||||
|
_checkCount++;
|
||||||
|
|
||||||
Dirty = true;
|
Dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user