Skip to content

Commit

Permalink
More safety
Browse files Browse the repository at this point in the history
  • Loading branch information
Soreepeong committed Nov 27, 2023
1 parent b0a1a1e commit f4e0f66
Show file tree
Hide file tree
Showing 18 changed files with 984 additions and 701 deletions.
99 changes: 99 additions & 0 deletions Dalamud/ImGuiScene/Helpers/UnknownComBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

using TerraFX.Interop.Windows;

namespace Dalamud.ImGuiScene.Helpers;

/// <summary>
/// Base for implementing COM. The implementor must have this struct at the beginning of the struct layout.
/// </summary>
/// <remarks>
/// Would use generics as UnknownComBase{T}, but doing so makes the types containing this fail to initialize.
/// </remarks>
[StructLayout(LayoutKind.Sequential)]
[SuppressMessage(
"StyleCop.CSharp.SpacingRules",
"SA1023:Dereference and access of symbols should be spaced correctly",
Justification = "Wtf")]
internal unsafe struct UnknownComBase
{
private static readonly nint[] Vtbl;

private void* pVtbl;
private void* pObject;
private uint* pRefCount;
private delegate*<void*, in Guid, void*> pfnDynamicCast;
private delegate*<void*, void> pfnFinalRelease;

static UnknownComBase()
{
Vtbl = GC.AllocateArray<nint>(3, true);
Vtbl[0] = (nint)(delegate*<UnknownComBase*, Guid*, void**, HRESULT>)&StaticQueryInterface;
Vtbl[1] = (nint)(delegate*<UnknownComBase*, uint>)&StaticAddRef;
Vtbl[2] = (nint)(delegate*<UnknownComBase*, uint>)&StaticRelease;
}

/// <summary>
/// Initializes a new instance of the <see cref="UnknownComBase"/> struct.
/// </summary>
/// <param name="pObject">A pointer to the parent struct.</param>
/// <param name="pRefCount">A pointer to the reference counter.</param>
/// <param name="pfnDynamicCast">Dynamic cast function. Return null to <see cref="E.E_NOINTERFACE"/>. Do not change refcount.</param>
/// <param name="pfnFinalRelease">Final release function.</param>
public UnknownComBase(
void* pObject,
uint* pRefCount,
delegate*<void*, in Guid, void*> pfnDynamicCast,
delegate*<void*, void> pfnFinalRelease = null)
{
this.pVtbl = Unsafe.AsPointer(ref Vtbl[0]);
this.pObject = pObject;
this.pRefCount = pRefCount;
this.pfnDynamicCast = pfnDynamicCast;
this.pfnFinalRelease = pfnFinalRelease;
}

/// <summary>
/// Gets the address of this struct as a pointer to <see cref="IUnknown"/>.
/// </summary>
public IUnknown* AsPunk => (IUnknown*)Unsafe.AsPointer(ref this);

private static HRESULT StaticQueryInterface(UnknownComBase* self, Guid* piid, void** ppvObject)
{
if (ppvObject == null)
return E.E_POINTER;

*ppvObject = null;
if (piid == null)
return E.E_INVALIDARG;

if (*piid == IID.IID_IUnknown)
{
StaticAddRef(self);
*ppvObject = self;
return S.S_OK;
}

if ((*ppvObject = self->pfnDynamicCast(self->pObject, *piid)) is null)
return E.E_NOINTERFACE;

StaticAddRef(self);
return S.S_OK;
}

private static uint StaticAddRef(UnknownComBase* self) => Interlocked.Increment(ref *self->pRefCount);

private static uint StaticRelease(UnknownComBase* self)
{
var r = Interlocked.Decrement(ref *self->pRefCount);
if (r > 0)
return r;
if (self->pfnFinalRelease is not null)
self->pfnFinalRelease(self->pObject);
Marshal.FreeHGlobal((nint)self->pObject);
return 0;
}
}
35 changes: 19 additions & 16 deletions Dalamud/ImGuiScene/IImGuiRenderer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Dalamud.Interface.Internal;
using System.Runtime.CompilerServices;

using Dalamud.Interface.Internal;

using ImGuiNET;

Expand Down Expand Up @@ -41,26 +43,20 @@ internal interface IImGuiRenderer : IDisposable

