Compare commits

...

7 Commits

Author SHA1 Message Date
Mary-nyan
7d8e198c33 fix: Support FFmpeg 5.1.x for decoding (#3816)
For some reason FFmpeg 5.1.x reverted part of the changes made in 5.0.x
on AVCodec.

This fix decoding issues with it.
2022-11-02 09:26:50 +01:00
riperiperi
3d98e1361b GPU: Use a bitmap to track buffer modified flags. (#3775)
* Initial implementation

* Some improvements.

* Fix incorrect cast

* Performance improvement and improved correctness

* Add very fast path when all handles are checked.

* Slightly faster

* Add comment

* De-virtualize region handle

All region handles are now bitmap backed.

* Remove non-bitmap tracking

* Remove unused methods

* Add docs, remove unused methods

* Address Feedback

* Rename file
2022-10-29 22:07:37 +00:00
TSRBerry
141cf61ff7 CI: Run git_short_hash inside of bash (#3808) 2022-10-29 19:00:08 +00:00
Wunk
3fe3598d41 Vulkan: Replace VK_EXT_debug_report usage with VK_EXT_debug_utils (#3802)
* Vulkan: Replace `VK_EXT_debug_report` usage with `VK_EXT_debug_utils`

[VK_EXT_debug_report](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_debug_report.html)
has been depreciated for quite some time now in favor of the much more
featureful
[VK_EXT_debug_utils](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_debug_utils.html)
extension.

This PR converts our debug-report-callback into the newer
debug-messenger pattern.

`VK_EXT_debug_utils` adds some additional diagnostic tooling for marking
debug-label scopes for queue-operations, command-buffers, and assigning
name-labels to vulkan objects to aid in debugging(for a later PR).

* Vulkan: Fix `DebugMessenger` severity-flag classification

Extension bits between the two flags, for reference:

https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkDebugUtilsMessageSeverityFlagBitsEXT.html

https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkDebugReportFlagBitsEXT.html
2022-10-29 14:09:25 -03:00
gdkchan
59cdf310bd SPIR-V: Fix tessellation control shader output types (#3807)
* SPIR-V: Fix tessellation control shader output types

* Shader cache version bump
2022-10-29 13:45:30 -03:00
dependabot[bot]
4e34170a84 nuget: bump System.IdentityModel.Tokens.Jwt from 6.15.0 to 6.25.0 (#3806)
Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.15.0 to 6.25.0.
- [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases)
- [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/commits)

---
updated-dependencies:
- dependency-name: System.IdentityModel.Tokens.Jwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-29 10:46:46 +02:00
Luke Warner
d540af5dc0 AppletAE: stub SetRecordVolumeMuted (#3804)
* Update IIrSensorServer.cs

* Update IIrSensorServer.cs

* Apply suggestions from code review

Addressed formatting feedback

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update IIrSensorServer.cs

* Update ISelfController.cs

* Update ISelfController.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-10-27 23:15:57 +00:00
19 changed files with 853 additions and 142 deletions

View File

@@ -57,6 +57,7 @@ jobs:
- name: Get git short hash
id: git_short_hash
run: echo "result=$(git rev-parse --short "${{ github.sha }}")" >> $GITHUB_OUTPUT
shell: bash
- name: Clear
run: dotnet clean && dotnet nuget locals all --clear
- name: Build

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
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 SharedDataFileName = "shared.data";

View File

@@ -7,7 +7,9 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
{
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 readonly AVCodec* _codec;
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.
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.
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
else
{
_decodeFrame = Marshal.GetDelegateForFunctionPointer<FFCodec.AVCodec_decode>(((FFCodecLegacy<AVCodecLegacy>*)_codec)->Decode);
_decodeFrame = Marshal.GetDelegateForFunctionPointer<AVCodec_decode>(((FFCodecLegacy<AVCodec>*)_codec)->Decode);
}
}

View File

@@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
public unsafe IntPtr PrivClass;
public IntPtr Profiles;
public unsafe byte* WrapperName;
public IntPtr ChLayouts;
#pragma warning restore CS0649
}
}

View File

@@ -2,7 +2,7 @@
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
{
struct AVCodecLegacy
struct AVCodec501
{
#pragma warning disable CS0649
public unsafe byte* Name;
@@ -20,7 +20,6 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
public unsafe IntPtr PrivClass;
public IntPtr Profiles;
public unsafe byte* WrapperName;
public IntPtr ChLayouts;
#pragma warning restore CS0649
}
}

View File

@@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
public unsafe IntPtr AvClass;
public int LogLevelOffset;
public int CodecType;
public unsafe AVCodecLegacy* Codec;
public unsafe AVCodec* Codec;
public AVCodecID CodecId;
public uint CodecTag;
public IntPtr PrivData;

View File

@@ -2,12 +2,10 @@
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
public AVCodec Base;
public T Base;
public int CapsInternalOrCbType;
public int PrivDataSize;
public IntPtr UpdateThreadContext;

View File

@@ -262,6 +262,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
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;
if (isUserAttr &&
@@ -273,7 +280,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
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);
}
@@ -308,12 +325,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
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());
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);
}
@@ -327,12 +361,29 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
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;
var ioVariable = isOutAttr ? OutputsArray : InputsArray;
var vecIndex = ShiftRightLogical(TypeS32(), attrIndex, Constant(TypeS32(), 2));
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);
}

