Files
sessionzero/vendor/rmlui_backend/RmlUi_Renderer_DX12.cpp
2026-07-17 20:18:38 -05:00

9996 lines
363 KiB
C++

#include "RmlUi_Renderer_DX12.h"
#include "RmlUi_Backend.h"
#include <RmlUi/Core/Core.h>
#include <RmlUi/Core/DecorationTypes.h>
#include <RmlUi/Core/FileInterface.h>
#include <RmlUi/Core/Log.h>
#include <RmlUi/Core/Mesh.h>
#include <RmlUi/Core/MeshUtilities.h>
#include <RmlUi/Core/Platform.h>
#include <RmlUi/Core/Profiling.h>
#include <algorithm>
#include <string.h>
#ifndef RMLUI_PLATFORM_WIN32
#error unable to compile platform specific renderer required Windows OS that support DirectX-12
#endif
#ifdef RMLUI_DEBUG
#define RMLUI_ATTR_ASSERT_VARIABLE
#else
#define RMLUI_ATTR_ASSERT_VARIABLE [[maybe_unused]]
#endif
#ifdef RMLUI_DX_DEBUG
#include <d3d12sdklayers.h>
#include <dxgidebug.h>
#endif
/// @brief in bytes see pShaderSourceText_Vertex - > cbuffer ConstantBuffer : register(b0)
static constexpr uint32_t kAllocationSize_ConstantBuffer_Vertex_Main = 72;
/// @brief in bytes see pShaderSourceText_Vertex_Blur - > cbuffer SharedConstantBuffer : register(b0)
static constexpr uint32_t kAllocationSize_ConstantBuffer_Vertex_Blur = 96;
static constexpr uint32_t kAllocationSize_ConstantBuffer_Pixel_Gradient = 416;
/// @brief generally saying it is not universal approach but for keeping things not so much complex better to specify max amount of constantbuffer
/// that's enough to satisfy all shaders otherwise better to use only those which size is required for allocation
static constexpr uint32_t kAllocationSizeMax_ConstantBuffer =
std::max({kAllocationSize_ConstantBuffer_Vertex_Main, kAllocationSize_ConstantBuffer_Vertex_Blur, kAllocationSize_ConstantBuffer_Pixel_Gradient});
#define MAX_NUM_STOPS 16
#define BLUR_SIZE 7
#define BLUR_NUM_WEIGHTS ((BLUR_SIZE + 1) / 2)
#define RMLUI_STRINGIFY_IMPL(x) #x
#define RMLUI_STRINGIFY(x) RMLUI_STRINGIFY_IMPL(x)
static constexpr const char pShaderSourceText_Color[] = R"(
struct VS_INPUT
{
float4 pos : SV_Position;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
float4 main(const VS_INPUT IN) : SV_TARGET
{
return IN.color;
}
)";
// main
static constexpr const char pShaderSourceText_Vertex[] = R"(
struct VS_INPUT
{
float2 position : POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
struct PS_OUTPUT
{
float4 position : SV_Position;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
cbuffer ConstantBuffer : register(b0)
{
float4x4 m_transform;
float2 m_translate;
};
PS_OUTPUT main(const VS_INPUT IN)
{
PS_OUTPUT OUT;
float2 translatedPos = IN.position + m_translate;
float4 resPos = mul(m_transform, float4(translatedPos.x, translatedPos.y, 0.0, 1.0));
OUT.position = resPos;
OUT.color = IN.color;
OUT.uv = IN.uv;
return OUT;
};
)";
static constexpr const char pShaderSourceText_Texture[] = R"(
struct VS_INPUT
{
float4 pos : SV_Position;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
Texture2D g_InputTexture : register(t0);
SamplerState g_SamplerLinear : register(s0);
float4 main(const VS_INPUT IN) : SV_TARGET
{
return IN.color * g_InputTexture.Sample(g_SamplerLinear, IN.uv);
}
)";
static constexpr const char pShaderSourceText_Vertex_PassThrough[] = R"(
struct VS_INPUT
{
float2 position : POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
struct PS_OUTPUT
{
float4 position : SV_Position;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
PS_OUTPUT main(const VS_INPUT IN)
{
PS_OUTPUT OUT;
OUT.position = float4(IN.position.x, IN.position.y, 0.0f, 1.0f);
OUT.color = IN.color;
// need to flip here since Rml::MeshUtilities::GenerateQuad makes valid data for GL APIs but on DirectX
// it is not valid UV (otherwise image will be flipped)
OUT.uv = float2(IN.uv.x, 1.0f - IN.uv.y);
return OUT;
}
)";
static constexpr const char pShaderSourceText_Pixel_Passthrough[] = R"(
struct PS_INPUT
{
float4 pos : SV_Position;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
Texture2D g_InputTexture : register(t0);
SamplerState g_SamplerLinear : register(s0);
float4 main(const PS_INPUT inputArgs) : SV_TARGET
{
return g_InputTexture.Sample(g_SamplerLinear, inputArgs.uv);
}
)";
#define RMLUI_SHADER_HEADER "#define MAX_NUM_STOPS " RMLUI_STRINGIFY(MAX_NUM_STOPS) "\n#line " RMLUI_STRINGIFY(__LINE__) "\n"
#define RMLUI_SHADER_BLUR_HEADER \
RMLUI_SHADER_HEADER "\n#define BLUR_SIZE " RMLUI_STRINGIFY(BLUR_SIZE) "\n#define BLUR_NUM_WEIGHTS " RMLUI_STRINGIFY(BLUR_NUM_WEIGHTS)
static constexpr const char pShaderSourceText_Vertex_Blur[] = RMLUI_SHADER_BLUR_HEADER R"(
struct VS_INPUT
{
float2 position : POSITION;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
struct PS_INPUT
{
float4 position : SV_Position;
float2 uv[BLUR_SIZE] : TEXCOORD;
};
cbuffer SharedConstantBuffer : register(b0)
{
float4x4 m_transform;
float2 m_translate;
float2 m_texelOffset;
float4 m_weights;
float2 m_texCoordMin;
float2 m_texCoordMax;
};
PS_INPUT main(const VS_INPUT IN)
{
PS_INPUT result = (PS_INPUT)0;
for (int i = 0; i < BLUR_SIZE; i++) {
result.uv[i] = IN.uv - float(i - BLUR_NUM_WEIGHTS + 1) * m_texelOffset;
}
result.position = float4(IN.position.xy, 1.0, 1.0);
return result;
};
)";
static constexpr const char pShaderSourceText_Pixel_Blur[] = RMLUI_SHADER_BLUR_HEADER R"(
Texture2D g_InputTexture : register(t0);
SamplerState g_SamplerLinear : register(s0);
cbuffer SharedConstantBuffer : register(b0)
{
float4x4 m_transform;
float2 m_translate;
float2 m_texelOffset;
float4 m_weights;
float2 m_texCoordMin;
float2 m_texCoordMax;
};
struct PS_INPUT
{
float4 position : SV_Position;
float2 uv[BLUR_SIZE] : TEXCOORD;
};
float4 main(const PS_INPUT IN) : SV_TARGET
{
float4 color = float4(0.0, 0.0, 0.0, 0.0);
for(int i = 0; i < BLUR_SIZE; i++)
{
float2 in_region = step(m_texCoordMin, IN.uv[i]) * step(IN.uv[i], m_texCoordMax);
color += g_InputTexture.Sample(g_SamplerLinear, float2(IN.uv[i].x, 1.0f - IN.uv[i].y)) * in_region.x * in_region.y * m_weights[abs(i - BLUR_NUM_WEIGHTS + 1)];
}
return color;
};
)";
static constexpr const char pShaderSourceText_Pixel_DropShadow[] = R"(
Texture2D g_InputTexture : register(t0);
SamplerState g_SamplerLinear : register(s0);
cbuffer DropShadowBuffer : register(b0)
{
float2 m_texCoordMin;
float2 m_texCoordMax;
float4 m_color;
};
struct PS_INPUT
{
float4 position : SV_Position;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
float4 main(const PS_INPUT IN) : SV_TARGET
{
float2 in_region = step(m_texCoordMin, IN.uv) * step(IN.uv, m_texCoordMax);
return g_InputTexture.Sample(g_SamplerLinear, IN.uv).a * in_region.x * in_region.y * m_color;
};
)";
static constexpr const char pShaderSourceTet_Pixel_ColorMatrix[] = RMLUI_SHADER_HEADER R"(
Texture2D g_InputTexture : register(t0);
SamplerState g_SamplerLinear : register(s0);
cbuffer ConstantBuffer : register(b0)
{
float4x4 m_color_matrix;
};
struct PS_Input
{
float4 position : SV_Position;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
float4 main(const PS_Input IN) : SV_TARGET
{
// The general case uses a 4x5 color matrix for full rgba transformation, plus a constant term with the last column.
// However, we only consider the case of rgb transformations. Thus, we could in principle use a 3x4 matrix, but we
// keep the alpha row for simplicity.
// In the general case we should do the matrix transformation in non-premultiplied space. However, without alpha
// transformations, we can do it directly in premultiplied space to avoid the extra division and multiplication
// steps. In this space, the constant term needs to be multiplied by the alpha value, instead of unity.
float4 texColor = g_InputTexture.Sample(g_SamplerLinear, IN.uv);
float3 transformedColor = mul(m_color_matrix, texColor).rgb;
return float4(transformedColor, texColor.a);
};
)";
static constexpr const char pShaderSourceText_Pixel_BlendMask[] = RMLUI_SHADER_HEADER R"(
Texture2D g_InputTexture : register(t0);
Texture2D g_MaskTexture : register(t1);
SamplerState g_SamplerLinear : register(s0);
struct PS_INPUT
{
float4 position : SV_Position;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
float4 main(const PS_INPUT IN) : SV_TARGET
{
float4 texColor = g_InputTexture.Sample(g_SamplerLinear, IN.uv);
float maskAlpha = g_MaskTexture.Sample(g_SamplerLinear, IN.uv).a;
return texColor * maskAlpha;
};
)";
// We need to round up at compile-time so that we can address each element of the float4's
#define CEILING(x, y) (((x) + (y) - 1) / (y))
static const char pShaderSourceText_Pixel_Gradient[] =
RMLUI_SHADER_HEADER "#define MAX_NUM_STOPS_PACKED (uint)" RMLUI_STRINGIFY(CEILING(MAX_NUM_STOPS, 4)) R"(
#define LINEAR 0
#define RADIAL 1
#define CONIC 2
#define REPEATING_LINEAR 3
#define REPEATING_RADIAL 4
#define REPEATING_CONIC 5
#define PI 3.14159265
cbuffer SharedConstantBuffer : register(b0)
{
float4x4 m_transform;
float2 m_translate;
// One to one translation of the OpenGL uniforms results in a LOT of wasted space due to CBuffer alignment rules.
// Changes from GL3:
// - Moved m_num_stops below m_func (saved 4 bytes of padding).
// - Packed m_stop_positions into a float4[MAX_NUM_STOPS / 4] array, as each array element starts a new 16-byte row.
// The below layout has 0 bytes of padding.
int m_func; // one of the above definitions
int m_num_stops;
float2 m_p; // linear: starting point, radial: center, conic: center
float2 m_v; // linear: vector to ending point, radial: 2d curvature (inverse radius), conic: angled unit vector
float4 m_stop_colors[MAX_NUM_STOPS];
float4 m_stop_positions[MAX_NUM_STOPS_PACKED]; // normalized, 0 -> starting point, 1 -> ending point
};
// Hide the way the data is packed in the cbuffer through a macro
// @NOTE: Hardcoded for MAX_NUM_STOPS 16.
// i >> 2 => i >> sqrt(MAX_NUM_STOPS)
#define GET_STOP_POS(i) (m_stop_positions[i >> 2][i & 3])
struct PS_INPUT
{
float4 position : SV_Position;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
#define glsl_mod(x,y) (((x)-(y)*floor((x)/(y))))
float4 lerp_stop_colors(float t) {
float4 color = m_stop_colors[0];
for (int i = 1; i < m_num_stops; i++)
color = lerp(color, m_stop_colors[i], smoothstep(GET_STOP_POS(i-1), GET_STOP_POS(i), t));
return color;
};
float4 main(const PS_INPUT IN) : SV_TARGET
{
float t = 0.0;
if (m_func == LINEAR || m_func == REPEATING_LINEAR) {
float dist_square = dot(m_v, m_v);
float2 V = IN.uv.xy - m_p;
t = dot(m_v, V) / dist_square;
}
else if (m_func == RADIAL || m_func == REPEATING_RADIAL) {
float2 V = IN.uv.xy - m_p;
t = length(m_v * V);
}
else if (m_func == CONIC || m_func == REPEATING_CONIC) {
float2x2 R = float2x2(m_v.x, -m_v.y, m_v.y, m_v.x);
float2 V = mul((IN.uv.xy - m_p), R);
t = 0.5 + atan2(-V.x, V.y) / (2.0 * PI);
}
if (m_func == REPEATING_LINEAR || m_func == REPEATING_RADIAL || m_func == REPEATING_CONIC) {
float t0 = GET_STOP_POS(0);
float t1 = GET_STOP_POS(m_num_stops - 1);
t = t0 + glsl_mod(t - t0, t1 - t0);
}
return IN.color * lerp_stop_colors(t);
};
)";
static constexpr const char pShaderSourceText_Pixel_Creation[] = RMLUI_SHADER_HEADER R"(
struct PS_Input
{
float4 position : SV_Position;
float4 color : COLOR;
float2 uv : TEXCOORD;
};
cbuffer SharedConstantBuffer : register(b0)
{
float4x4 m_transform;
float2 m_translate;
float2 m_dimensions;
float m_value;
};
#define glsl_mod(x,y) (((x)-(y)*floor((x)/(y))))
float4 main(const PS_Input IN) : SV_TARGET
{
float t = m_value;
float3 c;
float l;
for (int i = 0; i < 3; i++) {
float2 p = IN.uv;
float2 uv = p;
p -= .5;
p.x *= m_dimensions.x / m_dimensions.y;
float z = t + ((float)i) * .07;
l = length(p);
uv += p / l * (sin(z) + 1.) * abs(sin(l * 9. - z - z));
c[i] = .01 / length(glsl_mod(uv, 1.) - .5);
}
return float4(c / l, IN.color.a);
};
)";
// AlignUp(314, 256) = 512
template <typename T>
static T AlignUp(T val, T alignment)
{
RMLUI_ZoneScopedN("DirectX 12 - AlignUp");
return (val + alignment - (T)1) & ~(alignment - (T)1);
}
static Rml::Colourf ConvertToColorf(Rml::ColourbPremultiplied c0)
{
RMLUI_ZoneScopedN("DirectX 12 - ConvertToColorf");
Rml::Colourf result;
for (int i = 0; i < 4; i++)
result[i] = (1.f / 255.f) * float(c0[i]);
return result;
}
/// Flip vertical axis of the rectangle, and move its origin to the vertically opposite side of the viewport.
/// @note Changes coordinate system from RmlUi to OpenGL, or equivalently in reverse.
/// @note The Rectangle::Top and Rectangle::Bottom members will have reverse meaning in the returned rectangle.
static Rml::Rectanglei VerticallyFlipped(Rml::Rectanglei rect, int viewport_height)
{
RMLUI_ZoneScopedN("DirectX 12 - VerticallyFlipped");
RMLUI_ASSERT(rect.Valid());
Rml::Rectanglei flipped_rect = rect;
flipped_rect.p0.y = viewport_height - rect.p1.y;
flipped_rect.p1.y = viewport_height - rect.p0.y;
return flipped_rect;
}
static void SetBlurWeights(Rml::Vector4f& p_weights, float sigma)
{
float weights[BLUR_NUM_WEIGHTS];
float normalization = 0.0f;
for (int i = 0; i < BLUR_NUM_WEIGHTS; i++)
{
if (Rml::Math::Absolute(sigma) < 0.1f)
weights[i] = float(i == 0);
else
weights[i] = Rml::Math::Exp(-float(i * i) / (2.0f * sigma * sigma)) / (Rml::Math::SquareRoot(2.f * Rml::Math::RMLUI_PI) * sigma);
normalization += (i == 0 ? 1.f : 2.0f) * weights[i];
}
for (int i = 0; i < BLUR_NUM_WEIGHTS; i++)
weights[i] /= normalization;
p_weights.x = weights[0];
p_weights.y = weights[1];
p_weights.z = weights[2];
p_weights.w = weights[3];
}
static void SetTexCoordLimits(Rml::Vector2f& p_tex_coord_min, Rml::Vector2f& p_tex_coord_max, Rml::Rectanglei rectangle_flipped,
Rml::Vector2i framebuffer_size)
{
// Offset by half-texel values so that texture lookups are clamped to fragment centers, thereby avoiding color
// bleeding from neighboring texels due to bilinear interpolation.
const Rml::Vector2f min = (Rml::Vector2f(rectangle_flipped.p0) + Rml::Vector2f(0.5f)) / Rml::Vector2f(framebuffer_size);
const Rml::Vector2f max = (Rml::Vector2f(rectangle_flipped.p1) - Rml::Vector2f(0.5f)) / Rml::Vector2f(framebuffer_size);
p_tex_coord_min.x = min.x;
p_tex_coord_min.y = min.y;
p_tex_coord_max.x = max.x;
p_tex_coord_max.y = max.y;
}
/// Helper function from the d3dx12 header (MIT License - Microsoft). We avoid the full header for lightness.
/// https://github.com/microsoft/DirectX-Graphics-Samples/blob/71f3c57648b6cecde532f67dccd07265485e2313/TechniqueDemos/D3D12MemoryManagement/src/d3dx12.h#L1761C5-L1762C43)
inline UINT64 GetRequiredIntermediateSize(_In_ ID3D12Resource* pDestinationResource, _In_range_(0, D3D12_REQ_SUBRESOURCES) UINT FirstSubresource,
_In_range_(0, D3D12_REQ_SUBRESOURCES - FirstSubresource) UINT NumSubresources)
{
D3D12_RESOURCE_DESC Desc = pDestinationResource->GetDesc();
UINT64 RequiredSize = 0;
ID3D12Device* pDevice = nullptr;
pDestinationResource->GetDevice(__uuidof(*pDevice), reinterpret_cast<void**>(&pDevice));
pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, 0, nullptr, nullptr, nullptr, &RequiredSize);
pDevice->Release();
return RequiredSize;
}
/// Helper function from the d3dx12 header (MIT License, Microsoft). We avoid the full header for lightness.
/// https://github.com/microsoft/DirectX-Graphics-Samples/blob/71f3c57648b6cecde532f67dccd07265485e2313/TechniqueDemos/D3D12MemoryManagement/src/d3dx12.h#L1740
inline void MemcpySubresource(const _In_ D3D12_MEMCPY_DEST* pDest, const _In_ D3D12_SUBRESOURCE_DATA* pSrc, SIZE_T RowSizeInBytes, UINT NumRows,
UINT NumSlices)
{
if (!pSrc)
return;
if (!pSrc->pData)
return;
if (!pDest)
return;
for (UINT z = 0; z < NumSlices; ++z)
{
BYTE* pDestSlice = reinterpret_cast<BYTE*>(pDest->pData) + pDest->SlicePitch * z;
const BYTE* pSrcSlice = reinterpret_cast<const BYTE*>(pSrc->pData) + pSrc->SlicePitch * z;
for (UINT y = 0; y < NumRows; ++y)
{
memcpy(pDestSlice + pDest->RowPitch * y, pSrcSlice + pSrc->RowPitch * y, RowSizeInBytes);
}
}
}
/// Helper function from the d3dx12 header (MIT License, Microsoft). We avoid the full header for lightness.
/// https://github.com/microsoft/DirectX-Graphics-Samples/blob/71f3c57648b6cecde532f67dccd07265485e2313/TechniqueDemos/D3D12MemoryManagement/src/d3dx12.h#L1780C15-L1780C33
inline UINT64 UpdateSubresources(_In_ ID3D12GraphicsCommandList* pCmdList, _In_ ID3D12Resource* pDestinationResource,
_In_ ID3D12Resource* pIntermediate, _In_range_(0, D3D12_REQ_SUBRESOURCES) UINT FirstSubresource,
_In_range_(0, D3D12_REQ_SUBRESOURCES - FirstSubresource) UINT NumSubresources, UINT64 RequiredSize,
_In_reads_(NumSubresources) const D3D12_PLACED_SUBRESOURCE_FOOTPRINT* pLayouts, _In_reads_(NumSubresources) const UINT* pNumRows,
_In_reads_(NumSubresources) const UINT64* pRowSizesInBytes, _In_reads_(NumSubresources) const D3D12_SUBRESOURCE_DATA* pSrcData)
{
// Minor validation
D3D12_RESOURCE_DESC IntermediateDesc = pIntermediate->GetDesc();
D3D12_RESOURCE_DESC DestinationDesc = pDestinationResource->GetDesc();
if (IntermediateDesc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER || IntermediateDesc.Width < RequiredSize + pLayouts[0].Offset ||
RequiredSize > (SIZE_T)-1 ||
(DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER && (FirstSubresource != 0 || NumSubresources != 1)))
{
return 0;
}
BYTE* pData;
HRESULT hr = pIntermediate->Map(0, NULL, reinterpret_cast<void**>(&pData));
if (FAILED(hr))
{
return 0;
}
for (UINT i = 0; i < NumSubresources; ++i)
{
if (pRowSizesInBytes[i] > (SIZE_T)-1)
return 0;
D3D12_MEMCPY_DEST DestData = {pData + pLayouts[i].Offset, pLayouts[i].Footprint.RowPitch, pLayouts[i].Footprint.RowPitch * pNumRows[i]};
MemcpySubresource(&DestData, &pSrcData[i], (SIZE_T)pRowSizesInBytes[i], pNumRows[i], pLayouts[i].Footprint.Depth);
}
pIntermediate->Unmap(0, NULL);
if (DestinationDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
{
D3D12_BOX SrcBox;
SrcBox.left = UINT(pLayouts[0].Offset);
SrcBox.right = UINT(pLayouts[0].Offset + pLayouts[0].Footprint.Width);
SrcBox.top = 0;
SrcBox.front = 0;
SrcBox.bottom = 1;
SrcBox.back = 1;
pCmdList->CopyBufferRegion(pDestinationResource, 0, pIntermediate, pLayouts[0].Offset, pLayouts[0].Footprint.Width);
}
else
{
for (UINT i = 0; i < NumSubresources; ++i)
{
D3D12_TEXTURE_COPY_LOCATION Dst;
Dst.pResource = pDestinationResource;
Dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
Dst.SubresourceIndex = i + FirstSubresource;
D3D12_TEXTURE_COPY_LOCATION Src;
Src.pResource = pIntermediate;
Src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
Src.PlacedFootprint = pLayouts[i];
pCmdList->CopyTextureRegion(&Dst, 0, 0, 0, &Src, nullptr);
}
}
return RequiredSize;
}
/// stack-allocating
/// Helper function from the d3dx12 header (MIT License, Microsoft). We avoid the full header for lightness.
/// https://github.com/microsoft/DirectX-Graphics-Samples/blob/71f3c57648b6cecde532f67dccd07265485e2313/TechniqueDemos/D3D12MemoryManagement/src/d3dx12.h#L1876
template <UINT MaxSubresources>
inline UINT64 UpdateSubresources(_In_ ID3D12GraphicsCommandList* pCmdList, _In_ ID3D12Resource* pDestinationResource,
_In_ ID3D12Resource* pIntermediate, UINT64 IntermediateOffset, _In_range_(0, MaxSubresources) UINT FirstSubresource,
_In_range_(1, MaxSubresources - FirstSubresource) UINT NumSubresources, _In_reads_(NumSubresources) D3D12_SUBRESOURCE_DATA* pSrcData)
{
UINT64 RequiredSize = 0;
D3D12_PLACED_SUBRESOURCE_FOOTPRINT Layouts[MaxSubresources];
UINT NumRows[MaxSubresources];
UINT64 RowSizesInBytes[MaxSubresources];
D3D12_RESOURCE_DESC Desc = pDestinationResource->GetDesc();
ID3D12Device* pDevice = nullptr;
pDestinationResource->GetDevice(__uuidof(*pDevice), reinterpret_cast<void**>(&pDevice));
pDevice->GetCopyableFootprints(&Desc, FirstSubresource, NumSubresources, IntermediateOffset, Layouts, NumRows, RowSizesInBytes, &RequiredSize);
pDevice->Release();
return UpdateSubresources(pCmdList, pDestinationResource, pIntermediate, FirstSubresource, NumSubresources, RequiredSize, Layouts, NumRows,
RowSizesInBytes, pSrcData);
}
enum class ShaderGradientFunction { Linear, Radial, Conic, RepeatingLinear, RepeatingRadial, RepeatingConic }; // Must match shader definitions below.
enum class FilterType { Invalid = 0, Passthrough, Blur, DropShadow, ColorMatrix, MaskImage };
struct CompiledFilter {
FilterType type;
// Passthrough
float blend_factor;
// Blur
float sigma;
// Drop shadow
Rml::Vector2f offset;
Rml::ColourbPremultiplied color;
// ColorMatrix
Rml::Matrix4f color_matrix;
};
enum class CompiledShaderType { Invalid = 0, Gradient, Creation };
struct CompiledShader {
CompiledShaderType type;
// Gradient
ShaderGradientFunction gradient_function;
Rml::Vector2f p;
Rml::Vector2f v;
Rml::Vector<float> stop_positions;
Rml::Vector<Rml::Colourf> stop_colors;
// Shader
Rml::Vector2f dimensions;
};
// those programs that have postfix as _MSAA it means that it accepts render target with sample count >= 2 (thus it is called MSAA)
enum class ProgramId : int {
None,
Color_Stencil_Always,
Color_Stencil_Equal,
Color_Stencil_Set,
Color_Stencil_SetInverse,
Color_Stencil_Intersect,
Color_Stencil_Disabled,
Texture_Stencil_Always,
Texture_Stencil_Equal,
Texture_Stencil_Disabled,
Gradient,
Creation,
// this is for presenting our msaa render target texture for NO MSAA RT
// if you do not correctly stuff DX12 validation will say about different
// sample count like it is expected 1 (because no MSAA) but your RT target texture was created with
// sample count = 2, so it is not a correct way of using it
Passthrough,
Passthrough_NoDepthStencil,
Passthrough_Opacity,
Passthrough_MSAA,
Passthrough_MSAA_Equal,
Passthrough_NoBlend, // for MSAA RT
Passthrough_NoBlendAndNoMSAA, // for RT that's not MSAA
ColorMatrix,
BlendMask,
Blur,
DropShadow,
Count
};
#define MAX_NUM_STOPS 16
#define BLUR_SIZE 7
#define BLUR_NUM_WEIGHTS ((BLUR_SIZE + 1) / 2)
#define RMLUI_STRINGIFY_IMPL(x) #x
#define RMLUI_STRINGIFY(x) RMLUI_STRINGIFY_IMPL(x)
#pragma comment(lib, "d3dcompiler.lib")
#pragma comment(lib, "d3d12.lib")
#pragma comment(lib, "dxgi.lib")
#ifdef RMLUI_DX_DEBUG
#pragma comment(lib, "dxguid.lib")
#endif
#ifdef RMLUI_DX_DEBUG
// first argument specifies that input string is char if it is needed to use other languages than English then use u8 prefix
// example: RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "::RenderGeometry");
// example UTF8: RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, u8"::RenderGeometry");
#define RMLUI_DX_MARKER_BEGIN(list, name) \
if (list) \
list->BeginEvent(1, reinterpret_cast<const char*>(name), static_cast<UINT>(strlen(name) + 1));
#define RMLUI_DX_MARKER_END(list) \
if (list) \
list->EndEvent();
#else
#define RMLUI_DX_MARKER_BEGIN(list, name)
#define RMLUI_DX_MARKER_END(list)
#endif
namespace Gfx {
struct FramebufferData {
public:
FramebufferData() :
m_is_render_target{true}, m_width{}, m_height{}, m_id{-1}, m_p_texture{}, m_p_texture_depth_stencil{}, m_texture_descriptor_resource_view{},
m_allocation_descriptor_offset{}
{}
FramebufferData(const FramebufferData&) = delete;
FramebufferData& operator=(const FramebufferData&) = delete;
// overload because we don't need to copy information about how our object was allocated (from storage or on stack, see m_is_allocated_on_stack)
FramebufferData(FramebufferData&& data) noexcept :
m_is_render_target{std::exchange(data.m_is_render_target, true)}, m_width{std::exchange(data.m_width, 0)},
m_height{std::exchange(data.m_height, 0)}, m_id{std::exchange(data.m_id, -1)}, m_p_texture{std::exchange(data.m_p_texture, nullptr)},
m_p_texture_depth_stencil{std::exchange(data.m_p_texture_depth_stencil, nullptr)},
m_texture_descriptor_resource_view{std::exchange(data.m_texture_descriptor_resource_view, {})},
m_allocation_descriptor_offset{std::exchange(data.m_allocation_descriptor_offset, {})}
{}
// overload because we don't need to copy information about how our object was allocated (from storage or on stack, see m_is_allocated_on_stack)
FramebufferData& operator=(FramebufferData&& data) noexcept
{
m_is_render_target = std::exchange(data.m_is_render_target, true);
m_width = std::exchange(data.m_width, 0);
m_height = std::exchange(data.m_height, 0);
m_id = std::exchange(data.m_id, -1);
m_p_texture = std::exchange(data.m_p_texture, nullptr);
m_p_texture_depth_stencil = std::exchange(data.m_p_texture_depth_stencil, nullptr);
m_texture_descriptor_resource_view = std::exchange(data.m_texture_descriptor_resource_view, {});
m_allocation_descriptor_offset = std::exchange(data.m_allocation_descriptor_offset, {});
return *this;
}
~FramebufferData()
{
m_id = -1;
#ifdef RMLUI_DX_DEBUG
if (m_is_allocated_on_stack == false)
RMLUI_ASSERTMSG(m_p_texture == nullptr, "you must manually deallocate texture and set this field as nullptr!");
#endif
}
int Get_Width() const { return m_width; }
void Set_Width(int value) { m_width = value; }
int Get_Height() const { return m_height; }
void Set_Height(int value) { m_height = value; }
void Set_ID(int layer_current_size_index) { m_id = layer_current_size_index; }
int Get_ID() const { return m_id; }
void Set_Texture(RenderInterface_DX12::TextureHandleType* p_texture) { m_p_texture = p_texture; }
RenderInterface_DX12::TextureHandleType* Get_Texture() const { return m_p_texture; }
// ResourceView means RTV or DSV
void Set_DescriptorResourceView(const D3D12_CPU_DESCRIPTOR_HANDLE& handle) { m_texture_descriptor_resource_view = handle; }
const D3D12_CPU_DESCRIPTOR_HANDLE& Get_DescriptorResourceView() const { return m_texture_descriptor_resource_view; }
void Set_SharedDepthStencilTexture(FramebufferData* p_data) { m_p_texture_depth_stencil = p_data; }
FramebufferData* Get_SharedDepthStencilTexture() const { return m_p_texture_depth_stencil; }
D3D12MA::VirtualAllocation* Get_VirtualAllocation_Descriptor() { return &m_allocation_descriptor_offset; }
bool Is_RenderTarget() const { return m_is_render_target; }
void Set_RenderTarget(bool value) { m_is_render_target = value; }
#ifdef RMLUI_DX_DEBUG
// in order to prevent false triggering assert in destructor we determine if object was created on stack or not
// if not we manually set this field to false
bool m_is_allocated_on_stack = true;
#endif
private:
bool m_is_render_target;
int m_width;
int m_height;
int m_id;
RenderInterface_DX12::TextureHandleType* m_p_texture;
// this is shared texture and original pointer stored and managed at renderlayerstack class
FramebufferData* m_p_texture_depth_stencil;
D3D12_CPU_DESCRIPTOR_HANDLE m_texture_descriptor_resource_view;
// from dsv or rtv heap!!
D3D12MA::VirtualAllocation m_allocation_descriptor_offset;
};
} // namespace Gfx
RenderInterface_DX12::RenderInterface_DX12(void* p_window_handle, const Backend::RmlRendererSettings& settings) :
m_is_use_vsync{settings.vsync}, m_is_use_tearing{}, m_is_scissor_was_set{}, m_is_stencil_enabled{}, m_is_stencil_equal{}, m_is_use_msaa{true},
m_msaa_sample_count{settings.msaa_sample_count}, m_user_framebuffer_index{}, m_width{}, m_height{}, m_current_clip_operation{-1},
m_active_program_id{}, m_size_descriptor_heap_render_target_view{}, m_size_descriptor_heap_shaders{}, m_current_back_buffer_index{},
m_stencil_ref_value{}, m_p_device{}, m_p_command_queue{}, m_p_copy_queue{}, m_p_swapchain{}, m_p_command_graphics_list{},
m_p_command_graphics_list_screenshot{}, m_p_command_allocator_screenshot{}, m_p_descriptor_heap_render_target_view{},
m_p_descriptor_heap_render_target_view_for_texture_manager{}, m_p_descriptor_heap_depth_stencil_view_for_texture_manager{},
m_p_descriptor_heap_shaders{}, m_p_descriptor_heap_depthstencil{}, m_p_depthstencil_resource{}, m_p_backbuffer_fence{}, m_p_fence_screenshot{},
m_p_adapter{}, m_p_copy_allocator{}, m_p_copy_command_list{}, m_p_allocator{}, m_p_offset_allocator_for_descriptor_heap_shaders{},
m_p_user_rtv_present{}, m_p_user_dsv_present{}, m_p_window_handle{}, m_p_fence_event{}, m_p_fence_event_screenshot{}, m_fence_value{},
m_fence_screenshot_value{}, m_precompiled_fullscreen_quad_geometry{}
{
static_assert(RenderInterface_DX12::NumPrograms == static_cast<size_t>(ProgramId::Count),
"Please adjust the NumPrograms constant for consistency with the number of program IDs.");
RMLUI_ZoneScopedN("DirectX 12 - Constructor (non user)");
RMLUI_ASSERTMSG(p_window_handle, "you can't pass an empty window handle! (also it must be castable to HWND)");
m_p_window_handle = static_cast<HWND>(p_window_handle);
std::memset(m_pipelines, 0, sizeof(m_pipelines));
std::memset(m_root_signatures, 0, sizeof(m_root_signatures));
std::memset(m_constant_buffer_count_per_frame.data(), 0, sizeof(m_constant_buffer_count_per_frame));
#ifdef RMLUI_DX_DEBUG
m_default_shader_flags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
m_default_shader_flags = 0;
#endif
RMLUI_ZoneScopedN("DirectX 12 - Initialize");
Initialize_DebugLayer();
Initialize_Adapter();
Initialize_Device();
unsigned char max_msaa_supported_sample_count = GetMSAASupportedSampleCount(64);
m_is_use_msaa = m_msaa_sample_count <= max_msaa_supported_sample_count;
// requested count is 1 so we force it to false because in case if GPU doesn't support multisampling and returns 1 we get 1 == 1 situation
// and m_is_use_msaa will be true but it is not right and validation layers will get assertions about this situation like we want to resolve
// resource that as source with sample count equal to 1 but it expects to be >= 2
if (m_msaa_sample_count == 1)
m_is_use_msaa = false;
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::LT_INFO, "[DirectX-12] Max supported MSAA sample count (Quality:0): %d", max_msaa_supported_sample_count);
Rml::Log::Message(Rml::Log::LT_INFO, "[DirectX-12] Requested MSAA level: %d (compile-time: %d | supported: %d)", m_msaa_sample_count,
RMLUI_RENDER_BACKEND_FIELD_MSAA_SAMPLE_COUNT, max_msaa_supported_sample_count);
Rml::Log::Message(Rml::Log::LT_INFO, "[DirectX-12] MSAA: %s", m_is_use_msaa ? "supported and enabled" : "Not supported by hardware and disabled");
#endif
m_p_command_queue = Create_CommandQueue(D3D12_COMMAND_LIST_TYPE_DIRECT);
m_p_copy_queue = Create_CommandQueue(D3D12_COMMAND_LIST_TYPE_COPY);
RMLUI_ASSERTMSG(m_p_command_queue, "must create command queue!");
RMLUI_ASSERTMSG(m_p_copy_queue, "must create copy queue!");
#ifdef RMLUI_DX_DEBUG
m_p_copy_queue->SetName(TEXT("Copy Queue (for texture manager)"));
#endif
Initialize_Swapchain(0, 0);
m_p_descriptor_heap_render_target_view = Create_Resource_DescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
D3D12_DESCRIPTOR_HEAP_FLAGS::D3D12_DESCRIPTOR_HEAP_FLAG_NONE, RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT);
m_p_descriptor_heap_render_target_view_for_texture_manager = Create_Resource_DescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
D3D12_DESCRIPTOR_HEAP_FLAGS::D3D12_DESCRIPTOR_HEAP_FLAG_NONE, RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_RTV);
m_p_descriptor_heap_shaders = Create_Resource_DescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
D3D12_DESCRIPTOR_HEAP_FLAGS::D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, RMLUI_RENDER_BACKEND_FIELD_DESCRIPTORAMOUNT_FOR_SRV_CBV_UAV);
m_p_descriptor_heap_depthstencil =
Create_Resource_DescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, D3D12_DESCRIPTOR_HEAP_FLAGS::D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 1);
m_p_descriptor_heap_depth_stencil_view_for_texture_manager = Create_Resource_DescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE_DSV,
D3D12_DESCRIPTOR_HEAP_FLAGS::D3D12_DESCRIPTOR_HEAP_FLAG_NONE, RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_DSV);
m_handle_shaders = D3D12_CPU_DESCRIPTOR_HANDLE(m_p_descriptor_heap_shaders->GetCPUDescriptorHandleForHeapStart());
m_size_descriptor_heap_render_target_view = m_p_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
m_size_descriptor_heap_shaders = m_p_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
Create_Resources_DependentOnSize();
Initialize_CommandAllocators();
m_p_command_graphics_list =
Create_CommandList(m_backbuffers_allocators.at(m_current_back_buffer_index), D3D12_COMMAND_LIST_TYPE::D3D12_COMMAND_LIST_TYPE_DIRECT);
Initialize_Allocator();
m_p_command_allocator_screenshot = Create_CommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT);
m_p_command_graphics_list_screenshot = Create_CommandList(m_p_command_allocator_screenshot, D3D12_COMMAND_LIST_TYPE_DIRECT);
Initialize_SyncPrimitives();
Initialize_SyncPrimitives_Screenshot();
m_p_copy_allocator = Create_CommandAllocator(D3D12_COMMAND_LIST_TYPE::D3D12_COMMAND_LIST_TYPE_COPY);
m_p_copy_command_list = Create_CommandList(m_p_copy_allocator, D3D12_COMMAND_LIST_TYPE::D3D12_COMMAND_LIST_TYPE_COPY);
m_p_offset_allocator_for_descriptor_heap_shaders =
new OffsetAllocator::Allocator(RMLUI_RENDER_BACKEND_FIELD_DESCRIPTORAMOUNT_FOR_SRV_CBV_UAV * m_size_descriptor_heap_shaders);
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "amount of srv_cbv_uav: %d size of increment: %d",
RMLUI_RENDER_BACKEND_FIELD_DESCRIPTORAMOUNT_FOR_SRV_CBV_UAV, m_size_descriptor_heap_shaders);
#endif
m_manager_buffer.Initialize(m_p_device, m_p_allocator, m_p_offset_allocator_for_descriptor_heap_shaders, &m_handle_shaders,
m_size_descriptor_heap_shaders);
m_manager_texture.Initialize(m_p_allocator, m_p_offset_allocator_for_descriptor_heap_shaders, m_p_device, m_p_copy_command_list,
m_p_command_graphics_list, m_p_copy_allocator, m_p_descriptor_heap_shaders, m_p_descriptor_heap_render_target_view_for_texture_manager,
m_p_descriptor_heap_depth_stencil_view_for_texture_manager, m_p_copy_queue, &m_handle_shaders, this);
m_manager_render_layer.Initialize(this);
Create_Resource_Pipelines();
Rml::Mesh mesh;
Rml::MeshUtilities::GenerateQuad(mesh, Rml::Vector2f(-1.f), Rml::Vector2f(2.f), {});
m_precompiled_fullscreen_quad_geometry = RenderInterface_DX12::CompileGeometry(mesh.vertices, mesh.indices);
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "DirectX 12 Initialize type: full");
#endif
}
RenderInterface_DX12::~RenderInterface_DX12()
{
RMLUI_ZoneScopedN("DirectX 12 - Destructor");
Flush();
m_manager_render_layer.Shutdown();
Destroy_Resource_Pipelines();
if (m_precompiled_fullscreen_quad_geometry)
{
Free_Geometry(reinterpret_cast<GeometryHandleType*>(m_precompiled_fullscreen_quad_geometry));
m_precompiled_fullscreen_quad_geometry = {};
}
m_manager_buffer.Shutdown();
m_manager_texture.Shutdown();
if (m_p_offset_allocator_for_descriptor_heap_shaders)
{
delete m_p_offset_allocator_for_descriptor_heap_shaders;
m_p_offset_allocator_for_descriptor_heap_shaders = nullptr;
}
if (m_p_command_graphics_list_screenshot)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_command_graphics_list_screenshot->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
if (m_p_command_allocator_screenshot)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_command_allocator_screenshot->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
Destroy_SyncPrimitives_Screenshot();
{
Destroy_Allocator();
Destroy_SyncPrimitives();
Destroy_Resource_RenderTagetViews();
Destroy_Swapchain();
Destroy_CommandAllocators();
if (m_p_command_graphics_list)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_command_graphics_list->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
if (m_p_command_queue)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_command_queue->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
if (m_p_descriptor_heap_render_target_view)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_descriptor_heap_render_target_view->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
if (m_p_descriptor_heap_render_target_view_for_texture_manager)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_descriptor_heap_render_target_view_for_texture_manager->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
if (m_p_descriptor_heap_depth_stencil_view_for_texture_manager)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_descriptor_heap_depth_stencil_view_for_texture_manager->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
if (m_p_descriptor_heap_shaders)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_descriptor_heap_shaders->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
if (m_p_descriptor_heap_depthstencil)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_descriptor_heap_depthstencil->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
if (m_p_copy_command_list)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_copy_command_list->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
if (m_p_copy_allocator)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_copy_allocator->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
if (m_p_copy_queue)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_copy_queue->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
if (m_p_adapter)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_adapter->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
if (m_p_device)
{
// if here you got assert on debug that's 100% leak and not just showing some connections that will be later released like other instances
// might show (but not true due to correct order of deallocations you get the correct report of unhanleded aka not released resources in
// dx12), so in good scenario every instance must return ref_count == 0
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_device->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
}
m_p_command_allocator_screenshot = nullptr;
m_p_command_graphics_list_screenshot = nullptr;
m_p_device = nullptr;
m_p_adapter = nullptr;
m_p_window_handle = nullptr;
m_p_swapchain = nullptr;
m_p_command_graphics_list = nullptr;
m_p_command_queue = nullptr;
m_p_descriptor_heap_render_target_view = nullptr;
m_p_allocator = nullptr;
m_p_descriptor_heap_shaders = nullptr;
m_p_descriptor_heap_depthstencil = nullptr;
m_p_copy_command_list = nullptr;
m_p_copy_allocator = nullptr;
#ifdef RMLUI_DX_DEBUG
{
auto dll_dxgidebug = LoadLibraryEx(TEXT("dxgidebug.dll"), nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (dll_dxgidebug)
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "loaded dxgidebug.dll for detecting memory leaks on DirectX's side!");
typedef HRESULT(WINAPI * LPDXGIGETDEBUGINTERFACE)(REFIID, void**);
auto callback_DXGIGetDebugInterface =
reinterpret_cast<LPDXGIGETDEBUGINTERFACE>(reinterpret_cast<void*>(GetProcAddress(dll_dxgidebug, "DXGIGetDebugInterface")));
if (callback_DXGIGetDebugInterface)
{
IDXGIDebug* p_debug_references{};
auto status = callback_DXGIGetDebugInterface(IID_PPV_ARGS(&p_debug_references));
RMLUI_DX_VERIFY_MSG(status, "failed to DXGIGetDebugInterface");
if (SUCCEEDED(status))
{
if (p_debug_references)
{
p_debug_references->ReportLiveObjects(DXGI_DEBUG_ALL,
DXGI_DEBUG_RLO_FLAGS(DXGI_DEBUG_RLO_SUMMARY | DXGI_DEBUG_RLO_IGNORE_INTERNAL));
p_debug_references->Release();
}
}
else
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG,
"Failed to initialize IDXGIDebug interface by DXGIGetDebugInterface function from dxgidebug.dll!");
}
}
}
else
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG,
"your Windows version is too old for loading dxgidebug.dll! (your OS's SDK doesn't provide a such dll)");
}
}
#endif
Rml::Log::Message(Rml::Log::Type::LT_INFO, "Backend DirectX 12 is destroyed!");
}
RenderInterface_DX12::operator bool() const
{
RMLUI_ZoneScopedN("DirectX 12 - operator bool()");
return !!(m_p_device);
}
void RenderInterface_DX12::BeginFrame()
{
RMLUI_ZoneScopedN("DirectX 12 - BeginFrame");
auto* p_command_allocator = m_backbuffers_allocators.at(m_current_back_buffer_index);
RMLUI_ASSERTMSG(p_command_allocator, "should be allocated and initialized! Probably early calling");
RMLUI_ASSERTMSG(m_p_command_graphics_list, "must be allocated and initialized! Probably early calling");
if (p_command_allocator)
{
RMLUI_DX_VERIFY_MSG(p_command_allocator->Reset(), "failed to reset command allocator");
}
if (m_p_command_graphics_list)
{
RMLUI_DX_VERIFY_MSG(m_p_command_graphics_list->Reset(p_command_allocator, nullptr), "failed to reset command graphics list");
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "BeginFrame");
m_stencil_ref_value = 0;
m_is_scissor_was_set = false;
SetTransform(nullptr);
m_manager_render_layer.BeginFrame(m_width, m_height);
BindRenderTarget(m_manager_render_layer.GetTopLayer());
D3D12_VIEWPORT viewport{};
viewport.Height = static_cast<FLOAT>(m_height);
viewport.Width = static_cast<FLOAT>(m_width);
viewport.MaxDepth = 1.0f;
m_p_command_graphics_list->RSSetViewports(1, &viewport);
UseProgram(ProgramId::None);
m_is_stencil_equal = false;
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
}
void RenderInterface_DX12::EndFrame()
{
RMLUI_ZoneScopedN("DirectX 12 - EndFrame");
auto* p_resource_backbuffer = m_backbuffers_resources.at(m_current_back_buffer_index);
RMLUI_ASSERTMSG(p_resource_backbuffer, "should be allocated and initialized! Probably early calling");
RMLUI_ASSERTMSG(m_p_command_graphics_list, "Must be allocated and initialzied. Probably early calling!");
RMLUI_ASSERTMSG(m_p_command_queue, "Must be allocated and initialzied. Probably early calling!");
if (m_p_command_graphics_list)
{
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "EndFrame");
D3D12_RESOURCE_BARRIER backbuffer_barrier_from_rt_to_present;
backbuffer_barrier_from_rt_to_present.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
backbuffer_barrier_from_rt_to_present.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
backbuffer_barrier_from_rt_to_present.Transition.pResource = p_resource_backbuffer;
backbuffer_barrier_from_rt_to_present.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
backbuffer_barrier_from_rt_to_present.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
backbuffer_barrier_from_rt_to_present.Transition.Subresource = 0;
const Gfx::FramebufferData& fb_active = m_manager_render_layer.GetTopLayer();
const Gfx::FramebufferData& fb_postprocess = m_manager_render_layer.GetPostprocessPrimary();
ID3D12Resource* p_msaa_texture{};
ID3D12Resource* p_postprocess_texture{};
TextureHandleType* p_handle_postprocess_texture = fb_postprocess.Get_Texture();
if (fb_active.Get_Texture())
{
TextureHandleType* p_resource = fb_active.Get_Texture();
RMLUI_ASSERTMSG(p_resource->Get_Info().buffer_index == -1, "can't be allocated as placed resource no sense!");
D3D12MA::Allocation* p_allocation = static_cast<D3D12MA::Allocation*>(p_resource->Get_Resource());
p_msaa_texture = p_allocation->GetResource();
}
if (fb_postprocess.Get_Texture())
{
TextureHandleType* p_resource = fb_postprocess.Get_Texture();
RMLUI_ASSERTMSG(p_resource->Get_Info().buffer_index == -1, "can't be allocated as place resource no sense!");
D3D12MA::Allocation* p_allocation = static_cast<D3D12MA::Allocation*>(p_resource->Get_Resource());
p_postprocess_texture = p_allocation->GetResource();
}
RMLUI_ASSERTMSG(p_msaa_texture, "can't be, must be a valid texture!");
RMLUI_ASSERTMSG(p_postprocess_texture, "can't be, must be a valid texture!");
RMLUI_ASSERTMSG(p_handle_postprocess_texture, "must be valid!");
RMLUI_ASSERTMSG(p_msaa_texture->GetDesc().Width == p_postprocess_texture->GetDesc().Width, "must be same otherwise use blitframebuffer!");
RMLUI_ASSERTMSG(p_msaa_texture->GetDesc().Height == p_postprocess_texture->GetDesc().Height, "must be same otherwise use blitframebuffer!");
#if RMLUI_RENDER_BACKEND_FIELD_MSAA_SAMPLE_COUNT > 1
D3D12_RESOURCE_BARRIER barriers[2]{};
barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[0].Transition.Subresource = 0;
barriers[0].Transition.pResource = p_msaa_texture;
barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RESOLVE_SOURCE;
barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barriers[1].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[1].Transition.Subresource = 0;
barriers[1].Transition.pResource = p_postprocess_texture;
barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RESOLVE_DEST;
barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
D3D12_RESOURCE_BARRIER barrier_transition_from_msaa_resolve_source_to_rt;
barrier_transition_from_msaa_resolve_source_to_rt.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier_transition_from_msaa_resolve_source_to_rt.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier_transition_from_msaa_resolve_source_to_rt.Transition.Subresource = 0;
barrier_transition_from_msaa_resolve_source_to_rt.Transition.pResource = p_msaa_texture;
barrier_transition_from_msaa_resolve_source_to_rt.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier_transition_from_msaa_resolve_source_to_rt.Transition.StateBefore = D3D12_RESOURCE_STATE_RESOLVE_SOURCE;
m_p_command_graphics_list->ResourceBarrier(2, barriers);
m_p_command_graphics_list->ResolveSubresource(p_postprocess_texture, 0, p_msaa_texture, 0, RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT);
m_p_command_graphics_list->ResourceBarrier(1, &barrier_transition_from_msaa_resolve_source_to_rt);
D3D12_RESOURCE_BARRIER offscreen_texture_barrier_for_shader;
offscreen_texture_barrier_for_shader.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
offscreen_texture_barrier_for_shader.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
offscreen_texture_barrier_for_shader.Transition.pResource = p_postprocess_texture;
offscreen_texture_barrier_for_shader.Transition.Subresource = 0;
offscreen_texture_barrier_for_shader.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
offscreen_texture_barrier_for_shader.Transition.StateBefore = D3D12_RESOURCE_STATE_RESOLVE_DEST;
m_p_command_graphics_list->ResourceBarrier(1, &offscreen_texture_barrier_for_shader);
#else
D3D12_RESOURCE_BARRIER barriers[2]{};
barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[0].Transition.Subresource = 0;
barriers[0].Transition.pResource = p_msaa_texture;
barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barriers[1].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[1].Transition.Subresource = 0;
barriers[1].Transition.pResource = p_postprocess_texture;
barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
D3D12_RESOURCE_BARRIER barrier_transition_from_msaa_resolve_source_to_rt;
barrier_transition_from_msaa_resolve_source_to_rt.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier_transition_from_msaa_resolve_source_to_rt.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier_transition_from_msaa_resolve_source_to_rt.Transition.Subresource = 0;
barrier_transition_from_msaa_resolve_source_to_rt.Transition.pResource = p_msaa_texture;
barrier_transition_from_msaa_resolve_source_to_rt.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier_transition_from_msaa_resolve_source_to_rt.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
m_p_command_graphics_list->ResourceBarrier(2, barriers);
m_p_command_graphics_list->CopyResource(p_postprocess_texture, p_msaa_texture);
m_p_command_graphics_list->ResourceBarrier(1, &barrier_transition_from_msaa_resolve_source_to_rt);
D3D12_RESOURCE_BARRIER offscreen_texture_barrier_for_shader;
offscreen_texture_barrier_for_shader.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
offscreen_texture_barrier_for_shader.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
offscreen_texture_barrier_for_shader.Transition.pResource = p_postprocess_texture;
offscreen_texture_barrier_for_shader.Transition.Subresource = 0;
offscreen_texture_barrier_for_shader.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
offscreen_texture_barrier_for_shader.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
m_p_command_graphics_list->ResourceBarrier(1, &offscreen_texture_barrier_for_shader);
#endif
D3D12_CPU_DESCRIPTOR_HANDLE handle_rtv(m_p_descriptor_heap_render_target_view->GetCPUDescriptorHandleForHeapStart());
handle_rtv.ptr += m_current_back_buffer_index * (m_size_descriptor_heap_render_target_view);
D3D12_CPU_DESCRIPTOR_HANDLE handle_dsv(m_p_descriptor_heap_depthstencil->GetCPUDescriptorHandleForHeapStart());
m_p_command_graphics_list->OMSetRenderTargets(1, &handle_rtv, FALSE, &handle_dsv);
UseProgram(ProgramId::Passthrough);
BindTexture(p_handle_postprocess_texture);
DrawFullscreenQuad();
m_manager_render_layer.EndFrame();
m_p_command_graphics_list->ResourceBarrier(1, &backbuffer_barrier_from_rt_to_present);
D3D12_RESOURCE_BARRIER restore_state_of_postprocess_texture_return_to_rt;
restore_state_of_postprocess_texture_return_to_rt.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
restore_state_of_postprocess_texture_return_to_rt.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
restore_state_of_postprocess_texture_return_to_rt.Transition.Subresource = 0;
restore_state_of_postprocess_texture_return_to_rt.Transition.pResource = p_postprocess_texture;
restore_state_of_postprocess_texture_return_to_rt.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
restore_state_of_postprocess_texture_return_to_rt.Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
m_p_command_graphics_list->ResourceBarrier(1, &restore_state_of_postprocess_texture_return_to_rt);
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
RMLUI_DX_VERIFY_MSG(m_p_command_graphics_list->Close(), "failed to Close");
ID3D12CommandList* const lists[] = {m_p_command_graphics_list};
if (m_p_command_queue)
{
m_p_command_queue->ExecuteCommandLists(_countof(lists), lists);
}
UINT sync_interval = m_is_use_vsync ? 1 : 0;
UINT present_flags = (m_is_use_tearing && !m_is_use_vsync) ? DXGI_PRESENT_ALLOW_TEARING : 0;
RMLUI_DX_VERIFY_MSG(m_p_swapchain->Present(sync_interval, present_flags), "failed to Present");
auto fence_value = Signal(m_current_back_buffer_index);
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] current allocated constant buffers per draw (for frame[%d]): %zu",
m_current_back_buffer_index, m_constant_buffer_count_per_frame[m_current_back_buffer_index]);
#endif
m_constant_buffer_count_per_frame[m_current_back_buffer_index] = 0;
m_current_back_buffer_index = (uint32_t)m_p_swapchain->GetCurrentBackBufferIndex();
WaitForFenceValue(m_current_back_buffer_index);
Update_PendingForDeletion_Geometry();
m_backbuffers_fence_values[m_current_back_buffer_index] = fence_value + 1;
}
}
void RenderInterface_DX12::Clear()
{
RMLUI_ZoneScopedN("DirectX 12 - Clear");
RMLUI_ASSERTMSG(m_p_command_graphics_list, "early calling prob!");
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "Clear");
auto* p_backbuffer = m_backbuffers_resources.at(m_current_back_buffer_index);
D3D12_RESOURCE_BARRIER barrier;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Transition.pResource = p_backbuffer;
barrier.Transition.Subresource = 0;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
m_p_command_graphics_list->ResourceBarrier(1, &barrier);
constexpr FLOAT clear_color[] = {RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_RENDERTARGET_COLOR_VAlUE};
D3D12_CPU_DESCRIPTOR_HANDLE rtv(m_p_descriptor_heap_render_target_view->GetCPUDescriptorHandleForHeapStart());
rtv.ptr += m_current_back_buffer_index * (m_size_descriptor_heap_render_target_view);
m_p_command_graphics_list->ClearDepthStencilView(m_p_descriptor_heap_depthstencil->GetCPUDescriptorHandleForHeapStart(),
D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);
m_p_command_graphics_list->ClearRenderTargetView(rtv, clear_color, 0, nullptr);
auto& p_current_rtv = m_manager_render_layer.GetTopLayer().Get_DescriptorResourceView();
// auto& p_current_dsv = m_manager_render_layer.GetTopLayer().Get_SharedDepthStencilTexture()->Get_DescriptorResourceView();
constexpr FLOAT clear_color_framebuffer[] = {0.0f, 0.0f, 0.0f, 0.0f};
m_p_command_graphics_list->ClearRenderTargetView(p_current_rtv, clear_color_framebuffer, 0, nullptr);
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
void RenderInterface_DX12::SetViewport(int viewport_width, int viewport_height)
{
RMLUI_ZoneScopedN("DirectX 12 - SetViewport");
if (viewport_width <= 0 || viewport_height <= 0)
return;
if (m_width != viewport_width || m_height != viewport_height)
{
Flush();
if (m_p_depthstencil_resource)
{
Destroy_Resource_DepthStencil();
}
m_width = viewport_width;
m_height = viewport_height;
m_projection = Rml::Matrix4f::ProjectOrtho(0, static_cast<float>(viewport_width), static_cast<float>(viewport_height), 0, -10000, 10000);
if (m_p_swapchain)
{
Destroy_Resources_DependentOnSize();
for (int i = 0; i < RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT; ++i)
{
m_backbuffers_fence_values[i] = m_backbuffers_fence_values[m_current_back_buffer_index];
}
DXGI_SWAP_CHAIN_DESC desc;
RMLUI_DX_VERIFY_MSG(m_p_swapchain->GetDesc(&desc), "failed to GetDesc");
RMLUI_DX_VERIFY_MSG(m_p_swapchain->ResizeBuffers(static_cast<UINT>(RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT),
static_cast<UINT>(m_width), static_cast<UINT>(m_height), desc.BufferDesc.Format, desc.Flags),
"failed to ResizeBuffers");
m_current_back_buffer_index = m_p_swapchain->GetCurrentBackBufferIndex();
Create_Resources_DependentOnSize();
Create_Resource_DepthStencil();
}
}
}
void RenderInterface_DX12::RenderGeometry(Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation, Rml::TextureHandle texture)
{
RMLUI_ZoneScopedN("DirectX 12 - RenderGeometry");
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "RenderGeometry");
GeometryHandleType* p_handle_geometry = reinterpret_cast<GeometryHandleType*>(geometry);
TextureHandleType* p_handle_texture{};
RMLUI_ASSERTMSG(p_handle_geometry, "expected valid pointer!");
ConstantBufferType* p_constant_buffer{};
uint32_t default_index_for_one_cbv_that_will_be_used_in_vertex_shader{};
const uint32_t* p_constant_buffer_root_parameter_indicies{&default_index_for_one_cbv_that_will_be_used_in_vertex_shader};
uint8_t amount_of_constant_buffer_root_parameter_indicies{1};
if (texture == TexturePostprocess)
{
if (p_handle_geometry->Get_ConstantBuffer())
{
p_constant_buffer = p_handle_geometry->Get_ConstantBuffer();
p_constant_buffer_root_parameter_indicies =
p_handle_geometry->Get_ConstantBufferRootParameterIndices(amount_of_constant_buffer_root_parameter_indicies);
}
}
else if (texture)
{
p_handle_texture = reinterpret_cast<TextureHandleType*>(texture);
RMLUI_ASSERTMSG(p_handle_texture, "expected valid pointer!");
if (m_is_stencil_enabled)
{
if (m_is_stencil_equal)
{
UseProgram(ProgramId::Texture_Stencil_Equal);
}
else
{
UseProgram(ProgramId::Texture_Stencil_Always);
}
}
else
{
UseProgram(ProgramId::Texture_Stencil_Disabled);
}
if (p_handle_geometry->Get_ConstantBuffer() == nullptr)
{
p_constant_buffer = Get_ConstantBuffer(m_current_back_buffer_index);
}
else
{
p_constant_buffer = p_handle_geometry->Get_ConstantBuffer();
p_constant_buffer_root_parameter_indicies =
p_handle_geometry->Get_ConstantBufferRootParameterIndices(amount_of_constant_buffer_root_parameter_indicies);
}
SubmitTransformUniform(*p_constant_buffer, translation);
if (texture != TextureEnableWithoutBinding)
{
D3D12_GPU_DESCRIPTOR_HANDLE srv_handle;
srv_handle.ptr =
m_p_descriptor_heap_shaders->GetGPUDescriptorHandleForHeapStart().ptr + p_handle_texture->Get_Allocation_DescriptorHeap().offset;
m_p_command_graphics_list->SetGraphicsRootDescriptorTable(1, srv_handle);
}
}
else
{
if (m_current_clip_operation == -1)
{
if (m_is_stencil_enabled)
{
if (m_is_stencil_equal)
{
UseProgram(ProgramId::Color_Stencil_Equal);
}
else
{
UseProgram(ProgramId::Color_Stencil_Always);
}
}
else
{
UseProgram(ProgramId::Color_Stencil_Disabled);
}
}
else if (m_current_clip_operation == static_cast<int>(Rml::ClipMaskOperation::Intersect))
{
if (m_is_stencil_enabled)
{
UseProgram(ProgramId::Color_Stencil_Intersect);
}
}
else if (m_current_clip_operation == static_cast<int>(Rml::ClipMaskOperation::Set))
{
if (m_is_stencil_enabled)
{
UseProgram(ProgramId::Color_Stencil_Set);
}
}
else if (m_current_clip_operation == static_cast<int>(Rml::ClipMaskOperation::SetInverse))
{
if (m_is_stencil_enabled)
{
UseProgram(ProgramId::Color_Stencil_SetInverse);
}
}
else
{
RMLUI_ASSERT(!"not reached code point, something is missing or corrupted data"[0]);
}
// SubmitTransformUniform(p_handle_geometry->Get_ConstantBuffer(), translation);
if (p_handle_geometry->Get_ConstantBuffer() == nullptr)
{
p_constant_buffer = Get_ConstantBuffer(m_current_back_buffer_index);
}
else
{
p_constant_buffer = p_handle_geometry->Get_ConstantBuffer();
p_constant_buffer_root_parameter_indicies =
p_handle_geometry->Get_ConstantBufferRootParameterIndices(amount_of_constant_buffer_root_parameter_indicies);
}
SubmitTransformUniform(*p_constant_buffer, translation);
}
if (!m_is_scissor_was_set)
{
D3D12_RECT scissor;
scissor.left = 0;
scissor.top = 0;
scissor.right = m_width;
scissor.bottom = m_height;
m_p_command_graphics_list->RSSetScissorRects(1, &scissor);
}
if (p_constant_buffer)
{
auto* p_dx_constant_buffer = m_manager_buffer.Get_BufferByIndex(p_constant_buffer->m_alloc_info.buffer_index);
RMLUI_ASSERTMSG(p_dx_constant_buffer, "must be valid!");
if (p_dx_constant_buffer)
{
auto* p_dx_resource = p_dx_constant_buffer->GetResource();
RMLUI_ASSERTMSG(p_dx_resource, "must be valid!");
RMLUI_ASSERTMSG(p_constant_buffer_root_parameter_indicies,
"you forgot to update the RPI in CompiledGeometry do that only using active_program_id field!");
RMLUI_ASSERTMSG(amount_of_constant_buffer_root_parameter_indicies <= MaxConstantBuffersPerShader &&
amount_of_constant_buffer_root_parameter_indicies,
"you use too big shaders for UI but honestly you have to change limits and change this field "
"_kRenderBackend_MaxConstantBuffersPerShader for desired amount otherwise value of amount is invalid aka equals to 0 that doesn't "
"make any sense if you have valid p_constant_buffer_root_parameter_indicies pointer");
if (p_dx_resource)
{
D3D12_GPU_VIRTUAL_ADDRESS one_shared_cbv_for_different_shaders_address =
p_dx_resource->GetGPUVirtualAddress() + p_constant_buffer->m_alloc_info.offset;
for (uint8_t i = 0; i < amount_of_constant_buffer_root_parameter_indicies; ++i)
{
uint32_t index = p_constant_buffer_root_parameter_indicies[i];
m_p_command_graphics_list->SetGraphicsRootConstantBufferView(index, one_shared_cbv_for_different_shaders_address);
}
}
}
}
auto* p_dx_buffer_vertex = m_manager_buffer.Get_BufferByIndex(p_handle_geometry->Get_InfoVertex().buffer_index);
RMLUI_ASSERTMSG(p_dx_buffer_vertex, "must be valid!");
m_p_command_graphics_list->IASetPrimitiveTopology(D3D12_PRIMITIVE_TOPOLOGY::D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
if (p_dx_buffer_vertex)
{
auto* p_dx_resource = p_dx_buffer_vertex->GetResource();
RMLUI_ASSERTMSG(p_dx_resource, "must be valid!");
if (p_dx_resource)
{
D3D12_VERTEX_BUFFER_VIEW view_vertex_buffer = {};
view_vertex_buffer.BufferLocation = p_dx_resource->GetGPUVirtualAddress() + p_handle_geometry->Get_InfoVertex().offset;
view_vertex_buffer.StrideInBytes = sizeof(Rml::Vertex);
view_vertex_buffer.SizeInBytes = static_cast<UINT>(p_handle_geometry->Get_InfoVertex().size);
m_p_command_graphics_list->IASetVertexBuffers(0, 1, &view_vertex_buffer);
}
}
auto* p_dx_buffer_index = m_manager_buffer.Get_BufferByIndex(p_handle_geometry->Get_InfoIndex().buffer_index);
RMLUI_ASSERTMSG(p_dx_buffer_index, "must be valid!");
if (p_dx_buffer_index)
{
auto* p_dx_resource = p_dx_buffer_index->GetResource();
RMLUI_ASSERTMSG(p_dx_resource, "must be valid!");
if (p_dx_resource)
{
D3D12_INDEX_BUFFER_VIEW view_index_buffer = {};
view_index_buffer.BufferLocation = p_dx_resource->GetGPUVirtualAddress() + p_handle_geometry->Get_InfoIndex().offset;
view_index_buffer.Format = DXGI_FORMAT::DXGI_FORMAT_R32_UINT;
view_index_buffer.SizeInBytes = static_cast<UINT>(p_handle_geometry->Get_InfoIndex().size);
m_p_command_graphics_list->IASetIndexBuffer(&view_index_buffer);
}
}
m_p_command_graphics_list->DrawIndexedInstanced(p_handle_geometry->Get_NumIndices(), 1, 0, 0, 0);
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
Rml::CompiledGeometryHandle RenderInterface_DX12::CompileGeometry(Rml::Span<const Rml::Vertex> vertices, Rml::Span<const int> indices)
{
RMLUI_ZoneScopedN("DirectX 12 - CompileGeometry");
GeometryHandleType* p_handle = new GeometryHandleType();
if (p_handle)
{
m_manager_buffer.Alloc_Vertex(vertices.data(), static_cast<int>(vertices.size()), sizeof(Rml::Vertex), p_handle);
m_manager_buffer.Alloc_Index(indices.data(), static_cast<int>(indices.size()), sizeof(int), p_handle);
p_handle->Set_HistoryBackBufferFrameIndex(m_current_back_buffer_index);
}
return reinterpret_cast<Rml::CompiledGeometryHandle>(p_handle);
}
void RenderInterface_DX12::ReleaseGeometry(Rml::CompiledGeometryHandle geometry)
{
RMLUI_ZoneScopedN("DirectX 12 - ReleaseGeometry");
GeometryHandleType* p_handle = reinterpret_cast<GeometryHandleType*>(geometry);
m_pending_for_deletion_geometry.push_back(p_handle);
}
void RenderInterface_DX12::EnableScissorRegion(bool enable)
{
RMLUI_ZoneScopedN("DirectX 12 - EnableScissorRegion");
if (!enable)
{
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "EnableScissorRegion");
SetScissor(Rml::Rectanglei::MakeInvalid(), false);
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
}
void RenderInterface_DX12::SetScissorRegion(Rml::Rectanglei region)
{
RMLUI_ZoneScopedN("DirectX 12 - SetScissorRegion");
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "SetScissorRegion");
SetScissor(region);
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
void RenderInterface_DX12::EnableClipMask(bool enable)
{
RMLUI_ZoneScopedN("DirectX 12 - EnableClipMask");
m_is_stencil_enabled = enable;
if (enable)
{
RMLUI_ASSERTMSG(m_p_command_graphics_list, "must be valid!");
if (m_p_command_graphics_list)
{
m_p_command_graphics_list->OMSetStencilRef(m_stencil_ref_value);
}
}
else
{
RMLUI_ASSERTMSG(m_p_command_graphics_list, "mmust be valid!");
if (m_p_command_graphics_list)
{
m_p_command_graphics_list->OMSetStencilRef(0);
}
}
}
void RenderInterface_DX12::RenderToClipMask(Rml::ClipMaskOperation mask_operation, Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation)
{
RMLUI_ZoneScopedN("DirectX 12 - RenderToClipMask");
RMLUI_ASSERTMSG(m_is_stencil_enabled, "must be enabled!");
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "RenderToClipMask");
const bool clear_stencil = (mask_operation == Rml::ClipMaskOperation::Set || mask_operation == Rml::ClipMaskOperation::SetInverse);
if (clear_stencil)
{
Rml::LayerHandle layer_handle = m_manager_render_layer.GetTopLayerHandle();
const Gfx::FramebufferData& framebuffer = m_manager_render_layer.GetLayer(layer_handle);
m_p_command_graphics_list->ClearDepthStencilView(framebuffer.Get_SharedDepthStencilTexture()->Get_DescriptorResourceView(),
D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);
}
switch (mask_operation)
{
case Rml::ClipMaskOperation::Set:
{
m_current_clip_operation = static_cast<int>(Rml::ClipMaskOperation::Set);
m_stencil_ref_value = 1;
break;
}
case Rml::ClipMaskOperation::SetInverse:
{
m_current_clip_operation = static_cast<int>(Rml::ClipMaskOperation::SetInverse);
m_stencil_ref_value = 0;
break;
}
case Rml::ClipMaskOperation::Intersect:
{
m_current_clip_operation = static_cast<int>(Rml::ClipMaskOperation::Intersect);
m_stencil_ref_value += 1;
break;
}
}
m_p_command_graphics_list->OMSetStencilRef(1);
RenderGeometry(geometry, translation, {});
m_is_stencil_equal = true;
m_current_clip_operation = -1;
m_p_command_graphics_list->OMSetStencilRef(m_stencil_ref_value);
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
// Set to byte packing, or the compiler will expand our struct, which means it won't read correctly from file
#pragma pack(1)
struct TGAHeader {
char idLength;
char colourMapType;
char dataType;
short int colourMapOrigin;
short int colourMapLength;
char colourMapDepth;
short int xOrigin;
short int yOrigin;
short int width;
short int height;
char bitsPerPixel;
char imageDescriptor;
};
// Restore packing
#pragma pack()
static int nGlobalID{};
Rml::TextureHandle RenderInterface_DX12::LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source)
{
RMLUI_ZoneScopedN("DirectX 12 - LoadTexture");
Rml::FileInterface* file_interface = Rml::GetFileInterface();
Rml::FileHandle file_handle = file_interface->Open(source);
if (!file_handle)
{
return false;
}
file_interface->Seek(file_handle, 0, SEEK_END);
size_t buffer_size = file_interface->Tell(file_handle);
file_interface->Seek(file_handle, 0, SEEK_SET);
if (buffer_size <= sizeof(TGAHeader))
{
Rml::Log::Message(Rml::Log::LT_ERROR, "Texture file size is smaller than TGAHeader, file is not a valid TGA image.");
file_interface->Close(file_handle);
return false;
}
using Rml::byte;
Rml::UniquePtr<byte[]> buffer(new byte[buffer_size]);
file_interface->Read(buffer.get(), buffer_size, file_handle);
file_interface->Close(file_handle);
TGAHeader header;
memcpy(&header, buffer.get(), sizeof(TGAHeader));
int color_mode = header.bitsPerPixel / 8;
const size_t image_size = header.width * header.height * 4; // We always make 32bit textures
if (header.dataType != 2)
{
Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24/32bit uncompressed TGAs are supported.");
return false;
}
// Ensure we have at least 3 colors
if (color_mode < 3)
{
Rml::Log::Message(Rml::Log::LT_ERROR, "Only 24 and 32bit textures are supported.");
return false;
}
const byte* image_src = buffer.get() + sizeof(TGAHeader);
Rml::UniquePtr<byte[]> image_dest_buffer(new byte[image_size]);
byte* image_dest = image_dest_buffer.get();
// Targa is BGR, swap to RGB, flip Y axis, and convert to premultiplied alpha.
for (long y = 0; y < header.height; y++)
{
long read_index = y * header.width * color_mode;
long write_index = ((header.imageDescriptor & 32) != 0) ? read_index : (header.height - y - 1) * header.width * 4;
for (long x = 0; x < header.width; x++)
{
image_dest[write_index] = image_src[read_index + 2];
image_dest[write_index + 1] = image_src[read_index + 1];
image_dest[write_index + 2] = image_src[read_index];
if (color_mode == 4)
{
const byte alpha = image_src[read_index + 3];
for (size_t j = 0; j < 3; j++)
image_dest[write_index + j] = byte((image_dest[write_index + j] * alpha) / 255);
image_dest[write_index + 3] = alpha;
}
else
image_dest[write_index + 3] = 255;
write_index += 4;
read_index += color_mode;
}
}
texture_dimensions.x = header.width;
texture_dimensions.y = header.height;
return GenerateTexture({image_dest, image_size}, texture_dimensions);
}
Rml::TextureHandle RenderInterface_DX12::GenerateTexture(Rml::Span<const Rml::byte> source_data, Rml::Vector2i source_dimensions)
{
RMLUI_ZoneScopedN("DirectX 12 - GenerateTexture");
// RMLUI_ASSERTMSG(source_data.data(), "must be valid source");
RMLUI_ASSERTMSG(m_p_allocator, "backend requires initialized allocator, but it is not initialized");
int width = source_dimensions.x;
int height = source_dimensions.y;
RMLUI_ASSERTMSG(width > 0, "width is less or equal to 0");
RMLUI_ASSERTMSG(height > 0, "height is less or equal to 0");
D3D12_RESOURCE_DESC desc_texture = {};
desc_texture.MipLevels = 1;
desc_texture.DepthOrArraySize = 1;
desc_texture.Format = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
desc_texture.Width = width;
desc_texture.Height = height;
desc_texture.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
desc_texture.SampleDesc.Count = 1;
desc_texture.SampleDesc.Quality = 0;
TextureHandleType* p_resource = new TextureHandleType();
#ifdef RMLUI_DX_DEBUG
if (p_resource)
{
p_resource->Set_ResourceName(std::to_string(nGlobalID));
++nGlobalID;
}
#endif
m_manager_texture.Alloc_Texture(desc_texture, p_resource, source_data.data()
#ifdef RMLUI_DX_DEBUG
,
p_resource->Get_ResourceName()
#endif
);
return reinterpret_cast<Rml::TextureHandle>(p_resource);
}
void RenderInterface_DX12::ReleaseTexture(Rml::TextureHandle texture_handle)
{
RMLUI_ZoneScopedN("DirectX 12 - ReleaseTexture");
TextureHandleType* p_texture = reinterpret_cast<TextureHandleType*>(texture_handle);
// m_pending_for_deletion_textures.push_back(p_texture);
if (p_texture)
{
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::LT_DEBUG, "Destroyed texture: [%s]", p_texture->Get_ResourceName().c_str());
#endif
m_manager_texture.Free_Texture(p_texture);
delete p_texture;
}
}
void RenderInterface_DX12::SetTransform(const Rml::Matrix4f* transform)
{
RMLUI_ZoneScopedN("DirectX 12 - SetTransform");
m_constant_buffer_data_transform = (transform ? m_projection * (*transform) : m_projection);
}
RenderInterface_DX12::RenderLayerStack::RenderLayerStack() :
m_msaa_sample_count{1}, m_width{}, m_height{}, m_layers_size{}, m_p_manager_texture{}, m_p_manager_buffer{}, m_p_device{},
m_p_depth_stencil_for_layers{}
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::Constructor");
m_fb_postprocess.resize(4);
// in order to prevent calling dtor when doing push_back on m_fb_layers
// we need to reserve memory, like how much we do expect elements in array (vector)
// otherwise you will get validation assert in dtor of FramebufferData struct and
// that validation supposed to be for memory leaks or wrong resource handling (like you forgot to delete resource somehow)
// if you didn't get it check this: https://en.cppreference.com/w/cpp/container/vector/reserve
// otherwise if your default implementation requires more layers by default, thus we have a field at compile-time (or at runtime as dynamic
// extension) RMLUI_RENDER_BACKEND_OVERRIDE_FIELD_RESERVECOUNT_OF_RENDERSTACK_LAYERS
m_fb_layers.reserve(RMLUI_RENDER_BACKEND_FIELD_RESERVECOUNT_OF_RENDERSTACK_LAYERS);
m_p_depth_stencil_for_layers = Rml::MakeUnique<Gfx::FramebufferData>();
m_p_depth_stencil_for_layers->Set_RenderTarget(false);
}
RenderInterface_DX12::RenderLayerStack::~RenderLayerStack()
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::Destructor");
m_p_device = nullptr;
m_p_manager_buffer = nullptr;
m_p_manager_texture = nullptr;
m_p_depth_stencil_for_layers.reset();
}
void RenderInterface_DX12::RenderLayerStack::Initialize(RenderInterface_DX12* p_owner)
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::Initialize");
RMLUI_ASSERTMSG(p_owner, "you must pass a valid pointer of RenderInterface_DX12 instance");
if (p_owner)
{
RMLUI_ASSERTMSG(p_owner->Get_TextureManager().Is_Initialized(),
"early call! you must initialize texture memory manager before calling this method!");
RMLUI_ASSERTMSG(p_owner->Get_BufferManager().Is_Initialized(),
"early call! you must initialize buffer memory manager before calling this method!");
RMLUI_ASSERTMSG(p_owner->Get_Device(), "you must initialize DirectX 12 before calling this method! device is nullptr");
}
if (p_owner)
{
m_p_device = p_owner->Get_Device();
m_p_manager_buffer = &p_owner->Get_BufferManager();
m_p_manager_texture = &p_owner->Get_TextureManager();
m_msaa_sample_count = p_owner->Get_MSAASampleCount();
}
}
void RenderInterface_DX12::RenderLayerStack::Shutdown()
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::Shutdown");
DestroyFramebuffers();
}
Rml::LayerHandle RenderInterface_DX12::RenderLayerStack::PushLayer()
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::PushLayer");
RMLUI_ASSERTMSG(m_layers_size <= static_cast<int>(m_fb_layers.size()), "overflow of layers!");
RMLUI_ASSERTMSG(m_p_depth_stencil_for_layers, "must be valid!");
if (m_layers_size == static_cast<int>(m_fb_layers.size()))
{
if (m_p_depth_stencil_for_layers->Get_Texture() == nullptr)
{
CreateFramebuffer(m_p_depth_stencil_for_layers.get(), m_width, m_height, m_msaa_sample_count, true);
#ifdef RMLUI_DX_DEBUG
if (m_p_depth_stencil_for_layers->Get_Texture())
{
wchar_t framebuffer_name[64];
wsprintf(framebuffer_name, L"framebuffer (layer) shared depth-stencil");
int index_buffer = m_p_depth_stencil_for_layers->Get_Texture()->Get_Info().buffer_index;
RMLUI_ASSERTMSG(index_buffer == -1, "you can't allocate framebuffer as placed resource no sense!");
if (index_buffer == -1)
{
RMLUI_ASSERTMSG(m_p_depth_stencil_for_layers->Get_Texture()->Get_Resource(), "failed to allocate framebuffer!");
D3D12MA::Allocation* p_committed_resource =
static_cast<D3D12MA::Allocation*>(m_p_depth_stencil_for_layers->Get_Texture()->Get_Resource());
if (p_committed_resource)
{
RMLUI_ASSERTMSG(p_committed_resource->GetResource(), "failed to allocate for D3D12MA! (GetResource==nullptr)");
p_committed_resource->GetResource()->SetName(framebuffer_name);
}
}
}
#endif
}
m_fb_layers.push_back(Gfx::FramebufferData{});
auto* p_buffer = &m_fb_layers.back();
CreateFramebuffer(p_buffer, m_width, m_height, m_msaa_sample_count, false);
p_buffer->Set_ID(static_cast<int>(m_fb_layers.size() - 1));
#ifdef RMLUI_DX_DEBUG
wchar_t framebuffer_name[32];
wsprintf(framebuffer_name, L"framebuffer (layer): %d", m_layers_size);
int index_buffer = p_buffer->Get_Texture()->Get_Info().buffer_index;
RMLUI_ASSERTMSG(index_buffer == -1, "you can't allocate framebuffer as placed resource no sense!");
if (index_buffer == -1)
{
RMLUI_ASSERTMSG(p_buffer->Get_Texture()->Get_Resource(), "failed to allocate framebuffer!");
D3D12MA::Allocation* p_committed_resource = static_cast<D3D12MA::Allocation*>(p_buffer->Get_Texture()->Get_Resource());
if (p_committed_resource->GetResource())
{
RMLUI_ASSERTMSG(p_committed_resource->GetResource(), "failed to allocate for D3D12MA! (GetResource==nullptr)");
p_committed_resource->GetResource()->SetName(framebuffer_name);
}
}
#endif
p_buffer->Set_SharedDepthStencilTexture(m_p_depth_stencil_for_layers.get());
}
++m_layers_size;
return GetTopLayerHandle();
}
void RenderInterface_DX12::RenderLayerStack::PopLayer()
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::PopLayer");
RMLUI_ASSERTMSG(m_layers_size > 0, "calculations are wrong, debug your code please!");
m_layers_size -= 1;
}
const Gfx::FramebufferData& RenderInterface_DX12::RenderLayerStack::GetLayer(Rml::LayerHandle layer) const
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::GetLayer");
RMLUI_ASSERT(static_cast<size_t>(layer) < static_cast<size_t>(m_layers_size) &&
"overflow or not correct calculation or something is broken, debug the code!");
return m_fb_layers.at(static_cast<size_t>(layer));
}
const Gfx::FramebufferData& RenderInterface_DX12::RenderLayerStack::GetTopLayer() const
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::GetTopLayer");
RMLUI_ASSERTMSG(m_layers_size > 0, "early calling!");
return m_fb_layers[m_layers_size - 1];
}
const Gfx::FramebufferData& RenderInterface_DX12::RenderLayerStack::Get_SharedDepthStencil_Layers()
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::Get_SharedDepthStencil_Layers");
RMLUI_ASSERTMSG(m_p_depth_stencil_for_layers, "early calling!");
return *m_p_depth_stencil_for_layers;
}
Rml::LayerHandle RenderInterface_DX12::RenderLayerStack::GetTopLayerHandle() const
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::GetTopLayerHandle");
RMLUI_ASSERTMSG(m_layers_size > 0, "early calling or something is broken!");
return static_cast<Rml::LayerHandle>(m_layers_size - 1);
}
void RenderInterface_DX12::RenderLayerStack::SwapPostprocessPrimarySecondary()
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::SwapPostprocessPrimarySecondary");
std::swap(m_fb_postprocess[0], m_fb_postprocess[1]);
}
void RenderInterface_DX12::RenderLayerStack::BeginFrame(int width_new, int height_new)
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::BeginFrame");
RMLUI_ASSERTMSG(m_layers_size == 0, "something is wrong and you forgot to clear/delete something!");
if (m_width != width_new || m_height != height_new)
{
m_width = width_new;
m_height = height_new;
DestroyFramebuffers();
}
PushLayer();
}
void RenderInterface_DX12::RenderLayerStack::EndFrame()
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::EndFrame");
RMLUI_ASSERTMSG(m_layers_size == 1, "order is wrong or something is broken!");
PopLayer();
}
void RenderInterface_DX12::RenderLayerStack::DestroyFramebuffers()
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::DestroyFramebuffers");
RMLUI_ASSERTMSG(m_layers_size == 0, "Do not call this during frame rendering, that is, between BeginFrame() and EndFrame().");
RMLUI_ASSERTMSG(m_p_manager_texture, "you must initialize this manager or it is a early calling or it is a late calling, debug please!");
// deleting shared depth stencil
if (m_p_manager_texture)
{
if (m_p_depth_stencil_for_layers->Get_Texture())
{
m_p_manager_texture->Free_Texture(m_p_depth_stencil_for_layers.get());
}
}
for (auto& fb : m_fb_layers)
{
if (fb.Get_Texture())
{
DestroyFramebuffer(&fb);
}
}
m_fb_layers.clear();
for (auto& fb : m_fb_postprocess)
{
if (fb.Get_Texture())
{
DestroyFramebuffer(&fb);
}
}
}
const Gfx::FramebufferData& RenderInterface_DX12::RenderLayerStack::EnsureFramebufferPostprocess(int index)
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::EnsureFramebufferPostprocess");
RMLUI_ASSERTMSG(index < static_cast<int>(m_fb_postprocess.size()), "overflow or wrong calculation, debug the code!");
Gfx::FramebufferData& fb = m_fb_postprocess.at(index);
if (!fb.Get_Texture())
{
CreateFramebuffer(&fb, m_width, m_height, 1, false);
fb.Set_ID(index);
#ifdef RMLUI_DX_DEBUG
fb.m_is_allocated_on_stack = false;
if (fb.Get_Texture())
{
wchar_t framebuffer_name[32];
wsprintf(framebuffer_name, L"framebuffer (postprocess): %d", index);
int buffer_index = fb.Get_Texture()->Get_Info().buffer_index;
RMLUI_ASSERTMSG(buffer_index == -1, "you can't allocate framebuffer as placed resource no sense!");
if (buffer_index == -1)
{
RMLUI_ASSERTMSG(fb.Get_Texture()->Get_Resource(), "must be valid resource failed to allocate!");
D3D12MA::Allocation* p_alloc = static_cast<D3D12MA::Allocation*>(fb.Get_Texture()->Get_Resource());
if (p_alloc)
{
ID3D12Resource* p_resource = p_alloc->GetResource();
RMLUI_ASSERTMSG(p_resource, "failed to allocate for D3D12MA");
if (p_resource)
{
p_resource->SetName(framebuffer_name);
}
}
}
}
#endif
}
return fb;
}
void RenderInterface_DX12::RenderLayerStack::CreateFramebuffer(Gfx::FramebufferData* p_result, int width, int height, int sample_count,
bool is_depth_stencil)
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::CreateFramebuffer");
RMLUI_ASSERTMSG(p_result, "you must pass a valid pointer!");
RMLUI_ASSERTMSG(sample_count > 0, "you must pass a valid value! it must be positive");
RMLUI_ASSERTMSG(width > 0, "pass a valid width!");
RMLUI_ASSERTMSG(height > 0, "pass a valid height");
RMLUI_ASSERTMSG(m_p_manager_texture, "you must register manager texture befor calling this method (manager texture is nullptr)");
if (m_p_manager_texture)
{
D3D12_RESOURCE_DESC desc_texture = {};
DXGI_FORMAT format{};
if (is_depth_stencil)
format = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT;
else
format = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
desc_texture.MipLevels = 1;
desc_texture.DepthOrArraySize = 1;
desc_texture.Format = format;
desc_texture.Width = static_cast<UINT64>(width);
desc_texture.Height = static_cast<UINT64>(height);
desc_texture.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
desc_texture.SampleDesc.Count = sample_count;
desc_texture.SampleDesc.Quality = 0;
TextureHandleType* p_resource = new TextureHandleType();
RMLUI_ASSERTMSG(p_resource, "[OS][ERROR] not enough memory for allocation!");
#ifdef RMLUI_DX_DEBUG
if (p_resource)
{
const char* pWhatTypeOfTexture{};
constexpr const char* kHardcodedRenderTargetName = "[RT] ";
constexpr const char* kHardcodedDepthStencilName = "[DS] ";
if (is_depth_stencil)
pWhatTypeOfTexture = kHardcodedDepthStencilName;
else
pWhatTypeOfTexture = kHardcodedRenderTargetName;
Rml::String msaa_info;
if (sample_count > 1)
msaa_info = " | MSAA[" + std::to_string(sample_count) + "]";
p_resource->Set_ResourceName(pWhatTypeOfTexture + std::string("Render2Texture[") + std::to_string(m_layers_size) + "]" + msaa_info);
}
#endif
p_result->Set_Width(width);
p_result->Set_Height(height);
p_result->Set_Texture(p_resource);
D3D12_RESOURCE_FLAGS flags{};
if (is_depth_stencil)
flags = D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
else
flags = D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
D3D12_RESOURCE_STATES states{};
if (is_depth_stencil)
states = D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_DEPTH_WRITE;
else
states = D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_RENDER_TARGET;
#ifdef RMLUI_DX_DEBUG
const char* pWhatTypeOfTextureForAllocationName{};
constexpr const char* kHardcodedNameForDepthStencil = "Render2Texture|Depth-Stencil";
constexpr const char* kHardcodedNameForRenderTarget = "Render2Texture|Render-Target";
if (is_depth_stencil)
pWhatTypeOfTextureForAllocationName = kHardcodedNameForDepthStencil;
else
pWhatTypeOfTextureForAllocationName = kHardcodedNameForRenderTarget;
#endif
m_p_manager_texture->Alloc_Texture(desc_texture, p_result, flags, states
#ifdef RMLUI_DX_DEBUG
,
pWhatTypeOfTextureForAllocationName
#endif
);
}
}
void RenderInterface_DX12::RenderLayerStack::DestroyFramebuffer(Gfx::FramebufferData* p_data)
{
RMLUI_ZoneScopedN("DirectX 12 - RenderLayerStack::DestroyFramebuffer");
RMLUI_ASSERTMSG(p_data, "you must pass a valid data");
RMLUI_ASSERTMSG(m_p_manager_texture, "early/late calling?");
if (p_data)
{
m_p_manager_texture->Free_Texture(p_data);
p_data->Set_Width(-1);
p_data->Set_Height(-1);
}
}
Rml::LayerHandle RenderInterface_DX12::PushLayer()
{
RMLUI_ZoneScopedN("DirectX 12 - PushLayer");
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "PushLayer");
const Rml::LayerHandle layer_handle = m_manager_render_layer.PushLayer();
const auto& framebuffer = m_manager_render_layer.GetLayer(layer_handle);
RMLUI_ASSERTMSG(framebuffer.Get_SharedDepthStencilTexture(), "you have to set shared depth stencil texture for layer!");
const auto& shared_depthstencil = *framebuffer.Get_SharedDepthStencilTexture();
m_p_command_graphics_list->OMSetRenderTargets(1, &framebuffer.Get_DescriptorResourceView(), FALSE,
&shared_depthstencil.Get_DescriptorResourceView());
constexpr FLOAT clear_color[] = {0.0f, 0.0f, 0.0f, 0.0f};
m_p_command_graphics_list->ClearRenderTargetView(framebuffer.Get_DescriptorResourceView(), clear_color, 0, nullptr);
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
return layer_handle;
}
void RenderInterface_DX12::BlitLayerToPostprocessPrimary(Rml::LayerHandle layer_id)
{
RMLUI_ZoneScopedN("DirectX 12 - BlitLayerToPostprocessPrimary");
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "BlitLayerToPostprocessPrimary");
const Gfx::FramebufferData& source_framebuffer = m_manager_render_layer.GetLayer(layer_id);
const Gfx::FramebufferData& destination_framebuffer = m_manager_render_layer.GetPostprocessPrimary();
RMLUI_ASSERTMSG(source_framebuffer.Get_Texture(), "texture must be presented when you call this method!");
RMLUI_ASSERTMSG(destination_framebuffer.Get_Texture(), "texture must be presented when you call this method!");
RMLUI_ASSERT(source_framebuffer.Get_Texture()->Get_Info().buffer_index == -1 &&
"expected that this texture was allocated as committed since it is 'framebuffer' ");
RMLUI_ASSERT(destination_framebuffer.Get_Texture()->Get_Info().buffer_index == -1 &&
"expected that this texture was allocated as committed since it is 'framebuffer' ");
RMLUI_ASSERTMSG(source_framebuffer.Get_Texture()->Get_Resource(), "texture must contain allocated and valid resource!");
RMLUI_ASSERTMSG(destination_framebuffer.Get_Texture()->Get_Resource(), "texture must contain allocated and valid resource!");
ID3D12Resource* p_src = static_cast<D3D12MA::Allocation*>(source_framebuffer.Get_Texture()->Get_Resource())->GetResource();
ID3D12Resource* p_dst = static_cast<D3D12MA::Allocation*>(destination_framebuffer.Get_Texture()->Get_Resource())->GetResource();
RMLUI_ASSERTMSG(p_src, "must be valid && allocated");
RMLUI_ASSERTMSG(p_dst, "must be valid && allocated");
RMLUI_ASSERTMSG(m_p_command_graphics_list, "must be initialized before calling this method");
if (!m_p_command_graphics_list)
return;
RMLUI_ASSERTMSG(p_src->GetDesc().Width == p_dst->GetDesc().Width, "must be same otherwise use blitframebuffer");
RMLUI_ASSERTMSG(p_src->GetDesc().Height == p_dst->GetDesc().Height, "must be same otherwise use blitframebuffer");
#if RMLUI_RENDER_BACKEND_FIELD_MSAA_SAMPLE_COUNT > 1
D3D12_RESOURCE_BARRIER barriers[2];
barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[0].Transition.pResource = p_src;
barriers[0].Transition.Subresource = 0;
barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RESOLVE_SOURCE;
barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barriers[1].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[1].Transition.pResource = p_dst;
barriers[1].Transition.Subresource = 0;
barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RESOLVE_DEST;
barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
m_p_command_graphics_list->ResourceBarrier(2, barriers);
m_p_command_graphics_list->ResolveSubresource(p_dst, 0, p_src, 0, RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT);
barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[0].Transition.pResource = p_dst;
barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RESOLVE_DEST;
barriers[0].Transition.Subresource = 0;
barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[1].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[1].Transition.pResource = p_src;
barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RESOLVE_SOURCE;
barriers[1].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(2, barriers);
#else
D3D12_RESOURCE_BARRIER barriers[2];
barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[0].Transition.pResource = p_src;
barriers[0].Transition.Subresource = 0;
barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barriers[1].Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[1].Transition.pResource = p_dst;
barriers[1].Transition.Subresource = 0;
barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
m_p_command_graphics_list->ResourceBarrier(2, barriers);
m_p_command_graphics_list->CopyResource(p_dst, p_src);
barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[0].Transition.pResource = p_dst;
barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
barriers[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
barriers[0].Transition.Subresource = 0;
barriers[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[1].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[1].Transition.pResource = p_src;
barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
barriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
barriers[1].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(2, barriers);
#endif
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
static void SigmaToParameters(const float desired_sigma, int& out_pass_level, float& out_sigma)
{
constexpr int max_num_passes = 10;
static_assert(max_num_passes < 31, "");
constexpr float max_single_pass_sigma = 3.0f;
out_pass_level = Rml::Math::Clamp(Rml::Math::Log2(int(desired_sigma * (2.f / max_single_pass_sigma))), 0, max_num_passes);
out_sigma = Rml::Math::Clamp(desired_sigma / float(1 << out_pass_level), 0.0f, max_single_pass_sigma);
}
void RenderInterface_DX12::DrawFullscreenQuad(ConstantBufferType* p_override_constant_buffer)
{
RMLUI_ZoneScopedN("DirectX 12 - DrawFullscreenQuad()");
RMLUI_ASSERTMSG(m_p_command_graphics_list, "must be valid!");
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "DrawFullscreenQuad");
// actually rml doesn't support anything for by passing custom data as additional argument for RenderGeometry method so
// some kind of variant for resolving a such situation
OverrideConstantBufferOfGeometry(m_precompiled_fullscreen_quad_geometry, p_override_constant_buffer);
RenderGeometry(m_precompiled_fullscreen_quad_geometry, {}, TexturePostprocess);
if (p_override_constant_buffer)
{
GeometryHandleType* p_geometry = reinterpret_cast<GeometryHandleType*>(m_precompiled_fullscreen_quad_geometry);
p_geometry->Reset_ConstantBuffer();
}
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
void RenderInterface_DX12::DrawFullscreenQuad(Rml::Vector2f uv_offset, Rml::Vector2f uv_scaling, ConstantBufferType* p_override_constant_buffer)
{
RMLUI_ZoneScopedN("DirectX 12 - DrawfullscreenQuad(uv_offset,uv_scaling)");
RMLUI_ASSERTMSG(m_p_command_graphics_list, "must be valid!");
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "DrawFullscreenQuad(uv_offset,uv_scaling)");
Rml::Mesh mesh;
Rml::MeshUtilities::GenerateQuad(mesh, Rml::Vector2f(-1), Rml::Vector2f(2), {});
if (uv_offset != Rml::Vector2f() || uv_scaling != Rml::Vector2f(1.f))
{
for (Rml::Vertex& vertex : mesh.vertices)
vertex.tex_coord = (vertex.tex_coord * uv_scaling) + uv_offset;
}
Rml::CompiledGeometryHandle geometry = CompileGeometry(mesh.vertices, mesh.indices);
OverrideConstantBufferOfGeometry(geometry, p_override_constant_buffer);
RenderGeometry(geometry, {}, TexturePostprocess);
ReleaseGeometry(geometry);
if (p_override_constant_buffer)
{
GeometryHandleType* p_geometry = reinterpret_cast<GeometryHandleType*>(geometry);
p_geometry->Reset_ConstantBuffer();
}
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
void RenderInterface_DX12::BindTexture(TextureHandleType* p_texture, UINT root_parameter_index)
{
RMLUI_ASSERTMSG(p_texture, "you have to pass a valid pointer!");
RMLUI_ASSERTMSG(m_p_command_graphics_list, "early calling must be initialized before calling this method!");
RMLUI_ASSERTMSG(m_p_descriptor_heap_shaders, "early calling must be initialzed before calling this method!");
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "BindTexture");
if (m_p_command_graphics_list)
{
if (p_texture)
{
if (m_p_descriptor_heap_shaders)
{
D3D12_GPU_DESCRIPTOR_HANDLE srv_handle;
srv_handle.ptr =
m_p_descriptor_heap_shaders->GetGPUDescriptorHandleForHeapStart().ptr + p_texture->Get_Allocation_DescriptorHeap().offset;
m_p_command_graphics_list->SetGraphicsRootDescriptorTable(root_parameter_index, srv_handle);
}
}
}
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
void RenderInterface_DX12::BindRenderTarget(const Gfx::FramebufferData& framebuffer, bool depth_included)
{
RMLUI_ASSERTMSG(m_p_command_graphics_list, "early calling must be initialized before calling this method!");
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "BindRenderTarget");
if (m_p_command_graphics_list)
{
const D3D12_CPU_DESCRIPTOR_HANDLE* p_dsv_handle = nullptr;
if (depth_included)
{
RMLUI_ASSERTMSG(framebuffer.Get_SharedDepthStencilTexture(), "expects that it was allocated and set to render target textures!");
p_dsv_handle = &framebuffer.Get_SharedDepthStencilTexture()->Get_DescriptorResourceView();
}
const D3D12_CPU_DESCRIPTOR_HANDLE* p_rtv_handle = &framebuffer.Get_DescriptorResourceView();
m_p_command_graphics_list->OMSetRenderTargets(1, p_rtv_handle, FALSE, p_dsv_handle);
}
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
void RenderInterface_DX12::OverrideConstantBufferOfGeometry(Rml::CompiledGeometryHandle geometry, ConstantBufferType* p_override_constant_buffer)
{
if (p_override_constant_buffer)
{
GeometryHandleType* p_geometry = reinterpret_cast<GeometryHandleType*>(geometry);
p_geometry->Set_ConstantBuffer(p_override_constant_buffer);
switch (m_active_program_id)
{
case ProgramId::Blur:
{
// this for vertex shader
p_geometry->Add_ConstantBufferRootParameterIndices(1);
// this for pixel shader
p_geometry->Add_ConstantBufferRootParameterIndices(2);
// so we can't combine and use one root parameters for pixel and vertex shader even if we use same CBV so yeah...
break;
}
case ProgramId::DropShadow:
{
// for pixel shader
p_geometry->Add_ConstantBufferRootParameterIndices(1);
break;
}
case ProgramId::ColorMatrix:
{
// for pixel shader
p_geometry->Add_ConstantBufferRootParameterIndices(1);
break;
}
default:
{
RMLUI_ASSERT(!"FATAL YOU FORGOT TO REGISTER CONSTANT BUFFER OVERRIDE SITUATION!");
break;
}
}
}
}
unsigned char RenderInterface_DX12::GetMSAASupportedSampleCount(unsigned char max_samples)
{
RMLUI_ASSERTMSG(m_p_device, "early calling this must be initialized before calling!");
if (m_p_device)
{
#ifdef RMLUI_DX_DEBUG
bool sample_counts[64]{};
#endif
D3D12_FEATURE_DATA_MULTISAMPLE_QUALITY_LEVELS desc{};
desc.SampleCount = static_cast<UINT>(max_samples);
desc.Format = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
desc.Flags = D3D12_MULTISAMPLE_QUALITY_LEVELS_FLAG_NONE;
unsigned char max_result = 1;
while (desc.SampleCount > 1)
{
HRESULT result = m_p_device->CheckFeatureSupport(D3D12_FEATURE_MULTISAMPLE_QUALITY_LEVELS, &desc, sizeof(desc));
if (SUCCEEDED(result))
{
#ifdef RMLUI_DX_DEBUG
sample_counts[desc.SampleCount - 1] = true;
#endif
if (desc.SampleCount > max_result)
{
RMLUI_ASSERT(desc.SampleCount <= std::numeric_limits<unsigned char>::max() &&
"report to developers, it is serious but in reality > 32 is not realistic at all");
max_result = static_cast<unsigned char>(desc.SampleCount);
}
#ifndef RMLUI_DX_DEBUG
// on debug we accumulate information about all possible (for debug purposes) max sample sample_counts[0] = false always be false due
// to sample count checking in while loop
break;
#endif
}
--desc.SampleCount;
}
return max_result;
}
return 1;
}
void RenderInterface_DX12::BlitFramebuffer(const Gfx::FramebufferData& source, const Gfx::FramebufferData& dest, int srcX0, int srcY0, int srcX1,
int srcY1, int dstX0, int dstY0, int dstX1, int dstY1)
{
RMLUI_ZoneScopedN("DirectX 12 - BlitFramebuffer");
RMLUI_ASSERTMSG(m_p_command_graphics_list, "must be initialized renderer before calling this method!");
if (!m_p_command_graphics_list)
return;
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "BlitFramebuffer");
int src_width = srcX1 - srcX0;
int src_height = srcY1 - srcY0;
int dest_width = dstX1 - dstX0;
int dest_height = dstY1 - dstY0;
bool is_flipped = src_width < 0 || src_height < 0 || dest_width < 0 || dest_height < 0;
bool is_stretched = src_width != dest_width || src_height != dest_height;
bool is_full_copy = src_width == dest_width && src_height == dest_height && srcX0 == 0 && srcY0 == 0 && dstX0 == 0 && dstY0 == 0;
if (is_flipped || is_stretched || !is_full_copy)
{
// Full draw call. Slow path because no equivalent in DX12
#ifdef RMLUI_DX_DEBUG
TextureHandleType* p_texture_source = source.Get_Texture();
TextureHandleType* p_texture_destination = dest.Get_Texture();
RMLUI_ASSERTMSG(p_texture_source, "must be valid pointer!");
RMLUI_ASSERTMSG(p_texture_destination, "must be valid pointer!");
RMLUI_ASSERT(p_texture_source->Get_Info().buffer_index == -1 &&
"expected that a such texture was allocated as committed (e.g. not placed resource), so in that case you can't cast to "
"D3D12MA::Allocation");
RMLUI_ASSERT(p_texture_destination->Get_Info().buffer_index == -1 &&
"expected that a such texture was allocated as committed (e.g. not placed resource) so in that case you can't cast to "
"D3D12MA::Allocation");
RMLUI_ASSERTMSG(p_texture_source->Get_Resource(), "must contain a valid resource");
RMLUI_ASSERTMSG(p_texture_destination->Get_Resource(), "must contain a valid resource");
D3D12MA::Allocation* p_allocation_source = static_cast<D3D12MA::Allocation*>(p_texture_source->Get_Resource());
D3D12MA::Allocation* p_allocation_destination = static_cast<D3D12MA::Allocation*>(p_texture_destination->Get_Resource());
RMLUI_ASSERTMSG(p_allocation_source->GetResource(), "allocation must contain a valid pointer to resource! something is broken");
RMLUI_ASSERTMSG(p_allocation_destination->GetResource(), "allocation must contain a valid pointer to resource! something is broken");
ID3D12Resource* p_resource_src = p_allocation_source->GetResource();
ID3D12Resource* p_resource_dst = p_allocation_destination->GetResource();
const D3D12_RESOURCE_DESC& desc_source = p_resource_src->GetDesc();
const D3D12_RESOURCE_DESC& desc_dest = p_resource_dst->GetDesc();
RMLUI_ASSERTMSG(desc_source.SampleDesc.Count <= 1, "expected only NO MSAA texture (source)");
RMLUI_ASSERTMSG(desc_dest.SampleDesc.Count <= 1, "expected only NO MSAA texture (dest)");
#endif
D3D12_VIEWPORT vp{};
vp.TopLeftX = 0;
vp.TopLeftY = 0;
vp.Width = (float)dest.Get_Width();
vp.Height = (float)dest.Get_Height();
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
m_p_command_graphics_list->RSSetViewports(1, &vp);
UseProgram(ProgramId::Passthrough_NoBlendAndNoMSAA);
BindRenderTarget(dest, false);
BindTexture(source.Get_Texture());
float uv_x_min = float(srcX0) / float(source.Get_Width()); // Map to 0
float uv_y_max = float(srcY0) / float(source.Get_Height()); // Map to 0
float uv_x_max = float(srcX1) / float(source.Get_Width()); // Map to +1
float uv_y_min = float(srcY1) / float(source.Get_Height()); // Map to +1
float pos_x_min = (dstX0 / float(dest.Get_Width())) * 2.0f - 1.0f;
float pos_y_min = ((dest.Get_Height() - dstY0 - dest_height) / float(dest.Get_Height())) * 2.0f - 1.0f;
float pos_x_max = ((dstX0 + dest_width) / float(dest.Get_Width())) * 2.0f - 1.0f;
float pos_y_max = ((dest.Get_Height() - dstY0) / float(dest.Get_Height())) * 2.0f - 1.0f;
Rml::Array<Rml::Vertex, 4> vertices;
constexpr int indices[6]{0, 3, 1, 1, 3, 2};
vertices[0].position.x = pos_x_min;
vertices[0].position.y = pos_y_min;
vertices[0].tex_coord.x = uv_x_min;
vertices[0].tex_coord.y = uv_y_min;
vertices[1].position.x = pos_x_max;
vertices[1].position.y = pos_y_min;
vertices[1].tex_coord.x = uv_x_max;
vertices[1].tex_coord.y = uv_y_min;
vertices[2].position.x = pos_x_max;
vertices[2].position.y = pos_y_max;
vertices[2].tex_coord.x = uv_x_max;
vertices[2].tex_coord.y = uv_y_max;
vertices[3].position.x = pos_x_min;
vertices[3].position.y = pos_y_max;
vertices[3].tex_coord.x = uv_x_min;
vertices[3].tex_coord.y = uv_y_max;
const Rml::CompiledGeometryHandle geometry =
CompileGeometry({&vertices[0], sizeof(vertices) / sizeof(vertices[0])}, {&indices[0], sizeof(indices) / sizeof(indices[0])});
RenderGeometry(geometry, {}, TexturePostprocess);
ReleaseGeometry(geometry);
}
else
{
RMLUI_ASSERT(!"use resolvesubresource manually! don't call this method for handling resolvesubresource!!!");
}
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
void RenderInterface_DX12::ValidateTextureAllocationNotAsPlaced(const Gfx::FramebufferData& source)
{
#ifdef RMLUI_DX_DEBUG
TextureHandleType* p_texture_source = source.Get_Texture();
RMLUI_ASSERTMSG(p_texture_source, "must be valid pointer!");
RMLUI_ASSERT(p_texture_source->Get_Info().buffer_index == -1 &&
"expected that a such texture was allocated as committed (e.g. not placed resource), so in that case you can't cast to "
"D3D12MA::Allocation");
RMLUI_ASSERTMSG(p_texture_source->Get_Resource(), "must contain a valid resource");
D3D12MA::Allocation* p_allocation_source = static_cast<D3D12MA::Allocation*>(p_texture_source->Get_Resource());
RMLUI_ASSERTMSG(p_allocation_source->GetResource(), "allocation must contain a valid pointer to resource! something is broken");
#else
(void)source;
#endif
}
ID3D12Resource* RenderInterface_DX12::GetResourceFromFramebufferData(const Gfx::FramebufferData& data)
{
RMLUI_ZoneScopedN("DirectX 12 - GetResourceFromFramebufferData");
ID3D12Resource* p_result{};
TextureHandleType* p_texture_source = data.Get_Texture();
if (p_texture_source == nullptr)
return p_result;
if (p_texture_source->Get_Info().buffer_index == -1)
{
// committed
if (p_texture_source->Get_Resource() == nullptr)
return p_result;
D3D12MA::Allocation* p_allocation_source = static_cast<D3D12MA::Allocation*>(p_texture_source->Get_Resource());
if (p_allocation_source == nullptr)
return p_result;
if (p_allocation_source->GetResource() == nullptr)
return p_result;
p_result = p_allocation_source->GetResource();
}
else
{
// placed
if (p_texture_source->Get_Resource() == nullptr)
return p_result;
p_result = static_cast<ID3D12Resource*>(p_texture_source->Get_Resource());
}
return p_result;
}
void RenderInterface_DX12::RenderBlur(float sigma, const Gfx::FramebufferData& source_destination, const Gfx::FramebufferData& temp,
const Rml::Rectanglei window_flipped)
{
RMLUI_ZoneScopedN("DirectX 12 - RenderBlur");
RMLUI_ASSERTMSG(&source_destination != &temp, "you can't pass the same object to source_destination and temp arguments!");
RMLUI_ASSERTMSG(source_destination.Get_Width() == temp.Get_Width(), "must be equal to the same size!");
RMLUI_ASSERTMSG(source_destination.Get_Height() == temp.Get_Height(), "must be equal to the same size!");
RMLUI_ASSERTMSG(window_flipped.Valid(), "must be valid!");
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "RenderBlur");
int pass_level = 0;
SigmaToParameters(sigma, pass_level, sigma);
if (sigma == 0)
return;
const Rml::Rectanglei original_scissor = m_scissor;
Rml::Rectanglei scissor = window_flipped;
// probably we expect only textures with sample count = 1 otherwise it is MSAA and we should dynamically determine which pipeline state we should
// use in UseProgram method
UseProgram(ProgramId::Passthrough_NoBlendAndNoMSAA);
SetScissor(scissor, true);
D3D12_VIEWPORT vp{};
vp.TopLeftX = 0;
vp.TopLeftY = source_destination.Get_Height() / 2.0f;
vp.Width = source_destination.Get_Width() / 2.0f;
vp.Height = source_destination.Get_Height() / 2.0f;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
m_p_command_graphics_list->RSSetViewports(1, &vp);
const Rml::Vector2f uv_scaling = {(source_destination.Get_Width() % 2 == 1) ? (1.f - 1.f / float(source_destination.Get_Width())) : 1.f,
(source_destination.Get_Height() % 2 == 1) ? (1.f - 1.f / float(source_destination.Get_Height())) : 1.f};
ID3D12Resource* p_resource{};
// be careful! runtime dx changes this thing BUT argument for ResourceBarrier is CONST IDK how they do in their API but def they change that by
// themselves or somehow corrupt the inner data and it is kinda sad SO what i want to say that you have to initialize all fields that are required
// for passing to resource barrier method calling!!!
// if you have a such long situation and you want to pass just using one variable on stack...
D3D12_RESOURCE_BARRIER bars[1];
for (int i = 0; i < pass_level; i++)
{
scissor.p0 = (scissor.p0 + Rml::Vector2i(1)) / 2;
scissor.p1 = Rml::Math::Max(scissor.p1 / 2, scissor.p0);
const bool from_source = (i % 2 == 0);
BindRenderTarget(from_source ? temp : source_destination, false);
RenderInterface_DX12::TextureHandleType* p_texture = nullptr;
if (from_source)
{
// we expect only committed allocated resources
ValidateTextureAllocationNotAsPlaced(source_destination);
p_resource = GetResourceFromFramebufferData(source_destination);
RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from source_destination framebuffer!");
}
else
{
// we expect only committed allocated resources
ValidateTextureAllocationNotAsPlaced(temp);
p_resource = GetResourceFromFramebufferData(temp);
RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from source_destination framebuffer!");
}
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = p_resource;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
p_texture = from_source ? source_destination.Get_Texture() : temp.Get_Texture();
RMLUI_ASSERTMSG(p_texture, "must be valid!");
BindTexture(p_texture);
SetScissor(scissor, true);
DrawFullscreenQuad({}, uv_scaling);
if (from_source)
{
// we expect only committed allocated resources
ValidateTextureAllocationNotAsPlaced(source_destination);
RMLUI_ATTR_ASSERT_VARIABLE ID3D12Resource* p_resource_sd = GetResourceFromFramebufferData(source_destination);
RMLUI_ASSERTMSG(p_resource_sd, "failed to obtain resource from source_destination framebuffer!");
}
else
{
// we expect only committed allocated resources
ValidateTextureAllocationNotAsPlaced(temp);
RMLUI_ATTR_ASSERT_VARIABLE ID3D12Resource* p_resource_t = GetResourceFromFramebufferData(temp);
RMLUI_ASSERTMSG(p_resource_t, "failed to obtain resource from temp framebuffer!");
}
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = p_resource;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
}
vp.TopLeftX = 0;
vp.TopLeftY = 0;
vp.Width = static_cast<FLOAT>(source_destination.Get_Width());
vp.Height = static_cast<FLOAT>(source_destination.Get_Height());
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
m_p_command_graphics_list->RSSetViewports(1, &vp);
const bool transfer_to_temp_buffer = (pass_level % 2 == 0);
if (transfer_to_temp_buffer)
{
BindRenderTarget(temp, false);
RenderInterface_DX12::TextureHandleType* p_texture = source_destination.Get_Texture();
RMLUI_ASSERTMSG(p_texture, "must be valid!");
p_resource = GetResourceFromFramebufferData(source_destination);
RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from framebuffer source_destination");
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.pResource = p_resource;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
BindTexture(p_texture);
DrawFullscreenQuad();
}
UseProgram(ProgramId::Blur);
ConstantBufferType* p_cb = Get_ConstantBuffer(m_current_back_buffer_index);
RMLUI_ASSERTMSG(p_cb, "unable to allocate constant buffer for blur!");
std::uint8_t* p_cb_begin = reinterpret_cast<std::uint8_t*>(p_cb->m_p_gpu_start_memory_for_binding_data);
RMLUI_ASSERTMSG(p_cb_begin, "must be valid pointer of buffer where CB was allocated!");
std::uint8_t* p_cb_real_begin = p_cb_begin + p_cb->m_alloc_info.offset + sizeof(m_constant_buffer_data_transform) + sizeof(Rml::Vector2f);
// sadly but here we can't optimize and upload directly using pointer from GPU
// keep allocating on stack still fastest way possible to handle a such small data for uploading :)
struct {
Rml::Vector2f texel_offset;
Rml::Vector4f weights;
Rml::Vector2f texcoord_min;
Rml::Vector2f texcoord_max;
} ShaderConstantBufferMapping_Blur;
SetBlurWeights(ShaderConstantBufferMapping_Blur.weights, sigma);
SetTexCoordLimits(ShaderConstantBufferMapping_Blur.texcoord_min, ShaderConstantBufferMapping_Blur.texcoord_max, scissor,
{source_destination.Get_Width(), source_destination.Get_Height()});
ShaderConstantBufferMapping_Blur.texel_offset = Rml::Vector2f(0.f, 1.f) * (1.0f / float(temp.Get_Height()));
std::memcpy(p_cb_real_begin, &ShaderConstantBufferMapping_Blur, sizeof(ShaderConstantBufferMapping_Blur));
if (transfer_to_temp_buffer)
{
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = GetResourceFromFramebufferData(source_destination);
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
}
BindRenderTarget(source_destination, false);
p_resource = GetResourceFromFramebufferData(temp);
RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from framebuffer temp!");
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = p_resource;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
BindTexture(temp.Get_Texture());
DrawFullscreenQuad(p_cb);
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.Subresource = 0;
bars[0].Transition.pResource = GetResourceFromFramebufferData(temp);
m_p_command_graphics_list->ResourceBarrier(1, bars);
BindRenderTarget(temp, false);
p_resource = GetResourceFromFramebufferData(source_destination);
RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from framebuffer source_destination");
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = p_resource;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
BindTexture(source_destination.Get_Texture());
// Add a 1px transparent border around the blur region by first clearing with a padded scissor. This helps prevent
// artifacts when upscaling the blur result in the later step. On Intel and AMD, we have observed that during
// blitting with linear filtering, pixels outside the 'src' region can be blended into the output. On the other
// hand, it looks like Nvidia clamps the pixels to the source edge, which is what we really want. Regardless, we
// work around the issue with this extra step.
SetScissor(scissor.Extend(1), true);
Rml::Rectanglei scissor_ext = scissor.Extend(1);
scissor_ext = VerticallyFlipped(scissor_ext, m_height);
// Some render APIs don't like offscreen positions (WebGL in particular), so clamp them to the viewport.
const int x_min = Rml::Math::Clamp(scissor_ext.Left(), 0, m_width);
const int y_min = Rml::Math::Clamp(scissor_ext.Top(), 0, m_height);
const int x_max = Rml::Math::Clamp(scissor_ext.Right(), 0, m_width);
const int y_max = Rml::Math::Clamp(scissor_ext.Bottom(), 0, m_height);
D3D12_RECT rect_scissor{};
rect_scissor.left = x_min;
rect_scissor.top = y_min;
rect_scissor.right = x_max;
rect_scissor.bottom = y_max;
constexpr FLOAT clear_color[4] = {0.0f, 0.0f, 0.0f, 0.0f};
m_p_command_graphics_list->ClearRenderTargetView(temp.Get_DescriptorResourceView(), clear_color, 1, &rect_scissor);
SetScissor(scissor, true);
ShaderConstantBufferMapping_Blur.texel_offset = Rml::Vector2f(1.0f, 0.0f) * (1.0f / float(source_destination.Get_Width()));
p_cb = Get_ConstantBuffer(m_current_back_buffer_index);
RMLUI_ASSERTMSG(p_cb, "unable to allocate constant buffer for blur!");
p_cb_begin = reinterpret_cast<std::uint8_t*>(p_cb->m_p_gpu_start_memory_for_binding_data);
RMLUI_ASSERTMSG(p_cb_begin, "must be valid pointer of buffer where CB was allocated!");
p_cb_real_begin = p_cb_begin + p_cb->m_alloc_info.offset + sizeof(m_constant_buffer_data_transform) + sizeof(Rml::Vector2f);
std::memcpy(p_cb_real_begin, &ShaderConstantBufferMapping_Blur, sizeof(ShaderConstantBufferMapping_Blur));
DrawFullscreenQuad(p_cb);
p_resource = GetResourceFromFramebufferData(temp);
RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from framebuffer temp");
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = p_resource;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
p_resource = GetResourceFromFramebufferData(source_destination);
RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from framebuffer source_destination");
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = p_resource;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
// Blit the blurred image to the scissor region with upscaling.
Rml::Rectanglei window_flipped_twice = VerticallyFlipped(window_flipped, m_height);
SetScissor(window_flipped_twice, false);
const Rml::Vector2i src_min = scissor.p0;
const Rml::Vector2i src_max = scissor.p1;
const Rml::Vector2i dst_min = window_flipped_twice.p0;
const Rml::Vector2i dst_max = window_flipped_twice.p1;
BlitFramebuffer(temp, source_destination, src_min.x, src_min.y, src_max.x, src_max.y, dst_min.x, dst_max.y, dst_max.x, dst_min.y);
// DirectX 12 implementation notice: The following note is not implemented for this renderer, see the OpenGL 3 reference for details.
// The above upscale blit might be jittery at low resolutions (large pass levels). This is especially noticeable
// when moving an element with backdrop blur around or when trying to click/hover an element within a blurred
// region since it may be rendered at an offset. For more stable and accurate rendering we next upscale the blur
// image by an exact power-of-two. However, this may not fill the edges completely so we need to do the above
// first. Note that this strategy may sometimes result in visible seams. Alternatively, we could try to enlarge
// the window to the next power-of-two size and then downsample and blur that.
p_resource = GetResourceFromFramebufferData(temp);
RMLUI_ASSERTMSG(p_resource, "failed to obtain resource from framebuffer temp");
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = p_resource;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
// restore things
D3D12_VIEWPORT viewport{};
viewport.Height = static_cast<FLOAT>(m_height);
viewport.Width = static_cast<FLOAT>(m_width);
viewport.MaxDepth = 1.0f;
m_p_command_graphics_list->RSSetViewports(1, &viewport);
SetScissor(original_scissor);
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
void RenderInterface_DX12::RenderFilters(Rml::Span<const Rml::CompiledFilterHandle> filter_handles)
{
RMLUI_ZoneScopedN("DirectX 12 - RenderFilters");
for (const Rml::CompiledFilterHandle filter_handle : filter_handles)
{
const CompiledFilter& filter = *reinterpret_cast<const CompiledFilter*>(filter_handle);
const FilterType type = filter.type;
#ifdef RMLUI_DX_DEBUG
const auto filter_type_to_string = [](FilterType type) -> const char* {
switch (type)
{
case FilterType::Invalid: return "Invalid";
case FilterType::Passthrough: return "Passthrough";
case FilterType::Blur: return "Blur";
case FilterType::DropShadow: return "DropShadow";
case FilterType::ColorMatrix: return "ColorMatrix";
case FilterType::MaskImage: return "MaskImage";
default: return "UnknownFilterType";
}
};
const auto marker_name = Rml::CreateString("RenderFilter=%s", filter_type_to_string(type));
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, marker_name.c_str());
#endif
switch (type)
{
case FilterType::Passthrough:
{
const Gfx::FramebufferData& source = m_manager_render_layer.GetPostprocessPrimary();
const Gfx::FramebufferData& destination = m_manager_render_layer.GetPostprocessSecondary();
TextureHandleType* p_texture_source = source.Get_Texture();
TextureHandleType* p_texture_destination = destination.Get_Texture();
RMLUI_ASSERTMSG(p_texture_source, "must be valid pointer!");
RMLUI_ASSERTMSG(p_texture_destination, "must be valid pointer!");
RMLUI_ASSERT(p_texture_source->Get_Info().buffer_index == -1 &&
"expected that a such texture was allocated as committed (e.g. not placed resource), so in that case you can't cast to "
"D3D12MA::Allocation");
RMLUI_ASSERT(p_texture_destination->Get_Info().buffer_index == -1 &&
"expected that a such texture was allocated as committed (e.g. not placed resource) so in that case you can't cast to "
"D3D12MA::Allocation");
RMLUI_ASSERTMSG(p_texture_source->Get_Resource(), "must contain a valid resource");
RMLUI_ASSERTMSG(p_texture_destination->Get_Resource(), "must contain a valid resource");
D3D12MA::Allocation* p_allocation_source = static_cast<D3D12MA::Allocation*>(p_texture_source->Get_Resource());
RMLUI_ATTR_ASSERT_VARIABLE D3D12MA::Allocation* p_allocation_destination =
static_cast<D3D12MA::Allocation*>(p_texture_destination->Get_Resource());
RMLUI_ASSERTMSG(p_allocation_source->GetResource(), "allocation must contain a valid pointer to resource! something is broken");
RMLUI_ASSERTMSG(p_allocation_destination->GetResource(), "allocation must contain a valid pointer to resource! something is broken");
ID3D12Resource* p_resource_as_srv = p_allocation_source->GetResource();
BindRenderTarget(destination, false);
const FLOAT blend_factor[] = {filter.blend_factor, filter.blend_factor, filter.blend_factor, filter.blend_factor};
m_p_command_graphics_list->OMSetBlendFactor(blend_factor);
UseProgram(ProgramId::Passthrough_Opacity);
{
D3D12_RESOURCE_BARRIER bars[1];
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = p_resource_as_srv;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
}
BindTexture(p_texture_source);
DrawFullscreenQuad();
{
D3D12_RESOURCE_BARRIER bars[1];
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = p_resource_as_srv;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
}
m_manager_render_layer.SwapPostprocessPrimarySecondary();
break;
}
case FilterType::Blur:
{
const Gfx::FramebufferData& source = m_manager_render_layer.GetPostprocessPrimary();
const Gfx::FramebufferData& temp = m_manager_render_layer.GetPostprocessSecondary();
const Rml::Rectanglei window_flipped = VerticallyFlipped(m_scissor, m_height);
RenderBlur(filter.sigma, source, temp, window_flipped);
break;
}
case FilterType::DropShadow:
{
UseProgram(ProgramId::DropShadow);
const Gfx::FramebufferData& primary = m_manager_render_layer.GetPostprocessPrimary();
const Gfx::FramebufferData& secondary = m_manager_render_layer.GetPostprocessSecondary();
BindRenderTarget(secondary, false);
ValidateTextureAllocationNotAsPlaced(primary);
{
ID3D12Resource* pPrimaryResource = GetResourceFromFramebufferData(primary);
RMLUI_ASSERTMSG(pPrimaryResource, "framebuffer primary doesn't contain a valid resource for barrier transition!");
D3D12_RESOURCE_BARRIER bars[1];
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = pPrimaryResource;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
}
BindTexture(primary.Get_Texture());
ConstantBufferType* p_cb_dropshadow = Get_ConstantBuffer(m_current_back_buffer_index);
RMLUI_ASSERTMSG(p_cb_dropshadow, "failed to obtain constant buffer for drop shadow");
struct CBV_DropShadow {
Rml::Vector2f uv_min;
Rml::Vector2f uv_max;
Rml::Vector4f color;
};
CBV_DropShadow* pUploadingData = nullptr;
if (p_cb_dropshadow)
{
std::uint8_t* p_gpu_begin = reinterpret_cast<std::uint8_t*>(p_cb_dropshadow->m_p_gpu_start_memory_for_binding_data);
RMLUI_ASSERTMSG(p_gpu_begin, "constant buffer must contain information about its GPU location (pointer for data binding/uploading)");
if (p_gpu_begin)
{
std::uint8_t* p_gpu_real_begin = p_gpu_begin + p_cb_dropshadow->m_alloc_info.offset;
RMLUI_ASSERTMSG(p_gpu_real_begin, "failed to apply offset for gpu pointer");
if (p_gpu_real_begin)
{
pUploadingData = reinterpret_cast<CBV_DropShadow*>(p_gpu_real_begin);
}
}
}
RMLUI_ASSERTMSG(pUploadingData, "failed to obtain uploading pointer for constant buffer (FATAL)");
const Rml::Colourf& color = ConvertToColorf(filter.color);
pUploadingData->color.x = color.red;
pUploadingData->color.y = color.green;
pUploadingData->color.z = color.blue;
pUploadingData->color.w = color.alpha;
const Rml::Rectanglei& window_flipped = VerticallyFlipped(m_scissor, m_height);
SetTexCoordLimits(pUploadingData->uv_min, pUploadingData->uv_max, m_scissor, {primary.Get_Width(), primary.Get_Height()});
const Rml::Vector2f& uv_offset = filter.offset / Rml::Vector2f(-(float)m_width, (float)m_height);
DrawFullscreenQuad(uv_offset, Rml::Vector2f(1.0f), p_cb_dropshadow);
if (filter.sigma >= 0.5f)
{
const Gfx::FramebufferData& tertiary = m_manager_render_layer.GetPostprocessTertiary();
RenderBlur(filter.sigma, secondary, tertiary, window_flipped);
}
UseProgram(ProgramId::Passthrough_NoDepthStencil);
BindRenderTarget(secondary, false);
BindTexture(primary.Get_Texture());
DrawFullscreenQuad();
{
ID3D12Resource* pPrimaryResource = GetResourceFromFramebufferData(primary);
RMLUI_ASSERTMSG(pPrimaryResource, "framebuffer primary doesn't contain a valid resource for barrier transition!");
D3D12_RESOURCE_BARRIER bars[1];
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = pPrimaryResource;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
}
m_manager_render_layer.SwapPostprocessPrimarySecondary();
break;
}
case FilterType::ColorMatrix:
{
UseProgram(ProgramId::ColorMatrix);
const Gfx::FramebufferData& source = m_manager_render_layer.GetPostprocessPrimary();
const Gfx::FramebufferData& destination = m_manager_render_layer.GetPostprocessSecondary();
BindRenderTarget(destination, false);
{
ID3D12Resource* pSource = GetResourceFromFramebufferData(source);
RMLUI_ASSERTMSG(pSource, "failed to obtain resource from framebuffer source!");
D3D12_RESOURCE_BARRIER bars[1];
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = pSource;
bars[0].Transition.Subresource = 0;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
m_p_command_graphics_list->ResourceBarrier(1, bars);
}
BindTexture(source.Get_Texture());
ConstantBufferType* p_cb = Get_ConstantBuffer(m_current_back_buffer_index);
RMLUI_ASSERTMSG(p_cb, "failed to obtain constant buffer for color matrix");
if (p_cb)
{
std::uint8_t* p_cb_begin = reinterpret_cast<std::uint8_t*>(p_cb->m_p_gpu_start_memory_for_binding_data);
RMLUI_ASSERTMSG(p_cb_begin, "constant buffer must provide gpu begin binding pointer for uploading data from CPU");
if (p_cb_begin)
{
std::uint8_t* p_cb_real_begin = p_cb_begin + p_cb->m_alloc_info.offset;
RMLUI_ASSERTMSG(p_cb_real_begin,
"constant buffer must provide gpu begin binding pointer for upload data from CPU (offset applied)");
if (p_cb_real_begin)
{
constexpr bool is_need_transpose = std::is_same<decltype(filter.color_matrix), Rml::RowMajorMatrix4f>::value;
const float* p_data = is_need_transpose ? filter.color_matrix.Transpose().data() : filter.color_matrix.data();
std::memcpy(p_cb_real_begin, p_data, sizeof(filter.color_matrix));
}
}
}
DrawFullscreenQuad(p_cb);
{
ID3D12Resource* pSource = GetResourceFromFramebufferData(source);
RMLUI_ASSERTMSG(pSource, "failed to obtain resource from framebuffer source!");
D3D12_RESOURCE_BARRIER bars[1];
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = pSource;
bars[0].Transition.Subresource = 0;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
m_p_command_graphics_list->ResourceBarrier(1, bars);
}
m_manager_render_layer.SwapPostprocessPrimarySecondary();
break;
}
case FilterType::MaskImage:
{
UseProgram(ProgramId::BlendMask);
const Gfx::FramebufferData& source = m_manager_render_layer.GetPostprocessPrimary();
const Gfx::FramebufferData& blend_mask = m_manager_render_layer.GetBlendMask();
const Gfx::FramebufferData& destination = m_manager_render_layer.GetPostprocessSecondary();
{
ID3D12Resource* pBM = GetResourceFromFramebufferData(blend_mask);
ID3D12Resource* pSrc = GetResourceFromFramebufferData(source);
RMLUI_ASSERTMSG(pBM, "failed to obtain resource from framebuffer blend_mask");
RMLUI_ASSERTMSG(pSrc, "failed to obtain resource from framebuffer destination");
D3D12_RESOURCE_BARRIER bars[2];
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = pBM;
bars[0].Transition.Subresource = 0;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[1].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[1].Transition.pResource = pSrc;
bars[1].Transition.Subresource = 0;
bars[1].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[1].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
m_p_command_graphics_list->ResourceBarrier(2, bars);
}
BindRenderTarget(destination, false);
BindTexture(source.Get_Texture(), 0);
BindTexture(blend_mask.Get_Texture(), 1);
DrawFullscreenQuad();
{
ID3D12Resource* pBM = GetResourceFromFramebufferData(blend_mask);
ID3D12Resource* pSrc = GetResourceFromFramebufferData(source);
RMLUI_ASSERTMSG(pBM, "failed to obtain resource from framebuffer blend_mask");
RMLUI_ASSERTMSG(pSrc, "failed to obtain resource from framebuffer destination");
D3D12_RESOURCE_BARRIER bars[2];
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = pBM;
bars[0].Transition.Subresource = 0;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[1].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[1].Transition.pResource = pSrc;
bars[1].Transition.Subresource = 0;
bars[1].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[1].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
m_p_command_graphics_list->ResourceBarrier(2, bars);
}
m_manager_render_layer.SwapPostprocessPrimarySecondary();
break;
}
case FilterType::Invalid:
{
Rml::Log::Message(Rml::Log::LT_WARNING, "Invalid (Unhandled) render filter: %d", static_cast<int>(type));
break;
}
default:
{
Rml::Log::Message(Rml::Log::LT_WARNING, "Unknown render filter: %d", static_cast<int>(type));
break;
}
}
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
}
void RenderInterface_DX12::CompositeLayers(Rml::LayerHandle source, Rml::LayerHandle destination, Rml::BlendMode blend_mode,
Rml::Span<const Rml::CompiledFilterHandle> filters)
{
RMLUI_ZoneScopedN("DirectX 12 - CompositeLayers");
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "CompositeLayers");
BlitLayerToPostprocessPrimary(source);
RenderFilters(filters);
// Consider instead to use a separate command list and wait for command queue.
Flush();
BindRenderTarget(m_manager_render_layer.GetLayer(destination));
if (blend_mode == Rml::BlendMode::Replace)
{
UseProgram(ProgramId::Passthrough_NoBlend);
}
else
{
// since we use msaa render target we should use appropriate version of pipeline
if (m_is_stencil_equal && m_is_stencil_enabled)
{
UseProgram(ProgramId::Passthrough_MSAA_Equal);
}
else
{
UseProgram(ProgramId::Passthrough_MSAA);
}
}
{
ID3D12Resource* pResource = GetResourceFromFramebufferData(m_manager_render_layer.GetPostprocessPrimary());
RMLUI_ASSERTMSG(pResource, "failed to obtain resource from m_manager_render_layer.GetPostprocessPrimary()");
D3D12_RESOURCE_BARRIER bars[1];
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = pResource;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
}
BindTexture(m_manager_render_layer.GetPostprocessPrimary().Get_Texture());
DrawFullscreenQuad();
{
ID3D12Resource* pResource = GetResourceFromFramebufferData(m_manager_render_layer.GetPostprocessPrimary());
RMLUI_ASSERTMSG(pResource, "failed to obtain resource from m_manager_render_layer.GetPostprocessPrimary()");
D3D12_RESOURCE_BARRIER bars[1];
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = pResource;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
}
// should we set like return blend state as enabled?
if (blend_mode == Rml::BlendMode::Replace)
{
UseProgram(ProgramId::Passthrough);
}
if (destination != m_manager_render_layer.GetTopLayerHandle())
{
BindRenderTarget(m_manager_render_layer.GetTopLayer());
}
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
void RenderInterface_DX12::PopLayer()
{
RMLUI_ZoneScopedN("DirectX 12 - PopLayer");
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "PopLayer");
m_manager_render_layer.PopLayer();
BindRenderTarget(m_manager_render_layer.GetTopLayer());
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
Rml::TextureHandle RenderInterface_DX12::SaveLayerAsTexture()
{
RMLUI_ZoneScopedN("DirectX 12 - SaveLayerAsTexture");
RMLUI_ASSERT(m_scissor.Valid());
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "SaveLayerAsTexture");
const Rml::Rectanglei bounds = m_scissor;
Rml::TextureHandle render_texture = GenerateTexture({}, bounds.Size());
if (!render_texture)
{
return {};
}
BlitLayerToPostprocessPrimary(m_manager_render_layer.GetTopLayerHandle());
EnableScissorRegion(false);
const Gfx::FramebufferData& source = m_manager_render_layer.GetPostprocessPrimary();
const Gfx::FramebufferData& destination = m_manager_render_layer.GetPostprocessSecondary();
{
ID3D12Resource* pResource = GetResourceFromFramebufferData(source);
RMLUI_ASSERTMSG(pResource, "failed to obtain resource from framebuffer source");
D3D12_RESOURCE_BARRIER bars[1];
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = pResource;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
}
BlitFramebuffer(source, destination, bounds.Left(), source.Get_Height() - bounds.Bottom(), bounds.Right(), source.Get_Height() - bounds.Top(), 0,
bounds.Height(), bounds.Width(), 0);
{
ID3D12Resource* pResource = GetResourceFromFramebufferData(source);
RMLUI_ASSERTMSG(pResource, "failed to obtain resource from framebuffer source");
D3D12_RESOURCE_BARRIER bars[1];
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = pResource;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(1, bars);
}
D3D12_BOX copy_box{};
copy_box.left = bounds.Left();
copy_box.right = bounds.Right();
copy_box.top = bounds.Top();
copy_box.bottom = bounds.Bottom();
copy_box.front = 0;
copy_box.back = 1;
TextureHandleType* p_casted_rt = reinterpret_cast<TextureHandleType*>(render_texture);
ID3D12Resource* p_resource_render_texture = nullptr;
if (p_casted_rt->Get_Info().buffer_index == -1)
{
// committed
D3D12MA::Allocation* p_allocation_source = static_cast<D3D12MA::Allocation*>(p_casted_rt->Get_Resource());
RMLUI_ASSERTMSG(p_allocation_source, "must be valid!");
RMLUI_ASSERTMSG(p_allocation_source->GetResource(), "must be valid!");
p_resource_render_texture = p_allocation_source->GetResource();
}
else
{
// placed
RMLUI_ASSERT(p_casted_rt->Get_Resource());
p_resource_render_texture = static_cast<ID3D12Resource*>(p_casted_rt->Get_Resource());
}
RMLUI_ASSERTMSG(p_resource_render_texture, "failed to obtain resource from allocated texture!");
#ifdef RMLUI_DX_DEBUG
if (p_resource_render_texture)
{
p_resource_render_texture->SetName(L"SaveLayerAsTexture = Texture dest copy");
}
#endif
D3D12_TEXTURE_COPY_LOCATION dest_copy;
dest_copy.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
dest_copy.pResource = p_resource_render_texture;
dest_copy.SubresourceIndex = 0;
ID3D12Resource* p_resource_from_destination = GetResourceFromFramebufferData(destination);
RMLUI_ASSERTMSG(p_resource_from_destination, "failed to obtain resource from framebuffer destination");
D3D12_TEXTURE_COPY_LOCATION src_copy;
src_copy.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
src_copy.SubresourceIndex = 0;
src_copy.pResource = p_resource_from_destination;
{
D3D12_RESOURCE_BARRIER bars[2];
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = p_resource_from_destination;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.Subresource = 0;
bars[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[1].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[1].Transition.pResource = p_resource_render_texture;
bars[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
bars[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COMMON;
bars[1].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(2, bars);
}
m_p_command_graphics_list->CopyTextureRegion(&dest_copy, 0, 0, 0, &src_copy, &copy_box);
{
D3D12_RESOURCE_BARRIER bars[2];
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = p_resource_from_destination;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
bars[0].Transition.Subresource = 0;
bars[1].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[1].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[1].Transition.pResource = p_resource_render_texture;
bars[1].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[1].Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
bars[1].Transition.Subresource = 0;
m_p_command_graphics_list->ResourceBarrier(2, bars);
}
SetScissor(bounds);
BindRenderTarget(m_manager_render_layer.GetTopLayer());
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
return render_texture;
}
Rml::CompiledFilterHandle RenderInterface_DX12::SaveLayerAsMaskImage()
{
RMLUI_ZoneScopedN("DirectX 12 - SaveLayerAsMaskImage");
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "SaveLayerAsMaskImage");
BlitLayerToPostprocessPrimary(m_manager_render_layer.GetTopLayerHandle());
const Gfx::FramebufferData& source = m_manager_render_layer.GetPostprocessPrimary();
const Gfx::FramebufferData& destination = m_manager_render_layer.GetBlendMask();
{
D3D12_RESOURCE_BARRIER bars[1];
ID3D12Resource* pSource = GetResourceFromFramebufferData(source);
RMLUI_ASSERTMSG(pSource, "failed to obtain resource from framebuffer source");
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = pSource;
bars[0].Transition.Subresource = 0;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
m_p_command_graphics_list->ResourceBarrier(1, bars);
}
UseProgram(ProgramId::Passthrough_NoBlendAndNoMSAA);
BindRenderTarget(destination, false);
BindTexture(source.Get_Texture());
DrawFullscreenQuad();
{
D3D12_RESOURCE_BARRIER bars[1];
ID3D12Resource* pSource = GetResourceFromFramebufferData(source);
RMLUI_ASSERTMSG(pSource, "failed to obtain resource from framebuffer source");
bars[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
bars[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
bars[0].Transition.pResource = pSource;
bars[0].Transition.Subresource = 0;
bars[0].Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
bars[0].Transition.StateBefore = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
m_p_command_graphics_list->ResourceBarrier(1, bars);
}
BindRenderTarget(m_manager_render_layer.GetTopLayer());
CompiledFilter filter = {};
filter.type = FilterType::MaskImage;
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
return reinterpret_cast<Rml::CompiledFilterHandle>(new CompiledFilter(std::move(filter)));
}
Rml::CompiledFilterHandle RenderInterface_DX12::CompileFilter(const Rml::String& name, const Rml::Dictionary& parameters)
{
RMLUI_ZoneScopedN("DirectX 12 - CompileFilter");
CompiledFilter filter = {};
if (name == "opacity")
{
filter.type = FilterType::Passthrough;
filter.blend_factor = Rml::Get(parameters, "value", 1.0f);
}
else if (name == "blur")
{
filter.type = FilterType::Blur;
filter.sigma = Rml::Get(parameters, "sigma", 1.0f);
}
else if (name == "drop-shadow")
{
filter.type = FilterType::DropShadow;
filter.sigma = Rml::Get(parameters, "sigma", 0.f);
filter.color = Rml::Get(parameters, "color", Rml::Colourb()).ToPremultiplied();
filter.offset = Rml::Get(parameters, "offset", Rml::Vector2f(0.f));
}
else if (name == "brightness")
{
filter.type = FilterType::ColorMatrix;
const float value = Rml::Get(parameters, "value", 1.0f);
filter.color_matrix = Rml::Matrix4f::Diag(value, value, value, 1.f);
}
else if (name == "contrast")
{
filter.type = FilterType::ColorMatrix;
const float value = Rml::Get(parameters, "value", 1.0f);
const float grayness = 0.5f - 0.5f * value;
filter.color_matrix = Rml::Matrix4f::Diag(value, value, value, 1.f);
filter.color_matrix.SetColumn(3, Rml::Vector4f(grayness, grayness, grayness, 1.f));
}
else if (name == "invert")
{
filter.type = FilterType::ColorMatrix;
const float value = Rml::Math::Clamp(Rml::Get(parameters, "value", 1.0f), 0.f, 1.f);
const float inverted = 1.f - 2.f * value;
filter.color_matrix = Rml::Matrix4f::Diag(inverted, inverted, inverted, 1.f);
filter.color_matrix.SetColumn(3, Rml::Vector4f(value, value, value, 1.f));
}
else if (name == "grayscale")
{
filter.type = FilterType::ColorMatrix;
const float value = Rml::Get(parameters, "value", 1.0f);
const float rev_value = 1.f - value;
const Rml::Vector3f gray = value * Rml::Vector3f(0.2126f, 0.7152f, 0.0722f);
// clang-format off
filter.color_matrix = Rml::Matrix4f::FromRows(
{gray.x + rev_value, gray.y, gray.z, 0.f},
{gray.x, gray.y + rev_value, gray.z, 0.f},
{gray.x, gray.y, gray.z + rev_value, 0.f},
{0.f, 0.f, 0.f, 1.f}
);
// clang-format on
}
else if (name == "sepia")
{
filter.type = FilterType::ColorMatrix;
const float value = Rml::Get(parameters, "value", 1.0f);
const float rev_value = 1.f - value;
const Rml::Vector3f r_mix = value * Rml::Vector3f(0.393f, 0.769f, 0.189f);
const Rml::Vector3f g_mix = value * Rml::Vector3f(0.349f, 0.686f, 0.168f);
const Rml::Vector3f b_mix = value * Rml::Vector3f(0.272f, 0.534f, 0.131f);
// clang-format off
filter.color_matrix = Rml::Matrix4f::FromRows(
{r_mix.x + rev_value, r_mix.y, r_mix.z, 0.f},
{g_mix.x, g_mix.y + rev_value, g_mix.z, 0.f},
{b_mix.x, b_mix.y, b_mix.z + rev_value, 0.f},
{0.f, 0.f, 0.f, 1.f}
);
// clang-format on
}
else if (name == "hue-rotate")
{
// Hue-rotation and saturation values based on: https://www.w3.org/TR/filter-effects-1/#attr-valuedef-type-huerotate
filter.type = FilterType::ColorMatrix;
const float value = Rml::Get(parameters, "value", 1.0f);
const float s = Rml::Math::Sin(value);
const float c = Rml::Math::Cos(value);
// clang-format off
filter.color_matrix = Rml::Matrix4f::FromRows(
{0.213f + 0.787f * c - 0.213f * s, 0.715f - 0.715f * c - 0.715f * s, 0.072f - 0.072f * c + 0.928f * s, 0.f},
{0.213f - 0.213f * c + 0.143f * s, 0.715f + 0.285f * c + 0.140f * s, 0.072f - 0.072f * c - 0.283f * s, 0.f},
{0.213f - 0.213f * c - 0.787f * s, 0.715f - 0.715f * c + 0.715f * s, 0.072f + 0.928f * c + 0.072f * s, 0.f},
{0.f, 0.f, 0.f, 1.f}
);
// clang-format on
}
else if (name == "saturate")
{
filter.type = FilterType::ColorMatrix;
const float value = Rml::Get(parameters, "value", 1.0f);
// clang-format off
filter.color_matrix = Rml::Matrix4f::FromRows(
{0.213f + 0.787f * value, 0.715f - 0.715f * value, 0.072f - 0.072f * value, 0.f},
{0.213f - 0.213f * value, 0.715f + 0.285f * value, 0.072f - 0.072f * value, 0.f},
{0.213f - 0.213f * value, 0.715f - 0.715f * value, 0.072f + 0.928f * value, 0.f},
{0.f, 0.f, 0.f, 1.f}
);
// clang-format on
}
if (filter.type != FilterType::Invalid)
return reinterpret_cast<Rml::CompiledFilterHandle>(new CompiledFilter(std::move(filter)));
Rml::Log::Message(Rml::Log::LT_WARNING, "Unsupported filter type '%s'.", name.c_str());
return {};
}
void RenderInterface_DX12::ReleaseFilter(Rml::CompiledFilterHandle filter)
{
RMLUI_ZoneScopedN("DirectX 12 - ReleaseFilter");
delete reinterpret_cast<CompiledFilter*>(filter);
}
Rml::CompiledShaderHandle RenderInterface_DX12::CompileShader(const Rml::String& name, const Rml::Dictionary& parameters)
{
RMLUI_ZoneScopedN("DirectX 12 - CompileShader");
auto ApplyColorStopList = [](CompiledShader& shader, const Rml::Dictionary& shader_parameters) {
auto it = shader_parameters.find("color_stop_list");
RMLUI_ASSERT(it != shader_parameters.end() && it->second.GetType() == Rml::Variant::COLORSTOPLIST);
const Rml::ColorStopList& color_stop_list = it->second.GetReference<Rml::ColorStopList>();
const int num_stops = Rml::Math::Min((int)color_stop_list.size(), MAX_NUM_STOPS);
shader.stop_positions.resize(num_stops);
shader.stop_colors.resize(num_stops);
for (int i = 0; i < num_stops; i++)
{
const Rml::ColorStop& stop = color_stop_list[i];
RMLUI_ASSERT(stop.position.unit == Rml::Unit::NUMBER);
shader.stop_positions[i] = stop.position.number;
shader.stop_colors[i] = ConvertToColorf(stop.color);
}
};
CompiledShader shader = {};
if (name == "linear-gradient")
{
shader.type = CompiledShaderType::Gradient;
const bool repeating = Rml::Get(parameters, "repeating", false);
shader.gradient_function = (repeating ? ShaderGradientFunction::RepeatingLinear : ShaderGradientFunction::Linear);
shader.p = Rml::Get(parameters, "p0", Rml::Vector2f(0.f));
shader.v = Rml::Get(parameters, "p1", Rml::Vector2f(0.f)) - shader.p;
ApplyColorStopList(shader, parameters);
}
else if (name == "radial-gradient")
{
shader.type = CompiledShaderType::Gradient;
const bool repeating = Rml::Get(parameters, "repeating", false);
shader.gradient_function = (repeating ? ShaderGradientFunction::RepeatingRadial : ShaderGradientFunction::Radial);
shader.p = Rml::Get(parameters, "center", Rml::Vector2f(0.f));
shader.v = Rml::Vector2f(1.f) / Rml::Get(parameters, "radius", Rml::Vector2f(1.f));
ApplyColorStopList(shader, parameters);
}
else if (name == "conic-gradient")
{
shader.type = CompiledShaderType::Gradient;
const bool repeating = Rml::Get(parameters, "repeating", false);
shader.gradient_function = (repeating ? ShaderGradientFunction::RepeatingConic : ShaderGradientFunction::Conic);
shader.p = Rml::Get(parameters, "center", Rml::Vector2f(0.f));
const float angle = Rml::Get(parameters, "angle", 0.f);
shader.v = {Rml::Math::Cos(angle), Rml::Math::Sin(angle)};
ApplyColorStopList(shader, parameters);
}
else if (name == "shader")
{
const Rml::String value = Rml::Get(parameters, "value", Rml::String());
if (value == "creation")
{
shader.type = CompiledShaderType::Creation;
shader.dimensions = Rml::Get(parameters, "dimensions", Rml::Vector2f(0.f));
}
}
if (shader.type != CompiledShaderType::Invalid)
return reinterpret_cast<Rml::CompiledShaderHandle>(new CompiledShader(std::move(shader)));
Rml::Log::Message(Rml::Log::LT_WARNING, "Unsupported shader type '%s'.", name.c_str());
return {};
}
void RenderInterface_DX12::RenderShader(Rml::CompiledShaderHandle shader_handle, Rml::CompiledGeometryHandle geometry_handle,
Rml::Vector2f translation, Rml::TextureHandle texture)
{
RMLUI_ZoneScopedN("DirectX 12 - RenderShader");
RMLUI_ASSERT(shader_handle && geometry_handle);
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "RenderShader");
// fixing unreferenced parameter
(void)(texture);
const CompiledShader& shader = *reinterpret_cast<CompiledShader*>(shader_handle);
const CompiledShaderType type = shader.type;
const GeometryHandleType* geometry = reinterpret_cast<GeometryHandleType*>(geometry_handle);
switch (type)
{
case CompiledShaderType::Gradient:
{
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "Gradient");
RMLUI_ASSERT(shader.stop_positions.size() == shader.stop_colors.size());
const int num_stops = (int)shader.stop_positions.size();
UseProgram(ProgramId::Gradient);
ConstantBufferType* p_cb = Get_ConstantBuffer(m_current_back_buffer_index);
RMLUI_ASSERTMSG(p_cb, "failed to obtain constant buffer for gradient");
struct CBV_Gradient {
Rml::Matrix4f transform;
Rml::Vector2f translate;
int func;
int num_stops;
Rml::Vector2f starting_point;
Rml::Vector2f ending_point;
Rml::Vector4f stop_colors[MAX_NUM_STOPS];
float stop_positions[MAX_NUM_STOPS];
};
if (p_cb)
{
std::uint8_t* p_cb_begin = reinterpret_cast<std::uint8_t*>(p_cb->m_p_gpu_start_memory_for_binding_data);
RMLUI_ASSERTMSG(p_cb_begin, "ConstantBuffer must contain valid pointer to begin of gpu binding pointer for uploading data from CPU!");
if (p_cb_begin)
{
std::uint8_t* p_cb_begin_real = p_cb_begin + p_cb->m_alloc_info.offset;
RMLUI_ASSERTMSG(p_cb_begin_real, "failed to offset gpu begin pointer for constant buffer!");
if (p_cb_begin_real)
{
CBV_Gradient* p_casted = reinterpret_cast<CBV_Gradient*>(p_cb_begin_real);
p_casted->transform = m_constant_buffer_data_transform;
p_casted->translate = translation;
p_casted->func = (int)(shader.gradient_function);
p_casted->starting_point = shader.p;
p_casted->ending_point = shader.v;
p_casted->num_stops = num_stops;
std::memset(p_casted->stop_positions, 0, sizeof(CBV_Gradient::stop_positions));
std::memset(p_casted->stop_colors, 0, sizeof(CBV_Gradient::stop_colors));
std::memcpy(&p_casted->stop_positions, shader.stop_positions.data(), num_stops * sizeof(float));
std::memcpy(&p_casted->stop_colors, shader.stop_colors.data(), num_stops * sizeof(Rml::Vector4f));
}
}
}
if (p_cb)
{
auto* p_dx_constant_buffer = m_manager_buffer.Get_BufferByIndex(p_cb->m_alloc_info.buffer_index);
RMLUI_ASSERTMSG(p_dx_constant_buffer, "must be valid!");
if (p_dx_constant_buffer)
{
auto* p_dx_resource = p_dx_constant_buffer->GetResource();
RMLUI_ASSERTMSG(p_dx_resource, "must be valid!");
if (p_dx_resource)
{
D3D12_GPU_VIRTUAL_ADDRESS one_shared_cbv_for_different_shaders_address =
p_dx_resource->GetGPUVirtualAddress() + p_cb->m_alloc_info.offset;
m_p_command_graphics_list->SetGraphicsRootConstantBufferView(0, one_shared_cbv_for_different_shaders_address);
m_p_command_graphics_list->SetGraphicsRootConstantBufferView(1, one_shared_cbv_for_different_shaders_address);
}
}
}
m_p_command_graphics_list->IASetPrimitiveTopology(D3D12_PRIMITIVE_TOPOLOGY::D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
auto* p_dx_buffer_vertex = m_manager_buffer.Get_BufferByIndex(geometry->Get_InfoVertex().buffer_index);
RMLUI_ASSERTMSG(p_dx_buffer_vertex, "must be valid!");
if (p_dx_buffer_vertex)
{
auto* p_dx_resource = p_dx_buffer_vertex->GetResource();
RMLUI_ASSERTMSG(p_dx_resource, "must be valid!");
if (p_dx_resource)
{
D3D12_VERTEX_BUFFER_VIEW view_vertex_buffer = {};
view_vertex_buffer.BufferLocation = p_dx_resource->GetGPUVirtualAddress() + geometry->Get_InfoVertex().offset;
view_vertex_buffer.StrideInBytes = sizeof(Rml::Vertex);
view_vertex_buffer.SizeInBytes = static_cast<UINT>(geometry->Get_InfoVertex().size);
m_p_command_graphics_list->IASetVertexBuffers(0, 1, &view_vertex_buffer);
}
}
auto* p_dx_buffer_index = m_manager_buffer.Get_BufferByIndex(geometry->Get_InfoIndex().buffer_index);
RMLUI_ASSERTMSG(p_dx_buffer_index, "must be valid!");
if (p_dx_buffer_index)
{
auto* p_dx_resource = p_dx_buffer_index->GetResource();
RMLUI_ASSERTMSG(p_dx_resource, "must be valid!");
if (p_dx_resource)
{
D3D12_INDEX_BUFFER_VIEW view_index_buffer = {};
view_index_buffer.BufferLocation = p_dx_resource->GetGPUVirtualAddress() + geometry->Get_InfoIndex().offset;
view_index_buffer.Format = DXGI_FORMAT::DXGI_FORMAT_R32_UINT;
view_index_buffer.SizeInBytes = static_cast<UINT>(geometry->Get_InfoIndex().size);
m_p_command_graphics_list->IASetIndexBuffer(&view_index_buffer);
}
}
m_p_command_graphics_list->DrawIndexedInstanced(geometry->Get_NumIndices(), 1, 0, 0, 0);
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
break;
}
case CompiledShaderType::Creation:
{
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "Creation");
UseProgram(ProgramId::Creation);
ConstantBufferType* p_cb = Get_ConstantBuffer(m_current_back_buffer_index);
RMLUI_ASSERTMSG(p_cb, "failed to obtain constant buffer for gradient");
struct CBV_Creation {
Rml::Matrix4f transform;
Rml::Vector2f translation;
Rml::Vector2f dimensions;
float time;
};
if (p_cb)
{
std::uint8_t* p_cb_begin = reinterpret_cast<std::uint8_t*>(p_cb->m_p_gpu_start_memory_for_binding_data);
RMLUI_ASSERTMSG(p_cb_begin, "ConstantBuffer must contain valid pointer to begin of gpu binding pointer for uploading data from CPU!");
if (p_cb_begin)
{
std::uint8_t* p_cb_begin_real = p_cb_begin + p_cb->m_alloc_info.offset;
RMLUI_ASSERTMSG(p_cb_begin_real, "failed to offset gpu begin pointer for constant buffer!");
if (p_cb_begin_real)
{
CBV_Creation* p_casted = reinterpret_cast<CBV_Creation*>(p_cb_begin_real);
p_casted->transform = m_constant_buffer_data_transform;
p_casted->translation = translation;
const double time = Rml::GetSystemInterface()->GetElapsedTime();
p_casted->time = (float)time;
p_casted->dimensions = shader.dimensions;
}
}
}
if (p_cb)
{
auto* p_dx_constant_buffer = m_manager_buffer.Get_BufferByIndex(p_cb->m_alloc_info.buffer_index);
RMLUI_ASSERTMSG(p_dx_constant_buffer, "must be valid!");
if (p_dx_constant_buffer)
{
auto* p_dx_resource = p_dx_constant_buffer->GetResource();
RMLUI_ASSERTMSG(p_dx_resource, "must be valid!");
if (p_dx_resource)
{
D3D12_GPU_VIRTUAL_ADDRESS one_shared_cbv_for_different_shaders_address =
p_dx_resource->GetGPUVirtualAddress() + p_cb->m_alloc_info.offset;
m_p_command_graphics_list->SetGraphicsRootConstantBufferView(0, one_shared_cbv_for_different_shaders_address);
m_p_command_graphics_list->SetGraphicsRootConstantBufferView(1, one_shared_cbv_for_different_shaders_address);
}
}
}
m_p_command_graphics_list->IASetPrimitiveTopology(D3D12_PRIMITIVE_TOPOLOGY::D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
auto* p_dx_buffer_vertex = m_manager_buffer.Get_BufferByIndex(geometry->Get_InfoVertex().buffer_index);
RMLUI_ASSERTMSG(p_dx_buffer_vertex, "must be valid!");
if (p_dx_buffer_vertex)
{
auto* p_dx_resource = p_dx_buffer_vertex->GetResource();
RMLUI_ASSERTMSG(p_dx_resource, "must be valid!");
if (p_dx_resource)
{
D3D12_VERTEX_BUFFER_VIEW view_vertex_buffer = {};
view_vertex_buffer.BufferLocation = p_dx_resource->GetGPUVirtualAddress() + geometry->Get_InfoVertex().offset;
view_vertex_buffer.StrideInBytes = sizeof(Rml::Vertex);
view_vertex_buffer.SizeInBytes = static_cast<UINT>(geometry->Get_InfoVertex().size);
m_p_command_graphics_list->IASetVertexBuffers(0, 1, &view_vertex_buffer);
}
}
auto* p_dx_buffer_index = m_manager_buffer.Get_BufferByIndex(geometry->Get_InfoIndex().buffer_index);
RMLUI_ASSERTMSG(p_dx_buffer_index, "must be valid!");
if (p_dx_buffer_index)
{
auto* p_dx_resource = p_dx_buffer_index->GetResource();
RMLUI_ASSERTMSG(p_dx_resource, "must be valid!");
if (p_dx_resource)
{
D3D12_INDEX_BUFFER_VIEW view_index_buffer = {};
view_index_buffer.BufferLocation = p_dx_resource->GetGPUVirtualAddress() + geometry->Get_InfoIndex().offset;
view_index_buffer.Format = DXGI_FORMAT::DXGI_FORMAT_R32_UINT;
view_index_buffer.SizeInBytes = static_cast<UINT>(geometry->Get_InfoIndex().size);
m_p_command_graphics_list->IASetIndexBuffer(&view_index_buffer);
}
}
m_p_command_graphics_list->DrawIndexedInstanced(geometry->Get_NumIndices(), 1, 0, 0, 0);
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
break;
}
case CompiledShaderType::Invalid:
{
Rml::Log::Message(Rml::Log::LT_WARNING, "Unhandled render shader %d", (int)type);
break;
}
}
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
}
void RenderInterface_DX12::ReleaseShader(Rml::CompiledShaderHandle effect_handle)
{
RMLUI_ZoneScopedN("DirectX 12 - ReleaseShader");
delete reinterpret_cast<CompiledShader*>(effect_handle);
}
bool RenderInterface_DX12::IsSwapchainValid() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - IsSwapchainValid");
return (m_p_swapchain != nullptr);
}
void RenderInterface_DX12::RecreateSwapchain() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - RecreateSwapchain");
SetViewport(m_width, m_height);
}
ID3D12Fence* RenderInterface_DX12::Get_Fence()
{
RMLUI_ZoneScopedN("DirectX 12 - Get_Fence");
return m_p_backbuffer_fence;
}
HANDLE RenderInterface_DX12::Get_FenceEvent()
{
RMLUI_ZoneScopedN("DirectX 12 - Get_FenceEvent");
return m_p_fence_event;
}
Rml::Array<uint64_t, RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT>& RenderInterface_DX12::Get_FenceValues()
{
RMLUI_ZoneScopedN("DirectX 12 - Get_FenceValues");
return m_backbuffers_fence_values;
}
uint32_t RenderInterface_DX12::Get_CurrentFrameIndex()
{
RMLUI_ZoneScopedN("DirectX 12 - Get_CurrentFrameIndex");
return m_current_back_buffer_index;
}
ID3D12Device* RenderInterface_DX12::Get_Device() const
{
RMLUI_ZoneScopedN("DirectX 12 - Get_Device");
return m_p_device;
}
RenderInterface_DX12::TextureMemoryManager& RenderInterface_DX12::Get_TextureManager()
{
RMLUI_ZoneScopedN("DirectX 12 - Get_TextureManager");
return m_manager_texture;
}
RenderInterface_DX12::BufferMemoryManager& RenderInterface_DX12::Get_BufferManager()
{
RMLUI_ZoneScopedN("DirectX 12 - Get_BufferManager");
return m_manager_buffer;
}
unsigned char RenderInterface_DX12::Get_MSAASampleCount() const
{
return m_msaa_sample_count;
}
void RenderInterface_DX12::Set_UserFramebufferIndex(unsigned char framebuffer_index)
{
m_current_back_buffer_index = static_cast<UINT>(framebuffer_index);
}
void RenderInterface_DX12::Set_UserRenderTarget(void* rtv_where_we_render_to)
{
RMLUI_ASSERTMSG(rtv_where_we_render_to, "you can't pass empty rtv for rendering");
m_p_user_rtv_present = reinterpret_cast<D3D12_CPU_DESCRIPTOR_HANDLE*>(rtv_where_we_render_to);
}
void RenderInterface_DX12::Set_UserDepthStencil(void* dsv_where_we_render_to)
{
RMLUI_ASSERTMSG(dsv_where_we_render_to, "you can't pass empty dsv for rendering");
m_p_user_dsv_present = reinterpret_cast<D3D12_CPU_DESCRIPTOR_HANDLE*>(dsv_where_we_render_to);
}
bool RenderInterface_DX12::CaptureScreen(int& out_width, int& out_height, int& out_num_components, Rml::UniquePtr<Rml::byte[]>& out_data)
{
out_width = -1;
out_height = -1;
out_num_components = -1;
out_data = {};
if (!m_p_device || !m_p_swapchain || !m_p_command_allocator_screenshot || !m_p_command_graphics_list_screenshot || !m_p_command_queue ||
!m_p_fence_screenshot || !m_p_fence_event_screenshot)
{
RMLUI_ERRORMSG("Early calling");
return false;
}
HRESULT status = m_p_command_allocator_screenshot->Reset();
RMLUI_DX_VERIFY_MSG(status, "failed ID3D12CommandAllocator::Reset (screenshot)");
status = m_p_command_graphics_list_screenshot->Reset(m_p_command_allocator_screenshot, nullptr);
RMLUI_DX_VERIFY_MSG(status, "failed ID3D12CommandGraphicsList::Reset (screenshot)");
ID3D12Resource* p_back_buffer{};
status = m_p_swapchain->GetBuffer(m_p_swapchain->GetCurrentBackBufferIndex(), IID_PPV_ARGS(&p_back_buffer));
if (!SUCCEEDED(status) || !p_back_buffer)
{
RMLUI_ERRORMSG("failed to obtain swapchain's backbuffer");
return false;
}
const D3D12_RESOURCE_DESC& desc = p_back_buffer->GetDesc();
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] Obtained swapchain's backbuffer: w=%zu h=%d format=%s", desc.Width, desc.Height,
Rml::DXGIFormatToString(desc.Format));
#endif
UINT64 required_size = 0;
D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout = {};
UINT64 source_row_size_bytes = 0;
UINT num_rows = 0;
m_p_device->GetCopyableFootprints(&desc, 0, 1, 0, &layout, &num_rows, &source_row_size_bytes, &required_size);
D3D12_HEAP_PROPERTIES heap_props = {};
heap_props.Type = D3D12_HEAP_TYPE_READBACK;
heap_props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heap_props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heap_props.CreationNodeMask = 1;
heap_props.VisibleNodeMask = 1;
D3D12_RESOURCE_DESC cpu_readaccess_buffer_desc = {};
cpu_readaccess_buffer_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
cpu_readaccess_buffer_desc.Alignment = 0;
cpu_readaccess_buffer_desc.Width = required_size;
cpu_readaccess_buffer_desc.Height = 1; // since we just want Rml::byte array there's no need in two dimensionality
cpu_readaccess_buffer_desc.DepthOrArraySize = 1;
cpu_readaccess_buffer_desc.MipLevels = 1;
cpu_readaccess_buffer_desc.Format = DXGI_FORMAT_UNKNOWN;
cpu_readaccess_buffer_desc.SampleDesc.Count = 1;
cpu_readaccess_buffer_desc.SampleDesc.Quality = 0;
cpu_readaccess_buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
cpu_readaccess_buffer_desc.Flags = D3D12_RESOURCE_FLAG_NONE;
ID3D12Resource* p_cpu_readaccess_buffer{};
status = m_p_device->CreateCommittedResource(&heap_props, D3D12_HEAP_FLAG_NONE, &cpu_readaccess_buffer_desc,
D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&p_cpu_readaccess_buffer));
if (!SUCCEEDED(status) || !p_cpu_readaccess_buffer)
{
RMLUI_ERRORMSG("failed to create buffer for CPU reading");
return false;
}
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = p_back_buffer;
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
m_p_command_graphics_list_screenshot->ResourceBarrier(1, &barrier);
D3D12_TEXTURE_COPY_LOCATION src_loc = {};
src_loc.pResource = p_back_buffer;
src_loc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
src_loc.SubresourceIndex = 0;
D3D12_TEXTURE_COPY_LOCATION dst_loc = {};
dst_loc.pResource = p_cpu_readaccess_buffer;
dst_loc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
dst_loc.PlacedFootprint = layout;
m_p_command_graphics_list_screenshot->CopyTextureRegion(&dst_loc, 0, 0, 0, &src_loc, nullptr);
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_SOURCE;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
m_p_command_graphics_list_screenshot->ResourceBarrier(1, &barrier);
status = m_p_command_graphics_list_screenshot->Close();
RMLUI_DX_VERIFY_MSG(status, "failed ID3D12CommandGraphicsList::Close");
ID3D12CommandList* p_lists[] = {m_p_command_graphics_list_screenshot};
m_p_command_queue->ExecuteCommandLists(1, p_lists);
status = m_p_command_queue->Signal(m_p_fence_screenshot, m_fence_screenshot_value);
RMLUI_DX_VERIFY_MSG(status, "failed ID3D12CommandQueue::Signal (screenshot)");
status = m_p_fence_screenshot->SetEventOnCompletion(m_fence_screenshot_value, m_p_fence_event_screenshot);
RMLUI_DX_VERIFY_MSG(status, "failed ID3D12Fence::SetEventOnCompletion (screenshot)");
WaitForSingleObject(m_p_fence_event_screenshot, INFINITE);
++m_fence_screenshot_value;
void* p_mapped_data = nullptr;
status = p_cpu_readaccess_buffer->Map(0, nullptr, &p_mapped_data);
RMLUI_DX_VERIFY_MSG(status, "failed to Map buffer (screenshot)");
bool result = false;
if (SUCCEEDED(status))
{
const size_t source_num_components = 4;
const size_t target_num_components = 3;
const size_t width = desc.Width;
const size_t height = desc.Height;
const size_t target_row_size_bytes = target_num_components * width;
const size_t target_data_size = target_row_size_bytes * height;
out_data.reset(new Rml::byte[target_data_size]);
for (size_t y = 0; y < height; y++)
{
byte* source_row_origin = reinterpret_cast<Rml::byte*>(p_mapped_data) + (height - 1 - y) * layout.Footprint.RowPitch;
byte* target_row_origin = out_data.get() + y * width * target_num_components;
for (size_t x = 0; x < width; x++)
{
std::memcpy(target_row_origin + x * target_num_components, source_row_origin + x * source_num_components, target_num_components);
}
}
out_width = static_cast<int>(width);
out_height = static_cast<int>(height);
out_num_components = target_num_components;
result = true;
}
p_cpu_readaccess_buffer->Unmap(0, nullptr);
p_back_buffer->Release();
p_cpu_readaccess_buffer->Release();
#ifdef RMLUI_DX_DEBUG
if (result)
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12]: successfully captured data of swapchain backbuffer");
#endif
return result;
}
void RenderInterface_DX12::Initialize_Device() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Initialize_Device");
RMLUI_ASSERTMSG(m_p_adapter, "you must call this when you initialized adapter!");
ID3D12Device2* p_device{};
RMLUI_DX_VERIFY_MSG(D3D12CreateDevice(m_p_adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&p_device)), "failed to D3D12CreateDevice");
m_p_device = p_device;
#ifdef RMLUI_DX_DEBUG
ID3D12InfoQueue* p_queue{};
if (p_device)
{
RMLUI_DX_VERIFY_MSG(p_device->QueryInterface(IID_PPV_ARGS(&p_queue)), "failed to QueryInterface of ID3D12InfoQueue");
if (p_queue)
{
// if implementation is good breaking will NOT be caused at all, but if something is bad you will get __debugbreak in Debug after
// ReportLiveObjects calling that means system works not correctly for D3D12 API at all and you must fix these problems what your device
// reported (it might be annoying because you will not see the result of ReportLiveObjects and you should comment these line of
// SetBreakOnSeverity in order to see full report from ReportLiveObjects calling)
p_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, 1);
p_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, 1);
// Currently we get warnings about scissors being set with zero drawable area, i.e. p0.x == p1.x or p0.y ==
// p1.y. This is how the RmlUi API works right now and is expected. We could add more conditions to avoid
// drawing in these situations, eeither on the RmlUi-side or in the renderer. But for now we ignore the
// warning, it shouldn't cause any issues.
// p_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, 1);
// Suppress messages based on their severity level
D3D12_MESSAGE_SEVERITY p_sevs[] = {D3D12_MESSAGE_SEVERITY_INFO};
D3D12_INFO_QUEUE_FILTER info_filter = {};
info_filter.DenyList.NumSeverities = _countof(p_sevs);
info_filter.DenyList.pSeverityList = p_sevs;
RMLUI_DX_VERIFY_MSG(p_queue->PushStorageFilter(&info_filter), "failed to PushStorageFilter");
p_queue->Release();
}
ID3D12DebugDevice* p_sdk_device{};
auto status = p_device->QueryInterface(IID_PPV_ARGS(&p_sdk_device));
RMLUI_DX_VERIFY_MSG(status, "failed to obtain debug device!");
if (p_sdk_device)
{
status = p_sdk_device->SetFeatureMask(D3D12_DEBUG_FEATURE_CONSERVATIVE_RESOURCE_STATE_TRACKING);
RMLUI_DX_VERIFY_MSG(status, "failed to enable feature conservative resource state tracking");
p_sdk_device->Release();
}
}
#endif
}
void RenderInterface_DX12::Initialize_Adapter() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Initialize_Adapter");
if (m_p_adapter)
{
m_p_adapter->Release();
}
m_p_adapter = Get_Adapter(false);
}
void RenderInterface_DX12::Initialize_DebugLayer() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Initialize_DebugLayer");
#ifdef RMLUI_DX_DEBUG
ID3D12Debug* p_debug{};
RMLUI_DX_VERIFY_MSG(D3D12GetDebugInterface(IID_PPV_ARGS(&p_debug)), "failed to D3D12GetDebugInterface");
if (p_debug)
{
p_debug->EnableDebugLayer();
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] Debug Layer = ENABLED!");
#endif
ID3D12Debug1* p_sdk_layers{};
auto status = p_debug->QueryInterface(IID_PPV_ARGS(&p_sdk_layers));
RMLUI_DX_VERIFY_MSG(status,
"failed to enable GPU based validation :( your driver or windows NT sdk doesn't support a such feature report to developers DX12 SDK or "
"to GPU vendor developers");
if (SUCCEEDED(status))
{
p_sdk_layers->SetEnableGPUBasedValidation(TRUE);
p_sdk_layers->SetEnableSynchronizedCommandQueueValidation(TRUE);
p_sdk_layers->Release();
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] GPU validation = ENABLED!");
#endif
}
p_debug->Release();
}
#endif
}
ID3D12CommandQueue* RenderInterface_DX12::Create_CommandQueue(D3D12_COMMAND_LIST_TYPE type) noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Create_CommandQueue");
RMLUI_ASSERTMSG(m_p_device, "you must initialize device before calling this method");
ID3D12CommandQueue* p_result{};
if (m_p_device)
{
D3D12_COMMAND_QUEUE_DESC desc = {};
desc.Type = type;
desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
desc.NodeMask = 0;
RMLUI_DX_VERIFY_MSG(m_p_device->CreateCommandQueue(&desc, IID_PPV_ARGS(&p_result)), "failed to CreateCommandQueue");
}
return p_result;
}
void RenderInterface_DX12::Initialize_Swapchain(int width, int height) noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Initialize_Swapchain");
RMLUI_ASSERTMSG(width >= 0, "must not be a negative value");
RMLUI_ASSERTMSG(height >= 0, "must not be a negative value");
// in dx12 0 means it will take size of window automatically
if (width < 0)
width = 0;
if (height < 0)
height = 0;
IDXGISwapChain4* p_swapchain{};
IDXGIFactory4* p_factory{};
uint32_t create_factory_flags{};
#ifdef RMLUI_DX_DEBUG
create_factory_flags = DXGI_CREATE_FACTORY_DEBUG;
#endif
RMLUI_DX_VERIFY_MSG(CreateDXGIFactory2(create_factory_flags, IID_PPV_ARGS(&p_factory)), "failed to CreateDXGIFactory2");
m_desc_sample.Count = m_msaa_sample_count;
m_desc_sample.Quality = 0;
DXGI_SWAP_CHAIN_DESC1 desc = {};
desc.Width = width;
desc.Height = height;
desc.Format = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
desc.Stereo = 0;
// since we can't use rt textures with different sample count than Swapchain's so we create framebuffers and presenting them on NO-MSAA
// Swapchain
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
uint32_t flags_swapchain{};
if (CheckTearingSupport())
{
flags_swapchain = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
m_is_use_tearing = true;
}
desc.Flags = flags_swapchain;
IDXGISwapChain1* p_swapchain1{};
RMLUI_DX_VERIFY_MSG(p_factory->CreateSwapChainForHwnd(m_p_command_queue, m_p_window_handle, &desc, nullptr, nullptr, &p_swapchain1),
"failed to CreateSwapChainForHwnd");
RMLUI_DX_VERIFY_MSG(p_factory->MakeWindowAssociation(m_p_window_handle, DXGI_MWA_NO_ALT_ENTER), "failed to MakeWindowAssociation");
RMLUI_DX_VERIFY_MSG(p_swapchain1->QueryInterface(IID_PPV_ARGS(&p_swapchain)), "failed to QueryInterface of IDXGISwapChain4");
m_p_swapchain = p_swapchain;
if (p_swapchain1)
{
p_swapchain1->Release();
}
if (p_factory)
{
p_factory->Release();
}
}
void RenderInterface_DX12::Initialize_SyncPrimitives() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Initialize_SyncPrimitives");
RMLUI_ASSERTMSG(m_p_device, "you must initialize device before calling this method!");
if (m_p_device)
{
for (int i = 0; i < RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT; ++i)
{
m_backbuffers_fence_values[i] = 0;
}
m_p_device->CreateFence(0, D3D12_FENCE_FLAGS::D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_p_backbuffer_fence));
m_p_fence_event = CreateEvent(NULL, FALSE, FALSE, NULL);
RMLUI_ASSERTMSG(m_p_fence_event, "failed to CreateEvent (WinAPI)");
}
}
void RenderInterface_DX12::Initialize_SyncPrimitives_Screenshot() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Initialize_SyncPrimitives_Screenshot");
RMLUI_ASSERTMSG(m_p_device, "you must initialize device before calling this method!");
if (m_p_device)
{
HRESULT status = m_p_device->CreateFence(0, D3D12_FENCE_FLAGS::D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_p_fence_screenshot));
RMLUI_DX_VERIFY_MSG(status, "ID3D12Device::CreateFence failed! (screenshot)");
m_p_fence_event_screenshot = CreateEvent(NULL, FALSE, FALSE, NULL);
RMLUI_ASSERTMSG(m_p_fence_event_screenshot, "failed to CreateEvent (Screenshot)");
}
}
void RenderInterface_DX12::Initialize_CommandAllocators()
{
RMLUI_ZoneScopedN("DirectX 12 - Initialize_CommandAllocators");
for (int i = 0; i < RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT; ++i)
{
m_backbuffers_allocators[i] = Create_CommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT);
}
}
void RenderInterface_DX12::Initialize_Allocator() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Initialize_Allocator");
// I guess better to isolate allocations from user side and using own allocators under of rmlui's renderer thus we forced for creating allocator
{
RMLUI_ASSERTMSG(!m_p_allocator, "forgot to destroy before initialization!");
RMLUI_ASSERTMSG(m_p_device, "must be valid when you call this method!");
RMLUI_ASSERTMSG(m_p_adapter, "must be valid when you call this method!");
D3D12MA::ALLOCATOR_DESC desc = {};
desc.pDevice = m_p_device;
desc.pAdapter = m_p_adapter;
D3D12MA::Allocator* p_allocator{};
RMLUI_DX_VERIFY_MSG(D3D12MA::CreateAllocator(&desc, &p_allocator), "failed to D3D12MA::CreateAllocator!");
RMLUI_ASSERTMSG(p_allocator, "failed to create allocator!");
m_p_allocator = p_allocator;
}
}
void RenderInterface_DX12::Destroy_Swapchain() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Destroy_Swapchain");
if (m_p_swapchain)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_swapchain->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
m_p_swapchain = nullptr;
}
void RenderInterface_DX12::Destroy_SyncPrimitives() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Destroy_SyncPrimitives");
if (m_p_backbuffer_fence)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_backbuffer_fence->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
m_p_backbuffer_fence = nullptr;
}
void RenderInterface_DX12::Destroy_CommandAllocators() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Destroy_CommandAllocators");
for (ID3D12CommandAllocator* p_allocator : m_backbuffers_allocators)
{
RMLUI_ASSERTMSG(p_allocator, "early calling or object is damaged!");
if (p_allocator)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = p_allocator->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
}
}
void RenderInterface_DX12::Destroy_CommandList() noexcept {}
void RenderInterface_DX12::Destroy_Allocator() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Destroy_Allocator");
if (m_p_allocator)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_allocator->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
}
void RenderInterface_DX12::Destroy_SyncPrimitives_Screenshot() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Destroy_SyncPrimitives_Screenshot");
if (m_p_fence_screenshot)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_fence_screenshot->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
m_p_fence_screenshot = nullptr;
}
if (m_p_fence_event_screenshot)
{
CloseHandle(m_p_fence_event_screenshot);
m_p_fence_event_screenshot = nullptr;
}
m_fence_screenshot_value = 0;
}
void RenderInterface_DX12::Flush() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Flush");
Signal(m_current_back_buffer_index);
RMLUI_ASSERTMSG(m_p_backbuffer_fence, "you must initialize ID3D12Fence first!");
RMLUI_ASSERTMSG(m_p_fence_event, "you must initialize fence event (HANDLE)");
if (m_p_backbuffer_fence)
{
if (m_p_fence_event)
{
RMLUI_DX_VERIFY_MSG(
m_p_backbuffer_fence->SetEventOnCompletion(m_backbuffers_fence_values.at(m_current_back_buffer_index), m_p_fence_event),
"failed to SetEventOnCompletion");
WaitForSingleObjectEx(m_p_fence_event, INFINITE, FALSE);
}
}
m_backbuffers_fence_values[m_current_back_buffer_index]++;
}
uint64_t RenderInterface_DX12::Signal(uint32_t frame_index) noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Signal");
RMLUI_ASSERTMSG(m_p_command_queue, "you must initialize it first before calling this method!");
RMLUI_ASSERTMSG(m_p_backbuffer_fence, "you must initialize it first before calling this method!");
if (m_p_command_queue)
{
if (m_p_backbuffer_fence)
{
auto value = (m_backbuffers_fence_values.at(frame_index));
RMLUI_DX_VERIFY_MSG(m_p_command_queue->Signal(m_p_backbuffer_fence, value), "failed to command queue::Signal!");
return value;
}
}
return 0;
}
void RenderInterface_DX12::WaitForFenceValue(uint32_t frame_index)
{
RMLUI_ZoneScopedN("DirectX 12 - WaitForFenceValue");
RMLUI_ASSERTMSG(m_p_backbuffer_fence, "you must initialize ID3D12Fence first!");
RMLUI_ASSERTMSG(m_p_fence_event, "you must initialize fence event (HANDLE)");
if (m_p_backbuffer_fence)
{
if (m_p_fence_event)
{
if (m_p_backbuffer_fence->GetCompletedValue() < m_backbuffers_fence_values.at(frame_index))
{
RMLUI_DX_VERIFY_MSG(m_p_backbuffer_fence->SetEventOnCompletion(m_backbuffers_fence_values.at(frame_index), m_p_fence_event),
"failed to SetEventOnCompletion");
WaitForSingleObjectEx(m_p_fence_event, INFINITE, FALSE);
}
}
}
}
void RenderInterface_DX12::Create_Resources_DependentOnSize() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Create_Resources_DependentOnSize");
Create_Resource_RenderTargetViews();
// Create_Resource_Pipelines();
}
void RenderInterface_DX12::Destroy_Resources_DependentOnSize() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Destroy_Resources_DependentOnSize");
// Destroy_Resource_Pipelines();
Destroy_Resource_RenderTagetViews();
}
void RenderInterface_DX12::Create_Resource_DepthStencil()
{
RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_DepthStencil");
RMLUI_ASSERTMSG(m_p_descriptor_heap_depthstencil, "you must initialize this descriptor heap before calling this method!");
RMLUI_ASSERTMSG(m_p_device, "you must create device!");
RMLUI_ASSERTMSG(m_width > 0, "invalid width");
RMLUI_ASSERTMSG(m_height > 0, "invalid height");
D3D12MA::ALLOCATION_DESC desc_alloc = {};
desc_alloc.HeapType = D3D12_HEAP_TYPE_DEFAULT;
D3D12_RESOURCE_DESC desc_texture = {};
desc_texture.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
desc_texture.Format = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT;
desc_texture.MipLevels = 1;
desc_texture.Width = m_width;
desc_texture.Height = m_height;
desc_texture.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
desc_texture.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
desc_texture.SampleDesc.Count = 1;
desc_texture.SampleDesc.Quality = 0;
desc_texture.DepthOrArraySize = 1;
desc_texture.Alignment = 0;
D3D12_CLEAR_VALUE depth_optimized_clear_value = {};
depth_optimized_clear_value.Format = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT;
depth_optimized_clear_value.DepthStencil.Depth = RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_DEPTHSTENCIL_DEPTH_VALUE;
depth_optimized_clear_value.DepthStencil.Stencil = RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_DEPTHSTENCIL_STENCIL_VALUE;
ID3D12Resource* p_temp{};
auto status = m_p_allocator->CreateResource(&desc_alloc, &desc_texture, D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_DEPTH_WRITE,
&depth_optimized_clear_value, &m_p_depthstencil_resource, IID_PPV_ARGS(&p_temp));
RMLUI_DX_VERIFY_MSG(status, "failed to create resource as depth stencil texture!");
RMLUI_ASSERTMSG(m_p_depthstencil_resource, "must be created!");
RMLUI_ASSERTMSG(p_temp, "must be created!");
#ifdef RMLUI_DX_DEBUG
m_p_depthstencil_resource->SetName(L"DepthStencil texture (resource)");
#endif
D3D12_DEPTH_STENCIL_VIEW_DESC desc_view = {};
desc_view.Format = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT;
desc_view.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
desc_view.Flags = D3D12_DSV_FLAG_NONE;
m_p_device->CreateDepthStencilView(m_p_depthstencil_resource->GetResource(), &desc_view,
m_p_descriptor_heap_depthstencil->GetCPUDescriptorHandleForHeapStart());
}
void RenderInterface_DX12::Destroy_Resource_DepthStencil()
{
RMLUI_ZoneScopedN("DirectX 12 - Destroy_Resource_DepthStencil");
RMLUI_ASSERTMSG(m_p_depthstencil_resource, "you must create resource for calling this method!");
RMLUI_ASSERTMSG(m_p_depthstencil_resource->GetResource(), "must be valid!");
if (m_p_depthstencil_resource)
{
if (m_p_depthstencil_resource->GetResource())
{
m_p_depthstencil_resource->GetResource()->Release();
}
RMLUI_ATTR_ASSERT_VARIABLE auto count = m_p_depthstencil_resource->Release();
RMLUI_ASSERTMSG(count == 0, "leak!");
m_p_depthstencil_resource = nullptr;
}
}
ID3D12DescriptorHeap* RenderInterface_DX12::Create_Resource_DescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE type, D3D12_DESCRIPTOR_HEAP_FLAGS flags,
uint32_t descriptor_count) noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_DescriptorHeap");
RMLUI_ASSERTMSG(m_p_device, "early calling you have to initialize device first");
ID3D12DescriptorHeap* p_result{};
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
desc.NumDescriptors = descriptor_count;
desc.Type = type;
desc.Flags = flags;
RMLUI_DX_VERIFY_MSG(m_p_device->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&p_result)), "failed to CreateDescriptorHeap");
return p_result;
}
ID3D12CommandAllocator* RenderInterface_DX12::Create_CommandAllocator(D3D12_COMMAND_LIST_TYPE type)
{
RMLUI_ZoneScopedN("DirectX 12 - Create_CommandAllocator");
ID3D12CommandAllocator* p_result{};
RMLUI_ASSERTMSG(m_p_device, "you must initialize device first!");
if (m_p_device)
{
RMLUI_DX_VERIFY_MSG(m_p_device->CreateCommandAllocator(type, IID_PPV_ARGS(&p_result)), "failed to CreateCommandAllocator");
RMLUI_ASSERTMSG(p_result, "can't allocate command allocator!");
}
return p_result;
}
ID3D12GraphicsCommandList* RenderInterface_DX12::Create_CommandList(ID3D12CommandAllocator* p_allocator, D3D12_COMMAND_LIST_TYPE type) noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Create_CommandList");
ID3D12GraphicsCommandList* p_result{};
RMLUI_ASSERTMSG(m_p_device, "you must initialize device first!");
RMLUI_ASSERTMSG(p_allocator, "you must pass a valid instance of ID3D12CommandAllocator*");
if (m_p_device)
{
RMLUI_DX_VERIFY_MSG(m_p_device->CreateCommandList(0, type, p_allocator, nullptr, IID_PPV_ARGS(&p_result)), "failed to CreateCommandList");
RMLUI_ASSERTMSG(p_result, "can't allocator command list!");
if (p_result)
{
RMLUI_DX_VERIFY_MSG(p_result->Close(), "failed to Close command list!");
}
}
return p_result;
}
void RenderInterface_DX12::Create_Resource_RenderTargetViews()
{
RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_RenderTargetViews");
RMLUI_ASSERTMSG(m_p_device, "early calling you have to initialize device first!");
RMLUI_ASSERTMSG(m_p_swapchain, "early calling you have to initialize swapchain first!");
RMLUI_ASSERTMSG(m_p_descriptor_heap_render_target_view, "early calling you have to initialize descriptor heap for render target views first!");
if (m_p_device)
{
if (m_p_swapchain)
{
if (m_p_descriptor_heap_render_target_view)
{
auto rtv_size = m_p_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle(m_p_descriptor_heap_render_target_view->GetCPUDescriptorHandleForHeapStart());
for (auto i = 0; i < RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT; ++i)
{
ID3D12Resource* p_back_buffer{};
RMLUI_DX_VERIFY_MSG(m_p_swapchain->GetBuffer(i, IID_PPV_ARGS(&p_back_buffer)), "failed to GetBuffer from swapchain");
m_p_device->CreateRenderTargetView(p_back_buffer, nullptr, rtv_handle);
m_backbuffers_resources[i] = p_back_buffer;
rtv_handle.ptr += (rtv_size);
}
}
}
}
}
void RenderInterface_DX12::Destroy_Resource_RenderTagetViews()
{
RMLUI_ZoneScopedN("DirectX 12 - Destroy_Resource_RenderTargetViews");
for (ID3D12Resource* p_backbuffer : m_backbuffers_resources)
{
RMLUI_ASSERTMSG(p_backbuffer, "it is strange must be always valid pointer!");
if (p_backbuffer)
{
p_backbuffer->Release();
}
}
for (int i = 0; i < RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT; ++i)
{
m_backbuffers_resources[i] = nullptr;
}
}
void RenderInterface_DX12::Destroy_Resource_For_Shaders()
{
RMLUI_ZoneScopedN("DirectX 12 - Destroy_Resource_For_Shaders");
for (auto& vec_cb_per_frame : m_constantbuffers)
{
for (auto& cb : vec_cb_per_frame)
{
m_manager_buffer.Free_ConstantBuffer(&cb);
}
}
for (unsigned char i = 0; i < RMLUI_RENDER_BACKEND_FIELD_SWAPCHAIN_BACKBUFFER_COUNT; ++i)
{
m_constantbuffers[i].clear();
}
Update_PendingForDeletion_Geometry();
}
void RenderInterface_DX12::Free_Geometry(RenderInterface_DX12::GeometryHandleType* p_handle)
{
RMLUI_ZoneScopedN("DirectX 12 - Free_Geometry");
RMLUI_ASSERTMSG(p_handle, "invalid handle");
if (p_handle)
{
m_manager_buffer.Free_Geometry(p_handle);
delete p_handle;
}
}
void RenderInterface_DX12::Free_Texture(RenderInterface_DX12::TextureHandleType* p_handle)
{
RMLUI_ZoneScopedN("DirectX 12 - Free_Texture");
RMLUI_ASSERTMSG(p_handle, "must be valid!");
if (p_handle)
{
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] Destroyed texture: [%s]", p_handle->Get_ResourceName().c_str());
#endif
m_manager_texture.Free_Texture(p_handle);
delete p_handle;
}
}
void RenderInterface_DX12::Update_PendingForDeletion_Geometry()
{
RMLUI_ZoneScopedN("DirectX 12 - Update_PendingForDeletion_Geometry");
for (auto& p_handle : m_pending_for_deletion_geometry)
{
Free_Geometry(p_handle);
}
m_pending_for_deletion_geometry.clear();
}
void RenderInterface_DX12::Create_Resource_Pipelines()
{
RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipelines");
for (auto& vec_cb : m_constantbuffers)
{
vec_cb.resize(RMLUI_RENDER_BACKEND_FIELD_PREALLOCATED_CONSTANTBUFFERS);
}
for (auto& vec_cb : m_constantbuffers)
{
for (auto& cb : vec_cb)
{
const auto& info = m_manager_buffer.Alloc_ConstantBuffer(&cb, kAllocationSizeMax_ConstantBuffer);
cb.m_alloc_info = info;
}
}
m_pending_for_deletion_geometry.reserve(RMLUI_RENDER_BACKEND_FIELD_PREALLOCATED_CONSTANTBUFFERS);
Create_Resource_Pipeline_BlendMask();
Create_Resource_Pipeline_Blur();
Create_Resource_Pipeline_Color();
Create_Resource_Pipeline_ColorMatrix();
Create_Resource_Pipeline_Creation();
Create_Resource_Pipeline_DropShadow();
Create_Resource_Pipeline_Gradient();
Create_Resource_Pipeline_Passthrough();
Create_Resource_Pipeline_Passthrough_NoBlend();
Create_Resource_Pipeline_Texture();
}
void RenderInterface_DX12::Create_Resource_Pipeline_Color()
{
RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_Color");
RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!");
if (m_p_device)
{
D3D12_ROOT_DESCRIPTOR descriptor_cbv;
descriptor_cbv.RegisterSpace = 0;
descriptor_cbv.ShaderRegister = 0;
D3D12_ROOT_PARAMETER parameters[1];
parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
parameters[0].Descriptor = descriptor_cbv;
parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
D3D12_ROOT_SIGNATURE_DESC desc_rootsignature;
desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
desc_rootsignature.NumStaticSamplers = 0;
desc_rootsignature.pParameters = parameters;
desc_rootsignature.NumParameters = _countof(parameters);
desc_rootsignature.pStaticSamplers = nullptr;
ID3DBlob* p_signature{};
ID3DBlob* p_error{};
auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error);
RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_Always)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
// stencil version
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_Intersect)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_Set)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_SetInverse)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_Equal)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_Disabled)]));
RMLUI_DX_VERIFY_MSG(status, "failed to Color_Stencil_Disabled");
if (p_signature)
{
p_signature->Release();
p_signature = nullptr;
}
if (p_error)
{
p_error->Release();
p_error = nullptr;
}
ID3DBlob* p_shader_vertex{};
ID3DBlob* p_shader_pixel{};
ID3DBlob* p_error_buff{};
status = D3DCompile(pShaderSourceText_Vertex, sizeof(pShaderSourceText_Vertex), nullptr, nullptr, nullptr, "main", "vs_5_0",
m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff);
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_ERROR, "failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer());
}
#endif
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
status = D3DCompile(pShaderSourceText_Color, sizeof(pShaderSourceText_Color), nullptr, nullptr, nullptr, "main", "ps_5_0",
m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff);
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_ERROR, "failed to compile shader: %s", (char*)(p_error_buff->GetBufferPointer()));
}
#endif
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {};
desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize();
desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer();
D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {};
desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize();
desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer();
D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}};
D3D12_INPUT_LAYOUT_DESC desc_input_layout = {};
desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC);
desc_input_layout.pInputElementDescs = desc_input_layout_elements;
D3D12_RASTERIZER_DESC desc_rasterizer = {};
desc_rasterizer.FillMode = D3D12_FILL_MODE_SOLID;
desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE;
desc_rasterizer.FrontCounterClockwise = FALSE;
desc_rasterizer.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
desc_rasterizer.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
desc_rasterizer.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
desc_rasterizer.DepthClipEnable = FALSE;
desc_rasterizer.MultisampleEnable = FALSE;
desc_rasterizer.AntialiasedLineEnable = FALSE;
desc_rasterizer.ForcedSampleCount = 0;
desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
D3D12_BLEND_DESC desc_blend_state = {};
desc_blend_state.AlphaToCoverageEnable = FALSE;
desc_blend_state.IndependentBlendEnable = FALSE;
const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {
TRUE,
FALSE,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_LOGIC_OP_NOOP,
D3D12_COLOR_WRITE_ENABLE_ALL,
};
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc;
D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {};
desc_depth_stencil.DepthEnable = FALSE;
desc_depth_stencil.StencilEnable = TRUE;
desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
desc_depth_stencil.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK;
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {};
desc_pipeline.InputLayout = desc_input_layout;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_Always)];
desc_pipeline.VS = desc_bytecode_vertex_shader;
desc_pipeline.PS = desc_bytecode_pixel_shader;
desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
desc_pipeline.DSVFormat = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT;
desc_pipeline.SampleDesc = m_desc_sample;
desc_pipeline.SampleMask = 0xffffffff;
desc_pipeline.RasterizerState = desc_rasterizer;
desc_pipeline.BlendState = desc_blend_state;
desc_pipeline.DepthStencilState = desc_depth_stencil;
desc_pipeline.NumRenderTargets = 1;
status =
m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Color_Stencil_Always)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (color)");
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_INCR;
desc_depth_stencil.BackFace = desc_depth_stencil.FrontFace;
desc_pipeline.DepthStencilState = desc_depth_stencil;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_Intersect)];
desc_blend_state.AlphaToCoverageEnable = FALSE;
desc_blend_state.IndependentBlendEnable = FALSE;
const D3D12_RENDER_TARGET_BLEND_DESC editedRenderTargetBlend = {
TRUE,
FALSE,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_LOGIC_OP_NOOP,
0,
};
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
desc_blend_state.RenderTarget[i] = editedRenderTargetBlend;
desc_pipeline.BlendState = desc_blend_state;
status =
m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Color_Stencil_Intersect)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (color_stencil_intersect)");
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_REPLACE;
desc_depth_stencil.BackFace = desc_depth_stencil.FrontFace;
desc_pipeline.DepthStencilState = desc_depth_stencil;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_SetInverse)];
status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline,
IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Color_Stencil_SetInverse)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (color_stencil_setinverse)");
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_REPLACE;
desc_depth_stencil.BackFace = desc_depth_stencil.FrontFace;
desc_pipeline.DepthStencilState = desc_depth_stencil;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_Set)];
status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Color_Stencil_Set)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (color_stencil_set)");
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
desc_depth_stencil.BackFace = desc_depth_stencil.FrontFace;
desc_pipeline.DepthStencilState = desc_depth_stencil;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_Equal)];
desc_blend_state.AlphaToCoverageEnable = FALSE;
desc_blend_state.IndependentBlendEnable = FALSE;
const D3D12_RENDER_TARGET_BLEND_DESC editedRenderTargetBlend2 = {
TRUE,
FALSE,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_LOGIC_OP_NOOP,
D3D12_COLOR_WRITE_ENABLE_ALL,
};
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
desc_blend_state.RenderTarget[i] = editedRenderTargetBlend2;
desc_pipeline.BlendState = desc_blend_state;
status =
m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Color_Stencil_Equal)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Color_Stencil_Equal)");
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
desc_depth_stencil.BackFace = desc_depth_stencil.FrontFace;
desc_depth_stencil.StencilEnable = FALSE;
desc_pipeline.DepthStencilState = desc_depth_stencil;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_Disabled)];
desc_pipeline.BlendState = desc_blend_state;
status =
m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Color_Stencil_Disabled)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Color_Stencil_Disabled)");
#ifdef RMLUI_DX_DEBUG
m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_Always)]->SetName(TEXT("rs of Color_Stencil_Always"));
m_pipelines[static_cast<int>(ProgramId::Color_Stencil_Always)]->SetName(TEXT("pipeline Color_Stencil_Always"));
m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_Set)]->SetName(TEXT("rs of Color_Stencil_Set"));
m_pipelines[static_cast<int>(ProgramId::Color_Stencil_Set)]->SetName(TEXT("pipeline Color_Stencil_Set"));
m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_SetInverse)]->SetName(TEXT("rs of Color_Stencil_SetInverse"));
m_pipelines[static_cast<int>(ProgramId::Color_Stencil_SetInverse)]->SetName(TEXT("pipeline Color_Stencil_SetInverse"));
m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_Intersect)]->SetName(TEXT("rs of Color_Stencil_Intersect"));
m_pipelines[static_cast<int>(ProgramId::Color_Stencil_Intersect)]->SetName(TEXT("pipeline Color_Stencil_Intersect"));
m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_Equal)]->SetName(TEXT("rs of Color_Stencil_Equal"));
m_pipelines[static_cast<int>(ProgramId::Color_Stencil_Equal)]->SetName(TEXT("pipeline Color_Stencil_Equal"));
m_root_signatures[static_cast<int>(ProgramId::Color_Stencil_Disabled)]->SetName(TEXT("rs of Color_Stencil_Disabled"));
m_pipelines[static_cast<int>(ProgramId::Color_Stencil_Disabled)]->SetName(TEXT("pipeline Color_Stencil_Disabled"));
#endif
}
}
void RenderInterface_DX12::Create_Resource_Pipeline_Texture()
{
RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_Texture");
RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!");
if (m_p_device)
{
D3D12_DESCRIPTOR_RANGE ranges[1];
ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
ranges[0].NumDescriptors = 1;
ranges[0].BaseShaderRegister = 0;
ranges[0].RegisterSpace = 0;
ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
D3D12_ROOT_DESCRIPTOR_TABLE table{};
table.NumDescriptorRanges = sizeof(ranges) / sizeof(decltype(ranges[0]));
table.pDescriptorRanges = ranges;
D3D12_ROOT_DESCRIPTOR descriptor_cbv{};
descriptor_cbv.RegisterSpace = 0;
descriptor_cbv.ShaderRegister = 0;
D3D12_ROOT_PARAMETER parameters[2];
parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
parameters[1].DescriptorTable = table;
parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
parameters[0].Descriptor = descriptor_cbv;
parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
D3D12_STATIC_SAMPLER_DESC sampler = {};
sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.MipLODBias = 0;
sampler.MaxAnisotropy = 0;
sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
sampler.MinLOD = 0.0f;
sampler.MaxLOD = D3D12_FLOAT32_MAX;
sampler.ShaderRegister = 0;
sampler.RegisterSpace = 0;
sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_ROOT_SIGNATURE_DESC desc_rootsignature;
desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
desc_rootsignature.NumParameters = _countof(parameters);
desc_rootsignature.pParameters = parameters;
desc_rootsignature.pStaticSamplers = &sampler;
desc_rootsignature.NumStaticSamplers = 1;
ID3DBlob* p_signature{};
ID3DBlob* p_error{};
auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error);
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to D3D12SerializeRootSignature: %s",
(char*)p_error->GetBufferPointer());
}
#endif
RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Texture_Stencil_Always)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Texture_Stencil_Equal)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Texture_Stencil_Disabled)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
if (p_signature)
{
p_signature->Release();
p_signature = nullptr;
}
if (p_error)
{
p_error->Release();
p_error = nullptr;
}
ID3DBlob* p_shader_vertex{};
ID3DBlob* p_shader_pixel{};
ID3DBlob* p_error_buff{};
const D3D_SHADER_MACRO macros[] = {NULL, NULL, NULL, NULL};
status = D3DCompile(pShaderSourceText_Vertex, sizeof(pShaderSourceText_Vertex), nullptr, macros, nullptr, "main", "vs_5_0",
m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff);
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer());
}
#endif
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
status = D3DCompile(pShaderSourceText_Texture, sizeof(pShaderSourceText_Texture), nullptr, nullptr, nullptr, "main", "ps_5_0",
m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff);
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s",
(char*)(p_error_buff->GetBufferPointer()));
}
#endif
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {};
desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize();
desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer();
D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {};
desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize();
desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer();
D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}};
D3D12_INPUT_LAYOUT_DESC desc_input_layout = {};
desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC);
desc_input_layout.pInputElementDescs = desc_input_layout_elements;
D3D12_RASTERIZER_DESC desc_rasterizer = {};
desc_rasterizer.AntialiasedLineEnable = FALSE;
desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE;
desc_rasterizer.DepthBias = 0;
desc_rasterizer.DepthBiasClamp = 0;
desc_rasterizer.DepthClipEnable = FALSE;
desc_rasterizer.ForcedSampleCount = 0;
desc_rasterizer.FrontCounterClockwise = FALSE;
desc_rasterizer.MultisampleEnable = FALSE;
desc_rasterizer.FillMode = D3D12_FILL_MODE::D3D12_FILL_MODE_SOLID;
desc_rasterizer.SlopeScaledDepthBias = 0;
D3D12_BLEND_DESC desc_blend_state = {};
desc_blend_state.AlphaToCoverageEnable = FALSE;
desc_blend_state.IndependentBlendEnable = FALSE;
const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {
TRUE,
FALSE,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_LOGIC_OP_NOOP,
D3D12_COLOR_WRITE_ENABLE_ALL,
};
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc;
D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {};
desc_depth_stencil.DepthEnable = FALSE;
desc_depth_stencil.StencilEnable = TRUE;
desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
desc_depth_stencil.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK;
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {};
desc_pipeline.InputLayout = desc_input_layout;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Texture_Stencil_Always)];
desc_pipeline.VS = desc_bytecode_vertex_shader;
desc_pipeline.PS = desc_bytecode_pixel_shader;
desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
desc_pipeline.DSVFormat = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT;
desc_pipeline.SampleDesc = m_desc_sample;
desc_pipeline.SampleMask = 0xffffffff;
desc_pipeline.RasterizerState = desc_rasterizer;
desc_pipeline.BlendState = desc_blend_state;
desc_pipeline.NumRenderTargets = 1;
desc_pipeline.DepthStencilState = desc_depth_stencil;
status =
m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Texture_Stencil_Always)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Texture_Stencil_Always)");
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
desc_depth_stencil.BackFace = desc_depth_stencil.FrontFace;
desc_pipeline.DepthStencilState = desc_depth_stencil;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Texture_Stencil_Equal)];
status =
m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Texture_Stencil_Equal)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Texture_Stencil_Equal)");
desc_depth_stencil.StencilEnable = FALSE;
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
desc_depth_stencil.BackFace = desc_depth_stencil.FrontFace;
desc_pipeline.DepthStencilState = desc_depth_stencil;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Texture_Stencil_Disabled)];
status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline,
IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Texture_Stencil_Disabled)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Texture_Stencil_Disabled)");
#ifdef RMLUI_DX_DEBUG
m_root_signatures[static_cast<int>(ProgramId::Texture_Stencil_Always)]->SetName(TEXT("rs of Texture_Stencil_Always"));
m_pipelines[static_cast<int>(ProgramId::Texture_Stencil_Always)]->SetName(TEXT("pipeline Texture_Stencil_Always"));
m_root_signatures[static_cast<int>(ProgramId::Texture_Stencil_Equal)]->SetName(TEXT("rs of Texture_Stencil_Equal"));
m_pipelines[static_cast<int>(ProgramId::Texture_Stencil_Equal)]->SetName(TEXT("pipeline Texture_Stencil_Equal"));
m_root_signatures[static_cast<int>(ProgramId::Texture_Stencil_Disabled)]->SetName(TEXT("rs of Texture_Stencil_Disabled"));
m_pipelines[static_cast<int>(ProgramId::Texture_Stencil_Disabled)]->SetName(TEXT("pipeline Texture_Stencil_Disabled"));
#endif
}
}
void RenderInterface_DX12::Create_Resource_Pipeline_Gradient()
{
RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_Gradient");
RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!");
if (m_p_device)
{
D3D12_ROOT_DESCRIPTOR descriptor_cbv{};
descriptor_cbv.RegisterSpace = 0;
descriptor_cbv.ShaderRegister = 0;
D3D12_ROOT_PARAMETER parameters[2];
parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
parameters[0].Descriptor = descriptor_cbv;
parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
parameters[1].Descriptor = descriptor_cbv;
parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_ROOT_SIGNATURE_DESC desc_rootsignature;
desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
desc_rootsignature.NumStaticSamplers = 0;
desc_rootsignature.pParameters = parameters;
desc_rootsignature.NumParameters = _countof(parameters);
desc_rootsignature.pStaticSamplers = nullptr;
ID3DBlob* p_signature{};
ID3DBlob* p_error{};
auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error);
RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Gradient)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
if (p_signature)
{
p_signature->Release();
p_signature = nullptr;
}
if (p_error)
{
p_error->Release();
p_error = nullptr;
}
ID3DBlob* p_shader_vertex{};
ID3DBlob* p_shader_pixel{};
ID3DBlob* p_error_buff{};
status = D3DCompile(pShaderSourceText_Vertex, sizeof(pShaderSourceText_Vertex), nullptr, nullptr, nullptr, "main", "vs_5_0",
m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff);
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_ERROR, "failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer());
}
#endif
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
status = D3DCompile(pShaderSourceText_Pixel_Gradient, sizeof(pShaderSourceText_Pixel_Gradient), nullptr, nullptr, nullptr, "main", "ps_5_0",
m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff);
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_ERROR, "failed to compile shader: %s", (char*)(p_error_buff->GetBufferPointer()));
}
#endif
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {};
desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize();
desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer();
D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {};
desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize();
desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer();
D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}};
D3D12_INPUT_LAYOUT_DESC desc_input_layout = {};
desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC);
desc_input_layout.pInputElementDescs = desc_input_layout_elements;
D3D12_RASTERIZER_DESC desc_rasterizer = {};
desc_rasterizer.FillMode = D3D12_FILL_MODE_SOLID;
desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE;
desc_rasterizer.FrontCounterClockwise = FALSE;
desc_rasterizer.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
desc_rasterizer.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
desc_rasterizer.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
desc_rasterizer.DepthClipEnable = FALSE;
desc_rasterizer.MultisampleEnable = FALSE;
desc_rasterizer.AntialiasedLineEnable = FALSE;
desc_rasterizer.ForcedSampleCount = 0;
desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
D3D12_BLEND_DESC desc_blend_state = {};
desc_blend_state.AlphaToCoverageEnable = FALSE;
desc_blend_state.IndependentBlendEnable = FALSE;
const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {
TRUE,
FALSE,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_LOGIC_OP_NOOP,
D3D12_COLOR_WRITE_ENABLE_ALL,
};
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc;
D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {};
desc_depth_stencil.DepthEnable = FALSE;
desc_depth_stencil.StencilEnable = TRUE;
desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
desc_depth_stencil.StencilWriteMask = 0x0;
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {};
desc_pipeline.InputLayout = desc_input_layout;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Gradient)];
desc_pipeline.VS = desc_bytecode_vertex_shader;
desc_pipeline.PS = desc_bytecode_pixel_shader;
desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
desc_pipeline.DSVFormat = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT;
desc_pipeline.SampleDesc = m_desc_sample;
desc_pipeline.SampleMask = 0xffffffff;
desc_pipeline.RasterizerState = desc_rasterizer;
desc_pipeline.BlendState = desc_blend_state;
desc_pipeline.DepthStencilState = desc_depth_stencil;
desc_pipeline.NumRenderTargets = 1;
status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Gradient)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Gradient)");
#ifdef RMLUI_DX_DEBUG
m_root_signatures[static_cast<int>(ProgramId::Gradient)]->SetName(TEXT("rs of Gradient"));
m_pipelines[static_cast<int>(ProgramId::Gradient)]->SetName(TEXT("pipeline Gradient"));
#endif
}
}
void RenderInterface_DX12::Create_Resource_Pipeline_Creation()
{
RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_Creation");
RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!");
if (m_p_device)
{
D3D12_ROOT_DESCRIPTOR descriptor_cbv{};
descriptor_cbv.RegisterSpace = 0;
descriptor_cbv.ShaderRegister = 0;
D3D12_ROOT_PARAMETER parameters[2];
parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
parameters[0].Descriptor = descriptor_cbv;
parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
parameters[1].Descriptor = descriptor_cbv;
parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_ROOT_SIGNATURE_DESC desc_rootsignature;
desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
desc_rootsignature.NumStaticSamplers = 0;
desc_rootsignature.pParameters = parameters;
desc_rootsignature.NumParameters = _countof(parameters);
desc_rootsignature.pStaticSamplers = nullptr;
ID3DBlob* p_signature{};
ID3DBlob* p_error{};
auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error);
RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Creation)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
if (p_signature)
{
p_signature->Release();
p_signature = nullptr;
}
if (p_error)
{
p_error->Release();
p_error = nullptr;
}
ID3DBlob* p_shader_vertex{};
ID3DBlob* p_shader_pixel{};
ID3DBlob* p_error_buff{};
status = D3DCompile(pShaderSourceText_Vertex, sizeof(pShaderSourceText_Vertex), nullptr, nullptr, nullptr, "main", "vs_5_0",
m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff);
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_ERROR, "failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer());
}
#endif
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
status = D3DCompile(pShaderSourceText_Pixel_Creation, sizeof(pShaderSourceText_Pixel_Creation), nullptr, nullptr, nullptr, "main", "ps_5_0",
m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff);
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_ERROR, "failed to compile shader: %s", (char*)(p_error_buff->GetBufferPointer()));
}
#endif
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {};
desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize();
desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer();
D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {};
desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize();
desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer();
D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}};
D3D12_INPUT_LAYOUT_DESC desc_input_layout = {};
desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC);
desc_input_layout.pInputElementDescs = desc_input_layout_elements;
D3D12_RASTERIZER_DESC desc_rasterizer = {};
desc_rasterizer.FillMode = D3D12_FILL_MODE_SOLID;
desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE;
desc_rasterizer.FrontCounterClockwise = FALSE;
desc_rasterizer.DepthBias = D3D12_DEFAULT_DEPTH_BIAS;
desc_rasterizer.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP;
desc_rasterizer.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS;
desc_rasterizer.DepthClipEnable = FALSE;
desc_rasterizer.MultisampleEnable = FALSE;
desc_rasterizer.AntialiasedLineEnable = FALSE;
desc_rasterizer.ForcedSampleCount = 0;
desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
D3D12_BLEND_DESC desc_blend_state = {};
desc_blend_state.AlphaToCoverageEnable = FALSE;
desc_blend_state.IndependentBlendEnable = FALSE;
const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {
TRUE,
FALSE,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_LOGIC_OP_NOOP,
D3D12_COLOR_WRITE_ENABLE_ALL,
};
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc;
D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {};
desc_depth_stencil.DepthEnable = FALSE;
desc_depth_stencil.StencilEnable = TRUE;
desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
desc_depth_stencil.StencilWriteMask = 0x0;
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {};
desc_pipeline.InputLayout = desc_input_layout;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Creation)];
desc_pipeline.VS = desc_bytecode_vertex_shader;
desc_pipeline.PS = desc_bytecode_pixel_shader;
desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
desc_pipeline.DSVFormat = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT;
desc_pipeline.SampleDesc = m_desc_sample;
desc_pipeline.SampleMask = 0xffffffff;
desc_pipeline.RasterizerState = desc_rasterizer;
desc_pipeline.BlendState = desc_blend_state;
desc_pipeline.DepthStencilState = desc_depth_stencil;
desc_pipeline.NumRenderTargets = 1;
status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Creation)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Creation)");
#ifdef RMLUI_DX_DEBUG
m_root_signatures[static_cast<int>(ProgramId::Creation)]->SetName(TEXT("rs of Creation"));
m_pipelines[static_cast<int>(ProgramId::Creation)]->SetName(TEXT("pipeline Creation"));
#endif
}
}
void RenderInterface_DX12::Create_Resource_Pipeline_Passthrough()
{
RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_Passthrough");
RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!");
if (m_p_device)
{
D3D12_DESCRIPTOR_RANGE ranges[1];
ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
ranges[0].NumDescriptors = 1;
ranges[0].BaseShaderRegister = 0;
ranges[0].RegisterSpace = 0;
ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
D3D12_ROOT_DESCRIPTOR_TABLE table{};
table.NumDescriptorRanges = sizeof(ranges) / sizeof(decltype(ranges[0]));
table.pDescriptorRanges = ranges;
D3D12_ROOT_PARAMETER parameters[1];
parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
parameters[0].DescriptorTable = table;
parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_STATIC_SAMPLER_DESC sampler = {};
sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.MipLODBias = 0;
sampler.MaxAnisotropy = 0;
sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
sampler.MinLOD = 0.0f;
sampler.MaxLOD = D3D12_FLOAT32_MAX;
sampler.ShaderRegister = 0;
sampler.RegisterSpace = 0;
sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_ROOT_SIGNATURE_DESC desc_rootsignature;
desc_rootsignature.NumParameters = _countof(parameters);
desc_rootsignature.pParameters = parameters;
desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
desc_rootsignature.NumStaticSamplers = 1;
desc_rootsignature.pStaticSamplers = &sampler;
ID3DBlob* p_signature{};
ID3DBlob* p_error{};
auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error);
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to D3D12SerializeRootSignature: %s",
(char*)p_error->GetBufferPointer());
}
#endif
RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Passthrough)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Passthrough_NoDepthStencil)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Passthrough_Opacity)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Passthrough_MSAA)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Passthrough_MSAA_Equal)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
if (p_signature)
{
p_signature->Release();
p_signature = nullptr;
}
if (p_error)
{
p_error->Release();
p_error = nullptr;
}
ID3DBlob* p_shader_vertex{};
ID3DBlob* p_shader_pixel{};
ID3DBlob* p_error_buff{};
const D3D_SHADER_MACRO macros[] = {NULL, NULL, NULL, NULL};
status = D3DCompile(pShaderSourceText_Vertex_PassThrough, sizeof(pShaderSourceText_Vertex_PassThrough), nullptr, macros, nullptr, "main",
"vs_5_0", m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff);
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer());
}
#endif
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
status = D3DCompile(pShaderSourceText_Pixel_Passthrough, sizeof(pShaderSourceText_Pixel_Passthrough), nullptr, nullptr, nullptr, "main",
"ps_5_0", m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff);
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s",
(char*)(p_error_buff->GetBufferPointer()));
}
#endif
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {};
desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize();
desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer();
D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {};
desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize();
desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer();
D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}};
D3D12_INPUT_LAYOUT_DESC desc_input_layout = {};
desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC);
desc_input_layout.pInputElementDescs = desc_input_layout_elements;
D3D12_RASTERIZER_DESC desc_rasterizer = {};
desc_rasterizer.AntialiasedLineEnable = FALSE;
desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE;
desc_rasterizer.DepthBias = 0;
desc_rasterizer.DepthBiasClamp = 0;
desc_rasterizer.DepthClipEnable = FALSE;
desc_rasterizer.ForcedSampleCount = 0;
desc_rasterizer.FrontCounterClockwise = FALSE;
desc_rasterizer.MultisampleEnable = FALSE;
desc_rasterizer.FillMode = D3D12_FILL_MODE::D3D12_FILL_MODE_SOLID;
desc_rasterizer.SlopeScaledDepthBias = 0;
D3D12_BLEND_DESC desc_blend_state = {};
desc_blend_state.AlphaToCoverageEnable = FALSE;
desc_blend_state.IndependentBlendEnable = FALSE;
const D3D12_RENDER_TARGET_BLEND_DESC default_render_target_blend_desc = {
TRUE,
FALSE,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_LOGIC_OP_NOOP,
D3D12_COLOR_WRITE_ENABLE_ALL,
};
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
desc_blend_state.RenderTarget[i] = default_render_target_blend_desc;
D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {};
desc_depth_stencil.DepthEnable = FALSE;
desc_depth_stencil.StencilEnable = TRUE;
desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
desc_depth_stencil.StencilWriteMask = 0;
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {};
desc_pipeline.InputLayout = desc_input_layout;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Passthrough)];
desc_pipeline.VS = desc_bytecode_vertex_shader;
desc_pipeline.PS = desc_bytecode_pixel_shader;
desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
desc_pipeline.DSVFormat = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT;
// since it is used for presenting MSAA texture on screen, we create swapchain and all RTs as NO-MSAA, keep this in mind
// otherwise it is wrong to mix target texture with different sample count than swapchain's
desc_pipeline.SampleDesc.Count = 1;
desc_pipeline.SampleDesc.Quality = 0;
desc_pipeline.SampleMask = 0xffffffff;
desc_pipeline.RasterizerState = desc_rasterizer;
desc_pipeline.BlendState = desc_blend_state;
desc_pipeline.NumRenderTargets = 1;
desc_pipeline.DepthStencilState = desc_depth_stencil;
status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Passthrough)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Passthrough)");
#ifdef RMLUI_DX_DEBUG
m_root_signatures[static_cast<int>(ProgramId::Passthrough)]->SetName(TEXT("rs of Passthrough"));
m_pipelines[static_cast<int>(ProgramId::Passthrough)]->SetName(TEXT("pipeline Passthrough"));
#endif
desc_pipeline.SampleDesc = m_desc_sample;
desc_pipeline.DepthStencilState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_pipeline.DepthStencilState.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Passthrough_MSAA)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Passthrough_MSAA)");
desc_pipeline.DepthStencilState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
desc_pipeline.DepthStencilState.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
status =
m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Passthrough_MSAA_Equal)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Passthrough_MSAA_Equal)");
#ifdef RMLUI_DX_DEBUG
m_root_signatures[static_cast<int>(ProgramId::Passthrough_MSAA)]->SetName(TEXT("rs of Passthrough_MSAA"));
m_pipelines[static_cast<int>(ProgramId::Passthrough_MSAA)]->SetName(TEXT("pipeline Passthrough_MSAA"));
m_root_signatures[static_cast<int>(ProgramId::Passthrough_MSAA_Equal)]->SetName(TEXT("rs of Passthrough_MSAA_Equal"));
m_pipelines[static_cast<int>(ProgramId::Passthrough_MSAA_Equal)]->SetName(TEXT("pipeline Passthrough_MSAA_Equal"));
#endif
desc_pipeline.SampleDesc.Count = 1;
desc_pipeline.SampleDesc.Quality = 0;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Passthrough_NoDepthStencil)];
desc_pipeline.DSVFormat = DXGI_FORMAT::DXGI_FORMAT_UNKNOWN;
status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline,
IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Passthrough_NoDepthStencil)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPieplineState (Passthrough_NoDepthStencil)");
#ifdef RMLUI_DX_DEBUG
m_root_signatures[static_cast<int>(ProgramId::Passthrough_NoDepthStencil)]->SetName(TEXT("rs of Passthrough_NoDepthStencil"));
m_pipelines[static_cast<int>(ProgramId::Passthrough_NoDepthStencil)]->SetName(TEXT("pipeline Passthrough_NoDepthStencil"));
#endif
D3D12_RENDER_TARGET_BLEND_DESC opacity_blend_desc;
opacity_blend_desc.BlendEnable = TRUE;
opacity_blend_desc.LogicOpEnable = FALSE;
opacity_blend_desc.SrcBlend = D3D12_BLEND_BLEND_FACTOR;
opacity_blend_desc.DestBlend = D3D12_BLEND_ZERO;
opacity_blend_desc.BlendOp = D3D12_BLEND_OP_ADD;
opacity_blend_desc.SrcBlendAlpha = D3D12_BLEND_BLEND_FACTOR;
opacity_blend_desc.DestBlendAlpha = D3D12_BLEND_ZERO;
opacity_blend_desc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
opacity_blend_desc.LogicOp = D3D12_LOGIC_OP_NOOP;
opacity_blend_desc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
D3D12_BLEND_DESC desc_blend_state_opacity = {};
desc_blend_state_opacity.AlphaToCoverageEnable = FALSE;
desc_blend_state_opacity.IndependentBlendEnable = FALSE;
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
desc_blend_state_opacity.RenderTarget[i] = opacity_blend_desc;
desc_pipeline.BlendState = desc_blend_state_opacity;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Passthrough_Opacity)];
status =
m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Passthrough_Opacity)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Passthrough_Opacity)");
#ifdef RMLUI_DX_DEBUG
m_root_signatures[static_cast<int>(ProgramId::Passthrough_Opacity)]->SetName(TEXT("rs of Passthrough_Opacity"));
m_pipelines[static_cast<int>(ProgramId::Passthrough_Opacity)]->SetName(TEXT("pipeline Passthrough_Opacity"));
#endif
}
}
void RenderInterface_DX12::Create_Resource_Pipeline_Passthrough_NoBlend()
{
RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_Passthrough");
RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!");
if (m_p_device)
{
D3D12_DESCRIPTOR_RANGE ranges[1];
ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
ranges[0].NumDescriptors = 1;
ranges[0].BaseShaderRegister = 0;
ranges[0].RegisterSpace = 0;
ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
D3D12_ROOT_DESCRIPTOR_TABLE table{};
table.NumDescriptorRanges = sizeof(ranges) / sizeof(decltype(ranges[0]));
table.pDescriptorRanges = ranges;
D3D12_ROOT_PARAMETER parameters[1];
parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
parameters[0].DescriptorTable = table;
parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_STATIC_SAMPLER_DESC sampler = {};
sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.MipLODBias = 0;
sampler.MaxAnisotropy = 0;
sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
sampler.MinLOD = 0.0f;
sampler.MaxLOD = D3D12_FLOAT32_MAX;
sampler.ShaderRegister = 0;
sampler.RegisterSpace = 0;
sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_ROOT_SIGNATURE_DESC desc_rootsignature;
desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
desc_rootsignature.NumParameters = _countof(parameters);
desc_rootsignature.pParameters = parameters;
desc_rootsignature.NumStaticSamplers = 1;
desc_rootsignature.pStaticSamplers = &sampler;
ID3DBlob* p_signature{};
ID3DBlob* p_error{};
auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error);
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to D3D12SerializeRootSignature: %s",
(char*)p_error->GetBufferPointer());
}
#endif
RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Passthrough_NoBlend)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Passthrough_NoBlendAndNoMSAA)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
if (p_signature)
{
p_signature->Release();
p_signature = nullptr;
}
if (p_error)
{
p_error->Release();
p_error = nullptr;
}
ID3DBlob* p_shader_vertex{};
ID3DBlob* p_shader_pixel{};
ID3DBlob* p_error_buff{};
const D3D_SHADER_MACRO macros[] = {NULL, NULL, NULL, NULL};
status = D3DCompile(pShaderSourceText_Vertex_PassThrough, sizeof(pShaderSourceText_Vertex_PassThrough), nullptr, macros, nullptr, "main",
"vs_5_0", m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff);
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer());
}
#endif
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
status = D3DCompile(pShaderSourceText_Pixel_Passthrough, sizeof(pShaderSourceText_Pixel_Passthrough), nullptr, nullptr, nullptr, "main",
"ps_5_0", m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff);
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s",
(char*)(p_error_buff->GetBufferPointer()));
}
#endif
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {};
desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize();
desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer();
D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {};
desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize();
desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer();
D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}};
D3D12_INPUT_LAYOUT_DESC desc_input_layout = {};
desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC);
desc_input_layout.pInputElementDescs = desc_input_layout_elements;
D3D12_RASTERIZER_DESC desc_rasterizer = {};
desc_rasterizer.AntialiasedLineEnable = FALSE;
desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE;
desc_rasterizer.DepthBias = 0;
desc_rasterizer.DepthBiasClamp = 0;
desc_rasterizer.DepthClipEnable = FALSE;
desc_rasterizer.ForcedSampleCount = 0;
desc_rasterizer.FrontCounterClockwise = FALSE;
desc_rasterizer.MultisampleEnable = FALSE;
desc_rasterizer.FillMode = D3D12_FILL_MODE::D3D12_FILL_MODE_SOLID;
desc_rasterizer.SlopeScaledDepthBias = 0;
D3D12_BLEND_DESC desc_blend_state = {};
desc_blend_state.AlphaToCoverageEnable = FALSE;
desc_blend_state.IndependentBlendEnable = FALSE;
const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {
FALSE,
FALSE,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_LOGIC_OP_NOOP,
D3D12_COLOR_WRITE_ENABLE_ALL,
};
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc;
D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {};
desc_depth_stencil.DepthEnable = FALSE;
desc_depth_stencil.StencilEnable = TRUE;
desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
desc_depth_stencil.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK;
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {};
desc_pipeline.InputLayout = desc_input_layout;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Passthrough_NoBlend)];
desc_pipeline.VS = desc_bytecode_vertex_shader;
desc_pipeline.PS = desc_bytecode_pixel_shader;
desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
desc_pipeline.DSVFormat = DXGI_FORMAT::DXGI_FORMAT_UNKNOWN;
desc_pipeline.SampleDesc = m_desc_sample; // depends on layer texture and layer texture depends on msaa level that user specifies
desc_pipeline.SampleMask = 0xffffffff;
desc_pipeline.RasterizerState = desc_rasterizer;
desc_pipeline.BlendState = desc_blend_state;
desc_pipeline.NumRenderTargets = 1;
desc_pipeline.DepthStencilState = desc_depth_stencil;
status =
m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Passthrough_NoBlend)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Passthrough_NoBlend)");
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Passthrough_NoBlendAndNoMSAA)];
desc_pipeline.SampleDesc.Count = 1;
desc_pipeline.SampleDesc.Quality = 0;
status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline,
IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Passthrough_NoBlendAndNoMSAA)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Passthrough_NoBlendAndNoMSAA");
#ifdef RMLUI_DX_DEBUG
m_root_signatures[static_cast<int>(ProgramId::Passthrough_NoBlend)]->SetName(TEXT("rs of Passthrough_NoBlend"));
m_pipelines[static_cast<int>(ProgramId::Passthrough_NoBlend)]->SetName(TEXT("pipeline Passthrough_NoBlend"));
m_root_signatures[static_cast<int>(ProgramId::Passthrough_NoBlendAndNoMSAA)]->SetName(TEXT("rs of Passthrough_NoBlendAndNoMSAA"));
m_pipelines[static_cast<int>(ProgramId::Passthrough_NoBlendAndNoMSAA)]->SetName(TEXT("pipeline Passthrough_NoBlendAndNoMSAA"));
#endif
}
}
void RenderInterface_DX12::Create_Resource_Pipeline_ColorMatrix()
{
RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_ColorMatrix");
RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!");
if (m_p_device)
{
D3D12_DESCRIPTOR_RANGE ranges[1];
ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
ranges[0].NumDescriptors = 1;
ranges[0].BaseShaderRegister = 0;
ranges[0].RegisterSpace = 0;
ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
D3D12_ROOT_DESCRIPTOR_TABLE table{};
table.NumDescriptorRanges = sizeof(ranges) / sizeof(decltype(ranges[0]));
table.pDescriptorRanges = ranges;
D3D12_ROOT_DESCRIPTOR descriptor_cbv{};
descriptor_cbv.RegisterSpace = 0;
descriptor_cbv.ShaderRegister = 0;
D3D12_ROOT_PARAMETER parameters[2];
parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
parameters[0].DescriptorTable = table;
parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
parameters[1].Descriptor = descriptor_cbv;
parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_STATIC_SAMPLER_DESC sampler = {};
sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.MipLODBias = 0;
sampler.MaxAnisotropy = 0;
sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
sampler.MinLOD = 0.0f;
sampler.MaxLOD = D3D12_FLOAT32_MAX;
sampler.ShaderRegister = 0;
sampler.RegisterSpace = 0;
sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_ROOT_SIGNATURE_DESC desc_rootsignature;
desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
desc_rootsignature.NumParameters = _countof(parameters);
desc_rootsignature.pParameters = parameters;
desc_rootsignature.NumStaticSamplers = 1;
desc_rootsignature.pStaticSamplers = &sampler;
ID3DBlob* p_signature{};
ID3DBlob* p_error{};
auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error);
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to D3D12SerializeRootSignature: %s",
(char*)p_error->GetBufferPointer());
}
#endif
RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::ColorMatrix)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
if (p_signature)
{
p_signature->Release();
p_signature = nullptr;
}
if (p_error)
{
p_error->Release();
p_error = nullptr;
}
ID3DBlob* p_shader_vertex{};
ID3DBlob* p_shader_pixel{};
ID3DBlob* p_error_buff{};
const D3D_SHADER_MACRO macros[] = {NULL, NULL, NULL, NULL};
status = D3DCompile(pShaderSourceText_Vertex_PassThrough, sizeof(pShaderSourceText_Vertex_PassThrough), nullptr, macros, nullptr, "main",
"vs_5_0", m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff);
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer());
}
#endif
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
status = D3DCompile(pShaderSourceTet_Pixel_ColorMatrix, sizeof(pShaderSourceTet_Pixel_ColorMatrix), nullptr, nullptr, nullptr, "main",
"ps_5_0", m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff);
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s",
(char*)(p_error_buff->GetBufferPointer()));
}
#endif
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {};
desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize();
desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer();
D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {};
desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize();
desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer();
D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}};
D3D12_INPUT_LAYOUT_DESC desc_input_layout = {};
desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC);
desc_input_layout.pInputElementDescs = desc_input_layout_elements;
D3D12_RASTERIZER_DESC desc_rasterizer = {};
desc_rasterizer.AntialiasedLineEnable = FALSE;
desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE;
desc_rasterizer.DepthBias = 0;
desc_rasterizer.DepthBiasClamp = 0;
desc_rasterizer.DepthClipEnable = FALSE;
desc_rasterizer.ForcedSampleCount = 0;
desc_rasterizer.FrontCounterClockwise = FALSE;
desc_rasterizer.MultisampleEnable = FALSE;
desc_rasterizer.FillMode = D3D12_FILL_MODE::D3D12_FILL_MODE_SOLID;
desc_rasterizer.SlopeScaledDepthBias = 0;
D3D12_BLEND_DESC desc_blend_state = {};
desc_blend_state.AlphaToCoverageEnable = FALSE;
desc_blend_state.IndependentBlendEnable = FALSE;
const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {
FALSE,
FALSE,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_LOGIC_OP_NOOP,
D3D12_COLOR_WRITE_ENABLE_ALL,
};
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc;
D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {};
desc_depth_stencil.DepthEnable = FALSE;
desc_depth_stencil.StencilEnable = TRUE;
desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
desc_depth_stencil.StencilWriteMask = 0;
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {};
desc_pipeline.InputLayout = desc_input_layout;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::ColorMatrix)];
desc_pipeline.VS = desc_bytecode_vertex_shader;
desc_pipeline.PS = desc_bytecode_pixel_shader;
desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
desc_pipeline.DSVFormat = DXGI_FORMAT::DXGI_FORMAT_UNKNOWN;
// desc_pipeline.SampleDesc = m_desc_sample;
desc_pipeline.SampleDesc.Count = 1;
desc_pipeline.SampleDesc.Quality = 0;
desc_pipeline.SampleMask = 0xffffffff;
desc_pipeline.RasterizerState = desc_rasterizer;
desc_pipeline.BlendState = desc_blend_state;
desc_pipeline.NumRenderTargets = 1;
desc_pipeline.DepthStencilState = desc_depth_stencil;
status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::ColorMatrix)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (ColorMatrix)");
#ifdef RMLUI_DX_DEBUG
m_root_signatures[static_cast<int>(ProgramId::ColorMatrix)]->SetName(TEXT("rs of ColorMatrix"));
m_pipelines[static_cast<int>(ProgramId::ColorMatrix)]->SetName(TEXT("pipeline ColorMatrix"));
#endif
}
}
void RenderInterface_DX12::Create_Resource_Pipeline_BlendMask()
{
RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_BlendMask");
RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!");
if (m_p_device)
{
D3D12_DESCRIPTOR_RANGE ranges[1];
ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
ranges[0].NumDescriptors = 1;
ranges[0].BaseShaderRegister = 0;
ranges[0].RegisterSpace = 0;
ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
D3D12_ROOT_DESCRIPTOR_TABLE table{};
table.NumDescriptorRanges = sizeof(ranges) / sizeof(decltype(ranges[0]));
table.pDescriptorRanges = ranges;
D3D12_ROOT_PARAMETER parameters[2];
parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
parameters[0].DescriptorTable = table;
parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_DESCRIPTOR_RANGE slot2_ranges[1];
slot2_ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
slot2_ranges[0].NumDescriptors = 1;
slot2_ranges[0].BaseShaderRegister = 1;
slot2_ranges[0].RegisterSpace = 0;
slot2_ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
D3D12_ROOT_DESCRIPTOR_TABLE slot2_table{};
slot2_table.NumDescriptorRanges = sizeof(slot2_ranges) / sizeof(decltype(slot2_ranges[0]));
slot2_table.pDescriptorRanges = slot2_ranges;
parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
parameters[1].DescriptorTable = slot2_table;
parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_STATIC_SAMPLER_DESC sampler = {};
sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.MipLODBias = 0;
sampler.MaxAnisotropy = 0;
sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
sampler.MinLOD = 0.0f;
sampler.MaxLOD = D3D12_FLOAT32_MAX;
sampler.ShaderRegister = 0;
sampler.RegisterSpace = 0;
sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_ROOT_SIGNATURE_DESC desc_rootsignature;
desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
desc_rootsignature.NumParameters = _countof(parameters);
desc_rootsignature.pParameters = parameters;
desc_rootsignature.NumStaticSamplers = 1;
desc_rootsignature.pStaticSamplers = &sampler;
ID3DBlob* p_signature{};
ID3DBlob* p_error{};
auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error);
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to D3D12SerializeRootSignature: %s",
(char*)p_error->GetBufferPointer());
}
#endif
RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::BlendMask)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
if (p_signature)
{
p_signature->Release();
p_signature = nullptr;
}
if (p_error)
{
p_error->Release();
p_error = nullptr;
}
ID3DBlob* p_shader_vertex{};
ID3DBlob* p_shader_pixel{};
ID3DBlob* p_error_buff{};
const D3D_SHADER_MACRO macros[] = {NULL, NULL, NULL, NULL};
status = D3DCompile(pShaderSourceText_Vertex_PassThrough, sizeof(pShaderSourceText_Vertex_PassThrough), nullptr, macros, nullptr, "main",
"vs_5_0", m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff);
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer());
}
#endif
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
status = D3DCompile(pShaderSourceText_Pixel_BlendMask, sizeof(pShaderSourceText_Pixel_BlendMask), nullptr, nullptr, nullptr, "main", "ps_5_0",
m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff);
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s",
(char*)(p_error_buff->GetBufferPointer()));
}
#endif
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {};
desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize();
desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer();
D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {};
desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize();
desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer();
D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}};
D3D12_INPUT_LAYOUT_DESC desc_input_layout = {};
desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC);
desc_input_layout.pInputElementDescs = desc_input_layout_elements;
D3D12_RASTERIZER_DESC desc_rasterizer = {};
desc_rasterizer.AntialiasedLineEnable = FALSE;
desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE;
desc_rasterizer.DepthBias = 0;
desc_rasterizer.DepthBiasClamp = 0;
desc_rasterizer.DepthClipEnable = FALSE;
desc_rasterizer.ForcedSampleCount = 0;
desc_rasterizer.FrontCounterClockwise = FALSE;
desc_rasterizer.MultisampleEnable = FALSE;
desc_rasterizer.FillMode = D3D12_FILL_MODE::D3D12_FILL_MODE_SOLID;
desc_rasterizer.SlopeScaledDepthBias = 0;
D3D12_BLEND_DESC desc_blend_state = {};
desc_blend_state.AlphaToCoverageEnable = FALSE;
desc_blend_state.IndependentBlendEnable = FALSE;
const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {
FALSE,
FALSE,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_LOGIC_OP_NOOP,
D3D12_COLOR_WRITE_ENABLE_ALL,
};
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc;
D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {};
desc_depth_stencil.DepthEnable = FALSE;
desc_depth_stencil.StencilEnable = TRUE;
desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
desc_depth_stencil.StencilWriteMask = 0;
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {};
desc_pipeline.InputLayout = desc_input_layout;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::BlendMask)];
desc_pipeline.VS = desc_bytecode_vertex_shader;
desc_pipeline.PS = desc_bytecode_pixel_shader;
desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
desc_pipeline.DSVFormat = DXGI_FORMAT::DXGI_FORMAT_UNKNOWN;
// desc_pipeline.SampleDesc = m_desc_sample;
desc_pipeline.SampleDesc.Count = 1;
desc_pipeline.SampleDesc.Quality = 0;
desc_pipeline.SampleMask = 0xffffffff;
desc_pipeline.RasterizerState = desc_rasterizer;
desc_pipeline.BlendState = desc_blend_state;
desc_pipeline.NumRenderTargets = 1;
desc_pipeline.DepthStencilState = desc_depth_stencil;
status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::BlendMask)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (BlendMask)");
#ifdef RMLUI_DX_DEBUG
m_root_signatures[static_cast<int>(ProgramId::BlendMask)]->SetName(TEXT("rs of BlendMask"));
m_pipelines[static_cast<int>(ProgramId::BlendMask)]->SetName(TEXT("pipeline BlendMask"));
#endif
}
}
void RenderInterface_DX12::Create_Resource_Pipeline_Blur()
{
RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_Blur");
RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!");
if (m_p_device)
{
D3D12_DESCRIPTOR_RANGE ranges[1];
ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
ranges[0].NumDescriptors = 1;
ranges[0].BaseShaderRegister = 0;
ranges[0].RegisterSpace = 0;
ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
D3D12_ROOT_DESCRIPTOR_TABLE table{};
table.NumDescriptorRanges = sizeof(ranges) / sizeof(decltype(ranges[0]));
table.pDescriptorRanges = ranges;
D3D12_ROOT_DESCRIPTOR descriptor_cbv{};
descriptor_cbv.RegisterSpace = 0;
descriptor_cbv.ShaderRegister = 0;
D3D12_ROOT_PARAMETER parameters[3];
parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
parameters[0].DescriptorTable = table;
parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
parameters[1].Descriptor = descriptor_cbv;
parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
parameters[2].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
parameters[2].Descriptor = descriptor_cbv;
parameters[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_STATIC_SAMPLER_DESC sampler = {};
sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.MipLODBias = 0;
sampler.MaxAnisotropy = 0;
sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
sampler.MinLOD = 0.0f;
sampler.MaxLOD = D3D12_FLOAT32_MAX;
sampler.ShaderRegister = 0;
sampler.RegisterSpace = 0;
sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_ROOT_SIGNATURE_DESC desc_rootsignature;
desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
desc_rootsignature.NumParameters = _countof(parameters);
desc_rootsignature.pParameters = parameters;
desc_rootsignature.NumStaticSamplers = 1;
desc_rootsignature.pStaticSamplers = &sampler;
ID3DBlob* p_signature{};
ID3DBlob* p_error{};
auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error);
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to D3D12SerializeRootSignature: %s",
(char*)p_error->GetBufferPointer());
}
#endif
RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::Blur)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
if (p_signature)
{
p_signature->Release();
p_signature = nullptr;
}
if (p_error)
{
p_error->Release();
p_error = nullptr;
}
ID3DBlob* p_shader_vertex{};
ID3DBlob* p_shader_pixel{};
ID3DBlob* p_error_buff{};
const D3D_SHADER_MACRO macros[] = {NULL, NULL, NULL, NULL};
status = D3DCompile(pShaderSourceText_Vertex_Blur, sizeof(pShaderSourceText_Vertex_Blur), nullptr, macros, nullptr, "main", "vs_5_0",
m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff);
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer());
}
#endif
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
status = D3DCompile(pShaderSourceText_Pixel_Blur, sizeof(pShaderSourceText_Pixel_Blur), nullptr, nullptr, nullptr, "main", "ps_5_0",
m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff);
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s",
(char*)(p_error_buff->GetBufferPointer()));
}
#endif
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {};
desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize();
desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer();
D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {};
desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize();
desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer();
D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}};
D3D12_INPUT_LAYOUT_DESC desc_input_layout = {};
desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC);
desc_input_layout.pInputElementDescs = desc_input_layout_elements;
D3D12_RASTERIZER_DESC desc_rasterizer = {};
desc_rasterizer.AntialiasedLineEnable = FALSE;
desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE;
desc_rasterizer.DepthBias = 0;
desc_rasterizer.DepthBiasClamp = 0;
desc_rasterizer.DepthClipEnable = FALSE;
desc_rasterizer.ForcedSampleCount = 0;
desc_rasterizer.FrontCounterClockwise = FALSE;
desc_rasterizer.MultisampleEnable = FALSE;
desc_rasterizer.FillMode = D3D12_FILL_MODE::D3D12_FILL_MODE_SOLID;
desc_rasterizer.SlopeScaledDepthBias = 0;
D3D12_BLEND_DESC desc_blend_state = {};
desc_blend_state.AlphaToCoverageEnable = FALSE;
desc_blend_state.IndependentBlendEnable = FALSE;
const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {
FALSE,
FALSE,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_LOGIC_OP_NOOP,
D3D12_COLOR_WRITE_ENABLE_ALL,
};
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc;
D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {};
desc_depth_stencil.DepthEnable = FALSE;
desc_depth_stencil.StencilEnable = TRUE;
desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
desc_depth_stencil.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK;
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {};
desc_pipeline.InputLayout = desc_input_layout;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::Blur)];
desc_pipeline.VS = desc_bytecode_vertex_shader;
desc_pipeline.PS = desc_bytecode_pixel_shader;
desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
desc_pipeline.DSVFormat = DXGI_FORMAT::DXGI_FORMAT_UNKNOWN;
// desc_pipeline.SampleDesc = m_desc_sample;
desc_pipeline.SampleDesc.Count = 1;
desc_pipeline.SampleDesc.Quality = 0;
desc_pipeline.SampleMask = 0xffffffff;
desc_pipeline.RasterizerState = desc_rasterizer;
desc_pipeline.BlendState = desc_blend_state;
desc_pipeline.NumRenderTargets = 1;
desc_pipeline.DepthStencilState = desc_depth_stencil;
status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::Blur)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (Blur)");
#ifdef RMLUI_DX_DEBUG
m_root_signatures[static_cast<int>(ProgramId::Blur)]->SetName(TEXT("rs of Blur"));
m_pipelines[static_cast<int>(ProgramId::Blur)]->SetName(TEXT("pipeline Blur"));
#endif
}
}
void RenderInterface_DX12::Create_Resource_Pipeline_DropShadow()
{
RMLUI_ZoneScopedN("DirectX 12 - Create_Resource_Pipeline_DropShadow");
RMLUI_ASSERTMSG(m_p_device, "must be valid when we call this method!");
if (m_p_device)
{
D3D12_DESCRIPTOR_RANGE ranges[1];
ranges[0].RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV;
ranges[0].NumDescriptors = 1;
ranges[0].BaseShaderRegister = 0;
ranges[0].RegisterSpace = 0;
ranges[0].OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND;
D3D12_ROOT_DESCRIPTOR_TABLE table{};
table.NumDescriptorRanges = sizeof(ranges) / sizeof(decltype(ranges[0]));
table.pDescriptorRanges = ranges;
D3D12_ROOT_DESCRIPTOR descriptor_cbv{};
descriptor_cbv.RegisterSpace = 0;
descriptor_cbv.ShaderRegister = 0;
D3D12_ROOT_PARAMETER parameters[2];
parameters[0].ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE;
parameters[0].DescriptorTable = table;
parameters[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
parameters[1].ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
parameters[1].Descriptor = descriptor_cbv;
parameters[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_STATIC_SAMPLER_DESC sampler = {};
sampler.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR;
sampler.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP;
sampler.MipLODBias = 0;
sampler.MaxAnisotropy = 0;
sampler.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER;
sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK;
sampler.MinLOD = 0.0f;
sampler.MaxLOD = D3D12_FLOAT32_MAX;
sampler.ShaderRegister = 0;
sampler.RegisterSpace = 0;
sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL;
D3D12_ROOT_SIGNATURE_DESC desc_rootsignature;
desc_rootsignature.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |
D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS |
D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS;
desc_rootsignature.NumParameters = _countof(parameters);
desc_rootsignature.pParameters = parameters;
desc_rootsignature.NumStaticSamplers = 1;
desc_rootsignature.pStaticSamplers = &sampler;
ID3DBlob* p_signature{};
ID3DBlob* p_error{};
auto status = D3D12SerializeRootSignature(&desc_rootsignature, D3D_ROOT_SIGNATURE_VERSION_1, &p_signature, &p_error);
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to D3D12SerializeRootSignature: %s",
(char*)p_error->GetBufferPointer());
}
#endif
RMLUI_DX_VERIFY_MSG(status, "failed to D3D12SerializeRootSignature");
status = m_p_device->CreateRootSignature(0, p_signature->GetBufferPointer(), p_signature->GetBufferSize(),
IID_PPV_ARGS(&m_root_signatures[static_cast<int>(ProgramId::DropShadow)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateRootSignature");
if (p_signature)
{
p_signature->Release();
p_signature = nullptr;
}
if (p_error)
{
p_error->Release();
p_error = nullptr;
}
ID3DBlob* p_shader_vertex{};
ID3DBlob* p_shader_pixel{};
ID3DBlob* p_error_buff{};
const D3D_SHADER_MACRO macros[] = {NULL, NULL, NULL, NULL};
status = D3DCompile(pShaderSourceText_Vertex_PassThrough, sizeof(pShaderSourceText_Vertex_PassThrough), nullptr, macros, nullptr, "main",
"vs_5_0", m_default_shader_flags, 0, &p_shader_vertex, &p_error_buff);
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s", (char*)p_error_buff->GetBufferPointer());
}
#endif
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
status = D3DCompile(pShaderSourceText_Pixel_DropShadow, sizeof(pShaderSourceText_Pixel_DropShadow), nullptr, nullptr, nullptr, "main",
"ps_5_0", m_default_shader_flags, 0, &p_shader_pixel, &p_error_buff);
#ifdef RMLUI_DX_DEBUG
if (FAILED(status))
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12][ERROR] failed to compile shader: %s",
(char*)(p_error_buff->GetBufferPointer()));
}
#endif
RMLUI_DX_VERIFY_MSG(status, "failed to D3DCompile");
if (p_error_buff)
{
p_error_buff->Release();
p_error_buff = nullptr;
}
D3D12_SHADER_BYTECODE desc_bytecode_pixel_shader = {};
desc_bytecode_pixel_shader.BytecodeLength = p_shader_pixel->GetBufferSize();
desc_bytecode_pixel_shader.pShaderBytecode = p_shader_pixel->GetBufferPointer();
D3D12_SHADER_BYTECODE desc_bytecode_vertex_shader = {};
desc_bytecode_vertex_shader.BytecodeLength = p_shader_vertex->GetBufferSize();
desc_bytecode_vertex_shader.pShaderBytecode = p_shader_vertex->GetBufferPointer();
D3D12_INPUT_ELEMENT_DESC desc_input_layout_elements[] = {
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, 8, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}};
D3D12_INPUT_LAYOUT_DESC desc_input_layout = {};
desc_input_layout.NumElements = sizeof(desc_input_layout_elements) / sizeof(D3D12_INPUT_ELEMENT_DESC);
desc_input_layout.pInputElementDescs = desc_input_layout_elements;
D3D12_RASTERIZER_DESC desc_rasterizer = {};
desc_rasterizer.AntialiasedLineEnable = FALSE;
desc_rasterizer.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF;
desc_rasterizer.CullMode = D3D12_CULL_MODE_NONE;
desc_rasterizer.DepthBias = 0;
desc_rasterizer.DepthBiasClamp = 0;
desc_rasterizer.DepthClipEnable = FALSE;
desc_rasterizer.ForcedSampleCount = 0;
desc_rasterizer.FrontCounterClockwise = FALSE;
desc_rasterizer.MultisampleEnable = FALSE;
desc_rasterizer.FillMode = D3D12_FILL_MODE::D3D12_FILL_MODE_SOLID;
desc_rasterizer.SlopeScaledDepthBias = 0;
D3D12_BLEND_DESC desc_blend_state = {};
desc_blend_state.AlphaToCoverageEnable = FALSE;
desc_blend_state.IndependentBlendEnable = FALSE;
const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {
FALSE,
FALSE,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_BLEND_ONE,
D3D12_BLEND_INV_SRC_ALPHA,
D3D12_BLEND_OP_ADD,
D3D12_LOGIC_OP_NOOP,
D3D12_COLOR_WRITE_ENABLE_ALL,
};
for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i)
desc_blend_state.RenderTarget[i] = defaultRenderTargetBlendDesc;
D3D12_DEPTH_STENCIL_DESC desc_depth_stencil = {};
desc_depth_stencil.DepthEnable = FALSE;
desc_depth_stencil.StencilEnable = TRUE;
desc_depth_stencil.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
desc_depth_stencil.DepthFunc = D3D12_COMPARISON_FUNC_ALWAYS;
desc_depth_stencil.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK;
desc_depth_stencil.StencilWriteMask = 0;
desc_depth_stencil.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
desc_depth_stencil.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFailOp = D3D12_STENCIL_OP_KEEP;
desc_depth_stencil.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL;
desc_depth_stencil.BackFace.StencilPassOp = D3D12_STENCIL_OP_KEEP;
D3D12_GRAPHICS_PIPELINE_STATE_DESC desc_pipeline = {};
desc_pipeline.InputLayout = desc_input_layout;
desc_pipeline.pRootSignature = m_root_signatures[static_cast<int>(ProgramId::DropShadow)];
desc_pipeline.VS = desc_bytecode_vertex_shader;
desc_pipeline.PS = desc_bytecode_pixel_shader;
desc_pipeline.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
desc_pipeline.RTVFormats[0] = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
desc_pipeline.DSVFormat = DXGI_FORMAT::DXGI_FORMAT_UNKNOWN;
// desc_pipeline.SampleDesc = m_desc_sample;
desc_pipeline.SampleDesc.Count = 1;
desc_pipeline.SampleDesc.Quality = 0;
desc_pipeline.SampleMask = 0xffffffff;
desc_pipeline.RasterizerState = desc_rasterizer;
desc_pipeline.BlendState = desc_blend_state;
desc_pipeline.NumRenderTargets = 1;
desc_pipeline.DepthStencilState = desc_depth_stencil;
status = m_p_device->CreateGraphicsPipelineState(&desc_pipeline, IID_PPV_ARGS(&m_pipelines[static_cast<int>(ProgramId::DropShadow)]));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateGraphicsPipelineState (DropShadow)");
#ifdef RMLUI_DX_DEBUG
m_root_signatures[static_cast<int>(ProgramId::DropShadow)]->SetName(TEXT("rs of DropShadow"));
m_pipelines[static_cast<int>(ProgramId::DropShadow)]->SetName(TEXT("pipeline DropShadow"));
#endif
}
}
void RenderInterface_DX12::Destroy_Resource_Pipelines()
{
RMLUI_ZoneScopedN("DirectX 12 - Destroy_Resource_Pipelines");
Destroy_Resource_For_Shaders();
if (m_p_depthstencil_resource)
{
Destroy_Resource_DepthStencil();
}
for (int i = 1; i < static_cast<int>(ProgramId::Count); ++i)
{
if (m_pipelines[i])
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_pipelines[i]->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
m_pipelines[i] = nullptr;
}
if (m_root_signatures[i])
{
m_root_signatures[i]->Release();
m_root_signatures[i] = nullptr;
}
}
}
bool RenderInterface_DX12::CheckTearingSupport() noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - CheckTearingSupport");
int result{};
IDXGIFactory4* pFactory4{};
if (SUCCEEDED(CreateDXGIFactory1(IID_PPV_ARGS(&pFactory4))))
{
IDXGIFactory5* pFactory5{};
if (SUCCEEDED(pFactory4->QueryInterface(IID_PPV_ARGS(&pFactory5))))
{
if (FAILED(pFactory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &result, sizeof(result))))
{
result = 0;
}
if (pFactory5)
{
pFactory5->Release();
}
}
}
if (pFactory4)
{
pFactory4->Release();
}
return result == 1;
}
IDXGIAdapter* RenderInterface_DX12::Get_Adapter(bool is_use_warp) noexcept
{
RMLUI_ZoneScopedN("DirectX 12 - Get_Adapter");
IDXGIFactory4* p_factory{};
uint32_t create_flags{};
#ifdef RMLUI_DX_DEBUG
create_flags = DXGI_CREATE_FACTORY_DEBUG;
#endif
RMLUI_DX_VERIFY_MSG(CreateDXGIFactory2(create_flags, IID_PPV_ARGS(&p_factory)), "failed to CreateDXGIFactory2");
IDXGIAdapter* p_adapter{};
IDXGIAdapter1* p_adapter1{};
IDXGIAdapter4* p_adapter4{};
if (is_use_warp)
{
if (p_factory)
{
RMLUI_DX_VERIFY_MSG(p_factory->EnumWarpAdapter(IID_PPV_ARGS(&p_adapter)), "failed to EnumAdapters");
RMLUI_ASSERTMSG(p_adapter, "returned invalid COM object from EnumWarpAdapter");
if (p_adapter)
{
p_adapter->QueryInterface(IID_PPV_ARGS(&p_adapter4));
RMLUI_ASSERTMSG(p_adapter4, "returned invalid COM object from QueryInterface");
PrintAdapterDesc(p_adapter);
}
}
}
else
{
if (p_factory)
{
size_t max_dedicated_video_memory{};
for (uint32_t i = 0; p_factory->EnumAdapters(i, &p_adapter) != DXGI_ERROR_NOT_FOUND; ++i)
{
if (p_adapter)
{
DXGI_ADAPTER_DESC1 desc;
RMLUI_DX_VERIFY_MSG(p_adapter->QueryInterface(IID_PPV_ARGS(&p_adapter1)), "failed to QueryInterface of IDXGIAdapter1");
if (p_adapter1)
{
p_adapter1->GetDesc1(&desc);
ID3D12Device* p_test_device{};
if ((desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) == 0 &&
SUCCEEDED(D3D12CreateDevice(p_adapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&p_test_device))) &&
desc.DedicatedVideoMemory > max_dedicated_video_memory)
{
max_dedicated_video_memory = desc.DedicatedVideoMemory;
// found 'device' with bigger memory means we found our discrete video card (not cpu integrated)
if (p_adapter4)
{
p_adapter4->Release();
p_adapter4 = nullptr;
}
RMLUI_DX_VERIFY_MSG(p_adapter->QueryInterface(IID_PPV_ARGS(&p_adapter4)), "failed to QueryInterface of IDXGIAdapter4");
PrintAdapterDesc(p_adapter);
}
if (p_test_device)
{
p_test_device->Release();
}
}
if (p_adapter)
{
p_adapter->Release();
}
if (p_adapter1)
{
p_adapter1->Release();
p_adapter1 = nullptr;
}
}
}
}
}
if (p_factory)
{
p_factory->Release();
}
if (p_adapter)
{
p_adapter->Release();
}
if (p_adapter1)
{
p_adapter1->Release();
}
return p_adapter4;
}
void RenderInterface_DX12::PrintAdapterDesc(IDXGIAdapter* p_adapter)
{
RMLUI_ZoneScopedN("DirectX 12 - PrintAdapterDesc");
RMLUI_ASSERTMSG(p_adapter, "you can't pass invalid argument");
if (p_adapter)
{
DXGI_ADAPTER_DESC desc;
p_adapter->GetDesc(&desc);
char p_converted[_countof(desc.Description)];
memset(p_converted, 0, sizeof(p_converted));
#ifdef UNICODE
sprintf(p_converted, "%ls", desc.Description);
#else
p_converted = desc.Description;
#endif
float vid_mem_in_bytes = static_cast<float>(desc.DedicatedVideoMemory);
float vid_mem_in_kilobytes = vid_mem_in_bytes / 1024.0f;
float vid_mem_in_megabytes = vid_mem_in_kilobytes / 1024.0f;
float vid_mem_in_gigabytes = vid_mem_in_megabytes / 1024.0f;
Rml::Log::Message(Rml::Log::LT_INFO, "Monitor[%s]\n VideoMemory[%f Bytes][%f Mbs][%f Gbs]\n", p_converted, vid_mem_in_bytes,
vid_mem_in_megabytes, vid_mem_in_gigabytes);
}
}
void RenderInterface_DX12::SetScissor(Rml::Rectanglei region, bool vertically_flip)
{
RMLUI_ZoneScopedN("DirectX 12 - SetScissor");
if (region.Valid() != m_scissor.Valid())
{
if (!region.Valid())
{
m_is_scissor_was_set = false;
if (m_p_command_graphics_list)
{
D3D12_RECT disable_scissor = {};
disable_scissor.left = 0;
disable_scissor.top = 0;
disable_scissor.right = m_width;
disable_scissor.bottom = m_height;
m_p_command_graphics_list->RSSetScissorRects(1, &disable_scissor);
}
return;
}
}
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, "SetScissor");
if (region.Valid() && vertically_flip)
{
region = VerticallyFlipped(region, m_height);
}
if (region.Valid())
{
if (m_p_command_graphics_list)
{
D3D12_RECT scissor;
const int x_min = Rml::Math::Clamp(region.Left(), 0, m_width);
const int y_min = Rml::Math::Clamp(region.Top(), 0, m_height);
const int x_max = Rml::Math::Clamp(region.Right(), 0, m_width);
const int y_max = Rml::Math::Clamp(region.Bottom(), 0, m_height);
scissor.left = x_min;
scissor.right = x_max;
scissor.bottom = y_max;
scissor.top = y_min;
m_p_command_graphics_list->RSSetScissorRects(1, &scissor);
m_is_scissor_was_set = true;
}
}
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
m_scissor = region;
}
void RenderInterface_DX12::SubmitTransformUniform(ConstantBufferType& constant_buffer, const Rml::Vector2f& translation)
{
RMLUI_ZoneScopedN("DirectX 12 - SubmitTransformUniform");
static_assert((size_t)ProgramId::Count < RMLUI_RENDER_BACKEND_FIELD_MAXNUMPROGRAMS, "Maximum number of pipelines exceeded");
std::uint8_t* p_gpu_binding_start = reinterpret_cast<std::uint8_t*>(constant_buffer.m_p_gpu_start_memory_for_binding_data);
{
RMLUI_ASSERTMSG(p_gpu_binding_start,
"your allocated constant buffer must contain a valid pointer of beginning mapping of its GPU buffer. Otherwise you destroyed it!");
if (p_gpu_binding_start)
{
std::uint8_t* p_gpu_binding_offset_to_transform = p_gpu_binding_start + constant_buffer.m_alloc_info.offset;
std::memcpy(p_gpu_binding_offset_to_transform, m_constant_buffer_data_transform.data(), sizeof(m_constant_buffer_data_transform));
}
}
if (p_gpu_binding_start)
{
std::uint8_t* p_gpu_binding_offset_to_translate =
p_gpu_binding_start + (constant_buffer.m_alloc_info.offset + sizeof(m_constant_buffer_data_transform));
std::memcpy(p_gpu_binding_offset_to_translate, &translation.x, sizeof(translation));
}
}
constexpr const char* translate_programId(ProgramId id)
{
switch (id)
{
case ProgramId::None: return "None";
case ProgramId::Color_Stencil_Always: return "Color_Stencil_Always";
case ProgramId::Color_Stencil_Equal: return "Color_Stencil_Equal";
case ProgramId::Color_Stencil_Set: return "Color_Stencil_Set";
case ProgramId::Color_Stencil_SetInverse: return "Color_Stencil_SetInverse";
case ProgramId::Color_Stencil_Intersect: return "Color_Stencil_Intersect";
case ProgramId::Color_Stencil_Disabled: return "Color_Stencil_Disabled";
case ProgramId::Texture_Stencil_Always: return "Texture_Stencil_Always";
case ProgramId::Texture_Stencil_Equal: return "Texture_Stencil_Equal";
case ProgramId::Texture_Stencil_Disabled: return "Texture_Stencil_Disabled";
case ProgramId::Gradient: return "Gradient";
case ProgramId::Creation: return "Creation";
case ProgramId::Passthrough: return "Passthrough";
case ProgramId::Passthrough_NoDepthStencil: return "Passthrough_NoDepthStencil";
case ProgramId::Passthrough_Opacity: return "Passthrough_Opacity";
case ProgramId::Passthrough_MSAA: return "Passthrough_MSAA";
case ProgramId::Passthrough_MSAA_Equal: return "Passthrough_MSAA_Equal";
case ProgramId::Passthrough_NoBlend: return "Passthrough_NoBlend";
case ProgramId::Passthrough_NoBlendAndNoMSAA: return "Passthrough_NoBlendAndNoMSAA";
case ProgramId::ColorMatrix: return "ColorMatrix";
case ProgramId::BlendMask: return "BlendMask";
case ProgramId::Blur: return "Blur";
case ProgramId::DropShadow: return "DropShadow";
case ProgramId::Count: return "Count";
default: return "UNKNOWN_PROGRAMID";
}
}
void RenderInterface_DX12::UseProgram(ProgramId pipeline_id)
{
RMLUI_ZoneScopedN("DirectX 12 - UseProgram");
RMLUI_ASSERTMSG(pipeline_id < ProgramId::Count, "overflow, too big value for indexing");
#ifdef RMLUI_DX_DEBUG
char msg[64];
std::sprintf(msg, "UseProgram = %s", translate_programId(pipeline_id));
#endif
RMLUI_DX_MARKER_BEGIN(m_p_command_graphics_list, msg);
if (pipeline_id != ProgramId::None)
{
RMLUI_ASSERTMSG(m_pipelines[static_cast<int>(pipeline_id)], "you forgot to initialize or deleted!");
RMLUI_ASSERTMSG(m_root_signatures[static_cast<int>(pipeline_id)], "you forgot to initialize or deleted!");
m_p_command_graphics_list->SetPipelineState(m_pipelines[static_cast<int>(pipeline_id)]);
m_p_command_graphics_list->SetGraphicsRootSignature(m_root_signatures[static_cast<int>(pipeline_id)]);
ID3D12DescriptorHeap* p_heaps[] = {m_p_descriptor_heap_shaders};
m_p_command_graphics_list->SetDescriptorHeaps(_countof(p_heaps), p_heaps);
}
RMLUI_DX_MARKER_END(m_p_command_graphics_list);
m_active_program_id = pipeline_id;
}
RenderInterface_DX12::ConstantBufferType* RenderInterface_DX12::Get_ConstantBuffer(uint32_t current_back_buffer_index)
{
RMLUI_ZoneScopedN("DirectX 12 - Get_ConstantBuffer");
RMLUI_ASSERTMSG(current_back_buffer_index != uint32_t(-1), "invalid index!");
auto max_index = RMLUI_RENDER_BACKEND_FIELD_PREALLOCATED_CONSTANTBUFFERS - 1;
if (m_constantbuffers[current_back_buffer_index].size() > RMLUI_RENDER_BACKEND_FIELD_PREALLOCATED_CONSTANTBUFFERS)
max_index = static_cast<int>(m_constantbuffers[current_back_buffer_index].size() - 1);
auto current_constant_buffer_index = m_constant_buffer_count_per_frame[current_back_buffer_index];
if (current_constant_buffer_index > max_index)
{
// resizing...
for (auto& vec : m_constantbuffers)
{
vec.emplace_back(std::move(ConstantBufferType()));
}
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] allocated new constant buffer instance for frame[%d], current size of storage[%zu]",
current_back_buffer_index, m_constantbuffers.at(current_back_buffer_index).size());
#endif
}
auto& vec_cbs = m_constantbuffers.at(current_back_buffer_index);
ConstantBufferType* p_result = &vec_cbs[current_constant_buffer_index];
if (p_result->m_alloc_info.buffer_index == -1)
{
p_result->m_alloc_info = m_manager_buffer.Alloc_ConstantBuffer(p_result, kAllocationSizeMax_ConstantBuffer);
}
++m_constant_buffer_count_per_frame[current_back_buffer_index];
return p_result;
}
RenderInterface_DX12::BufferMemoryManager::BufferMemoryManager() :
m_descriptor_increment_size_srv_cbv_uav{}, m_size_for_allocation_in_bytes{}, m_size_alignment_in_bytes{}, m_p_device{}, m_p_allocator{},
m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav{}, m_p_start_pointer_of_descriptor_heap_srv_cbv_uav{}
{
RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Constructor");
}
RenderInterface_DX12::BufferMemoryManager::~BufferMemoryManager()
{
RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Destructor");
}
bool RenderInterface_DX12::BufferMemoryManager::Is_Initialized() const
{
RMLUI_ZoneScopedN("DirectX 12 - BufferManagerManager::Is_Initialized");
return static_cast<bool>(m_p_device != nullptr);
}
void RenderInterface_DX12::BufferMemoryManager::Initialize(ID3D12Device* p_device, D3D12MA::Allocator* p_allocator,
OffsetAllocator::Allocator* p_offset_allocator_for_descriptor_heap_srv_cbv_uav, D3D12_CPU_DESCRIPTOR_HANDLE* p_handle,
uint32_t size_descriptor_srv_cbv_uav, size_t size_for_allocation, size_t size_alignment)
{
RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Initialize");
RMLUI_ASSERTMSG(p_allocator, "must be valid!");
RMLUI_ASSERTMSG(size_for_allocation, "must be greater than 0");
RMLUI_ASSERTMSG(size_alignment, "must be greater than 0");
RMLUI_ASSERTMSG(p_offset_allocator_for_descriptor_heap_srv_cbv_uav, "must be valid!");
RMLUI_ASSERTMSG(p_handle, "must be valid!");
RMLUI_ASSERTMSG(p_device, "must be valid!");
m_p_allocator = p_allocator;
m_size_for_allocation_in_bytes = size_for_allocation;
m_size_alignment_in_bytes = size_alignment;
m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav = p_offset_allocator_for_descriptor_heap_srv_cbv_uav;
m_p_start_pointer_of_descriptor_heap_srv_cbv_uav = p_handle;
m_descriptor_increment_size_srv_cbv_uav = size_descriptor_srv_cbv_uav;
m_p_device = p_device;
Alloc_Buffer(size_for_allocation
#ifdef RMLUI_DX_DEBUG
,
std::wstring(L"buffer[") + std::to_wstring(m_buffers.size()) + L"]"
#endif
);
}
void RenderInterface_DX12::BufferMemoryManager::Shutdown()
{
RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Shutdown");
for (auto& pair : m_buffers)
{
auto* p_allocation = pair.first;
if (p_allocation)
{
if (p_allocation->GetResource())
{
p_allocation->GetResource()->Unmap(0, nullptr);
p_allocation->GetResource()->Release();
}
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = p_allocation->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak!");
}
}
for (auto* p_block : m_virtual_buffers)
{
if (p_block)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = p_block->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak! (virtual block)");
}
}
if (m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav)
{
m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav = nullptr;
}
if (m_p_allocator)
{
m_p_allocator = nullptr;
}
m_buffers.clear();
m_virtual_buffers.clear();
m_p_device = nullptr;
}
void RenderInterface_DX12::BufferMemoryManager::Alloc_Vertex(const void* p_data, int num_vertices, size_t size_of_one_element_in_p_data,
GeometryHandleType* p_handle)
{
RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Alloc_Vertex");
RMLUI_ASSERTMSG(p_data, "data for mapping to buffer must valid!");
RMLUI_ASSERTMSG(num_vertices, "amount of vertices must be greater than zero!");
RMLUI_ASSERTMSG(size_of_one_element_in_p_data > 0, "size of one element must be greater than 0");
RMLUI_ASSERTMSG(p_handle, "must be valid!");
if (p_handle)
{
RMLUI_ASSERT(p_handle->Get_InfoVertex().buffer_index == -1 &&
"info is already initialized that means you didn't destroy your buffer! Something is wrong!");
p_handle->Set_NumVertices(num_vertices);
p_handle->Set_SizeOfOneVertex(size_of_one_element_in_p_data);
GraphicsAllocationInfo info;
Alloc(info, num_vertices * size_of_one_element_in_p_data);
void* p_writable_part = Get_WritableMemoryFromBufferByOffset(info);
RMLUI_ASSERTMSG(p_writable_part, "something is wrong!");
if (p_writable_part)
{
std::memcpy(p_writable_part, p_data, info.size);
}
p_handle->Set_InfoVertex(info);
#ifdef RMLUI_DX_DEBUG
auto* p_block = m_virtual_buffers.at(info.buffer_index);
RMLUI_ASSERTMSG(p_block, "can't be invalid!");
D3D12MA::Statistics stats;
p_block->GetStatistics(&stats);
auto available_memory = stats.BlockBytes - stats.AllocationBytes;
Rml::Log::Message(Rml::Log::Type::LT_DEBUG,
"[DirectX-12] allocated vertex buffer with size[%zu] (in bytes) in buffer[%d] available memory for this buffer [%zu] (in bytes)",
info.size, info.buffer_index, available_memory);
#endif
}
}
void RenderInterface_DX12::BufferMemoryManager::Alloc_Index(const void* p_data, int num_vertices, size_t size_of_one_element_in_p_data,
GeometryHandleType* p_handle)
{
RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Alloc_Index");
RMLUI_ASSERTMSG(p_data, "data for mapping to buffer must valid!");
RMLUI_ASSERTMSG(num_vertices, "amount of vertices must be greater than zero!");
RMLUI_ASSERTMSG(size_of_one_element_in_p_data > 0, "size of one element must be greater than 0");
RMLUI_ASSERTMSG(p_handle, "must be valid!");
if (p_handle)
{
RMLUI_ASSERT(p_handle->Get_InfoIndex().buffer_index == -1 &&
"info is already initialized that means you didn't destroy your buffer! Something is wrong!");
p_handle->Set_NumIndices(num_vertices);
p_handle->Set_SizeOfOneIndex(size_of_one_element_in_p_data);
GraphicsAllocationInfo info;
Alloc(info, num_vertices * size_of_one_element_in_p_data);
void* p_writable_part = Get_WritableMemoryFromBufferByOffset(info);
RMLUI_ASSERTMSG(p_writable_part, "something is wrong!");
if (p_writable_part)
{
std::memcpy(p_writable_part, p_data, num_vertices * size_of_one_element_in_p_data);
}
p_handle->Set_InfoIndex(info);
#ifdef RMLUI_DX_DEBUG
auto* p_block = m_virtual_buffers.at(info.buffer_index);
RMLUI_ASSERTMSG(p_block, "can't be invalid!");
D3D12MA::Statistics stats;
p_block->GetStatistics(&stats);
auto available_memory = stats.BlockBytes - stats.AllocationBytes;
Rml::Log::Message(Rml::Log::Type::LT_DEBUG,
"[DirectX-12] allocated index buffer with size[%zu] (in bytes) in buffer[%d] available memory for this buffer [%zu] (in bytes)",
info.size, info.buffer_index, available_memory);
#endif
}
}
RenderInterface_DX12::GraphicsAllocationInfo RenderInterface_DX12::BufferMemoryManager::Alloc_ConstantBuffer(ConstantBufferType* p_resource,
size_t size)
{
RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Alloc_ConstantBuffer");
RMLUI_ASSERTMSG(!m_buffers.empty(), "you forgot to allocate buffer on initialize stage of this manager!");
RMLUI_ASSERTMSG(p_resource, "must be valid!");
GraphicsAllocationInfo result;
auto result_index = Alloc(result, size, 256);
if (p_resource)
{
if (result_index != -1)
{
#ifdef RMLUI_DX_DEBUG
auto* p_block = m_virtual_buffers.at(result.buffer_index);
RMLUI_ASSERTMSG(p_block, "can't be invalid!");
D3D12MA::Statistics stats;
p_block->GetStatistics(&stats);
auto available_memory = stats.BlockBytes - stats.AllocationBytes;
Rml::Log::Message(Rml::Log::Type::LT_DEBUG,
"[DirectX-12] allocated constant buffer with size[%zu] (in bytes) in buffer[%d] available memory for this buffer [%zu] (in bytes)",
result.size, result.buffer_index, available_memory);
#endif
auto* p_dx_allocation = m_buffers.at(result_index).first;
RMLUI_ATTR_ASSERT_VARIABLE auto* p_dx_resource = p_dx_allocation->GetResource();
RMLUI_ASSERTMSG(p_dx_allocation, "something is broken!");
RMLUI_ASSERTMSG(p_dx_resource, "something is broken!");
p_resource->m_p_gpu_start_memory_for_binding_data = m_buffers.at(result_index).second;
}
}
return result;
}
void RenderInterface_DX12::BufferMemoryManager::Free_ConstantBuffer(ConstantBufferType* p_constantbuffer)
{
RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Free_ConstantBuffer");
RMLUI_ASSERTMSG(p_constantbuffer, "you must pass a valid object!");
if (p_constantbuffer)
{
const auto& info = p_constantbuffer->m_alloc_info;
RMLUI_ASSERTMSG(info.buffer_index != -1, "must be valid data of this info");
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] deallocated constant buffer with size[%zu] in buffer[%d]", info.size,
info.buffer_index);
#endif
if (m_virtual_buffers.empty() == false)
{
if (D3D12MA::VirtualBlock* p_block = m_virtual_buffers.at(info.buffer_index))
{
p_block->FreeAllocation(info.alloc_info);
p_constantbuffer->m_alloc_info = {};
}
}
}
}
void RenderInterface_DX12::BufferMemoryManager::Free_Geometry(GeometryHandleType* p_handle)
{
RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Free_Geometry");
RMLUI_ASSERTMSG(p_handle, "must be valid!");
RMLUI_ASSERTMSG(p_handle->Get_InfoVertex().buffer_index != -1, "not initialized, maybe you passing twice for deallocation?");
RMLUI_ASSERTMSG(p_handle->Get_InfoIndex().buffer_index != -1, "not initialized, maybe you passing twice for deallocation?");
if (p_handle)
{
const auto& info_vertex = p_handle->Get_InfoVertex();
const auto& info_index = p_handle->Get_InfoIndex();
if (m_virtual_buffers.empty() == false)
{
D3D12MA::VirtualBlock* p_block_vertex = m_virtual_buffers.at(info_vertex.buffer_index);
D3D12MA::VirtualBlock* p_block_index = m_virtual_buffers.at(info_index.buffer_index);
if (p_block_vertex)
{
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] deallocated vertex buffer with size[%zu] in buffer[%d] frame[%d]",
info_vertex.size, info_vertex.buffer_index, p_handle->Get_HistoryBackBufferFrameIndex());
#endif
p_block_vertex->FreeAllocation(info_vertex.alloc_info);
GraphicsAllocationInfo invalidate;
p_handle->Set_InfoVertex(invalidate);
}
if (p_block_index)
{
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] deallocated index buffer with size[%zu] in buffer[%d]", info_index.size,
info_index.buffer_index);
#endif
p_block_index->FreeAllocation(info_index.alloc_info);
GraphicsAllocationInfo invalidate;
p_handle->Set_InfoIndex(invalidate);
}
// Free_ConstantBuffer(&p_handle->Get_ConstantBuffer());
}
}
}
void* RenderInterface_DX12::BufferMemoryManager::Get_WritableMemoryFromBufferByOffset(const GraphicsAllocationInfo& info)
{
RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Get_WritableMemoryFromBufferByOffset");
RMLUI_ASSERTMSG(info.buffer_index != -1, "you pass not initialized graphics allocation info!");
void* p_result{};
if (info.buffer_index != -1)
{
std::uint8_t* p_begin = reinterpret_cast<std::uint8_t*>(m_buffers.at(info.buffer_index).second);
RMLUI_ASSERTMSG(p_begin, "being pointer is not valid! it's terribly wrong thing!!!!");
p_result = p_begin + info.offset;
}
return p_result;
}
D3D12MA::Allocation* RenderInterface_DX12::BufferMemoryManager::Get_BufferByIndex(int buffer_index)
{
RMLUI_ZoneScopedN("DirectX 12 - Get_BufferByIndex");
RMLUI_ASSERTMSG(buffer_index >= 0, "index must be valid!");
RMLUI_ASSERTMSG(buffer_index < m_buffers.size(), "overflow index!");
D3D12MA::Allocation* p_result{};
if (buffer_index >= 0)
{
if (buffer_index < m_buffers.size())
{
p_result = m_buffers.at(buffer_index).first;
}
}
return p_result;
}
void RenderInterface_DX12::BufferMemoryManager::Alloc_Buffer(size_t size
#ifdef RMLUI_DX_DEBUG
,
const std::wstring& debug_name
#endif
)
{
RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Alloc_Buffer");
RMLUI_ASSERTMSG(size, "must be greater than 0");
D3D12_RESOURCE_DESC desc_constantbuffer = {};
desc_constantbuffer.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc_constantbuffer.Alignment = 0;
desc_constantbuffer.Width = size;
desc_constantbuffer.Height = 1;
desc_constantbuffer.DepthOrArraySize = 1;
desc_constantbuffer.MipLevels = 1;
desc_constantbuffer.Format = DXGI_FORMAT_UNKNOWN;
desc_constantbuffer.SampleDesc.Count = 1;
desc_constantbuffer.SampleDesc.Quality = 0;
desc_constantbuffer.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc_constantbuffer.Flags = D3D12_RESOURCE_FLAG_NONE;
D3D12MA::ALLOCATION_DESC desc_allocation = {};
desc_allocation.HeapType = D3D12_HEAP_TYPE_UPLOAD;
ID3D12Resource* p_resource{};
D3D12MA::Allocation* p_allocation{};
auto result = m_p_allocator->CreateResource(&desc_allocation, &desc_constantbuffer, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &p_allocation,
IID_PPV_ARGS(&p_resource));
RMLUI_DX_VERIFY_MSG(result, "failed to CreateResource");
void* p_begin_writable_data{};
D3D12_RANGE range;
range.Begin = 0;
range.End = 0;
result = p_allocation->GetResource()->Map(0, &range, &p_begin_writable_data);
RMLUI_DX_VERIFY_MSG(result, "failed to ID3D12Resource::Map");
#ifdef RMLUI_DX_DEBUG
p_allocation->SetName(debug_name.c_str());
#endif
m_buffers.push_back({p_allocation, p_begin_writable_data});
D3D12MA::VIRTUAL_BLOCK_DESC desc_virtual = {};
desc_virtual.Size = size;
D3D12MA::VirtualBlock* p_block{};
result = D3D12MA::CreateVirtualBlock(&desc_virtual, &p_block);
RMLUI_DX_VERIFY_MSG(result, "failed to D3D12MA::CreateVirtualBlock");
m_virtual_buffers.push_back(p_block);
}
D3D12MA::VirtualBlock* RenderInterface_DX12::BufferMemoryManager::Get_AvailableBlock(size_t size_for_allocation, int* result_index)
{
RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Get_AvailableBlock");
RMLUI_ASSERTMSG(result_index, "must be valid part of memory!");
D3D12MA::VirtualBlock* p_result{};
int index{};
for (auto* p_block : m_virtual_buffers)
{
if (p_block)
{
D3D12MA::Statistics stats;
p_block->GetStatistics(&stats);
if ((stats.BlockBytes - stats.AllocationBytes) >= size_for_allocation)
{
p_result = p_block;
*result_index = index;
break;
}
}
++index;
}
return p_result;
}
D3D12MA::VirtualBlock* RenderInterface_DX12::BufferMemoryManager::Get_NotOutOfMemoryAndAvailableBlock(size_t size_for_allocation, int* result_index)
{
RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Get_NotOutOfMemoryAndAvailableBlock");
RMLUI_ASSERTMSG(result_index, "must be valid part of memory!");
RMLUI_ASSERTMSG(*result_index != -1,
"use this method when you found of available block then tried to allocate from it but got out of memory status!");
D3D12MA::VirtualBlock* p_result{};
// we skip out of memory block since it shows to us as available
auto from = *result_index + 1;
for (int i = from; i < m_virtual_buffers.size(); ++i)
{
auto* p_block = m_virtual_buffers.at(i);
if (p_block)
{
D3D12MA::Statistics stats;
p_block->GetStatistics(&stats);
if ((stats.BlockBytes - stats.AllocationBytes) >= size_for_allocation)
{
p_result = p_block;
*result_index = i;
break;
}
}
}
return p_result;
}
int RenderInterface_DX12::BufferMemoryManager::Alloc(GraphicsAllocationInfo& info, size_t size, size_t alignment)
{
RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::Alloc");
RMLUI_ASSERTMSG(!m_buffers.empty(), "you forgot to allocate buffer on initialize stage of this manager!");
// we don't want to use any recursions because it is slow af
constexpr int kHowManyRequestsWeCanDoForResolvingOutOfMemory = 15;
int result_index{-1};
if (alignment > 0)
size = AlignUp(size, alignment);
auto* p_block = Get_AvailableBlock(size, &result_index);
if (p_block)
{
D3D12MA::VIRTUAL_ALLOCATION_DESC desc_alloc = {};
desc_alloc.Size = size;
if (alignment % 2 == 0)
desc_alloc.Alignment = alignment;
D3D12MA::VirtualAllocation alloc;
UINT64 offset{};
auto status = p_block->Allocate(&desc_alloc, &alloc, &offset);
if (status == E_OUTOFMEMORY)
{
for (int i = 0; i < kHowManyRequestsWeCanDoForResolvingOutOfMemory; ++i)
{
p_block = Get_NotOutOfMemoryAndAvailableBlock(size, &result_index);
if (!p_block)
{
if (size > m_size_for_allocation_in_bytes)
{
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] auto correction size for buffer from [%zu] to [%zu]",
m_size_for_allocation_in_bytes, size);
#endif
m_size_for_allocation_in_bytes = size;
}
Alloc_Buffer(m_size_for_allocation_in_bytes
#ifdef RMLUI_DX_DEBUG
,
std::wstring(L"buffer[") + std::to_wstring(m_buffers.size()) + L"]"
#endif
);
result_index = static_cast<int>(m_buffers.size() - 1);
}
p_block = m_virtual_buffers.at(result_index);
desc_alloc = {};
desc_alloc.Size = size;
if (alignment % 2 == 0)
desc_alloc.Alignment = alignment;
auto status_allocation = p_block->Allocate(&desc_alloc, &alloc, &offset);
if (status_allocation == E_OUTOFMEMORY)
{
continue;
}
else if (status_allocation == S_OK)
{
break;
}
else
{
RMLUI_ERRORMSG("Unknown allocation status");
}
}
RMLUI_ASSERTMSG(offset == UINT64_MAX,
"it was last greedy try for allocating, it is really hard case for handling (report and describe your case on github), try to "
"optimize your 'stuff' (very calmly saying) "
"by your own, it is only for really rare specials cases where user doesn't want to think but UI must survive no matter what");
}
info.size = size;
info.alloc_info = alloc;
info.offset = offset;
info.buffer_index = result_index;
}
else
{
if (size > m_size_for_allocation_in_bytes)
{
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] auto correction size for buffer from [%zu] to [%zu]",
m_size_for_allocation_in_bytes, size);
#endif
m_size_for_allocation_in_bytes = size;
}
Alloc_Buffer(m_size_for_allocation_in_bytes
#ifdef RMLUI_DX_DEBUG
,
std::wstring(L"buffer[") + std::to_wstring(m_buffers.size()) + L"]"
#endif
);
result_index = -1;
p_block = Get_AvailableBlock(size, &result_index);
RMLUI_ASSERTMSG(p_block, "can't be because in previous line of code you added new allocated fresh buffer!");
if (p_block)
{
D3D12MA::VIRTUAL_ALLOCATION_DESC desc_alloc = {};
desc_alloc.Size = size;
if (alignment % 2 == 0)
desc_alloc.Alignment = alignment;
D3D12MA::VirtualAllocation alloc;
UINT64 offset{};
auto status = p_block->Allocate(&desc_alloc, &alloc, &offset);
RMLUI_DX_VERIFY_MSG(status, "failed to Allocate");
info.size = size;
info.alloc_info = alloc;
info.offset = offset;
info.buffer_index = result_index;
}
}
TryToFreeAvailableBlock();
return result_index;
}
void RenderInterface_DX12::BufferMemoryManager::TryToFreeAvailableBlock()
{
RMLUI_ZoneScopedN("DirectX 12 - BufferMemoryManager::TryToFreeAvailableBlock");
Rml::Array<std::pair<Rml::Vector<D3D12MA::VirtualBlock*>::const_iterator, Rml::Vector<Rml::Pair<D3D12MA::Allocation*, void*>>::const_iterator>, 1>
max_for_free;
for (size_t i = 0; i < max_for_free.size(); ++i)
{
max_for_free[i].first = m_virtual_buffers.end();
max_for_free[i].second = m_buffers.end();
}
int total_count{};
int limit_for_break{static_cast<int>(max_for_free.size())};
int index{};
for (auto cur = m_virtual_buffers.begin(); cur != m_virtual_buffers.end(); ++cur)
{
if (total_count == limit_for_break)
break;
if (*cur)
{
auto* p_block = *cur;
D3D12MA::Statistics stats;
p_block->GetStatistics(&stats);
if (stats.AllocationCount == 0 && stats.BlockCount == 0)
{
auto ref_count = p_block->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
ref_count = m_buffers.at(index).first->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
m_buffers.at(index).second = nullptr;
max_for_free[total_count].first = cur;
max_for_free[total_count].second = m_buffers.begin() + index;
++total_count;
}
}
++index;
}
for (int i = 0; i < total_count; ++i)
{
auto& pair = max_for_free.at(i);
m_virtual_buffers.erase(pair.first);
m_buffers.erase(pair.second);
}
}
RenderInterface_DX12::TextureMemoryManager::TextureMemoryManager() :
m_size_for_placed_heap{}, m_size_limit_for_being_placed{}, m_size_srv_cbv_uav_descriptor{}, m_size_rtv_descriptor{}, m_size_dsv_descriptor{},
m_fence_value{}, m_p_allocator{}, m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav{}, m_p_device{}, m_p_copy_command_list{},
m_p_backend_command_list{}, m_p_command_allocator{}, m_p_descriptor_heap_srv{}, m_p_copy_queue{}, m_p_fence{}, m_p_fence_event{}, m_p_handle{},
m_p_renderer{}, m_p_virtual_block_for_render_target_heap_allocations{}, m_p_virtual_block_for_depth_stencil_heap_allocations{},
m_p_descriptor_heap_rtv{}, m_p_descriptor_heap_dsv(), m_p_upload_buffer()
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Constructor");
}
RenderInterface_DX12::TextureMemoryManager::~TextureMemoryManager()
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Destructor");
}
bool RenderInterface_DX12::TextureMemoryManager::Is_Initialized() const
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Is_Initialized");
return static_cast<bool>(m_p_device != nullptr);
}
void RenderInterface_DX12::TextureMemoryManager::Initialize(D3D12MA::Allocator* p_allocator,
OffsetAllocator::Allocator* p_offset_allocator_for_descriptor_heap_srv_cbv_uav, ID3D12Device* p_device,
ID3D12GraphicsCommandList* p_copy_command_list, ID3D12GraphicsCommandList* p_backend_command_list, ID3D12CommandAllocator* p_allocator_command,
ID3D12DescriptorHeap* p_descriptor_heap_srv, ID3D12DescriptorHeap* p_descriptor_heap_rtv, ID3D12DescriptorHeap* p_descriptor_heap_dsv,
ID3D12CommandQueue* p_copy_queue, D3D12_CPU_DESCRIPTOR_HANDLE* p_handle, RenderInterface_DX12* p_renderer, size_t size_for_placed_heap)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Initialize");
RMLUI_ASSERTMSG(p_allocator, "you must pass a valid allocator pointer");
RMLUI_ASSERTMSG(size_for_placed_heap > 0, "there's no point in creating in such small heap");
RMLUI_ASSERTMSG(size_for_placed_heap != size_t(-1), "invalid value!");
RMLUI_ASSERTMSG(p_device, "must be valid!");
RMLUI_ASSERTMSG(p_copy_command_list, "must be valid!");
RMLUI_ASSERTMSG(p_backend_command_list, "must be valid!");
RMLUI_ASSERTMSG(p_allocator_command, "must be valid!");
RMLUI_ASSERTMSG(p_descriptor_heap_srv, "must be valid!");
RMLUI_ASSERTMSG(p_copy_queue, "must be valid!");
RMLUI_ASSERTMSG(p_handle, "must be valid!");
RMLUI_ASSERTMSG(p_renderer, "must be valid!");
RMLUI_ASSERTMSG(p_offset_allocator_for_descriptor_heap_srv_cbv_uav, "must be valid!");
RMLUI_ASSERTMSG(p_descriptor_heap_rtv, "must be valid!");
RMLUI_ASSERTMSG(p_descriptor_heap_dsv, "must be valid!");
m_p_device = p_device;
m_p_allocator = p_allocator;
m_p_copy_command_list = p_copy_command_list;
m_p_backend_command_list = p_backend_command_list;
m_p_command_allocator = p_allocator_command;
m_size_for_placed_heap = size_for_placed_heap;
m_p_descriptor_heap_srv = p_descriptor_heap_srv;
m_p_descriptor_heap_rtv = p_descriptor_heap_rtv;
m_p_descriptor_heap_dsv = p_descriptor_heap_dsv;
m_p_copy_queue = p_copy_queue;
m_p_renderer = p_renderer;
m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav = p_offset_allocator_for_descriptor_heap_srv_cbv_uav;
// if I have 4 MB then 1 MB is optimal as a maximum size for determing that resource can be allocated as placed resource so we just take 25% from
// size_for_placed_heap value
m_size_limit_for_being_placed = size_t((double(size_for_placed_heap) / 100.0) * 25.0);
if (m_p_device)
{
m_size_srv_cbv_uav_descriptor = m_p_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
m_size_rtv_descriptor = m_p_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
m_size_dsv_descriptor = m_p_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
}
m_p_handle = p_handle;
RMLUI_ASSERTMSG(m_size_srv_cbv_uav_descriptor > 0, "must be positive");
RMLUI_ASSERTMSG(m_size_rtv_descriptor > 0, "must be positive");
RMLUI_ASSERTMSG(m_size_limit_for_being_placed > 0 && m_size_limit_for_being_placed != size_t(-1), "something is wrong!");
if (m_p_device)
{
RMLUI_DX_VERIFY_MSG(m_p_device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_p_fence)), "failed to create fence for upload queue");
m_p_fence_event = CreateEvent(NULL, FALSE, FALSE, TEXT("Event for copy queue fence"));
}
if (m_p_descriptor_heap_rtv)
{
D3D12MA::VIRTUAL_BLOCK_DESC desc_block{};
desc_block.Size = m_size_rtv_descriptor * RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_RTV;
auto status = D3D12MA::CreateVirtualBlock(&desc_block, &m_p_virtual_block_for_render_target_heap_allocations);
RMLUI_DX_VERIFY_MSG(status, "failed to D3D12MA::CreateVirtualBlock (for rtv descriptor heap, texture manager)");
}
if (m_p_descriptor_heap_dsv)
{
D3D12MA::VIRTUAL_BLOCK_DESC desc_block{};
desc_block.Size = m_size_dsv_descriptor * RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_DSV;
auto status = D3D12MA::CreateVirtualBlock(&desc_block, &m_p_virtual_block_for_depth_stencil_heap_allocations);
RMLUI_DX_VERIFY_MSG(status, "failed to D3D12MA::CreateVirtualBlock (for dsv descriptor heap, texture manager)");
}
#if RMLUI_RENDER_BACKEND_FIELD_STAGING_BUFFER_CACHE_ENABLED == 1
if (m_p_allocator)
{
D3D12MA::Allocation* p_allocation = nullptr;
ID3D12Resource* p_upload_buffer{};
D3D12_RESOURCE_DESC buffer_desc;
buffer_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
buffer_desc.Alignment = 0;
buffer_desc.Width = RMLUI_RENDER_BACKEND_FIELD_STAGING_BUFFER_SIZE;
buffer_desc.Height = 1;
buffer_desc.DepthOrArraySize = 1;
buffer_desc.MipLevels = 1;
buffer_desc.Format = DXGI_FORMAT_UNKNOWN;
buffer_desc.SampleDesc.Count = 1;
buffer_desc.SampleDesc.Quality = 0;
buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
buffer_desc.Flags = D3D12_RESOURCE_FLAG_NONE;
D3D12MA::ALLOCATION_DESC desc_alloc{};
desc_alloc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
auto status = m_p_allocator->CreateResource(&desc_alloc, &buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &p_allocation,
IID_PPV_ARGS(&p_upload_buffer));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateResource (upload buffer for texture)");
m_p_upload_buffer = p_allocation;
}
#endif
}
void RenderInterface_DX12::TextureMemoryManager::Shutdown()
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Shutdown");
if (m_p_fence)
{
m_p_fence->Release();
m_p_fence = nullptr;
}
if (m_p_fence_event)
{
CloseHandle(m_p_fence_event);
m_p_fence_event = nullptr;
}
size_t index{};
for (auto* p_heap : m_heaps_placed)
{
p_heap->Release();
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_blocks.at(index)->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
++index;
}
if (m_p_virtual_block_for_render_target_heap_allocations)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_virtual_block_for_render_target_heap_allocations->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
if (m_p_virtual_block_for_depth_stencil_heap_allocations)
{
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_virtual_block_for_depth_stencil_heap_allocations->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak");
}
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12]: TextureMemoryManager -> total blocks in session were allocated = %zu",
m_blocks.size());
#endif
m_blocks.clear();
m_heaps_placed.clear();
#if RMLUI_RENDER_BACKEND_FIELD_STAGING_BUFFER_CACHE_ENABLED == 1
if (m_p_upload_buffer)
{
if (m_p_upload_buffer->GetResource())
{
m_p_upload_buffer->GetResource()->Release();
}
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = m_p_upload_buffer->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak!");
}
#endif
m_p_allocator = nullptr;
m_p_device = nullptr;
m_p_copy_command_list = nullptr;
m_p_backend_command_list = nullptr;
m_p_command_allocator = nullptr;
m_p_descriptor_heap_srv = nullptr;
m_p_copy_queue = nullptr;
m_p_handle = nullptr;
m_p_renderer = nullptr;
m_p_descriptor_heap_rtv = nullptr;
m_p_virtual_block_for_render_target_heap_allocations = nullptr;
m_p_virtual_block_for_depth_stencil_heap_allocations = nullptr;
}
ID3D12Resource* RenderInterface_DX12::TextureMemoryManager::Alloc_Texture(D3D12_RESOURCE_DESC& desc, TextureHandleType* p_impl,
const Rml::byte* p_data
#ifdef RMLUI_DX_DEBUG
,
const Rml::String& debug_name
#endif
)
{
#ifdef RMLUI_DX_DEBUG
(void)(debug_name);
#endif
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Alloc_Texture");
RMLUI_ASSERTMSG(desc.Dimension == D3D12_RESOURCE_DIMENSION::D3D12_RESOURCE_DIMENSION_TEXTURE2D,
"this manager doesn't support 1D or 3D textures. (why do we need to support it?)");
RMLUI_ASSERTMSG(desc.DepthOrArraySize <= 1, "we don't support a such sizes");
RMLUI_ASSERTMSG(desc.SampleDesc.Count == 1, "this manager not for allocating render targets or depth stencil!");
RMLUI_ASSERTMSG(desc.Width, "must specify value for width field");
RMLUI_ASSERTMSG(desc.Height, "must specify value for height field");
RMLUI_ASSERTMSG(p_impl, "must be valid!");
ID3D12Resource* p_result{};
// this size stands for real size of texture and how much it will occupy for allocated block, because if it is larger than 1 MB it is better to
// allocate as committed resource instead of using placed resource technique (by default we allocate block for 4MB because 1.0 MB textures can be
// occupied 4 times and smaller even more times, but it is not reasonable to use placed resources for >1MB textures)
auto base_memory_size_for_allocation_in_bytes = desc.Width * desc.Height * BytesPerPixel(desc.Format);
size_t total_memory_for_allocation = base_memory_size_for_allocation_in_bytes;
size_t mipmemory = base_memory_size_for_allocation_in_bytes;
for (int i = 2; i <= desc.MipLevels; ++i)
{
if (mipmemory <= 1)
{
break;
}
mipmemory /= 4;
total_memory_for_allocation += mipmemory;
}
#ifdef RMLUI_DX_DEBUG
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX-12] allocating texture with base memory[%zu] and total memory with mips levels[%zu]",
base_memory_size_for_allocation_in_bytes, total_memory_for_allocation);
#endif
// we need to pass full memory with mipmaps otherwise the DirectX API will validate it by its own. we must ensure what we do.
if (CanBePlacedResource(total_memory_for_allocation))
{
Alloc_As_Placed(base_memory_size_for_allocation_in_bytes, total_memory_for_allocation, desc, p_impl, p_data);
p_result = static_cast<ID3D12Resource*>(p_impl->Get_Resource());
}
else
{
Alloc_As_Committed(base_memory_size_for_allocation_in_bytes, total_memory_for_allocation, desc, p_impl, p_data);
p_result = static_cast<D3D12MA::Allocation*>(p_impl->Get_Resource())->GetResource();
}
return p_result;
}
ID3D12Resource* RenderInterface_DX12::TextureMemoryManager::Alloc_Texture(D3D12_RESOURCE_DESC& desc, Gfx::FramebufferData* p_impl,
D3D12_RESOURCE_FLAGS flags, D3D12_RESOURCE_STATES initial_state
#ifdef RMLUI_DX_DEBUG
,
const Rml::String& debug_name
#endif
)
{
#ifdef RMLUI_DX_DEBUG
(void)(debug_name);
#endif
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Alloc_Texture");
RMLUI_ASSERTMSG(desc.Dimension == D3D12_RESOURCE_DIMENSION::D3D12_RESOURCE_DIMENSION_TEXTURE2D,
"this manager doesn't support 1D or 3D textures. (why do we need to support it?)");
RMLUI_ASSERTMSG(desc.DepthOrArraySize <= 1, "we don't support a such sizes");
RMLUI_ASSERTMSG(desc.Width, "must specify value for width field");
RMLUI_ASSERTMSG(desc.Height, "must specify value for height field");
RMLUI_ASSERTMSG(p_impl, "must be valid!");
RMLUI_ASSERT(p_impl->Get_Texture() &&
"you must allocate Gfx::FramebufferData's field (texture) before calling this method! So you need to set your allocated pointer to "
"Set_Texture method, dude...");
ID3D12Resource* p_result{};
auto base_memory_size_for_allocation_in_bytes = desc.Width * desc.Height * BytesPerPixel(desc.Format);
size_t total_memory_for_allocation = base_memory_size_for_allocation_in_bytes;
size_t mipmemory = base_memory_size_for_allocation_in_bytes;
for (int i = 2; i <= desc.MipLevels; ++i)
{
if (mipmemory <= 1)
{
break;
}
mipmemory /= 4;
total_memory_for_allocation += mipmemory;
}
desc.Flags = flags;
#ifdef RMLUI_DX_DEBUG
bool is_rt = flags & D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
bool is_ds = flags & D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
RMLUI_ASSERTMSG(is_rt || is_ds, "must be allow for RT or DS otherwise you will get assert from DX12");
const char* type_of_flag_for_texture_debug_name{};
constexpr const char* hardcoded_name_render_target = "render target";
constexpr const char* hardcoded_name_depth_stencil = "depth-stencil";
constexpr const char* hardcoded_name_unknown = "unknown";
if (is_rt)
{
type_of_flag_for_texture_debug_name = hardcoded_name_render_target;
}
else if (is_ds)
{
type_of_flag_for_texture_debug_name = hardcoded_name_depth_stencil;
}
else
{
type_of_flag_for_texture_debug_name = hardcoded_name_unknown;
}
Rml::Log::Message(Rml::Log::Type::LT_DEBUG,
"[DirectX-12] allocating render2texture (%s) with base memory[%zu] and total memory with mips levels[%zu]",
type_of_flag_for_texture_debug_name, base_memory_size_for_allocation_in_bytes, total_memory_for_allocation);
#endif
Alloc_As_Committed(base_memory_size_for_allocation_in_bytes, total_memory_for_allocation, desc, initial_state, p_impl->Get_Texture(), p_impl);
p_result = static_cast<D3D12MA::Allocation*>(p_impl->Get_Texture()->Get_Resource())->GetResource();
// dx12 sdk was updated and if we don't do that operation we get a validation error see
// https://asawicki.info/news_1724_initializing_dx12_textures_after_allocation_and_aliasing direct quote: "If the metadata of such compression are
// uninitialized, it might have consequences more severe than observing random colors. It's actually an undefined behavior."
if (p_result)
{
bool is_render_target = flags & D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
RMLUI_ASSERTMSG(m_p_backend_command_list, "must be initialized");
RMLUI_ASSERTMSG(p_impl, "must be valid");
if (is_render_target)
{
constexpr FLOAT clear_color[] = {0.0f, 0.0f, 0.0f, 0.0f};
m_p_backend_command_list->ClearRenderTargetView(p_impl->Get_DescriptorResourceView(), clear_color, 0, nullptr);
}
}
return p_result;
}
void RenderInterface_DX12::TextureMemoryManager::Free_Texture(TextureHandleType* p_texture)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Free_Texture(TextureHandleType)");
RMLUI_ASSERTMSG(p_texture, "must be valid");
if (p_texture)
{
auto index = p_texture->Get_Info().buffer_index;
RMLUI_ASSERTMSG(p_texture->Get_Resource(), "must be valid! can't be! data is corrupted! or was already destroyed!");
if (index != -1)
{
m_blocks.at(index)->FreeAllocation(p_texture->Get_Info().alloc_info);
}
if (m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav)
{
m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav->free(p_texture->Get_Allocation_DescriptorHeap());
}
p_texture->Destroy();
}
}
void RenderInterface_DX12::TextureMemoryManager::Free_Texture(Gfx::FramebufferData* p_impl)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Free_Texture(FramebufferData)");
RMLUI_ASSERTMSG(p_impl, "must be valid!");
RMLUI_ASSERTMSG(p_impl->Get_Texture(), "must be valid!");
RMLUI_ASSERTMSG(m_p_virtual_block_for_render_target_heap_allocations, "must be valid! early calling");
RMLUI_ASSERTMSG(m_p_virtual_block_for_depth_stencil_heap_allocations, "must be valid! early calling");
if (p_impl)
{
Free_Texture(p_impl->Get_Texture(), p_impl->Is_RenderTarget(), *(p_impl->Get_VirtualAllocation_Descriptor()));
delete p_impl->Get_Texture();
p_impl->Set_Texture(nullptr);
p_impl->Set_DescriptorResourceView({});
p_impl->Set_ID(-1);
}
}
void RenderInterface_DX12::TextureMemoryManager::Free_Texture(TextureHandleType* p_texture, bool is_rt, D3D12MA::VirtualAllocation& allocation)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Free_Texture(TextureHandleType,bool,D3D12MA::VirtualAllocation)");
RMLUI_ASSERTMSG(p_texture, "you must pass a valid pointer!");
Free_Texture(p_texture);
if (is_rt)
{
if (m_p_virtual_block_for_render_target_heap_allocations)
{
m_p_virtual_block_for_render_target_heap_allocations->FreeAllocation(allocation);
}
}
else
{
if (m_p_virtual_block_for_depth_stencil_heap_allocations)
{
m_p_virtual_block_for_depth_stencil_heap_allocations->FreeAllocation(allocation);
}
}
}
bool RenderInterface_DX12::TextureMemoryManager::CanAllocate(size_t total_memory_for_allocation, D3D12MA::VirtualBlock* p_block)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::CanAllocate(size_t, D3D12MA::VirtualBlock)");
RMLUI_ASSERTMSG(total_memory_for_allocation > 0 && total_memory_for_allocation != size_t(-1), "must be a valid number!");
RMLUI_ASSERTMSG(p_block, "must be valid virtual block");
bool result{};
if (p_block)
{
D3D12MA::Statistics stats;
p_block->GetStatistics(&stats);
result = (stats.BlockBytes - stats.AllocationBytes) >= total_memory_for_allocation;
}
return result;
}
bool RenderInterface_DX12::TextureMemoryManager::CanBePlacedResource(size_t total_memory_for_allocation)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::CanBePlacedResource");
RMLUI_ASSERTMSG(total_memory_for_allocation > 0 && total_memory_for_allocation != size_t(-1), "must be a valid number!");
bool result{};
if (total_memory_for_allocation <= m_size_limit_for_being_placed)
result = true;
return result;
}
bool RenderInterface_DX12::TextureMemoryManager::CanBeSmallResource(size_t base_memory)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::CanBeSmallResource");
RMLUI_ASSERTMSG(base_memory > 0, "must be greater than zero!");
RMLUI_ASSERTMSG(base_memory != size_t(-1), "must be a valid number!");
bool result{};
constexpr size_t _kNonMSAASmallResourceMemoryLimit = 128 * 128 * 4;
// if this is not MSAA texture (because for now we didn't implement a such support)
if (base_memory <= _kNonMSAASmallResourceMemoryLimit)
result = true;
return result;
}
D3D12MA::VirtualBlock* RenderInterface_DX12::TextureMemoryManager::Get_AvailableBlock(size_t total_memory_for_allocation, int* result_index)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Get_AvailableBlock");
RMLUI_ASSERTMSG(m_p_device, "must be valid!");
RMLUI_ASSERTMSG(result_index, "must be valid!");
RMLUI_ASSERTMSG(total_memory_for_allocation <= m_size_limit_for_being_placed, "you can't pass a such size here!");
RMLUI_ASSERTMSG(m_size_limit_for_being_placed < m_size_for_placed_heap, "something is wrong and you initialized your manager wrong!!!!");
D3D12MA::VirtualBlock* p_result{};
if (m_blocks.empty())
{
RMLUI_ASSERTMSG(m_heaps_placed.empty(), "if blocks are empty heaps must be too!");
auto pair = Create_HeapPlaced(m_size_for_placed_heap);
p_result = pair.second;
*result_index = 0;
}
else
{
int index{};
for (auto* p_block : m_blocks)
{
if (p_block)
{
if (CanAllocate(total_memory_for_allocation, p_block))
{
p_result = p_block;
*result_index = index;
break;
}
else
{
if (index >= m_blocks.size() - 1)
{
auto pair = Create_HeapPlaced(m_size_for_placed_heap);
++index;
*result_index = index;
p_result = pair.second;
break;
}
}
}
++index;
}
}
return p_result;
}
void RenderInterface_DX12::TextureMemoryManager::Alloc_As_Committed(RMLUI_ATTR_ASSERT_VARIABLE size_t base_memory,
RMLUI_ATTR_ASSERT_VARIABLE size_t total_memory, D3D12_RESOURCE_DESC& desc, TextureHandleType* p_impl, const Rml::byte* p_data)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Alloc_As_Committed");
RMLUI_ASSERTMSG(base_memory > 0, "must be greater than zero!");
RMLUI_ASSERTMSG(total_memory > 0, "must be greater than zero!");
RMLUI_ASSERTMSG(base_memory != size_t(-1), "must be valid number!");
RMLUI_ASSERTMSG(total_memory != size_t(-1), "must be valid number!");
RMLUI_ASSERTMSG(m_p_device, "must be valid!");
RMLUI_ASSERTMSG(p_impl, "must be valid!");
RMLUI_ASSERTMSG(m_p_command_allocator, "must be valid!");
RMLUI_ASSERTMSG(m_p_copy_command_list, "must be valid!");
RMLUI_ASSERTMSG(m_p_allocator, "allocator must be valid!");
if (m_p_allocator)
{
D3D12MA::ALLOCATION_DESC desc_allocation = {};
desc_allocation.HeapType = D3D12_HEAP_TYPE_DEFAULT;
ID3D12Resource* p_resource{};
D3D12MA::Allocation* p_allocation{};
auto status =
m_p_allocator->CreateResource(&desc_allocation, &desc, D3D12_RESOURCE_STATE_COMMON, nullptr, &p_allocation, IID_PPV_ARGS(&p_resource));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateResource (D3D12MA)");
if (p_impl)
{
p_impl->Set_Resource(p_allocation);
}
if (m_p_command_allocator)
{
auto status_reset = m_p_command_allocator->Reset();
RMLUI_DX_VERIFY_MSG(status_reset, "failed to Reset (command allocator)");
}
if (m_p_copy_command_list)
{
auto status_reset = m_p_copy_command_list->Reset(m_p_command_allocator, nullptr);
RMLUI_DX_VERIFY_MSG(status_reset, "failed to Reset (command list)");
}
Upload(true, p_impl, desc, p_data, p_resource);
}
}
void RenderInterface_DX12::TextureMemoryManager::Alloc_As_Committed(RMLUI_ATTR_ASSERT_VARIABLE size_t base_memory,
RMLUI_ATTR_ASSERT_VARIABLE size_t total_memory, D3D12_RESOURCE_DESC& desc, D3D12_RESOURCE_STATES initial_state, TextureHandleType* p_texture,
Gfx::FramebufferData* p_impl)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Alloc_As_Committed");
RMLUI_ASSERTMSG(base_memory > 0, "must be greater than zero!");
RMLUI_ASSERTMSG(total_memory > 0, "must be greater than zero!");
RMLUI_ASSERTMSG(base_memory != size_t(-1), "must be valid number!");
RMLUI_ASSERTMSG(total_memory != size_t(-1), "must be valid number!");
RMLUI_ASSERTMSG(m_p_device, "must be valid!");
RMLUI_ASSERTMSG(p_impl, "must be valid!");
RMLUI_ASSERTMSG(m_p_command_allocator, "must be valid!");
RMLUI_ASSERTMSG(m_p_copy_command_list, "must be valid!");
RMLUI_ASSERTMSG(m_p_allocator, "allocator must be valid!");
RMLUI_ASSERTMSG(p_texture, "must be valid!");
if (m_p_allocator)
{
D3D12MA::ALLOCATION_DESC desc_allocation = {};
desc_allocation.HeapType = D3D12_HEAP_TYPE_DEFAULT;
D3D12_CLEAR_VALUE optimized_clear_value = {};
if (p_impl->Is_RenderTarget())
{
optimized_clear_value.Format = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
optimized_clear_value.Color[0] = 0.0f;
optimized_clear_value.Color[1] = 0.0f;
optimized_clear_value.Color[2] = 0.0f;
optimized_clear_value.Color[3] = 0.0f;
}
else
{
optimized_clear_value.Format = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT;
optimized_clear_value.DepthStencil.Depth = RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_DEPTHSTENCIL_DEPTH_VALUE;
optimized_clear_value.DepthStencil.Stencil = RMLUI_RENDER_BACKEND_FIELD_CLEAR_VALUE_DEPTHSTENCIL_STENCIL_VALUE;
}
ID3D12Resource* p_resource{};
D3D12MA::Allocation* p_allocation{};
auto status =
m_p_allocator->CreateResource(&desc_allocation, &desc, initial_state, &optimized_clear_value, &p_allocation, IID_PPV_ARGS(&p_resource));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateResource (D3D12MA) (RenderTargetTexture)");
if (p_texture)
{
p_texture->Set_Resource(p_allocation);
}
D3D12_SHADER_RESOURCE_VIEW_DESC desc_srv{};
desc_srv.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
bool is_rt = desc.Flags & D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
bool is_ds = desc.Flags & D3D12_RESOURCE_FLAGS::D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
if (is_rt)
desc_srv.Format = desc.Format;
else if (is_ds)
desc_srv.Format = DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS;
if (desc.SampleDesc.Count > 1)
{
desc_srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
}
else
{
desc_srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
}
desc_srv.Texture2D.MipLevels = desc.MipLevels;
RMLUI_ASSERTMSG((is_rt || is_ds), "this method for dsv or rtv resources");
auto descriptor_allocation =
m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav->allocate(static_cast<OffsetAllocator::uint32>(m_size_srv_cbv_uav_descriptor));
auto offset_pointer = SIZE_T(INT64(m_p_handle->ptr)) + INT64(descriptor_allocation.offset);
D3D12_CPU_DESCRIPTOR_HANDLE cast_offset_pointer;
cast_offset_pointer.ptr = offset_pointer;
m_p_device->CreateShaderResourceView(p_resource, &desc_srv, cast_offset_pointer);
p_texture->Set_Allocation_DescriptorHeap(descriptor_allocation);
if (is_rt)
p_impl->Set_DescriptorResourceView(Alloc_RenderTargetResourceView(p_resource, p_impl->Get_VirtualAllocation_Descriptor()));
else if (is_ds)
p_impl->Set_DescriptorResourceView(Alloc_DepthStencilResourceView(p_resource, p_impl->Get_VirtualAllocation_Descriptor()));
}
}
void RenderInterface_DX12::TextureMemoryManager::Alloc_As_Placed(size_t base_memory, RMLUI_ATTR_ASSERT_VARIABLE size_t total_memory,
D3D12_RESOURCE_DESC& desc, TextureHandleType* p_impl, const Rml::byte* p_data)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Alloc_As_Placed");
RMLUI_ASSERTMSG(base_memory > 0, "must be greater than zero!");
RMLUI_ASSERTMSG(total_memory > 0, "must be greater than zero!");
RMLUI_ASSERTMSG(base_memory != size_t(-1), "must be valid number!");
RMLUI_ASSERTMSG(total_memory != size_t(-1), "must be valid number!");
RMLUI_ASSERTMSG(m_p_device, "must be valid!");
RMLUI_ASSERTMSG(p_impl, "must be valid!");
RMLUI_ASSERTMSG(m_p_command_allocator, "must be valid!");
RMLUI_ASSERTMSG(m_p_copy_command_list, "must be valid!");
D3D12_RESOURCE_ALLOCATION_INFO info_for_alloc{};
if (CanBeSmallResource(base_memory))
{
desc.Alignment = D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT;
info_for_alloc = m_p_device->GetResourceAllocationInfo(0, 1, &desc);
RMLUI_ASSERTMSG(info_for_alloc.Alignment == D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT, "wrong calculation! check CanBeSmallResource method!");
#ifdef RMLUI_DX_DEBUG
if (total_memory != info_for_alloc.SizeInBytes)
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG,
"[DirectX-12]: WARNING! Probably not aligned size of texture for creation w=[%zu] h=[%d] size=[%zu] align_size=[%zu] since gpu "
"driver wants to have align data "
"for allocating you should keep resources dimensions to be multiple of two!",
desc.Width, desc.Height, total_memory, info_for_alloc.SizeInBytes);
}
#endif
}
else
{
desc.Alignment = 0;
info_for_alloc = m_p_device->GetResourceAllocationInfo(0, 1, &desc);
RMLUI_ASSERTMSG(info_for_alloc.Alignment != D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT, "wrong calculation! check CanBeSmallResource method!");
#ifdef RMLUI_DX_DEBUG
if (total_memory != info_for_alloc.SizeInBytes)
{
Rml::Log::Message(Rml::Log::Type::LT_DEBUG,
"[DirectX-12]: WARNING! Probably not aligned size of texture for creation w=[%zu] h=[%d] size=[%zu] align_size=[%zu] since gpu "
"driver wants to have align data "
"for allocating you should keep resources dimensions to be multiple of two!",
desc.Width, desc.Height, total_memory, info_for_alloc.SizeInBytes);
}
#endif
}
int heap_index{-1};
auto* p_block = Get_AvailableBlock(info_for_alloc.SizeInBytes, &heap_index);
RMLUI_ASSERTMSG(heap_index != -1, "something is wrong!");
RMLUI_ASSERTMSG(p_block, "something is wrong!");
auto* p_heap = m_heaps_placed.at(heap_index);
RMLUI_ASSERTMSG(p_heap, "something is wrong!");
D3D12_RESOURCE_BARRIER bar;
D3D12MA::VIRTUAL_ALLOCATION_DESC desc_alloc{};
desc_alloc.Size = info_for_alloc.SizeInBytes;
desc_alloc.Alignment = info_for_alloc.Alignment;
D3D12MA::VirtualAllocation alloc_virtual;
UINT64 offset{};
// wh1t3lord: yeah, we can get a situation of fast allocating things that like visual test sample where we can hold left button and just
// switch across all samples fast but we could defragment our block that formally the unused space is ENOUGH for allocation but for real there's
// no free block and D3D12MA doesn't provide a good way for resolving accurately using stats or something else, so if we receive from block that
// was found in Get_AvailableBlock still can return from ->Allocate E_OUTOFMEMORY and it means we need try to find another one until we tried all
// and if all failed we have to allocate new block sadly
auto status = p_block->Allocate(&desc_alloc, &alloc_virtual, &offset);
// found a block but need to resolve it
if (status == E_OUTOFMEMORY)
{
// let's find another block if it is available
D3D12MA::VirtualBlock* p_valid_block_obtained{};
heap_index = -1;
p_heap = nullptr;
for (int i = 0; i < m_blocks.size(); ++i)
{
D3D12MA::VirtualBlock* p_candidate_block = m_blocks[i];
RMLUI_ASSERTMSG(p_candidate_block, "probably we can't keep nullptr in valid container");
if (p_candidate_block != p_block)
{
if (CanAllocate(info_for_alloc.SizeInBytes, p_candidate_block))
{
status = p_candidate_block->Allocate(&desc_alloc, &alloc_virtual, &offset);
if (SUCCEEDED(status))
{
p_valid_block_obtained = p_candidate_block;
p_heap = m_heaps_placed[i];
heap_index = i;
break;
}
}
}
}
if (!p_valid_block_obtained)
{
const auto& new_heap_and_block = Create_HeapPlaced(m_size_for_placed_heap);
RMLUI_ASSERTMSG(new_heap_and_block.second, "must be valid!");
RMLUI_ASSERTMSG(new_heap_and_block.first, "must be valid!");
if (new_heap_and_block.second)
{
status = new_heap_and_block.second->Allocate(&desc_alloc, &alloc_virtual, &offset);
RMLUI_DX_VERIFY_MSG(SUCCEEDED(status), "can't resolve allocation for resource, report to developers!");
p_heap = new_heap_and_block.first;
for (int i = 0; i < m_heaps_placed.size(); ++i)
{
if (m_heaps_placed[i] == p_heap)
{
heap_index = i;
break;
}
}
}
}
}
RMLUI_DX_VERIFY_MSG(status, "can't allocate virtual alloc!");
ID3D12Resource* p_resource{};
status = m_p_device->CreatePlacedResource(p_heap, offset, &desc, D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&p_resource));
RMLUI_DX_VERIFY_MSG(status, "failed to CreatePlacedResource!");
RMLUI_ASSERTMSG(p_resource, "must be valid pointer of ID3D12Resource*!");
if (p_impl)
{
GraphicsAllocationInfo info_graphics;
info_graphics.size = info_for_alloc.SizeInBytes;
info_graphics.buffer_index = heap_index;
info_graphics.offset = offset;
info_graphics.alloc_info = alloc_virtual;
p_impl->Set_Info(info_graphics);
p_impl->Set_Resource(p_resource);
}
bar.Flags = D3D12_RESOURCE_BARRIER_FLAGS::D3D12_RESOURCE_BARRIER_FLAG_NONE;
bar.Type = D3D12_RESOURCE_BARRIER_TYPE::D3D12_RESOURCE_BARRIER_TYPE_ALIASING;
bar.Aliasing.pResourceBefore = nullptr;
bar.Aliasing.pResourceAfter = p_resource;
if (m_p_command_allocator)
{
auto status_reset = m_p_command_allocator->Reset();
RMLUI_DX_VERIFY_MSG(status_reset, "failed to Reset (command allocator)");
}
if (m_p_copy_command_list)
{
auto status_reset = m_p_copy_command_list->Reset(m_p_command_allocator, nullptr);
RMLUI_DX_VERIFY_MSG(status_reset, "failed to Reset (command list)");
m_p_copy_command_list->ResourceBarrier(1, &bar);
}
Upload(false, p_impl, desc, p_data, p_resource);
}
void RenderInterface_DX12::TextureMemoryManager::Upload(bool is_committed, TextureHandleType* p_texture_handle, const D3D12_RESOURCE_DESC& desc,
const Rml::byte* p_data, ID3D12Resource* p_resource)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Upload");
RMLUI_ASSERTMSG(m_p_device, "must be valid!");
// RMLUI_ASSERTMSG(p_data, "must be valid!");
RMLUI_ASSERTMSG(p_resource, "must be valid!");
RMLUI_ASSERTMSG(m_p_descriptor_heap_srv, "must be valid!");
RMLUI_ASSERTMSG(m_p_allocator, "must be valid!");
RMLUI_ASSERTMSG(m_p_copy_command_list, "must be valid!");
RMLUI_ASSERTMSG(m_p_handle, "must be valid!");
RMLUI_ASSERTMSG(m_p_copy_queue, "must be valid!");
RMLUI_ASSERTMSG(m_p_renderer, "must be valid!");
RMLUI_ASSERTMSG(m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav, "must be valid!");
RMLUI_ASSERTMSG(p_texture_handle, "must be valid!");
RMLUI_ASSERTMSG(m_p_fence, "must be valid!");
RMLUI_ASSERTMSG(m_p_fence_event, "must be valid!");
auto upload_size = GetRequiredIntermediateSize(p_resource, 0, 1);
D3D12MA::Allocation* p_allocation{};
HRESULT status = S_OK;
#if RMLUI_RENDER_BACKEND_FIELD_STAGING_BUFFER_CACHE_ENABLED == 1
RMLUI_ASSERTMSG(m_p_upload_buffer, "must be valid and initialized");
p_allocation = m_p_upload_buffer;
bool was_allocated_temp = false;
RMLUI_ASSERTMSG(m_p_upload_buffer->GetResource(), "must be initialized");
if (p_allocation->GetResource())
{
if (p_allocation->GetResource()->GetDesc().Width < upload_size)
{
Rml::Log::Message(Rml::Log::Type::LT_WARNING,
"! [DirectX 12]: you are trying to upload huge texture = %zu bytes (%zu MB), so if you can't optimize its size for loading then "
"ignore "
"this "
"message, otherwise we "
"expect you to upload using staging buffer from UploadResourceManager instance",
upload_size, upload_size / (1024 * 1024));
p_allocation = nullptr;
ID3D12Resource* p_upload_buffer{};
D3D12_RESOURCE_DESC buffer_desc;
buffer_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
buffer_desc.Alignment = 0;
buffer_desc.Width = upload_size;
buffer_desc.Height = 1;
buffer_desc.DepthOrArraySize = 1;
buffer_desc.MipLevels = 1;
buffer_desc.Format = DXGI_FORMAT_UNKNOWN;
buffer_desc.SampleDesc.Count = 1;
buffer_desc.SampleDesc.Quality = 0;
buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
buffer_desc.Flags = D3D12_RESOURCE_FLAG_NONE;
D3D12MA::ALLOCATION_DESC desc_alloc{};
desc_alloc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
status = m_p_allocator->CreateResource(&desc_alloc, &buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &p_allocation,
IID_PPV_ARGS(&p_upload_buffer));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateResource (upload buffer for texture)");
was_allocated_temp = true;
}
}
#else
ID3D12Resource* p_upload_buffer{};
D3D12_RESOURCE_DESC buffer_desc;
buffer_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
buffer_desc.Alignment = 0;
buffer_desc.Width = upload_size;
buffer_desc.Height = 1;
buffer_desc.DepthOrArraySize = 1;
buffer_desc.MipLevels = 1;
buffer_desc.Format = DXGI_FORMAT_UNKNOWN;
buffer_desc.SampleDesc.Count = 1;
buffer_desc.SampleDesc.Quality = 0;
buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
buffer_desc.Flags = D3D12_RESOURCE_FLAG_NONE;
D3D12MA::ALLOCATION_DESC desc_alloc{};
desc_alloc.HeapType = D3D12_HEAP_TYPE_UPLOAD;
status = m_p_allocator->CreateResource(&desc_alloc, &buffer_desc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, &p_allocation,
IID_PPV_ARGS(&p_upload_buffer));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateResource (upload buffer for texture)");
#endif
D3D12_SUBRESOURCE_DATA desc_data{};
desc_data.pData = p_data;
desc_data.RowPitch = desc.Width * BytesPerPixel(desc.Format);
desc_data.SlicePitch = desc_data.RowPitch * desc.Height;
auto allocated_size = UpdateSubresources<1>(m_p_copy_command_list, p_resource, p_allocation->GetResource(), 0, 0, 1, &desc_data);
#ifdef RMLUI_DX_DEBUG
constexpr const char* p_committed = "committed";
constexpr const char* p_placed = "placed";
const char* p_current_resource_type_name{};
if (is_committed)
p_current_resource_type_name = p_committed;
else
p_current_resource_type_name = p_placed;
Rml::Log::Message(Rml::Log::Type::LT_DEBUG, "[DirectX 12] allocated %s resource to GPU with size: [%zu]", p_current_resource_type_name,
allocated_size);
#else
(void)is_committed;
(void)allocated_size;
#endif
D3D12_SHADER_RESOURCE_VIEW_DESC desc_srv{};
desc_srv.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
desc_srv.Format = desc.Format;
desc_srv.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
desc_srv.Texture2D.MipLevels = desc.MipLevels;
auto descriptor_allocation =
m_p_offset_allocator_for_descriptor_heap_srv_cbv_uav->allocate(static_cast<OffsetAllocator::uint32>(m_size_srv_cbv_uav_descriptor));
auto offset_pointer = SIZE_T(INT64(m_p_handle->ptr) + INT64(descriptor_allocation.offset));
D3D12_CPU_DESCRIPTOR_HANDLE cast_offset_pointer;
cast_offset_pointer.ptr = offset_pointer;
m_p_device->CreateShaderResourceView(p_resource, &desc_srv, cast_offset_pointer);
p_texture_handle->Set_Allocation_DescriptorHeap(descriptor_allocation);
status = m_p_copy_command_list->Close();
RMLUI_DX_VERIFY_MSG(status, "failed to Close (command list for copy)");
ID3D12CommandList* p_lists[] = {m_p_copy_command_list};
m_p_copy_queue->ExecuteCommandLists(1, p_lists);
// wait queue for completion
if (m_p_device)
{
++m_fence_value;
RMLUI_DX_VERIFY_MSG(m_p_copy_queue->Signal(m_p_fence, m_fence_value), "failed to signal copy queue's fence");
if (m_p_fence->GetCompletedValue() < m_fence_value)
{
RMLUI_DX_VERIFY_MSG(m_p_fence->SetEventOnCompletion(m_fence_value, m_p_fence_event), "failed to SetEventOnCompletion");
WaitForSingleObjectEx(m_p_fence_event, INFINITE, FALSE);
}
}
#if RMLUI_RENDER_BACKEND_FIELD_STAGING_BUFFER_CACHE_ENABLED == 1
if (was_allocated_temp)
{
if (p_allocation)
{
if (p_allocation->GetResource())
{
p_allocation->GetResource()->Release();
}
RMLUI_ATTR_ASSERT_VARIABLE auto ref_count = p_allocation->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak!");
}
}
#else
if (p_allocation)
{
if (p_allocation->GetResource())
{
p_allocation->GetResource()->Release();
}
auto ref_count = p_allocation->Release();
RMLUI_ASSERTMSG(ref_count == 0, "leak!");
}
#endif
}
size_t RenderInterface_DX12::TextureMemoryManager::BytesPerPixel(DXGI_FORMAT format)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::BytesPerPixel");
return BitsPerPixel(format) / static_cast<size_t>(8);
}
size_t RenderInterface_DX12::TextureMemoryManager::BitsPerPixel(DXGI_FORMAT format)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::BitsPerPixel");
switch (format)
{
case DXGI_FORMAT_R32G32B32A32_TYPELESS:
case DXGI_FORMAT_R32G32B32A32_FLOAT:
case DXGI_FORMAT_R32G32B32A32_UINT:
case DXGI_FORMAT_R32G32B32A32_SINT: return 128;
case DXGI_FORMAT_R32G32B32_TYPELESS:
case DXGI_FORMAT_R32G32B32_FLOAT:
case DXGI_FORMAT_R32G32B32_UINT:
case DXGI_FORMAT_R32G32B32_SINT: return 96;
case DXGI_FORMAT_R16G16B16A16_TYPELESS:
case DXGI_FORMAT_R16G16B16A16_FLOAT:
case DXGI_FORMAT_R16G16B16A16_UNORM:
case DXGI_FORMAT_R16G16B16A16_UINT:
case DXGI_FORMAT_R16G16B16A16_SNORM:
case DXGI_FORMAT_R16G16B16A16_SINT:
case DXGI_FORMAT_R32G32_TYPELESS:
case DXGI_FORMAT_R32G32_FLOAT:
case DXGI_FORMAT_R32G32_UINT:
case DXGI_FORMAT_R32G32_SINT:
case DXGI_FORMAT_R32G8X24_TYPELESS:
case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:
case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:
case DXGI_FORMAT_Y416:
case DXGI_FORMAT_Y210:
case DXGI_FORMAT_Y216: return 64;
case DXGI_FORMAT_R10G10B10A2_TYPELESS:
case DXGI_FORMAT_R10G10B10A2_UNORM:
case DXGI_FORMAT_R10G10B10A2_UINT:
case DXGI_FORMAT_R11G11B10_FLOAT:
case DXGI_FORMAT_R8G8B8A8_TYPELESS:
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
case DXGI_FORMAT_R8G8B8A8_UINT:
case DXGI_FORMAT_R8G8B8A8_SNORM:
case DXGI_FORMAT_R8G8B8A8_SINT:
case DXGI_FORMAT_R16G16_TYPELESS:
case DXGI_FORMAT_R16G16_FLOAT:
case DXGI_FORMAT_R16G16_UNORM:
case DXGI_FORMAT_R16G16_UINT:
case DXGI_FORMAT_R16G16_SNORM:
case DXGI_FORMAT_R16G16_SINT:
case DXGI_FORMAT_R32_TYPELESS:
case DXGI_FORMAT_D32_FLOAT:
case DXGI_FORMAT_R32_FLOAT:
case DXGI_FORMAT_R32_UINT:
case DXGI_FORMAT_R32_SINT:
case DXGI_FORMAT_R24G8_TYPELESS:
case DXGI_FORMAT_D24_UNORM_S8_UINT:
case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
case DXGI_FORMAT_X24_TYPELESS_G8_UINT:
case DXGI_FORMAT_R9G9B9E5_SHAREDEXP:
case DXGI_FORMAT_R8G8_B8G8_UNORM:
case DXGI_FORMAT_G8R8_G8B8_UNORM:
case DXGI_FORMAT_B8G8R8A8_UNORM:
case DXGI_FORMAT_B8G8R8X8_UNORM:
case DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM:
case DXGI_FORMAT_B8G8R8A8_TYPELESS:
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
case DXGI_FORMAT_B8G8R8X8_TYPELESS:
case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
case DXGI_FORMAT_AYUV:
case DXGI_FORMAT_Y410:
case DXGI_FORMAT_YUY2: return 32;
case DXGI_FORMAT_P010:
case DXGI_FORMAT_P016: return 24;
case DXGI_FORMAT_R8G8_TYPELESS:
case DXGI_FORMAT_R8G8_UNORM:
case DXGI_FORMAT_R8G8_UINT:
case DXGI_FORMAT_R8G8_SNORM:
case DXGI_FORMAT_R8G8_SINT:
case DXGI_FORMAT_R16_TYPELESS:
case DXGI_FORMAT_R16_FLOAT:
case DXGI_FORMAT_D16_UNORM:
case DXGI_FORMAT_R16_UNORM:
case DXGI_FORMAT_R16_UINT:
case DXGI_FORMAT_R16_SNORM:
case DXGI_FORMAT_R16_SINT:
case DXGI_FORMAT_B5G6R5_UNORM:
case DXGI_FORMAT_B5G5R5A1_UNORM:
case DXGI_FORMAT_A8P8:
case DXGI_FORMAT_B4G4R4A4_UNORM: return 16;
case DXGI_FORMAT_NV12:
case DXGI_FORMAT_420_OPAQUE:
case DXGI_FORMAT_NV11: return 12;
case DXGI_FORMAT_R8_TYPELESS:
case DXGI_FORMAT_R8_UNORM:
case DXGI_FORMAT_R8_UINT:
case DXGI_FORMAT_R8_SNORM:
case DXGI_FORMAT_R8_SINT:
case DXGI_FORMAT_A8_UNORM:
case DXGI_FORMAT_AI44:
case DXGI_FORMAT_IA44:
case DXGI_FORMAT_P8: return 8;
case DXGI_FORMAT_R1_UNORM: return 1;
case DXGI_FORMAT_BC1_TYPELESS:
case DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT_BC1_UNORM_SRGB:
case DXGI_FORMAT_BC4_TYPELESS:
case DXGI_FORMAT_BC4_UNORM:
case DXGI_FORMAT_BC4_SNORM: return 4;
case DXGI_FORMAT_BC2_TYPELESS:
case DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT_BC2_UNORM_SRGB:
case DXGI_FORMAT_BC3_TYPELESS:
case DXGI_FORMAT_BC3_UNORM:
case DXGI_FORMAT_BC3_UNORM_SRGB:
case DXGI_FORMAT_BC5_TYPELESS:
case DXGI_FORMAT_BC5_UNORM:
case DXGI_FORMAT_BC5_SNORM:
case DXGI_FORMAT_BC6H_TYPELESS:
case DXGI_FORMAT_BC6H_UF16:
case DXGI_FORMAT_BC6H_SF16:
case DXGI_FORMAT_BC7_TYPELESS:
case DXGI_FORMAT_BC7_UNORM:
case DXGI_FORMAT_BC7_UNORM_SRGB: return 8;
default:
{
RMLUI_ASSERT(!"failed to determine texture type that wasn't registered report to developers (https://github.com/mikke89/RmlUi/issues)");
return 0;
}
}
}
Rml::Pair<ID3D12Heap*, D3D12MA::VirtualBlock*> RenderInterface_DX12::TextureMemoryManager::Create_HeapPlaced(size_t size_for_creation)
{
(void)(size_for_creation);
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Create_HeapPlaced");
RMLUI_ASSERTMSG(m_p_device, "must be valid!");
D3D12MA::VIRTUAL_BLOCK_DESC desc_block{};
desc_block.Size = m_size_for_placed_heap;
D3D12MA::VirtualBlock* p_block{};
auto status = D3D12MA::CreateVirtualBlock(&desc_block, &p_block);
RMLUI_DX_VERIFY_MSG(status, "failed to D3D12MA::CreateVirtualBlock");
m_blocks.push_back(p_block);
D3D12_HEAP_DESC desc_heap;
desc_heap.Flags = D3D12_HEAP_FLAG_DENY_BUFFERS | D3D12_HEAP_FLAG_DENY_RT_DS_TEXTURES;
desc_heap.SizeInBytes = m_size_for_placed_heap;
desc_heap.Alignment = 0;
desc_heap.Properties.Type = D3D12_HEAP_TYPE::D3D12_HEAP_TYPE_DEFAULT;
desc_heap.Properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
desc_heap.Properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
desc_heap.Properties.CreationNodeMask = 1;
desc_heap.Properties.VisibleNodeMask = 1;
ID3D12Heap* p_heap{};
status = m_p_device->CreateHeap(&desc_heap, IID_PPV_ARGS(&p_heap));
RMLUI_DX_VERIFY_MSG(status, "failed to CreateHeap!");
m_heaps_placed.push_back(p_heap);
return {p_heap, p_block};
}
D3D12_CPU_DESCRIPTOR_HANDLE RenderInterface_DX12::TextureMemoryManager::Alloc_DepthStencilResourceView(ID3D12Resource* p_resource,
D3D12MA::VirtualAllocation* p_alloc)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Alloc_DepthStecilResourceView");
RMLUI_ASSERTMSG(p_resource, "must be allocated!");
RMLUI_ASSERTMSG(m_p_device, "must be allocated!");
RMLUI_ASSERTMSG(m_p_virtual_block_for_depth_stencil_heap_allocations, "must be initialized");
RMLUI_ASSERTMSG(p_alloc, "must be valid!");
RMLUI_ASSERTMSG(m_p_descriptor_heap_dsv, "must be valid!");
D3D12_CPU_DESCRIPTOR_HANDLE calculated_offset{};
if (p_resource)
{
if (m_p_virtual_block_for_depth_stencil_heap_allocations)
{
if (m_p_device)
{
if (m_p_descriptor_heap_dsv)
{
D3D12MA::VIRTUAL_ALLOCATION_DESC desc_alloc = {};
desc_alloc.Size = m_size_dsv_descriptor;
UINT64 offset{};
auto status = m_p_virtual_block_for_depth_stencil_heap_allocations->Allocate(&desc_alloc, p_alloc, &offset);
RMLUI_DX_VERIFY_MSG(status,
"failed to allocate descriptor rtv, it means you need to resize and set higher size than previous, overflow (see "
"RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_RTV constant)");
D3D12_DEPTH_STENCIL_VIEW_DESC desc_rtv = {};
desc_rtv.Format = RMLUI_RENDER_BACKEND_FIELD_DEPTHSTENCIL_TEXTURE_FORMAT;
desc_rtv.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS;
calculated_offset.ptr = (m_p_descriptor_heap_dsv->GetCPUDescriptorHandleForHeapStart().ptr + offset);
m_p_device->CreateDepthStencilView(p_resource, &desc_rtv, calculated_offset);
}
}
}
}
return calculated_offset;
}
D3D12_CPU_DESCRIPTOR_HANDLE RenderInterface_DX12::TextureMemoryManager::Alloc_RenderTargetResourceView(ID3D12Resource* p_resource,
D3D12MA::VirtualAllocation* p_alloc)
{
RMLUI_ZoneScopedN("DirectX 12 - TextureMemoryManager::Alloc_RenderTargetResourceView");
RMLUI_ASSERTMSG(p_resource, "must be allocated!");
RMLUI_ASSERTMSG(m_p_device, "must be allocated!");
RMLUI_ASSERTMSG(m_p_virtual_block_for_render_target_heap_allocations, "must be initialized");
RMLUI_ASSERTMSG(p_alloc, "must be valid!");
RMLUI_ASSERTMSG(m_p_descriptor_heap_rtv, "must be valid!");
D3D12_CPU_DESCRIPTOR_HANDLE calculated_offset{};
if (p_resource)
{
if (m_p_virtual_block_for_render_target_heap_allocations)
{
if (m_p_device)
{
if (m_p_descriptor_heap_rtv)
{
D3D12MA::VIRTUAL_ALLOCATION_DESC desc_alloc = {};
desc_alloc.Size = m_size_rtv_descriptor;
UINT64 offset{};
auto status = m_p_virtual_block_for_render_target_heap_allocations->Allocate(&desc_alloc, p_alloc, &offset);
RMLUI_DX_VERIFY_MSG(status,
"failed to allocate descriptor rtv, it means you need to resize and set higher size than previous, overflow (see "
"RMLUI_RENDER_BACKEND_FIELD_DESCRIPTOR_HEAP_RTV constant)");
D3D12_RENDER_TARGET_VIEW_DESC desc_rtv = {};
desc_rtv.Format = RMLUI_RENDER_BACKEND_FIELD_COLOR_TEXTURE_FORMAT;
desc_rtv.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS;
calculated_offset.ptr = (m_p_descriptor_heap_rtv->GetCPUDescriptorHandleForHeapStart().ptr + offset);
m_p_device->CreateRenderTargetView(p_resource, &desc_rtv, calculated_offset);
}
}
}
}
return calculated_offset;
}
#if defined _MSC_VER
#pragma warning(push, 0)
#pragma warning(push, 4189)
#elif defined __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wall"
#pragma clang diagnostic ignored "-Wextra"
#pragma clang diagnostic ignored "-Wnullability-extension"
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#pragma clang diagnostic ignored "-Wnullability-completeness"
#elif defined __GNUC__
#pragma GCC system_header
#endif
#include "RmlUi_DirectX/D3D12MemAlloc.cpp"
#include "RmlUi_DirectX/offsetAllocator.cpp"
#if defined _MSC_VER
#pragma warning(pop)
#elif defined __clang__
#pragma clang diagnostic pop
#endif