/// <summary>
/// Sets the texture pipeline. The pipeline must be created from the concrete implementation of this interface.<br />
/// The references of <paramref name="textureHandle"/> and <paramref name="pipelineHandle"/> are copied.
/// You may dispose <paramref name="pipelineHandle"/> after the call.
/// The references of <paramref name="texture"/> and <paramref name="pipeline"/> are copied.
/// You may dispose <paramref name="pipeline"/> after the call.
/// </summary>
/// <param name="textureHandle">The texture handle.</param>
/// <param name="pipelineHandle">The pipeline handle.</param>
void SetTexturePipeline(nint textureHandle, nint pipelineHandle);
/// <param name="texture">The texture handle.</param>
/// <param name="pipeline">The pipeline handle to set, or null to clear.</param>
void SetTexturePipeline(IDalamudTextureWrap texture, ITexturePipelineWrap? pipeline);

/// <summary>
/// Creates a new reference of the pipeline registered for use with the given texture.<br />
/// Dispose after use.
/// </summary>
/// <param name="textureHandle">The texture handle.</param>
/// <returns>The previous pixel shader handle, or 0 if none.</returns>
nint GetTexturePipeline(nint textureHandle);

/// <summary>
/// Disposes a reference to the pipeline.
/// </summary>
/// <param name="pipelineHandle">The pipeline handle.</param>
void ReleaseTexturePipeline(nint pipelineHandle);
/// <param name="texture">The texture handle.</param>
/// <returns>The previous pixel shader handle, or null if none.</returns>
ITexturePipelineWrap? GetTexturePipeline(IDalamudTextureWrap texture);

/// <summary>
/// Adds a user callback handler.
Expand All @@ -83,6 +79,13 @@ internal interface IImGuiRenderer : IDisposable
/// <param name="width">The width in pixels.</param>
/// <param name="height">The height in pixels.</param>
/// <param name="format">Format of the texture.</param>
/// <param name="debugName">Name for debugging.</param>
/// <returns>A texture, ready to use in ImGui.</returns>
IDalamudTextureWrap LoadTexture(ReadOnlySpan<byte> data, int pitch, int width, int height, int format);
IDalamudTextureWrap LoadTexture(
ReadOnlySpan<byte> data,
int pitch,
int width,
int height,
int format,
[CallerMemberName] string debugName = "");
}
25 changes: 4 additions & 21 deletions Dalamud/ImGuiScene/IImGuiScene.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,28 +117,11 @@ internal interface IImGuiScene : IDisposable
/// <returns>A texture, ready to use in ImGui.</returns>
IDalamudTextureWrap LoadImageFormat(ReadOnlySpan<byte> data, int pitch, int width, int height, int format);

/// <summary>
/// Sets the texture pipeline. The pipeline must be created from the concrete implementation of this interface.<br />
/// The references of <paramref name="textureHandle"/> and <paramref name="pipelineHandle"/> are copied.
/// You may dispose <paramref name="pipelineHandle"/> after the call.
/// </summary>
/// <param name="textureHandle">The texture handle.</param>
/// <param name="pipelineHandle">The pipeline handle.</param>
void SetTexturePipeline(nint textureHandle, nint pipelineHandle);
/// <inheritdoc cref="IImGuiRenderer.SetTexturePipeline"/>
void SetTexturePipeline(IDalamudTextureWrap textureHandle, ITexturePipelineWrap? pipelineHandle);

/// <summary>
/// Creates a new reference of the pipeline registered for use with the given texture.<br />
/// Dispose after use.
/// </summary>
/// <param name="textureHandle">The texture handle.</param>
/// <returns>The previous pixel shader handle, or 0 if none.</returns>
nint GetTexturePipeline(nint textureHandle);

/// <summary>
/// Disposes a reference to the pipeline.
/// </summary>
/// <param name="pipelineHandle">The pipeline handle.</param>
void ReleaseTexturePipeline(nint pipelineHandle);
/// <inheritdoc cref="IImGuiRenderer.GetTexturePipeline"/>
ITexturePipelineWrap? GetTexturePipeline(IDalamudTextureWrap textureHandle);

/// <summary>
/// Determines if <paramref name="cursorHandle"/> is owned by this.
Expand Down
18 changes: 18 additions & 0 deletions Dalamud/ImGuiScene/ITexturePipelineWrap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Dalamud.ImGuiScene;

/// <summary>
/// Represents a handle to an immutable texture pipeline.
/// </summary>
public interface ITexturePipelineWrap : ICloneable, IDisposable
{
/// <summary>
/// Gets a value indicating whether this instance of <see cref="ITexturePipelineWrap"/> has been disposed.
/// </summary>
bool IsDisposed { get; }

/// <inheritdoc cref="ICloneable.Clone"/>
new ITexturePipelineWrap Clone();

/// <inheritdoc cref="ICloneable.Clone"/>
object ICloneable.Clone() => this.Clone();
}
Loading

0 comments on commit f4e0f66

Please sign in to comment.