cache DirectWrite render targets
This commit is contained in:
parent
c140b6271b
commit
9fc666d49d
@ -51,11 +51,14 @@ GC_Run GC_RunFromString(Arena *arena, String str, GC_FontKey font, f32 font_size
|
||||
u32 *uncached_codepoints = PushStructsNoZero(scratch.arena, u32, codepoints_count);
|
||||
u64 uncached_codepoints_count = 0;
|
||||
|
||||
/* TODO: Include advances for glyphs in run that have rasterized but not finished uploading to atlas */
|
||||
u64 pending_glyphs_count = 0;
|
||||
{
|
||||
if (codepoints_count > 0)
|
||||
{
|
||||
Lock lock = LockS(&GC.glyphs_mutex);
|
||||
{
|
||||
i64 completion = G_CompletionValueFromQueue(G_QueueKind_AsyncCopy);
|
||||
for (u64 codepoint_idx = 0; codepoint_idx < codepoints_count; ++codepoint_idx)
|
||||
{
|
||||
u32 codepoint = codepoints[codepoint_idx];
|
||||
@ -78,7 +81,7 @@ GC_Run GC_RunFromString(Arena *arena, String str, GC_FontKey font, f32 font_size
|
||||
uncached_codepoints[uncached_codepoints_count] = codepoint;
|
||||
uncached_codepoints_count += 1;
|
||||
}
|
||||
else if (Atomic32Fetch(&glyph->ready) == 0)
|
||||
else if (completion < Atomic64Fetch(&glyph->async_copy_completion_target))
|
||||
{
|
||||
pending_glyphs_count += 1;
|
||||
}
|
||||
@ -88,6 +91,7 @@ GC_Run GC_RunFromString(Arena *arena, String str, GC_FontKey font, f32 font_size
|
||||
ready_glyphs_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Unlock(&lock);
|
||||
}
|
||||
}
|
||||
@ -121,6 +125,7 @@ GC_Run GC_RunFromString(Arena *arena, String str, GC_FontKey font, f32 font_size
|
||||
glyph = PushStruct(perm, GC_Glyph);
|
||||
glyph->desc = desc;
|
||||
glyph->hash = hash;
|
||||
Atomic64FetchSet(&glyph->async_copy_completion_target, I64Max);
|
||||
SllStackPush(bin->first, glyph);
|
||||
/* Create cmd */
|
||||
{
|
||||
@ -217,12 +222,14 @@ void GC_TickAsync(WaveLaneCtx *lane, AsyncTickCtx *tick)
|
||||
GC_AsyncCtx *async = &GC.async_ctx;
|
||||
|
||||
//////////////////////////////
|
||||
//- Collect cmds
|
||||
//- Begin tick
|
||||
|
||||
/* TODO: Limit cmds processed per-tick */
|
||||
|
||||
if (lane->idx == 0)
|
||||
{
|
||||
ZeroStruct(&async->cmds);
|
||||
|
||||
Lock lock = LockE(&GC.submit.mutex);
|
||||
{
|
||||
/* Pop cmds from submission queue */
|
||||
@ -245,12 +252,43 @@ void GC_TickAsync(WaveLaneCtx *lane, AsyncTickCtx *tick)
|
||||
|
||||
WaveSync(lane);
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
//- Allocate atlas rects
|
||||
//- Rasterize glyphs
|
||||
|
||||
/* TODO: Process cmds unevenly to account for varying work size */
|
||||
|
||||
{
|
||||
RngU64 cmd_idxs = WaveIdxRangeFromCount(lane, async->cmds.count);
|
||||
for (u64 cmd_idx = cmd_idxs.min; cmd_idx < cmd_idxs.max; ++cmd_idx)
|
||||
{
|
||||
GC_Cmd *cmd = &async->cmds.v[cmd_idx];
|
||||
GC_Glyph *glyph = cmd->glyph;
|
||||
ResourceKey resource = glyph->desc.font.r;
|
||||
GC_GlyphDesc desc = glyph->desc;
|
||||
TTF_GlyphResult ttf_result = TTF_RasterizeGlyphFromCodepoint(tick->arena, desc.codepoint, resource, desc.font_size);;
|
||||
glyph->font_size = desc.font_size;
|
||||
glyph->font_ascent = ttf_result.font_ascent;
|
||||
glyph->font_descent = ttf_result.font_descent;
|
||||
glyph->font_cap = ttf_result.font_cap;
|
||||
glyph->advance = ttf_result.advance;
|
||||
glyph->bounds = ttf_result.bounds;
|
||||
cmd->rasterized = ttf_result;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Only sync first lane? */
|
||||
|
||||
WaveSync(lane);
|
||||
|
||||
////////////////////////////
|
||||
//- Allocate atlas slices
|
||||
|
||||
if (lane->idx == 0)
|
||||
{
|
||||
/* TODO: Remove this */
|
||||
/* FIXME: Dynamic atlases */
|
||||
/* Create atlas */
|
||||
Vec2I32 atlas_dims = VEC2I32(8192, 8192);
|
||||
if (G_IsResourceNil(GC.atlas))
|
||||
@ -270,27 +308,10 @@ void GC_TickAsync(WaveLaneCtx *lane, AsyncTickCtx *tick)
|
||||
{
|
||||
GC_Cmd *cmd = &async->cmds.v[cmd_idx];
|
||||
GC_Glyph *glyph = cmd->glyph;
|
||||
|
||||
ResourceKey resource = glyph->desc.font.r;
|
||||
String resource_data = DataFromResource(resource);
|
||||
String resource_name = NameFromResource(resource);
|
||||
|
||||
GC_GlyphDesc desc = glyph->desc;
|
||||
TTF_GlyphResult ttf_result = cmd->rasterized;
|
||||
|
||||
TTF_GlyphResult ttf_info = TTF_RasterizeGlyphFromCodepoint(tick->arena, desc.codepoint, resource, desc.font_size);
|
||||
glyph->font_size = desc.font_size;
|
||||
glyph->font_ascent = ttf_info.font_ascent;
|
||||
glyph->font_descent = ttf_info.font_descent;
|
||||
glyph->font_cap = ttf_info.font_cap;
|
||||
glyph->advance = ttf_info.advance;
|
||||
glyph->bounds = ttf_info.bounds;
|
||||
|
||||
|
||||
|
||||
u32 *image_pixels = ttf_info.image_pixels;
|
||||
Vec2I32 image_dims = ttf_info.image_dims;
|
||||
|
||||
|
||||
Vec2I32 image_dims = ttf_result.image_dims;
|
||||
|
||||
Vec2I32 atlas_offset = GC.atlas_pos;
|
||||
GC.atlas_row_height = MaxI32(GC.atlas_row_height, image_dims.y);
|
||||
@ -309,14 +330,36 @@ void GC_TickAsync(WaveLaneCtx *lane, AsyncTickCtx *tick)
|
||||
glyph->tex_slice_uv.p0.y = (f32)glyph->tex_slice.p0.y / (f32)atlas_dims.x;
|
||||
glyph->tex_slice_uv.p1.x = (f32)glyph->tex_slice.p1.x / (f32)atlas_dims.x;
|
||||
glyph->tex_slice_uv.p1.y = (f32)glyph->tex_slice.p1.y / (f32)atlas_dims.x;
|
||||
}
|
||||
}
|
||||
|
||||
WaveSync(lane);
|
||||
|
||||
//////////////////////////////
|
||||
//- Upload glyphs to atlas
|
||||
|
||||
{
|
||||
RngU64 cmd_idxs = WaveIdxRangeFromCount(lane, async->cmds.count);
|
||||
for (u64 cmd_idx = cmd_idxs.min; cmd_idx < cmd_idxs.max; ++cmd_idx)
|
||||
{
|
||||
GC_Cmd *cmd = &async->cmds.v[cmd_idx];
|
||||
GC_Glyph *glyph = cmd->glyph;
|
||||
ResourceKey resource = glyph->desc.font.r;
|
||||
GC_GlyphDesc desc = glyph->desc;
|
||||
TTF_GlyphResult ttf_result = TTF_RasterizeGlyphFromCodepoint(tick->arena, desc.codepoint, resource, desc.font_size);;
|
||||
|
||||
u32 *image_pixels = ttf_result.image_pixels;
|
||||
Vec2I32 image_dims = ttf_result.image_dims;
|
||||
|
||||
i64 completion_target = 0;
|
||||
if (image_dims.x > 0 && image_dims.y > 0)
|
||||
{
|
||||
G_CommandListHandle cl = G_PrepareCommandList(G_QueueKind_AsyncCopy);
|
||||
{
|
||||
|
||||
G_CopyCpuToTexture(
|
||||
cl,
|
||||
GC.atlas, VEC3I32(atlas_offset.x, atlas_offset.y, 0),
|
||||
GC.atlas, VEC3I32(glyph->tex_slice.p0.x, glyph->tex_slice.p0.y, 0),
|
||||
image_pixels, VEC3I32(image_dims.x, image_dims.y, 1),
|
||||
RNG3I32(
|
||||
VEC3I32(0, 0, 0),
|
||||
@ -324,133 +367,13 @@ void GC_TickAsync(WaveLaneCtx *lane, AsyncTickCtx *tick)
|
||||
)
|
||||
);
|
||||
}
|
||||
G_CommitCommandList(cl);
|
||||
|
||||
G_SyncCpu(G_QueueMask_All);
|
||||
completion_target = G_CommitCommandList(cl);
|
||||
}
|
||||
|
||||
Atomic32Set(&glyph->ready, 1);
|
||||
Atomic64Set(&glyph->async_copy_completion_target, completion_target);
|
||||
}
|
||||
}
|
||||
|
||||
WaveSync(lane);
|
||||
|
||||
// //////////////////////////////
|
||||
// //- Allocate atlas rects
|
||||
|
||||
// if (lane->idx == 0)
|
||||
// {
|
||||
// /* TODO: Remove this */
|
||||
// /* Create atlas */
|
||||
// Vec2I32 atlas_dims = VEC2I32(8192, 8192);
|
||||
// if (G_IsResourceNil(GC.atlas))
|
||||
// {
|
||||
// G_ArenaHandle gpu_perm = G_PermArena();
|
||||
// GC.atlas = G_PushTexture2D(
|
||||
// gpu_perm,
|
||||
// G_Format_R8G8B8A8_Unorm_Srgb,
|
||||
// atlas_dims,
|
||||
// /* FIXME: We may need simultaneous access? */
|
||||
// G_Layout_AnyQueue_ShaderRead_CopyRead_CopyWrite_Present,
|
||||
// );
|
||||
// }
|
||||
|
||||
// for (u64 cmd_idx = 0; cmd_idx < async->cmds.count; ++cmd_idx)
|
||||
// {
|
||||
// GC_Cmd *cmd = &async->cmds.v[cmd_idx];
|
||||
// GC_Glyph *glyph = cmd->glyph;
|
||||
|
||||
// ResourceKey resource = glyph->desc.font.r;
|
||||
// String resource_data = DataFromResource(resource);
|
||||
// String resource_name = NameFromResource(resource);
|
||||
|
||||
// GC_GlyphDesc desc = glyph->desc;
|
||||
|
||||
// TTF_GlyphDesc ttf_desc = TTF_GlyphDescFromCodepoint(desc.codepoint, resource, desc.font_size);
|
||||
// glyph->font_size = ttf_desc.font_size;
|
||||
// glyph->font_ascent = ttf_desc.font_ascent;
|
||||
// glyph->font_descent = ttf_desc.font_descent;
|
||||
// glyph->font_cap = ttf_desc.font_cap;
|
||||
|
||||
// glyph->advance = ttf_desc.advance;
|
||||
// glyph->bounds = ttf_desc.bounds;
|
||||
// Vec2 dims = DimsFromRng2(glyph->bounds);
|
||||
|
||||
// Vec2I32 atlas_offset = GC.atlas_pos;
|
||||
// GC.atlas_row_height = MaxI32(GC.atlas_row_height, dims.y);
|
||||
// if (atlas_offset.x + dims.x > atlas_dims.x);
|
||||
// {
|
||||
// GC.atlas_pos.x = 0;
|
||||
// GC.atlas_pos.y += GC.atlas_row_height;
|
||||
// GC.atlas_row_height = dims.y;
|
||||
// }
|
||||
// GC.atlas_pos.x += dims.x;
|
||||
|
||||
// Rng2I32 src_slice = ZI;
|
||||
// src_slice.p0.x = 0;
|
||||
// src_slice.p0.y = 0;
|
||||
// src_slice.p1.x = RoundF32ToI32(dims.x);
|
||||
// src_slice.p1.y = RoundF32ToI32(dims.y);
|
||||
|
||||
// Vec2I32 src_dims = DimsFromRng2I32(src_slice);
|
||||
// if (src_dims.x > 0 && src_dims.y > 0)
|
||||
// {
|
||||
// u64 src_pixels_count = src_dims.x * src_dims.y;
|
||||
// u32 *src_pixels = PushStructsNoZero(tick->arena, u32, src_pixels_count);
|
||||
// TTF_RasterizeGlyph(src_pixels, src_dims, src_slice, ttf_desc);
|
||||
|
||||
// DEBUGBREAKABLE;
|
||||
|
||||
|
||||
// // G_CommandListHandle cl = G_PrepareCommandList(G_QueueKind_AsyncCopy);
|
||||
// G_CommandListHandle cl = G_PrepareCommandList(G_QueueKind_Direct);
|
||||
// {
|
||||
// G_CopyCpuToTexture(
|
||||
// cl,
|
||||
// GC.atlas, VEC3I32(atlas_offset.x, atlas_offset.y, 0),
|
||||
// src_pixels, VEC3I32(src_dims.x, src_dims.y, 0),
|
||||
// RNG3I32(
|
||||
// VEC3I32(src_slice.p0.x, src_slice.p0.y, 0),
|
||||
// VEC3I32(src_slice.p1.x, src_slice.p1.y, 1)
|
||||
// )
|
||||
// );
|
||||
// }
|
||||
// G_CommitCommandList(cl);
|
||||
|
||||
// G_SyncCpu(G_QueueMask_All);
|
||||
// }
|
||||
|
||||
// Atomic32Set(&glyph->ready, 1);
|
||||
|
||||
// // ResourceKey resource = desc.font.r;
|
||||
// // String resource_data = DataFromResource(resource);
|
||||
// }
|
||||
// }
|
||||
|
||||
// WaveSync(lane);
|
||||
|
||||
//////////////////////////////
|
||||
//- Process cmds
|
||||
|
||||
/* TODO: Process cmds unevenly to account for varying work size */
|
||||
|
||||
// if (async->cmds.count > 0)
|
||||
// {
|
||||
// RngU64 cmd_idxs = WaveIdxRangeFromCount(lane, async->cmds.count);
|
||||
// for (u64 cmd_idx = cmd_idxs.min; cmd_idx < cmd_idxs.max; ++cmd_idx)
|
||||
// {
|
||||
// GC_Cmd *cmd = &async->cmds.v[cmd_idx];
|
||||
// GC_Glyph *glyph = cmd->glyph;
|
||||
|
||||
// GC_GlyphDesc desc = glyph->desc;
|
||||
|
||||
// ResourceKey resource = desc.font.r;
|
||||
// String resource_data = DataFromResource(resource);
|
||||
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
//////////////////////////////
|
||||
//- End tick
|
||||
|
||||
|
||||
@ -22,8 +22,7 @@ Struct(GC_Glyph)
|
||||
|
||||
GC_GlyphDesc desc;
|
||||
u64 hash;
|
||||
|
||||
Atomic32 ready;
|
||||
Atomic64 async_copy_completion_target;
|
||||
|
||||
/* Font info */
|
||||
f32 font_size;
|
||||
@ -96,6 +95,9 @@ Struct(GC_Run)
|
||||
Struct(GC_Cmd)
|
||||
{
|
||||
GC_Glyph *glyph;
|
||||
|
||||
/* Async temporary data */
|
||||
TTF_GlyphResult rasterized;
|
||||
};
|
||||
|
||||
Struct(GC_CmdNode)
|
||||
|
||||
@ -729,7 +729,6 @@ i64 G_D12_CommitRawCommandList(G_D12_RawCommandList *cl)
|
||||
{
|
||||
/* Execute */
|
||||
ID3D12CommandQueue_ExecuteCommandLists(queue->d3d_queue, 1, (ID3D12CommandList **)&cl->d3d_cl);
|
||||
|
||||
Lock lock = LockE(&queue->commit_mutex);
|
||||
{
|
||||
completion_target = ++queue->commit_fence_target;
|
||||
@ -1462,6 +1461,7 @@ G_D12_Cmd *G_D12_PushCmd(G_D12_CmdList *cl)
|
||||
|
||||
/* Push cmd to chunk */
|
||||
G_D12_Cmd *cmd = &chunk->cmds[chunk->cmds_count++];
|
||||
ZeroStruct(cmd);
|
||||
++cl->cmds_count;
|
||||
return cmd;
|
||||
}
|
||||
@ -1488,7 +1488,7 @@ G_D12_StagingRegionNode *G_D12_PushStagingRegion(G_D12_CmdList *cl, u64 size)
|
||||
{
|
||||
G_D12_StagingRing *old_ring = 0;
|
||||
G_D12_StagingRing *ring = queue->staging_ring;
|
||||
i64 completed = ID3D12Fence_GetCompletedValue(queue->commit_fence);
|
||||
i64 completion = ID3D12Fence_GetCompletedValue(queue->commit_fence);
|
||||
|
||||
/* Find first completed region with matching size.
|
||||
* For each region in ring:
|
||||
@ -1504,9 +1504,6 @@ G_D12_StagingRegionNode *G_D12_PushStagingRegion(G_D12_CmdList *cl, u64 size)
|
||||
|
||||
|
||||
|
||||
/* FIXME: Region completion target should be atomic, and initialized to
|
||||
* u64/i64 max until cl submission actually sets value */
|
||||
|
||||
/* Find region with large enough size */
|
||||
G_D12_StagingRegionNode *match = 0;
|
||||
if (ring && ring->size >= size)
|
||||
@ -1515,7 +1512,7 @@ G_D12_StagingRegionNode *G_D12_PushStagingRegion(G_D12_CmdList *cl, u64 size)
|
||||
for (;;)
|
||||
{
|
||||
G_D12_StagingRegionNode *next = r->next;
|
||||
b32 is_completed = completed >= Atomic64Fetch(&r->completion_target);
|
||||
b32 is_completed = completion >= Atomic64Fetch(&r->completion_target);
|
||||
if (is_completed)
|
||||
{
|
||||
u64 region_size = 0;
|
||||
@ -1530,7 +1527,7 @@ G_D12_StagingRegionNode *G_D12_PushStagingRegion(G_D12_CmdList *cl, u64 size)
|
||||
|
||||
if (region_size < size)
|
||||
{
|
||||
b32 next_is_completed = completed >= Atomic64Fetch(&next->completion_target);
|
||||
b32 next_is_completed = completion >= Atomic64Fetch(&next->completion_target);
|
||||
if (next_is_completed)
|
||||
{
|
||||
if (next->pos > r->pos)
|
||||
@ -1587,8 +1584,7 @@ G_D12_StagingRegionNode *G_D12_PushStagingRegion(G_D12_CmdList *cl, u64 size)
|
||||
/* Queue old ring for deletion */
|
||||
old_ring = ring;
|
||||
ring = 0;
|
||||
u64 new_ring_size = MaxU64(AlignU64ToNextPow2(size), Kibi(64));
|
||||
// u64 new_ring_size = MaxU64(AlignU64ToNextPow2(size), Kibi(128));
|
||||
u64 new_ring_size = MaxU64(AlignU64ToNextPow2(size), Mebi(8));
|
||||
if (old_ring)
|
||||
{
|
||||
new_ring_size = MaxU64(new_ring_size, old_ring->size * 2);
|
||||
@ -1667,6 +1663,7 @@ G_D12_StagingRegionNode *G_D12_PushStagingRegion(G_D12_CmdList *cl, u64 size)
|
||||
if (old_ring)
|
||||
{
|
||||
/* FIXME: Queue old ring for deletion with command list */
|
||||
DEBUGBREAKABLE;
|
||||
}
|
||||
}
|
||||
Unlock(&lock);
|
||||
@ -2425,14 +2422,16 @@ void G_CopyCpuToTexture(G_CommandListHandle cl_handle, G_ResourceHandle dst_hand
|
||||
ID3D12Device_GetCopyableFootprints(g->device, &src_desc, 0, 1, 0, &footprint, (u32 *)&footprint_rows_count, &footprint_row_size, &footprint_size);
|
||||
}
|
||||
|
||||
G_D12_StagingRegionNode *region = G_D12_PushStagingRegion(cl, footprint_size);
|
||||
footprint.Offset = region->pos;
|
||||
G_D12_StagingRegionNode *staging_region = G_D12_PushStagingRegion(cl, footprint_size);
|
||||
G_D12_Resource *staging_resource = staging_region->ring->resource;
|
||||
G_ResourceHandle staging_resource_handle = G_D12_MakeHandle(G_ResourceHandle, staging_resource);
|
||||
footprint.Offset = staging_region->pos;
|
||||
|
||||
/* Fill staging buffer */
|
||||
{
|
||||
D3D12_RANGE read_range = ZI;
|
||||
u8 *src_base = src;
|
||||
u8 *dst_base = (u8 *)region->ring->base + footprint.Offset;
|
||||
u8 *dst_base = (u8 *)staging_region->ring->base + footprint.Offset;
|
||||
u32 z_size = footprint_row_size * footprint_rows_count;
|
||||
for (i32 z = 0; z < src_dims.z; ++z)
|
||||
{
|
||||
@ -2454,7 +2453,7 @@ void G_CopyCpuToTexture(G_CommandListHandle cl_handle, G_ResourceHandle dst_hand
|
||||
G_CopyBufferToTexture(
|
||||
cl_handle,
|
||||
dst_handle, dst_copy_range,
|
||||
G_D12_MakeHandle(G_ResourceHandle, region->ring->resource), footprint.Offset
|
||||
staging_resource_handle, footprint.Offset
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,8 +8,11 @@ V_WidgetTheme V_GetWidgetTheme(void)
|
||||
// theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/fixedsys.ttf")));
|
||||
// theme.font_size = 16;
|
||||
|
||||
theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/roboto-med.ttf")));
|
||||
theme.font_size = 100;
|
||||
theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/fixedsys.ttf")));
|
||||
theme.font_size = 64;
|
||||
|
||||
// theme.font = GC_FontKeyFromResource(ResourceKeyFromStore(&V_Resources, Lit("font/roboto-med.ttf")));
|
||||
// theme.font_size = 100;
|
||||
|
||||
theme.window_background_color = Rgb32(0xff1a1d1e);
|
||||
theme.window_border_color = Rgb32(0xff343a3b);
|
||||
|
||||
@ -79,12 +79,13 @@ TTF_GlyphResult TTF_RasterizeGlyphFromCodepoint(Arena *arena, u32 codepoint, Res
|
||||
f32 em_size = font_size * (3.0 / 4.0);
|
||||
f32 pixels_per_em = em_size * (TTF_DW_Dpi / 72.0f);
|
||||
|
||||
/* Load font */
|
||||
//////////////////////////////
|
||||
//- Load font
|
||||
|
||||
HRESULT hr = 0;
|
||||
TTF_DW_Font *font = 0;
|
||||
{
|
||||
u64 hash = RandU64FromSeeds(ttf.v, (u64)(*(u32 *)&font_size));
|
||||
/* Grab font from cache */
|
||||
{
|
||||
Lock lock = LockS(&TTF_DW.font_bins_mutex);
|
||||
{
|
||||
@ -99,7 +100,6 @@ TTF_GlyphResult TTF_RasterizeGlyphFromCodepoint(Arena *arena, u32 codepoint, Res
|
||||
}
|
||||
Unlock(&lock);
|
||||
}
|
||||
/* Create font new font */
|
||||
if (!font)
|
||||
{
|
||||
Lock lock = LockE(&TTF_DW.font_bins_mutex);
|
||||
@ -172,11 +172,60 @@ TTF_GlyphResult TTF_RasterizeGlyphFromCodepoint(Arena *arena, u32 codepoint, Res
|
||||
f32 font_descent = font->design_metrics.descent * pixels_per_design_unit;
|
||||
f32 font_cap = font->design_metrics.capHeight * pixels_per_design_unit;
|
||||
|
||||
/* Render glyph */
|
||||
TTF_GlyphResult result = ZI;
|
||||
{
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
/* TODO: Dynamic render target dimensions? */
|
||||
|
||||
//////////////////////////////
|
||||
//- Fetch render target
|
||||
|
||||
Vec2I32 rt_dims = VEC2I32(256, 256);
|
||||
TTF_DW_RenderTarget *rt = 0;
|
||||
{
|
||||
Lock lock = LockE(&TTF_DW.free_render_targets_mutex);
|
||||
{
|
||||
rt = TTF_DW.first_free_render_target;
|
||||
if (rt)
|
||||
{
|
||||
SllStackPop(TTF_DW.first_free_render_target);
|
||||
}
|
||||
}
|
||||
Unlock(&lock);
|
||||
if (!rt)
|
||||
{
|
||||
Arena *perm = PermArena();
|
||||
rt = PushStruct(perm, TTF_DW_RenderTarget);
|
||||
IDWriteBitmapRenderTarget *dw_rt = 0;
|
||||
HDC dc = 0;
|
||||
DIBSECTION dib = ZI;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = IDWriteGdiInterop_CreateBitmapRenderTarget(TTF_DW.gdi_interop, 0, (UINT32)rt_dims.x, (UINT32)rt_dims.y, &dw_rt);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = IDWriteBitmapRenderTarget_SetPixelsPerDip(dw_rt, TTF_DW_Dpi / 96.0);
|
||||
dc = IDWriteBitmapRenderTarget_GetMemoryDC(dw_rt);
|
||||
HBITMAP bitmap = (HBITMAP)GetCurrentObject(dc, OBJ_BITMAP);
|
||||
GetObject(bitmap, sizeof(dib), &dib);
|
||||
}
|
||||
}
|
||||
rt->dims = rt_dims;
|
||||
rt->dw_rt = dw_rt;
|
||||
rt->dc = dc;
|
||||
rt->dib = dib;
|
||||
rt->hr = hr;
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = font->hr;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Render
|
||||
|
||||
/* Glyph idx */
|
||||
u16 glyph_idx = 0;
|
||||
if (SUCCEEDED(hr))
|
||||
@ -192,44 +241,25 @@ TTF_GlyphResult TTF_RasterizeGlyphFromCodepoint(Arena *arena, u32 codepoint, Res
|
||||
}
|
||||
f32 advance = (f32)m.advanceWidth * pixels_per_design_unit;
|
||||
|
||||
/* FIXME: Dynamic render target */
|
||||
Vec2I32 rt_dims = VEC2I32(256, 256);
|
||||
|
||||
/* Best-guess a position in the middle of the render target based on metrics */
|
||||
Vec2I32 rt_baseline = ZI;
|
||||
rt_baseline.x = (rt_dims.x / 2) - (advance / 2);
|
||||
rt_baseline.y = (rt_dims.y / 2) + (font_cap / 2);
|
||||
|
||||
/* Create render target */
|
||||
IDWriteBitmapRenderTarget *render_target = 0;
|
||||
HDC dc = 0;
|
||||
DIBSECTION dib = ZI;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = IDWriteGdiInterop_CreateBitmapRenderTarget(TTF_DW.gdi_interop, 0, (UINT32)rt_dims.x, (UINT32)rt_dims.y, &render_target);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = IDWriteBitmapRenderTarget_SetPixelsPerDip(render_target, TTF_DW_Dpi / 96.0);
|
||||
dc = IDWriteBitmapRenderTarget_GetMemoryDC(render_target);
|
||||
HBITMAP bitmap = (HBITMAP)GetCurrentObject(dc, OBJ_BITMAP);
|
||||
GetObject(bitmap, sizeof(dib), &dib);
|
||||
}
|
||||
}
|
||||
|
||||
/* Render */
|
||||
Rng2I32 rt_slice = ZI;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
/* Clear target */
|
||||
{
|
||||
HGDIOBJ original = SelectObject(dc, GetStockObject(DC_PEN));
|
||||
HGDIOBJ original = SelectObject(rt->dc, GetStockObject(DC_PEN));
|
||||
{
|
||||
SetDCPenColor(dc, bg_color);
|
||||
SelectObject(dc, GetStockObject(DC_BRUSH));
|
||||
SetDCBrushColor(dc, bg_color);
|
||||
Rectangle(dc, 0, 0, rt_dims.x, rt_dims.y);
|
||||
SetDCPenColor(rt->dc, bg_color);
|
||||
SelectObject(rt->dc, GetStockObject(DC_BRUSH));
|
||||
SetDCBrushColor(rt->dc, bg_color);
|
||||
Rectangle(rt->dc, 0, 0, rt_dims.x, rt_dims.y);
|
||||
}
|
||||
SelectObject(dc, original);
|
||||
SelectObject(rt->dc, original);
|
||||
}
|
||||
/* Draw glyph */
|
||||
{
|
||||
@ -242,7 +272,7 @@ TTF_GlyphResult TTF_RasterizeGlyphFromCodepoint(Arena *arena, u32 codepoint, Res
|
||||
}
|
||||
RECT bounding_box = ZI;
|
||||
hr = IDWriteBitmapRenderTarget_DrawGlyphRun(
|
||||
render_target,
|
||||
rt->dw_rt,
|
||||
rt_baseline.x,
|
||||
rt_baseline.y,
|
||||
DWRITE_MEASURING_MODE_NATURAL,
|
||||
@ -262,6 +292,9 @@ TTF_GlyphResult TTF_RasterizeGlyphFromCodepoint(Arena *arena, u32 codepoint, Res
|
||||
rt_slice.p1.y = MinI32(rt_slice.p1.y, rt_dims.y);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Copy result
|
||||
|
||||
/* Copy from target to result */
|
||||
Vec2I32 dst_dims = ZI;
|
||||
u32 *dst_pixels = 0;
|
||||
@ -269,8 +302,8 @@ TTF_GlyphResult TTF_RasterizeGlyphFromCodepoint(Arena *arena, u32 codepoint, Res
|
||||
{
|
||||
dst_dims = DimsFromRng2I32(rt_slice);
|
||||
dst_pixels = PushStructsNoZero(arena, u32, dst_dims.x * dst_dims.y);
|
||||
u64 src_pitch = (u64)dib.dsBm.bmWidthBytes / 4;
|
||||
u32 *src_pixels = (u32 *)dib.dsBm.bmBits;
|
||||
u64 src_pitch = (u64)rt->dib.dsBm.bmWidthBytes / 4;
|
||||
u32 *src_pixels = (u32 *)rt->dib.dsBm.bmBits;
|
||||
for (i32 y = 0; y < dst_dims.y; ++y)
|
||||
{
|
||||
u64 src_y = rt_slice.p0.y + y;
|
||||
@ -292,7 +325,12 @@ TTF_GlyphResult TTF_RasterizeGlyphFromCodepoint(Arena *arena, u32 codepoint, Res
|
||||
result.image_dims = dst_dims;
|
||||
result.image_pixels = dst_pixels;
|
||||
|
||||
IDWriteBitmapRenderTarget_Release(render_target);
|
||||
/* Free render target */
|
||||
{
|
||||
Lock lock = LockE(&TTF_DW.free_render_targets_mutex);
|
||||
SllStackPush(TTF_DW.first_free_render_target, rt);
|
||||
Unlock(&lock);
|
||||
}
|
||||
}
|
||||
result.ttf = ttf;
|
||||
result.codepoint = codepoint;
|
||||
|
||||
@ -158,6 +158,16 @@ Struct(TTF_DW_Font)
|
||||
HRESULT hr;
|
||||
};
|
||||
|
||||
Struct(TTF_DW_RenderTarget)
|
||||
{
|
||||
TTF_DW_RenderTarget *next;
|
||||
Vec2I32 dims;
|
||||
IDWriteBitmapRenderTarget *dw_rt;
|
||||
HDC dc;
|
||||
DIBSECTION dib;
|
||||
HRESULT hr;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Context types
|
||||
|
||||
@ -174,6 +184,9 @@ Struct(TTF_DW_Ctx)
|
||||
|
||||
Mutex font_bins_mutex;
|
||||
TTF_DW_Font *font_bins[1024];
|
||||
|
||||
Mutex free_render_targets_mutex;
|
||||
TTF_DW_RenderTarget *first_free_render_target;
|
||||
};
|
||||
|
||||
extern TTF_DW_Ctx TTF_DW;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user