View File

@@ -473,6 +473,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
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 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 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));
}
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 spvVar = context.Variable(spvType, storageClass);

View File

@@ -37,7 +37,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
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;
while (passthroughAttributes != 0)

View File

@@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Vulkan
"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>();
@@ -89,7 +89,7 @@ namespace Ryujinx.Graphics.Vulkan
AddAvailableLayer("VK_LAYER_KHRONOS_validation");
}
var enabledExtensions = requiredExtensions.Append(ExtDebugReport.ExtensionName).ToArray();
var enabledExtensions = requiredExtensions.Append(ExtDebugUtils.ExtensionName).ToArray();
var appName = Marshal.StringToHGlobalAnsi(AppName);
@@ -139,22 +139,18 @@ namespace Ryujinx.Graphics.Vulkan
Marshal.FreeHGlobal(ppEnabledLayers[i]);
}
CreateDebugCallbacks(api, logLevel, instance, out debugReport, out debugReportCallback);
CreateDebugMessenger(api, logLevel, instance, out debugUtils, out debugUtilsMessenger);
return instance;
}
private unsafe static uint DebugReport(
uint flags,
DebugReportObjectTypeEXT objectType,
ulong @object,
nuint location,
int messageCode,
byte* layerPrefix,
byte* message,
void* userData)
private unsafe static uint DebugMessenger(
DebugUtilsMessageSeverityFlagsEXT messageSeverity,
DebugUtilsMessageTypeFlagsEXT messageTypes,
DebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData)
{
var msg = Marshal.PtrToStringAnsi((IntPtr)message);
var msg = Marshal.PtrToStringAnsi((IntPtr)pCallbackData->PMessage);
foreach (string excludedMessagePart in _excludedMessages)
{
@@ -164,26 +160,20 @@ namespace Ryujinx.Graphics.Vulkan
}
}
DebugReportFlagsEXT debugFlags = (DebugReportFlagsEXT)flags;
if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportErrorBitExt))
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt))
{
Logger.Error?.Print(LogClass.Gpu, msg);
//throw new Exception(msg);
}
else if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportWarningBitExt))
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt))
{
Logger.Warning?.Print(LogClass.Gpu, msg);
}
else if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportInformationBitExt))
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityInfoBitExt))
{
Logger.Info?.Print(LogClass.Gpu, msg);
}
else if (debugFlags.HasFlag(DebugReportFlagsEXT.DebugReportPerformanceWarningBitExt))
{
Logger.Warning?.Print(LogClass.Gpu, msg);
}
else
else // if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityVerboseBitExt))
{
Logger.Debug?.Print(LogClass.Gpu, msg);
}
@@ -551,46 +541,59 @@ namespace Ryujinx.Graphics.Vulkan
return new CommandBufferPool(api, device, queue, queueLock, queueFamilyIndex);
}
internal unsafe static void CreateDebugCallbacks(
internal unsafe static void CreateDebugMessenger(
Vk api,
GraphicsDebugLevel logLevel,
Instance instance,
out ExtDebugReport debugReport,
out DebugReportCallbackEXT debugReportCallback)
out ExtDebugUtils debugUtils,
out DebugUtilsMessengerEXT debugUtilsMessenger)
{
debugReport = default;
debugUtils = default;
if (logLevel != GraphicsDebugLevel.None)
{
if (!api.TryGetInstanceExtension(instance, out debugReport))
if (!api.TryGetInstanceExtension(instance, out debugUtils))
{
debugReportCallback = default;
debugUtilsMessenger = default;
return;
}
var flags = logLevel switch
var filterLogType = logLevel switch
{
GraphicsDebugLevel.Error => DebugReportFlagsEXT.DebugReportErrorBitExt,
GraphicsDebugLevel.Slowdowns => DebugReportFlagsEXT.DebugReportErrorBitExt | DebugReportFlagsEXT.DebugReportPerformanceWarningBitExt,
GraphicsDebugLevel.All => DebugReportFlagsEXT.DebugReportInformationBitExt |
DebugReportFlagsEXT.DebugReportWarningBitExt |
DebugReportFlagsEXT.DebugReportPerformanceWarningBitExt |
DebugReportFlagsEXT.DebugReportErrorBitExt |
DebugReportFlagsEXT.DebugReportDebugBitExt,
GraphicsDebugLevel.Error => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt,
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt |
DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypePerformanceBitExt,
GraphicsDebugLevel.All => DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeGeneralBitExt |
DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt |
DebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypePerformanceBitExt,
_ => throw new ArgumentException($"Invalid log level \"{logLevel}\".")
};
var debugReportCallbackCreateInfo = new DebugReportCallbackCreateInfoEXT()
var filterLogSeverity = logLevel switch
{
SType = StructureType.DebugReportCallbackCreateInfoExt,
Flags = flags,
PfnCallback = new PfnDebugReportCallbackEXT(DebugReport)
GraphicsDebugLevel.Error => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt,
GraphicsDebugLevel.Slowdowns => DebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt |
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
{
debugReportCallback = default;
debugUtilsMessenger = default;
}
}
}

View File

@@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Vulkan
internal KhrPushDescriptor PushDescriptorApi { get; private set; }
internal ExtTransformFeedback TransformFeedbackApi { 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 Queue Queue { get; private set; }
@@ -57,7 +57,7 @@ namespace Ryujinx.Graphics.Vulkan
private SyncManager _syncManager;
private PipelineFull _pipeline;
private DebugReportCallbackEXT _debugReportCallback;
private DebugUtilsMessengerEXT _debugUtilsMessenger;
internal HelperShader HelperShader { get; private set; }
internal PipelineFull PipelineInternal => _pipeline;
@@ -237,9 +237,9 @@ namespace Ryujinx.Graphics.Vulkan
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))
{
@@ -584,9 +584,9 @@ namespace Ryujinx.Graphics.Vulkan
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)

