More cleanup

This commit is contained in:
Soreepeong 2024-03-03 00:51:28 +09:00
parent 3415df5d40
commit 3853191c48
7 changed files with 226 additions and 196 deletions

View file

@ -216,13 +216,37 @@ internal class TexWidget : IDataWindowWidget
if (this.renderTargetChoiceInt < 0 || this.renderTargetChoiceInt >= supportedFormats.Length)
return;
var texTask = this.textureManager.CreateFromExistingTextureAsync(
source,
source.CreateWrapSharingLowLevelResource(),
new(0.25f),
new(0.75f),
supportedFormats[this.renderTargetChoiceInt]);
this.addedTextures.Add(new() { Api10 = texTask });
};
}
ImGui.SameLine();
ImGui.AlignTextToFramePadding();
unsafe
{
if (t.GetTexture(this.textureManager) is { } source)
{
var psrv = (ID3D11ShaderResourceView*)source.ImGuiHandle;
var rcsrv = psrv->AddRef() - 1;
psrv->Release();
var pres = default(ID3D11Resource*);
psrv->GetResource(&pres);
var rcres = pres->AddRef() - 1;
pres->Release();
pres->Release();
ImGui.TextUnformatted($"RC: Resource({rcres})/View({rcsrv})");
}
else
{
ImGui.TextUnformatted("RC: -");
}
}
try
{

View file

@ -21,10 +21,6 @@ using ImGuiScene;
using Lumina.Data.Files;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using TerraFX.Interop.DirectX;
namespace Dalamud.Interface.ManagedFontAtlas.Internals;
@ -249,31 +245,12 @@ internal sealed partial class FontAtlasFactory
var fileIndex = textureIndex / 4;
var channelIndex = FdtReader.FontTableEntry.TextureChannelOrder[textureIndex % 4];
wraps[textureIndex] ??= this.GetChannelTexture(texPathFormat, fileIndex, channelIndex);
return CloneTextureWrap(wraps[textureIndex]);
return wraps[textureIndex].CreateWrapSharingLowLevelResource();
}
}
private static T ExtractResult<T>(Task<T> t) => t.IsCompleted ? t.Result : t.GetAwaiter().GetResult();
/// <summary>
/// Clones a texture wrap, by getting a new reference to the underlying <see cref="ShaderResourceView"/> and the
/// texture behind.
/// </summary>
/// <param name="wrap">The <see cref="IDalamudTextureWrap"/> to clone from.</param>
/// <returns>The cloned <see cref="IDalamudTextureWrap"/>.</returns>
private static IDalamudTextureWrap CloneTextureWrap(IDalamudTextureWrap wrap)
{
var srv = CppObject.FromPointer<ShaderResourceView>(wrap.ImGuiHandle);
using var res = srv.Resource;
using var tex2D = res.QueryInterface<Texture2D>();
var description = tex2D.Description;
return new DalamudTextureWrap(
new D3DTextureWrap(
srv.QueryInterface<ShaderResourceView>(),
description.Width,
description.Height));
}
private static unsafe void ExtractChannelFromB8G8R8A8(
Span<byte> target,
ReadOnlySpan<byte> source,
@ -384,7 +361,9 @@ internal sealed partial class FontAtlasFactory
texFile.Header.Width,
texFile.Header.Height,
texFile.Header.Width * bpp,
(int)(targetIsB4G4R4A4 ? Format.B4G4R4A4_UNorm : Format.B8G8R8A8_UNorm)),
(int)(targetIsB4G4R4A4
? DXGI_FORMAT.DXGI_FORMAT_B4G4R4A4_UNORM
: DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM)),
buffer));
}
finally

View file

