From 9fc666d49df558fd6f3290fddd91dcd4a988d00a Mon Sep 17 00:00:00 2001 From: jacob Date: Sun, 14 Dec 2025 15:53:00 -0600 Subject: [PATCH] cache DirectWrite render targets --- src/glyph_cache/glyph_cache.c | 265 +++++++++++-------------------- src/glyph_cache/glyph_cache.h | 6 +- src/gpu/gpu_dx12/gpu_dx12_core.c | 25 ++- src/pp/pp_vis/pp_vis_widgets.c | 7 +- src/ttf/ttf_dwrite/ttf_dwrite.c | 104 ++++++++---- src/ttf/ttf_dwrite/ttf_dwrite.h | 13 ++ 6 files changed, 199 insertions(+), 221 deletions(-) diff --git a/src/glyph_cache/glyph_cache.c b/src/glyph_cache/glyph_cache.c index 715d01a9..7f29b345 100644 --- a/src/glyph_cache/glyph_cache.c +++ b/src/glyph_cache/glyph_cache.c @@ -51,41 +51,45 @@ 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); - for (u64 codepoint_idx = 0; codepoint_idx < codepoints_count; ++codepoint_idx) { - u32 codepoint = codepoints[codepoint_idx]; + i64 completion = G_CompletionValueFromQueue(G_QueueKind_AsyncCopy); + for (u64 codepoint_idx = 0; codepoint_idx < codepoints_count; ++codepoint_idx) + { + u32 codepoint = codepoints[codepoint_idx]; - GC_GlyphDesc desc = ZI; - desc.font = font; - desc.font_size = font_size; - desc.codepoint = codepoint; + GC_GlyphDesc desc = ZI; + desc.font = font; + desc.font_size = font_size; + desc.codepoint = codepoint; - u64 hash = GC_HashFromGlyphDesc(desc); - GC_GlyphBin *bin = &GC.glyph_bins[hash % countof(GC.glyph_bins)]; - GC_Glyph *glyph = bin->first; - for (; glyph; glyph = glyph->next) - { - if (glyph->hash == hash) break; - } + u64 hash = GC_HashFromGlyphDesc(desc); + GC_GlyphBin *bin = &GC.glyph_bins[hash % countof(GC.glyph_bins)]; + GC_Glyph *glyph = bin->first; + for (; glyph; glyph = glyph->next) + { + if (glyph->hash == hash) break; + } - if (glyph == 0) - { - uncached_codepoints[uncached_codepoints_count] = codepoint; - uncached_codepoints_count += 1; - } - else if (Atomic32Fetch(&glyph->ready) == 0) - { - pending_glyphs_count += 1; - } - else - { - ready_glyphs[ready_glyphs_count] = glyph; - ready_glyphs_count += 1; + if (glyph == 0) + { + uncached_codepoints[uncached_codepoints_count] = codepoint; + uncached_codepoints_count += 1; + } + else if (completion < Atomic64Fetch(&glyph->async_copy_completion_target)) + { + pending_glyphs_count += 1; + } + else + { + ready_glyphs[ready_glyphs_count] = glyph; + 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 diff --git a/src/glyph_cache/glyph_cache.h b/src/glyph_cache/glyph_cache.h index 740250c4..0aaf4a45 100644 --- a/src/glyph_cache/glyph_cache.h +++ b/src/glyph_cache/glyph_cache.h @@ -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) diff --git a/src/gpu/gpu_dx12/gpu_dx12_core.c b/src/gpu/gpu_dx12/gpu_dx12_core.c index 9fc27f01..f1df6b3b 100644 --- a/src/gpu/gpu_dx12/gpu_dx12_core.c +++ b/src/gpu/gpu_dx12/gpu_dx12_core.c @@ -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 ); } } diff --git a/src/pp/pp_vis/pp_vis_widgets.c b/src/pp/pp_vis/pp_vis_widgets.c index 62822622..ea8b603d 100644 --- a/src/pp/pp_vis/pp_vis_widgets.c +++ b/src/pp/pp_vis/pp_vis_widgets.c @@ -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); diff --git a/src/ttf/ttf_dwrite/ttf_dwrite.c b/src/ttf/ttf_dwrite/ttf_dwrite.c index 31730fac..34fc557d 100644 --- a/src/ttf/ttf_dwrite/ttf_dwrite.c +++ b/src/ttf/ttf_dwrite/ttf_dwrite.c @@ -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; diff --git a/src/ttf/ttf_dwrite/ttf_dwrite.h b/src/ttf/ttf_dwrite/ttf_dwrite.h index 4cca115b..a709bf63 100644 --- a/src/ttf/ttf_dwrite/ttf_dwrite.h +++ b/src/ttf/ttf_dwrite/ttf_dwrite.h @@ -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;