我正在尝试使用 Direct2D 和 Direct3D 互操作性,如 Microsoft Docs 中所述 我正在使用 C# 和 Silk.Net ,所以我制作了这个封装了 D2D1 和 DWrite Logic 的类,渲染目标绑定到后台缓冲区。
internal class Silk2DOverLay : IDisposable
{
private bool _isDisposed;
private bool _isDrawing;
private D2D _d2D;
private ComPtr<ID2D1Factory> _factory;
private ComPtr<ID2D1RenderTarget> _renderTarget;
private ComPtr<ID2D1SolidColorBrush> _mouseOverBrush;
private ComPtr<ID2D1SolidColorBrush> _solidColorBrush;
private ComPtr<ID2D1SolidColorBrush> _backColorBrush;
private ComPtr<IDWriteTextFormat>[] _textFormats;
public unsafe Silk2DOverLay()
{
_d2D = D2D.GetApi();
var guid = ID2D1Factory.Guid;
var options = new FactoryOptions { DebugLevel = DebugLevel.None };
ID2D1Factory* factory;
var ppFactory = (void**)&factory;
SilkMarshal.ThrowHResult(
_d2D.D2D1CreateFactory(FactoryType.SingleThreaded, ref guid, in options, ppFactory)
);
_factory = factory;
factory->Release();
factory = null;
InitFont();
}
public unsafe void Dispose()
{
if (!_isDisposed)
{
if (_isDrawing)
End();
for (var i = 0; i < _textFormats.Length; i++)
Disposer.Dispose(ref _textFormats[i]);
if (_renderTarget.Handle != null)
{
ulong tag1 = 0;
ulong tag2 = 0;
_renderTarget.Flush(&tag1, &tag2);
_renderTarget.Dispose();
}
if (_solidColorBrush.Handle != null)
_solidColorBrush.Dispose();
if (_backColorBrush.Handle != null)
_backColorBrush.Dispose();
if (_mouseOverBrush.Handle != null)
_mouseOverBrush.Dispose();
Disposer.Dispose(ref _factory);
Disposer.Dispose(ref _d2D);
GC.SuppressFinalize(this);
_isDisposed = true;
Debug.WriteLine("2DDevise Disposed");
}
}
public void Release()
{
Disposer.Dispose(ref _renderTarget);
Disposer.Dispose(ref _solidColorBrush);
Disposer.Dispose(ref _backColorBrush);
Disposer.Dispose(ref _mouseOverBrush);
}
private unsafe void InitFont()
{
// Dispose of existing text formats
if (_textFormats != null)
for (var i = 0; i < _textFormats.Length; i++)
Disposer.Dispose(ref _textFormats[i]);
ComPtr<IDWriteFactory6> dWriteFactory;
var dWrite = DWrite.GetApi();
SilkMarshal.ThrowHResult(
dWrite.DWriteCreateFactory(Silk.NET.DirectWrite.FactoryType.Isolated, out dWriteFactory)
);
var fontManager = new SoulsFontManager((IntPtr)dWriteFactory.Handle);
var fontCollection = (IDWriteFontCollection*)fontManager?.GetFontCollection();
// Define font settings for each text format
var fontSettings = new[]
{
new
{
FontName = "ViewPortIcons",
FontSize = 23f,
Weight = FontWeight.Normal,
},
new
{
FontName = "Jura",
FontSize = 20f,
Weight = FontWeight.Normal,
},
new
{
FontName = "Jura",
FontSize = 15f,
Weight = FontWeight.Normal,
},
};
// Initialize text formats array
_textFormats = new ComPtr<IDWriteTextFormat>[fontSettings.Length];
for (var i = 0; i < fontSettings.Length; i++)
{
var setting = fontSettings[i];
_textFormats[i] = CreateTextFormat(
dWriteFactory,
setting.FontName,
fontCollection,
"en-US",
setting.FontSize,
FontWeight.Normal,
FontStyle.Normal
);
_textFormats[i].SetTextAlignment(TextAlignment.Center);
_textFormats[i].SetParagraphAlignment(ParagraphAlignment.Center);
}
dWrite.Dispose();
dWriteFactory.Dispose();
fontManager?.Dispose();
}
// Helper method to create a text format
private unsafe ComPtr<IDWriteTextFormat> CreateTextFormat(
ComPtr<IDWriteFactory6> factory,
string fontName,
IDWriteFontCollection* fontCollection,
string localeName,
float fontSize,
FontWeight fontWeight,
FontStyle fontStyle
)
{
ComPtr<IDWriteTextFormat> textFormat = default;
fixed (char* fontname = fontName)
fixed (char* localname = localeName)
{
SilkMarshal.ThrowHResult(
factory.CreateTextFormat(
fontname,
fontCollection,
fontWeight,
fontStyle,
FontStretch.Normal,
fontSize,
localname,
textFormat.GetAddressOf()
)
);
}
return textFormat;
}
/// <summary>
/// Update all resources
/// </summary>
/// <param name="backBuffer">BackBuffer</param>
internal unsafe void UpdateResources(ID3D11Texture2D* backBuffer)
{
var d2dSurface = backBuffer->QueryInterface<IDXGISurface>();
var props = new RenderTargetProperties(
RenderTargetType.Default,
new PixelFormat(Format.FormatUnknown, AlphaMode.Premultiplied),
96,
96
);
ID2D1RenderTarget* pRenderTarget = null;
SilkMarshal.ThrowHResult(
_factory.CreateDxgiSurfaceRenderTarget(d2dSurface.Handle, props, &pRenderTarget)
);
_renderTarget = pRenderTarget;
if (pRenderTarget != null)
{
pRenderTarget->Release();
pRenderTarget = null;
}
d2dSurface.Dispose();
CreateFontBrush();
}
internal unsafe void CreateFontBrush()
{
Disposer.Dispose(ref _solidColorBrush);
Disposer.Dispose(ref _backColorBrush);
Disposer.Dispose(ref _mouseOverBrush);
ID2D1SolidColorBrush* brush = null;
var fontcolor = SilkColors.White.D3DColor;
var props = new BrushProperties { Opacity = 1f, Transform = Matrix3X2<float>.Identity };
SilkMarshal.ThrowHResult(_renderTarget.CreateSolidColorBrush(&fontcolor, props, &brush));
_solidColorBrush = brush;
fontcolor = SilkColors.Black.D3DColor;
props.Opacity = 0.2f;
SilkMarshal.ThrowHResult(_renderTarget.CreateSolidColorBrush(&fontcolor, props, &brush));
_backColorBrush = brush;
props.Opacity = 0.3f;
fontcolor = SilkColors.Snow.D3DColor;
SilkMarshal.ThrowHResult(_renderTarget.CreateSolidColorBrush(&fontcolor, props, &brush));
_mouseOverBrush = brush;
if (brush != null)
{
brush->Release();
brush = null;
}
}
/// <summary>
/// Draw 2D Block
/// </summary>
public unsafe void Draw2D(D2DBlock block)
{
if (_textFormats.Length < 3)
throw new InvalidOperationException();
IDWriteTextFormat* tFormat = null;
if (block.TextFormat < _textFormats.Length)
tFormat = _textFormats[block.TextFormat];
else
tFormat = _textFormats[2];
var textFormat = (Silk.NET.Direct2D.IDWriteTextFormat*)tFormat;
ID2D1Brush* brush2 = default;
if (block.IsMouseOver)
brush2 = (ID2D1Brush*)_mouseOverBrush.Handle;
else
brush2 = (ID2D1Brush*)_backColorBrush.Handle;
if (block.HasBackground)
_renderTarget.FillEllipse(block.BackGroundEllipse, brush2);
var brush = (ID2D1Brush*)_solidColorBrush.Handle;
fixed (char* pText = block.ContentText)
{
_renderTarget.DrawTextA(
pText,
(uint)block.ContentText.Length,
textFormat,
block.BoundingBox,
brush,
DrawTextOptions.EnableColorFont,
DwriteMeasuringMode.Natural
);
}
}
/// <summary>
/// Begin a 2D drawing session
/// </summary>
public void Begin()
{
_isDrawing = true;
_renderTarget.BeginDraw();
}
/// <summary>
/// End drawing session
/// </summary>
public unsafe void End()
{
ulong tag1 = 0;
ulong tag2 = 0;
SilkMarshal.ThrowHResult(_renderTarget.EndDraw(&tag1, &tag2));
_isDrawing = false;
}
}
问题是关闭应用程序时,我收到许多 D3D11Device Live Objects 警告,但我确保所有 D3D 资源都得到正确处理。 当使用
ReportLiveObjects()
时,活动对象都是 D3D11 Recourses 。
但如果我不初始化 Silk2DOverLay
实例以防止其被使用,警告就会消失
public SilkGraphicsDevice(ISilkView viewport)
{
_viewport = viewport;
InitializeApis();
Initialize();
_silk2DOverLay = new Silk2DOverLay();//if i removed this line
OnResize();
CreateFence();
}
这就是为什么我没有遇到问题,如果问题出在我的处理逻辑上,无论
Silk2DOverLay
如何,都会出现警告,有人可以帮助我吗?
Device和交换链的dispose逻辑 公共无效处置() {
_silk2DOverLay.Release();
Disposer.Dispose(ref _backBufferView);
Disposer.Dispose(ref _factory);
Disposer.Dispose(ref _swapChain);
Disposer.Dispose(ref _depthBufferView);
Disposer.Dispose(ref _deviceContext);
Disposer.Dispose(ref _threadShield);
Disposer.Dispose(ref _d3d11);
Disposer.Dispose(ref _dxgi);
Disposer.Dispose(ref _fence);
Disposer.Dispose(ref _device);
Disposer.Dispose(ref _silk2DOverLay);
GC.SuppressFinalize(this);
} 所有对象都将在此类的窗口关闭事件中被处理
public static class Disposer
{
public static void Dispose<T>(ref T disposable)
where T : class?, IDisposable?
{
if (disposable != null)
{
disposable.Dispose();
disposable = null;
}
}
public static unsafe void Dispose<T>(ref ComPtr<T> disposable)
where T : unmanaged, IComVtbl<T>
{
if (disposable.Handle != null)
{
disposable.Dispose();
disposable = null;
}
}
}
输出警告和 ReportLiveObjects() 输出此处
看起来您遇到了与 D3D11 资源相关的
LiveObjects
警告,即使您确保正确处置也是如此。该问题可能与 Direct3D 和 Direct2D 对象与您的 Silk2DOverLay
实例关联时的管理方式有关。创建和使用 Silk2DOverLay
实例时,它可能会创建未正确清理的引用,这会导致退出应用程序时出现 D3D11 活动对象警告。
这里有一些需要检查和尝试的事情:
验证释放顺序:确保在处置
Silk2DOverLay
、_device
和_swapChain
等主要资源之前释放_d3d11
资源(以及与其关联的D3D资源)。释放这些资源的时间可能会影响清理过程。
使用显式清理:不要依赖自动处置,而是按照创建和处置的顺序手动释放或取消每个资源。这有助于确保不存在对 D3D 对象的延迟引用。
检查关联 ComPtrs 的处置:确保类中的
ComPtr
对象(如 _renderTarget
和 _factory
)在处置时正确释放并清空。有时,丢失或错误处理 ComPtr
可能会导致这些警告。
跟踪资源所有权:如果
Silk2DOverLay
保留 D3D 对象(如 ID2D1RenderTarget
或 ID3D11Texture2D
),请仔细检查是否已清除对这些对象的所有引用。如果任何对象被其他对象隐式保留或在释放后未显式清空,则可能会导致活动对象警告。
在关键点
调用
ReportLiveObjects
:处置资源后,使用ReportLiveObjects
确保没有D3D11对象仍然存活。这将帮助您查明任何可能未正确释放的资源。
调查 Silk.NET 清理:由于您使用的是 Silk.NET,请确保其内部清理机制(例如自动处置
ComPtr
对象)按预期工作。在某些情况下,Silk.NET 可能需要关于何时以及如何清理某些对象的明确指示。
通过关注资源处置的时间和完整性并检查对象的跟踪和释放方式,您应该能够识别活动对象持续存在的位置并纠正问题。