Skip to content

Commit 8b46fb3

Browse files
committed
d3d11 wip
1 parent 82889a4 commit 8b46fb3

27 files changed

+1331
-1549
lines changed
Lines changed: 364 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,364 @@
1+
using System.Buffers;
2+
using System.Diagnostics.CodeAnalysis;
3+
using System.Numerics;
4+
using System.Reflection;
5+
6+
using Dalamud.Utility;
7+
8+
using ImGuiNET;
9+
10+
using TerraFX.Interop.DirectX;
11+
using TerraFX.Interop.Windows;
12+
13+
namespace Dalamud.ImGuiScene.Helpers;
14+
15+
/// <summary>
16+
/// Helper class for drawing simple rectangles.
17+
/// </summary>
18+
[SuppressMessage(
19+
"StyleCop.CSharp.LayoutRules",
20+
"SA1519:Braces should not be omitted from multi-line child statement",
21+
Justification = "Multiple fixed/using scopes")]
22+
internal sealed unsafe class D3D11RectangleDrawer : IDisposable
23+
{
24+
private ComPtr<ID3D11SamplerState> sampler;
25+
private ComPtr<ID3D11VertexShader> vertexShader;
26+
private ComPtr<ID3D11PixelShader> pixelShader;
27+
private ComPtr<ID3D11InputLayout> inputLayout;
28+
private ComPtr<ID3D11Buffer> vertexConstantBuffer;
29+
private ComPtr<ID3D11BlendState> blendState;
30+
private ComPtr<ID3D11BlendState> blendStateForStrippingAlpha;
31+
private ComPtr<ID3D11RasterizerState> rasterizerState;
32+
private ComPtr<ID3D11Buffer> vertexBufferFill;
33+
private ComPtr<ID3D11Buffer> vertexBufferMutable;
34+
private ComPtr<ID3D11Buffer> indexBuffer;
35+
36+
/// <summary>Finalizes an instance of the <see cref="D3D11RectangleDrawer"/> class.</summary>
37+
~D3D11RectangleDrawer() => this.Dispose();
38+
39+
/// <inheritdoc/>
40+
public void Dispose()
41+
{
42+
this.sampler.Reset();
43+
this.vertexShader.Reset();
44+
this.pixelShader.Reset();
45+
this.inputLayout.Reset();
46+
this.vertexConstantBuffer.Reset();
47+
this.blendState.Reset();
48+
this.blendStateForStrippingAlpha.Reset();
49+
this.rasterizerState.Reset();
50+
this.vertexBufferFill.Reset();
51+
this.vertexBufferMutable.Reset();
52+
this.indexBuffer.Reset();
53+
GC.SuppressFinalize(this);
54+
}
55+
56+
/// <summary>Sets up this instance of <see cref="D3D11RectangleDrawer"/>.</summary>
57+
/// <param name="device">The device.</param>
58+
/// <typeparam name="T">ID3D11Device.</typeparam>
59+
public void Setup<T>(T* device) where T : unmanaged, ID3D11Device.Interface
60+
{
61+
var assembly = Assembly.GetExecutingAssembly();
62+
63+
// Create the vertex shader
64+
if (this.vertexShader.IsEmpty() || this.inputLayout.IsEmpty())
65+
{
66+
using var stream = assembly.GetManifestResourceStream("imgui-vertex.hlsl.bytes")!;
67+
var array = ArrayPool<byte>.Shared.Rent((int)stream.Length);
68+
stream.ReadExactly(array, 0, (int)stream.Length);
69+
fixed (byte* pArray = array)
70+
fixed (ID3D11VertexShader** ppShader = &this.vertexShader.GetPinnableReference())
71+
fixed (ID3D11InputLayout** ppInputLayout = &this.inputLayout.GetPinnableReference())
72+
fixed (void* pszPosition = "POSITION"u8)
73+
fixed (void* pszTexCoord = "TEXCOORD"u8)
74+
fixed (void* pszColor = "COLOR"u8)
75+
{
76+
device->CreateVertexShader(pArray, (nuint)stream.Length, null, ppShader).ThrowOnError();
77+
78+
var ied = stackalloc D3D11_INPUT_ELEMENT_DESC[]
79+
{
80+
new()
81+
{
82+
SemanticName = (sbyte*)pszPosition,
83+
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT,
84+
AlignedByteOffset = uint.MaxValue,
85+
},
86+
new()
87+
{
88+
SemanticName = (sbyte*)pszTexCoord,
89+
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32_FLOAT,
90+
AlignedByteOffset = uint.MaxValue,
91+
},
92+
new()
93+
{
94+
SemanticName = (sbyte*)pszColor,
95+
Format = DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM,
96+
AlignedByteOffset = uint.MaxValue,
97+
},
98+
};
99+
device->CreateInputLayout(ied, 3, pArray, (nuint)stream.Length, ppInputLayout).ThrowOnError();
100+
}
101+
102+
ArrayPool<byte>.Shared.Return(array);
103+
}
104+
105+
// Create the constant buffer
106+
if (this.vertexConstantBuffer.IsEmpty())
107+
{
108+
var bufferDesc = new D3D11_BUFFER_DESC(
109+
(uint)sizeof(Matrix4x4),
110+
(uint)D3D11_BIND_FLAG.D3D11_BIND_CONSTANT_BUFFER,
111+
D3D11_USAGE.D3D11_USAGE_IMMUTABLE);
112+
var data = Matrix4x4.Identity;
113+
var subr = new D3D11_SUBRESOURCE_DATA { pSysMem = &data };
114+
fixed (ID3D11Buffer** ppBuffer = &this.vertexConstantBuffer.GetPinnableReference())
115+
device->CreateBuffer(&bufferDesc, &subr, ppBuffer).ThrowOnError();
116+
}
117+
118+
// Create the pixel shader
119+
if (this.pixelShader.IsEmpty())
120+
{
121+
using var stream = assembly.GetManifestResourceStream("imgui-frag.hlsl.bytes")!;
122+
var array = ArrayPool<byte>.Shared.Rent((int)stream.Length);
123+
stream.ReadExactly(array, 0, (int)stream.Length);
124+
fixed (byte* pArray = array)
125+
fixed (ID3D11PixelShader** ppShader = &this.pixelShader.GetPinnableReference())
126+
device->CreatePixelShader(pArray, (nuint)stream.Length, null, ppShader).ThrowOnError();
127+
128+
ArrayPool<byte>.Shared.Return(array);
129+
}
130+
131+
// Create the blending setup
132+
if (this.blendState.IsEmpty())
133+
{
134+
var blendStateDesc = new D3D11_BLEND_DESC
135+
{
136+
RenderTarget =
137+
{
138+
e0 =
139+
{
140+
BlendEnable = true,
141+
SrcBlend = D3D11_BLEND.D3D11_BLEND_SRC_ALPHA,
142+
DestBlend = D3D11_BLEND.D3D11_BLEND_INV_SRC_ALPHA,
143+
BlendOp = D3D11_BLEND_OP.D3D11_BLEND_OP_ADD,
144+
SrcBlendAlpha = D3D11_BLEND.D3D11_BLEND_INV_SRC_ALPHA,
145+
DestBlendAlpha = D3D11_BLEND.D3D11_BLEND_ZERO,
146+
BlendOpAlpha = D3D11_BLEND_OP.D3D11_BLEND_OP_ADD,
147+
RenderTargetWriteMask = (byte)D3D11_COLOR_WRITE_ENABLE.D3D11_COLOR_WRITE_ENABLE_ALL,
148+
},
149+
},
150+
};
151+
fixed (ID3D11BlendState** ppBlendState = &this.blendState.GetPinnableReference())
152+
device->CreateBlendState(&blendStateDesc, ppBlendState).ThrowOnError();
153+
}
154+
155+
if (this.blendStateForStrippingAlpha.IsEmpty())
156+
{
157+
var blendStateDesc = new D3D11_BLEND_DESC
158+
{
159+
RenderTarget =
160+
{
161+
e0 =
162+
{
163+
BlendEnable = true,
164+
SrcBlend = D3D11_BLEND.D3D11_BLEND_ZERO,
165+
DestBlend = D3D11_BLEND.D3D11_BLEND_ONE,
166+
BlendOp = D3D11_BLEND_OP.D3D11_BLEND_OP_ADD,
167+
SrcBlendAlpha = D3D11_BLEND.D3D11_BLEND_ONE,
168+
DestBlendAlpha = D3D11_BLEND.D3D11_BLEND_ZERO,
169+
BlendOpAlpha = D3D11_BLEND_OP.D3D11_BLEND_OP_ADD,
170+
RenderTargetWriteMask = (byte)D3D11_COLOR_WRITE_ENABLE.D3D11_COLOR_WRITE_ENABLE_ALPHA,
171+
},
172+
},
173+
};
174+
fixed (ID3D11BlendState** ppBlendState = &this.blendStateForStrippingAlpha.GetPinnableReference())
175+
device->CreateBlendState(&blendStateDesc, ppBlendState).ThrowOnError();
176+
}
177+
178+
// Create the rasterizer state
179+
if (this.rasterizerState.IsEmpty())
180+
{
181+
var rasterizerDesc = new D3D11_RASTERIZER_DESC
182+
{
183+
FillMode = D3D11_FILL_MODE.D3D11_FILL_SOLID,
184+
CullMode = D3D11_CULL_MODE.D3D11_CULL_NONE,
185+
};
186+
fixed (ID3D11RasterizerState** ppRasterizerState = &this.rasterizerState.GetPinnableReference())
187+
device->CreateRasterizerState(&rasterizerDesc, ppRasterizerState).ThrowOnError();
188+
}
189+
190+
// Create the font sampler
191+
if (this.sampler.IsEmpty())
192+
{
193+
var samplerDesc = new D3D11_SAMPLER_DESC(
194+
D3D11_FILTER.D3D11_FILTER_MIN_MAG_MIP_LINEAR,
195+
D3D11_TEXTURE_ADDRESS_MODE.D3D11_TEXTURE_ADDRESS_WRAP,
196+
D3D11_TEXTURE_ADDRESS_MODE.D3D11_TEXTURE_ADDRESS_WRAP,
197+
D3D11_TEXTURE_ADDRESS_MODE.D3D11_TEXTURE_ADDRESS_WRAP,
198+
0f,
199+
0,
200+
D3D11_COMPARISON_FUNC.D3D11_COMPARISON_ALWAYS,
201+
null,
202+
0,
203+
0);
204+
fixed (ID3D11SamplerState** ppSampler = &this.sampler.GetPinnableReference())
205+
device->CreateSamplerState(&samplerDesc, ppSampler).ThrowOnError();
206+
}
207+
208+
if (this.vertexBufferFill.IsEmpty())
209+
{
210+
var data = stackalloc ImDrawVert[]
211+
{
212+
new() { col = uint.MaxValue, pos = new(-1, 1), uv = new(0, 0) },
213+
new() { col = uint.MaxValue, pos = new(-1, -1), uv = new(0, 1) },
214+
new() { col = uint.MaxValue, pos = new(1, 1), uv = new(1, 0) },
215+
new() { col = uint.MaxValue, pos = new(1, -1), uv = new(1, 1) },
216+
};
217+
var desc = new D3D11_BUFFER_DESC(
218+
(uint)(sizeof(ImDrawVert) * 4),
219+
(uint)D3D11_BIND_FLAG.D3D11_BIND_VERTEX_BUFFER,
220+
D3D11_USAGE.D3D11_USAGE_IMMUTABLE);
221+
var subr = new D3D11_SUBRESOURCE_DATA { pSysMem = data };
222+
var buffer = default(ID3D11Buffer*);
223+
device->CreateBuffer(&desc, &subr, &buffer).ThrowOnError();
224+
this.vertexBufferFill.Attach(buffer);
225+
}
226+
227+
if (this.vertexBufferMutable.IsEmpty())
228+
{
229+
var desc = new D3D11_BUFFER_DESC(
230+
(uint)(sizeof(ImDrawVert) * 4),
231+
(uint)D3D11_BIND_FLAG.D3D11_BIND_VERTEX_BUFFER,
232+
D3D11_USAGE.D3D11_USAGE_DYNAMIC,
233+
(uint)D3D11_CPU_ACCESS_FLAG.D3D11_CPU_ACCESS_WRITE);
234+
var buffer = default(ID3D11Buffer*);
235+
device->CreateBuffer(&desc, null, &buffer).ThrowOnError();
236+
this.vertexBufferMutable.Attach(buffer);
237+
}
238+
239+
if (this.indexBuffer.IsEmpty())
240+
{
241+
var data = stackalloc ushort[] { 0, 1, 2, 1, 2, 3 };
242+
var desc = new D3D11_BUFFER_DESC(
243+
sizeof(ushort) * 6,
244+
(uint)D3D11_BIND_FLAG.D3D11_BIND_INDEX_BUFFER,
245+
D3D11_USAGE.D3D11_USAGE_IMMUTABLE);
246+
var subr = new D3D11_SUBRESOURCE_DATA { pSysMem = data };
247+
var buffer = default(ID3D11Buffer*);
248+
device->CreateBuffer(&desc, &subr, &buffer).ThrowOnError();
249+
this.indexBuffer.Attach(buffer);
250+
}
251+
}
252+
253+
/// <summary>Draws the given shader resource view to the current render target.</summary>
254+
/// <param name="ctx">An instance of <see cref="ID3D11DeviceContext"/>.</param>
255+
/// <param name="rtv">The render target view.</param>
256+
/// <param name="targetUv0">The left top coordinates relative to the size of the target texture.</param>
257+
/// <param name="targetUv1">The right bottom coordinates relative to the size of the target texture.</param>
258+
/// <param name="srv">The shader resource view.</param>
259+
/// <param name="sourceUv0">The left top coordinates relative to the size of the source texture.</param>
260+
/// <param name="sourceUv1">The right bottom coordinates relative to the size of the source texture.</param>
261+
/// <param name="copyAlphaOnly">Whether to only copy the alpha channel.</param>
262+
/// <remarks>This function does not throw.</remarks>
263+
public void Draw(
264+
ID3D11DeviceContext* ctx,
265+
ID3D11RenderTargetView* rtv,
266+
Vector2 targetUv0,
267+
Vector2 targetUv1,
268+
ID3D11ShaderResourceView* srv,
269+
Vector2 sourceUv0,
270+
Vector2 sourceUv1,
271+
bool copyAlphaOnly)
272+
{
273+
using var rtvRes = default(ComPtr<ID3D11Resource>);
274+
rtv->GetResource(rtvRes.GetAddressOf());
275+
276+
using var rtvTex = default(ComPtr<ID3D11Texture2D>);
277+
if (rtvRes.As(&rtvTex).FAILED)
278+
return;
279+
280+
D3D11_TEXTURE2D_DESC texDesc;
281+
rtvTex.Get()->GetDesc(&texDesc);
282+
283+
ID3D11Buffer* buffer;
284+
if (sourceUv0 == Vector2.Zero && sourceUv1 == Vector2.One)
285+
{
286+
buffer = this.vertexBufferFill.Get();
287+
}
288+
else
289+
{
290+
buffer = this.vertexBufferMutable.Get();
291+
var mapped = default(D3D11_MAPPED_SUBRESOURCE);
292+
if (ctx->Map((ID3D11Resource*)buffer, 0, D3D11_MAP.D3D11_MAP_WRITE_DISCARD, 0u, &mapped).FAILED)
293+
return;
294+
_ = new Span<ImDrawVert>(mapped.pData, 4)
295+
{
296+
[0] = new()
297+
{
298+
col = uint.MaxValue,
299+
pos = new(-1 + (2 * targetUv0.X), 1 - (2 * targetUv0.Y)),
300+
uv = sourceUv0,
301+
},
302+
[1] = new()
303+
{
304+
col = uint.MaxValue,
305+
pos = new(-1 + (2 * targetUv0.X), 1 - (2 * targetUv1.Y)),
306+
uv = new(sourceUv0.X, sourceUv1.Y),
307+
},
308+
[2] = new()
309+
{
310+
col = uint.MaxValue,
311+
pos = new(-1 + (2 * targetUv1.X), 1 - (2 * targetUv0.Y)),
312+
uv = new(sourceUv1.X, sourceUv0.Y),
313+
},
314+
[3] = new()
315+
{
316+
col = uint.MaxValue,
317+
pos = new(-1 + (2 * targetUv1.X), 1 - (2 * targetUv1.Y)),
318+
uv = sourceUv1,
319+
},
320+
};
321+
ctx->Unmap((ID3D11Resource*)buffer, 0u);
322+
}
323+
324+
var stride = (uint)sizeof(ImDrawVert);
325+
var offset = 0u;
326+
327+
ctx->IASetInputLayout(this.inputLayout);
328+
ctx->IASetVertexBuffers(0, 1, &buffer, &stride, &offset);
329+
ctx->IASetIndexBuffer(this.indexBuffer, DXGI_FORMAT.DXGI_FORMAT_R16_UINT, 0);
330+
ctx->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY.D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
331+
332+
var viewport = new D3D11_VIEWPORT(0, 0, texDesc.Width, texDesc.Height);
333+
ctx->RSSetState(this.rasterizerState);
334+
ctx->RSSetViewports(1, &viewport);
335+
336+
var blendColor = default(Vector4);
337+
ctx->OMSetRenderTargets(1, &rtv, null);
338+
if (copyAlphaOnly)
339+
ctx->OMSetBlendState(this.blendStateForStrippingAlpha, (float*)&blendColor, 0xffffffff);
340+
else
341+
ctx->OMSetBlendState(this.blendState, (float*)&blendColor, 0xffffffff);
342+
ctx->OMSetDepthStencilState(null, 0);
343+
344+
ctx->VSSetShader(this.vertexShader.Get(), null, 0);
345+
buffer = this.vertexConstantBuffer.Get();
346+
ctx->VSSetConstantBuffers(0, 1, &buffer);
347+
348+
ctx->PSSetShader(this.pixelShader, null, 0);
349+
var simp = this.sampler.Get();
350+
ctx->PSSetSamplers(0, 1, &simp);
351+
ctx->PSSetShaderResources(0, 1, &srv);
352+
353+
ctx->GSSetShader(null, null, 0);
354+
ctx->HSSetShader(null, null, 0);
355+
ctx->DSSetShader(null, null, 0);
356+
ctx->CSSetShader(null, null, 0);
357+
ctx->DrawIndexed(6, 0, 0);
358+
359+
var ppn = default(ID3D11ShaderResourceView*);
360+
ctx->PSSetShaderResources(0, 1, &ppn);
361+
362+
ctx->OMSetRenderTargets(0, null, null);
363+
}
364+
}

0 commit comments

Comments
 (0)