From 15dda0115c783805753119984decb4d9249b1f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Mon, 22 Jun 2020 19:57:53 +0200 Subject: UI: Widget: Replace geometry by fragment shader drawing This means all the antiailasing is done inside the fragment shader. We use a Signed Distance Field to draw the 2D rounded boxes. This ensure the best quality for AA. This reduce the averge Batch for widget to 16 verts instead of ~600 and reduce overshading a lot. Theme Emboss alpha and tria alpha needs to be changed after this refactor. The shadow drawing is left unchanged and still use geometry. Reviewed By: Severin Differential Revision: https://developer.blender.org/D7833 --- source/blender/editors/interface/interface_draw.c | 152 +++++----- .../blender/editors/interface/interface_intern.h | 5 +- .../blender/editors/interface/interface_widgets.c | 250 +++-------------- .../shaders/gpu_shader_2D_widget_base_frag.glsl | 81 +++++- .../shaders/gpu_shader_2D_widget_base_vert.glsl | 311 ++++++++------------- 5 files changed, 317 insertions(+), 482 deletions(-) (limited to 'source/blender') diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 499d2f7f1fa..1cb8565b38d 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -118,51 +118,53 @@ void UI_draw_roundbox_aa( bool filled, float minx, float miny, float maxx, float maxy, float rad, const float color[4]) { uiWidgetBaseParameters widget_params = { - .recti.xmin = minx, - .recti.ymin = miny, - .recti.xmax = maxx, - .recti.ymax = maxy, + .recti.xmin = minx + U.pixelsize, + .recti.ymin = miny + U.pixelsize, + .recti.xmax = maxx - U.pixelsize, + .recti.ymax = maxy - U.pixelsize, + .rect.xmin = minx, + .rect.ymin = miny, + .rect.xmax = maxx, + .rect.ymax = maxy, .radi = rad, + .rad = rad, .round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f, .round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f, .round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f, .round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f, - .color_inner1[0] = color[0], - .color_inner2[0] = color[0], - .color_inner1[1] = color[1], - .color_inner2[1] = color[1], - .color_inner1[2] = color[2], - .color_inner2[2] = color[2], - .color_inner1[3] = color[3], - .color_inner2[3] = color[3], + .color_inner1[0] = filled ? color[0] : 0.0f, + .color_inner1[1] = filled ? color[1] : 0.0f, + .color_inner1[2] = filled ? color[2] : 0.0f, + .color_inner1[3] = filled ? color[3] : 0.0f, + .color_inner2[0] = filled ? color[0] : 0.0f, + .color_inner2[1] = filled ? color[1] : 0.0f, + .color_inner2[2] = filled ? color[2] : 0.0f, + .color_inner2[3] = filled ? color[3] : 0.0f, + .color_outline[0] = color[0], + .color_outline[1] = color[1], + .color_outline[2] = color[2], + .color_outline[3] = color[3], .alpha_discard = 1.0f, }; - GPU_blend(true); - + /* XXX this is to emulate previous behavior of semitransparent fills but that's was a side effect + * of the previous AA method. Better fix the callers. */ if (filled) { - /* plain antialiased filled box */ - widget_params.color_inner1[3] *= 0.125f; - widget_params.color_inner2[3] *= 0.125f; - - /* WATCH: This is assuming the ModelViewProjectionMatrix is area pixel space. - * If it has been scaled, then it's no longer valid. */ - GPUBatch *batch = ui_batch_roundbox_get(filled, true); - GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); - GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params); - GPU_batch_draw(batch); + widget_params.color_inner1[3] *= 0.65f; + widget_params.color_inner2[3] *= 0.65f; + widget_params.color_outline[3] *= 0.65f; } - else { - /* plain antialiased unfilled box */ - GPU_line_smooth(true); - GPUBatch *batch = ui_batch_roundbox_get(filled, false); - GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); - GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params); - GPU_batch_draw(batch); + /* WATCH: This is assuming the ModelViewProjectionMatrix is area pixel space. + * If it has been scaled, then it's no longer valid. */ - GPU_line_smooth(false); - } + GPUBatch *batch = ui_batch_roundbox_widget_get(); + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); + GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params); + + GPU_blend(true); + + GPU_batch_draw(batch); GPU_blend(false); } @@ -251,32 +253,49 @@ void UI_draw_roundbox_4fv( immEnd(); immUnbindProgram(); #endif - uiWidgetBaseParameters widget_params = { - .recti.xmin = minx, - .recti.ymin = miny, - .recti.xmax = maxx, - .recti.ymax = maxy, + .recti.xmin = minx + U.pixelsize, + .recti.ymin = miny + U.pixelsize, + .recti.xmax = maxx - U.pixelsize, + .recti.ymax = maxy - U.pixelsize, + .rect.xmin = minx, + .rect.ymin = miny, + .rect.xmax = maxx, + .rect.ymax = maxy, .radi = rad, + .rad = rad, .round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f, .round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f, .round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f, .round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f, - .color_inner1[0] = col[0], - .color_inner2[0] = col[0], - .color_inner1[1] = col[1], - .color_inner2[1] = col[1], - .color_inner1[2] = col[2], - .color_inner2[2] = col[2], - .color_inner1[3] = col[3], - .color_inner2[3] = col[3], + .color_inner1[0] = filled ? col[0] : 0.0f, + .color_inner1[1] = filled ? col[1] : 0.0f, + .color_inner1[2] = filled ? col[2] : 0.0f, + .color_inner1[3] = filled ? col[3] : 0.0f, + .color_inner2[0] = filled ? col[0] : 0.0f, + .color_inner2[1] = filled ? col[1] : 0.0f, + .color_inner2[2] = filled ? col[2] : 0.0f, + .color_inner2[3] = filled ? col[3] : 0.0f, + .color_outline[0] = col[0], + .color_outline[1] = col[1], + .color_outline[2] = col[2], + .color_outline[3] = col[3], .alpha_discard = 1.0f, }; + /* Exactly the same as UI_draw_roundbox_aa but does not do the legacy transparency. */ + + /* WATCH: This is assuming the ModelViewProjectionMatrix is area pixel space. + * If it has been scaled, then it's no longer valid. */ - GPUBatch *batch = ui_batch_roundbox_get(filled, false); + GPUBatch *batch = ui_batch_roundbox_widget_get(); GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params); + + GPU_blend(true); + GPU_batch_draw(batch); + + GPU_blend(false); } #if 0 @@ -427,29 +446,38 @@ void UI_draw_roundbox_shade_x(bool filled, immEnd(); immUnbindProgram(); #endif - uiWidgetBaseParameters widget_params = { - .recti.xmin = minx, - .recti.ymin = miny, - .recti.xmax = maxx, - .recti.ymax = maxy, + .recti.xmin = minx + U.pixelsize, + .recti.ymin = miny + U.pixelsize, + .recti.xmax = maxx - U.pixelsize, + .recti.ymax = maxy - U.pixelsize, + .rect.xmin = minx, + .rect.ymin = miny, + .rect.xmax = maxx, + .rect.ymax = maxy, .radi = rad, + .rad = rad, .round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f, .round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f, .round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f, .round_corners[3] = (roundboxtype & UI_CNR_TOP_LEFT) ? 1.0f : 0.0f, - .color_inner1[0] = min_ff(1.0f, col[0] + shadetop), - .color_inner2[0] = max_ff(0.0f, col[0] + shadedown), - .color_inner1[1] = min_ff(1.0f, col[1] + shadetop), - .color_inner2[1] = max_ff(0.0f, col[1] + shadedown), - .color_inner1[2] = min_ff(1.0f, col[2] + shadetop), - .color_inner2[2] = max_ff(0.0f, col[2] + shadedown), - .color_inner1[3] = 1.0f, - .color_inner2[3] = 1.0f, + .color_inner1[0] = !filled ? 0.0f : min_ff(1.0f, col[0] + shadetop), + .color_inner1[1] = !filled ? 0.0f : min_ff(1.0f, col[1] + shadetop), + .color_inner1[2] = !filled ? 0.0f : min_ff(1.0f, col[2] + shadetop), + .color_inner1[3] = !filled ? 0.0f : 1.0f, + .color_inner2[0] = !filled ? 0.0f : max_ff(0.0f, col[0] + shadedown), + .color_inner2[1] = !filled ? 0.0f : max_ff(0.0f, col[1] + shadedown), + .color_inner2[2] = !filled ? 0.0f : max_ff(0.0f, col[2] + shadedown), + .color_inner2[3] = !filled ? 0.0f : 1.0f, + /* TODO: non-filled box don't have gradients. Just use middle color. */ + .color_outline[0] = clamp_f(col[0] + shadetop + shadedown, 0.0f, 1.0f), + .color_outline[1] = clamp_f(col[1] + shadetop + shadedown, 0.0f, 1.0f), + .color_outline[2] = clamp_f(col[2] + shadetop + shadedown, 0.0f, 1.0f), + .color_outline[3] = clamp_f(col[3] + shadetop + shadedown, 0.0f, 1.0f), .alpha_discard = 1.0f, }; - GPUBatch *batch = ui_batch_roundbox_get(filled, false); + GPUBatch *batch = ui_batch_roundbox_widget_get(); GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float *)&widget_params); GPU_batch_draw(batch); @@ -2678,7 +2706,6 @@ void ui_draw_dropshadow( GPU_batch_draw(batch); /* outline emphasis */ - GPU_line_smooth(true); float color[4] = {0.0f, 0.0f, 0.0f, 0.4f}; UI_draw_roundbox_4fv(false, rct->xmin - 0.5f, @@ -2687,7 +2714,6 @@ void ui_draw_dropshadow( rct->ymax + 0.5f, radius + 0.5f, color); - GPU_line_smooth(false); GPU_blend(false); } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 6cd990ec2b0..fac78808a9a 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -848,6 +848,8 @@ typedef struct uiWidgetBaseParameters { * The absolute value itself is the discard factor. * Initialize value to 1.0.f if you don't want discard */ float alpha_discard; + float tria_type; + float _pad[3]; } uiWidgetBaseParameters; enum { @@ -861,8 +863,7 @@ enum { ROUNDBOX_TRIA_MAX, /* don't use */ }; -struct GPUBatch *ui_batch_roundbox_get(bool filled, bool antialiased); -struct GPUBatch *ui_batch_roundbox_widget_get(int tria); +struct GPUBatch *ui_batch_roundbox_widget_get(void); struct GPUBatch *ui_batch_roundbox_shadow_get(void); void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 0498b312618..16a5a13885b 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -393,35 +393,14 @@ static const uint g_shape_preset_hold_action_face[2][3] = {{2, 0, 1}, {3, 5, 4}} * * \{ */ -/* offset in triavec[] in shader per type */ -static const int tria_ofs[ROUNDBOX_TRIA_MAX] = { - [ROUNDBOX_TRIA_NONE] = 0, - [ROUNDBOX_TRIA_ARROWS] = 0, - [ROUNDBOX_TRIA_SCROLL] = 12, - [ROUNDBOX_TRIA_MENU] = 28, - [ROUNDBOX_TRIA_CHECK] = 34, - [ROUNDBOX_TRIA_HOLD_ACTION_ARROW] = 40, -}; -static const int tria_vcount[ROUNDBOX_TRIA_MAX] = { - [ROUNDBOX_TRIA_NONE] = 0, - [ROUNDBOX_TRIA_ARROWS] = 6, - [ROUNDBOX_TRIA_SCROLL] = 16, - [ROUNDBOX_TRIA_MENU] = 6, - [ROUNDBOX_TRIA_CHECK] = 6, - [ROUNDBOX_TRIA_HOLD_ACTION_ARROW] = 3, -}; - static struct { - GPUBatch *roundbox_widget[ROUNDBOX_TRIA_MAX]; - - GPUBatch *roundbox_simple; - GPUBatch *roundbox_simple_aa; - GPUBatch *roundbox_simple_outline; + GPUBatch *roundbox_widget; GPUBatch *roundbox_shadow; + /* TODO remove */ GPUVertFormat format; uint vflag_id; -} g_ui_batch_cache = {{0}}; +} g_ui_batch_cache = {0}; static GPUVertFormat *vflag_format(void) { @@ -436,7 +415,7 @@ static GPUVertFormat *vflag_format(void) #define INNER 0 #define OUTLINE 1 #define EMBOSS 2 -#define NO_AA WIDGET_AA_JITTER +#define NO_AA 0 static void set_roundbox_vertex_data(GPUVertBufRaw *vflag_step, uint32_t d) { @@ -462,176 +441,30 @@ static uint32_t set_roundbox_vertex(GPUVertBufRaw *vflag_step, return *data; } -static uint32_t set_tria_vertex( - GPUVertBufRaw *vflag_step, int tria_type, int tria_v, int tria_id, int jit_v) -{ - uint32_t *data = GPU_vertbuf_raw_step(vflag_step); - if (ELEM(tria_type, ROUNDBOX_TRIA_ARROWS)) { - tria_v += tria_id * tria_vcount[ROUNDBOX_TRIA_ARROWS]; - } - *data = tria_ofs[tria_type] + tria_v; - *data |= jit_v << 6; - *data |= (tria_id == 0) ? (1 << 10) : 0; /* is first tria */ - *data |= 1 << 14; /* is tria vert */ - return *data; -} - -static void roundbox_batch_add_tria(GPUVertBufRaw *vflag_step, int tria, uint32_t last_data) +GPUBatch *ui_batch_roundbox_widget_get(void) { - const int tria_num = - ELEM(tria, ROUNDBOX_TRIA_CHECK, ROUNDBOX_TRIA_HOLD_ACTION_ARROW, ROUNDBOX_TRIA_MENU) ? 1 : 2; - /* for each tria */ - for (int t = 0; t < tria_num; t++) { - for (int j = 0; j < WIDGET_AA_JITTER; j++) { - /* restart */ - set_roundbox_vertex_data(vflag_step, last_data); - set_tria_vertex(vflag_step, tria, 0, t, j); - for (int v = 0; v < tria_vcount[tria]; v++) { - last_data = set_tria_vertex(vflag_step, tria, v, t, j); - } - } - } -} - -GPUBatch *ui_batch_roundbox_widget_get(int tria) -{ - if (g_ui_batch_cache.roundbox_widget[tria] == NULL) { - uint32_t last_data; - GPUVertBufRaw vflag_step; + if (g_ui_batch_cache.roundbox_widget == NULL) { GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format()); - int vcount = WIDGET_SIZE_MAX; /* inner */ - vcount += 2; /* restart */ - vcount += ((WIDGET_SIZE_MAX + 1) * 2) * WIDGET_AA_JITTER; /* outline (edges) */ - vcount += 2; /* restart */ - vcount += ((WIDGET_CURVE_RESOLU * 2) * 2) * WIDGET_AA_JITTER; /* emboss */ - if (tria) { - vcount += (tria_vcount[tria] + 2) * WIDGET_AA_JITTER; /* tria1 */ - if (!ELEM(tria, ROUNDBOX_TRIA_CHECK, ROUNDBOX_TRIA_HOLD_ACTION_ARROW, ROUNDBOX_TRIA_MENU)) { - vcount += (tria_vcount[tria] + 2) * WIDGET_AA_JITTER; /* tria2 */ - } - } - GPU_vertbuf_data_alloc(vbo, vcount); - GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step); - /* Inner */ - for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) { - for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU - 1; a2 >= 0; a1++, a2--) { - last_data = set_roundbox_vertex(&vflag_step, c1, a1, NO_AA, true, false, INNER); - last_data = set_roundbox_vertex(&vflag_step, c2, a2, NO_AA, true, false, INNER); - } - } - /* restart */ - set_roundbox_vertex_data(&vflag_step, last_data); - set_roundbox_vertex(&vflag_step, 0, 0, 0, true, false, OUTLINE); - /* Outlines */ - for (int j = 0; j < WIDGET_AA_JITTER; j++) { - for (int c = 0; c < 4; c++) { - for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) { - set_roundbox_vertex(&vflag_step, c, a, j, true, false, OUTLINE); - set_roundbox_vertex(&vflag_step, c, a, j, false, false, OUTLINE); - } - } - /* Close the loop. */ - set_roundbox_vertex(&vflag_step, 0, 0, j, true, false, OUTLINE); - last_data = set_roundbox_vertex(&vflag_step, 0, 0, j, false, false, OUTLINE); - } - /* restart */ - set_roundbox_vertex_data(&vflag_step, last_data); - set_roundbox_vertex(&vflag_step, 0, 0, 0, false, false, EMBOSS); - /* Emboss */ - /* go back and forth : avoid degenerate triangle (but beware of backface cull) */ - bool rev = false; - for (int j = 0; j < WIDGET_AA_JITTER; j++, rev = !rev) { - for (int c = (rev) ? 1 : 0; (rev) ? c >= 0 : c < 2; (rev) ? c-- : c++) { - int sta = (rev) ? WIDGET_CURVE_RESOLU - 1 : 0; - int end = WIDGET_CURVE_RESOLU; - for (int a = sta; (rev) ? a >= 0 : a < end; (rev) ? a-- : a++) { - set_roundbox_vertex(&vflag_step, c, a, j, false, false, EMBOSS); - last_data = set_roundbox_vertex(&vflag_step, c, a, j, false, true, EMBOSS); - } - } - } - if (tria) { - roundbox_batch_add_tria(&vflag_step, tria, last_data); - } - g_ui_batch_cache.roundbox_widget[tria] = GPU_batch_create_ex( - GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO); - gpu_batch_presets_register(g_ui_batch_cache.roundbox_widget[tria]); - } - return g_ui_batch_cache.roundbox_widget[tria]; -} -GPUBatch *ui_batch_roundbox_get(bool filled, bool antialiased) -{ - GPUBatch **batch = NULL; + GPU_vertbuf_data_alloc(vbo, 12); - if (filled) { - if (antialiased) { - batch = &g_ui_batch_cache.roundbox_simple_aa; - } - else { - batch = &g_ui_batch_cache.roundbox_simple; - } - } - else { - if (antialiased) { - BLI_assert(0); /* Use GL_LINE_SMOOTH instead!!: */ - } - else { - batch = &g_ui_batch_cache.roundbox_simple_outline; - } - } + GPUIndexBufBuilder ibuf; + GPU_indexbuf_init(&ibuf, GPU_PRIM_TRIS, 6, 12); + /* Widget */ + GPU_indexbuf_add_tri_verts(&ibuf, 0, 1, 2); + GPU_indexbuf_add_tri_verts(&ibuf, 2, 1, 3); + /* Trias */ + GPU_indexbuf_add_tri_verts(&ibuf, 4, 5, 6); + GPU_indexbuf_add_tri_verts(&ibuf, 6, 5, 7); - if (*batch == NULL) { - uint32_t last_data; - GPUVertBufRaw vflag_step; - GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format()); - int vcount = WIDGET_SIZE_MAX; - vcount += (filled) ? 2 : 0; - vcount *= (antialiased) ? WIDGET_AA_JITTER : 1; - GPU_vertbuf_data_alloc(vbo, vcount); - GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step); + GPU_indexbuf_add_tri_verts(&ibuf, 8, 9, 10); + GPU_indexbuf_add_tri_verts(&ibuf, 10, 9, 11); - if (filled) { - for (int j = 0; j < WIDGET_AA_JITTER; j++) { - if (!antialiased) { - j = NO_AA; - } - /* restart */ - set_roundbox_vertex(&vflag_step, 0, 0, j, true, false, INNER); - for (int c1 = 0, c2 = 3; c1 < 2; c1++, c2--) { - for (int a1 = 0, a2 = WIDGET_CURVE_RESOLU - 1; a2 >= 0; a1++, a2--) { - last_data = set_roundbox_vertex(&vflag_step, c1, a1, j, true, false, INNER); - last_data = set_roundbox_vertex(&vflag_step, c2, a2, j, true, false, INNER); - } - } - /* restart */ - set_roundbox_vertex_data(&vflag_step, last_data); - if (!antialiased) { - break; - } - } - *batch = GPU_batch_create_ex(GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO); - } - else { - for (int j = 0; j < WIDGET_AA_JITTER; j++) { - if (!antialiased) { - j = NO_AA; - } - for (int c = 0; c < 4; c++) { - for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) { - set_roundbox_vertex(&vflag_step, c, a, j, true, false, INNER); - } - } - if (!antialiased) { - break; - } - } - *batch = GPU_batch_create_ex(GPU_PRIM_LINE_LOOP, vbo, NULL, GPU_BATCH_OWNS_VBO); - } - - gpu_batch_presets_register(*batch); + g_ui_batch_cache.roundbox_widget = GPU_batch_create_ex( + GPU_PRIM_TRIS, vbo, GPU_indexbuf_build(&ibuf), GPU_BATCH_OWNS_INDEX | GPU_BATCH_OWNS_VBO); + gpu_batch_presets_register(g_ui_batch_cache.roundbox_widget); } - return *batch; + return g_ui_batch_cache.roundbox_widget; } GPUBatch *ui_batch_roundbox_shadow_get(void) @@ -1314,14 +1147,13 @@ static void widgetbase_set_uniform_colors_ubv(uiWidgetBase *wtb, /* keep in sync with shader */ #define MAX_WIDGET_BASE_BATCH 6 -#define MAX_WIDGET_PARAMETERS 11 +#define MAX_WIDGET_PARAMETERS 12 static struct { - GPUBatch *batch; /* Batch type */ uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]; int count; bool enabled; -} g_widget_base_batch = {0}; +} g_widget_base_batch = {{{{0}}}}; void UI_widgetbase_draw_cache_flush(void) { @@ -1332,7 +1164,7 @@ void UI_widgetbase_draw_cache_flush(void) return; } - GPUBatch *batch = g_widget_base_batch.batch; + GPUBatch *batch = ui_batch_roundbox_widget_get(); if (g_widget_base_batch.count == 1) { /* draw single */ GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); @@ -1376,31 +1208,15 @@ void UI_widgetbase_draw_cache_end(void) GPU_blend(false); } -static void draw_widgetbase_batch(GPUBatch *batch, uiWidgetBase *wtb) +static void draw_widgetbase_batch(uiWidgetBase *wtb) { + wtb->uniform_params.tria_type = wtb->tria1.type; wtb->uniform_params.tria1_size = wtb->tria1.size; wtb->uniform_params.tria2_size = wtb->tria2.size; copy_v2_v2(wtb->uniform_params.tria1_center, wtb->tria1.center); copy_v2_v2(wtb->uniform_params.tria2_center, wtb->tria2.center); if (g_widget_base_batch.enabled) { - if (g_widget_base_batch.batch == NULL) { - g_widget_base_batch.batch = ui_batch_roundbox_widget_get(ROUNDBOX_TRIA_ARROWS); - } - - /* draw multi */ - if (batch != g_ui_batch_cache.roundbox_widget[ROUNDBOX_TRIA_NONE] && - batch != g_widget_base_batch.batch) { - /* issue previous calls before changing batch type. */ - UI_widgetbase_draw_cache_flush(); - g_widget_base_batch.batch = batch; - } - - /* No need to change batch if tria is not visible. Just scale it to 0. */ - if (batch == g_ui_batch_cache.roundbox_widget[ROUNDBOX_TRIA_NONE]) { - wtb->uniform_params.tria1_size = wtb->uniform_params.tria2_size = 0; - } - g_widget_base_batch.params[g_widget_base_batch.count] = wtb->uniform_params; g_widget_base_batch.count++; @@ -1412,6 +1228,7 @@ static void draw_widgetbase_batch(GPUBatch *batch, uiWidgetBase *wtb) const float checker_params[3] = { UI_ALPHA_CHECKER_DARK / 255.0f, UI_ALPHA_CHECKER_LIGHT / 255.0f, 8.0f}; /* draw single */ + GPUBatch *batch = ui_batch_roundbox_widget_get(); GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); GPU_batch_uniform_4fv_array( batch, "parameters", MAX_WIDGET_PARAMETERS, (float *)&wtb->uniform_params); @@ -1434,8 +1251,6 @@ static void widgetbase_draw_ex(uiWidgetBase *wtb, show_alpha_checkers = false; } - GPU_blend(true); - /* backdrop non AA */ if (wtb->draw_inner) { if (wcol->shaded == 0) { @@ -1455,7 +1270,7 @@ static void widgetbase_draw_ex(uiWidgetBase *wtb, outline_col[0] = wcol->outline[0]; outline_col[1] = wcol->outline[1]; outline_col[2] = wcol->outline[2]; - outline_col[3] = wcol->outline[3] / WIDGET_AA_JITTER; + outline_col[3] = wcol->outline[3]; /* emboss bottom shadow */ if (wtb->draw_emboss) { @@ -1467,7 +1282,7 @@ static void widgetbase_draw_ex(uiWidgetBase *wtb, tria_col[0] = wcol->item[0]; tria_col[1] = wcol->item[1]; tria_col[2] = wcol->item[2]; - tria_col[3] = (uchar)((float)wcol->item[3] / WIDGET_AA_JITTER); + tria_col[3] = wcol->item[3]; } /* Draw everything in one drawcall */ @@ -1476,11 +1291,10 @@ static void widgetbase_draw_ex(uiWidgetBase *wtb, widgetbase_set_uniform_colors_ubv( wtb, inner_col1, inner_col2, outline_col, emboss_col, tria_col, show_alpha_checkers); - GPUBatch *roundbox_batch = ui_batch_roundbox_widget_get(wtb->tria1.type); - draw_widgetbase_batch(roundbox_batch, wtb); + GPU_blend(true); + draw_widgetbase_batch(wtb); + GPU_blend(false); } - - GPU_blend(false); } static void widgetbase_draw(uiWidgetBase *wtb, const uiWidgetColors *wcol) diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl index bdc87baf924..05fbae53e18 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl @@ -1,11 +1,62 @@ uniform vec3 checkerColorAndSize; -noperspective in vec4 finalColor; +noperspective in vec2 uvInterp; noperspective in float butCo; flat in float discardFac; +flat in float shadeTri; +flat in vec2 outRectSize; +flat in vec4 outRoundCorners; +noperspective in vec4 innerColor; +flat in vec4 borderColor; +flat in vec4 embossColor; +flat in float lineWidth; out vec4 fragColor; +vec3 compute_masks(vec2 uv) +{ + bool upper_half = uv.y > outRectSize.y * 0.5; + bool right_half = uv.x > outRectSize.x * 0.5; + float corner_rad; + + vec2 uv_sdf = uv; + if (right_half) { + uv_sdf.x = outRectSize.x - uv_sdf.x; + } + if (upper_half) { + uv_sdf.y = outRectSize.y - uv_sdf.y; + corner_rad = right_half ? outRoundCorners.z : outRoundCorners.w; + } + else { + corner_rad = right_half ? outRoundCorners.y : outRoundCorners.x; + } + + /* Signed distance field from the corner (in pixel). + * inner_sdf is sharp and outer_sdf is rounded. */ + uv_sdf -= corner_rad; + float inner_sdf = max(0.0, min(uv_sdf.x, uv_sdf.y)); + float outer_sdf = -length(min(uv_sdf, 0.0)); + float sdf = inner_sdf + outer_sdf + corner_rad; + + /* Fade emboss at the border. */ + float emboss_size = clamp((upper_half) ? 0.0 : (uv.x / corner_rad), 0.0, 1.0); + + /* Clamp line width to be at least 1px wide. This can happen if the projection matrix + * has been scaled (i.e: Node editor)... */ + float line_width = (lineWidth > 0.0) ? max(fwidth(uv.x), lineWidth) : 0.0; + + const float aa_radius = 0.5; + vec3 masks; + masks.x = smoothstep(-aa_radius, aa_radius, sdf); + masks.y = smoothstep(-aa_radius, aa_radius, sdf - line_width); + masks.z = smoothstep(-aa_radius, aa_radius, sdf + line_width * emboss_size); + + /* Compose masks together to avoid having too much alpha. */ + masks.zx = max(vec2(0.0), masks.zx - masks.xy); + + return masks; +} + vec4 do_checkerboard() { float size = checkerColorAndSize.z; @@ -25,16 +76,32 @@ void main() discard; } - fragColor = finalColor; - - if (butCo > 0.5) { - vec4 checker = do_checkerboard(); - fragColor = mix(checker, fragColor, fragColor.a); - } + vec3 masks = compute_masks(uvInterp); if (butCo > 0.0) { + /* Alpha checker widget. */ + if (butCo > 0.5) { + vec4 checker = do_checkerboard(); + fragColor = mix(checker, innerColor, innerColor.a); + } + else { + /* Set alpha to 1.0. */ + fragColor = innerColor; + } fragColor.a = 1.0; } + else { + /* Premultiply here. */ + fragColor = innerColor * vec4(innerColor.aaa, 1.0); + } + fragColor *= masks.y; + fragColor += masks.x * borderColor; + fragColor += masks.z * embossColor; + + /* Un-premult because the blend equation is already doing the mult. */ + if (fragColor.a > 0.0) { + fragColor.rgb /= fragColor.a; + } fragColor = blender_srgb_to_framebuffer_space(fragColor); } diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl index d7cc851556b..30d0cd05926 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl @@ -1,127 +1,7 @@ -#define BIT_RANGE(x) uint((1 << x) - 1) - -/* 2 bits for corner */ -/* Attention! Not the same order as in UI_interface.h! - * Ordered by drawing order. */ -#define BOTTOM_LEFT 0u -#define BOTTOM_RIGHT 1u -#define TOP_RIGHT 2u -#define TOP_LEFT 3u -#define CNR_FLAG_RANGE BIT_RANGE(2) - -/* 4bits for corner id */ -#define CORNER_VEC_OFS 2u -#define CORNER_VEC_RANGE BIT_RANGE(4) -const vec2 cornervec[9] = vec2[9](vec2(0.0, 1.0), - vec2(0.02, 0.805), - vec2(0.067, 0.617), - vec2(0.169, 0.45), - vec2(0.293, 0.293), - vec2(0.45, 0.169), - vec2(0.617, 0.076), - vec2(0.805, 0.02), - vec2(1.0, 0.0)); - -/* 4bits for jitter id */ -#define JIT_OFS 6u -#define JIT_RANGE BIT_RANGE(4) -const vec2 jit[9] = vec2[9](vec2(0.468813, -0.481430), - vec2(-0.155755, -0.352820), - vec2(0.219306, -0.238501), - vec2(-0.393286, -0.110949), - vec2(-0.024699, 0.013908), - vec2(0.343805, 0.147431), - vec2(-0.272855, 0.269918), - vec2(0.095909, 0.388710), - vec2(0.0, 0.0)); - -/* 2bits for other flags */ -#define INNER_FLAG uint(1 << 10) /* is inner vert */ -#define EMBOSS_FLAG uint(1 << 11) /* is emboss vert */ - -/* 2bits for color */ -#define COLOR_OFS 12u -#define COLOR_RANGE BIT_RANGE(2) -#define COLOR_INNER 0u -#define COLOR_EDGE 1u -#define COLOR_EMBOSS 2u - -/* 2bits for trias type */ -#define TRIA_FLAG uint(1 << 14) /* is tria vert */ -#define TRIA_FIRST INNER_FLAG /* is first tria (reuse INNER_FLAG) */ - -/* We can reuse the CORNER_* bits for tria */ -#define TRIA_VEC_RANGE BIT_RANGE(6) - -/* Some GPUs have performanse issues with this array being const (Doesn't fit in the registers?). - * To resolve this issue, store the array as a uniform buffer. - * (The array is still stored in the registry, but indexing is done in the uniform buffer.) */ -uniform vec2 triavec[43] = vec2[43]( - - /* ROUNDBOX_TRIA_ARROWS */ - vec2(-0.170000, 0.400000), - vec2(-0.050000, 0.520000), - vec2(0.250000, 0.000000), - vec2(0.470000, -0.000000), - vec2(-0.170000, -0.400000), - vec2(-0.050000, -0.520000), - vec2(0.170000, 0.400000), - vec2(0.050000, 0.520000), - vec2(-0.250000, 0.000000), - vec2(-0.470000, -0.000000), - vec2(0.170000, -0.400000), - vec2(0.050000, -0.520000), - - /* ROUNDBOX_TRIA_SCROLL - circle tria (triangle strip) */ - vec2(0.000000, 1.000000), - vec2(0.382684, 0.923879), - vec2(-0.382683, 0.923880), - vec2(0.707107, 0.707107), - vec2(-0.707107, 0.707107), - vec2(0.923879, 0.382684), - vec2(-0.923879, 0.382684), - vec2(1.000000, 0.000000), - vec2(-1.000000, 0.000000), - vec2(0.923879, -0.382684), - vec2(-0.923879, -0.382684), - vec2(0.707107, -0.707107), - vec2(-0.707107, -0.707107), - vec2(0.382684, -0.923879), - vec2(-0.382683, -0.923880), - vec2(0.000000, -1.000000), - - /* ROUNDBOX_TRIA_MENU - menu arrows */ - vec2(-0.51, 0.07), - vec2(-0.4, 0.18), - vec2(-0.05, -0.39), - vec2(-0.05, -0.17), - vec2(0.41, 0.07), - vec2(0.3, 0.18), - - /* ROUNDBOX_TRIA_CHECK - check mark */ - vec2(-0.67000, 0.020000), - vec2(-0.500000, 0.190000), - vec2(-0.130000, -0.520000), - vec2(-0.130000, -0.170000), - vec2(0.720000, 0.430000), - vec2(0.530000, 0.590000), - -/* ROUNDBOX_TRIA_HOLD_ACTION_ARROW - hold action arrows */ -#define OX (-0.32) -#define OY (0.1) -#define SC (0.35 * 2) - // vec2(-0.5 + SC, 1.0 + OY), vec2( 0.5, 1.0 + OY), vec2( 0.5, 0.0 + OY + SC), - vec2((0.5 - SC) + OX, 1.0 + OY), - vec2(-0.5 + OX, 1.0 + OY), - vec2(-0.5 + OX, SC + OY) -#undef OX -#undef OY -#undef SC -); uniform mat4 ModelViewProjectionMatrix; -#define MAX_PARAM 11 +#define MAX_PARAM 12 #ifdef USE_INSTANCE # define MAX_INSTANCE 6 uniform vec4 parameters[MAX_PARAM * MAX_INSTANCE]; @@ -147,105 +27,152 @@ uniform vec4 parameters[MAX_PARAM]; #define tria2Size parameters[gl_InstanceID * MAX_PARAM + 10].y #define shadeDir parameters[gl_InstanceID * MAX_PARAM + 10].z #define alphaDiscard parameters[gl_InstanceID * MAX_PARAM + 10].w +#define triaType parameters[gl_InstanceID * MAX_PARAM + 11].x /* We encode alpha check and discard factor together. */ #define doAlphaCheck (alphaDiscard < 0.0) #define discardFactor abs(alphaDiscard) -in uint vflag; - -noperspective out vec4 finalColor; +noperspective out vec2 uvInterp; +flat out vec2 outRectSize; +flat out vec4 outRoundCorners; +noperspective out vec4 innerColor; +flat out vec4 borderColor; +flat out vec4 embossColor; +flat out float lineWidth; noperspective out float butCo; flat out float discardFac; vec2 do_widget(void) { - uint cflag = vflag & CNR_FLAG_RANGE; - uint vofs = (vflag >> CORNER_VEC_OFS) & CORNER_VEC_RANGE; - bool is_inner = (vflag & INNER_FLAG) != 0u; - - vec2 v = cornervec[vofs]; - /* Scale by corner radius */ - v *= roundCorners[cflag] * ((is_inner) ? radsi : rads); - /* Flip in the right direction and osition to corner */ - vec4 rct = (is_inner) ? recti : rect; - if (cflag == BOTTOM_LEFT) { - v += rct.xz; - } - else if (cflag == BOTTOM_RIGHT) { - v = vec2(-v.y, v.x); - v += rct.yz; - } - else if (cflag == TOP_RIGHT) { - v = -v; - v += rct.yw; - } - else /* (cflag == TOP_LEFT) */ { - v = vec2(v.y, -v.x); - v += rct.xw; - } - - vec2 uv = faci * (v - recti.xz); - - /* compute uv and color gradient */ - uint color_id = (vflag >> COLOR_OFS) & COLOR_RANGE; - if (color_id == COLOR_INNER) { - float fac = clamp((shadeDir > 0.0) ? uv.y : uv.x, 0.0, 1.0); - - if (doAlphaCheck) { - finalColor = colorInner1; - butCo = uv.x; - } - else { - finalColor = mix(colorInner2, colorInner1, fac); - butCo = -abs(uv.x); - } - } - else if (color_id == COLOR_EDGE) { - finalColor = colorEdge; - butCo = -abs(uv.x); + lineWidth = abs(rect.x - recti.x); + vec2 emboss_ofs = vec2(0.0, -lineWidth); + vec2 v_pos[4] = vec2[4](rect.xz + emboss_ofs, rect.xw, rect.yz + emboss_ofs, rect.yw); + vec2 pos = v_pos[gl_VertexID]; + + uvInterp = pos - rect.xz; + outRectSize = rect.yw - rect.xz; + outRoundCorners = rads * roundCorners; + + vec2 uv = uvInterp / outRectSize; + float fac = clamp((shadeDir > 0.0) ? uv.y : uv.x, 0.0, 1.0); + /* Note innerColor is premultiplied inside the fragment shader. */ + if (doAlphaCheck) { + innerColor = colorInner1; + butCo = uv.x; } - else /* (color_id == COLOR_EMBOSS) */ { - finalColor = colorEmboss; + else { + innerColor = mix(colorInner2, colorInner1, fac); butCo = -abs(uv.x); } - bool is_emboss = (vflag & EMBOSS_FLAG) != 0u; - v.y -= (is_emboss) ? (recti.z - rect.z) : 0.0; + /* We need premultiplied color for transparency. */ + borderColor = colorEdge * vec4(colorEdge.aaa, 1.0); + embossColor = colorEmboss * vec4(colorEmboss.aaa, 1.0); - return v; + return pos; } vec2 do_tria() { - uint vofs = vflag & TRIA_VEC_RANGE; + int vidx = gl_VertexID % 4; + bool tria2 = gl_VertexID > 7; + + vec2 pos; + float size = (tria2) ? -tria2Size : tria1Size; + vec2 center = (tria2) ? tria2Center : tria1Center; + + vec2 arrow_pos[4] = vec2[4](vec2(0.0, 0.6), vec2(0.6, 0.0), vec2(-0.6, 0.0), vec2(0.0, -0.6)); + /* Rotated uv space by 45deg and mirrored. */ + vec2 arrow_uvs[4] = vec2[4](vec2(0.0, 0.85), vec2(0.85, 0.85), vec2(0.0, 0.0), vec2(0.0, 0.85)); + + vec2 point_pos[4] = vec2[4](vec2(-1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, -1.0), vec2(1.0, 1.0)); + vec2 point_uvs[4] = vec2[4](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 0.0), vec2(1.0, 1.0)); + + /* We reuse the SDF roundbox rendering of widget to render the tria shapes. + * This means we do clever tricks to position the rectangle the way we want using + * the 2 triangles uvs. */ + if (triaType == 0.0) { + /* ROUNDBOX_TRIA_NONE */ + outRectSize = uvInterp = pos = vec2(0); + outRoundCorners = vec4(0.01); + } + else if (triaType == 1.0) { + /* ROUNDBOX_TRIA_ARROWS */ + pos = arrow_pos[vidx]; + uvInterp = arrow_uvs[vidx]; + uvInterp -= vec2(0.05, 0.63); /* Translate */ + outRectSize = vec2(0.74, 0.17); + outRoundCorners = vec4(0.08); + } + else if (triaType == 2.0) { + /* ROUNDBOX_TRIA_SCROLL */ + pos = point_pos[vidx]; + uvInterp = point_uvs[vidx]; + outRectSize = vec2(1.0); + outRoundCorners = vec4(0.5); + } + else if (triaType == 3.0) { + /* ROUNDBOX_TRIA_MENU */ + pos = tria2 ? vec2(0.0) : arrow_pos[vidx]; /* Solo tria */ + pos = vec2(pos.y, -pos.x); /* Rotate */ + pos += vec2(-0.05, 0.0); /* Translate */ + size *= 0.8; /* Scale */ + uvInterp = arrow_uvs[vidx]; + uvInterp -= vec2(0.05, 0.63); /* Translate */ + outRectSize = vec2(0.74, 0.17); + outRoundCorners = vec4(0.01); + } + else if (triaType == 4.0) { + /* ROUNDBOX_TRIA_CHECK */ + /* A bit more hacky: We use the two trias joined together to render + * both sides of the checkmark with different length. */ + pos = arrow_pos[min(vidx, 2)]; /* Only keep 1 triangle. */ + pos.y = tria2 ? -pos.y : pos.y; /* Mirror along X */ + pos = pos.x * vec2(0.0872, -0.996) + pos.y * vec2(0.996, 0.0872); /* Rotate (85deg) */ + pos += vec2(-0.1, 0.2); /* Translate */ + center = tria1Center; + size = tria1Size * 1.7; /* Scale */ + uvInterp = arrow_uvs[vidx]; + uvInterp -= tria2 ? vec2(0.4, 0.65) : vec2(0.08, 0.65); /* Translate */ + outRectSize = vec2(0.74, 0.14); + outRoundCorners = vec4(0.01); + } + else { + /* ROUNDBOX_TRIA_HOLD_ACTION_ARROW */ + /* We use a single triangle to cut the round rect in half. The edge will not be Antialiased. */ + pos = tria2 ? vec2(0.0) : arrow_pos[min(vidx, 2)]; /* Only keep 1 triangle. */ + pos = pos.x * vec2(0.707, 0.707) + pos.y * vec2(-0.707, 0.707); /* Rotate (45deg) */ + pos += vec2(-1.7, 2.4); /* Translate (hardcoded, might want to remove) */ + size *= 0.4; /* Scale */ + uvInterp = arrow_uvs[vidx]; + uvInterp -= vec2(0.05, 0.05); /* Translate */ + outRectSize = vec2(0.75); + outRoundCorners = vec4(0.01); + } - vec2 v = triavec[vofs]; + uvInterp *= abs(size); + outRectSize *= abs(size); + outRoundCorners *= abs(size); - finalColor = colorTria; - butCo = -1.0; + pos = pos * size + center; - bool is_tria_first = (vflag & TRIA_FIRST) != 0u; + innerColor = colorTria * vec4(colorTria.aaa, 1.0); - if (is_tria_first) { - v = v * tria1Size + tria1Center; - } - else { - v = v * tria2Size + tria2Center; - } + lineWidth = 0.0; + borderColor = vec4(0.0); + embossColor = vec4(0.0); + + butCo = -1.0; - return v; + return pos; } void main() { discardFac = discardFactor; - bool is_tria = (vflag & TRIA_FLAG) != 0u; - - vec2 v = (is_tria) ? do_tria() : do_widget(); - - /* Antialiasing offset */ - v += jit[(vflag >> JIT_OFS) & JIT_RANGE]; + bool is_tria = (gl_VertexID > 3); + vec2 pos = (is_tria) ? do_tria() : do_widget(); - gl_Position = ModelViewProjectionMatrix * vec4(v, 0.0, 1.0); + gl_Position = ModelViewProjectionMatrix * vec4(pos, 0.0, 1.0); } -- cgit v1.2.3