337 lines
12 KiB
C++
337 lines
12 KiB
C++
#include "post_processing.hpp"
|
|
|
|
// WIP
|
|
|
|
#define SAFE_RELEASE(p) \
|
|
{ \
|
|
if (p) { \
|
|
(p)->Release(); \
|
|
(p) = nullptr; \
|
|
} \
|
|
}
|
|
|
|
static inline ID3D11Device* pDevice; // DO NOT RELEASE!
|
|
static inline ID3D11DeviceContext* pDeviceContext;
|
|
static inline ID3D11RenderTargetView* pRenderTarget; // DO NOT RELEASE!
|
|
static inline IDXGISwapChain* pSwapChain;
|
|
|
|
[[nodiscard]] static ID3D11Texture2D* createTexture(int width,
|
|
int height) noexcept {
|
|
D3D11_TEXTURE2D_DESC desc = {};
|
|
desc.Width = width;
|
|
desc.Height = height;
|
|
desc.MipLevels = 1;
|
|
desc.ArraySize = 1;
|
|
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
desc.SampleDesc.Count = 1;
|
|
desc.Usage = D3D11_USAGE_DEFAULT;
|
|
desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
|
|
|
|
ID3D11Texture2D* texture = nullptr;
|
|
pDevice->CreateTexture2D(&desc, nullptr, &texture);
|
|
return texture;
|
|
}
|
|
|
|
static void copyBackbufferToTexture(ID3D11Texture2D* texture) noexcept {
|
|
pSwapChain->GetBuffer(0, __uuidof(texture),
|
|
reinterpret_cast<void**>(&texture));
|
|
}
|
|
|
|
static void setRenderTarget(ID3D11Texture2D* rtTexture) noexcept {
|
|
ID3D11RenderTargetView* rtv = nullptr;
|
|
pDevice->CreateRenderTargetView(rtTexture, nullptr, &rtv);
|
|
pDeviceContext->OMSetRenderTargets(1, &rtv, nullptr);
|
|
rtv->Release();
|
|
}
|
|
|
|
class ShaderProgram {
|
|
public:
|
|
~ShaderProgram() {
|
|
if (pixelShader) pixelShader->Release();
|
|
}
|
|
|
|
void use(float uniform, int location) const noexcept {
|
|
pDeviceContext->PSSetShader(pixelShader, nullptr, 0);
|
|
pDeviceContext->PSSetConstantBuffers(location, 1, &constBuffer);
|
|
}
|
|
|
|
void init(const BYTE* pixelShaderSrc, size_t size) noexcept {
|
|
if (initialized) return;
|
|
initialized = true;
|
|
pDevice->CreatePixelShader(pixelShaderSrc, size, nullptr, &pixelShader);
|
|
}
|
|
|
|
private:
|
|
ID3D11PixelShader* pixelShader = nullptr;
|
|
ID3D11Buffer* constBuffer = nullptr;
|
|
bool initialized = false;
|
|
};
|
|
|
|
class BlurEffect {
|
|
public:
|
|
static void draw(ImDrawList* drawList, float alpha) noexcept {
|
|
instance()._draw(drawList, alpha);
|
|
}
|
|
|
|
static void draws(ImDrawList* drawList, const ImVec2& p_min,
|
|
const ImVec2& p_max, float alpha) noexcept {
|
|
instance()._draws(drawList, p_min, p_max, alpha);
|
|
}
|
|
|
|
static void clearTextures() noexcept {
|
|
SAFE_RELEASE(instance().pBlurTexture1);
|
|
SAFE_RELEASE(instance().pBlurTexture2);
|
|
}
|
|
|
|
private:
|
|
ID3D11RenderTargetView* pRTBackup = nullptr;
|
|
ID3D11Texture2D* pBlurTexture1 = nullptr;
|
|
ID3D11Texture2D* pBlurTexture2 = nullptr;
|
|
ID3D11RasterizerState* pRasterStateWithScissorDisabled = nullptr;
|
|
ID3D11RasterizerState* pOriginalRasterState = nullptr;
|
|
ID3D11RenderTargetView* pBlurTexture1RTV = nullptr;
|
|
ID3D11RenderTargetView* pBlurTexture2RTV = nullptr;
|
|
ID3D11Buffer* pVSConstantBuffer = nullptr;
|
|
|
|
ID3D11ShaderResourceView* pBlurTexture1SRV = nullptr;
|
|
ID3D11ShaderResourceView* pBlurTexture2SRV = nullptr;
|
|
|
|
ShaderProgram blurShaderX;
|
|
ShaderProgram blurShaderY;
|
|
|
|
int backbufferWidth = 0;
|
|
int backbufferHeight = 0;
|
|
static constexpr auto blurDownsample = 2;
|
|
|
|
BlurEffect() = default;
|
|
BlurEffect(const BlurEffect&) = delete;
|
|
|
|
~BlurEffect() {
|
|
SAFE_RELEASE(pRTBackup);
|
|
SAFE_RELEASE(pRasterStateWithScissorDisabled);
|
|
SAFE_RELEASE(pOriginalRasterState);
|
|
SAFE_RELEASE(pBlurTexture1);
|
|
SAFE_RELEASE(pBlurTexture2);
|
|
SAFE_RELEASE(pBlurTexture1RTV);
|
|
SAFE_RELEASE(pBlurTexture2RTV);
|
|
SAFE_RELEASE(pBlurTexture1SRV);
|
|
SAFE_RELEASE(pBlurTexture2SRV);
|
|
SAFE_RELEASE(pVSConstantBuffer);
|
|
}
|
|
|
|
static BlurEffect& instance() noexcept {
|
|
static BlurEffect blurEffect;
|
|
return blurEffect;
|
|
}
|
|
|
|
static void begin(const ImDrawList*, const ImDrawCmd*) noexcept {
|
|
instance()._begin();
|
|
}
|
|
|
|
static void firstPass(const ImDrawList*, const ImDrawCmd*) noexcept {
|
|
instance()._firstPass();
|
|
}
|
|
|
|
static void secondPass(const ImDrawList*, const ImDrawCmd*) noexcept {
|
|
instance()._secondPass();
|
|
}
|
|
|
|
static void end(const ImDrawList*, const ImDrawCmd*) noexcept {
|
|
instance()._end();
|
|
}
|
|
|
|
void createTextures() noexcept {
|
|
if (const auto [width, height] = ImGui::GetIO().DisplaySize;
|
|
backbufferWidth != static_cast<int>(width) ||
|
|
backbufferHeight != static_cast<int>(height)) {
|
|
clearTextures();
|
|
backbufferWidth = static_cast<int>(width);
|
|
backbufferHeight = static_cast<int>(height);
|
|
}
|
|
|
|
if (!pBlurTexture1)
|
|
pBlurTexture1 = createTexture(backbufferWidth / blurDownsample,
|
|
backbufferHeight / blurDownsample);
|
|
if (!pBlurTexture2)
|
|
pBlurTexture2 = createTexture(backbufferWidth / blurDownsample,
|
|
backbufferHeight / blurDownsample);
|
|
|
|
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
|
|
rtvDesc.Format =
|
|
DXGI_FORMAT_R8G8B8A8_UNORM; // Match this with your texture format.
|
|
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
|
|
rtvDesc.Texture2D.MipSlice = 0;
|
|
|
|
pDevice->CreateRenderTargetView(pBlurTexture1, &rtvDesc, &pBlurTexture1RTV);
|
|
pDevice->CreateRenderTargetView(pBlurTexture2, &rtvDesc, &pBlurTexture2RTV);
|
|
}
|
|
|
|
void createShaders() noexcept {
|
|
blurShaderX.init(reinterpret_cast<const BYTE*>(gaussian_blur_x),
|
|
sizeof(gaussian_blur_x));
|
|
blurShaderY.init(reinterpret_cast<const BYTE*>(gaussian_blur_y),
|
|
sizeof(gaussian_blur_y));
|
|
}
|
|
|
|
void _begin() noexcept {
|
|
pDeviceContext->OMGetRenderTargets(1, &pRTBackup, nullptr);
|
|
|
|
copyBackbufferToTexture(pBlurTexture1);
|
|
|
|
D3D11_SAMPLER_DESC sampDesc = {};
|
|
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
|
|
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
|
|
sampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
|
|
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
|
|
sampDesc.MaxLOD = D3D11_FLOAT32_MAX;
|
|
ID3D11SamplerState* samplerState;
|
|
pDevice->CreateSamplerState(&sampDesc, &samplerState);
|
|
pDeviceContext->PSSetSamplers(0, 1, &samplerState);
|
|
|
|
pDeviceContext->RSGetState(&pOriginalRasterState);
|
|
|
|
D3D11_RASTERIZER_DESC rasterDesc = {};
|
|
rasterDesc.FillMode = D3D11_FILL_SOLID; // Solid fill mode
|
|
rasterDesc.CullMode = D3D11_CULL_BACK; // Cull back-facing triangles
|
|
rasterDesc.ScissorEnable = FALSE; // Disable scissor test
|
|
rasterDesc.DepthClipEnable = TRUE; // Enable depth clipping
|
|
HRESULT hr = pDevice->CreateRasterizerState(
|
|
&rasterDesc, &pRasterStateWithScissorDisabled);
|
|
|
|
if (FAILED(hr)) {
|
|
printf("Failed CreateRasterizerState\n");
|
|
}
|
|
|
|
pDeviceContext->RSSetState(pRasterStateWithScissorDisabled);
|
|
|
|
D3D11_BUFFER_DESC cbDesc = {};
|
|
cbDesc.Usage = D3D11_USAGE_DEFAULT;
|
|
cbDesc.ByteWidth = sizeof(VSConstants);
|
|
cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
|
|
|
pDevice->CreateBuffer(&cbDesc, nullptr, &pVSConstantBuffer);
|
|
|
|
float aspectRatio = static_cast<float>(backbufferWidth) /
|
|
static_cast<float>(backbufferHeight);
|
|
DirectX::XMMATRIX projection = DirectX::XMMatrixOrthographicOffCenterLH(
|
|
-1.0f / aspectRatio, 1.0f / aspectRatio, -1.0f, 1.0f, 0.0f, 1.0f);
|
|
|
|
VSConstants vsConsts = {};
|
|
vsConsts.projection = XMMatrixTranspose(
|
|
projection); // HLSL expects matrices to be transposed
|
|
|
|
pDeviceContext->UpdateSubresource(pVSConstantBuffer, 0, nullptr, &vsConsts,
|
|
0, 0);
|
|
pDeviceContext->VSSetConstantBuffers(0, 1, &pVSConstantBuffer);
|
|
}
|
|
|
|
void _firstPass() noexcept {
|
|
blurShaderX.use(2.0f / (backbufferWidth / blurDownsample), 0);
|
|
pDeviceContext->OMSetRenderTargets(1, &pBlurTexture2RTV, nullptr);
|
|
}
|
|
|
|
void _secondPass() noexcept {
|
|
blurShaderY.use(2.0f / (backbufferHeight / blurDownsample), 0);
|
|
pDeviceContext->OMSetRenderTargets(1, &pBlurTexture1RTV, nullptr);
|
|
|
|
pDevice->CreateShaderResourceView(pBlurTexture2, nullptr,
|
|
&pBlurTexture2SRV);
|
|
}
|
|
|
|
void _end() noexcept {
|
|
pDeviceContext->OMSetRenderTargets(1, &pRTBackup, nullptr);
|
|
pRTBackup->Release();
|
|
pDeviceContext->PSSetShader(nullptr, nullptr, 0);
|
|
pDeviceContext->RSSetState(pOriginalRasterState);
|
|
|
|
if (pRasterStateWithScissorDisabled)
|
|
pRasterStateWithScissorDisabled->Release();
|
|
}
|
|
|
|
void _draw(ImDrawList* drawList, float alpha) noexcept {
|
|
createTextures();
|
|
createShaders();
|
|
|
|
if (!pBlurTexture1RTV || !pBlurTexture2RTV) return;
|
|
|
|
drawList->AddCallback(&begin, nullptr);
|
|
for (int i = 0; i < 8; ++i) {
|
|
drawList->AddCallback(&firstPass, nullptr);
|
|
pDevice->CreateShaderResourceView(pBlurTexture1, nullptr,
|
|
&pBlurTexture1SRV);
|
|
drawList->AddImage(
|
|
(ImTextureID)pBlurTexture1SRV, ImVec2(-1.0f, -1.0f),
|
|
ImVec2(1.0f, 1.0f)); // Assuming blurTexture1SRV is the Shader
|
|
// Resource View for blurTexture1
|
|
drawList->AddCallback(&secondPass, nullptr);
|
|
drawList->AddImage((ImTextureID)pBlurTexture2SRV, ImVec2(-1.0f, -1.0f),
|
|
ImVec2(1.0f, 1.0f)); // And similarly for blurTexture2
|
|
}
|
|
drawList->AddCallback(&end, nullptr);
|
|
drawList->AddCallback(ImDrawCallback_ResetRenderState, nullptr);
|
|
|
|
drawList->AddImage(
|
|
reinterpret_cast<ImTextureID>(pBlurTexture1SRV), {0.0f, 0.0f},
|
|
{backbufferWidth * 1.0f, backbufferHeight * 1.0f}, {0.0f, 0.0f},
|
|
{1.0f, 1.0f}, IM_COL32(255, 255, 255, 255 * alpha));
|
|
|
|
if (pOriginalRasterState) pOriginalRasterState->Release();
|
|
}
|
|
|
|
void _draws(ImDrawList* drawList, const ImVec2& p_min, const ImVec2& p_max,
|
|
float alpha) noexcept {
|
|
createTextures();
|
|
createShaders();
|
|
|
|
if (!pBlurTexture1RTV || !pBlurTexture2RTV) return;
|
|
|
|
drawList->AddCallback(&begin, nullptr);
|
|
for (int i = 0; i < 8; ++i) {
|
|
drawList->AddCallback(&firstPass, nullptr);
|
|
pDevice->CreateShaderResourceView(pBlurTexture1, nullptr,
|
|
&pBlurTexture1SRV);
|
|
drawList->AddImage(reinterpret_cast<ImTextureID>(pBlurTexture1SRV), p_min,
|
|
p_max, {1.0f, 1.0f});
|
|
drawList->AddCallback(&secondPass, nullptr);
|
|
pDevice->CreateShaderResourceView(pBlurTexture2, nullptr,
|
|
&pBlurTexture2SRV);
|
|
drawList->AddImage(reinterpret_cast<ImTextureID>(pBlurTexture2SRV), p_min,
|
|
p_max, {1.0f, 1.0f});
|
|
}
|
|
drawList->AddCallback(&end, nullptr);
|
|
drawList->AddCallback(ImDrawCallback_ResetRenderState, nullptr);
|
|
|
|
drawList->AddImage(
|
|
reinterpret_cast<ImTextureID>(pBlurTexture2SRV), p_min, p_max,
|
|
ImVec2((float)backbufferWidth * 1, (float)backbufferHeight * 1),
|
|
ImVec2((float)backbufferWidth * 1, (float)backbufferHeight * 1),
|
|
IM_COL32(255, 255, 255, 255 * alpha));
|
|
}
|
|
};
|
|
|
|
void PostProcessing::setDevice(ID3D11Device* device,
|
|
ID3D11DeviceContext* deviceContext,
|
|
ID3D11RenderTargetView* renderTarget,
|
|
IDXGISwapChain* swapChain) noexcept {
|
|
pDevice = device;
|
|
pDeviceContext = deviceContext;
|
|
pRenderTarget = renderTarget;
|
|
pSwapChain = swapChain;
|
|
}
|
|
|
|
void PostProcessing::clearBlurTextures() noexcept {
|
|
BlurEffect::clearTextures();
|
|
}
|
|
|
|
void PostProcessing::onDeviceReset() noexcept { BlurEffect::clearTextures(); }
|
|
|
|
void PostProcessing::performFullscreenBlur(ImDrawList* drawList,
|
|
float alpha) noexcept {
|
|
BlurEffect::draw(drawList, alpha);
|
|
}
|
|
|
|
void PostProcessing::performBlur(ImDrawList* drawList, const ImVec2& p_min,
|
|
const ImVec2& p_max, float alpha) noexcept {
|
|
BlurEffect::draws(drawList, p_min, p_max, alpha);
|
|
}
|