@ -72,18 +72,21 @@ internal class TextureLoadThrottler : IServiceType, IDisposable
/// <param name="basis">The throttle basis.</param>
/// <param name="immediateLoadFunction">The immediate load function.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <param name="disposables">Disposables to dispose when the task completes.</param>
/// <returns>The task.</returns>
public Task<IDalamudTextureWrap> LoadTextureAsync(
IThrottleBasisProvider basis,
Func<CancellationToken, Task<IDalamudTextureWrap>> immediateLoadFunction,
CancellationToken cancellationToken)
CancellationToken cancellationToken,
params IDisposable?[] disposables)
{
var work = new WorkItem(basis, immediateLoadFunction, cancellationToken);
var work = new WorkItem(basis, immediateLoadFunction, cancellationToken, disposables);
return
this.newItemChannel.Writer.TryWrite(work)
? work.Task
: Task.FromException<IDalamudTextureWrap>(new ObjectDisposedException(nameof(TextureLoadThrottler)));
if (this.newItemChannel.Writer.TryWrite(work))
return work.Task;
work.Dispose();
return Task.FromException<IDalamudTextureWrap>(new ObjectDisposedException(nameof(TextureLoadThrottler)));
}
private async Task LoopAddWorkItemAsync()
@ -118,6 +121,7 @@ internal class TextureLoadThrottler : IServiceType, IDisposable
continue;
await work.Process(this.disposeCancellationTokenSource.Token);
work.Dispose();
}
}
@ -136,6 +140,7 @@ internal class TextureLoadThrottler : IServiceType, IDisposable
{
if (itemRef.CancelAsRequested())
{
itemRef.Dispose();
itemRef = lastRef;
this.workItemPending.RemoveAt(this.workItemPending.Count - 1);
break;
@ -152,7 +157,13 @@ internal class TextureLoadThrottler : IServiceType, IDisposable
var last = this.workItemPending[^1];
this.workItemPending.RemoveAt(this.workItemPending.Count - 1);
return last.CancelAsRequested() ? null : last;
if (last.CancelAsRequested())
{
last.Dispose();
return null;
}
return last;
}
}
@ -171,26 +182,34 @@ internal class TextureLoadThrottler : IServiceType, IDisposable
public long LatestRequestedTick { get; init; } = Environment.TickCount64;
}
private class WorkItem : IComparable<WorkItem>
private sealed class WorkItem : IComparable<WorkItem>, IDisposable
{
private readonly TaskCompletionSource<IDalamudTextureWrap> taskCompletionSource;
private readonly IThrottleBasisProvider basis;
private readonly CancellationToken cancellationToken;
private readonly Func<CancellationToken, Task<IDalamudTextureWrap>> immediateLoadFunction;
private readonly IDisposable?[] disposables;
public WorkItem(
IThrottleBasisProvider basis,
Func<CancellationToken, Task<IDalamudTextureWrap>> immediateLoadFunction,
CancellationToken cancellationToken)
CancellationToken cancellationToken, IDisposable?[] disposables)
{
this.taskCompletionSource = new();
this.basis = basis;
this.cancellationToken = cancellationToken;
this.disposables = disposables;
this.immediateLoadFunction = immediateLoadFunction;
}
public Task<IDalamudTextureWrap> Task => this.taskCompletionSource.Task;
public void Dispose()
{
foreach (ref var d in this.disposables.AsSpan())
Interlocked.Exchange(ref d, null)?.Dispose();
}
public int CompareTo(WorkItem other)
{
if (this.basis.IsOpportunistic != other.basis.IsOpportunistic)

View file

@ -10,9 +10,6 @@ using Dalamud.Utility;
using ImGuiNET;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
@ -28,14 +25,8 @@ internal sealed partial class TextureManager
this.IsDxgiFormatSupportedForCreateFromExistingTextureAsync((DXGI_FORMAT)dxgiFormat);
/// <inheritdoc cref="ITextureProvider.IsDxgiFormatSupportedForCreateFromExistingTextureAsync"/>
public bool IsDxgiFormatSupportedForCreateFromExistingTextureAsync(DXGI_FORMAT dxgiFormat)
public unsafe bool IsDxgiFormatSupportedForCreateFromExistingTextureAsync(DXGI_FORMAT dxgiFormat)
{
if (this.interfaceManager.Scene is not { } scene)
{
_ = Service<InterfaceManager.InterfaceManagerWithScene>.Get();
scene = this.interfaceManager.Scene ?? throw new InvalidOperationException();
}
switch (dxgiFormat)
{
// https://learn.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format
@ -48,12 +39,14 @@ internal sealed partial class TextureManager
return false;
}
var format = (Format)dxgiFormat;
var support = scene.Device.CheckFormatSupport(format);
const FormatSupport required =
FormatSupport.RenderTarget |
FormatSupport.Texture2D;
return (support & required) == required;
D3D11_FORMAT_SUPPORT supported;
if (this.Device.Get()->CheckFormatSupport(dxgiFormat, (uint*)&supported).FAILED)
return false;
const D3D11_FORMAT_SUPPORT required =
D3D11_FORMAT_SUPPORT.D3D11_FORMAT_SUPPORT_TEXTURE2D
| D3D11_FORMAT_SUPPORT.D3D11_FORMAT_SUPPORT_RENDER_TARGET;
return (supported & required) == required;
}
/// <inheritdoc/>
@ -62,61 +55,56 @@ internal sealed partial class TextureManager
Vector2 uv0,
Vector2 uv1,
int dxgiFormat,
bool leaveWrapOpen,
CancellationToken cancellationToken) =>
this.CreateFromExistingTextureAsync(wrap, uv0, uv1, (DXGI_FORMAT)dxgiFormat, cancellationToken);
this.CreateFromExistingTextureAsync(
wrap,
uv0,
uv1,
(DXGI_FORMAT)dxgiFormat,
leaveWrapOpen,
cancellationToken);
/// <inheritdoc cref="ITextureProvider.CreateFromExistingTextureAsync"/>
public Task<IDalamudTextureWrap> CreateFromExistingTextureAsync(
IDalamudTextureWrap wrap,
Vector2 uv0,
Vector2 uv1,
DXGI_FORMAT format,
DXGI_FORMAT format = DXGI_FORMAT.DXGI_FORMAT_UNKNOWN,
bool leaveWrapOpen = false,
CancellationToken cancellationToken = default)
{
var wrapCopy = wrap.CreateWrapSharingLowLevelResource();
return this.textureLoadThrottler.LoadTextureAsync(
new TextureLoadThrottler.ReadOnlyThrottleBasisProvider(),
async _ =>
{
using var tex = await this.NoThrottleCreateFromExistingTextureAsync(
wrapCopy,
uv0,
uv1,
format);
using var device = default(ComPtr<ID3D11Device>);
using var srv = default(ComPtr<ID3D11ShaderResourceView>);
var desc = default(D3D11_TEXTURE2D_DESC);
unsafe
{
tex.Get()->GetDevice(device.GetAddressOf());
new TextureLoadThrottler.ReadOnlyThrottleBasisProvider(),
ImmediateLoadFunction,
cancellationToken,
leaveWrapOpen ? null : wrap);
var srvDesc = new D3D11_SHADER_RESOURCE_VIEW_DESC(
tex,
D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE2D);
device.Get()->CreateShaderResourceView(
(ID3D11Resource*)tex.Get(),
&srvDesc,
srv.GetAddressOf())
.ThrowOnError();
async Task<IDalamudTextureWrap> ImmediateLoadFunction(CancellationToken ct)
{
using var tex = await this.NoThrottleCreateFromExistingTextureAsync(wrap, uv0, uv1, format);
tex.Get()->GetDesc(&desc);
unsafe
{
var srvDesc = new D3D11_SHADER_RESOURCE_VIEW_DESC(
tex,
D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE2D);
using var srv = default(ComPtr<ID3D11ShaderResourceView>);
this.Device.Get()->CreateShaderResourceView(
(ID3D11Resource*)tex.Get(),
&srvDesc,
srv.GetAddressOf())
.ThrowOnError();
return new UnknownTextureWrap(
(IUnknown*)srv.Get(),
(int)desc.Width,
(int)desc.Height,
true);
}
},
cancellationToken)
.ContinueWith(
r =>
{
wrapCopy.Dispose();
return r;
},
default(CancellationToken))
.Unwrap();
var desc = default(D3D11_TEXTURE2D_DESC);
tex.Get()->GetDesc(&desc);
return new UnknownTextureWrap(
(IUnknown*)srv.Get(),
(int)desc.Width,
(int)desc.Height,
true);
}
}
}
/// <inheritdoc/>
@ -125,34 +113,37 @@ internal sealed partial class TextureManager
Vector2 uv0,
Vector2 uv1,
int dxgiFormat,
bool leaveWrapOpen,
CancellationToken cancellationToken) =>
this.GetRawDataFromExistingTextureAsync(wrap, uv0, uv1, (DXGI_FORMAT)dxgiFormat, cancellationToken);
this.GetRawDataFromExistingTextureAsync(
wrap,
uv0,
uv1,
(DXGI_FORMAT)dxgiFormat,
leaveWrapOpen,
cancellationToken);
/// <inheritdoc cref="ITextureProvider.GetRawDataFromExistingTextureAsync"/>
public async Task<(RawImageSpecification Specification, byte[] RawData)> GetRawDataFromExistingTextureAsync(
IDalamudTextureWrap wrap,
Vector2 uv0,
Vector2 uv1,
DXGI_FORMAT dxgiFormat,
CancellationToken cancellationToken)
DXGI_FORMAT dxgiFormat = DXGI_FORMAT.DXGI_FORMAT_UNKNOWN,
bool leaveWrapOpen = false,
CancellationToken cancellationToken = default)
{
using var resUnk = default(ComPtr<IUnknown>);
using var wrapDispose = leaveWrapOpen ? null : wrap;
using var texSrv = default(ComPtr<ID3D11ShaderResourceView>);
using var device = default(ComPtr<ID3D11Device>);
using var context = default(ComPtr<ID3D11DeviceContext>);
using var tex2D = default(ComPtr<ID3D11Texture2D>);
var texDesc = default(D3D11_TEXTURE2D_DESC);
unsafe
{
resUnk.Attach((IUnknown*)wrap.ImGuiHandle);
resUnk.Get()->AddRef();
fixed (Guid* piid = &IID.IID_ID3D11ShaderResourceView)
((IUnknown*)wrap.ImGuiHandle)->QueryInterface(piid, (void**)texSrv.GetAddressOf()).ThrowOnError();
resUnk.As(&texSrv).ThrowOnError();
texSrv.Get()->GetDevice(device.GetAddressOf());
device.Get()->GetImmediateContext(context.GetAddressOf());
this.Device.Get()->GetImmediateContext(context.GetAddressOf());
using (var texRes = default(ComPtr<ID3D11Resource>))
{
@ -177,7 +168,7 @@ internal sealed partial class TextureManager
cancellationToken.ThrowIfCancellationRequested();
return await this.interfaceManager.RunBeforePresent(
() => ExtractMappedResource(device, context, tex2D, cancellationToken));
() => ExtractMappedResource(this.Device, context, tex2D, cancellationToken));
static unsafe (RawImageSpecification Specification, byte[] RawData) ExtractMappedResource(
ComPtr<ID3D11Device> device,
@ -242,28 +233,17 @@ internal sealed partial class TextureManager
Vector2 uv1,
DXGI_FORMAT format)
{
using var resUnk = default(ComPtr<IUnknown>);
using var texSrv = default(ComPtr<ID3D11ShaderResourceView>);
using var device = default(ComPtr<ID3D11Device>);
using var context = default(ComPtr<ID3D11DeviceContext>);
using var tex2D = default(ComPtr<ID3D11Texture2D>);
var texDesc = default(D3D11_TEXTURE2D_DESC);
unsafe
{
resUnk.Attach((IUnknown*)wrap.ImGuiHandle);
resUnk.Get()->AddRef();
fixed (Guid* piid = &IID.IID_ID3D11ShaderResourceView)
((IUnknown*)wrap.ImGuiHandle)->QueryInterface(piid, (void**)texSrv.GetAddressOf()).ThrowOnError();
using (var texSrv2 = default(ComPtr<ID3D11ShaderResourceView>))
{
resUnk.As(&texSrv2).ThrowOnError();
texSrv.Attach(texSrv2);
texSrv2.Detach();
}
texSrv.Get()->GetDevice(device.GetAddressOf());
device.Get()->GetImmediateContext(context.GetAddressOf());
this.Device.Get()->GetImmediateContext(context.GetAddressOf());
using (var texRes = default(ComPtr<ID3D11Resource>))
{
@ -274,6 +254,9 @@ internal sealed partial class TextureManager
tex2D.Get()->GetDesc(&texDesc);
}
if (format == DXGI_FORMAT.DXGI_FORMAT_UNKNOWN)
format = texDesc.Format;
var newWidth = checked((uint)MathF.Round((uv1.X - uv0.X) * texDesc.Width));
var newHeight = checked((uint)MathF.Round((uv1.Y - uv0.Y) * texDesc.Height));
@ -289,11 +272,12 @@ internal sealed partial class TextureManager
Format = format,
SampleDesc = new(1, 0),
Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT,
BindFlags = (uint)(D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_FLAG.D3D11_BIND_RENDER_TARGET),
BindFlags =
(uint)(D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_FLAG.D3D11_BIND_RENDER_TARGET),
CPUAccessFlags = 0u,
MiscFlags = 0u,
};
device.Get()->CreateTexture2D(&tex2DCopyTempDesc, null, tex2DCopyTemp.GetAddressOf()).ThrowOnError();
this.Device.Get()->CreateTexture2D(&tex2DCopyTempDesc, null, tex2DCopyTemp.GetAddressOf()).ThrowOnError();
}
await this.interfaceManager.RunBeforePresent(
@ -305,13 +289,13 @@ internal sealed partial class TextureManager
var rtvCopyTempDesc = new D3D11_RENDER_TARGET_VIEW_DESC(
tex2DCopyTemp,
D3D11_RTV_DIMENSION.D3D11_RTV_DIMENSION_TEXTURE2D);
device.Get()->CreateRenderTargetView(
this.Device.Get()->CreateRenderTargetView(
(ID3D11Resource*)tex2DCopyTemp.Get(),
&rtvCopyTempDesc,
rtvCopyTemp.GetAddressOf()).ThrowOnError();
this.drawsOneSquare ??= new();
this.drawsOneSquare.Setup(device.Get());
this.drawsOneSquare.Setup(this.Device.Get());
context.Get()->OMSetRenderTargets(1u, rtvCopyTemp.GetAddressOf(), null);
this.drawsOneSquare.Draw(
@ -593,6 +577,9 @@ internal sealed partial class TextureManager
ctx->DSSetShader(null, null, 0);
ctx->CSSetShader(null, null, 0);
ctx->DrawIndexed(6, 0, 0);
var ppn = default(ID3D11ShaderResourceView*);
ctx->PSSetShaderResources(0, 1, &ppn);
}
}
}

View file

@ -37,23 +37,21 @@ internal sealed partial class TextureManager
IDalamudTextureWrap wrap,
Guid containerGuid,
Stream stream,
bool leaveOpen = false,
IReadOnlyDictionary<string, object>? props = null,
bool leaveWrapOpen = false,
bool leaveStreamOpen = false,
CancellationToken cancellationToken = default)
{
using var istream = ManagedIStream.Create(stream, leaveOpen);
using var wrapDispose = leaveWrapOpen ? null : wrap;
using var istream = ManagedIStream.Create(stream, leaveStreamOpen);
RawImageSpecification specs;
byte[] bytes;
using (var wrapCopy = wrap.CreateWrapSharingLowLevelResource())
{
(specs, bytes) = await this.GetRawDataFromExistingTextureAsync(
wrapCopy,
var (specs, bytes) = await this.GetRawDataFromExistingTextureAsync(
wrap,
Vector2.Zero,
Vector2.One,
DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM,
true,
cancellationToken).ConfigureAwait(false);
}
this.Wic.SaveToStreamUsingWic(
specs,
@ -70,12 +68,21 @@ internal sealed partial class TextureManager
Guid containerGuid,
string path,
IReadOnlyDictionary<string, object>? props = null,
bool leaveWrapOpen = false,
CancellationToken cancellationToken = default)
{
using var wrapDispose = leaveWrapOpen ? null : wrap;
var pathTemp = $"{path}.{GetCurrentThreadId():X08}{Environment.TickCount64:X16}.tmp";
try
{
await this.SaveToStreamAsync(wrap, containerGuid, File.Create(pathTemp), false, props, cancellationToken);
await this.SaveToStreamAsync(
wrap,
containerGuid,
File.Create(pathTemp),
props,
true,
false,
cancellationToken);
}
catch (Exception e)
{

View file

@ -16,12 +16,8 @@ using Dalamud.Utility;
using Lumina.Data;
using Lumina.Data.Files;
using SharpDX;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using TerraFX.Interop.DirectX;
using TerraFX.Interop.Windows;
namespace Dalamud.Interface.Textures.Internal;
@ -67,6 +63,23 @@ internal sealed partial class TextureManager : IServiceType, IDisposable, ITextu
this.wicManager = new(this);
}
/// <summary>Gets the D3D11 Device used to create textures.</summary>
public unsafe ComPtr<ID3D11Device> Device
{
get
{
if (this.interfaceManager.Scene is not { } scene)
{
_ = Service<InterfaceManager.InterfaceManagerWithScene>.Get();
scene = this.interfaceManager.Scene ?? throw new InvalidOperationException();
}
var device = default(ComPtr<ID3D11Device>);
device.Attach((ID3D11Device*)scene.Device.NativePointer);
return device;
}
}
/// <summary>Gets the shared texture manager.</summary>
public SharedTextureManager Shared =>
this.sharedTextureManager ??
@ -183,65 +196,54 @@ internal sealed partial class TextureManager : IServiceType, IDisposable, ITextu
this.IsDxgiFormatSupported((DXGI_FORMAT)dxgiFormat);
/// <inheritdoc cref="ITextureProvider.IsDxgiFormatSupported"/>
public bool IsDxgiFormatSupported(DXGI_FORMAT dxgiFormat)
public unsafe bool IsDxgiFormatSupported(DXGI_FORMAT dxgiFormat)
{
if (this.interfaceManager.Scene is not { } scene)
{
_ = Service<InterfaceManager.InterfaceManagerWithScene>.Get();
scene = this.interfaceManager.Scene ?? throw new InvalidOperationException();
}
var format = (Format)dxgiFormat;
var support = scene.Device.CheckFormatSupport(format);
const FormatSupport required = FormatSupport.Texture2D;
return (support & required) == required;
D3D11_FORMAT_SUPPORT supported;
if (this.Device.Get()->CheckFormatSupport(dxgiFormat, (uint*)&supported).FAILED)
return false;
const D3D11_FORMAT_SUPPORT required = D3D11_FORMAT_SUPPORT.D3D11_FORMAT_SUPPORT_TEXTURE2D;
return (supported & required) == required;
}
/// <inheritdoc cref="ITextureProvider.CreateFromRaw"/>
internal IDalamudTextureWrap NoThrottleCreateFromRaw(
internal unsafe IDalamudTextureWrap NoThrottleCreateFromRaw(
RawImageSpecification specs,
ReadOnlySpan<byte> bytes)
{
if (this.interfaceManager.Scene is not { } scene)
var device = this.Device;
var texd = new D3D11_TEXTURE2D_DESC
{
_ = Service<InterfaceManager.InterfaceManagerWithScene>.Get();
scene = this.interfaceManager.Scene ?? throw new InvalidOperationException();
}
ShaderResourceView resView;
unsafe
Width = (uint)specs.Width,
Height = (uint)specs.Height,
MipLevels = 1,
ArraySize = 1,
Format = (DXGI_FORMAT)specs.DxgiFormat,
SampleDesc = new(1, 0),
Usage = D3D11_USAGE.D3D11_USAGE_IMMUTABLE,
BindFlags = (uint)D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE,
CPUAccessFlags = 0,
MiscFlags = 0,
};
using var texture = default(ComPtr<ID3D11Texture2D>);
fixed (void* dataPtr = bytes)
{
fixed (void* pData = bytes)
{
var texDesc = new Texture2DDescription
{
Width = specs.Width,
Height = specs.Height,
MipLevels = 1,
ArraySize = 1,
Format = (Format)specs.DxgiFormat,
SampleDescription = new(1, 0),
Usage = ResourceUsage.Immutable,
BindFlags = BindFlags.ShaderResource,
CpuAccessFlags = CpuAccessFlags.None,
OptionFlags = ResourceOptionFlags.None,
};
using var texture = new Texture2D(scene.Device, texDesc, new DataRectangle(new(pData), specs.Pitch));
resView = new(
scene.Device,
texture,
new()
{
Format = texDesc.Format,
Dimension = ShaderResourceViewDimension.Texture2D,
Texture2D = { MipLevels = texDesc.MipLevels },
});
}
var subrdata = new D3D11_SUBRESOURCE_DATA { pSysMem = dataPtr, SysMemPitch = (uint)specs.Pitch };
device.Get()->CreateTexture2D(&texd, &subrdata, texture.GetAddressOf()).ThrowOnError();
}
var viewDesc = new D3D11_SHADER_RESOURCE_VIEW_DESC
{
Format = texd.Format,
ViewDimension = D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE2D,
Texture2D = new() { MipLevels = texd.MipLevels },
};
using var view = default(ComPtr<ID3D11ShaderResourceView>);
device.Get()->CreateShaderResourceView((ID3D11Resource*)texture.Get(), &viewDesc, view.GetAddressOf())
.ThrowOnError();
// no sampler for now because the ImGui implementation we copied doesn't allow for changing it
return new DalamudTextureWrap(new D3DTextureWrap(resView, specs.Width, specs.Height));
return new UnknownTextureWrap((IUnknown*)view.Get(), specs.Width, specs.Height, true);
}
/// <summary>Creates a texture from the given <see cref="TexFile"/>. Skips the load throttler; intended to be used
@ -257,7 +259,7 @@ internal sealed partial class TextureManager : IServiceType, IDisposable, ITextu
if (conversion != TexFile.DxgiFormatConversion.NoConversion ||
!this.IsDxgiFormatSupported((DXGI_FORMAT)dxgiFormat))
{
dxgiFormat = (int)Format.B8G8R8A8_UNorm;
dxgiFormat = (int)DXGI_FORMAT.DXGI_FORMAT_B8G8R8A8_UNORM;
buffer = buffer.Filter(0, 0, TexFile.TextureFormat.B8G8R8A8);
}

View file

@ -36,7 +36,9 @@ public partial interface ITextureProvider
/// without having to wait for the completion of the returned <see cref="Task{TResult}"/>.</param>
/// <param name="uv0">The left top coordinates relative to the size of the source texture.</param>
/// <param name="uv1">The right bottom coordinates relative to the size of the source texture.</param>
/// <param name="dxgiFormat">The desired target format.</param>
/// <param name="dxgiFormat">The desired target format. Use 0 to use the source format.</param>
/// <param name="leaveWrapOpen">Whether to leave <paramref name="wrap"/> non-disposed when the returned
/// <see cref="Task{TResult}"/> completes.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A <see cref="Task{TResult}"/> containing the copied texture on success. Dispose after use.</returns>
/// <remarks>
@ -49,7 +51,8 @@ public partial interface ITextureProvider
IDalamudTextureWrap wrap,
Vector2 uv0,
Vector2 uv1,
int dxgiFormat,
int dxgiFormat = 0,
bool leaveWrapOpen = false,
CancellationToken cancellationToken = default);
/// <summary>Gets a texture from the given bytes, trying to interpret it as a .tex file or other well-known image
@ -185,12 +188,13 @@ public partial interface ITextureProvider
bool TryGetIconPath(in GameIconLookup lookup, [NotNullWhen(true)] out string? path);
/// <summary>Gets the raw data of a texture wrap.</summary>
/// <param name="wrap">The source texture wrap. The passed value may be disposed once this function returns,
/// without having to wait for the completion of the returned <see cref="Task{TResult}"/>.</param>
/// <param name="wrap">The source texture wrap.</param>
/// <param name="uv0">The left top coordinates relative to the size of the source texture.</param>
/// <param name="uv1">The right bottom coordinates relative to the size of the source texture.</param>
/// <param name="dxgiFormat">The desired target format.
/// If 0 (unknown) is passed, then the format will not be converted.</param>
/// <param name="leaveWrapOpen">Whether to leave <paramref name="wrap"/> non-disposed when the returned
/// <see cref="Task{TResult}"/> completes.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The raw data and its specifications.</returns>
/// <remarks>
@ -206,27 +210,32 @@ public partial interface ITextureProvider
Vector2 uv0,
Vector2 uv1,
int dxgiFormat = 0,
bool leaveWrapOpen = false,
CancellationToken cancellationToken = default);
/// <summary>Saves a texture wrap to a stream in an image file format.</summary>
/// <param name="wrap">The texture wrap to save.</param>
/// <param name="containerGuid">The container GUID, obtained from <see cref="GetSupportedImageEncoderInfos"/>.</param>
/// <param name="stream">The stream to save to.</param>
/// <param name="leaveOpen">Whether to leave <paramref name="stream"/> open.</param>
/// <param name="props">Properties to pass to the encoder. See
/// <a href="https://learn.microsoft.com/en-us/windows/win32/wic/-wic-creating-encoder#encoder-options">Microsoft
/// Learn</a> for available parameters.</param>
/// <param name="leaveWrapOpen">Whether to leave <paramref name="wrap"/> non-disposed when the returned
/// <see cref="Task{TResult}"/> completes.</param>
/// <param name="leaveStreamOpen">Whether to leave <paramref name="stream"/> open when the returned
/// <see cref="Task{TResult}"/> completes.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task representing the save process.</returns>
/// <remarks>
/// <para><paramref name="wrap"/> may be disposed as soon as this function returns.</para>
/// <para><paramref name="wrap"/> must not be disposed until the task finishes.</para>
/// </remarks>
Task SaveToStreamAsync(
IDalamudTextureWrap wrap,
Guid containerGuid,
Stream stream,
bool leaveOpen = false,
IReadOnlyDictionary<string, object>? props = null,
bool leaveWrapOpen = false,
bool leaveStreamOpen = false,
CancellationToken cancellationToken = default);
/// <summary>Saves a texture wrap to a file as an image file.</summary>
@ -236,16 +245,19 @@ public partial interface ITextureProvider
/// <param name="props">Properties to pass to the encoder. See
/// <a href="https://learn.microsoft.com/en-us/windows/win32/wic/-wic-creating-encoder#encoder-options">Microsoft
/// Learn</a> for available parameters.</param>
/// <param name="leaveWrapOpen">Whether to leave <paramref name="wrap"/> non-disposed when the returned
/// <see cref="Task{TResult}"/> completes.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task representing the save process.</returns>
/// <remarks>
/// <para><paramref name="wrap"/> may be disposed as soon as this function returns.</para>
/// <para><paramref name="wrap"/> must not be disposed until the task finishes.</para>
/// </remarks>
Task SaveToFileAsync(
IDalamudTextureWrap wrap,
Guid containerGuid,
string path,
IReadOnlyDictionary<string, object>? props = null,
bool leaveWrapOpen = false,
CancellationToken cancellationToken = default);
/// <summary>