win32 gui: decouple screen renderer from text renderer
I tried adding Andrew's TrueType renderer as an option, however, I realized it doesn't work with zig 0.13.0, so it'll have to wait. Until then here's the changes needed to decouple the screen/text rendering which would make it easier to add more text renderers and just cleans things up a bit more in general.
This commit is contained in:
parent
ce068ee0dc
commit
7558a63819
2 changed files with 163 additions and 167 deletions
|
@ -7,61 +7,16 @@ const win32ext = @import("win32ext.zig");
|
||||||
const dwrite = @import("dwrite.zig");
|
const dwrite = @import("dwrite.zig");
|
||||||
const XY = @import("xy.zig").XY;
|
const XY = @import("xy.zig").XY;
|
||||||
|
|
||||||
staging_texture: StagingTexture = .{},
|
pub const Font = dwrite.Font;
|
||||||
|
|
||||||
|
pub const needs_direct2d = true;
|
||||||
|
|
||||||
const StagingTexture = struct {
|
|
||||||
const Cached = struct {
|
|
||||||
size: XY(u16),
|
|
||||||
texture: *win32.ID3D11Texture2D,
|
|
||||||
render_target: *win32.ID2D1RenderTarget,
|
render_target: *win32.ID2D1RenderTarget,
|
||||||
white_brush: *win32.ID2D1SolidColorBrush,
|
white_brush: *win32.ID2D1SolidColorBrush,
|
||||||
};
|
pub fn init(
|
||||||
cached: ?Cached = null,
|
|
||||||
pub fn update(
|
|
||||||
self: *StagingTexture,
|
|
||||||
d3d_device: *win32.ID3D11Device,
|
|
||||||
d2d_factory: *win32.ID2D1Factory,
|
d2d_factory: *win32.ID2D1Factory,
|
||||||
size: XY(u16),
|
|
||||||
) struct {
|
|
||||||
texture: *win32.ID3D11Texture2D,
|
texture: *win32.ID3D11Texture2D,
|
||||||
render_target: *win32.ID2D1RenderTarget,
|
) DwriteRenderer {
|
||||||
white_brush: *win32.ID2D1SolidColorBrush,
|
|
||||||
} {
|
|
||||||
if (self.cached) |cached| {
|
|
||||||
if (cached.size.eql(size)) return .{
|
|
||||||
.texture = cached.texture,
|
|
||||||
.render_target = cached.render_target,
|
|
||||||
.white_brush = cached.white_brush,
|
|
||||||
};
|
|
||||||
std.log.debug(
|
|
||||||
"resizing staging texture from {}x{} to {}x{}",
|
|
||||||
.{ cached.size.x, cached.size.y, size.x, size.y },
|
|
||||||
);
|
|
||||||
_ = cached.white_brush.IUnknown.Release();
|
|
||||||
_ = cached.render_target.IUnknown.Release();
|
|
||||||
_ = cached.texture.IUnknown.Release();
|
|
||||||
self.cached = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var texture: *win32.ID3D11Texture2D = undefined;
|
|
||||||
const desc: win32.D3D11_TEXTURE2D_DESC = .{
|
|
||||||
.Width = size.x,
|
|
||||||
.Height = size.y,
|
|
||||||
.MipLevels = 1,
|
|
||||||
.ArraySize = 1,
|
|
||||||
.Format = .A8_UNORM,
|
|
||||||
.SampleDesc = .{ .Count = 1, .Quality = 0 },
|
|
||||||
.Usage = .DEFAULT,
|
|
||||||
.BindFlags = .{ .RENDER_TARGET = 1 },
|
|
||||||
.CPUAccessFlags = .{},
|
|
||||||
.MiscFlags = .{},
|
|
||||||
};
|
|
||||||
{
|
|
||||||
const hr = d3d_device.CreateTexture2D(&desc, null, &texture);
|
|
||||||
if (hr < 0) fatalHr("CreateStagingTexture", hr);
|
|
||||||
}
|
|
||||||
errdefer _ = texture.IUnknown.Release();
|
|
||||||
|
|
||||||
const dxgi_surface = win32ext.queryInterface(texture, win32.IDXGISurface);
|
const dxgi_surface = win32ext.queryInterface(texture, win32.IDXGISurface);
|
||||||
defer _ = dxgi_surface.IUnknown.Release();
|
defer _ = dxgi_surface.IUnknown.Release();
|
||||||
|
|
||||||
|
@ -104,48 +59,24 @@ const StagingTexture = struct {
|
||||||
}
|
}
|
||||||
errdefer _ = white_brush.IUnknown.Release();
|
errdefer _ = white_brush.IUnknown.Release();
|
||||||
|
|
||||||
self.cached = .{
|
return .{
|
||||||
.size = size,
|
|
||||||
.texture = texture,
|
|
||||||
.render_target = render_target,
|
.render_target = render_target,
|
||||||
.white_brush = white_brush,
|
.white_brush = white_brush,
|
||||||
};
|
};
|
||||||
return .{
|
|
||||||
.texture = self.cached.?.texture,
|
|
||||||
.render_target = self.cached.?.render_target,
|
|
||||||
.white_brush = self.cached.?.white_brush,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
pub fn deinit(self: *DwriteRenderer) void {
|
||||||
|
_ = self.white_brush.IUnknown.Release();
|
||||||
|
_ = self.render_target.IUnknown.Release();
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render(
|
pub fn render(
|
||||||
self: *DwriteRenderer,
|
self: *const DwriteRenderer,
|
||||||
d3d_device: *win32.ID3D11Device,
|
font: Font,
|
||||||
d3d_context: *win32.ID3D11DeviceContext,
|
utf8: []const u8,
|
||||||
d2d_factory: *win32.ID2D1Factory,
|
|
||||||
font: dwrite.Font,
|
|
||||||
texture: *win32.ID3D11Texture2D,
|
|
||||||
codepoint: u21,
|
|
||||||
coord: XY(u16),
|
|
||||||
) void {
|
) void {
|
||||||
const staging = self.staging_texture.update(
|
|
||||||
d3d_device,
|
|
||||||
d2d_factory,
|
|
||||||
font.cell_size,
|
|
||||||
);
|
|
||||||
|
|
||||||
var utf16_buf: [10]u16 = undefined;
|
var utf16_buf: [10]u16 = undefined;
|
||||||
|
const utf16_len = std.unicode.utf8ToUtf16Le(&utf16_buf, utf8) catch unreachable;
|
||||||
const utf16_len = blk: {
|
|
||||||
var utf8_buf: [7]u8 = undefined;
|
|
||||||
const utf8_len: u3 = std.unicode.utf8Encode(codepoint, &utf8_buf) catch |e| std.debug.panic(
|
|
||||||
"todo: handle invalid codepoint {} (0x{0x}) ({s})",
|
|
||||||
.{ codepoint, @errorName(e) },
|
|
||||||
);
|
|
||||||
const utf8 = utf8_buf[0..utf8_len];
|
|
||||||
break :blk std.unicode.utf8ToUtf16Le(&utf16_buf, utf8) catch unreachable;
|
|
||||||
};
|
|
||||||
|
|
||||||
const utf16 = utf16_buf[0..utf16_len];
|
const utf16 = utf16_buf[0..utf16_len];
|
||||||
std.debug.assert(utf16.len <= 2);
|
std.debug.assert(utf16.len <= 2);
|
||||||
|
|
||||||
|
@ -156,48 +87,28 @@ pub fn render(
|
||||||
.right = @floatFromInt(font.cell_size.x),
|
.right = @floatFromInt(font.cell_size.x),
|
||||||
.bottom = @floatFromInt(font.cell_size.y),
|
.bottom = @floatFromInt(font.cell_size.y),
|
||||||
};
|
};
|
||||||
staging.render_target.BeginDraw();
|
self.render_target.BeginDraw();
|
||||||
{
|
{
|
||||||
const color: win32.D2D_COLOR_F = .{ .r = 0, .g = 0, .b = 0, .a = 0 };
|
const color: win32.D2D_COLOR_F = .{ .r = 0, .g = 0, .b = 0, .a = 0 };
|
||||||
staging.render_target.Clear(&color);
|
self.render_target.Clear(&color);
|
||||||
}
|
}
|
||||||
staging.render_target.DrawText(
|
self.render_target.DrawText(
|
||||||
@ptrCast(utf16.ptr),
|
@ptrCast(utf16.ptr),
|
||||||
@intCast(utf16.len),
|
@intCast(utf16.len),
|
||||||
font.text_format,
|
font.text_format,
|
||||||
&rect,
|
&rect,
|
||||||
&staging.white_brush.ID2D1Brush,
|
&self.white_brush.ID2D1Brush,
|
||||||
.{},
|
.{},
|
||||||
.NATURAL,
|
.NATURAL,
|
||||||
);
|
);
|
||||||
var tag1: u64 = undefined;
|
var tag1: u64 = undefined;
|
||||||
var tag2: u64 = undefined;
|
var tag2: u64 = undefined;
|
||||||
const hr = staging.render_target.EndDraw(&tag1, &tag2);
|
const hr = self.render_target.EndDraw(&tag1, &tag2);
|
||||||
if (hr < 0) std.debug.panic(
|
if (hr < 0) std.debug.panic(
|
||||||
"D2D DrawText failed, hresult=0x{x}, tag1={}, tag2={}",
|
"D2D DrawText failed, hresult=0x{x}, tag1={}, tag2={}",
|
||||||
.{ @as(u32, @bitCast(hr)), tag1, tag2 },
|
.{ @as(u32, @bitCast(hr)), tag1, tag2 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const box: win32.D3D11_BOX = .{
|
|
||||||
.left = 0,
|
|
||||||
.top = 0,
|
|
||||||
.front = 0,
|
|
||||||
.right = font.cell_size.x,
|
|
||||||
.bottom = font.cell_size.y,
|
|
||||||
.back = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
d3d_context.CopySubresourceRegion(
|
|
||||||
&texture.ID3D11Resource,
|
|
||||||
0, // subresource
|
|
||||||
coord.x,
|
|
||||||
coord.y,
|
|
||||||
0, // z
|
|
||||||
&staging.texture.ID3D11Resource,
|
|
||||||
0, // subresource
|
|
||||||
&box,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fatalHr(what: []const u8, hresult: win32.HRESULT) noreturn {
|
fn fatalHr(what: []const u8, hresult: win32.HRESULT) noreturn {
|
||||||
|
|
|
@ -6,9 +6,10 @@ const win32ext = @import("win32ext.zig");
|
||||||
const dwrite = @import("dwrite.zig");
|
const dwrite = @import("dwrite.zig");
|
||||||
const GlyphIndexCache = @import("GlyphIndexCache.zig");
|
const GlyphIndexCache = @import("GlyphIndexCache.zig");
|
||||||
const TextRenderer = @import("DwriteRenderer.zig");
|
const TextRenderer = @import("DwriteRenderer.zig");
|
||||||
|
|
||||||
const XY = @import("xy.zig").XY;
|
const XY = @import("xy.zig").XY;
|
||||||
|
|
||||||
pub const Font = dwrite.Font;
|
pub const Font = TextRenderer.Font;
|
||||||
|
|
||||||
const log = std.log.scoped(.d3d);
|
const log = std.log.scoped(.d3d);
|
||||||
|
|
||||||
|
@ -16,14 +17,16 @@ const log = std.log.scoped(.d3d);
|
||||||
// bad artifacts when the window is resized
|
// bad artifacts when the window is resized
|
||||||
pub const NOREDIRECTIONBITMAP = 1;
|
pub const NOREDIRECTIONBITMAP = 1;
|
||||||
|
|
||||||
|
const D2dFactory = if (TextRenderer.needs_direct2d) *win32.ID2D1Factory else void;
|
||||||
|
|
||||||
const global = struct {
|
const global = struct {
|
||||||
var init_called: bool = false;
|
var init_called: bool = false;
|
||||||
var d3d: D3d = undefined;
|
var d3d: D3d = undefined;
|
||||||
var shaders: Shaders = undefined;
|
var shaders: Shaders = undefined;
|
||||||
var const_buf: *win32.ID3D11Buffer = undefined;
|
var const_buf: *win32.ID3D11Buffer = undefined;
|
||||||
var d2d_factory: *win32.ID2D1Factory = undefined;
|
var d2d_factory: D2dFactory = undefined;
|
||||||
var glyph_cache_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
var glyph_cache_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
var text_renderer: TextRenderer = undefined;
|
var staging_texture: StagingTexture = .{};
|
||||||
var background: Rgba8 = .{ .r = 19, .g = 19, .b = 19, .a = 255 };
|
var background: Rgba8 = .{ .r = 19, .g = 19, .b = 19, .a = 255 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -106,7 +109,7 @@ pub fn init(opt: struct {
|
||||||
if (hr < 0) fatalHr("CreateBuffer for grid config", hr);
|
if (hr < 0) fatalHr("CreateBuffer for grid config", hr);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
if (TextRenderer.needs_direct2d) {
|
||||||
const hr = win32.D2D1CreateFactory(
|
const hr = win32.D2D1CreateFactory(
|
||||||
.SINGLE_THREADED,
|
.SINGLE_THREADED,
|
||||||
win32.IID_ID2D1Factory,
|
win32.IID_ID2D1Factory,
|
||||||
|
@ -189,14 +192,34 @@ pub const WindowState = struct {
|
||||||
// defer if (!render_success) state.glyph_index_cache.remove(reserved.index);
|
// defer if (!render_success) state.glyph_index_cache.remove(reserved.index);
|
||||||
const pos: XY(u16) = cellPosFromIndex(reserved.index, texture_cell_count.x);
|
const pos: XY(u16) = cellPosFromIndex(reserved.index, texture_cell_count.x);
|
||||||
const coord = coordFromCellPos(font.cell_size, pos);
|
const coord = coordFromCellPos(font.cell_size, pos);
|
||||||
global.text_renderer.render(
|
const staging = global.staging_texture.update(font.cell_size);
|
||||||
global.d3d.device,
|
var utf8_buf: [7]u8 = undefined;
|
||||||
global.d3d.context,
|
const utf8_len: u3 = std.unicode.utf8Encode(codepoint, &utf8_buf) catch |e| std.debug.panic(
|
||||||
global.d2d_factory,
|
"todo: handle invalid codepoint {} (0x{0x}) ({s})",
|
||||||
|
.{ codepoint, @errorName(e) },
|
||||||
|
);
|
||||||
|
staging.text_renderer.render(
|
||||||
font,
|
font,
|
||||||
state.glyph_texture.obj,
|
utf8_buf[0..utf8_len],
|
||||||
codepoint,
|
);
|
||||||
coord,
|
const box: win32.D3D11_BOX = .{
|
||||||
|
.left = 0,
|
||||||
|
.top = 0,
|
||||||
|
.front = 0,
|
||||||
|
.right = font.cell_size.x,
|
||||||
|
.bottom = font.cell_size.y,
|
||||||
|
.back = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
global.d3d.context.CopySubresourceRegion(
|
||||||
|
&state.glyph_texture.obj.ID3D11Resource,
|
||||||
|
0, // subresource
|
||||||
|
coord.x,
|
||||||
|
coord.y,
|
||||||
|
0, // z
|
||||||
|
&staging.texture.ID3D11Resource,
|
||||||
|
0, // subresource
|
||||||
|
&box,
|
||||||
);
|
);
|
||||||
return reserved.index;
|
return reserved.index;
|
||||||
},
|
},
|
||||||
|
@ -683,6 +706,68 @@ const GlyphTexture = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const StagingTexture = struct {
|
||||||
|
const Cached = struct {
|
||||||
|
size: XY(u16),
|
||||||
|
texture: *win32.ID3D11Texture2D,
|
||||||
|
text_renderer: TextRenderer,
|
||||||
|
};
|
||||||
|
cached: ?Cached = null,
|
||||||
|
pub fn update(
|
||||||
|
self: *StagingTexture,
|
||||||
|
size: XY(u16),
|
||||||
|
) struct {
|
||||||
|
texture: *win32.ID3D11Texture2D,
|
||||||
|
text_renderer: TextRenderer,
|
||||||
|
} {
|
||||||
|
if (self.cached) |*cached| {
|
||||||
|
if (cached.size.eql(size)) return .{
|
||||||
|
.texture = cached.texture,
|
||||||
|
.text_renderer = cached.text_renderer,
|
||||||
|
};
|
||||||
|
std.log.debug(
|
||||||
|
"resizing staging texture from {}x{} to {}x{}",
|
||||||
|
.{ cached.size.x, cached.size.y, size.x, size.y },
|
||||||
|
);
|
||||||
|
cached.text_renderer.deinit();
|
||||||
|
_ = cached.texture.IUnknown.Release();
|
||||||
|
self.cached = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var texture: *win32.ID3D11Texture2D = undefined;
|
||||||
|
const desc: win32.D3D11_TEXTURE2D_DESC = .{
|
||||||
|
.Width = size.x,
|
||||||
|
.Height = size.y,
|
||||||
|
.MipLevels = 1,
|
||||||
|
.ArraySize = 1,
|
||||||
|
.Format = .A8_UNORM,
|
||||||
|
.SampleDesc = .{ .Count = 1, .Quality = 0 },
|
||||||
|
.Usage = .DEFAULT,
|
||||||
|
.BindFlags = .{ .RENDER_TARGET = 1 },
|
||||||
|
.CPUAccessFlags = .{},
|
||||||
|
.MiscFlags = .{},
|
||||||
|
};
|
||||||
|
{
|
||||||
|
const hr = global.d3d.device.CreateTexture2D(&desc, null, &texture);
|
||||||
|
if (hr < 0) fatalHr("CreateStagingTexture", hr);
|
||||||
|
}
|
||||||
|
errdefer _ = texture.IUnknown.Release();
|
||||||
|
|
||||||
|
const text_renderer = TextRenderer.init(global.d2d_factory, texture);
|
||||||
|
errdefer text_renderer.deinit();
|
||||||
|
|
||||||
|
self.cached = .{
|
||||||
|
.size = size,
|
||||||
|
.texture = texture,
|
||||||
|
.text_renderer = text_renderer,
|
||||||
|
};
|
||||||
|
return .{
|
||||||
|
.texture = self.cached.?.texture,
|
||||||
|
.text_renderer = text_renderer,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
fn createRenderTargetView(
|
fn createRenderTargetView(
|
||||||
device: *win32.ID3D11Device,
|
device: *win32.ID3D11Device,
|
||||||
swap_chain: *win32.IDXGISwapChain,
|
swap_chain: *win32.IDXGISwapChain,
|
||||||
|
|
Loading…
Add table
Reference in a new issue