优化字体渲染,优化内存占用

This commit is contained in:
Nanako 2024-12-23 18:44:13 +08:00
parent cdd77d3cbb
commit c158670a19
14 changed files with 254 additions and 117 deletions

View File

@ -5,7 +5,7 @@ class scope_exit_guard {
public: public:
scope_exit_guard(FuncType&& in_func) : func((FuncType&&)in_func) { scope_exit_guard(FuncType&& in_func) : func((FuncType&&)in_func) {
} }
~scope_exit_guard() { virtual ~scope_exit_guard() {
func(); func();
} }
private: private:
@ -13,6 +13,7 @@ private:
}; };
struct scope_exit_syntax_support { struct scope_exit_syntax_support {
virtual ~scope_exit_syntax_support() = default;
template <typename FuncType> template <typename FuncType>
scope_exit_guard<FuncType> operator+(FuncType&& InFunc) scope_exit_guard<FuncType> operator+(FuncType&& InFunc)
{ {

View File

@ -58,12 +58,12 @@ bool dx_renderer::render() {
return true; return true;
} }
renderer_texture* dx_renderer::create_texture(uint32_t in_size_width, uint32_t in_size_height, texture_format in_format) { renderer_texture* dx_renderer::create_texture(uint32_t in_size_width, uint32_t in_size_height, texture_format in_format, device_memory_type in_memory_type) {
return new dx_texture({ in_size_width, in_size_height }, in_format); return new dx_texture({ in_size_width, in_size_height }, in_format, in_memory_type);
} }
renderer_texture_array* dx_renderer::create_texture_array(uint32_t in_size_width, uint32_t in_size_height, uint32_t in_count, texture_format in_format) { renderer_texture_array* dx_renderer::create_texture_array(uint32_t in_size_width, uint32_t in_size_height, uint32_t in_count, texture_format in_format, device_memory_type in_memory_type) {
return new dx_texture_array({ in_size_width, in_size_height }, in_format, in_count); return new dx_texture_array({ in_size_width, in_size_height }, in_format, in_count, in_memory_type);
} }
Eigen::Matrix4f dx_renderer::make_projection_matrix(const Eigen::Vector2i& size) { Eigen::Matrix4f dx_renderer::make_projection_matrix(const Eigen::Vector2i& size) {

View File

@ -16,8 +16,8 @@ public:
void destroy() override; void destroy() override;
bool render() override; bool render() override;
renderer_texture* create_texture(uint32_t in_size_width, uint32_t in_size_height, texture_format in_format) override; renderer_texture* create_texture(uint32_t in_size_width, uint32_t in_size_height, texture_format in_format, device_memory_type in_memory_type) override;
renderer_texture_array* create_texture_array(uint32_t in_size_width, uint32_t in_size_height, uint32_t in_count, texture_format in_format) override; renderer_texture_array* create_texture_array(uint32_t in_size_width, uint32_t in_size_height, uint32_t in_count, texture_format in_format, device_memory_type in_memory_type) override;
[[nodiscard]] ID3D11Device* get_d3d_device() const { return device; } [[nodiscard]] ID3D11Device* get_d3d_device() const { return device; }
[[nodiscard]] ID3D11DeviceContext* get_d3d_context() const { return context; } [[nodiscard]] ID3D11DeviceContext* get_d3d_context() const { return context; }

View File

@ -1,9 +1,24 @@
#include "dx_texture.h" #include "dx_texture.h"
#include "dx_renderer.h" #include "dx_renderer.h"
#include "misc/scope_exit.h"
dx_texture::dx_texture(const Eigen::Vector2i& size, const texture_format format, uint32_t count) : renderer_texture(format) { int get_cpu_access_flag(device_memory_type in_memory_type) {
create_texture(size, format, count); switch (in_memory_type) {
case device_memory_type::gpu:
return 0;
case device_memory_type::shared:
return D3D11_CPU_ACCESS_WRITE;
case device_memory_type::cpu:
return D3D11_CPU_ACCESS_WRITE;
default:
return D3D11_CPU_ACCESS_WRITE;
}
}
dx_texture::dx_texture(const Eigen::Vector2i& size, const texture_format format, device_memory_type in_memory_type, uint32_t count) : renderer_texture(format) {
memory_type = in_memory_type;
create_texture(size, format, in_memory_type, count);
} }
dx_texture::~dx_texture() { dx_texture::~dx_texture() {
@ -18,28 +33,48 @@ void dx_texture::unlock() {
return unlock(0); return unlock(0);
} }
void* dx_texture::lock(uint32_t index, uint32_t* out_row_pitch) const { void* dx_texture::lock(uint32_t index, uint32_t* out_row_pitch) {
D3D11_TEXTURE2D_DESC desc; auto r = aorii::get_renderer<dx_renderer>();
m_texture->GetDesc(&desc); auto hr = r->get_d3d_device()->CreateTexture2D(&upload_desc, nullptr, &upload_texture);
if (FAILED(hr)) {
spdlog::critical("无法创建上传纹理, 错误: 0x{:08X}", static_cast<unsigned int>(hr));
return nullptr;
}
D3D11_MAPPED_SUBRESOURCE mappedResource; D3D11_MAPPED_SUBRESOURCE mappedResource;
auto context = aorii::get_renderer<dx_renderer>()->get_d3d_context(); const auto context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
context->Map(m_texture, index, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource); hr = context->Map(upload_texture, index, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if (FAILED(hr)) {
spdlog::critical("无法映射纹理, 错误: 0x{:08X}", static_cast<unsigned int>(hr));
return nullptr;
}
if (out_row_pitch) *out_row_pitch = mappedResource.RowPitch; if (out_row_pitch) *out_row_pitch = mappedResource.RowPitch;
return mappedResource.pData; return mappedResource.pData;
} }
void dx_texture::unlock(uint32_t index) const { void dx_texture::unlock(uint32_t index) const {
auto context = aorii::get_renderer<dx_renderer>()->get_d3d_context(); const auto context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
context->Unmap(m_texture, index); context->Unmap(upload_texture, index);
// 复制到目标纹理
context->CopyResource(m_texture, upload_texture);
} }
void dx_texture::update_subresource(const Eigen::AlignedBox2i& in_area, const void* in_buffer, const uint32_t in_row_pitch) { void dx_texture::update_subresource(const Eigen::AlignedBox2i& in_area, const void* in_buffer, const uint32_t in_row_pitch) {
update_subresource(0, in_area, in_buffer, in_row_pitch); update_subresource(0, in_area, in_buffer, in_row_pitch);
} }
void dx_texture::update_subresource(uint32_t index, const Eigen::AlignedBox2i& in_area, const void* in_buffer, void dx_texture::update_subresource(uint32_t index, const Eigen::AlignedBox2i& in_area, const void* in_buffer, const uint32_t in_row_pitch) {
const uint32_t in_row_pitch) const { auto r = aorii::get_renderer<dx_renderer>();
D3D11_BOX box = {}; const auto context = r->get_d3d_context();
auto device = r->get_d3d_device();
const auto& size = in_area.sizes();
// 更新上传纹理描述符
upload_desc.Width = size.x();
upload_desc.Height = size.y();
// 定义目标区域
D3D11_BOX box{};
box.left = in_area.min().x(); box.left = in_area.min().x();
box.top = in_area.min().y(); box.top = in_area.min().y();
box.right = in_area.max().x(); box.right = in_area.max().x();
@ -47,8 +82,59 @@ void dx_texture::update_subresource(uint32_t index, const Eigen::AlignedBox2i& i
box.front = 0; box.front = 0;
box.back = 1; box.back = 1;
const auto context = aorii::get_renderer<dx_renderer>()->get_d3d_context(); // 定义源区域
context->UpdateSubresource(m_texture, index, &box, in_buffer, in_row_pitch, 0); D3D11_BOX src_box{};
src_box.left = 0;
src_box.top = 0;
src_box.right = size.x();
src_box.bottom = size.y();
src_box.front = 0;
src_box.back = 1;
// 创建临时上传纹理
auto hr = device->CreateTexture2D(&upload_desc, nullptr, &upload_texture);
if (FAILED(hr)) {
spdlog::critical("无法创建上传纹理, 错误: 0x{:08X}", static_cast<unsigned int>(hr));
return;
}
ON_SCOPE_EXIT{
// 删除临时纹理
upload_texture->Release();
upload_texture = nullptr;
};
// 映射上传纹理
D3D11_MAPPED_SUBRESOURCE mapped;
hr = context->Map(upload_texture, 0, D3D11_MAP_WRITE, 0, &mapped);
if (FAILED(hr)) {
spdlog::critical("无法映射上传纹理, 错误: 0x{:08X}", static_cast<unsigned int>(hr));
return;
}
// 复制数据到上传纹理
const auto src_data = static_cast<const uint8_t*>(in_buffer);
const auto dst_data = static_cast<uint8_t*>(mapped.pData);
for (int y = 0; y < size.y(); ++y) {
memcpy(
dst_data + mapped.RowPitch * y,
src_data + in_row_pitch * y,
size.x()
);
}
// 解除映射
context->Unmap(upload_texture, 0);
// 复制到目标纹理
context->CopySubresourceRegion(
m_texture, // 目标纹理
index, // 目标子资源索引
box.left, box.top, 0, // 目标位置
upload_texture, // 源纹理
0, // 源子资源索引
&src_box // 源区域
);
} }
bool dx_texture::resize(const Eigen::Vector2i& size) { bool dx_texture::resize(const Eigen::Vector2i& size) {
@ -57,7 +143,7 @@ bool dx_texture::resize(const Eigen::Vector2i& size) {
if (desc.Width == size.x() && desc.Height == size.y()) { if (desc.Width == size.x() && desc.Height == size.y()) {
return true; return true;
} }
return create_texture(size, get_format()); return create_texture(size, get_format(), memory_type, get_count());
} }
Eigen::Vector2i dx_texture::size() { Eigen::Vector2i dx_texture::size() {
@ -79,10 +165,10 @@ void dx_texture::set_count(const uint32_t in_count) {
D3D11_TEXTURE2D_DESC desc; D3D11_TEXTURE2D_DESC desc;
m_texture->GetDesc(&desc); m_texture->GetDesc(&desc);
desc.ArraySize = in_count; desc.ArraySize = in_count;
create_texture({ desc.Width, desc.Height }, get_format(), in_count); create_texture({ desc.Width, desc.Height }, get_format(), memory_type, in_count);
} }
bool dx_texture::create_texture(const Eigen::Vector2i& in_size, texture_format in_format, uint32_t in_count) { bool dx_texture::create_texture(const Eigen::Vector2i& in_size, texture_format in_format, device_memory_type in_memory_type, uint32_t in_count) {
// 释放旧资源 // 释放旧资源
release_texture(); release_texture();
@ -96,16 +182,23 @@ bool dx_texture::create_texture(const Eigen::Vector2i& in_size, texture_format i
desc.Format = get_dxgi_format(in_format); desc.Format = get_dxgi_format(in_format);
desc.SampleDesc.Count = 1; desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0; desc.SampleDesc.Quality = 0;
desc.Usage = in_count > 1 ? D3D11_USAGE_DEFAULT : D3D11_USAGE_DYNAMIC; desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; desc.CPUAccessFlags = get_cpu_access_flag(in_memory_type);
desc.MiscFlags = 0; desc.MiscFlags = 0;
auto hr = d3d_device->CreateTexture2D(&desc, nullptr, &m_texture); auto hr = d3d_device->CreateTexture2D(&desc, nullptr, &m_texture);
if (FAILED(hr)) { if (FAILED(hr)) {
spdlog::critical("无法创建纹理, 错误: {0}", hr); spdlog::critical("无法创建纹理, 错误: 0x{:08X}", static_cast<unsigned int>(hr));
return false; return false;
} }
// 创建临时的上传纹理
upload_desc = desc;
upload_desc.ArraySize = 1;
upload_desc.Usage = D3D11_USAGE_STAGING; // 用于CPU到GPU的传输
upload_desc.BindFlags = 0;
upload_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = {}; D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = {};
srv_desc.Format = desc.Format; srv_desc.Format = desc.Format;
srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
@ -113,7 +206,7 @@ bool dx_texture::create_texture(const Eigen::Vector2i& in_size, texture_format i
srv_desc.Texture2D.MipLevels = 1; srv_desc.Texture2D.MipLevels = 1;
hr = d3d_device->CreateShaderResourceView(m_texture, &srv_desc, &srv); hr = d3d_device->CreateShaderResourceView(m_texture, &srv_desc, &srv);
if (FAILED(hr)) { if (FAILED(hr)) {
spdlog::critical("无法创建着色器资源视图, 错误: {0}", hr); spdlog::critical("无法创建着色器资源视图, 错误: 0x{:08X}", static_cast<unsigned int>(hr));
return false; return false;
} }
return true; return true;
@ -122,6 +215,8 @@ bool dx_texture::create_texture(const Eigen::Vector2i& in_size, texture_format i
void dx_texture::release_texture() { void dx_texture::release_texture() {
if (srv) srv->Release(); if (srv) srv->Release();
if (m_texture) m_texture->Release(); if (m_texture) m_texture->Release();
if (upload_texture) upload_texture->Release();
srv = nullptr; srv = nullptr;
m_texture = nullptr; m_texture = nullptr;
upload_texture = nullptr;
} }

View File

@ -7,17 +7,17 @@
// DX11纹理 // DX11纹理
class dx_texture : public renderer_texture { class dx_texture : public renderer_texture {
public: public:
dx_texture(const Eigen::Vector2i& size, texture_format format, uint32_t count = 1); dx_texture(const Eigen::Vector2i& size, texture_format format, device_memory_type in_memory_type, uint32_t count = 1);
~dx_texture() override; ~dx_texture() override;
void* lock(uint32_t* out_row_pitch) override; void* lock(uint32_t* out_row_pitch) override;
void unlock() override; void unlock() override;
void* lock(uint32_t index, uint32_t* out_row_pitch) const; void* lock(uint32_t index, uint32_t* out_row_pitch);
void unlock(uint32_t index) const; void unlock(uint32_t index) const;
void update_subresource(const Eigen::AlignedBox2i& in_area, const void* in_buffer, uint32_t in_row_pitch) override; void update_subresource(const Eigen::AlignedBox2i& in_area, const void* in_buffer, uint32_t in_row_pitch) override;
void update_subresource(uint32_t index, const Eigen::AlignedBox2i& in_area, const void* in_buffer, uint32_t in_row_pitch) const; void update_subresource(uint32_t index, const Eigen::AlignedBox2i& in_area, const void* in_buffer, uint32_t in_row_pitch);
bool resize(const Eigen::Vector2i& size) override; bool resize(const Eigen::Vector2i& size) override;
Eigen::Vector2i size() override; Eigen::Vector2i size() override;
@ -29,9 +29,13 @@ public:
[[nodiscard]] uint32_t get_count() const; [[nodiscard]] uint32_t get_count() const;
void set_count(uint32_t in_count); void set_count(uint32_t in_count);
private: private:
bool create_texture(const Eigen::Vector2i& in_size, texture_format in_format, uint32_t in_count = 1); bool create_texture(const Eigen::Vector2i& in_size, texture_format in_format, device_memory_type in_memory_type, uint32_t in_count = 1);
void release_texture(); void release_texture();
ID3D11Texture2D* m_texture{}; ID3D11Texture2D* m_texture{};
ID3D11ShaderResourceView* srv{}; ID3D11ShaderResourceView* srv{};
device_memory_type memory_type;
D3D11_TEXTURE2D_DESC upload_desc;
ID3D11Texture2D* upload_texture{};
}; };

View File

@ -6,8 +6,8 @@ class dx_texture;
class dx_texture_array : public renderer_texture_array { class dx_texture_array : public renderer_texture_array {
public: public:
dx_texture_array(const Eigen::Vector2i& size, texture_format format, const uint32_t count): renderer_texture_array(format) { dx_texture_array(const Eigen::Vector2i& size, texture_format format, const uint32_t count, device_memory_type in_memory_type): renderer_texture_array(format) {
texture = new dx_texture(size, format, count); texture = new dx_texture(size, format, in_memory_type, count);
} }
~dx_texture_array() override { delete texture; } ~dx_texture_array() override { delete texture; }

View File

@ -112,7 +112,7 @@ void dx_window::begin_frame() {
// context.draw_line( { 600, 600 }, { mouse_x, mouse_y }, { 1, 0, 1, 1 }, thickness); // context.draw_line( { 600, 600 }, { mouse_x, mouse_y }, { 1, 0, 1, 1 }, thickness);
// if (test_texture) context.draw_texture({ 0.f, 0.f }, test_texture->size().cast<float>(), test_texture); // if (test_texture) context.draw_texture({ 0.f, 0.f }, test_texture->size().cast<float>(), test_texture);
context.draw_string({0, 0}, L"你好,世界!", 64); context.draw_string({0, 0}, L"你好,世界!", 32);
context.flush(); context.flush();

View File

@ -38,7 +38,7 @@ renderer_texture* renderer::load_image(const std::string& file_path, texture_for
int target_channel_count = get_format_channel_count(in_format); int target_channel_count = get_format_channel_count(in_format);
unsigned int row_pitch = 0; unsigned int row_pitch = 0;
auto texture = create_texture(width, height, in_format); auto texture = create_texture(width, height, in_format, device_memory_type::gpu);
auto data = texture->lock(&row_pitch); auto data = texture->lock(&row_pitch);
for (int y = 0; y < height; ++y) { for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) { for (int x = 0; x < width; ++x) {

View File

@ -22,6 +22,12 @@ enum class renderer_api {
metal, metal,
}; };
enum class device_memory_type {
gpu, // 显存
shared, // 共享内存
cpu, // 内存
};
enum class texture_format { enum class texture_format {
// 未压缩的颜色格式 // 未压缩的颜色格式
@ -221,16 +227,16 @@ public:
virtual bool render() = 0; virtual bool render() = 0;
template<typename T> template<typename T>
renderer_texture* create_texture(const Eigen::Vector2<T>& in_size, texture_format in_format) { renderer_texture* create_texture(const Eigen::Vector2<T>& in_size, texture_format in_format, device_memory_type in_memory_type) {
return create_texture(in_size.x(), in_size.y(), in_format); return create_texture(in_size.x(), in_size.y(), in_format, in_memory_type);
} }
virtual renderer_texture* create_texture(uint32_t in_size_width, uint32_t in_size_height, texture_format in_format) = 0; virtual renderer_texture* create_texture(uint32_t in_size_width, uint32_t in_size_height, texture_format in_format, device_memory_type in_memory_type) = 0;
virtual void destroy_texture(renderer_texture* texture); virtual void destroy_texture(renderer_texture* texture);
template<typename T> template<typename T>
renderer_texture_array* create_texture_array(const Eigen::Vector2<T>& in_size, const int in_count, const texture_format in_format) { renderer_texture_array* create_texture_array(const Eigen::Vector2<T>& in_size, const int in_count, const texture_format in_format) {
return create_texture_array(in_size.x(), in_size.y(), in_count, in_format); return create_texture_array(in_size.x(), in_size.y(), in_count, in_format);
} }
virtual renderer_texture_array* create_texture_array(uint32_t in_size_width, uint32_t in_size_height, uint32_t in_count, texture_format in_format) = 0; virtual renderer_texture_array* create_texture_array(uint32_t in_size_width, uint32_t in_size_height, uint32_t in_count, texture_format in_format, device_memory_type in_memory_type) = 0;
virtual void destroy_texture_array(renderer_texture_array* texture_array); virtual void destroy_texture_array(renderer_texture_array* texture_array);
renderer_texture* load_image(const std::string& file_path, texture_format in_format); renderer_texture* load_image(const std::string& file_path, texture_format in_format);

View File

@ -6,7 +6,7 @@
aorii_text* text = nullptr; aorii_text* text = nullptr;
void renderer_context::init() { void renderer_context::init() {
text = new aorii_text(); text = new aorii_text(2048);
text->initialize(LR"(C:\Windows\Fonts\msyh.ttc)", 32); text->initialize(LR"(C:\Windows\Fonts\msyh.ttc)", 32);
text->precache_common_characters(); text->precache_common_characters();
} }
@ -39,28 +39,33 @@ void renderer_context::draw_texture(const Eigen::Vector2f& in_pos, const Eigen::
flush(); flush();
} }
void renderer_context::draw_string(const Eigen::Vector2f& in_pos, const std::wstring& in_str, float in_height, void renderer_context::draw_string(const Eigen::Vector2f& in_pos, const std::wstring& in_str, float in_height, const linear_color& in_color) {
const linear_color& in_color) {
to_text_pipeline(in_color); to_text_pipeline(in_color);
float cursor_x = in_pos.x(); float cursor_x = in_pos.x();
const float scale = in_height / 32; const float scale = in_height / text->get_font_pixel_size();
wchar_t last_char = 0;
for (const auto c : in_str) { for (const auto c : in_str) {
cursor_x += last_char ? text->get_kerning(last_char, c) : 0;
character_info info{}; character_info info{};
text->get_or_create_character(c, info); text->get_or_create_character(c, info);
// 根据in_height缩放字符大小 // 根据in_height缩放字符大小
const Eigen::Vector2f size = { info.width * scale, info.height * scale }; const float y_offset = (info.y_offset + text->get_ascent()) * scale;
const Eigen::Vector2f pos = { cursor_x, in_pos.y() - info.height }; const Eigen::Vector2f size { info.width * scale, info.height * scale };
cursor_x += size.x() + info.offset_x; const Eigen::Vector2f pos { cursor_x + info.x_offset * scale, in_pos.y() + y_offset };
cursor_x += info.advance * scale;
aorii_vertex_param param{}; aorii_vertex_param param{};
param.param_a1 = info.tex_u; param.param_a1 = info.tex_u;
param.param_a2 = info.tex_v; param.param_a2 = info.tex_v;
param.param_a3 = info.tex_z; param.param_a3 = info.tex_z;
param.param_b1 = info.width; param.param_b1 = info.width / (float)text->get_texture_size();
param.param_b2 = info.height; param.param_b2 = info.height / (float)text->get_texture_size();
make_rect(pos, size, in_color, 0, param); make_rect(pos, size, in_color, 0, param);
last_char = c;
} }
} }

View File

@ -10,6 +10,8 @@
#define STB_TRUETYPE_IMPLEMENTATION #define STB_TRUETYPE_IMPLEMENTATION
#include <stb_truetype.h> #include <stb_truetype.h>
#include "misc/scope_exit.h"
// 在SDFFontCache.cpp中添加实现 // 在SDFFontCache.cpp中添加实现
const std::wstring aorii_text::COMMON_ASCII = const std::wstring aorii_text::COMMON_ASCII =
L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
@ -23,8 +25,8 @@ const std::wstring aorii_text::COMMON_PUNCTUATION =
const std::wstring aorii_text::COMMON_NUMBERS = const std::wstring aorii_text::COMMON_NUMBERS =
L"0123456789"; L"0123456789";
aorii_text::aorii_text(const uint32_t in_texture_size, const uint32_t in_char_size) : font(nullptr), texture_array(nullptr), aorii_text::aorii_text(const uint32_t in_texture_size) : font(nullptr), texture_array(nullptr),
texture_size(in_texture_size), char_size(in_char_size), texture_size(in_texture_size),
current_x(0), current_y(0), current_texture_index(0), scale(0) { current_x(0), current_y(0), current_texture_index(0), scale(0) {
} }
@ -43,6 +45,7 @@ bool aorii_text::initialize(const wchar_t* in_font_path, const float in_font_pix
const std::string font_path_a(in_font_path, in_font_path + wcslen(in_font_path)); const std::string font_path_a(in_font_path, in_font_path + wcslen(in_font_path));
font = new stbtt_fontinfo(); font = new stbtt_fontinfo();
font_pixel_size = in_font_pixel_size;
// 加载字体文件 // 加载字体文件
FILE* font_file = nullptr; FILE* font_file = nullptr;
@ -53,7 +56,7 @@ bool aorii_text::initialize(const wchar_t* in_font_path, const float in_font_pix
} }
fseek(font_file, 0, SEEK_END); fseek(font_file, 0, SEEK_END);
size_t font_size = ftell(font_file); const size_t font_size = ftell(font_file);
fseek(font_file, 0, SEEK_SET); fseek(font_file, 0, SEEK_SET);
auto* font_buffer = static_cast<uint8_t*>(malloc(font_size)); auto* font_buffer = static_cast<uint8_t*>(malloc(font_size));
@ -71,21 +74,25 @@ bool aorii_text::initialize(const wchar_t* in_font_path, const float in_font_pix
scale = stbtt_ScaleForPixelHeight(font, in_font_pixel_size); scale = stbtt_ScaleForPixelHeight(font, in_font_pixel_size);
// 获取字体的垂直度量 // 获取字体的垂直度量
int ascent, descent, lineGap; stbtt_GetFontVMetrics(font, &ascent, &descent, &line_gap);
stbtt_GetFontVMetrics(font, &ascent, &descent, &lineGap); ascent *= scale;
spdlog::info("Font vertical metrics:\n Ascent: {}, Descent: {}, LineGap: {}", ascent, descent, lineGap); descent *= scale;
line_gap *= scale;
spdlog::info("Font vertical metrics:\n 上升距离: {}, 下降距离: {}, 行间距: {}", ascent, descent, line_gap);
// Create initial texture atlas // Create initial texture atlas
return create_new_texture_atlas(); return create_new_texture_atlas();
} }
float aorii_text::get_kerning(wchar_t ch1, wchar_t ch2) const {
// 获取字距调整
return stbtt_GetCodepointKernAdvance(font, ch1, ch2) * scale;
}
bool aorii_text::create_new_texture_atlas() { bool aorii_text::create_new_texture_atlas() {
// 每张纹理包含的字符数量 // 预计需要的纹理数量(8张纹理几乎可以容纳所有字符)
const uint32_t count_per_texture = (texture_size / char_size) * (texture_size / char_size); constexpr uint32_t expected_textures = 8;
const uint32_t font_count = get_font_char_count(); texture_array = aorii::get_renderer_raw()->create_texture_array(texture_size, texture_size, expected_textures, texture_format::R8_UNORM, device_memory_type::gpu);
// 预计需要的纹理数量
const uint32_t expected_textures = (font_count + count_per_texture - 1) / count_per_texture;
texture_array = aorii::get_renderer_raw()->create_texture_array(texture_size, texture_size, expected_textures, texture_format::R8_UNORM);
return texture_array != nullptr; return texture_array != nullptr;
} }
@ -159,69 +166,76 @@ bool aorii_text::precache_common_characters() {
return precache_characters(all_common_chars); return precache_characters(all_common_chars);
} }
float aorii_text::get_atlas_utilization() const {
const size_t total_slots = (texture_size / char_size) * (texture_size / char_size) * (1 + current_texture_index);
return static_cast<float>(character_map.size()) / total_slots;
}
uint32_t aorii_text::get_font_char_count() const { return font->numGlyphs; } uint32_t aorii_text::get_font_char_count() const { return font->numGlyphs; }
character_info& aorii_text::add_character_to_atlas(int32_t glyph_index, const wchar_t ch) { character_info& aorii_text::add_character_to_atlas(int32_t glyph_index, const wchar_t ch) {
// Check if we need to move to next row // 获取SDF尺寸和位图
if (current_x + char_size > texture_size) {
current_x = 0;
current_y += char_size;
}
// Check if we have room in current texture
if (current_y + char_size > texture_size) {
current_texture_index++;
current_x = 0;
current_y = 0;
}
int32_t width, height, x_offset, y_offset; int32_t width, height, x_offset, y_offset;
// 生成 SDF constexpr int padding = 8;
constexpr int padding = 8; // 距离场的扩展范围 constexpr uint8_t on_edge_value = 128;
constexpr uint8_t on_edge_value = 128; // 边界处的值(通常为 128 constexpr float pixel_dist_scale = 16.f;
constexpr float pixel_dist_scale = 64.0f / (float)padding; // 控制距离场的缩放比例
const auto* sdf_bitmap = stbtt_GetGlyphSDF( auto* sdf_bitmap = stbtt_GetGlyphSDF(
font, // 字体信息 font,
scale, // 缩放 scale,
glyph_index, // 字形索引 glyph_index,
padding, // 间距 padding,
on_edge_value, // 距离场边界值 on_edge_value,
pixel_dist_scale, // 距离缩放 pixel_dist_scale,
&width, &height, // 位图尺寸和填充 &width, &height,
&x_offset, &y_offset // 字形偏移 &x_offset, &y_offset
); );
// Update texture if (!sdf_bitmap) {
Eigen::AlignedBox2i box = {}; throw std::runtime_error("Failed to generate SDF for glyph");
box.min().x() = current_x; }
box.min().y() = current_y; ON_SCOPE_EXIT {
box.max().x() = current_x + width; stbtt_FreeSDF(sdf_bitmap, font->userdata);
box.max().y() = current_y + height; };
// 检查当前行是否有足够空间
if (current_x + width > texture_size) {
// 移动到下一行
current_x = 0;
current_y = next_row_y;
}
// 检查是否需要新的纹理
if (current_y + height > texture_size) {
current_texture_index++;
if (current_texture_index >= texture_array->get_count()) {
throw std::runtime_error("Texture array capacity exceeded");
}
current_x = 0;
current_y = 0;
next_row_y = 0;
}
// 更新纹理
Eigen::AlignedBox2i box;
box.min() = Eigen::Vector2i(current_x, current_y);
box.max() = Eigen::Vector2i(current_x + width, current_y + height);
texture_array->update_subresource(current_texture_index, box, sdf_bitmap, width); texture_array->update_subresource(current_texture_index, box, sdf_bitmap, width);
// 获取字形的水平度量 // 获取字形度量信息
int advance_width, left_side_bearing; int advance_width, left_side_bearing;
stbtt_GetGlyphHMetrics(font, glyph_index, &advance_width, &left_side_bearing); stbtt_GetGlyphHMetrics(font, glyph_index, &advance_width, &left_side_bearing);
// Store character info // 创建并存储字符信息
character_info info = {}; character_info info;
info.tex_u = current_x / static_cast<float>(texture_size); info.tex_u = current_x / static_cast<float>(texture_size);
info.tex_v = current_y / static_cast<float>(texture_size); info.tex_v = current_y / static_cast<float>(texture_size);
info.tex_z = current_texture_index;
info.advance = advance_width * scale; info.advance = advance_width * scale;
info.tex_z = static_cast<float>(current_texture_index);
info.width = width; info.width = width;
info.height = height; info.height = height;
info.offset_x = x_offset; info.x_offset = x_offset;
info.offset_y = y_offset; info.y_offset = y_offset;
// Update current position // 更新位置和下一行的y坐标
current_x += char_size; current_x += width;
next_row_y = std::max(next_row_y, current_y + height);
return character_map[ch] = info; return character_map[ch] = info;
} }

View File

@ -9,19 +9,19 @@ class renderer_texture_array;
struct stbtt_fontinfo; struct stbtt_fontinfo;
struct character_info { struct character_info {
float tex_u; // X position in texture float tex_u; // U position in atlas
float tex_v; // Y position in texture float tex_v; // V position in atlas
float advance; // Advance width
uint8_t tex_z; // Z position in texture array uint8_t tex_z; // Z position in texture array
uint8_t width; // Character width uint8_t width; // Character width
uint8_t height; // Character height uint8_t height; // Character height
float advance; // Advance width int8_t x_offset; // X offset from baseline
int8_t offset_x; // Offset from baseline to left int8_t y_offset; // Y offset from baseline
int8_t offset_y; // Offset from baseline to top
}; };
class aorii_text { class aorii_text {
public: public:
aorii_text(uint32_t in_texture_size = 2048, uint32_t in_char_size = 32); aorii_text(uint32_t in_texture_size = 2048);
~aorii_text(); ~aorii_text();
static bool init_freetype(); static bool init_freetype();
@ -29,6 +29,16 @@ public:
bool initialize(const wchar_t* in_font_path, float in_font_pixel_size); bool initialize(const wchar_t* in_font_path, float in_font_pixel_size);
bool get_or_create_character(wchar_t ch, character_info& out_info); bool get_or_create_character(wchar_t ch, character_info& out_info);
// 上升距离
[[nodiscard]] int32_t get_ascent() const { return ascent; }
// 下降距离
[[nodiscard]] int32_t get_descent() const { return descent; }
// 行间距
[[nodiscard]] int32_t get_line_gap() const { return line_gap; }
// 纹理尺寸
[[nodiscard]] uint32_t get_texture_size() const { return texture_size; }
// 字体像素大小
[[nodiscard]] float get_font_pixel_size() const { return font_pixel_size; }
// 预缓存一组字符 // 预缓存一组字符
bool precache_characters(const std::wstring& characters); bool precache_characters(const std::wstring& characters);
@ -38,12 +48,13 @@ public:
// 获取缓存状态 // 获取缓存状态
[[nodiscard]] size_t get_cached_character_count() const { return character_map.size(); } [[nodiscard]] size_t get_cached_character_count() const { return character_map.size(); }
[[nodiscard]] float get_atlas_utilization() const;
// 获取字体字符数量 // 获取字体字符数量
[[nodiscard]] uint32_t get_font_char_count() const; [[nodiscard]] uint32_t get_font_char_count() const;
[[nodiscard]] renderer_texture_array* get_texture_array() const { return texture_array; } [[nodiscard]] renderer_texture_array* get_texture_array() const { return texture_array; }
float get_kerning(wchar_t ch1, wchar_t ch2) const;
private: private:
bool create_new_texture_atlas(); bool create_new_texture_atlas();
character_info& add_character_to_atlas(int32_t glyph_index, const wchar_t ch); character_info& add_character_to_atlas(int32_t glyph_index, const wchar_t ch);
@ -51,10 +62,12 @@ private:
renderer_texture_array* texture_array; renderer_texture_array* texture_array;
const uint32_t texture_size; const uint32_t texture_size;
const uint32_t char_size; float font_pixel_size;
uint32_t current_x; uint32_t current_x;
uint32_t current_y; uint32_t current_y;
uint32_t next_row_y = 0; // 跟踪当前行的最大高度
uint8_t current_texture_index; uint8_t current_texture_index;
int32_t ascent, descent, line_gap;
float scale; float scale;
std::unordered_map<wchar_t, character_info> character_map; std::unordered_map<wchar_t, character_info> character_map;

View File

@ -21,7 +21,6 @@ public:
virtual void* get_shader_resource_view() = 0; virtual void* get_shader_resource_view() = 0;
[[nodiscard]] texture_format get_format() const { return format; } [[nodiscard]] texture_format get_format() const { return format; }
static void convert_rgb_to_rgba(const unsigned char* rgb, unsigned char* rgba, int width, int height) { static void convert_rgb_to_rgba(const unsigned char* rgb, unsigned char* rgba, int width, int height) {
for (int i = 0; i < width * height; ++i) { for (int i = 0; i < width * height; ++i) {
rgba[i * 4 + 0] = rgb[i * 3 + 0]; // R rgba[i * 4 + 0] = rgb[i * 3 + 0]; // R

View File

@ -37,10 +37,10 @@ Texture2DArray atlas_texture : register(t0);
SamplerState sampler_state : register(s0); SamplerState sampler_state : register(s0);
float4 pixel_main(PSInput input) : SV_Target { float4 pixel_main(PSInput input) : SV_Target {
float2 uv = input.altas_uv + input.uv / 64; float2 uv = input.altas_uv + input.char_size * input.uv;
float distance = atlas_texture.Sample(sampler_state, float3(uv, input.altas_index)).r; float distance = atlas_texture.Sample(sampler_state, float3(uv, input.altas_index)).r;
// return float4(distance, distance, distance, 1.0); // return float4(distance, distance, distance, 1.0);
float alpha = smoothstep(0.49, 0.51, distance); float alpha = smoothstep(0.47, 0.53, distance);
float4 color = input.color; float4 color = input.color;
color.a *= alpha; color.a *= alpha;