View File

@@ -32,6 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
private bool _handlesRequestToDisplay = false;
private bool _autoSleepDisabled = false;
private bool _albumImageTakenNotificationEnabled = false;
private bool _recordVolumeMuted = false;
private uint _screenShotImageOrientation = 0;
private uint _idleTimeDetectionExtension = 0;
@@ -389,5 +390,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
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;
}
}
}
}

View File

@@ -26,7 +26,7 @@
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
<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>
<!-- Due to Concentus. -->

View 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;
}
}
}
}

View 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);
}
}
}
}

View File

@@ -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>
/// Signal that a virtual memory event happened at the given location (one byte).
/// </summary>

View File

@@ -1,11 +1,15 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Threading;
namespace Ryujinx.Memory.Tracking
{
/// <summary>
/// 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>
public class MultiRegionHandle : IMultiRegionHandle
{
@@ -17,6 +21,12 @@ namespace Ryujinx.Memory.Tracking
private readonly ulong Granularity;
private readonly ulong Size;
private ConcurrentBitmap _dirtyBitmap;
private int _sequenceNumber;
private BitMap _sequenceNumberBitmap;
private int _uncheckedHandles;
public bool Dirty { get; private set; } = true;
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];
Granularity = granularity;
_dirtyBitmap = new ConcurrentBitmap(_handles.Length, true);
_sequenceNumberBitmap = new BitMap(_handles.Length);
int i = 0;
if (handles != null)
@@ -40,37 +53,43 @@ namespace Ryujinx.Memory.Tracking
// Fill any gap left before this handle.
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;
_handles[i++] = fillHandle;
}
if (handle.Size == granularity)
lock (tracking.TrackingLock)
{
handle.Parent = this;
_handles[i++] = handle;
}
else
{
int endIndex = (int)((handle.EndAddress - address) / granularity);
while (i < endIndex)
if (handle is RegionHandle bitHandle && handle.Size == granularity)
{
RegionHandle splitHandle = tracking.BeginTracking(address + (ulong)i * granularity, granularity);
splitHandle.Parent = this;
handle.Parent = this;
splitHandle.Reprotect(handle.Dirty);
bitHandle.ReplaceBitmap(_dirtyBitmap, i);
RegionSignal signal = handle.PreAction;
if (signal != null)
_handles[i++] = bitHandle;
}
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.
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;
_handles[i++] = handle;
}
_uncheckedHandles = _handles.Length;
Address = address;
Size = size;
}
public void SignalWrite()
{
Dirty = true;
}
public IEnumerable<RegionHandle> GetHandles()
{
return _handles;
}
public void ForceDirty(ulong address, ulong size)
{
Dirty = true;
@@ -96,21 +127,15 @@ namespace Ryujinx.Memory.Tracking
for (int i = startHandle; i <= lastHandle; i++)
{
_handles[i].SequenceNumber--;
if (_sequenceNumberBitmap.Clear(i))
{
_uncheckedHandles++;
}
_handles[i].ForceDirty();
}
}
public IEnumerable<RegionHandle> GetHandles()
{
return _handles;
}
public void SignalWrite()
{
Dirty = true;
}
public void QueryModified(Action<ulong, ulong> modifiedAction)
{
if (!Dirty)
@@ -123,33 +148,95 @@ namespace Ryujinx.Memory.Tracking
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);
int lastHandle = (int)((address + (size - 1) - Address) / Granularity);
ulong rgStart = _handles[startHandle].Address;
ulong rgSize = 0;
for (int i = startHandle; i <= lastHandle; i++)
while (dirtyBits != 0)
{
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)
{
rgSize += handle.Size;
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
if (rgSize != 0)
{
modifiedAction(rgStart, rgSize);
rgSize = 0;
}
rgStart = handle.EndAddress;
handle.Reprotect();
modifiedAction(rgStart, handle.Size);
}
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)
@@ -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);
int lastHandle = (int)((address + (size - 1) - Address) / Granularity);
long seqMask = mask & ~seqMasks[index];
dirtyBits &= seqMask;
ulong rgStart = _handles[startHandle].Address;
ulong rgSize = 0;
for (int i = startHandle; i <= lastHandle; i++)
while (dirtyBits != 0)
{
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;
handle.Reprotect();
}
else
{
// Submit the region scanned so far as dirty
// Submit handles scanned until the gap as dirty
if (rgSize != 0)
{
modifiedAction(rgStart, rgSize);
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)

View File

@@ -1,5 +1,4 @@
using Ryujinx.Memory.Range;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
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,
/// and an action can be performed when the region is read to or written from.
/// </summary>
public class RegionHandle : IRegionHandle, IRange
public class RegionHandle : IRegionHandle
{
/// <summary>
/// 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>
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 ulong Address { get; }
@@ -31,7 +43,6 @@ namespace Ryujinx.Memory.Tracking
public ulong EndAddress { get; }
internal IMultiRegionHandle Parent { get; set; }
internal int SequenceNumber { get; set; }
private event Action _onDirty;
@@ -68,17 +79,26 @@ namespace Ryujinx.Memory.Tracking
internal RegionSignal PreAction => _preAction;
internal ConcurrentBitmap Bitmap;
internal int DirtyBit;
/// <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.
/// </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="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>
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;
Unmapped = !mapped;
Address = address;
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>
/// Clear the volatile state of this handle.
/// </summary>
@@ -108,7 +176,7 @@ namespace Ryujinx.Memory.Tracking
public bool DirtyOrVolatile()
{
_checkCount++;
return Dirty || _volatile;
return _volatile || Dirty;
}
/// <summary>
@@ -195,6 +263,8 @@ namespace Ryujinx.Memory.Tracking
/// </summary>
public void ForceDirty()
{
_checkCount++;
Dirty = true;
}