From a02eee7e98632bc1d604bb2ef69d504bd3d1b4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Wed, 9 Feb 2022 11:21:18 +0100 Subject: DRW: Add new draw_debug_print_lib.glsl This lib allows any shader to use `print()` like functions for logging and debugging shaders. Usage is described in the comment at the top of the file. --- source/blender/draw/CMakeLists.txt | 4 + source/blender/draw/intern/draw_debug.c | 62 +++- source/blender/draw/intern/draw_manager.h | 2 + source/blender/draw/intern/draw_manager_data.c | 25 ++ source/blender/draw/intern/draw_shader.c | 10 + source/blender/draw/intern/draw_shader.h | 1 + source/blender/draw/intern/draw_shader_shared.h | 6 + .../intern/shaders/common_debug_print_lib.glsl | 379 +++++++++++++++++++++ .../draw/intern/shaders/common_view_lib.glsl | 3 + .../blender/draw/intern/shaders/draw_debug_info.hh | 25 ++ .../shaders/draw_debug_print_display_frag.glsl | 133 ++++++++ .../shaders/draw_debug_print_display_vert.glsl | 27 ++ source/blender/gpu/CMakeLists.txt | 1 + source/blender/gpu/GPU_shader.h | 3 +- source/blender/gpu/intern/gpu_shader_dependency.cc | 190 +++++++++++ source/blender/gpu/intern/gpu_shader_interface.hh | 2 + 16 files changed, 871 insertions(+), 2 deletions(-) create mode 100644 source/blender/draw/intern/shaders/common_debug_print_lib.glsl create mode 100644 source/blender/draw/intern/shaders/draw_debug_info.hh create mode 100644 source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl create mode 100644 source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index def6ebb7773..4cba8e5320c 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -415,6 +415,7 @@ set(GLSL_SRC intern/shaders/common_attribute_lib.glsl intern/shaders/common_colormanagement_lib.glsl intern/shaders/common_debug_lib.glsl + intern/shaders/common_debug_print_lib.glsl intern/shaders/common_fullscreen_vert.glsl intern/shaders/common_fxaa_lib.glsl intern/shaders/common_globals_lib.glsl @@ -444,6 +445,9 @@ set(GLSL_SRC intern/shaders/common_subdiv_vbo_lnor_comp.glsl intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl + intern/shaders/draw_debug_print_display_vert.glsl + intern/shaders/draw_debug_print_display_frag.glsl + intern/draw_shader_shared.h engines/gpencil/shaders/gpencil_frag.glsl diff --git a/source/blender/draw/intern/draw_debug.c b/source/blender/draw/intern/draw_debug.c index dad71539c9f..bd3dcddee91 100644 --- a/source/blender/draw/intern/draw_debug.c +++ b/source/blender/draw/intern/draw_debug.c @@ -30,10 +30,14 @@ #include "BLI_link_utils.h" +#include "GPU_capabilities.h" +#include "GPU_compute.h" #include "GPU_immediate.h" +#include "GPU_uniform_buffer.h" #include "draw_debug.h" #include "draw_manager.h" +#include "draw_shader.h" /* --------- Register --------- */ @@ -149,8 +153,9 @@ void DRW_debug_sphere(const float center[3], const float radius, const float col /* --------- Indirect Rendering --------- */ -/* Keep in sync with shader. */ +/* Keep in sync with shaders. */ #define DEBUG_VERT_MAX 16 * 4096 +#define DRW_DEBUG_PRINT_MAX 4096 static GPUVertFormat *debug_buf_format(void) { @@ -185,6 +190,25 @@ GPUVertBuf *drw_debug_line_buffer_get() return buf->verts; } +GPUVertBuf *drw_debug_print_buffer_get() +{ + if (!DST.debug.print_buffer) { + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "char_data", GPU_COMP_U32, 1, GPU_FETCH_INT); + } + + GPUVertBuf *char_buf = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(char_buf, DRW_DEBUG_PRINT_MAX); + + uint *data = GPU_vertbuf_get_data(char_buf); + memset(data, 0, sizeof(*data) * DRW_DEBUG_PRINT_MAX); + + DST.debug.print_buffer = char_buf; + } + return DST.debug.print_buffer; +} + /* --------- Render --------- */ static void drw_debug_draw_lines(void) @@ -293,11 +317,47 @@ static void drw_debug_draw_buffers(void) } } +static void drw_debug_draw_print(void) +{ + if (!GPU_compute_shader_support() || DST.debug.print_buffer == NULL) { + return; + } + + { + /* Display the characters. */ + GPUUniformBuf *view_ubo = G_draw.view_ubo; + + GPUShader *sh = DRW_shader_debug_print_display_get(); + GPUBatch *batch = GPU_batch_create_ex( + GPU_PRIM_POINTS, DST.debug.print_buffer, NULL, GPU_BATCH_OWNS_VBO); + GPU_batch_set_shader(batch, sh); + + eGPUBlend blend = GPU_blend_get(); + + GPU_blend(GPU_BLEND_ALPHA_PREMULT); + GPU_program_point_size(true); + GPU_uniformbuf_bind(view_ubo, GPU_shader_get_builtin_block(sh, GPU_UNIFORM_BLOCK_DRW_VIEW)); + + /* Wait for all writting jobs */ + GPU_memory_barrier(GPU_BARRIER_VERTEX_ATTRIB_ARRAY); + + GPU_batch_draw(batch); + GPU_batch_discard(batch); + /* Freed, with the batch. */ + DST.debug.print_buffer = NULL; + + GPU_uniformbuf_unbind(G_draw.view_ubo); + GPU_program_point_size(false); + GPU_blend(blend); + } +} + void drw_debug_draw(void) { drw_debug_draw_lines(); drw_debug_draw_spheres(); drw_debug_draw_buffers(); + drw_debug_draw_print(); } void drw_debug_init(void) diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 1f3fb42fecc..7d97ec9a44b 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -657,6 +657,7 @@ typedef struct DRWManager { DRWDebugLine *lines; DRWDebugSphere *spheres; DRWDebugBuffer *line_buffers; + GPUVertBuf *print_buffer; } debug; } DRWManager; @@ -671,6 +672,7 @@ void *drw_viewport_engine_data_ensure(void *engine_type); void drw_state_set(DRWState state); GPUVertBuf *drw_debug_line_buffer_get(void); +GPUVertBuf *drw_debug_print_buffer_get(void); void drw_debug_draw(void); void drw_debug_init(void); diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 3afc516a817..b69ce70e046 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -22,6 +22,7 @@ #include "draw_manager.h" +#include "BKE_context.h" #include "BKE_curve.h" #include "BKE_duplilist.h" #include "BKE_global.h" @@ -31,6 +32,9 @@ #include "BKE_paint.h" #include "BKE_pbvh.h" +#include "WM_api.h" +#include "wm_window.h" + #include "DNA_curve_types.h" #include "DNA_gpencil_types.h" #include "DNA_mesh_types.h" @@ -54,6 +58,9 @@ #include "intern/gpu_codegen.h" +/* We add */ +//#define DISABLE_DEBUG_SHADER_PRINT_BARRIER + /* -------------------------------------------------------------------- */ /** \name Uniform Buffer Object (DRW_uniformbuffer) * \{ */ @@ -1473,6 +1480,17 @@ static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader) GPUVertBuf *vertbuf = drw_debug_line_buffer_get(); drw_shgroup_uniform_create_ex( shgroup, debugbuf_location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, vertbuf, 0, 0, 1); + + int debug_print_location = GPU_shader_get_builtin_ssbo(shader, GPU_BUFFER_BLOCK_DEBUG_PRINT); + if (debug_print_location != -1) { + GPUVertBuf *vertbuf = drw_debug_print_buffer_get(); + drw_shgroup_uniform_create_ex( + shgroup, debug_print_location, DRW_UNIFORM_VERTEX_BUFFER_AS_STORAGE, vertbuf, 0, 0, 1); + /* Add a barrier to allow multiple shader writting to the same buffer. */ +# ifndef DISABLE_DEBUG_SHADER_PRINT_BARRIER + /** IMPORTANT: This might change the application behavior. Comment if needed. */ + DRW_shgroup_barrier(shgroup, GPU_BARRIER_SHADER_STORAGE); +# endif } #endif @@ -1968,6 +1986,13 @@ DRWView *DRW_view_create(const float viewmat[4][4], copy_v4_fl4(view->storage.viewcamtexcofac, 1.0f, 1.0f, 0.0f, 0.0f); + if (DST.draw_ctx.evil_C && DST.draw_ctx.region) { + int region_origin[2] = {DST.draw_ctx.region->winrct.xmin, DST.draw_ctx.region->winrct.ymin}; + struct wmWindow *win = CTX_wm_window(DST.draw_ctx.evil_C); + wm_cursor_position_get(win, &view->storage.mouse_pixel[0], &view->storage.mouse_pixel[1]); + sub_v2_v2v2_int(view->storage.mouse_pixel, view->storage.mouse_pixel, region_origin); + } + DRW_view_update(view, viewmat, winmat, culling_viewmat, culling_winmat); return view; diff --git a/source/blender/draw/intern/draw_shader.c b/source/blender/draw/intern/draw_shader.c index 28ac76ccd7f..018e095529a 100644 --- a/source/blender/draw/intern/draw_shader.c +++ b/source/blender/draw/intern/draw_shader.c @@ -39,6 +39,7 @@ extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; static struct { struct GPUShader *hair_refine_sh[PART_REFINE_MAX_SHADER]; + struct GPUShader *debug_print_display_sh; } e_data = {{NULL}}; /* -------------------------------------------------------------------- */ @@ -107,6 +108,14 @@ GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, return e_data.hair_refine_sh[refinement]; } +GPUShader *DRW_shader_debug_print_display_get() +{ + if (e_data.debug_print_display_sh == NULL) { + e_data.debug_print_display_sh = GPU_shader_create_from_info_name("draw_debug_print_display"); + } + return e_data.debug_print_display_sh; +} + /** \} */ void DRW_shaders_free(void) @@ -114,4 +123,5 @@ void DRW_shaders_free(void) for (int i = 0; i < PART_REFINE_MAX_SHADER; i++) { DRW_SHADER_FREE_SAFE(e_data.hair_refine_sh[i]); } + DRW_SHADER_FREE_SAFE(e_data.debug_print_display_sh); } diff --git a/source/blender/draw/intern/draw_shader.h b/source/blender/draw/intern/draw_shader.h index f9fa452671b..17b4243b956 100644 --- a/source/blender/draw/intern/draw_shader.h +++ b/source/blender/draw/intern/draw_shader.h @@ -40,6 +40,7 @@ typedef enum eParticleRefineShaderType { /* draw_shader.c */ struct GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, eParticleRefineShaderType sh_type); +struct GPUShader *DRW_shader_debug_print_display_get(void); void DRW_shaders_free(void); #ifdef __cplusplus diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h index a73cb4ad5b6..c54d81a552a 100644 --- a/source/blender/draw/intern/draw_shader_shared.h +++ b/source/blender/draw/intern/draw_shader_shared.h @@ -32,6 +32,12 @@ struct ViewInfos { /** NOTE: vec3 arrays are paded to vec4. */ float4 frustum_corners[8]; float4 frustum_planes[6]; + + /** For debugging purpose */ + /* Mouse pixel. */ + int2 mouse_pixel; + + int2 _pad0; }; BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16) diff --git a/source/blender/draw/intern/shaders/common_debug_print_lib.glsl b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl new file mode 100644 index 00000000000..ade2d4cb2c4 --- /dev/null +++ b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl @@ -0,0 +1,379 @@ + +/** + * Debug print implementation for shaders. + * + * `print()`: + * Log variable or strings inside the viewport. + * Using a unique non string argument will print the variable name with it. + * Concatenate by using multiple arguments. i.e: `print("Looped ", n, " times.")`. + * `print_no_endl()`: + * Same as `print()` but does not finish the line. + * `print_value()`: + * Display only the value of a variable. Does not finish the line. + * `print_value_hex()`: + * Display only the hex representation of a variable. Does not finish the line. + * `print_value_binary()`: + * Display only the binary representation of a variable. Does not finish the line. + * + * IMPORTANT: As it is now, it is not yet thread safe. Only print from one thread. You can use the + * IS_DEBUG_MOUSE_FRAGMENT macro in fragment shader to filter using mouse position or + * IS_FIRST_INVOCATION in compute shaders. + * + * NOTE: Floating point representation might not be very precise (see print_value(float)). + * + * IMPORTANT: Multipler drawcalls can write to the buffer in sequence (if they are from different + * shgroups). However, we add barriers to support this case and it might change the application + * behavior. Uncomment DISABLE_DEBUG_SHADER_PRINT_BARRIER to remove the barriers if that happens. + * But then you are limited to a single invocation output. + */ + +#ifndef DRAW_DEBUG_PRINT_DATA +/* Comment that out if you want to debug a library included in multiply shader. + * Then use DRAW_DEBUG_PRINT_DATA to check if the print buffer is present. */ +// # error "Missing draw_debug_print additional create info on shader create info" +#else + +/* Vertex shader discards the first 3 vertex. */ +# define print_row drw_print_buf[0] +# define print_col drw_print_buf[1] +# define print_cursor drw_print_buf[2] + +# define DRW_DEBUG_PRINT_MAX 4096 + +# define PRINT_WORD_WRAP_COLUMN 80u + +void print_newline() +{ + atomicExchange(print_col, 0u); + atomicAdd(print_row, 1u); +} + +void print_string_start(uint len) +{ + /* Break before word. */ + if (print_col + len > PRINT_WORD_WRAP_COLUMN) { + print_newline(); + } +} + +void print_char4(uint data) +{ + /* Convert into char stream. */ + for (; data != 0u; data >>= 8u) { + uint char1 = data & 0xFFu; + /* Check for null terminator. */ + if (char1 == 0x00) { + break; + } + uint cursor = atomicAdd(print_cursor, 1u) + 3; + if (cursor < DRW_DEBUG_PRINT_MAX) { + /* For future usage. (i.e: Color) */ + uint flags = 0u; + uint col = atomicAdd(print_col, 1u); + uint print_header = (flags << 24u) | (print_row << 16u) | (col << 8u); + drw_print_buf[cursor] = print_header | char1; + /* Break word. */ + if (print_col > PRINT_WORD_WRAP_COLUMN) { + print_newline(); + } + } + } +} + +/** + * NOTE(fclem): Strange behavior emerge when trying to increment the digit counter inside the + * append function. It looks like the compiler does not see it is referenced as an index for char4 + * and thus do not capture the right reference. I do not know if this is undefined behavior. + * As a matter of precaution, we implement all the append function separately. + * This behavior was observed on both Mesa & amdgpu-pro. + */ +/* Using ascii char code. Expect char1 to be less or equal to 0xFF. Appends chars to the + * right. */ +void print_append_char(uint char1, inout uint char4) +{ + char4 = (char4 << 8u) | char1; +} + +void print_append_digit(uint digit, inout uint char4) +{ + char4 = (char4 << 8u) | ((digit > 9u) ? 0x41u - 10u : 0x30u) + digit; +} + +void print_append_space(inout uint char4) +{ + char4 = (char4 << 8u) | 0x20u; +} + +void print_value_binary(uint value) +{ + print_no_endl("0b"); + print_string_start(10u * 4u); + uint digits[10] = array(uint)(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u); + uint digit = 0u; + for (uint i = 0u; i < 32u; i++) { + print_append_digit(((value >> i) & 1u), digits[digit / 4u]); + digit++; + if ((i % 4u) == 3u) { + print_append_space(digits[digit / 4u]); + digit++; + } + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 9; j >= 0; j--) { + print_char4(digits[j]); + } +} + +void print_value_binary(int value) +{ + print_value_binary(uint(value)); +} + +void print_value_binary(float value) +{ + print_value_binary(floatBitsToUint(value)); +} + +void print_value_uint(uint value, const bool hex, bool is_negative, const bool is_unsigned) +{ + print_string_start(3u * 4u); + const uint blank_value = hex ? 0x30303030u : 0x20202020u; + const uint prefix = hex ? 0x78302020u : 0x20202020u; + uint digits[3] = array(uint)(blank_value, blank_value, prefix); + const uint base = hex ? 16u : 10u; + uint digit = 0u; + /* Add `u` suffix. */ + if (is_unsigned) { + print_append_char('u', digits[digit / 4u]); + digit++; + } + /* Number's digits. */ + for (; value != 0u || digit == uint(is_unsigned); value /= base) { + print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Add negative sign. */ + if (is_negative) { + print_append_char('-', digits[digit / 4u]); + digit++; + } + /* Need to pad to uint alignment because we are issuing chars in "reverse". */ + for (uint i = digit % 4u; i < 4u && i > 0u; i++) { + print_append_space(digits[digit / 4u]); + digit++; + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 2; j >= 0; j--) { + print_char4(digits[j]); + } +} + +void print_value_hex(uint value) +{ + print_value_uint(value, true, false, false); +} + +void print_value_hex(int value) +{ + print_value_uint(uint(value), true, false, false); +} + +void print_value_hex(float value) +{ + print_value_uint(floatBitsToUint(value), true, false, false); +} + +void print_value(uint value) +{ + print_value_uint(value, false, false, true); +} + +void print_value(int value) +{ + print_value_uint(uint(abs(value)), false, (value < 0), false); +} + +void print_value(bool value) +{ + if (value) { + print_no_endl("true "); + } + else { + print_no_endl("false"); + } +} + +/* NOTE(@fclem): This is homebrew and might not be 100% accurate (accuracy has not been tested and + * might dependent on compiler implementation). If unsure, use print_value_hex and transcribe the + * value manually with another tool. */ +void print_value(float val) +{ + /* We pad the string to match normal float values length. */ + if (isnan(val)) { + print_no_endl(" NaN"); + return; + } + if (isinf(val)) { + if (sign(val) < 0.0) { + print_no_endl(" -Inf"); + } + else { + print_no_endl(" Inf"); + } + return; + } + + /* Adjusted for significant digits (6) with sign (1), decimal separator (1) and exponent (4). */ + const float significant_digits = 6.0; + print_string_start(3u * 4u); + uint digits[3] = array(uint)(0x20202020u, 0x20202020u, 0x20202020u); + + float exponent = floor(log(abs(val)) / log(10.0)); + bool display_exponent = exponent >= (significant_digits) || + exponent <= (-significant_digits + 1.0); + + float int_significant_digits = min(exponent + 1.0, significant_digits); + float dec_significant_digits = max(0.0, significant_digits - int_significant_digits); + /* Power to get to the rounding point. */ + float rounding_power = dec_significant_digits; + + if (val == 0.0 || isinf(exponent)) { + display_exponent = false; + int_significant_digits = dec_significant_digits = 1.0; + } + /* Remap to keep significant numbers count. */ + if (display_exponent) { + int_significant_digits = 1.0; + dec_significant_digits = significant_digits - int_significant_digits; + rounding_power = -exponent + dec_significant_digits; + } + /* Round at the last significant digit. */ + val = round(val * pow(10.0, rounding_power)); + /* Get back to final exponent. */ + val *= pow(10.0, -dec_significant_digits); + + float int_part; + float dec_part = modf(val, int_part); + + dec_part *= pow(10.0, dec_significant_digits); + + const uint base = 10u; + uint digit = 0u; + /* Exponent */ + uint value = uint(abs(exponent)); + if (display_exponent) { + for (int i = 0; value != 0u || i == 0; i++, value /= base) { + print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Exponent sign. */ + uint sign_char = (exponent < 0.0) ? '-' : '+'; + print_append_char(sign_char, digits[digit / 4u]); + digit++; + /* Exponent `e` suffix. */ + print_append_char(0x65u, digits[digit / 4u]); + digit++; + } + /* Decimal part. */ + value = uint(abs(dec_part)); +# if 0 /* We don't do that because it makes unstable values really hard to read. */ + /* Trim trailing zeros. */ + while ((value % base) == 0u) { + value /= base; + if (value == 0u) { + break; + } + } +# endif + if (value != 0u) { + for (int i = 0; value != 0u || i == 0; i++, value /= base) { + print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Point separator. */ + print_append_char('.', digits[digit / 4u]); + digit++; + } + /* Integer part. */ + value = uint(abs(int_part)); + for (int i = 0; value != 0u || i == 0; i++, value /= base) { + print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Negative sign. */ + if (val < 0.0) { + print_append_char('-', digits[digit / 4u]); + digit++; + } + /* Need to pad to uint alignment because we are issuing chars in "reverse". */ + for (uint i = digit % 4u; i < 4u && i > 0u; i++) { + print_append_space(digits[digit / 4u]); + digit++; + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 2; j >= 0; j--) { + print_char4(digits[j]); + } +} + +void print_value(vec2 value) +{ + print_no_endl("vec2(", value[0], ", ", value[1], ")"); +} + +void print_value(vec3 value) +{ + print_no_endl("vec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void print_value(vec4 value) +{ + print_no_endl("vec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +void print_value(ivec2 value) +{ + print_no_endl("ivec2(", value[0], ", ", value[1], ")"); +} + +void print_value(ivec3 value) +{ + print_no_endl("ivec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void print_value(ivec4 value) +{ + print_no_endl("ivec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +void print_value(uvec2 value) +{ + print_no_endl("uvec2(", value[0], ", ", value[1], ")"); +} + +void print_value(uvec3 value) +{ + print_no_endl("uvec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void print_value(uvec4 value) +{ + print_no_endl("uvec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +void print_value(bvec2 value) +{ + print_no_endl("bvec2(", value[0], ", ", value[1], ")"); +} + +void print_value(bvec3 value) +{ + print_no_endl("bvec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void print_value(bvec4 value) +{ + print_no_endl("bvec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +#endif diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl index c1a65f6b261..3eb519d1851 100644 --- a/source/blender/draw/intern/shaders/common_view_lib.glsl +++ b/source/blender/draw/intern/shaders/common_view_lib.glsl @@ -37,6 +37,9 @@ layout(std140) uniform viewBlock # endif #endif +#define IS_DEBUG_MOUSE_FRAGMENT (ivec2(gl_FragCoord) == drw_view.mouse_pixel) +#define IS_FIRST_INVOCATION (gl_GlobalInvocationID == uvec3(0)) + #define ViewNear (ViewVecs[0].w) #define ViewFar (ViewVecs[1].w) diff --git a/source/blender/draw/intern/shaders/draw_debug_info.hh b/source/blender/draw/intern/shaders/draw_debug_info.hh new file mode 100644 index 00000000000..2d170840830 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_info.hh @@ -0,0 +1,25 @@ + +#include "gpu_shader_create_info.hh" + +/* -------------------------------------------------------------------- */ +/** \name Debug print + * + * Allows print() function to have logging support inside shaders. + * \{ */ + +GPU_SHADER_CREATE_INFO(draw_debug_print) + .define("DRAW_DEBUG_PRINT_DATA") + .storage_buf(15, Qualifier::READ_WRITE, "uint", "drw_print_buf[]", Frequency::PASS); + +GPU_SHADER_INTERFACE_INFO(draw_debug_print_display_iface, "").flat(Type::UINT, "char_index"); + +GPU_SHADER_CREATE_INFO(draw_debug_print_display) + .do_static_compilation(true) + .vertex_in(0, Type::UINT, "char_data") + .vertex_out(draw_debug_print_display_iface) + .fragment_out(0, Type::VEC4, "out_color") + .vertex_source("draw_debug_print_display_vert.glsl") + .fragment_source("draw_debug_print_display_frag.glsl") + .additional_info("draw_view"); + +/** \} */ diff --git a/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl b/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl new file mode 100644 index 00000000000..83179c80da6 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl @@ -0,0 +1,133 @@ + +/** + * Display characters using an ascii table. + **/ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +bool char_intersect(uvec2 bitmap_position) +{ + /* Using 8x8 = 64bits = uvec2. */ + uvec2 ascii_bitmap[96] = array(uvec2)(uvec2(0x00000000u, 0x00000000u), + uvec2(0x18001800u, 0x183c3c18u), + uvec2(0x00000000u, 0x36360000u), + uvec2(0x7f363600u, 0x36367f36u), + uvec2(0x301f0c00u, 0x0c3e031eu), + uvec2(0x0c666300u, 0x00633318u), + uvec2(0x3b336e00u, 0x1c361c6eu), + uvec2(0x00000000u, 0x06060300u), + uvec2(0x060c1800u, 0x180c0606u), + uvec2(0x180c0600u, 0x060c1818u), + uvec2(0x3c660000u, 0x00663cffu), + uvec2(0x0c0c0000u, 0x000c0c3fu), + uvec2(0x000c0c06u, 0x00000000u), + uvec2(0x00000000u, 0x0000003fu), + uvec2(0x000c0c00u, 0x00000000u), + uvec2(0x06030100u, 0x6030180cu), + uvec2(0x6f673e00u, 0x3e63737bu), + uvec2(0x0c0c3f00u, 0x0c0e0c0cu), + uvec2(0x06333f00u, 0x1e33301cu), + uvec2(0x30331e00u, 0x1e33301cu), + uvec2(0x7f307800u, 0x383c3633u), + uvec2(0x30331e00u, 0x3f031f30u), + uvec2(0x33331e00u, 0x1c06031fu), + uvec2(0x0c0c0c00u, 0x3f333018u), + uvec2(0x33331e00u, 0x1e33331eu), + uvec2(0x30180e00u, 0x1e33333eu), + uvec2(0x000c0c00u, 0x000c0c00u), + uvec2(0x000c0c06u, 0x000c0c00u), + uvec2(0x060c1800u, 0x180c0603u), + uvec2(0x003f0000u, 0x00003f00u), + uvec2(0x180c0600u, 0x060c1830u), + uvec2(0x0c000c00u, 0x1e333018u), + uvec2(0x7b031e00u, 0x3e637b7bu), + uvec2(0x3f333300u, 0x0c1e3333u), + uvec2(0x66663f00u, 0x3f66663eu), + uvec2(0x03663c00u, 0x3c660303u), + uvec2(0x66361f00u, 0x1f366666u), + uvec2(0x16467f00u, 0x7f46161eu), + uvec2(0x16060f00u, 0x7f46161eu), + uvec2(0x73667c00u, 0x3c660303u), + uvec2(0x33333300u, 0x3333333fu), + uvec2(0x0c0c1e00u, 0x1e0c0c0cu), + uvec2(0x33331e00u, 0x78303030u), + uvec2(0x36666700u, 0x6766361eu), + uvec2(0x46667f00u, 0x0f060606u), + uvec2(0x6b636300u, 0x63777f7fu), + uvec2(0x73636300u, 0x63676f7bu), + uvec2(0x63361c00u, 0x1c366363u), + uvec2(0x06060f00u, 0x3f66663eu), + uvec2(0x3b1e3800u, 0x1e333333u), + uvec2(0x36666700u, 0x3f66663eu), + uvec2(0x38331e00u, 0x1e33070eu), + uvec2(0x0c0c1e00u, 0x3f2d0c0cu), + uvec2(0x33333f00u, 0x33333333u), + uvec2(0x331e0c00u, 0x33333333u), + uvec2(0x7f776300u, 0x6363636bu), + uvec2(0x1c366300u, 0x6363361cu), + uvec2(0x0c0c1e00u, 0x3333331eu), + uvec2(0x4c667f00u, 0x7f633118u), + uvec2(0x06061e00u, 0x1e060606u), + uvec2(0x30604000u, 0x03060c18u), + uvec2(0x18181e00u, 0x1e181818u), + uvec2(0x00000000u, 0x081c3663u), + uvec2(0x000000ffu, 0x00000000u), + uvec2(0x00000000u, 0x0c0c1800u), + uvec2(0x3e336e00u, 0x00001e30u), + uvec2(0x66663b00u, 0x0706063eu), + uvec2(0x03331e00u, 0x00001e33u), + uvec2(0x33336e00u, 0x3830303eu), + uvec2(0x3f031e00u, 0x00001e33u), + uvec2(0x06060f00u, 0x1c36060fu), + uvec2(0x333e301fu, 0x00006e33u), + uvec2(0x66666700u, 0x0706366eu), + uvec2(0x0c0c1e00u, 0x0c000e0cu), + uvec2(0x3033331eu, 0x30003030u), + uvec2(0x1e366700u, 0x07066636u), + uvec2(0x0c0c1e00u, 0x0e0c0c0cu), + uvec2(0x7f6b6300u, 0x0000337fu), + uvec2(0x33333300u, 0x00001f33u), + uvec2(0x33331e00u, 0x00001e33u), + uvec2(0x663e060fu, 0x00003b66u), + uvec2(0x333e3078u, 0x00006e33u), + uvec2(0x66060f00u, 0x00003b6eu), + uvec2(0x1e301f00u, 0x00003e03u), + uvec2(0x0c2c1800u, 0x080c3e0cu), + uvec2(0x33336e00u, 0x00003333u), + uvec2(0x331e0c00u, 0x00003333u), + uvec2(0x7f7f3600u, 0x0000636bu), + uvec2(0x1c366300u, 0x00006336u), + uvec2(0x333e301fu, 0x00003333u), + uvec2(0x0c263f00u, 0x00003f19u), + uvec2(0x0c0c3800u, 0x380c0c07u), + uvec2(0x18181800u, 0x18181800u), + uvec2(0x0c0c0700u, 0x070c0c38u), + uvec2(0x00000000u, 0x6e3b0000u), + uvec2(0x00000000u, 0x00000000u)); + + if (!in_range_inclusive(bitmap_position, uvec2(0), uvec2(7))) { + return false; + } + uint char_bits = ascii_bitmap[char_index][bitmap_position.y >> 2u & 1u]; + char_bits = (char_bits >> ((bitmap_position.y & 3u) * 8u + bitmap_position.x)); + return (char_bits & 1u) != 0u; +} + +void main() +{ + uvec2 bitmap_position = uvec2(gl_PointCoord.xy * 8.0); + /* Point coord start from top left corner. But layout is from bottom to top. */ + bitmap_position.y = 7 - bitmap_position.y; + + if (char_intersect(bitmap_position)) { + out_color = vec4(1); + } + else if (char_intersect(bitmap_position + uvec2(0, 1))) { + /* Shadow */ + out_color = vec4(0, 0, 0, 1); + } + else { + /* Transparent Background for ease of read. */ + out_color = vec4(0, 0, 0, 0.2); + } +} \ No newline at end of file diff --git a/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl b/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl new file mode 100644 index 00000000000..61404bf7f7a --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl @@ -0,0 +1,27 @@ + +/** + * Display characters using an ascii table. Outputs one point per character. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +void main() +{ + char_index = (char_data & 0xFFu) - 0x20u; + + /* Discard invalid chars and the first 3 vertex containing header data. */ + if (char_index >= 96u || gl_VertexID < 3) { + gl_Position = vec4(-1); + gl_PointSize = 0.0; + return; + } + uint row = (char_data >> 16u) & 0xFFu; + uint col = (char_data >> 8u) & 0xFFu; + + float char_size = 16.0; + /* Change anchor point to the top left. */ + vec2 pos_on_screen = char_size * vec2(col, row) + char_size * 4; + gl_Position = vec4( + pos_on_screen * drw_view.viewport_size_inverse * vec2(2.0, -2.0) - vec2(1.0, -1.0), 0, 1); + gl_PointSize = char_size; +} \ No newline at end of file diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 13522f049e8..f1283ff6407 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -424,6 +424,7 @@ set(SHADER_CREATE_INFOS ../draw/engines/workbench/shaders/infos/workbench_transparent_resolve_info.hh ../draw/engines/workbench/shaders/infos/workbench_volume_info.hh ../draw/engines/image/shaders/infos/engine_image_info.hh + ../draw/intern/shaders/draw_debug_info.hh ../draw/intern/shaders/draw_fullscreen_info.hh ../draw/intern/shaders/draw_object_infos_info.hh ../draw/intern/shaders/draw_view_info.hh diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 8dacc1e09be..846bda07127 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -164,7 +164,8 @@ typedef enum { } GPUUniformBlockBuiltin; typedef enum { - GPU_BUFFER_BLOCK_DEBUG = 0, /* debugBuf */ + GPU_BUFFER_BLOCK_DEBUG = 0, /* debugBuf */ + GPU_BUFFER_BLOCK_DEBUG_PRINT, /* drw_print_buf */ GPU_NUM_BUFFER_BLOCKS, /* Special value, denotes number of builtin buffer blocks. */ } GPUBufferBlockBuiltin; diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index 267acb27072..61a732932eb 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -26,6 +26,7 @@ #include #include +#include #include "BLI_ghash.h" #include "BLI_map.hh" @@ -112,6 +113,14 @@ struct GPUSource { enum_preprocess(); } + if (source.find("'")) { + char_literals_preprocess(); + } + + if (source.find("print")) { + string_preprocess(); + } + if (is_from_material_library()) { material_functions_parse(g_functions); } @@ -445,6 +454,187 @@ struct GPUSource { } } + void char_literals_preprocess() + { + const StringRefNull input = source; + std::stringstream output; + int64_t cursor = -1; + int64_t last_pos = 0; + + while (true) { + cursor = find_token(input, '\'', cursor + 1); + if (cursor == -1) { + break; + } + /* Output anything between 2 print statement. */ + output << input.substr(last_pos, cursor - last_pos); + + /* Extract string. */ + int64_t char_start = cursor + 1; + int64_t char_end = find_token(input, '\'', char_start); + CHECK(char_end, input, cursor, "Malformed char literal. Missing ending `'`."); + + StringRef input_char = input.substr(char_start, char_end - char_start); + if (input_char.size() == 0) { + CHECK(-1, input, cursor, "Malformed char literal. Empty character constant"); + } + + uint8_t char_value = input_char[0]; + + if (input_char[0] == '\\') { + if (input_char[1] == 'n') { + char_value = '\n'; + } + else { + CHECK(-1, input, cursor, "Unsupported escaped character"); + } + } + else { + if (input_char.size() > 1) { + CHECK(-1, input, cursor, "Malformed char literal. Multi-character character constant"); + } + } + + char hex[8]; + SNPRINTF(hex, "0x%.2Xu", char_value); + output << hex; + + cursor = last_pos = char_end + 1; + } + /* If nothing has been changed, do not allocate processed_source. */ + if (last_pos == 0) { + return; + } + + if (last_pos != 0) { + output << input.substr(last_pos); + } + processed_source = output.str(); + source = processed_source.c_str(); + } + + /* Replace print(string) by equivalent print_char4() sequence. */ + void string_preprocess() + { + const StringRefNull input = source; + std::stringstream output; + int64_t cursor = -1; + int64_t last_pos = 0; + + while (true) { + cursor = find_keyword(input, "print", cursor + 1); + if (cursor == -1) { + break; + } + + bool do_endl = false; + StringRef func = input.substr(cursor); + if (func.startswith("print(")) { + do_endl = true; + } + else if (func.startswith("print_no_endl(")) { + do_endl = false; + } + else { + continue; + } + + /* Output anything between 2 print statement. */ + output << input.substr(last_pos, cursor - last_pos); + + /* Extract string. */ + int64_t str_start = input.find('(', cursor) + 1; + int64_t semicolon = find_token(input, ';', str_start + 1); + CHECK(semicolon, input, cursor, "Malformed print(). Missing `;` ."); + int64_t str_end = rfind_token(input, ')', semicolon); + if (str_end < str_start) { + CHECK(-1, input, cursor, "Malformed print(). Missing closing `)` ."); + } + + std::stringstream sub_output; + StringRef input_args = input.substr(str_start, str_end - str_start); + + auto print_string = [&](std::string str) -> int { + size_t len_before_pad = str.length(); + /* Pad string to uint size. */ + while (str.length() % 4 != 0) { + str += " "; + } + /* Keep everything in one line to not mess with the shader logs. */ + sub_output << "/* " << str << "*/"; + sub_output << "print_string_start(" << len_before_pad << ");"; + for (size_t i = 0; i < len_before_pad; i += 4) { + uint8_t chars[4] = {*(reinterpret_cast(str.c_str()) + i + 0), + *(reinterpret_cast(str.c_str()) + i + 1), + *(reinterpret_cast(str.c_str()) + i + 2), + *(reinterpret_cast(str.c_str()) + i + 3)}; + if (i + 4 > len_before_pad) { + chars[len_before_pad - i] = '\0'; + } + char uint_hex[12]; + SNPRINTF(uint_hex, "0x%.2X%.2X%.2X%.2Xu", chars[3], chars[2], chars[1], chars[0]); + sub_output << "print_char4(" << StringRefNull(uint_hex) << ");"; + } + return 0; + }; + + const bool print_as_variable = (input_args[0] != '"') && find_token(input_args, ',') == -1; + if (print_as_variable) { + /* Variable or expression debuging. */ + std::string arg = input_args; + /* Pad align most values. */ + while (arg.length() % 4 != 0) { + arg += " "; + } + print_string(arg); + print_string("= "); + sub_output << "print_value(" << input_args << ");"; + } + else { + const std::regex arg_regex( + /* String args. */ + "\"([^\\r\\n\\t\\f\\v\"]+)\"" + /* OR. */ + "|" + /* value args. */ + "[\\s]+([^\\r\\n\\t\\f\\v\"]+),"); + std::smatch args_match; + std::string func_args = input_args; + std::string::const_iterator args_search_start(func_args.cbegin()); + while (std::regex_search(args_search_start, func_args.cend(), args_match, arg_regex)) { + args_search_start = args_match.suffix().first; + std::string arg_string = args_match[1].str(); + std::string arg_val = args_match[2].str(); + + if (arg_string.empty()) { + sub_output << "print_value(" << arg_val << ");"; + } + else { + print_string(arg_string); + } + } + } + + if (do_endl) { + sub_output << "print_newline();"; + } + + output << sub_output.str(); + + cursor = last_pos = str_end + 1; + } + /* If nothing has been changed, do not allocate processed_source. */ + if (last_pos == 0) { + return; + } + + if (last_pos != 0) { + output << input.substr(last_pos); + } + processed_source = output.str(); + source = processed_source.c_str(); + } + #undef find_keyword #undef rfind_keyword #undef find_token diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh index f23c9ac03eb..9de41873229 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.hh +++ b/source/blender/gpu/intern/gpu_shader_interface.hh @@ -241,6 +241,8 @@ inline const char *ShaderInterface::builtin_buffer_block_name(GPUBufferBlockBuil switch (u) { case GPU_BUFFER_BLOCK_DEBUG: return "debugBuf"; + case GPU_BUFFER_BLOCK_DEBUG_PRINT: + return "drw_print_buf"; default: return NULL; } -- cgit v1.2.3