Backlog/engine/ui/src/PapyrusIntegration.zig

741 lines
26 KiB
Zig

gc: *graphics.NeonVkContext,
allocator: std.mem.Allocator,
pipeData: gpd.GpuPipeData = undefined,
materialName: core.Name = core.Name.MakeComptime("mat_papyrus"),
materialNameText: core.Name = core.Name.MakeComptime("mat_papyrus_text"),
material: *graphics.Material = undefined, // main material used for anything that isn't text
defaultTextureSet: vk.DescriptorSet,
textMaterial: *graphics.Material = undefined, // main material used for text
mappedBuffers: []gpd.GpuMappingData(ImageGpu) = undefined,
textImageBuffers: []gpd.GpuMappingData(FontInfo) = undefined,
indexBuffer: graphics.IndexBuffer = undefined,
drawList: papyrus.DrawList,
stringArena: std.heap.ArenaAllocator,
fontTexture: *graphics.Texture = undefined,
papyrusCtx: *papyrus.Context,
quad: *graphics.Mesh,
ssboCount: u32 = 0,
textSsboCount: u32 = 0,
time: f64 = 0,
textPipeData: gpd.GpuPipeData = undefined,
displayDemo: bool = true,
drawCommands: std.ArrayList(VkCommand),
textRenderer: *TextRenderer,
averageFrameTime: f64 = 0,
onDebugInfoBinding: usize = 0,
sharedData: [graphics.NumFrames]SharedData = undefined,
const std = @import("std");
const core = @import("core");
const memory = core.MemoryTracker;
const assets = @import("assets");
const graphics = @import("graphics");
const gpd = graphics.gpu_pipe_data;
const RenderThread = graphics.RenderThread;
const platform = @import("platform");
const papyrus = @import("papyrus");
const papyrus_vk_vert = @import("papyrus_vk_vert");
const papyrus_vk_frag = @import("papyrus_vk_frag");
const FontSDF_vert = @import("FontSDF_vert");
const FontSDF_frag = @import("FontSDF_frag");
const gl = @import("glslTypes");
const vk = @import("vulkan");
const tracy = core.tracy;
const Text = papyrus.Text;
const VkCommand = @import("VkPapyrusRenderCommand.zig").VkCommand;
const text_render = @import("text_render.zig");
const TextRenderer = text_render.TextRenderer;
const DisplayText = text_render.DisplayText;
const FontAtlasVk = text_render.FontAtlasVk;
const Key = papyrus.Event.Key;
pub const RawInputListenerVTable = platform.windowing.RawInputListenerInterface.from(@This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
pub const RendererInterfaceVTable = graphics.RendererInterface.from(@This());
pub const PushConstant = FontSDF_vert.constants;
pub const ImageGpu = papyrus_vk_vert.ImageRenderData;
pub const FontInfo = FontSDF_vert.FontInfo;
pub fn init(allocator: std.mem.Allocator) !*@This() {
const papyrusCtx = try papyrus.initialize(allocator);
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
.gc = graphics.getContext(),
.papyrusCtx = papyrusCtx,
.quad = try allocator.create(graphics.Mesh),
.drawCommands = std.ArrayList(VkCommand).init(allocator),
.textRenderer = try TextRenderer.init(allocator, graphics.getContext(), papyrusCtx),
.drawList = papyrus.DrawList.init(allocator),
.stringArena = std.heap.ArenaAllocator.init(allocator),
.defaultTextureSet = undefined,
};
self.initShared();
self.onDebugInfoBinding = try core.addEngineDelegateBinding("onFrameDebugInfoEmitted", onFrameDebugInfo, self);
// core.engine_logs("PapyrusSystem init");
memory.MTPrintStatsDelta();
try platform.getInstance().installListener(self);
return self;
}
pub fn onFrameDebugInfo(ctx: *anyopaque, averageFrameTime: f64) core.EngineDataEventError!void {
const self: *@This() = @ptrCast(@alignCast(ctx));
self.averageFrameTime = averageFrameTime;
}
pub fn OnIoEvent(self: *@This(), event: platform.IOEvent) platform.InputListenerError!void {
try OnIoEvent_GLFW(self, event);
}
pub fn OnIoEvent_GLFW(self: *@This(), event: platform.IOEvent) platform.InputListenerError!void {
switch (event) {
.mousePosition => |mousePosition| {
_ = mousePosition;
},
.mouseButton => |mouseButton| {
var keycode: Key = .Unknown;
var eventType: papyrus.Event.PressedType = .onPressed;
switch (mouseButton.button) {
0 => {
// left click
keycode = Key.Mouse1;
},
1 => {
// right click
keycode = Key.Mouse2;
},
2 => {
// middle click
keycode = Key.Mouse3;
},
3 => {
// button 3
keycode = Key.Mouse4;
},
4 => {
// button 4
keycode = Key.Mouse5;
},
else => {},
}
switch (mouseButton.action) {
0 => {
eventType = .onReleased;
},
1 => {
eventType = .onPressed;
},
else => {},
}
// todo: use the right error code here
self.papyrusCtx.onKey(keycode, eventType) catch unreachable;
},
.windowResize => |e| {
const pi = platform.getInstance();
self.papyrusCtx.get(.{}).setSize(.{
.x = e.newSize.x / pi.contentScale.x,
.y = e.newSize.y / pi.contentScale.y,
});
},
.codepoint => |codepoint| {
try self.papyrusCtx.textEntry.sendCodePoint(@as(u32, codepoint));
},
.key => |keyEvent| {
var te = self.papyrusCtx.textEntry;
const actions = platform.glfw_defs.actions;
const keys = platform.glfw_defs.keys;
if (@as(actions, @enumFromInt(keyEvent.action)) == actions.Press or
@as(actions, @enumFromInt(keyEvent.action)) == actions.Repeat)
{
// reference glfw3.h
switch (@as(keys, @enumFromInt(keyEvent.key))) {
keys.Escape => {
try te.sendEscape();
},
keys.Enter => {
try te.sendEnter();
},
keys.Tab => {
try te.sendTab();
},
keys.Backspace => {
try te.sendBackspace();
},
keys.Delete => {
try te.sendDelete();
},
keys.Right => {
try te.sendRight();
},
keys.Left => {
try te.sendLeft();
},
keys.Up => {
try te.sendUp();
},
keys.Down => {
try te.sendDown();
},
keys.Pageup => {
try te.sendPageup();
},
keys.Pagedown => {
try te.sendPagedown();
},
keys.Home => {
try te.sendHome();
},
keys.End => {
try te.sendEnd();
},
else => {},
}
}
},
else => {},
}
}
const t_white_name = core.StaticName("t_white");
pub fn setup(self: *@This(), gc: *graphics.NeonVkContext) !void {
core.ui_log("Papyrus Subsystem setup {x}", .{@intFromPtr(self)});
self.gc = gc;
try self.preparePipeline();
try self.setupMeshes();
try self.gc.registerRendererPlugin(self);
self.defaultTextureSet = self.gc.textureSets.get(t_white_name.handle()).?;
self.mappedBuffers = try self.pipeData.mapBuffers(self.gc, ImageGpu, 0);
self.textImageBuffers = try self.textPipeData.mapBuffers(self.gc, FontInfo, 0);
// core.ui_log("Mapping buffers.", .{});
const extent = self.gc.actual_extent;
const pi = platform.getInstance();
self.papyrusCtx.get(.{}).setSize(.{
.x = @as(f32, @floatFromInt(extent.width)) / pi.contentScale.x,
.y = @as(f32, @floatFromInt(extent.height)) / pi.contentScale.y,
});
}
pub fn preDraw(self: *@This(), frameId: usize) void {
_ = self;
_ = frameId;
}
// todo: this thing should be optional
pub fn onBindObject(self: *@This(), objectHandle: core.ObjectHandle, objectIndex: usize, cmd: vk.CommandBuffer, frameIndex: usize) void {
_ = self;
_ = objectHandle;
_ = objectIndex;
_ = cmd;
_ = frameIndex;
}
// Uploads a new primitive mesh and an index buffer to the gpu.
fn setupMeshes(self: *@This()) !void {
self.quad.* = graphics.Mesh.init(self.gc, self.allocator);
try self.quad.vertices.resize(4);
var indexBuffer: [6]u32 = undefined;
self.quad.vertices.items[0].position = .{ .x = 1, .y = 1, .z = 0 }; // bot right
self.quad.vertices.items[1].position = .{ .x = 1, .y = -1, .z = 0 }; // top right
self.quad.vertices.items[2].position = .{ .x = -1, .y = -1, .z = 0 }; // top left
self.quad.vertices.items[3].position = .{ .x = -1, .y = 1, .z = 0 }; // bot left
self.quad.vertices.items[0].uv = .{ .x = 1.0, .y = 1.0 };
self.quad.vertices.items[1].uv = .{ .x = 1.0, .y = 0.0 };
self.quad.vertices.items[2].uv = .{ .x = 0.0, .y = 0.0 };
self.quad.vertices.items[3].uv = .{ .x = 0.0, .y = 1.0 };
indexBuffer[0] = 0;
indexBuffer[1] = 1;
indexBuffer[2] = 2;
indexBuffer[3] = 2;
indexBuffer[4] = 3;
indexBuffer[5] = 0;
self.indexBuffer = try graphics.IndexBuffer.uploadIndexBuffer(self.gc, &indexBuffer, self.allocator);
try self.quad.upload(self.gc);
}
var lastEventCount: usize = 0;
var displayEventsPerSecond: f64 = 0;
pub fn tick(self: *@This(), deltaTime: f64) void {
self.time += deltaTime;
const cursor = platform.getInstance().inputState.mousePos;
// TODO, use OnIoEvent, but ehh this is fine.
self.papyrusCtx.setCursorLocation(.{
.x = @floatCast(cursor.x),
.y = @floatCast(cursor.y),
});
self.papyrusCtx.tick(deltaTime) catch unreachable;
if (memory.MTGet()) |mt| {
const eventsPerFrame = @as(f64, @floatFromInt(mt.eventsCount - lastEventCount));
displayEventsPerSecond = (displayEventsPerSecond + eventsPerFrame / 60.0) - displayEventsPerSecond / 60.0;
self.papyrusCtx.pushDebugText("memory used: {d:.3}MB {d} allocations ({d} events per frame)", .{
@as(f32, @floatFromInt(mt.totalAllocSize)) / 1e6,
mt.allocationsCount,
eventsPerFrame,
}) catch {};
lastEventCount = mt.eventsCount;
}
if (self.gc.vulkanValidation) {
self.papyrusCtx.pushDebugText("vulkan validation: ON", .{}) catch unreachable;
}
self.papyrusCtx.pushDebugText("ecs entities: {d}", .{core.getRegistry().baseSet.count()}) catch unreachable;
self.papyrusCtx.pushDebugText("frameTime (ms): {d:.4} fps: {d:.3} engine uptime: {d:.3}", .{ self.averageFrameTime * 1000.0, 1.0 / self.averageFrameTime, core.getEngineUptime() }) catch unreachable;
self.papyrusCtx.pushDebugText(" systems (ms): {d:.4}", .{
core.getEngine().systemsThreadTime * 1000.0,
}) catch unreachable;
self.papyrusCtx.pushDebugText(" renderthread (ms): {d:.4}", .{core.getEngine().renderThreadTime * 1000.0}) catch unreachable;
// for(core.getEngine().)
}
pub fn buildTextPipeline(self: *@This()) !void {
var gpdBuilder = gpd.GpuPipeDataBuilder.init(self.allocator, self.gc);
gpdBuilder.objectCount = 64;
try gpdBuilder.addBufferBinding(
FontInfo,
.storage_buffer,
.{ .vertex_bit = true, .fragment_bit = true },
.storageBuffer,
);
self.textPipeData = try gpdBuilder.build("Papyrus-Text");
defer gpdBuilder.deinit();
// const vert_spv = try graphics.loadSpv(self.allocator, "FontSDF_vert.spv");
// defer self.allocator.free(vert_spv);
// const frag_spv = try graphics.loadSpv(self.allocator, "FontSDF_frag.spv");
// defer self.allocator.free(frag_spv);
const vert_spv = FontSDF_vert.spv();
const frag_spv = FontSDF_frag.spv();
var builder = try graphics.NeonVkPipelineBuilder.init(
self.gc.dev,
self.gc.vkd,
self.gc.allocator,
self.gc.vkAllocator,
vert_spv,
frag_spv,
);
defer builder.deinit();
try builder.add_mesh_description();
try builder.add_layout(self.textPipeData.descriptorSetLayout);
try builder.add_layout(self.gc.singleTextureSetLayout);
try builder.add_depth_stencil();
try builder.add_push_constant_custom(PushConstant);
try builder.init_triangle_pipeline(self.gc.actual_extent);
self.textMaterial = try self.allocator.create(graphics.Material);
self.textMaterial.* = graphics.Material{
.materialName = self.materialNameText,
.pipeline = (try builder.build(self.gc.renderPass)).?,
.layout = builder.pipelineLayout,
};
try self.gc.add_material(self.textMaterial);
}
pub fn buildImagePipeline(self: *@This()) !void {
var spriteDataBuilder = gpd.GpuPipeDataBuilder.init(self.allocator, self.gc);
try spriteDataBuilder.addBufferBinding(
ImageGpu,
.storage_buffer,
.{ .vertex_bit = true, .fragment_bit = true },
.storageBuffer,
);
self.pipeData = try spriteDataBuilder.build("Papyrus");
defer spriteDataBuilder.deinit();
const vert_spv = papyrus_vk_vert.spv();
const frag_spv = papyrus_vk_frag.spv();
var builder = try graphics.NeonVkPipelineBuilder.init(
self.gc.dev,
self.gc.vkd,
self.gc.allocator,
self.gc.vkAllocator,
vert_spv,
frag_spv,
);
defer builder.deinit();
try builder.add_mesh_description();
try builder.add_layout(self.pipeData.descriptorSetLayout);
try builder.add_layout(self.gc.singleTextureSetLayout);
try builder.add_depth_stencil();
try builder.add_push_constant_custom(PushConstant);
try builder.init_triangle_pipeline(self.gc.actual_extent);
const material = try self.gc.allocator.create(graphics.Material);
material.* = graphics.Material{
.materialName = self.materialName,
.pipeline = (try builder.build(self.gc.renderPass)).?,
.layout = builder.pipelineLayout,
};
try self.gc.add_material(material);
self.material = material;
}
pub fn preparePipeline(self: *@This()) !void {
try self.buildTextPipeline();
try self.buildImagePipeline();
}
pub fn uploadSSBOData(self: *@This(), frameId: usize, drawList: *const papyrus.DrawList) !void {
var z = tracy.ZoneN(@src(), "Uploading SSBOs");
defer z.End();
var imagesGpu = self.mappedBuffers[frameId].objects;
var imagesText = self.textImageBuffers[frameId].objects;
self.textSsboCount = 0;
self.ssboCount = 0;
var textFrameContext = self.textRenderer.startRendering();
for (drawList.items) |drawCmd| {
switch (drawCmd.primitive) {
.Rect => |rect| {
imagesGpu[self.ssboCount] = ImageGpu{
.imagePosition = .{ .x = rect.tl.x, .y = rect.tl.y },
.imageSize = .{ .x = rect.size.x, .y = rect.size.y },
.anchorPoint = .{ .x = -1.0, .y = -1.0 },
.scale = .{ .x = 1.0, .y = 1.0 },
.alpha = 1.0,
.pad0 = std.mem.zeroes([4]u8),
.baseColor = .{
.x = rect.backgroundColor.r,
.y = rect.backgroundColor.g,
.z = rect.backgroundColor.b,
.w = rect.backgroundColor.a,
},
.rounding = .{
.x = rect.rounding.tl,
.y = rect.rounding.tr,
.z = rect.rounding.bl,
.w = rect.rounding.br,
},
.borderColor = .{
.x = rect.borderColor.r,
.y = rect.borderColor.g,
.z = rect.borderColor.b,
.w = rect.borderColor.a,
},
.borderWidth = rect.borderWidth,
.flags = 0,
};
var imageSet: ?vk.DescriptorSet = null;
if (rect.imageRef) |_imageRef| {
var ref = _imageRef;
if (self.gc.textureSets.get(ref.handle())) |maybeImageSet| {
imageSet = maybeImageSet;
imagesGpu[self.ssboCount].flags = 1;
}
}
try self.drawCommands.append(.{
.image = .{ .index = self.ssboCount, .imageSet = imageSet },
});
// core.ui_log("drawCmd: {any} {any} {any}", .{
// drawCmd.node,
// imagesGpu[self.ssboCount].imagePosition,
// imagesGpu[self.ssboCount].imageSize,
// });
self.ssboCount += 1;
},
.Text => |text| {
const nextDisplay = self.textRenderer.getNextSlot(text.text.utf8.len, &textFrameContext);
var textDisplay: *DisplayText = undefined;
if (nextDisplay.small) {
textDisplay = self.textRenderer.smallDisplays.items[nextDisplay.index];
} else {
textDisplay = self.textRenderer.displays.items[nextDisplay.index];
}
textDisplay.displaySize = text.textSize;
textDisplay.renderMode = text.renderMode;
textDisplay.boxSize = .{ .x = text.size.x, .y = text.size.y };
textDisplay.color = text.color;
textDisplay.position = .{ .x = text.tl.x, .y = text.tl.y };
// TODO this is really bad and confusing.
// The renderer should not be storing a hash on the papyrus resource.
if (text.rendererHash != 0) {
textDisplay.atlas = self.textRenderer.fonts.get(text.rendererHash).?;
}
textDisplay.setString(&text.text.utf8);
try textDisplay.updateMesh(text.flags.setSourceGeometry);
self.papyrusCtx.get(drawCmd.node).textRenderedSize = textDisplay.renderedSize;
if (text.flags.setSourceGeometry) {
self.papyrusCtx.textEntry.trg = textDisplay.renderedGeo;
}
// core.ui_log("nextDisplay = {any}, font = {d} sdf={any} ssbo={d}", .{
// nextDisplay,
// text.rendererHash,
// textDisplay.atlas.atlas.isSDF,
// self.textSsboCount,
// });
imagesText[self.textSsboCount] = .{
.isSdf = if (textDisplay.atlas.atlas.isSDF) 1 else 0,
.position = .{ .x = textDisplay.position.x, .y = textDisplay.position.y },
.size = .{ .x = textDisplay.boxSize.x, .y = textDisplay.boxSize.y },
.pad0 = 0,
.pad2 = undefined,
};
try self.drawCommands.append(.{ .text = .{
.index = nextDisplay.index,
.small = nextDisplay.small,
.ssbo = self.textSsboCount,
} });
self.textSsboCount += 1;
},
}
}
}
pub fn rtPostDraw(self: *@This(), rt: *RenderThread, cmd: vk.CommandBuffer, frameIndex: u32) void {
const shared = self.getShared(frameIndex);
self.drawCommands.clearRetainingCapacity();
const rtShared = rt.getShared(frameIndex);
shared.lock.lock();
self.uploadSSBOData(frameIndex, &shared.drawList) catch unreachable;
shared.lock.unlock();
var vertexBufferOffset: u64 = 0;
var pushConstant = PushConstant{
.extent = .{
.x = rtShared.extent.x,
.y = rtShared.extent.y,
},
};
for (self.drawCommands.items) |command| {
switch (command) {
.text => |t| {
self.gc.vkd.cmdPushConstants(cmd, self.textMaterial.layout, .{ .vertex_bit = true, .fragment_bit = true }, 0, @sizeOf(PushConstant), &pushConstant);
if (t.small) {
var drawText = self.textRenderer.smallDisplays.items[t.index];
drawText.draw(frameIndex, cmd, self.textMaterial, t.ssbo, self.textPipeData);
} else {
var drawText = self.textRenderer.displays.items[t.index];
drawText.draw(frameIndex, cmd, self.textMaterial, t.ssbo, self.textPipeData);
}
},
.image => |img| {
self.gc.vkd.cmdPushConstants(cmd, self.material.layout, .{ .vertex_bit = true, .fragment_bit = true }, 0, @sizeOf(PushConstant), &pushConstant);
const index = img.index;
self.gc.vkd.cmdBindPipeline(cmd, .graphics, self.material.pipeline);
self.gc.vkd.cmdBindVertexBuffers(cmd, 0, 1, @ptrCast(&self.quad.buffer.buffer), @ptrCast(&vertexBufferOffset));
self.gc.vkd.cmdBindIndexBuffer(cmd, self.indexBuffer.buffer.buffer, 0, .uint32);
self.gc.vkd.cmdBindDescriptorSets(cmd, .graphics, self.material.layout, 0, 1, self.pipeData.getDescriptorSet(frameIndex), 0, undefined);
if (img.imageSet) |imageSet| {
self.gc.vkd.cmdBindDescriptorSets(cmd, .graphics, self.material.layout, 1, 1, @ptrCast(&imageSet), 0, undefined);
} else {
self.gc.vkd.cmdBindDescriptorSets(cmd, .graphics, self.material.layout, 1, 1, @ptrCast(&self.defaultTextureSet), 0, undefined);
}
self.gc.vkd.cmdDrawIndexed(cmd, @as(u32, @intCast(self.indexBuffer.indices.len)), 1, 0, 0, index);
},
}
}
}
pub fn getShared(self: *@This(), fi: u32) *SharedData {
return &self.sharedData[fi];
}
pub fn sendShared(self: *@This(), frameIndex: u32) void {
const z1 = tracy.ZoneN(@src(), "PapryusIntegration - UploadingShared");
defer z1.End();
const shared = self.getShared(frameIndex);
shared.lock.lock();
defer shared.lock.unlock();
self.papyrusCtx.makeDrawList(&shared.drawList, &shared.stringArena) catch unreachable;
}
pub fn postDraw(self: *@This(), cmd: vk.CommandBuffer, frameIndex: usize, frameTime: f64) void {
_ = frameTime;
var z = tracy.ZoneN(@src(), "Papyrus post draw");
defer z.End();
var vertexBufferOffset: u64 = 0;
if (!self.displayDemo) {
return;
}
var z1 = tracy.ZoneN(@src(), "Papyrus Making Draw List");
self.papyrusCtx.makeDrawList(&self.drawList, &self.stringArena) catch unreachable;
z1.End();
self.drawCommands.clearRetainingCapacity();
self.uploadSSBOData(frameIndex, &self.drawList) catch unreachable;
var pushConstant = PushConstant{
.extent = .{
.x = @as(f32, @floatFromInt(self.gc.extent.width)),
.y = @as(f32, @floatFromInt(self.gc.extent.height)),
},
};
for (self.drawCommands.items) |command| {
switch (command) {
.text => |t| {
self.gc.vkd.cmdPushConstants(cmd, self.textMaterial.layout, .{ .vertex_bit = true, .fragment_bit = true }, 0, @sizeOf(PushConstant), &pushConstant);
if (t.small) {
var drawText = self.textRenderer.smallDisplays.items[t.index];
drawText.draw(frameIndex, cmd, self.textMaterial, t.ssbo, self.textPipeData);
} else {
var drawText = self.textRenderer.displays.items[t.index];
drawText.draw(frameIndex, cmd, self.textMaterial, t.ssbo, self.textPipeData);
}
},
.image => |img| {
self.gc.vkd.cmdPushConstants(cmd, self.material.layout, .{ .vertex_bit = true, .fragment_bit = true }, 0, @sizeOf(PushConstant), &pushConstant);
const index = img.index;
self.gc.vkd.cmdBindPipeline(cmd, .graphics, self.material.pipeline);
self.gc.vkd.cmdBindVertexBuffers(cmd, 0, 1, @ptrCast(&self.quad.buffer.buffer), @ptrCast(&vertexBufferOffset));
self.gc.vkd.cmdBindIndexBuffer(cmd, self.indexBuffer.buffer.buffer, 0, .uint32);
self.gc.vkd.cmdBindDescriptorSets(cmd, .graphics, self.material.layout, 0, 1, self.pipeData.getDescriptorSet(frameIndex), 0, undefined);
if (img.imageSet) |imageSet| {
self.gc.vkd.cmdBindDescriptorSets(cmd, .graphics, self.material.layout, 1, 1, @ptrCast(&imageSet), 0, undefined);
} else {
self.gc.vkd.cmdBindDescriptorSets(cmd, .graphics, self.material.layout, 1, 1, @ptrCast(&self.defaultTextureSet), 0, undefined);
}
self.gc.vkd.cmdDrawIndexed(cmd, @as(u32, @intCast(self.indexBuffer.indices.len)), 1, 0, 0, index);
},
}
}
}
pub fn shutdown(self: *@This()) void {
self.gc.vkd.deviceWaitIdle(self.gc.dev) catch unreachable;
core.ui_logs("Shutting down UI");
for (self.mappedBuffers) |*mapped| {
mapped.unmap(self.gc);
}
for (self.textImageBuffers) |*mapped| {
mapped.unmap(self.gc);
}
self.drawCommands.deinit();
self.stringArena.deinit();
self.quad.deinit(self.gc);
self.allocator.destroy(self.quad);
self.gc.allocator.free(self.mappedBuffers);
self.gc.allocator.free(self.textImageBuffers);
self.textRenderer.deinit(self.allocator);
self.pipeData.deinit(self.allocator, self.gc);
self.textPipeData.deinit(self.allocator, self.gc);
self.indexBuffer.deinit(self.gc);
self.drawList.deinit();
self.papyrusCtx.deinit();
self.deinitShared();
core.ui_logs("finished shutting down ui");
}
pub fn deinit(self: *@This()) void {
self.shutdown();
self.allocator.destroy(self);
}
pub fn processEvents(self: *@This(), frameNumber: u64) core.EngineDataEventError!void {
_ = frameNumber;
_ = self;
}
const SharedData = struct {
lock: std.Thread.Mutex,
drawList: papyrus.DrawList,
stringArena: std.heap.ArenaAllocator,
};
pub fn initShared(self: *@This()) void {
for (&self.sharedData) |*s| {
s.* = .{
.lock = .{},
.drawList = papyrus.DrawList.init(self.allocator),
.stringArena = std.heap.ArenaAllocator.init(self.allocator),
};
}
}
pub fn deinitShared(self: *@This()) void {
for (&self.sharedData) |*s| {
s.drawList.deinit();
s.stringArena.deinit();
}
}