Added screen-corner anchoring to the UI pipeline

main
noah metz 2024-10-22 17:48:37 -06:00
parent d448eb81d1
commit 4cf1ed725d
9 changed files with 111 additions and 26 deletions

@ -47,8 +47,8 @@ VkResult command_transition_image_layout(VkDevice device, VkCommandPool transfer
}
extern const uint32_t MAX_FRAMES_IN_FLIGHT;
extern const uint32_t WINDOW_WIDTH;
extern const uint32_t WINDOW_HEIGHT;
extern const uint32_t WINDOW_MIN_WIDTH;
extern const uint32_t WINDOW_MIN_HEIGHT;
typedef struct SwapchainDetailsStruct {
VkSurfaceCapabilitiesKHR capabilities;

@ -9,6 +9,12 @@
#include "ft2build.h"
#include FT_FREETYPE_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
typedef struct ComputePipelineStruct {
VkPipelineLayout layout;
VkPipeline pipeline;
@ -76,6 +82,7 @@ typedef struct UIStringStruct {
float size;
uint32_t offset;
uint32_t length;
uint32_t font;
uint32_t id;
} UIString;
@ -85,6 +92,7 @@ typedef struct UIDrawableStruct {
vec4 color;
uint32_t type;
uint32_t code;
uint32_t index;
uint32_t id;
} UIDrawable;
@ -97,7 +105,6 @@ typedef struct UILayerStruct {
DrawCommand draw;
DispatchCommand dispatch_strings;
uint32_t font_index;
uint32_t max_drawables;
uint32_t max_codes;
uint32_t max_strings;
@ -129,6 +136,7 @@ typedef struct UIlayerStorageStruct {
typedef struct UIContainerStruct {
vec2 offset;
vec2 size;
uint32_t anchor;
} UIContainer;
typedef struct UIContainerStorageStruct {
@ -148,6 +156,7 @@ typedef struct UIContainerStorageStruct {
typedef struct UIContextStruct {
VkDeviceAddress font_infos;
vec2 screen;
vec2 extent;
vec2 scale;
} UIContext;
@ -211,7 +220,6 @@ typedef struct UILayerInputStruct {
uint32_t num_strings;
uint32_t num_codes;
uint32_t num_drawables;
uint32_t font;
UIString* strings;
uint32_t* codes;

@ -13,7 +13,7 @@ layout(local_size_x = 1) in;
void main() {
uint gID = gl_GlobalInvocationID.x;
String string = pc.layer.strings.s[gID];
Font font = pc.context.fonts.f[pc.layer.font_index];
Font font = pc.context.fonts.f[string.font];
uint buffer_pos = atomicAdd(pc.layer.draw.instance_count, string.len);
float x = 0;
@ -25,6 +25,7 @@ void main() {
pc.layer.drawables.d[buffer_pos + i].size = vec2(string.size, string.size);
pc.layer.drawables.d[buffer_pos + i].color = string.color;
pc.layer.drawables.d[buffer_pos + i].code = pc.layer.codes.c[string.offset + i];
pc.layer.drawables.d[buffer_pos + i].index = string.font;
pc.layer.drawables.d[buffer_pos + i].type = 1;
}
}

@ -20,12 +20,13 @@ layout(location = 1) in vec2 fragUV;
layout(location = 2) flat in uint code;
layout(location = 3) flat in uint index;
layout(location = 4) flat in uint type;
layout(location = 5) flat in vec2 container_offset;
layout(location = 0) out vec4 outColor;
void main() {
vec2 pos = (gl_FragCoord.xy - vec2(0.5, 0.5))/pc.context.scale;
vec2 min = pc.layer.container.offset;
vec2 min = container_offset;
vec2 max = min + pc.layer.container.size;
if(pos.x < min.x || pos.y < min.y
|| pos.x > max.x || pos.y > max.y) {

@ -13,6 +13,7 @@ layout(location = 1) out vec2 fragUV;
layout(location = 2) out flat uint code;
layout(location = 3) out flat uint index;
layout(location = 4) out flat uint type;
layout(location = 5) out flat vec2 container_offset;
const vec2 square[6] = {
vec2(0.0, 0.0),
@ -27,9 +28,30 @@ void main() {
Drawable drawable = pc.layer.drawables.d[gl_InstanceIndex];
vec2 pos = square[gl_VertexIndex];
// Top Left
if(pc.layer.container.anchor == 0) {
container_offset = pc.layer.container.offset;
}
// Top Right
if(pc.layer.container.anchor == 1) {
container_offset = pc.layer.container.offset + vec2(pc.context.extent.x - pc.layer.container.size.x, 0);
}
// Bottom Left
if(pc.layer.container.anchor == 2) {
container_offset = pc.layer.container.offset + vec2(0, pc.context.extent.y - pc.layer.container.size.y);
}
// Bottom Right
if(pc.layer.container.anchor == 3) {
container_offset = pc.layer.container.offset + vec2(pc.context.extent.x - pc.layer.container.size.x, pc.context.extent.y - pc.layer.container.size.y);
}
if(drawable.type == 1){
// Glyph
Font font = pc.context.fonts.f[pc.layer.font_index];
Font font = pc.context.fonts.f[drawable.index];
Symbol symbol = font.symbols.s[drawable.code];
fragUV = pos * vec2(symbol.width, symbol.height);
@ -38,11 +60,11 @@ void main() {
fragUV = pos;
}
gl_Position = vec4((pos * drawable.size + drawable.pos + pc.layer.container.offset) * pc.context.screen * 2, 0.0, 1.0) - vec4(1.0, 1.0, 0.0, 0.0);
gl_Position = vec4((pos * drawable.size + drawable.pos + container_offset) * pc.context.screen * 2, 0.0, 1.0) - vec4(1.0, 1.0, 0.0, 0.0);
fragColor = drawable.color;
code = drawable.code;
type = drawable.type;
index = pc.layer.font_index;
index = drawable.index;
}

@ -44,6 +44,7 @@ struct String {
float size;
uint offset;
uint len;
uint font;
uint id;
};
@ -61,6 +62,7 @@ struct Drawable {
vec4 color;
uint type;
uint code;
uint index;
uint id;
};
@ -71,6 +73,7 @@ layout(std430, buffer_reference) readonly buffer DrawableList {
layout(std430, buffer_reference) readonly buffer Container {
vec2 offset;
vec2 size;
uint anchor;
};
layout(std430, buffer_reference) readonly buffer Layer {
@ -81,7 +84,6 @@ layout(std430, buffer_reference) readonly buffer Layer {
DrawCommand draw;
DispatchCommand dispatch;
uint font_index;
uint max_drawables;
uint max_codes;
uint max_strings;
@ -93,5 +95,6 @@ layout(std430, buffer_reference) readonly buffer Layer {
layout(std430, buffer_reference) buffer Context {
FontList fonts;
vec2 screen;
vec2 extent;
vec2 scale;
};

@ -6,8 +6,8 @@
#include "vulkan/vulkan_core.h"
const uint32_t MAX_FRAMES_IN_FLIGHT = 2;
const uint32_t WINDOW_WIDTH = 800;
const uint32_t WINDOW_HEIGHT = 600;
const uint32_t WINDOW_MIN_WIDTH = 800;
const uint32_t WINDOW_MIN_HEIGHT = 600;
const char * validation_layers[] = {
"VK_LAYER_KHRONOS_validation",
@ -56,9 +56,12 @@ GLFWwindow* init_window() {
glfwSetErrorCallback(glfw_error);
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
// TODO: recreate the framebuffer on resize
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "roleplay", 0, 0);
GLFWwindow* window = glfwCreateWindow(WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT, "roleplay", 0, 0);
glfwSetWindowSizeLimits(window, WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT, GLFW_DONT_CARE, GLFW_DONT_CARE);
return window;
}

@ -33,13 +33,44 @@ VkResult render_thread(GLFWwindow* window, RenderContext* render) {
VK_RESULT(load_font("test.ttf", 16, VK_TRUE, library, render, &ui, &font_index));
UIString context_strings[] = {
{
.pos = {0, 16},
.size = 16,
.color = {1.0, 1.0, 1.0, 1.0},
.offset = 0,
.length = strlen("Example"),
.font = font_index,
},
};
uint32_t context_codes[100];
map_string("Example Text", context_codes, 0, ui.fonts[0].charmap, ui.fonts[0].num_symbols);
UILayerInput context_layers[] = {
{
.num_strings = 1,
.strings = context_strings,
.num_codes = 100,
.codes = context_codes,
},
};
UIContainerInput context_info = {
.id = 0x01,
.anchor = ANCHOR_TOP_LEFT,
.size = {WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT},
.layer_count = sizeof(context_layers)/sizeof(UILayerInput),
.layers = context_layers,
};
vec2 map_size = {250, 200};
UIDrawable map_rects[] = {
{
.pos = {0.0, 0.0},
.size = {map_size[0], map_size[1]},
.color = {0.0, 0.5, 0.8, 1.0},
.color = {0.0, 0.5,0.8, 1.0},
.type = 0,
},
};
@ -52,14 +83,14 @@ VkResult render_thread(GLFWwindow* window, RenderContext* render) {
};
UIContainerInput map_info = {
.id = 0x01,
.offset = {WINDOW_WIDTH-map_size[0], 0.0},
.id = 0x02,
.anchor = ANCHOR_TOP_RIGHT,
.size = {map_size[0], map_size[1]},
.layer_count = sizeof(map_layers)/sizeof(UILayerInput),
.layers = map_layers,
};
vec2 inventory_size = {map_size[0], WINDOW_HEIGHT-map_size[1]};
vec2 inventory_size = {map_size[0], WINDOW_MIN_HEIGHT-map_size[1]};
UIDrawable inventory_rects[] = {
{
@ -78,14 +109,14 @@ VkResult render_thread(GLFWwindow* window, RenderContext* render) {
};
UIContainerInput inventory_info = {
.id = 0x02,
.offset = {WINDOW_WIDTH-inventory_size[0], WINDOW_HEIGHT-inventory_size[1]},
.id = 0x03,
.anchor = ANCHOR_BOTTOM_RIGHT,
.size = {inventory_size[0], inventory_size[1]},
.layer_count = sizeof(inventory_layers)/sizeof(UILayerInput),
.layers = inventory_layers,
};
vec2 chat_size = {WINDOW_WIDTH-inventory_size[0], 200};
vec2 chat_size = {WINDOW_MIN_WIDTH-inventory_size[0], 200};
UIDrawable chat_rects[] = {
{
@ -104,13 +135,14 @@ VkResult render_thread(GLFWwindow* window, RenderContext* render) {
};
UIContainerInput chat_info = {
.id = 0x02,
.offset = {0, WINDOW_HEIGHT-chat_size[1]},
.id = 0x04,
.anchor = ANCHOR_BOTTOM_LEFT,
.size = {chat_size[0], chat_size[1]},
.layer_count = sizeof(chat_layers)/sizeof(UILayerInput),
.layers = chat_layers,
};
VK_RESULT(create_container(&context_info, render, &ui));
VK_RESULT(create_container(&map_info, render, &ui));
VK_RESULT(create_container(&inventory_info, render, &ui));
VK_RESULT(create_container(&chat_info, render, &ui));
@ -142,9 +174,15 @@ int network_thread() {
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
(void)window;
(void)key;
(void)scancode;
(void)action;
(void)mods;
}
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) {
(void)mods;
switch(button) {
// Handle camera hover
case GLFW_MOUSE_BUTTON_MIDDLE:
@ -166,9 +204,15 @@ void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) {
(void)window;
(void)xoffset;
(void)yoffset;
}
void cursor_pos_callback(GLFWwindow* window, double xpos, double ypos) {
(void)window;
(void)xpos;
(void)ypos;
}
int main() {

@ -215,14 +215,15 @@ VkResult create_ui_pipeline(
.alphaToOneEnable = VK_FALSE,
};
// additive blending
// For blending, calculate the new color as new_color = (1-a)(old_color)+(a)(fragment_color)
// Don't store an alpha value in the color attachment, since it's not used
VkPipelineColorBlendAttachmentState color_blend_attachment = {
.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
.blendEnable = VK_TRUE,
.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE,
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
.colorBlendOp = VK_BLEND_OP_ADD,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
.alphaBlendOp = VK_BLEND_OP_ADD,
};
@ -302,6 +303,7 @@ VkResult create_container(
context->containers[index].data.offset[1] = container->offset[1];
context->containers[index].data.size[0] = container->size[0];
context->containers[index].data.size[1] = container->size[1];
context->containers[index].data.anchor = container->anchor;
memcpy(mapped, &context->containers[index].data, sizeof(UIContainer));
VkCommandBuffer command_buffer = command_begin_single(gpu->device, gpu->transfer_pool);
@ -374,7 +376,6 @@ VkResult create_layer(
container->layers[index].data.dispatch_strings.y = 1;
container->layers[index].data.dispatch_strings.z = 1;
container->layers[index].data.font_index = input->font;
container->layers[index].data.max_drawables = input->num_drawables + input->num_codes;
container->layers[index].data.max_strings = input->num_strings;
container->layers[index].data.num_drawables = input->num_drawables;
@ -1078,6 +1079,8 @@ VkResult create_ui_context(
memory->data.font_infos = buffer_address(gpu->device, memory->font_infos);
memory->data.screen[0] = gpu->window_scale[0] / gpu->swapchain_extent.width;
memory->data.screen[1] = gpu->window_scale[1] / gpu->swapchain_extent.height;
memory->data.extent[0] = gpu->swapchain_extent.width / gpu->window_scale[0];
memory->data.extent[1] = gpu->swapchain_extent.height / gpu->window_scale[1];
memory->data.scale[0] = gpu->window_scale[0];
memory->data.scale[1] = gpu->window_scale[1];
memcpy(mapped, &memory->data, sizeof(UIContext));