roleplay/client/include/ui.h

353 lines
8.0 KiB
C

#ifndef PIPELINE_H
#define PIPELINE_H
#include "vulkan/vulkan_core.h"
#include "cglm/types.h"
#include "vk_mem_alloc.h"
#include "gpu.h"
#include "ft2build.h"
#include FT_FREETYPE_H
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#define ANCHOR_TOP_LEFT 0
#define ANCHOR_TOP_RIGHT 1
#define ANCHOR_BOTTOM_LEFT 2
#define ANCHOR_BOTTOM_RIGHT 3
#define ANCHOR_CENTER 4
#define DRAWABLE_TYPE_RECT 0
#define DRAWABLE_TYPE_GLYPH 1
#define DRAWABLE_TYPE_IMAGE 2
#define DRAWABLE_TYPE_RECT_HSV 3
// Tombstone: excluded from the index list; the vertex shader also emits a
// degenerate quad for it since glyph scratch slots are tombstoned GPU-side
#define DRAWABLE_TYPE_NONE 0xFFFFFFFF
// GPUString.scratch value for strings with no glyph slot reservation
// (dead slots and max_length == 0); string.comp skips them
#define STRING_SCRATCH_NONE 0xFFFFFFFF
#define UI_EVENT_CURSOR (1 << 0)
#define UI_EVENT_SCROLL (1 << 1)
#define UI_EVENT_BUTTON (1 << 2)
#define CONTAINER_MIN_DRAWABLES 16
#define CONTAINER_MIN_STRINGS 4
#define CONTAINER_MIN_CODES 64
typedef struct UIPushConstantStruct {
VkDeviceAddress container;
float time;
uint32_t pad;
} UIPushConstant;
typedef struct GPUFontStruct {
VkDeviceAddress symbol_list;
uint32_t num_symbols;
uint32_t width;
uint32_t height;
} GPUFont;
typedef struct GPUSymbolStruct {
float top;
float left;
float width;
float height;
vec2 advance;
} GPUSymbol;
typedef struct FontStruct {
VmaAllocation symbol_memory;
VmaAllocation image_memory;
VkBuffer symbols;
VkImage image;
VkImageView view;
VkSampler sampler;
uint32_t* charmap;
uint32_t num_symbols;
uint32_t index;
char* family;
char* style;
} Font;
typedef struct TextureStruct {
VmaAllocation image_memory;
VkImage image;
VkImageView view;
VkSampler sampler;
char* path;
} Texture;
// Layout mirrors std430 in ui_common.glsl; cglm vec4 is 16-byte aligned so
// C offsets match. z and scratch fill what used to be tail padding.
typedef struct GPUStringStruct {
vec2 pos;
vec4 color;
float size;
uint32_t length;
uint32_t font;
uint32_t max_length;
float z;
uint32_t scratch;
VkDeviceAddress codes;
} GPUString;
typedef struct GPUDrawableStruct {
vec2 pos;
vec2 size;
vec4 color[4];
uint32_t type;
uint32_t var;
uint32_t events;
float z;
} GPUDrawable;
typedef struct GPUContainerStruct {
DrawCommand draw;
DispatchCommand dispatch_strings;
uint32_t anchor;
vec2 offset;
vec2 size;
VkDeviceAddress strings;
VkDeviceAddress drawables;
VkDeviceAddress indices;
VkDeviceAddress context;
} GPUContainer;
typedef struct StringResourcesStruct {
VkBuffer codes[MAX_FRAMES_IN_FLIGHT];
VmaAllocation codes_memory[MAX_FRAMES_IN_FLIGHT];
uint32_t* codes_buffer;
uint32_t max_codes;
} StringResources;
typedef struct UIContextStruct UIContext;
typedef struct ContainerStruct Container;
struct ContainerStruct {
VkBuffer container[MAX_FRAMES_IN_FLIGHT];
VkBuffer strings[MAX_FRAMES_IN_FLIGHT];
VkBuffer drawables[MAX_FRAMES_IN_FLIGHT];
VkBuffer indices[MAX_FRAMES_IN_FLIGHT];
VmaAllocation container_memory[MAX_FRAMES_IN_FLIGHT];
VmaAllocation strings_memory[MAX_FRAMES_IN_FLIGHT];
VmaAllocation drawables_memory[MAX_FRAMES_IN_FLIGHT];
VmaAllocation indices_memory[MAX_FRAMES_IN_FLIGHT];
VkDeviceAddress address[MAX_FRAMES_IN_FLIGHT];
GPUContainer data;
// CPU mirrors; drawable pool slots [cap_drawables, cap_drawables+cap_codes)
// are glyph scratch written by string.comp and have no CPU mirror entries
GPUDrawable* drawables_buffer;
GPUString* strings_buffer;
StringResources* string_resources;
uint32_t* indices_buffer;
uint32_t cap_drawables;
uint32_t cap_codes;
uint32_t cap_strings;
uint32_t num_drawables;
uint32_t num_strings;
uint32_t scratch_used;
uint8_t* drawable_live;
uint8_t* string_live;
// Generations invalidate stale Lua handles after slot reuse; refs keep each
// slot's handle userdata alive so event dispatch can pass the same object
uint32_t* drawable_generation;
uint32_t* string_generation;
int* drawable_ref;
int* string_ref;
uint32_t* free_drawables;
uint32_t free_drawable_count;
uint32_t* free_strings;
uint32_t free_string_count;
uint32_t id;
int script_env; // Lua registry ref for this container's environment table, 0 = no script
char* script_path;
};
typedef struct ContainerInputStruct {
uint32_t id;
uint32_t anchor;
vec2 offset;
vec2 size;
const char* script_path; // path to the .lua file declaring the container's elements
} ContainerInput;
typedef struct GPUUIContextStruct {
VkDeviceAddress font_infos;
vec2 screen;
vec2 extent;
vec2 scale;
} GPUUIContext;
struct UIContextStruct {
VkDeviceAddress address;
VkBuffer context;
VmaAllocation context_memory;
VkBuffer font_infos;
VmaAllocation font_infos_memory;
VkDescriptorSet textures;
VkDescriptorSet samplers;
VkDescriptorSet font_samplers;
VkDescriptorSet font_textures;
VkDescriptorSetLayout samplers_layout;
VkDescriptorSetLayout textures_layout;
VkDescriptorPool fonts_pool;
VkDescriptorPool textures_pool;
GraphicsPipeline pipeline;
ComputePipeline string_pipeline;
uint32_t max_fonts;
uint32_t max_textures;
Font* fonts;
Texture* texture_slots;
uint32_t max_containers;
Container* containers;
// Container slot indices in draw order (back to front). Loading appends,
// so new containers spawn on top.
uint32_t* container_order;
uint32_t container_order_count;
GPUUIContext data;
FT_Library freetype;
lua_State* lua;
Container* active_container;
uint32_t active_element;
double cursor[2];
};
VkResult create_ui_context(
uint32_t max_fonts,
uint32_t max_textures,
uint32_t max_containers,
RenderContext* gpu,
UIContext* context);
VkResult load_font(
uint32_t index,
const char* ttf_file,
uint32_t size,
VkBool32 antialias,
RenderContext* gpu,
UIContext* context);
VkResult load_texture(
const char* png_path,
RenderContext* gpu,
UIContext* context,
uint32_t* index);
VkResult load_container(
ContainerInput* container,
RenderContext* gpu,
UIContext* context);
VkResult unload_container(
uint32_t id,
RenderContext* gpu,
UIContext* context);
void ui_container_to_front(uint32_t id, UIContext* ui);
// Runtime element management. Slots are stable for the element's lifetime;
// destroyed slots are recycled by later creates.
VkResult ui_create_drawable(
Container* container,
const GPUDrawable* drawable,
RenderContext* gpu,
uint32_t* slot);
VkResult ui_destroy_drawable(
Container* container,
uint32_t slot,
RenderContext* gpu);
VkResult ui_create_string(
Container* container,
const GPUString* string,
RenderContext* gpu,
uint32_t* slot);
VkResult ui_destroy_string(
Container* container,
uint32_t slot,
RenderContext* gpu);
VkResult ui_set_drawable_z(
Container* container,
uint32_t slot,
float z,
RenderContext* gpu);
VkResult ui_set_string_z(
Container* container,
uint32_t slot,
float z,
RenderContext* gpu);
VkResult map_string(
const char* text,
uint32_t* buffer,
uint32_t font,
UIContext* context);
VkResult update_ui_context_resolution(
UIContext* ui,
RenderContext* gpu);
VkResult update_ui_string(
const char* string,
Container* container,
uint32_t string_index,
UIContext* ui,
RenderContext* gpu);
Container* context_container(uint32_t container_id, UIContext* ui);
bool ui_intersect(
double cursor[2],
RenderContext* gpu,
UIContext* ui,
uint32_t events,
uint32_t* container,
uint32_t* element,
vec2 position);
void set_active_element(uint32_t container, uint32_t element, UIContext* ui);
void clear_active_element(UIContext* ui, RenderContext* gpu);
void anchor_offset(RenderContext* gpu, Container* container, vec2 offset);
#endif