mirror of
https://github.com/GabsPuNs/Project-Zenith-Main.git
synced 2026-07-09 23:38:09 +00:00
Nearest-Neighbor is the default filter used in Minecraft for the UI. Hardcore mode never existed in console version. Someone just added it with fake 4J comments.
803 lines
22 KiB
C++
803 lines
22 KiB
C++
#pragma push_macro("byte")
|
|
#pragma push_macro("GetNextSibling")
|
|
#pragma push_macro("GetFirstChild")
|
|
#undef byte
|
|
#undef GetNextSibling
|
|
#undef GetFirstChild
|
|
|
|
#include "RmlUi_Renderer_D3D11.h"
|
|
#include <RmlUi/Core/Core.h>
|
|
#include <RmlUi/Core/FileInterface.h>
|
|
#include <d3d11.h>
|
|
#include <d3dcompiler.h>
|
|
|
|
#pragma comment(lib, "d3dcompiler.lib")
|
|
|
|
// ---- Embedded HLSL shaders ----
|
|
|
|
static const char* g_vs_hlsl = R"(
|
|
cbuffer Projection : register(b0) {
|
|
float4x4 proj;
|
|
float2 translate;
|
|
};
|
|
struct VS_IN {
|
|
float2 pos : POSITION;
|
|
float2 tex : TEXCOORD0;
|
|
float4 col : COLOR0;
|
|
};
|
|
struct VS_OUT {
|
|
float4 pos : SV_POSITION;
|
|
float2 tex : TEXCOORD0;
|
|
float4 col : COLOR0;
|
|
};
|
|
VS_OUT main(VS_IN input) {
|
|
VS_OUT output;
|
|
float2 p = input.pos + translate;
|
|
output.pos = mul(proj, float4(p, 0.0, 1.0));
|
|
output.tex = input.tex;
|
|
output.col = input.col;
|
|
return output;
|
|
}
|
|
)";
|
|
|
|
static const char* g_ps_textured_hlsl = R"(
|
|
struct PS_IN {
|
|
float4 pos : SV_POSITION;
|
|
float2 tex : TEXCOORD0;
|
|
float4 col : COLOR0;
|
|
};
|
|
Texture2D tex : register(t0);
|
|
SamplerState samp : register(s0);
|
|
float4 main(PS_IN input) : SV_Target {
|
|
float4 tex_col = tex.Sample(samp, input.tex);
|
|
return input.col * tex_col;
|
|
}
|
|
)";
|
|
|
|
static const char* g_ps_color_hlsl = R"(
|
|
struct PS_IN {
|
|
float4 pos : SV_POSITION;
|
|
float2 tex : TEXCOORD0;
|
|
float4 col : COLOR0;
|
|
};
|
|
float4 main(PS_IN input) : SV_Target {
|
|
return input.col;
|
|
}
|
|
)";
|
|
|
|
RenderInterface_D3D11::RenderInterface_D3D11(ID3D11Device* device, ID3D11DeviceContext* context)
|
|
: m_device(device)
|
|
, m_context(context)
|
|
, m_initialised(false)
|
|
, m_vertex_shader(nullptr)
|
|
, m_pixel_shader_textured(nullptr)
|
|
, m_pixel_shader_color(nullptr)
|
|
, m_input_layout(nullptr)
|
|
, m_blend_state(nullptr)
|
|
, m_depth_state(nullptr)
|
|
, m_raster_state(nullptr)
|
|
, m_sampler_state(nullptr)
|
|
, m_projection_cb(nullptr)
|
|
, m_transform_active(false)
|
|
, m_scissor_enabled(false)
|
|
, m_white_texture(nullptr)
|
|
, m_width(0)
|
|
, m_height(0)
|
|
, m_next_geometry_handle(1)
|
|
, m_next_texture_handle(1)
|
|
{
|
|
// Initialise COM for WIC image loading
|
|
HRESULT com_hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
|
|
if (FAILED(com_hr) && com_hr != RPC_E_CHANGED_MODE)
|
|
{
|
|
app.DebugPrintf("[RmlUi] CoInitializeEx failed: 0x%08X\n", com_hr);
|
|
}
|
|
|
|
m_projection = Rml::Matrix4f::Identity();
|
|
|
|
if (!CompileShaders())
|
|
{
|
|
app.DebugPrintf("[RmlUi] Failed to compile shaders!\n");
|
|
return;
|
|
}
|
|
|
|
if (!CreateStateObjects())
|
|
{
|
|
app.DebugPrintf("[RmlUi] Failed to create state objects!\n");
|
|
return;
|
|
}
|
|
|
|
// Create 1x1 white texture for untextured geometry
|
|
{
|
|
unsigned char white_pixel[4] = { 255, 255, 255, 255 };
|
|
D3D11_SUBRESOURCE_DATA init_data = {};
|
|
init_data.pSysMem = white_pixel;
|
|
init_data.SysMemPitch = 4;
|
|
|
|
D3D11_TEXTURE2D_DESC desc = {};
|
|
desc.Width = 1;
|
|
desc.Height = 1;
|
|
desc.MipLevels = 1;
|
|
desc.ArraySize = 1;
|
|
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
desc.SampleDesc.Count = 1;
|
|
desc.Usage = D3D11_USAGE_IMMUTABLE;
|
|
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
|
|
|
ID3D11Texture2D* tex = nullptr;
|
|
if (SUCCEEDED(m_device->CreateTexture2D(&desc, &init_data, &tex)))
|
|
{
|
|
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = {};
|
|
srv_desc.Format = desc.Format;
|
|
srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
|
srv_desc.Texture2D.MipLevels = 1;
|
|
m_device->CreateShaderResourceView(tex, &srv_desc, &m_white_texture);
|
|
tex->Release();
|
|
}
|
|
}
|
|
|
|
m_initialised = true;
|
|
}
|
|
|
|
RenderInterface_D3D11::~RenderInterface_D3D11()
|
|
{
|
|
if (m_white_texture) m_white_texture->Release();
|
|
if (m_projection_cb) m_projection_cb->Release();
|
|
if (m_sampler_state) m_sampler_state->Release();
|
|
if (m_raster_state) m_raster_state->Release();
|
|
if (m_depth_state) m_depth_state->Release();
|
|
if (m_blend_state) m_blend_state->Release();
|
|
if (m_input_layout) m_input_layout->Release();
|
|
if (m_vertex_shader) m_vertex_shader->Release();
|
|
if (m_pixel_shader_textured) m_pixel_shader_textured->Release();
|
|
if (m_pixel_shader_color) m_pixel_shader_color->Release();
|
|
|
|
for (auto* g : m_geometry_lookup)
|
|
{
|
|
if (g)
|
|
{
|
|
if (g->vertex_buffer) g->vertex_buffer->Release();
|
|
if (g->index_buffer) g->index_buffer->Release();
|
|
delete g;
|
|
}
|
|
}
|
|
|
|
for (auto* t : m_texture_lookup)
|
|
{
|
|
if (t)
|
|
{
|
|
if (t->srv) t->srv->Release();
|
|
delete t;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool RenderInterface_D3D11::CompileShaders()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Vertex shader
|
|
ID3DBlob* vs_blob = nullptr;
|
|
ID3DBlob* error_blob = nullptr;
|
|
hr = D3DCompile(g_vs_hlsl, strlen(g_vs_hlsl), "vs", nullptr, nullptr, "main", "vs_4_0",
|
|
D3DCOMPILE_ENABLE_STRICTNESS, 0, &vs_blob, &error_blob);
|
|
if (FAILED(hr))
|
|
{
|
|
if (error_blob)
|
|
{
|
|
app.DebugPrintf("[RmlUi] VS compile error: %s\n", (const char*)error_blob->GetBufferPointer());
|
|
error_blob->Release();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
hr = m_device->CreateVertexShader(vs_blob->GetBufferPointer(), vs_blob->GetBufferSize(), nullptr, &m_vertex_shader);
|
|
if (FAILED(hr))
|
|
{
|
|
vs_blob->Release();
|
|
return false;
|
|
}
|
|
|
|
// Input layout
|
|
D3D11_INPUT_ELEMENT_DESC layout[] = {
|
|
{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(RmlD3D11Vertex, position), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
|
{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, offsetof(RmlD3D11Vertex, texcoord), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
|
{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, offsetof(RmlD3D11Vertex, color), D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
|
};
|
|
hr = m_device->CreateInputLayout(layout, 3, vs_blob->GetBufferPointer(), vs_blob->GetBufferSize(), &m_input_layout);
|
|
vs_blob->Release();
|
|
if (FAILED(hr))
|
|
return false;
|
|
|
|
// Textured pixel shader
|
|
ID3DBlob* ps_blob = nullptr;
|
|
hr = D3DCompile(g_ps_textured_hlsl, strlen(g_ps_textured_hlsl), "ps_tex", nullptr, nullptr, "main", "ps_4_0",
|
|
D3DCOMPILE_ENABLE_STRICTNESS, 0, &ps_blob, &error_blob);
|
|
if (FAILED(hr))
|
|
{
|
|
if (error_blob)
|
|
{
|
|
app.DebugPrintf("[RmlUi] PS textured compile error: %s\n", (const char*)error_blob->GetBufferPointer());
|
|
error_blob->Release();
|
|
}
|
|
return false;
|
|
}
|
|
hr = m_device->CreatePixelShader(ps_blob->GetBufferPointer(), ps_blob->GetBufferSize(), nullptr, &m_pixel_shader_textured);
|
|
ps_blob->Release();
|
|
if (FAILED(hr))
|
|
return false;
|
|
|
|
// Color-only pixel shader
|
|
hr = D3DCompile(g_ps_color_hlsl, strlen(g_ps_color_hlsl), "ps_col", nullptr, nullptr, "main", "ps_4_0",
|
|
D3DCOMPILE_ENABLE_STRICTNESS, 0, &ps_blob, &error_blob);
|
|
if (FAILED(hr))
|
|
{
|
|
if (error_blob)
|
|
{
|
|
app.DebugPrintf("[RmlUi] PS color compile error: %s\n", (const char*)error_blob->GetBufferPointer());
|
|
error_blob->Release();
|
|
}
|
|
return false;
|
|
}
|
|
hr = m_device->CreatePixelShader(ps_blob->GetBufferPointer(), ps_blob->GetBufferSize(), nullptr, &m_pixel_shader_color);
|
|
ps_blob->Release();
|
|
if (FAILED(hr))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RenderInterface_D3D11::CreateStateObjects()
|
|
{
|
|
HRESULT hr;
|
|
|
|
// Blend state: premultiplied alpha blending
|
|
D3D11_BLEND_DESC blend_desc = {};
|
|
blend_desc.RenderTarget[0].BlendEnable = TRUE;
|
|
blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
|
|
blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
|
|
blend_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
|
|
blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
|
|
blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
|
|
blend_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
|
|
blend_desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
|
|
hr = m_device->CreateBlendState(&blend_desc, &m_blend_state);
|
|
if (FAILED(hr)) return false;
|
|
|
|
// Depth-stencil state: disable depth testing
|
|
D3D11_DEPTH_STENCIL_DESC ds_desc = {};
|
|
ds_desc.DepthEnable = FALSE;
|
|
ds_desc.StencilEnable = FALSE;
|
|
hr = m_device->CreateDepthStencilState(&ds_desc, &m_depth_state);
|
|
if (FAILED(hr)) return false;
|
|
|
|
// Rasterizer state: no culling, scissor enabled
|
|
D3D11_RASTERIZER_DESC rast_desc = {};
|
|
rast_desc.FillMode = D3D11_FILL_SOLID;
|
|
rast_desc.CullMode = D3D11_CULL_NONE;
|
|
rast_desc.ScissorEnable = TRUE;
|
|
rast_desc.DepthClipEnable = TRUE;
|
|
rast_desc.AntialiasedLineEnable = FALSE;
|
|
rast_desc.MultisampleEnable = FALSE;
|
|
rast_desc.FrontCounterClockwise = FALSE;
|
|
hr = m_device->CreateRasterizerState(&rast_desc, &m_raster_state);
|
|
if (FAILED(hr)) return false;
|
|
|
|
// Sampler state: Nearest-Neighbor, clamp
|
|
D3D11_SAMPLER_DESC samp_desc = {};
|
|
samp_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
|
|
samp_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
|
|
samp_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
|
|
samp_desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
|
|
samp_desc.MaxLOD = FLT_MAX;
|
|
hr = m_device->CreateSamplerState(&samp_desc, &m_sampler_state);
|
|
if (FAILED(hr)) return false;
|
|
|
|
// Constant buffer: float4x4 + float2 (translation).
|
|
// D3D11 requires constant buffer sizes to be multiples of 16 bytes.
|
|
// HLSL layout: float4x4(64) + float2(8) = 72, padded to 80.
|
|
constexpr UINT kCBufferSize = 80;
|
|
D3D11_BUFFER_DESC cb_desc = {};
|
|
cb_desc.ByteWidth = kCBufferSize;
|
|
cb_desc.Usage = D3D11_USAGE_DYNAMIC;
|
|
cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
|
|
cb_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
|
hr = m_device->CreateBuffer(&cb_desc, nullptr, &m_projection_cb);
|
|
if (FAILED(hr)) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void RenderInterface_D3D11::SetViewport(int width, int height)
|
|
{
|
|
m_width = width;
|
|
m_height = height;
|
|
|
|
// Orthographic projection: maps pixel coords to clip space
|
|
// (0,0) = top-left, (width,height) = bottom-right
|
|
float l = 0.0f;
|
|
float r = static_cast<float>(width);
|
|
float b = static_cast<float>(height);
|
|
float t = 0.0f;
|
|
float n = -1.0f;
|
|
float f = 1.0f;
|
|
|
|
m_projection = Rml::Matrix4f::ProjectOrtho(l, r, b, t, n, f);
|
|
|
|
// Update constant buffer (matrix + zero translation)
|
|
D3D11_MAPPED_SUBRESOURCE mapped;
|
|
if (SUCCEEDED(m_context->Map(m_projection_cb, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped)))
|
|
{
|
|
memcpy(mapped.pData, &m_projection, sizeof(Rml::Matrix4f));
|
|
float zero[2] = { 0, 0 };
|
|
memcpy(static_cast<char*>(mapped.pData) + sizeof(Rml::Matrix4f), zero, sizeof(zero));
|
|
m_context->Unmap(m_projection_cb, 0);
|
|
}
|
|
}
|
|
|
|
void RenderInterface_D3D11::BeginFrame()
|
|
{
|
|
if (!m_initialised)
|
|
return;
|
|
|
|
// Set viewport
|
|
D3D11_VIEWPORT vp;
|
|
vp.TopLeftX = 0;
|
|
vp.TopLeftY = 0;
|
|
vp.Width = static_cast<FLOAT>(m_width);
|
|
vp.Height = static_cast<FLOAT>(m_height);
|
|
vp.MinDepth = 0.0f;
|
|
vp.MaxDepth = 1.0f;
|
|
m_context->RSSetViewports(1, &vp);
|
|
|
|
// Set states
|
|
m_context->IASetInputLayout(m_input_layout);
|
|
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
m_context->VSSetConstantBuffers(0, 1, &m_projection_cb);
|
|
m_context->OMSetBlendState(m_blend_state, nullptr, 0xffffffff);
|
|
m_context->OMSetDepthStencilState(m_depth_state, 0);
|
|
m_context->RSSetState(m_raster_state);
|
|
m_context->PSSetSamplers(0, 1, &m_sampler_state);
|
|
}
|
|
|
|
void RenderInterface_D3D11::EndFrame()
|
|
{
|
|
}
|
|
|
|
// ---- Geometry ----
|
|
|
|
Rml::CompiledGeometryHandle RenderInterface_D3D11::CompileGeometry(Rml::Span<const Rml::Vertex> vertices, Rml::Span<const int> indices)
|
|
{
|
|
auto* geom = new CompiledGeometry();
|
|
geom->num_vertices = static_cast<int>(vertices.size());
|
|
geom->num_indices = static_cast<int>(indices.size());
|
|
geom->vertex_buffer = nullptr;
|
|
geom->index_buffer = nullptr;
|
|
|
|
// Convert Rml::Vertex to RmlD3D11Vertex
|
|
std::vector<RmlD3D11Vertex> converted(vertices.size());
|
|
for (size_t i = 0; i < vertices.size(); i++)
|
|
{
|
|
converted[i].position[0] = vertices[i].position.x;
|
|
converted[i].position[1] = vertices[i].position.y;
|
|
converted[i].texcoord[0] = vertices[i].tex_coord.x;
|
|
converted[i].texcoord[1] = vertices[i].tex_coord.y;
|
|
converted[i].color[0] = vertices[i].colour.red;
|
|
converted[i].color[1] = vertices[i].colour.green;
|
|
converted[i].color[2] = vertices[i].colour.blue;
|
|
converted[i].color[3] = vertices[i].colour.alpha;
|
|
}
|
|
|
|
geom->vertex_stride = sizeof(RmlD3D11Vertex);
|
|
|
|
// Create vertex buffer
|
|
D3D11_BUFFER_DESC vb_desc = {};
|
|
vb_desc.ByteWidth = static_cast<UINT>(vertices.size() * sizeof(RmlD3D11Vertex));
|
|
vb_desc.Usage = D3D11_USAGE_IMMUTABLE;
|
|
vb_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
|
|
|
D3D11_SUBRESOURCE_DATA vb_data = {};
|
|
vb_data.pSysMem = converted.data();
|
|
|
|
if (FAILED(m_device->CreateBuffer(&vb_desc, &vb_data, &geom->vertex_buffer)))
|
|
{
|
|
delete geom;
|
|
return 0;
|
|
}
|
|
|
|
// Create index buffer
|
|
bool large_indices = vertices.size() > 65535;
|
|
geom->index_format = large_indices ? DXGI_FORMAT_R32_UINT : DXGI_FORMAT_R16_UINT;
|
|
geom->index_stride = large_indices ? 4 : 2;
|
|
|
|
D3D11_BUFFER_DESC ib_desc = {};
|
|
ib_desc.ByteWidth = static_cast<UINT>(indices.size() * geom->index_stride);
|
|
ib_desc.Usage = D3D11_USAGE_IMMUTABLE;
|
|
ib_desc.BindFlags = D3D11_BIND_INDEX_BUFFER;
|
|
|
|
if (large_indices)
|
|
{
|
|
D3D11_SUBRESOURCE_DATA ib_data = {};
|
|
ib_data.pSysMem = indices.data();
|
|
if (FAILED(m_device->CreateBuffer(&ib_desc, &ib_data, &geom->index_buffer)))
|
|
{
|
|
geom->vertex_buffer->Release();
|
|
delete geom;
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Convert indices to 16-bit
|
|
std::vector<unsigned short> indices16(indices.size());
|
|
for (size_t i = 0; i < indices.size(); i++)
|
|
indices16[i] = static_cast<unsigned short>(indices[i]);
|
|
|
|
D3D11_SUBRESOURCE_DATA ib_data = {};
|
|
ib_data.pSysMem = indices16.data();
|
|
if (FAILED(m_device->CreateBuffer(&ib_desc, &ib_data, &geom->index_buffer)))
|
|
{
|
|
geom->vertex_buffer->Release();
|
|
delete geom;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Assign handle
|
|
Rml::CompiledGeometryHandle handle = m_next_geometry_handle++;
|
|
if (handle >= m_geometry_lookup.size())
|
|
m_geometry_lookup.resize(handle + 1, nullptr);
|
|
m_geometry_lookup[handle] = geom;
|
|
|
|
return handle;
|
|
}
|
|
|
|
void RenderInterface_D3D11::RenderGeometry(Rml::CompiledGeometryHandle geometry, Rml::Vector2f translation, Rml::TextureHandle texture)
|
|
{
|
|
if (!m_initialised)
|
|
return;
|
|
|
|
if (geometry == 0 || geometry >= m_geometry_lookup.size())
|
|
return;
|
|
|
|
CompiledGeometry* geom = m_geometry_lookup[geometry];
|
|
if (!geom)
|
|
return;
|
|
|
|
// Look up texture
|
|
ID3D11ShaderResourceView* texture_srv = m_white_texture;
|
|
if (texture != 0 && texture < m_texture_lookup.size())
|
|
{
|
|
TextureData* tex_data = m_texture_lookup[texture];
|
|
if (tex_data)
|
|
texture_srv = tex_data->srv;
|
|
}
|
|
|
|
DrawCompiledGeometry(geom, translation, texture_srv);
|
|
}
|
|
|
|
void RenderInterface_D3D11::DrawCompiledGeometry(CompiledGeometry* geom, const Rml::Vector2f& translation, ID3D11ShaderResourceView* texture_srv)
|
|
{
|
|
// Compute the transform matrix (projection, possibly combined with element transform).
|
|
// Translation is NOT baked into the matrix — it's uploaded as a separate cbuffer member
|
|
// and applied in the vertex shader before the projection.
|
|
Rml::Matrix4f transform_mat;
|
|
if (m_transform_active)
|
|
transform_mat = m_projection * m_transform;
|
|
else
|
|
transform_mat = m_projection;
|
|
|
|
// Update constant buffer: matrix (64 bytes) + translation (8 bytes)
|
|
D3D11_MAPPED_SUBRESOURCE mapped;
|
|
if (SUCCEEDED(m_context->Map(m_projection_cb, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped)))
|
|
{
|
|
memcpy(mapped.pData, &transform_mat, sizeof(Rml::Matrix4f));
|
|
float trans[2] = { translation.x, translation.y };
|
|
memcpy(static_cast<char*>(mapped.pData) + sizeof(Rml::Matrix4f), trans, sizeof(trans));
|
|
m_context->Unmap(m_projection_cb, 0);
|
|
}
|
|
|
|
// Select pixel shader
|
|
if (texture_srv == m_white_texture)
|
|
{
|
|
m_context->PSSetShader(m_pixel_shader_color, nullptr, 0);
|
|
}
|
|
else
|
|
{
|
|
m_context->PSSetShader(m_pixel_shader_textured, nullptr, 0);
|
|
m_context->PSSetShaderResources(0, 1, &texture_srv);
|
|
}
|
|
|
|
// Set scissor rect
|
|
if (m_scissor_enabled)
|
|
{
|
|
m_context->RSSetScissorRects(1, &m_scissor_rect);
|
|
}
|
|
else
|
|
{
|
|
D3D11_RECT full_rect = { 0, 0, m_width, m_height };
|
|
m_context->RSSetScissorRects(1, &full_rect);
|
|
}
|
|
|
|
m_context->VSSetShader(m_vertex_shader, nullptr, 0);
|
|
m_context->VSSetConstantBuffers(0, 1, &m_projection_cb);
|
|
|
|
// Set vertex buffer
|
|
UINT stride = geom->vertex_stride;
|
|
UINT offset = 0;
|
|
m_context->IASetVertexBuffers(0, 1, &geom->vertex_buffer, &stride, &offset);
|
|
m_context->IASetIndexBuffer(geom->index_buffer, geom->index_format, 0);
|
|
|
|
m_context->DrawIndexed(geom->num_indices, 0, 0);
|
|
|
|
// Cleanup texture binding
|
|
if (texture_srv != m_white_texture)
|
|
{
|
|
ID3D11ShaderResourceView* null_srv[1] = { nullptr };
|
|
m_context->PSSetShaderResources(0, 1, null_srv);
|
|
}
|
|
}
|
|
|
|
void RenderInterface_D3D11::ReleaseGeometry(Rml::CompiledGeometryHandle geometry)
|
|
{
|
|
if (geometry == 0 || geometry >= m_geometry_lookup.size())
|
|
return;
|
|
|
|
CompiledGeometry* geom = m_geometry_lookup[geometry];
|
|
if (geom)
|
|
{
|
|
if (geom->vertex_buffer) geom->vertex_buffer->Release();
|
|
if (geom->index_buffer) geom->index_buffer->Release();
|
|
delete geom;
|
|
}
|
|
m_geometry_lookup[geometry] = nullptr;
|
|
}
|
|
|
|
// ---- Textures ----
|
|
|
|
// Include WIC for texture loading
|
|
#include <wincodec.h>
|
|
#pragma comment(lib, "windowscodecs.lib")
|
|
|
|
static bool LoadImageWithWIC(const unsigned char* file_data, size_t file_size, std::vector<unsigned char>& out_rgba, int& out_w, int& out_h)
|
|
{
|
|
HRESULT hr;
|
|
IWICImagingFactory* factory = nullptr;
|
|
hr = CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory));
|
|
if (FAILED(hr) || !factory)
|
|
return false;
|
|
|
|
IWICStream* stream = nullptr;
|
|
hr = factory->CreateStream(&stream);
|
|
if (FAILED(hr))
|
|
{
|
|
factory->Release();
|
|
return false;
|
|
}
|
|
|
|
hr = stream->InitializeFromMemory(const_cast<BYTE*>(file_data), static_cast<DWORD>(file_size));
|
|
if (FAILED(hr))
|
|
{
|
|
stream->Release();
|
|
factory->Release();
|
|
return false;
|
|
}
|
|
|
|
IWICBitmapDecoder* decoder = nullptr;
|
|
hr = factory->CreateDecoderFromStream(stream, nullptr, WICDecodeMetadataCacheOnLoad, &decoder);
|
|
stream->Release();
|
|
if (FAILED(hr))
|
|
{
|
|
factory->Release();
|
|
return false;
|
|
}
|
|
|
|
IWICBitmapFrameDecode* frame = nullptr;
|
|
hr = decoder->GetFrame(0, &frame);
|
|
if (FAILED(hr))
|
|
{
|
|
decoder->Release();
|
|
factory->Release();
|
|
return false;
|
|
}
|
|
|
|
UINT w = 0, h = 0;
|
|
frame->GetSize(&w, &h);
|
|
out_w = static_cast<int>(w);
|
|
out_h = static_cast<int>(h);
|
|
|
|
// Convert to 32bpp BGRA (WIC native format) then swap to RGBA
|
|
IWICFormatConverter* converter = nullptr;
|
|
hr = factory->CreateFormatConverter(&converter);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = converter->Initialize(frame, GUID_WICPixelFormat32bppBGRA, WICBitmapDitherTypeNone, nullptr, 0.0, WICBitmapPaletteTypeCustom);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
out_rgba.resize(w * h * 4);
|
|
hr = converter->CopyPixels(nullptr, w * 4, static_cast<UINT>(out_rgba.size()), out_rgba.data());
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Swap BGRA -> RGBA
|
|
for (size_t i = 0; i < w * h; i++)
|
|
{
|
|
unsigned char tmp = out_rgba[i * 4];
|
|
out_rgba[i * 4] = out_rgba[i * 4 + 2];
|
|
out_rgba[i * 4 + 2] = tmp;
|
|
}
|
|
}
|
|
}
|
|
converter->Release();
|
|
}
|
|
|
|
frame->Release();
|
|
decoder->Release();
|
|
factory->Release();
|
|
|
|
return !out_rgba.empty();
|
|
}
|
|
|
|
Rml::TextureHandle RenderInterface_D3D11::LoadTexture(Rml::Vector2i& texture_dimensions, const Rml::String& source)
|
|
{
|
|
Rml::FileInterface* file_interface = Rml::GetFileInterface();
|
|
Rml::FileHandle file_handle = file_interface->Open(source);
|
|
if (!file_handle)
|
|
return 0;
|
|
|
|
file_interface->Seek(file_handle, 0, SEEK_END);
|
|
long file_size = static_cast<long>(file_interface->Tell(file_handle));
|
|
file_interface->Seek(file_handle, 0, SEEK_SET);
|
|
|
|
std::vector<unsigned char> file_data(file_size);
|
|
file_interface->Read(file_data.data(), file_size, file_handle);
|
|
file_interface->Close(file_handle);
|
|
|
|
int w = 0, h = 0;
|
|
std::vector<unsigned char> rgba;
|
|
if (!LoadImageWithWIC(file_data.data(), file_data.size(), rgba, w, h))
|
|
{
|
|
app.DebugPrintf("[RmlUi] Failed to load texture: %s\n", source.c_str());
|
|
return 0;
|
|
}
|
|
|
|
texture_dimensions.x = w;
|
|
texture_dimensions.y = h;
|
|
|
|
D3D11_TEXTURE2D_DESC desc = {};
|
|
desc.Width = static_cast<UINT>(w);
|
|
desc.Height = static_cast<UINT>(h);
|
|
desc.MipLevels = 1;
|
|
desc.ArraySize = 1;
|
|
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
desc.SampleDesc.Count = 1;
|
|
desc.Usage = D3D11_USAGE_IMMUTABLE;
|
|
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
|
|
|
D3D11_SUBRESOURCE_DATA init_data = {};
|
|
init_data.pSysMem = rgba.data();
|
|
init_data.SysMemPitch = static_cast<UINT>(w * 4);
|
|
|
|
ID3D11Texture2D* tex = nullptr;
|
|
if (FAILED(m_device->CreateTexture2D(&desc, &init_data, &tex)))
|
|
return 0;
|
|
|
|
ID3D11ShaderResourceView* srv = nullptr;
|
|
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = {};
|
|
srv_desc.Format = desc.Format;
|
|
srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
|
srv_desc.Texture2D.MipLevels = 1;
|
|
m_device->CreateShaderResourceView(tex, &srv_desc, &srv);
|
|
tex->Release();
|
|
|
|
if (!srv)
|
|
return 0;
|
|
|
|
auto* tex_data = new TextureData();
|
|
tex_data->srv = srv;
|
|
tex_data->width = w;
|
|
tex_data->height = h;
|
|
|
|
Rml::TextureHandle handle = m_next_texture_handle++;
|
|
if (handle >= m_texture_lookup.size())
|
|
m_texture_lookup.resize(handle + 1, nullptr);
|
|
m_texture_lookup[handle] = tex_data;
|
|
|
|
return handle;
|
|
}
|
|
|
|
Rml::TextureHandle RenderInterface_D3D11::GenerateTexture(Rml::Span<const Rml::byte> source_data, Rml::Vector2i source_dimensions)
|
|
{
|
|
int w = source_dimensions.x;
|
|
int h = source_dimensions.y;
|
|
|
|
D3D11_TEXTURE2D_DESC desc = {};
|
|
desc.Width = static_cast<UINT>(w);
|
|
desc.Height = static_cast<UINT>(h);
|
|
desc.MipLevels = 1;
|
|
desc.ArraySize = 1;
|
|
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
|
desc.SampleDesc.Count = 1;
|
|
desc.Usage = D3D11_USAGE_IMMUTABLE;
|
|
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
|
|
|
|
D3D11_SUBRESOURCE_DATA init_data = {};
|
|
init_data.pSysMem = source_data.data();
|
|
init_data.SysMemPitch = static_cast<UINT>(w * 4);
|
|
|
|
ID3D11Texture2D* tex = nullptr;
|
|
if (FAILED(m_device->CreateTexture2D(&desc, &init_data, &tex)))
|
|
return 0;
|
|
|
|
ID3D11ShaderResourceView* srv = nullptr;
|
|
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = {};
|
|
srv_desc.Format = desc.Format;
|
|
srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
|
srv_desc.Texture2D.MipLevels = 1;
|
|
m_device->CreateShaderResourceView(tex, &srv_desc, &srv);
|
|
tex->Release();
|
|
|
|
if (!srv)
|
|
return 0;
|
|
|
|
auto* tex_data = new TextureData();
|
|
tex_data->srv = srv;
|
|
tex_data->width = w;
|
|
tex_data->height = h;
|
|
|
|
Rml::TextureHandle handle = m_next_texture_handle++;
|
|
if (handle >= m_texture_lookup.size())
|
|
m_texture_lookup.resize(handle + 1, nullptr);
|
|
m_texture_lookup[handle] = tex_data;
|
|
|
|
return handle;
|
|
}
|
|
|
|
void RenderInterface_D3D11::ReleaseTexture(Rml::TextureHandle texture)
|
|
{
|
|
if (texture == 0 || texture >= m_texture_lookup.size())
|
|
return;
|
|
|
|
TextureData* tex_data = m_texture_lookup[texture];
|
|
if (tex_data)
|
|
{
|
|
if (tex_data->srv) tex_data->srv->Release();
|
|
delete tex_data;
|
|
}
|
|
m_texture_lookup[texture] = nullptr;
|
|
}
|
|
|
|
// ---- Scissor ----
|
|
|
|
void RenderInterface_D3D11::EnableScissorRegion(bool enable)
|
|
{
|
|
m_scissor_enabled = enable;
|
|
}
|
|
|
|
void RenderInterface_D3D11::SetScissorRegion(Rml::Rectanglei region)
|
|
{
|
|
m_scissor_rect.left = region.Left();
|
|
m_scissor_rect.top = region.Top();
|
|
m_scissor_rect.right = region.Right();
|
|
m_scissor_rect.bottom = region.Bottom();
|
|
}
|
|
|
|
// ---- Transform ----
|
|
|
|
void RenderInterface_D3D11::SetTransform(const Rml::Matrix4f* transform)
|
|
{
|
|
if (transform)
|
|
{
|
|
m_transform = *transform;
|
|
m_transform_active = true;
|
|
}
|
|
else
|
|
{
|
|
m_transform = Rml::Matrix4f::Identity();
|
|
m_transform_active = false;
|
|
}
|
|
}
|
|
|
|
#pragma pop_macro("GetFirstChild")
|
|
#pragma pop_macro("GetNextSibling")
|
|
#pragma pop_macro("byte")
|