优化字体渲染,优化内存占用
This commit is contained in:
parent
cdd77d3cbb
commit
c158670a19
@ -5,7 +5,7 @@ class scope_exit_guard {
|
||||
public:
|
||||
scope_exit_guard(FuncType&& in_func) : func((FuncType&&)in_func) {
|
||||
}
|
||||
~scope_exit_guard() {
|
||||
virtual ~scope_exit_guard() {
|
||||
func();
|
||||
}
|
||||
private:
|
||||
@ -13,6 +13,7 @@ private:
|
||||
};
|
||||
|
||||
struct scope_exit_syntax_support {
|
||||
virtual ~scope_exit_syntax_support() = default;
|
||||
template <typename FuncType>
|
||||
scope_exit_guard<FuncType> operator+(FuncType&& InFunc)
|
||||
{
|
||||
|
@ -58,12 +58,12 @@ bool dx_renderer::render() {
|
||||
return true;
|
||||
}
|
||||
|
||||
renderer_texture* dx_renderer::create_texture(uint32_t in_size_width, uint32_t in_size_height, texture_format in_format) {
|
||||
return new dx_texture({ in_size_width, in_size_height }, 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, 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) {
|
||||
return new dx_texture_array({ in_size_width, in_size_height }, in_format, in_count);
|
||||
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, in_memory_type);
|
||||
}
|
||||
|
||||
Eigen::Matrix4f dx_renderer::make_projection_matrix(const Eigen::Vector2i& size) {
|
||||
|
@ -16,8 +16,8 @@ public:
|
||||
void destroy() 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_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* 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, device_memory_type in_memory_type) override;
|
||||
|
||||
[[nodiscard]] ID3D11Device* get_d3d_device() const { return device; }
|
||||
[[nodiscard]] ID3D11DeviceContext* get_d3d_context() const { return context; }
|
||||
|
@ -1,9 +1,24 @@
|
||||
#include "dx_texture.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) {
|
||||
create_texture(size, format, count);
|
||||
int get_cpu_access_flag(device_memory_type in_memory_type) {
|
||||
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() {
|
||||
@ -18,28 +33,48 @@ void dx_texture::unlock() {
|
||||
return unlock(0);
|
||||
}
|
||||
|
||||
void* dx_texture::lock(uint32_t index, uint32_t* out_row_pitch) const {
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
m_texture->GetDesc(&desc);
|
||||
void* dx_texture::lock(uint32_t index, uint32_t* out_row_pitch) {
|
||||
auto r = aorii::get_renderer<dx_renderer>();
|
||||
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;
|
||||
auto context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
|
||||
context->Map(m_texture, index, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
|
||||
const auto context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
|
||||
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;
|
||||
return mappedResource.pData;
|
||||
}
|
||||
|
||||
void dx_texture::unlock(uint32_t index) const {
|
||||
auto context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
|
||||
context->Unmap(m_texture, index);
|
||||
const auto context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
|
||||
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) {
|
||||
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,
|
||||
const uint32_t in_row_pitch) const {
|
||||
D3D11_BOX box = {};
|
||||
void dx_texture::update_subresource(uint32_t index, const Eigen::AlignedBox2i& in_area, const void* in_buffer, const uint32_t in_row_pitch) {
|
||||
auto r = aorii::get_renderer<dx_renderer>();
|
||||
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.top = in_area.min().y();
|
||||
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.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) {
|
||||
@ -57,7 +143,7 @@ bool dx_texture::resize(const Eigen::Vector2i& size) {
|
||||
if (desc.Width == size.x() && desc.Height == size.y()) {
|
||||
return true;
|
||||
}
|
||||
return create_texture(size, get_format());
|
||||
return create_texture(size, get_format(), memory_type, get_count());
|
||||
}
|
||||
|
||||
Eigen::Vector2i dx_texture::size() {
|
||||
@ -79,10 +165,10 @@ void dx_texture::set_count(const uint32_t in_count) {
|
||||
D3D11_TEXTURE2D_DESC desc;
|
||||
m_texture->GetDesc(&desc);
|
||||
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();
|
||||
|
||||
@ -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.SampleDesc.Count = 1;
|
||||
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.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
||||
desc.CPUAccessFlags = get_cpu_access_flag(in_memory_type);
|
||||
desc.MiscFlags = 0;
|
||||
auto hr = d3d_device->CreateTexture2D(&desc, nullptr, &m_texture);
|
||||
if (FAILED(hr)) {
|
||||
spdlog::critical("无法创建纹理, 错误: {0}", hr);
|
||||
spdlog::critical("无法创建纹理, 错误: 0x{:08X}", static_cast<unsigned int>(hr));
|
||||
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 = {};
|
||||
srv_desc.Format = desc.Format;
|
||||
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;
|
||||
hr = d3d_device->CreateShaderResourceView(m_texture, &srv_desc, &srv);
|
||||
if (FAILED(hr)) {
|
||||
spdlog::critical("无法创建着色器资源视图, 错误: {0}", hr);
|
||||
spdlog::critical("无法创建着色器资源视图, 错误: 0x{:08X}", static_cast<unsigned int>(hr));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -122,6 +215,8 @@ bool dx_texture::create_texture(const Eigen::Vector2i& in_size, texture_format i
|
||||
void dx_texture::release_texture() {
|
||||
if (srv) srv->Release();
|
||||
if (m_texture) m_texture->Release();
|
||||
if (upload_texture) upload_texture->Release();
|
||||
srv = nullptr;
|
||||
m_texture = nullptr;
|
||||
upload_texture = nullptr;
|
||||
}
|
||||
|
@ -7,17 +7,17 @@
|
||||
// DX11纹理
|
||||
class dx_texture : public renderer_texture {
|
||||
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;
|
||||
|
||||
void* lock(uint32_t* out_row_pitch) 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 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;
|
||||
Eigen::Vector2i size() override;
|
||||
@ -29,9 +29,13 @@ public:
|
||||
[[nodiscard]] uint32_t get_count() const;
|
||||
void set_count(uint32_t in_count);
|
||||
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();
|
||||
|
||||
ID3D11Texture2D* m_texture{};
|
||||
ID3D11ShaderResourceView* srv{};
|
||||
device_memory_type memory_type;
|
||||
|
||||
D3D11_TEXTURE2D_DESC upload_desc;
|
||||
ID3D11Texture2D* upload_texture{};
|
||||
};
|
||||
|
@ -6,8 +6,8 @@ class dx_texture;
|
||||
|
||||
class dx_texture_array : public renderer_texture_array {
|
||||
public:
|
||||
dx_texture_array(const Eigen::Vector2i& size, texture_format format, const uint32_t count): renderer_texture_array(format) {
|
||||
texture = new dx_texture(size, format, count);
|
||||
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, in_memory_type, count);
|
||||
}
|
||||
~dx_texture_array() override { delete texture; }
|
||||
|
||||
|
@ -112,7 +112,7 @@ void dx_window::begin_frame() {
|
||||
// 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);
|
||||
context.draw_string({0, 0}, L"你好,世界!", 64);
|
||||
context.draw_string({0, 0}, L"你好,世界!", 32);
|
||||
|
||||
context.flush();
|
||||
|
||||
|
@ -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);
|
||||
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);
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
|
@ -22,6 +22,12 @@ enum class renderer_api {
|
||||
metal,
|
||||
};
|
||||
|
||||
enum class device_memory_type {
|
||||
gpu, // 显存
|
||||
shared, // 共享内存
|
||||
cpu, // 内存
|
||||
};
|
||||
|
||||
enum class texture_format {
|
||||
// 未压缩的颜色格式
|
||||
|
||||
@ -221,16 +227,16 @@ public:
|
||||
virtual bool render() = 0;
|
||||
|
||||
template<typename T>
|
||||
renderer_texture* create_texture(const Eigen::Vector2<T>& in_size, texture_format in_format) {
|
||||
return create_texture(in_size.x(), in_size.y(), 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, 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);
|
||||
template<typename T>
|
||||
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);
|
||||
}
|
||||
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);
|
||||
|
||||
renderer_texture* load_image(const std::string& file_path, texture_format in_format);
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
aorii_text* text = nullptr;
|
||||
void renderer_context::init() {
|
||||
text = new aorii_text();
|
||||
text = new aorii_text(2048);
|
||||
text->initialize(LR"(C:\Windows\Fonts\msyh.ttc)", 32);
|
||||
text->precache_common_characters();
|
||||
}
|
||||
@ -39,28 +39,33 @@ void renderer_context::draw_texture(const Eigen::Vector2f& in_pos, const Eigen::
|
||||
flush();
|
||||
}
|
||||
|
||||
void renderer_context::draw_string(const Eigen::Vector2f& in_pos, const std::wstring& in_str, float in_height,
|
||||
const linear_color& in_color) {
|
||||
void renderer_context::draw_string(const Eigen::Vector2f& in_pos, const std::wstring& in_str, float in_height, const linear_color& in_color) {
|
||||
to_text_pipeline(in_color);
|
||||
|
||||
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) {
|
||||
cursor_x += last_char ? text->get_kerning(last_char, c) : 0;
|
||||
|
||||
character_info info{};
|
||||
text->get_or_create_character(c, info);
|
||||
|
||||
// 根据in_height缩放字符大小
|
||||
const Eigen::Vector2f size = { info.width * scale, info.height * scale };
|
||||
const Eigen::Vector2f pos = { cursor_x, in_pos.y() - info.height };
|
||||
cursor_x += size.x() + info.offset_x;
|
||||
const float y_offset = (info.y_offset + text->get_ascent()) * scale;
|
||||
const Eigen::Vector2f size { info.width * scale, info.height * scale };
|
||||
const Eigen::Vector2f pos { cursor_x + info.x_offset * scale, in_pos.y() + y_offset };
|
||||
cursor_x += info.advance * scale;
|
||||
|
||||
aorii_vertex_param param{};
|
||||
param.param_a1 = info.tex_u;
|
||||
param.param_a2 = info.tex_v;
|
||||
param.param_a3 = info.tex_z;
|
||||
param.param_b1 = info.width;
|
||||
param.param_b2 = info.height;
|
||||
param.param_b1 = info.width / (float)text->get_texture_size();
|
||||
param.param_b2 = info.height / (float)text->get_texture_size();
|
||||
|
||||
make_rect(pos, size, in_color, 0, param);
|
||||
last_char = c;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,8 @@
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include <stb_truetype.h>
|
||||
|
||||
#include "misc/scope_exit.h"
|
||||
|
||||
// 在SDFFontCache.cpp中添加实现
|
||||
const std::wstring aorii_text::COMMON_ASCII =
|
||||
L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
@ -23,8 +25,8 @@ const std::wstring aorii_text::COMMON_PUNCTUATION =
|
||||
const std::wstring aorii_text::COMMON_NUMBERS =
|
||||
L"0123456789";
|
||||
|
||||
aorii_text::aorii_text(const uint32_t in_texture_size, const uint32_t in_char_size) : font(nullptr), texture_array(nullptr),
|
||||
texture_size(in_texture_size), char_size(in_char_size),
|
||||
aorii_text::aorii_text(const uint32_t in_texture_size) : font(nullptr), texture_array(nullptr),
|
||||
texture_size(in_texture_size),
|
||||
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));
|
||||
|
||||
font = new stbtt_fontinfo();
|
||||
font_pixel_size = in_font_pixel_size;
|
||||
|
||||
// 加载字体文件
|
||||
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);
|
||||
size_t font_size = ftell(font_file);
|
||||
const size_t font_size = ftell(font_file);
|
||||
fseek(font_file, 0, SEEK_SET);
|
||||
|
||||
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);
|
||||
|
||||
// 获取字体的垂直度量
|
||||
int ascent, descent, lineGap;
|
||||
stbtt_GetFontVMetrics(font, &ascent, &descent, &lineGap);
|
||||
spdlog::info("Font vertical metrics:\n Ascent: {}, Descent: {}, LineGap: {}", ascent, descent, lineGap);
|
||||
stbtt_GetFontVMetrics(font, &ascent, &descent, &line_gap);
|
||||
ascent *= scale;
|
||||
descent *= scale;
|
||||
line_gap *= scale;
|
||||
spdlog::info("Font vertical metrics:\n 上升距离: {}, 下降距离: {}, 行间距: {}", ascent, descent, line_gap);
|
||||
|
||||
// Create initial 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() {
|
||||
// 每张纹理包含的字符数量
|
||||
const uint32_t count_per_texture = (texture_size / char_size) * (texture_size / char_size);
|
||||
const uint32_t font_count = get_font_char_count();
|
||||
// 预计需要的纹理数量
|
||||
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);
|
||||
// 预计需要的纹理数量(8张纹理几乎可以容纳所有字符)
|
||||
constexpr uint32_t expected_textures = 8;
|
||||
texture_array = aorii::get_renderer_raw()->create_texture_array(texture_size, texture_size, expected_textures, texture_format::R8_UNORM, device_memory_type::gpu);
|
||||
return texture_array != nullptr;
|
||||
}
|
||||
|
||||
@ -159,69 +166,76 @@ bool aorii_text::precache_common_characters() {
|
||||
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; }
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
// 获取SDF尺寸和位图
|
||||
int32_t width, height, x_offset, y_offset;
|
||||
// 生成 SDF
|
||||
constexpr int padding = 8; // 距离场的扩展范围
|
||||
constexpr uint8_t on_edge_value = 128; // 边界处的值(通常为 128)
|
||||
constexpr float pixel_dist_scale = 64.0f / (float)padding; // 控制距离场的缩放比例
|
||||
const auto* sdf_bitmap = stbtt_GetGlyphSDF(
|
||||
font, // 字体信息
|
||||
scale, // 缩放
|
||||
glyph_index, // 字形索引
|
||||
padding, // 间距
|
||||
on_edge_value, // 距离场边界值
|
||||
pixel_dist_scale, // 距离缩放
|
||||
&width, &height, // 位图尺寸和填充
|
||||
&x_offset, &y_offset // 字形偏移
|
||||
constexpr int padding = 8;
|
||||
constexpr uint8_t on_edge_value = 128;
|
||||
constexpr float pixel_dist_scale = 16.f;
|
||||
|
||||
auto* sdf_bitmap = stbtt_GetGlyphSDF(
|
||||
font,
|
||||
scale,
|
||||
glyph_index,
|
||||
padding,
|
||||
on_edge_value,
|
||||
pixel_dist_scale,
|
||||
&width, &height,
|
||||
&x_offset, &y_offset
|
||||
);
|
||||
|
||||
// Update texture
|
||||
Eigen::AlignedBox2i box = {};
|
||||
box.min().x() = current_x;
|
||||
box.min().y() = current_y;
|
||||
box.max().x() = current_x + width;
|
||||
box.max().y() = current_y + height;
|
||||
if (!sdf_bitmap) {
|
||||
throw std::runtime_error("Failed to generate SDF for glyph");
|
||||
}
|
||||
ON_SCOPE_EXIT {
|
||||
stbtt_FreeSDF(sdf_bitmap, font->userdata);
|
||||
};
|
||||
|
||||
// 检查当前行是否有足够空间
|
||||
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);
|
||||
|
||||
// 获取字形的水平度量
|
||||
// 获取字形度量信息
|
||||
int 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_v = current_y / static_cast<float>(texture_size);
|
||||
info.tex_z = current_texture_index;
|
||||
info.advance = advance_width * scale;
|
||||
info.tex_z = static_cast<float>(current_texture_index);
|
||||
info.width = width;
|
||||
info.height = height;
|
||||
info.offset_x = x_offset;
|
||||
info.offset_y = y_offset;
|
||||
info.x_offset = x_offset;
|
||||
info.y_offset = y_offset;
|
||||
|
||||
// Update current position
|
||||
current_x += char_size;
|
||||
// 更新位置和下一行的y坐标
|
||||
current_x += width;
|
||||
next_row_y = std::max(next_row_y, current_y + height);
|
||||
|
||||
return character_map[ch] = info;
|
||||
}
|
||||
|
@ -9,19 +9,19 @@ class renderer_texture_array;
|
||||
struct stbtt_fontinfo;
|
||||
|
||||
struct character_info {
|
||||
float tex_u; // X position in texture
|
||||
float tex_v; // Y position in texture
|
||||
float tex_u; // U position in atlas
|
||||
float tex_v; // V position in atlas
|
||||
float advance; // Advance width
|
||||
uint8_t tex_z; // Z position in texture array
|
||||
uint8_t width; // Character width
|
||||
uint8_t height; // Character height
|
||||
float advance; // Advance width
|
||||
int8_t offset_x; // Offset from baseline to left
|
||||
int8_t offset_y; // Offset from baseline to top
|
||||
int8_t x_offset; // X offset from baseline
|
||||
int8_t y_offset; // Y offset from baseline
|
||||
};
|
||||
|
||||
class aorii_text {
|
||||
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();
|
||||
|
||||
static bool init_freetype();
|
||||
@ -29,6 +29,16 @@ public:
|
||||
|
||||
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);
|
||||
// 上升距离
|
||||
[[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);
|
||||
@ -38,12 +48,13 @@ public:
|
||||
|
||||
// 获取缓存状态
|
||||
[[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]] renderer_texture_array* get_texture_array() const { return texture_array; }
|
||||
|
||||
float get_kerning(wchar_t ch1, wchar_t ch2) const;
|
||||
private:
|
||||
bool create_new_texture_atlas();
|
||||
character_info& add_character_to_atlas(int32_t glyph_index, const wchar_t ch);
|
||||
@ -51,10 +62,12 @@ private:
|
||||
|
||||
renderer_texture_array* texture_array;
|
||||
const uint32_t texture_size;
|
||||
const uint32_t char_size;
|
||||
float font_pixel_size;
|
||||
uint32_t current_x;
|
||||
uint32_t current_y;
|
||||
uint32_t next_row_y = 0; // 跟踪当前行的最大高度
|
||||
uint8_t current_texture_index;
|
||||
int32_t ascent, descent, line_gap;
|
||||
float scale;
|
||||
|
||||
std::unordered_map<wchar_t, character_info> character_map;
|
||||
|
@ -21,7 +21,6 @@ public:
|
||||
virtual void* get_shader_resource_view() = 0;
|
||||
[[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) {
|
||||
for (int i = 0; i < width * height; ++i) {
|
||||
rgba[i * 4 + 0] = rgb[i * 3 + 0]; // R
|
||||
|
@ -37,10 +37,10 @@ Texture2DArray atlas_texture : register(t0);
|
||||
SamplerState sampler_state : register(s0);
|
||||
|
||||
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;
|
||||
// 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;
|
||||
color.a *= alpha;
|
||||
|
Loading…
x
Reference in New Issue
Block a user