DrawListTextureWrap: use two textures (#2285)

Making premultiplied pixel data into straight alpha in-place using UAV
seems to be not working on older graphics cards. Now every instance of
DrawListTextureWrap keeps two GPU textures, where one keeps a
premultiplied data which will be written to using ImGui draw data and
read from to calculate straight alpha pixel data.
This commit is contained in:
srkizer 2025-05-30 00:06:48 +09:00 committed by GitHub
parent d80202a755
commit e415699bb3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 47 additions and 30 deletions

View file

@ -26,7 +26,10 @@ internal sealed unsafe partial class DrawListTextureWrap : IDrawListTextureWrap,
private ComPtr<ID3D11Texture2D> tex; private ComPtr<ID3D11Texture2D> tex;
private ComPtr<ID3D11ShaderResourceView> srv; private ComPtr<ID3D11ShaderResourceView> srv;
private ComPtr<ID3D11RenderTargetView> rtv; private ComPtr<ID3D11RenderTargetView> rtv;
private ComPtr<ID3D11UnorderedAccessView> uav;
private ComPtr<ID3D11Texture2D> texPremultiplied;
private ComPtr<ID3D11ShaderResourceView> srvPremultiplied;
private ComPtr<ID3D11RenderTargetView> rtvPremultiplied;
private int width; private int width;
private int height; private int height;
@ -138,7 +141,9 @@ internal sealed unsafe partial class DrawListTextureWrap : IDrawListTextureWrap,
this.srv.Reset(); this.srv.Reset();
this.tex.Reset(); this.tex.Reset();
this.rtv.Reset(); this.rtv.Reset();
this.uav.Reset(); this.srvPremultiplied.Reset();
this.texPremultiplied.Reset();
this.rtvPremultiplied.Reset();
this.device.Reset(); this.device.Reset();
this.deviceContext.Reset(); this.deviceContext.Reset();
@ -180,7 +185,7 @@ internal sealed unsafe partial class DrawListTextureWrap : IDrawListTextureWrap,
// Clear the texture first, as the texture exists. // Clear the texture first, as the texture exists.
var clearColor = this.ClearColor; var clearColor = this.ClearColor;
this.deviceContext.Get()->ClearRenderTargetView(this.rtv.Get(), (float*)&clearColor); this.deviceContext.Get()->ClearRenderTargetView(this.rtvPremultiplied.Get(), (float*)&clearColor);
// If there is nothing to draw, then stop. // If there is nothing to draw, then stop.
if (!drawData.Valid if (!drawData.Valid
@ -196,8 +201,8 @@ internal sealed unsafe partial class DrawListTextureWrap : IDrawListTextureWrap,
using (new DeviceContextStateBackup(this.device.Get()->GetFeatureLevel(), this.deviceContext)) using (new DeviceContextStateBackup(this.device.Get()->GetFeatureLevel(), this.deviceContext))
{ {
Service<Renderer>.Get().RenderDrawData(this.rtv.Get(), drawData); Service<Renderer>.Get().RenderDrawData(this.rtvPremultiplied.Get(), drawData);
Service<Renderer>.Get().MakeStraight(this.uav.Get()); Service<Renderer>.Get().MakeStraight(this.srvPremultiplied.Get(), this.rtv.Get());
} }
} }
@ -217,7 +222,9 @@ internal sealed unsafe partial class DrawListTextureWrap : IDrawListTextureWrap,
this.tex.Reset(); this.tex.Reset();
this.srv.Reset(); this.srv.Reset();
this.rtv.Reset(); this.rtv.Reset();
this.uav.Reset(); this.texPremultiplied.Reset();
this.srvPremultiplied.Reset();
this.rtvPremultiplied.Reset();
this.width = newWidth; this.width = newWidth;
this.Height = newHeight; this.Height = newHeight;
this.srv = new((ID3D11ShaderResourceView*)this.emptyTexture.ImGuiHandle); this.srv = new((ID3D11ShaderResourceView*)this.emptyTexture.ImGuiHandle);
@ -231,7 +238,9 @@ internal sealed unsafe partial class DrawListTextureWrap : IDrawListTextureWrap,
using var tmptex = default(ComPtr<ID3D11Texture2D>); using var tmptex = default(ComPtr<ID3D11Texture2D>);
using var tmpsrv = default(ComPtr<ID3D11ShaderResourceView>); using var tmpsrv = default(ComPtr<ID3D11ShaderResourceView>);
using var tmprtv = default(ComPtr<ID3D11RenderTargetView>); using var tmprtv = default(ComPtr<ID3D11RenderTargetView>);
using var tmpuav = default(ComPtr<ID3D11UnorderedAccessView>); using var tmptexPremultiplied = default(ComPtr<ID3D11Texture2D>);
using var tmpsrvPremultiplied = default(ComPtr<ID3D11ShaderResourceView>);
using var tmprtvPremultiplied = default(ComPtr<ID3D11RenderTargetView>);
var tmpTexDesc = new D3D11_TEXTURE2D_DESC var tmpTexDesc = new D3D11_TEXTURE2D_DESC
{ {
@ -243,8 +252,7 @@ internal sealed unsafe partial class DrawListTextureWrap : IDrawListTextureWrap,
SampleDesc = new(1, 0), SampleDesc = new(1, 0),
Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT, Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT,
BindFlags = (uint)(D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE | BindFlags = (uint)(D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE |
D3D11_BIND_FLAG.D3D11_BIND_RENDER_TARGET | D3D11_BIND_FLAG.D3D11_BIND_RENDER_TARGET),
D3D11_BIND_FLAG.D3D11_BIND_UNORDERED_ACCESS),
CPUAccessFlags = 0u, CPUAccessFlags = 0u,
MiscFlags = 0u, MiscFlags = 0u,
}; };
@ -263,15 +271,27 @@ internal sealed unsafe partial class DrawListTextureWrap : IDrawListTextureWrap,
if (hr.FAILED) if (hr.FAILED)
return hr; return hr;
var uavDesc = new D3D11_UNORDERED_ACCESS_VIEW_DESC(tmptex, D3D11_UAV_DIMENSION.D3D11_UAV_DIMENSION_TEXTURE2D); hr = this.device.Get()->CreateTexture2D(&tmpTexDesc, null, tmptexPremultiplied.GetAddressOf());
hr = this.device.Get()->CreateUnorderedAccessView(tmpres, &uavDesc, tmpuav.GetAddressOf()); if (hr.FAILED)
return hr;
tmpres = (ID3D11Resource*)tmptexPremultiplied.Get();
srvDesc = new(tmptexPremultiplied, D3D_SRV_DIMENSION.D3D11_SRV_DIMENSION_TEXTURE2D);
hr = this.device.Get()->CreateShaderResourceView(tmpres, &srvDesc, tmpsrvPremultiplied.GetAddressOf());
if (hr.FAILED)
return hr;
rtvDesc = new(tmptexPremultiplied, D3D11_RTV_DIMENSION.D3D11_RTV_DIMENSION_TEXTURE2D);
hr = this.device.Get()->CreateRenderTargetView(tmpres, &rtvDesc, tmprtvPremultiplied.GetAddressOf());
if (hr.FAILED) if (hr.FAILED)
return hr; return hr;
tmptex.Swap(ref this.tex); tmptex.Swap(ref this.tex);
tmpsrv.Swap(ref this.srv); tmpsrv.Swap(ref this.srv);
tmprtv.Swap(ref this.rtv); tmprtv.Swap(ref this.rtv);
tmpuav.Swap(ref this.uav); tmptexPremultiplied.Swap(ref this.texPremultiplied);
tmpsrvPremultiplied.Swap(ref this.srvPremultiplied);
tmprtvPremultiplied.Swap(ref this.rtvPremultiplied);
this.width = newWidth; this.width = newWidth;
this.height = newHeight; this.height = newHeight;
this.format = newFormat; this.format = newFormat;

View file

@ -1,4 +1,4 @@
#include "DrawListTexture.Renderer.Common.hlsl" #include "Renderer.Common.hlsl"
struct ImDrawVert { struct ImDrawVert {
float2 position : POSITION; float2 position : POSITION;
@ -18,7 +18,6 @@ struct PsData {
Texture2D s_texture : register(t0); Texture2D s_texture : register(t0);
SamplerState s_sampler : register(s0); SamplerState s_sampler : register(s0);
RWTexture2D<float4> s_output : register(u1);
VsData vs_main(const ImDrawVert idv) { VsData vs_main(const ImDrawVert idv) {
VsData result; VsData result;
@ -34,7 +33,7 @@ float4 ps_main(const VsData vd) : SV_TARGET {
/* /*
fxc /Zi /T vs_5_0 /E vs_main /Fo DrawListTexture.Renderer.DrawToPremul.vs.bin DrawListTexture.Renderer.DrawToPremul.hlsl fxc /Zi /T vs_5_0 /E vs_main /Fo Renderer.DrawToPremul.vs.bin Renderer.DrawToPremul.hlsl
fxc /Zi /T ps_5_0 /E ps_main /Fo DrawListTexture.Renderer.DrawToPremul.ps.bin DrawListTexture.Renderer.DrawToPremul.hlsl fxc /Zi /T ps_5_0 /E ps_main /Fo Renderer.DrawToPremul.ps.bin Renderer.DrawToPremul.hlsl
*/ */

View file

@ -1,22 +1,19 @@
RWTexture2D<unorm float4> s_output : register(u1); Texture2D s_texture : register(t0);
float4 vs_main(const float2 position : POSITION) : SV_POSITION { float4 vs_main(const float2 position : POSITION) : SV_POSITION {
return float4(position, 0, 1); return float4(position, 0, 1);
} }
float4 ps_main(const float4 position : SV_POSITION) : SV_TARGET { float4 ps_main(const float4 position : SV_POSITION) : SV_TARGET {
const float4 src = s_output[position.xy]; const float4 src = s_texture[position.xy];
s_output[position.xy] = return src.a > 0
src.a > 0
? float4(src.rgb / src.a, src.a) ? float4(src.rgb / src.a, src.a)
: float4(0, 0, 0, 0); : float4(0, 0, 0, 0);
return float4(0, 0, 0, 0); // unused
} }
/* /*
fxc /Zi /T vs_5_0 /E vs_main /Fo DrawListTexture.Renderer.MakeStraight.vs.bin DrawListTexture.Renderer.MakeStraight.hlsl fxc /Zi /T vs_5_0 /E vs_main /Fo Renderer.MakeStraight.vs.bin Renderer.MakeStraight.hlsl
fxc /Zi /T ps_5_0 /E ps_main /Fo DrawListTexture.Renderer.MakeStraight.ps.bin DrawListTexture.Renderer.MakeStraight.hlsl fxc /Zi /T ps_5_0 /E ps_main /Fo Renderer.MakeStraight.ps.bin Renderer.MakeStraight.hlsl
*/ */

View file

@ -259,15 +259,16 @@ internal sealed unsafe partial class DrawListTextureWrap
} }
/// <summary>Renders draw data.</summary> /// <summary>Renders draw data.</summary>
/// <param name="puav">The pointer to a Texture2D UAV to make straight.</param> /// <param name="psrv">The pointer to a Texture2D SRV to read premultiplied data from.</param>
public void MakeStraight(ID3D11UnorderedAccessView* puav) /// <param name="prtv">The pointer to a Texture2D RTV to write straightened data.</param>
public void MakeStraight(ID3D11ShaderResourceView* psrv, ID3D11RenderTargetView* prtv)
{ {
ThreadSafety.AssertMainThread(); ThreadSafety.AssertMainThread();
D3D11_TEXTURE2D_DESC texDesc; D3D11_TEXTURE2D_DESC texDesc;
using (var texRes = default(ComPtr<ID3D11Resource>)) using (var texRes = default(ComPtr<ID3D11Resource>))
{ {
puav->GetResource(texRes.GetAddressOf()); prtv->GetResource(texRes.GetAddressOf());
using var tex = default(ComPtr<ID3D11Texture2D>); using var tex = default(ComPtr<ID3D11Texture2D>);
texRes.As(&tex).ThrowOnError(); texRes.As(&tex).ThrowOnError();
@ -292,10 +293,9 @@ internal sealed unsafe partial class DrawListTextureWrap
var viewport = new D3D11_VIEWPORT(0, 0, texDesc.Width, texDesc.Height); var viewport = new D3D11_VIEWPORT(0, 0, texDesc.Width, texDesc.Height);
this.deviceContext.Get()->RSSetViewports(1, &viewport); this.deviceContext.Get()->RSSetViewports(1, &viewport);
this.deviceContext.Get()->OMSetBlendState(null, null, 0xFFFFFFFF); this.deviceContext.Get()->OMSetBlendState(null, null, 0xffffffff);
this.deviceContext.Get()->OMSetDepthStencilState(this.depthStencilState, 0); this.deviceContext.Get()->OMSetDepthStencilState(this.depthStencilState, 0);
var nullrtv = default(ID3D11RenderTargetView*); this.deviceContext.Get()->OMSetRenderTargets(1, &prtv, null);
this.deviceContext.Get()->OMSetRenderTargetsAndUnorderedAccessViews(1, &nullrtv, null, 1, 1, &puav, null);
this.deviceContext.Get()->VSSetShader(this.makeStraightVertexShader.Get(), null, 0); this.deviceContext.Get()->VSSetShader(this.makeStraightVertexShader.Get(), null, 0);
this.deviceContext.Get()->PSSetShader(this.makeStraightPixelShader.Get(), null, 0); this.deviceContext.Get()->PSSetShader(this.makeStraightPixelShader.Get(), null, 0);
@ -304,6 +304,7 @@ internal sealed unsafe partial class DrawListTextureWrap
this.deviceContext.Get()->DSSetShader(null, null, 0); this.deviceContext.Get()->DSSetShader(null, null, 0);
this.deviceContext.Get()->CSSetShader(null, null, 0); this.deviceContext.Get()->CSSetShader(null, null, 0);
this.deviceContext.Get()->PSSetShaderResources(0, 1, &psrv);
this.deviceContext.Get()->DrawIndexed(6, 0, 0); this.deviceContext.Get()->DrawIndexed(6, 0, 0);
} }