summaryrefslogtreecommitdiffstats
path: root/demos/ui.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--demos/ui.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/demos/ui.c b/demos/ui.c
new file mode 100644
index 0000000..6cdc7c6
--- /dev/null
+++ b/demos/ui.c
@@ -0,0 +1,221 @@
+// License: CC0 / Public Domain
+
+#define NK_IMPLEMENTATION
+#include "ui.h"
+
+#include <libplacebo/dispatch.h>
+#include <libplacebo/shaders/custom.h>
+
+struct ui_vertex {
+ float pos[2];
+ float coord[2];
+ uint8_t color[4];
+};
+
+#define NUM_VERTEX_ATTRIBS 3
+
+struct ui {
+ pl_gpu gpu;
+ pl_dispatch dp;
+ struct nk_context nk;
+ struct nk_font_atlas atlas;
+ struct nk_buffer cmds, verts, idx;
+ pl_tex font_tex;
+ struct pl_vertex_attrib attribs_pl[NUM_VERTEX_ATTRIBS];
+ struct nk_draw_vertex_layout_element attribs_nk[NUM_VERTEX_ATTRIBS+1];
+ struct nk_convert_config convert_cfg;
+};
+
+struct ui *ui_create(pl_gpu gpu)
+{
+ struct ui *ui = malloc(sizeof(struct ui));
+ if (!ui)
+ return NULL;
+
+ *ui = (struct ui) {
+ .gpu = gpu,
+ .dp = pl_dispatch_create(gpu->log, gpu),
+ .attribs_pl = {
+ {
+ .name = "pos",
+ .offset = offsetof(struct ui_vertex, pos),
+ .fmt = pl_find_vertex_fmt(gpu, PL_FMT_FLOAT, 2),
+ }, {
+ .name = "coord",
+ .offset = offsetof(struct ui_vertex, coord),
+ .fmt = pl_find_vertex_fmt(gpu, PL_FMT_FLOAT, 2),
+ }, {
+ .name = "vcolor",
+ .offset = offsetof(struct ui_vertex, color),
+ .fmt = pl_find_named_fmt(gpu, "rgba8"),
+ }
+ },
+ .attribs_nk = {
+ {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, offsetof(struct ui_vertex, pos)},
+ {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, offsetof(struct ui_vertex, coord)},
+ {NK_VERTEX_COLOR, NK_FORMAT_R8G8B8A8, offsetof(struct ui_vertex, color)},
+ {NK_VERTEX_LAYOUT_END}
+ },
+ .convert_cfg = {
+ .vertex_layout = ui->attribs_nk,
+ .vertex_size = sizeof(struct ui_vertex),
+ .vertex_alignment = NK_ALIGNOF(struct ui_vertex),
+ .shape_AA = NK_ANTI_ALIASING_ON,
+ .line_AA = NK_ANTI_ALIASING_ON,
+ .circle_segment_count = 22,
+ .curve_segment_count = 22,
+ .arc_segment_count = 22,
+ .global_alpha = 1.0f,
+ },
+ };
+
+ // Initialize font atlas using built-in font
+ nk_font_atlas_init_default(&ui->atlas);
+ nk_font_atlas_begin(&ui->atlas);
+ struct nk_font *font = nk_font_atlas_add_default(&ui->atlas, 20, NULL);
+ struct pl_tex_params tparams = {
+ .format = pl_find_named_fmt(gpu, "r8"),
+ .sampleable = true,
+ .initial_data = nk_font_atlas_bake(&ui->atlas, &tparams.w, &tparams.h,
+ NK_FONT_ATLAS_ALPHA8),
+ .debug_tag = PL_DEBUG_TAG,
+ };
+ ui->font_tex = pl_tex_create(gpu, &tparams);
+ nk_font_atlas_end(&ui->atlas, nk_handle_ptr((void *) ui->font_tex),
+ &ui->convert_cfg.tex_null);
+ nk_font_atlas_cleanup(&ui->atlas);
+
+ if (!ui->font_tex)
+ goto error;
+
+ // Initialize nuklear state
+ if (!nk_init_default(&ui->nk, &font->handle)) {
+ fprintf(stderr, "NK: failed initializing UI!\n");
+ goto error;
+ }
+
+ nk_buffer_init_default(&ui->cmds);
+ nk_buffer_init_default(&ui->verts);
+ nk_buffer_init_default(&ui->idx);
+
+ return ui;
+
+error:
+ ui_destroy(&ui);
+ return NULL;
+}
+
+void ui_destroy(struct ui **ptr)
+{
+ struct ui *ui = *ptr;
+ if (!ui)
+ return;
+
+ nk_buffer_free(&ui->cmds);
+ nk_buffer_free(&ui->verts);
+ nk_buffer_free(&ui->idx);
+ nk_free(&ui->nk);
+ nk_font_atlas_clear(&ui->atlas);
+ pl_tex_destroy(ui->gpu, &ui->font_tex);
+ pl_dispatch_destroy(&ui->dp);
+
+ free(ui);
+ *ptr = NULL;
+}
+
+void ui_update_input(struct ui *ui, const struct window *win)
+{
+ int x, y;
+ window_get_cursor(win, &x, &y);
+ nk_input_begin(&ui->nk);
+ nk_input_motion(&ui->nk, x, y);
+ nk_input_button(&ui->nk, NK_BUTTON_LEFT, x, y, window_get_button(win, BTN_LEFT));
+ nk_input_button(&ui->nk, NK_BUTTON_RIGHT, x, y, window_get_button(win, BTN_RIGHT));
+ nk_input_button(&ui->nk, NK_BUTTON_MIDDLE, x, y, window_get_button(win, BTN_MIDDLE));
+ struct nk_vec2 scroll;
+ window_get_scroll(win, &scroll.x, &scroll.y);
+ nk_input_scroll(&ui->nk, scroll);
+ nk_input_end(&ui->nk);
+}
+
+struct nk_context *ui_get_context(struct ui *ui)
+{
+ return &ui->nk;
+}
+
+bool ui_draw(struct ui *ui, const struct pl_swapchain_frame *frame)
+{
+ if (nk_convert(&ui->nk, &ui->cmds, &ui->verts, &ui->idx, &ui->convert_cfg) != NK_CONVERT_SUCCESS) {
+ fprintf(stderr, "NK: failed converting draw commands!\n");
+ return false;
+ }
+
+ const struct nk_draw_command *cmd = NULL;
+ const uint8_t *vertices = nk_buffer_memory(&ui->verts);
+ const nk_draw_index *indices = nk_buffer_memory(&ui->idx);
+ nk_draw_foreach(cmd, &ui->nk, &ui->cmds) {
+ if (!cmd->elem_count)
+ continue;
+
+ pl_shader sh = pl_dispatch_begin(ui->dp);
+ pl_shader_custom(sh, &(struct pl_custom_shader) {
+ .description = "nuklear UI",
+ .body = "color = textureLod(ui_tex, coord, 0.0).r * vcolor;",
+ .output = PL_SHADER_SIG_COLOR,
+ .num_descriptors = 1,
+ .descriptors = &(struct pl_shader_desc) {
+ .desc = {
+ .name = "ui_tex",
+ .type = PL_DESC_SAMPLED_TEX,
+ },
+ .binding = {
+ .object = cmd->texture.ptr,
+ .sample_mode = PL_TEX_SAMPLE_NEAREST,
+ },
+ },
+ });
+
+ struct pl_color_repr repr = frame->color_repr;
+ pl_shader_color_map_ex(sh, NULL, pl_color_map_args(
+ .src = pl_color_space_srgb,
+ .dst = frame->color_space,
+ ));
+ pl_shader_encode_color(sh, &repr);
+
+ bool ok = pl_dispatch_vertex(ui->dp, pl_dispatch_vertex_params(
+ .shader = &sh,
+ .target = frame->fbo,
+ .blend_params = &pl_alpha_overlay,
+ .scissors = {
+ .x0 = cmd->clip_rect.x,
+ .y0 = cmd->clip_rect.y,
+ .x1 = cmd->clip_rect.x + cmd->clip_rect.w,
+ .y1 = cmd->clip_rect.y + cmd->clip_rect.h,
+ },
+ .vertex_attribs = ui->attribs_pl,
+ .num_vertex_attribs = NUM_VERTEX_ATTRIBS,
+ .vertex_stride = sizeof(struct ui_vertex),
+ .vertex_position_idx = 0,
+ .vertex_coords = PL_COORDS_ABSOLUTE,
+ .vertex_flipped = frame->flipped,
+ .vertex_type = PL_PRIM_TRIANGLE_LIST,
+ .vertex_count = cmd->elem_count,
+ .vertex_data = vertices,
+ .index_data = indices,
+ .index_fmt = PL_INDEX_UINT32,
+ ));
+
+ if (!ok) {
+ fprintf(stderr, "placebo: failed rendering UI!\n");
+ return false;
+ }
+
+ indices += cmd->elem_count;
+ }
+
+ nk_clear(&ui->nk);
+ nk_buffer_clear(&ui->cmds);
+ nk_buffer_clear(&ui->verts);
+ nk_buffer_clear(&ui->idx);
+ return true;
+}