diff options
Diffstat (limited to 'source/blender/gpu')
24 files changed, 456 insertions, 202 deletions
diff --git a/source/blender/gpu/GPU_select.h b/source/blender/gpu/GPU_select.h index c02af763311..3076058c075 100644 --- a/source/blender/gpu/GPU_select.h +++ b/source/blender/gpu/GPU_select.h @@ -32,7 +32,7 @@ extern "C" { struct rcti; /** Flags for mode of operation. */ -enum { +typedef enum eGPUSelectMode { GPU_SELECT_ALL = 1, /* gpu_select_query */ GPU_SELECT_NEAREST_FIRST_PASS = 2, @@ -40,13 +40,32 @@ enum { /* gpu_select_pick */ GPU_SELECT_PICK_ALL = 4, GPU_SELECT_PICK_NEAREST = 5, -}; +} eGPUSelectMode; + +/** + * The result of calling #GPU_select_begin & #GPU_select_end. + */ +typedef struct GPUSelectResult { + /** The selection identifier matching the value passed in by #GPU_select_load_id. */ + unsigned int id; + /** + * The nearest depth. + * - Only supported by picking modes (#GPU_SELECT_PICK_ALL and #GPU_SELECT_PICK_NEAREST) + * since occlusion quires don't provide a convenient way of accessing the depth-buffer. + * - OpenGL's `GL_SELECT` supported both near and far depths, + * this has not been included as Blender doesn't need this however support could be added. + */ + unsigned int depth; +} GPUSelectResult; /** * Initialize and provide buffer for results. */ -void GPU_select_begin( - unsigned int *buffer, unsigned int bufsize, const struct rcti *input, char mode, int oldhits); +void GPU_select_begin(GPUSelectResult *buffer, + unsigned int buffer_len, + const struct rcti *input, + eGPUSelectMode mode, + int oldhits); /** * Loads a new selection id and ends previous query, if any. * In second pass of selection it also returns @@ -79,8 +98,8 @@ void GPU_select_cache_end(void); * * Note that comparing depth as uint is fine. */ -const uint *GPU_select_buffer_near(const uint *buffer, int hits); -uint GPU_select_buffer_remove_by_id(uint *buffer, int hits, uint select_id); +const GPUSelectResult *GPU_select_buffer_near(const GPUSelectResult *buffer, int hits); +uint GPU_select_buffer_remove_by_id(GPUSelectResult *buffer, int hits, uint select_id); /** * Part of the solution copied from `rect_subregion_stride_calc`. */ diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 972758febd4..05c992274eb 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -71,6 +71,8 @@ GPUShader *GPU_shader_create_ex(const char *vertcode, GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info); GPUShader *GPU_shader_create_from_info_name(const char *info_name); +const GPUShaderCreateInfo *GPU_shader_create_info_get(const char *info_name); + struct GPU_ShaderCreateFromArray_Params { const char **vert, **geom, **frag, **defs; }; diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index 6fae4a13918..7812c7a7257 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -272,7 +272,8 @@ void *GPU_texture_read(GPUTexture *tex, eGPUDataFormat data_format, int miplvl); * Fills the whole texture with the same data for all pixels. * \warning Only work for 2D texture for now. * \warning Only clears the mip 0 of the texture. - * \param data_format: data format of the pixel data. \note The format is float for unorm textures. + * \param data_format: data format of the pixel data. + * \note The format is float for unorm textures. * \param data: 1 pixel worth of data to fill the texture with. */ void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat data_format, const void *data); diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 568462ec2a6..e1c6901470a 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -711,10 +711,10 @@ static char *code_generate_vertex(GPUNodeGraph *graph, LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { const char *type_str = gpu_data_type_to_string(attr->gputype); const char *prefix = attr_prefix_get(attr->type); - /* XXX FIXME : see notes in mesh_render_data_create() */ - /* NOTE : Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ + /* XXX FIXME: see notes in mesh_render_data_create() */ + /* NOTE: Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ if (attr->type == CD_ORCO) { - /* OPTI : orco is computed from local positions, but only if no modifier is present. */ + /* OPTI: orco is computed from local positions, but only if no modifier is present. */ BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n"); } diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index 5b1eac2e82f..28f5b8b02b0 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -437,7 +437,7 @@ void GPU_framebuffer_blit(GPUFrameBuffer *gpufb_read, fb_read->blit_to(blit_buffers, read_slot, fb_write, write_slot, 0, 0); - /* FIXME(fclem) sRGB is not saved. */ + /* FIXME(@fclem): sRGB is not saved. */ prev_fb->bind(true); } diff --git a/source/blender/gpu/intern/gpu_select.c b/source/blender/gpu/intern/gpu_select.c index 958aab65b57..afef0e70ccc 100644 --- a/source/blender/gpu/intern/gpu_select.c +++ b/source/blender/gpu/intern/gpu_select.c @@ -43,22 +43,22 @@ * \{ */ /* Internal algorithm used */ -enum { +typedef enum eGPUSelectAlgo { /** glBegin/EndQuery(GL_SAMPLES_PASSED... ), `gpu_select_query.c` * Only sets 4th component (ID) correctly. */ ALGO_GL_QUERY = 1, /** Read depth buffer for every drawing pass and extract depths, `gpu_select_pick.c` * Only sets 4th component (ID) correctly. */ ALGO_GL_PICK = 2, -}; +} eGPUSelectAlgo; typedef struct GPUSelectState { /* To ignore selection id calls when not initialized */ bool select_is_active; /* mode of operation */ - char mode; + eGPUSelectMode mode; /* internal algorithm for selection */ - char algorithm; + eGPUSelectAlgo algorithm; /* allow GPU_select_begin/end without drawing */ bool use_cache; /** @@ -80,7 +80,11 @@ static GPUSelectState g_select_state = {0}; /** \name Public API * \{ */ -void GPU_select_begin(uint *buffer, uint bufsize, const rcti *input, char mode, int oldhits) +void GPU_select_begin(GPUSelectResult *buffer, + const uint buffer_len, + const rcti *input, + eGPUSelectMode mode, + int oldhits) { if (mode == GPU_SELECT_NEAREST_SECOND_PASS) { /* In the case hits was '-1', @@ -119,12 +123,12 @@ void GPU_select_begin(uint *buffer, uint bufsize, const rcti *input, char mode, switch (g_select_state.algorithm) { case ALGO_GL_QUERY: { - gpu_select_query_begin((uint(*)[4])buffer, bufsize / 4, input, mode, oldhits); + gpu_select_query_begin(buffer, buffer_len, input, mode, oldhits); break; } default: /* ALGO_GL_PICK */ { - gpu_select_pick_begin((uint(*)[4])buffer, bufsize / 4, input, mode); + gpu_select_pick_begin(buffer, buffer_len, input, mode); break; } } @@ -219,35 +223,35 @@ bool GPU_select_is_cached(void) /** \name Utilities * \{ */ -const uint *GPU_select_buffer_near(const uint *buffer, int hits) +const GPUSelectResult *GPU_select_buffer_near(const GPUSelectResult *buffer, int hits) { - const uint *buffer_near = NULL; + const GPUSelectResult *buffer_near = NULL; uint depth_min = (uint)-1; for (int i = 0; i < hits; i++) { - if (buffer[1] < depth_min) { - BLI_assert(buffer[3] != -1); - depth_min = buffer[1]; + if (buffer->depth < depth_min) { + BLI_assert(buffer->id != -1); + depth_min = buffer->depth; buffer_near = buffer; } - buffer += 4; + buffer++; } return buffer_near; } -uint GPU_select_buffer_remove_by_id(uint *buffer, int hits, uint select_id) +uint GPU_select_buffer_remove_by_id(GPUSelectResult *buffer, int hits, uint select_id) { - uint *buffer_src = buffer; - uint *buffer_dst = buffer; + GPUSelectResult *buffer_src = buffer; + GPUSelectResult *buffer_dst = buffer; int hits_final = 0; for (int i = 0; i < hits; i++) { - if (buffer_src[3] != select_id) { + if (buffer_src->id != select_id) { if (buffer_dst != buffer_src) { - memcpy(buffer_dst, buffer_src, sizeof(int[4])); + memcpy(buffer_dst, buffer_src, sizeof(GPUSelectResult)); } - buffer_dst += 4; + buffer_dst++; hits_final += 1; } - buffer_src += 4; + buffer_src++; } return hits_final; } diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c index ddd3dfc6879..a5c0b2b1a14 100644 --- a/source/blender/gpu/intern/gpu_select_pick.c +++ b/source/blender/gpu/intern/gpu_select_pick.c @@ -55,15 +55,19 @@ /** \name #SubRectStride * \{ */ -/* For looping over a sub-region of a rect, could be moved into 'rct.c'. */ +/** For looping over a sub-region of a #rcti, could be moved into 'rct.c'. */ typedef struct SubRectStride { - uint start; /* start here */ - uint span; /* read these */ - uint span_len; /* len times (read span 'len' times). */ - uint skip; /* skip those */ + /** Start here. */ + uint start; + /** Read these. */ + uint span; + /** `len` times (read span 'len' times). */ + uint span_len; + /** Skip those. */ + uint skip; } SubRectStride; -/* we may want to change back to float if uint isn't well supported */ +/** We may want to change back to float if `uint` isn't well supported. */ typedef uint depth_t; /** @@ -104,11 +108,11 @@ BLI_INLINE bool depth_is_filled(const depth_t *prev, const depth_t *curr) /* -------------------------------------------------------------------- */ /** \name #DepthBufCache * - * Result of reading #glReadPixels, + * Result of reading #GPU_framebuffer_read_depth, * use for both cache and non-cached storage. * \{ */ -/** Store result of #glReadPixels. */ +/** Store result of #GPU_framebuffer_read_depth. */ typedef struct DepthBufCache { struct DepthBufCache *next, *prev; uint id; @@ -174,7 +178,7 @@ static bool depth_buf_subrect_depth_any_filled(const DepthBufCache *rect_src, const DepthBufCache *rect_dst, const SubRectStride *sub_rect) { - /* same as above but different rect sizes */ + /* Same as above but different rectangle sizes. */ const depth_t *prev = rect_src->buf + sub_rect->start; const depth_t *curr = rect_dst->buf + sub_rect->start; for (uint i = 0; i < sub_rect->span_len; i++) { @@ -235,66 +239,68 @@ static int depth_cmp(const void *v1, const void *v2) /** \name Main Selection Begin/End/Load API * \{ */ -/* depth sorting */ +/** Depth sorting. */ typedef struct GPUPickState { - /* cache on initialization */ - uint (*buffer)[4]; + /** Cache on initialization. */ + GPUSelectResult *buffer; + uint buffer_len; + /** Mode of this operation. */ + eGPUSelectMode mode; - /* Buffer size (stores number of integers, for actual size multiply by sizeof integer). */ - uint bufsize; - /* mode of operation */ - char mode; - - /* OpenGL drawing, never use when (is_cached == true). */ + /** GPU drawing, never use when `is_cached == true`. */ struct { - /* The current depth, accumulated as we draw */ + /** The current depth, accumulated while drawing. */ DepthBufCache *rect_depth; - /* Scratch buffer, avoid allocs every time (when not caching) */ + /** Scratch buffer, avoid allocations every time (when not caching). */ DepthBufCache *rect_depth_test; - /* Pass to glReadPixels (x, y, w, h) */ + /** Pass to `GPU_framebuffer_read_depth(x, y, w, h)`. */ int clip_readpixels[4]; - /* Set after first draw */ + /** Set after first draw. */ bool is_init; uint prev_id; - } gl; + } gpu; - /* src: data stored in 'cache' and 'gl', - * dst: use when cached region is smaller (where src -> dst isn't 1:1) */ + /** + * `src`: data stored in 'cache' and 'gpu', + * `dst`: use when cached region is smaller (where `src` -> `dst` isn't 1:1). + */ struct { rcti clip_rect; uint rect_len; } src, dst; - /* Store cache between `GPU_select_cache_begin/end` */ + /** Store cache between `GPU_select_cache_begin/end` */ bool use_cache; bool is_cached; struct { - /* Cleanup used for iterating over both source and destination buffers: - * src.clip_rect -> dst.clip_rect */ + /** + * Cleanup used for iterating over both source and destination buffers: + * `src.clip_rect` -> `dst.clip_rect`. + */ SubRectStride sub_rect; - /* List of DepthBufCache, sized of 'src.clip_rect' */ + /** List of #DepthBufCache, sized of 'src.clip_rect'. */ ListBase bufs; } cache; - /* Picking methods. */ + /** Picking methods. */ union { - /* GPU_SELECT_PICK_ALL */ + /** #GPU_SELECT_PICK_ALL */ struct { DepthID *hits; uint hits_len; uint hits_len_alloc; } all; - /* GPU_SELECT_PICK_NEAREST */ + /** #GPU_SELECT_PICK_NEAREST */ struct { uint *rect_id; } nearest; }; - /* Previous state to restore after drawing. */ + /** Previous state to restore after drawing. */ int viewport[4]; int scissor[4]; eGPUWriteMask write_mask; @@ -303,37 +309,44 @@ typedef struct GPUPickState { static GPUPickState g_pick_state = {0}; -void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, char mode) +void gpu_select_pick_begin(GPUSelectResult *buffer, + const uint buffer_len, + const rcti *input, + eGPUSelectMode mode) { GPUPickState *ps = &g_pick_state; #ifdef DEBUG_PRINT - printf("%s: mode=%d, use_cache=%d, is_cache=%d\n", __func__, mode, ps->use_cache, ps->is_cached); + printf("%s: mode=%d, use_cache=%d, is_cache=%d\n", + __func__, + (int)mode, + ps->use_cache, + ps->is_cached); #endif GPU_debug_group_begin("Selection Pick"); - ps->bufsize = bufsize; ps->buffer = buffer; + ps->buffer_len = buffer_len; ps->mode = mode; const uint rect_len = (uint)(BLI_rcti_size_x(input) * BLI_rcti_size_y(input)); ps->dst.clip_rect = *input; ps->dst.rect_len = rect_len; - /* Restrict OpenGL operations for when we don't have cache */ + /* Avoids unnecessary GPU operations when cache is available and they are unnecessary. */ if (ps->is_cached == false) { ps->write_mask = GPU_write_mask_get(); ps->depth_test = GPU_depth_test_get(); GPU_scissor_get(ps->scissor); - /* disable writing to the framebuffer */ + /* Disable writing to the frame-buffer. */ GPU_color_mask(false, false, false, false); GPU_depth_mask(true); - /* Always use #GL_LEQUAL even though GPU_SELECT_PICK_ALL always clears the buffer. This is - * because individual objects themselves might have sections that overlap and we need these - * to have the correct distance information. */ + /* Always use #GPU_DEPTH_LESS_EQUAL even though #GPU_SELECT_PICK_ALL always clears the buffer. + * This is because individual objects themselves might have sections that overlap and we need + * these to have the correct distance information. */ GPU_depth_test(GPU_DEPTH_LESS_EQUAL); float viewport[4]; @@ -342,35 +355,35 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c ps->src.clip_rect = *input; ps->src.rect_len = rect_len; - ps->gl.clip_readpixels[0] = (int)viewport[0]; - ps->gl.clip_readpixels[1] = (int)viewport[1]; - ps->gl.clip_readpixels[2] = BLI_rcti_size_x(&ps->src.clip_rect); - ps->gl.clip_readpixels[3] = BLI_rcti_size_y(&ps->src.clip_rect); + ps->gpu.clip_readpixels[0] = (int)viewport[0]; + ps->gpu.clip_readpixels[1] = (int)viewport[1]; + ps->gpu.clip_readpixels[2] = BLI_rcti_size_x(&ps->src.clip_rect); + ps->gpu.clip_readpixels[3] = BLI_rcti_size_y(&ps->src.clip_rect); - GPU_viewport(UNPACK4(ps->gl.clip_readpixels)); + GPU_viewport(UNPACK4(ps->gpu.clip_readpixels)); /* It's possible we don't want to clear depth buffer, * so existing elements are masked by current z-buffer. */ GPU_clear_depth(1.0f); /* scratch buffer (read new values here) */ - ps->gl.rect_depth_test = depth_buf_malloc(rect_len); - ps->gl.rect_depth = depth_buf_malloc(rect_len); + ps->gpu.rect_depth_test = depth_buf_malloc(rect_len); + ps->gpu.rect_depth = depth_buf_malloc(rect_len); - /* set initial 'far' value */ + /* Set initial 'far' value. */ for (uint i = 0; i < rect_len; i++) { - ps->gl.rect_depth->buf[i] = DEPTH_MAX; + ps->gpu.rect_depth->buf[i] = DEPTH_MAX; } - ps->gl.is_init = false; - ps->gl.prev_id = 0; + ps->gpu.is_init = false; + ps->gpu.prev_id = 0; } else { - /* Using cache (ps->is_cached == true) */ - /* src.clip_rect -> dst.clip_rect */ + /* Using cache `ps->is_cached == true`. */ + /* `src.clip_rect` -> `dst.clip_rect`. */ rect_subregion_stride_calc(&ps->src.clip_rect, &ps->dst.clip_rect, &ps->cache.sub_rect); - BLI_assert(ps->gl.rect_depth == NULL); - BLI_assert(ps->gl.rect_depth_test == NULL); + BLI_assert(ps->gpu.rect_depth == NULL); + BLI_assert(ps->gpu.rect_depth_test == NULL); } if (mode == GPU_SELECT_PICK_ALL) { @@ -379,7 +392,7 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c ps->all.hits_len_alloc = ALLOC_DEPTHS; } else { - /* Set to 0xff for SELECT_ID_NONE */ + /* Set to 0xff for #SELECT_ID_NONE. */ ps->nearest.rect_id = MEM_mallocN(sizeof(uint) * ps->dst.rect_len, __func__); memset(ps->nearest.rect_id, 0xff, sizeof(uint) * ps->dst.rect_len); } @@ -411,7 +424,7 @@ static void gpu_select_load_id_pass_all(const DepthBufCache *rect_curr) } } else { - /* same as above but different rect sizes */ + /* Same as above but different rectangle sizes. */ const depth_t *curr = rect_curr->buf + ps->cache.sub_rect.start; for (uint i = 0; i < ps->cache.sub_rect.span_len; i++) { const depth_t *curr_end = curr + ps->cache.sub_rect.span; @@ -424,7 +437,7 @@ static void gpu_select_load_id_pass_all(const DepthBufCache *rect_curr) #undef EVAL_TEST - /* ensure enough space */ + /* Ensure enough space. */ if (UNLIKELY(ps->all.hits_len == ps->all.hits_len_alloc)) { ps->all.hits_len_alloc += ALLOC_DEPTHS; ps->all.hits = MEM_reallocN(ps->all.hits, ps->all.hits_len_alloc * sizeof(*ps->all.hits)); @@ -439,7 +452,7 @@ static void gpu_select_load_id_pass_nearest(const DepthBufCache *rect_prev, { GPUPickState *ps = &g_pick_state; const uint id = rect_curr->id; - /* keep track each pixels ID in 'nearest.rect_id' */ + /* Keep track each pixels ID in `nearest.rect_id`. */ if (id != SELECT_ID_NONE) { uint *id_ptr = ps->nearest.rect_id; @@ -483,8 +496,8 @@ bool gpu_select_pick_load_id(uint id, bool end) { GPUPickState *ps = &g_pick_state; - if (ps->gl.is_init) { - if (id == ps->gl.prev_id && !end) { + if (ps->gpu.is_init) { + if (id == ps->gpu.prev_id && !end) { /* No need to read if we are still drawing for the same id since * all these depths will be merged / de-duplicated in the end. */ return true; @@ -493,21 +506,21 @@ bool gpu_select_pick_load_id(uint id, bool end) const uint rect_len = ps->src.rect_len; GPUFrameBuffer *fb = GPU_framebuffer_active_get(); GPU_framebuffer_read_depth( - fb, UNPACK4(ps->gl.clip_readpixels), GPU_DATA_UINT, ps->gl.rect_depth_test->buf); + fb, UNPACK4(ps->gpu.clip_readpixels), GPU_DATA_UINT, ps->gpu.rect_depth_test->buf); /* Perform initial check since most cases the array remains unchanged. */ bool do_pass = false; if (g_pick_state.mode == GPU_SELECT_PICK_ALL) { - if (depth_buf_rect_depth_any(ps->gl.rect_depth_test, rect_len)) { - ps->gl.rect_depth_test->id = ps->gl.prev_id; - gpu_select_load_id_pass_all(ps->gl.rect_depth_test); + if (depth_buf_rect_depth_any(ps->gpu.rect_depth_test, rect_len)) { + ps->gpu.rect_depth_test->id = ps->gpu.prev_id; + gpu_select_load_id_pass_all(ps->gpu.rect_depth_test); do_pass = true; } } else { - if (depth_buf_rect_depth_any_filled(ps->gl.rect_depth, ps->gl.rect_depth_test, rect_len)) { - ps->gl.rect_depth_test->id = ps->gl.prev_id; - gpu_select_load_id_pass_nearest(ps->gl.rect_depth, ps->gl.rect_depth_test); + if (depth_buf_rect_depth_any_filled(ps->gpu.rect_depth, ps->gpu.rect_depth_test, rect_len)) { + ps->gpu.rect_depth_test->id = ps->gpu.prev_id; + gpu_select_load_id_pass_nearest(ps->gpu.rect_depth, ps->gpu.rect_depth_test); do_pass = true; } } @@ -515,11 +528,11 @@ bool gpu_select_pick_load_id(uint id, bool end) if (do_pass) { /* Store depth in cache */ if (ps->use_cache) { - BLI_addtail(&ps->cache.bufs, ps->gl.rect_depth); - ps->gl.rect_depth = depth_buf_malloc(ps->src.rect_len); + BLI_addtail(&ps->cache.bufs, ps->gpu.rect_depth); + ps->gpu.rect_depth = depth_buf_malloc(ps->src.rect_len); } - SWAP(DepthBufCache *, ps->gl.rect_depth, ps->gl.rect_depth_test); + SWAP(DepthBufCache *, ps->gpu.rect_depth, ps->gpu.rect_depth_test); if (g_pick_state.mode == GPU_SELECT_PICK_ALL) { /* (fclem) This is to be on the safe side. I don't know if this is required. */ @@ -533,8 +546,8 @@ bool gpu_select_pick_load_id(uint id, bool end) } } - ps->gl.is_init = true; - ps->gl.prev_id = id; + ps->gpu.is_init = true; + ps->gpu.prev_id = id; return true; } @@ -548,9 +561,9 @@ uint gpu_select_pick_end(void) #endif if (ps->is_cached == false) { - if (ps->gl.is_init) { + if (ps->gpu.is_init) { /* force finishing last pass */ - gpu_select_pick_load_id(ps->gl.prev_id, true); + gpu_select_pick_load_id(ps->gpu.prev_id, true); } GPU_write_mask(ps->write_mask); GPU_depth_test(ps->depth_test); @@ -559,39 +572,39 @@ uint gpu_select_pick_end(void) GPU_debug_group_end(); - /* assign but never free directly since it may be in cache */ + /* Assign but never free directly since it may be in cache. */ DepthBufCache *rect_depth_final; /* Store depth in cache */ if (ps->use_cache && !ps->is_cached) { - BLI_addtail(&ps->cache.bufs, ps->gl.rect_depth); - ps->gl.rect_depth = NULL; + BLI_addtail(&ps->cache.bufs, ps->gpu.rect_depth); + ps->gpu.rect_depth = NULL; rect_depth_final = ps->cache.bufs.last; } else if (ps->is_cached) { rect_depth_final = ps->cache.bufs.last; } else { - /* common case, no cache */ - rect_depth_final = ps->gl.rect_depth; + /* Common case, no cache. */ + rect_depth_final = ps->gpu.rect_depth; } - uint maxhits = g_pick_state.bufsize; + uint maxhits = g_pick_state.buffer_len; DepthID *depth_data; uint depth_data_len = 0; if (g_pick_state.mode == GPU_SELECT_PICK_ALL) { depth_data = ps->all.hits; depth_data_len = ps->all.hits_len; - /* move ownership */ + /* Move ownership. */ ps->all.hits = NULL; ps->all.hits_len = 0; ps->all.hits_len_alloc = 0; } else { - /* GPU_SELECT_PICK_NEAREST */ + /* #GPU_SELECT_PICK_NEAREST */ - /* Over alloc (unlikely we have as many depths as pixels) */ + /* Over allocate (unlikely we have as many depths as pixels). */ uint depth_data_len_first_pass = 0; depth_data = MEM_mallocN(ps->dst.rect_len * sizeof(*depth_data), __func__); @@ -624,7 +637,7 @@ uint gpu_select_pick_end(void) } } else { - /* same as above but different rect sizes */ + /* Same as above but different rectangle sizes. */ uint i_src = ps->cache.sub_rect.start, i_dst = 0; for (uint j = 0; j < ps->cache.sub_rect.span_len; j++) { const uint i_src_end = i_src + ps->cache.sub_rect.span; @@ -640,7 +653,7 @@ uint gpu_select_pick_end(void) qsort(depth_data, depth_data_len_first_pass, sizeof(DepthID), depth_id_cmp); - /* Sort by ID's then keep the best depth for each ID */ + /* Sort by ID's then keep the best depth for each ID. */ depth_data_len = 0; { DepthID *depth_last = NULL; @@ -657,25 +670,22 @@ uint gpu_select_pick_end(void) } /* Finally sort each unique (id, depth) pair by depth - * so the final hit-list is sorted by depth (nearest first) */ + * so the final hit-list is sorted by depth (nearest first). */ uint hits = 0; if (depth_data_len > maxhits) { hits = (uint)-1; } else { - /* leave sorting up to the caller */ + /* Leave sorting up to the caller. */ qsort(depth_data, depth_data_len, sizeof(DepthID), depth_cmp); for (uint i = 0; i < depth_data_len; i++) { #ifdef DEBUG_PRINT printf(" hit: %u: depth %u\n", depth_data[i].id, depth_data[i].depth); #endif - /* first 3 are dummy values */ - g_pick_state.buffer[hits][0] = 1; - g_pick_state.buffer[hits][1] = depth_data[i].depth; - g_pick_state.buffer[hits][2] = 0x0; /* z-far is currently never used. */ - g_pick_state.buffer[hits][3] = depth_data[i].id; + g_pick_state.buffer[hits].depth = depth_data[i].depth; + g_pick_state.buffer[hits].id = depth_data[i].id; hits++; } BLI_assert(hits < maxhits); @@ -683,8 +693,8 @@ uint gpu_select_pick_end(void) MEM_freeN(depth_data); - MEM_SAFE_FREE(ps->gl.rect_depth); - MEM_SAFE_FREE(ps->gl.rect_depth_test); + MEM_SAFE_FREE(ps->gpu.rect_depth); + MEM_SAFE_FREE(ps->gpu.rect_depth_test); if (g_pick_state.mode == GPU_SELECT_PICK_ALL) { /* 'hits' already freed as 'depth_data' */ @@ -744,8 +754,8 @@ void gpu_select_pick_cache_load_id(void) #endif LISTBASE_FOREACH (DepthBufCache *, rect_depth, &ps->cache.bufs) { if (rect_depth->next != NULL) { - /* we know the buffers differ, but this sub-region may not. - * double check before adding an id-pass */ + /* We know the buffers differ, but this sub-region may not. + * Double check before adding an id-pass. */ if (g_pick_state.mode == GPU_SELECT_PICK_ALL) { if (depth_buf_subrect_depth_any(rect_depth->next, &ps->cache.sub_rect)) { gpu_select_load_id_pass_all(rect_depth->next); diff --git a/source/blender/gpu/intern/gpu_select_private.h b/source/blender/gpu/intern/gpu_select_private.h index e5a84a037a6..0ec9f083c07 100644 --- a/source/blender/gpu/intern/gpu_select_private.h +++ b/source/blender/gpu/intern/gpu_select_private.h @@ -31,7 +31,10 @@ extern "C" { /* gpu_select_pick */ -void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, char mode); +void gpu_select_pick_begin(GPUSelectResult *buffer, + uint buffer_len, + const rcti *input, + eGPUSelectMode mode); bool gpu_select_pick_load_id(uint id, bool end); uint gpu_select_pick_end(void); @@ -46,7 +49,7 @@ void gpu_select_pick_cache_load_id(void); /* gpu_select_sample_query */ void gpu_select_query_begin( - uint (*buffer)[4], uint bufsize, const rcti *input, char mode, int oldhits); + GPUSelectResult *buffer, uint buffer_len, const rcti *input, eGPUSelectMode mode, int oldhits); bool gpu_select_query_load_id(uint id); uint gpu_select_query_end(void); diff --git a/source/blender/gpu/intern/gpu_select_sample_query.cc b/source/blender/gpu/intern/gpu_select_sample_query.cc index a430d4a9d62..7559358aaca 100644 --- a/source/blender/gpu/intern/gpu_select_sample_query.cc +++ b/source/blender/gpu/intern/gpu_select_sample_query.cc @@ -48,22 +48,22 @@ using namespace blender; using namespace blender::gpu; struct GPUSelectQueryState { - /* Tracks whether a query has been issued so that gpu_load_id can end the previous one. */ + /** Tracks whether a query has been issued so that gpu_load_id can end the previous one. */ bool query_issued; - /* GPU queries abstraction. Contains an array of queries. */ + /** GPU queries abstraction. Contains an array of queries. */ QueryPool *queries; - /* Array holding the id corresponding id to each query. */ + /** Array holding the id corresponding id to each query. */ Vector<uint> *ids; - /* Cache on initialization. */ - uint (*buffer)[4]; - /* Buffer size (stores number of integers, for actual size multiply by `sizeof(int)`). */ - uint bufsize; - /* Mode of operation. */ - char mode; + /** Cache on initialization. */ + GPUSelectResult *buffer; + /** The capacity of the `buffer` array. */ + uint buffer_len; + /** Mode of operation. */ + eGPUSelectMode mode; uint index; int oldhits; - /* Previous state to restore after drawing. */ + /** Previous state to restore after drawing. */ int viewport[4]; int scissor[4]; eGPUWriteMask write_mask; @@ -72,14 +72,17 @@ struct GPUSelectQueryState { static GPUSelectQueryState g_query_state = {false}; -void gpu_select_query_begin( - uint (*buffer)[4], uint bufsize, const rcti *input, char mode, int oldhits) +void gpu_select_query_begin(GPUSelectResult *buffer, + uint buffer_len, + const rcti *input, + const eGPUSelectMode mode, + int oldhits) { GPU_debug_group_begin("Selection Queries"); g_query_state.query_issued = false; - g_query_state.bufsize = bufsize; g_query_state.buffer = buffer; + g_query_state.buffer_len = buffer_len; g_query_state.mode = mode; g_query_state.index = 0; g_query_state.oldhits = oldhits; @@ -111,7 +114,7 @@ void gpu_select_query_begin( /* occlusion queries operates on fragments that pass tests and since we are interested on all * objects in the view frustum independently of their order, we need to disable the depth test */ if (mode == GPU_SELECT_ALL) { - /* glQueries on Windows+Intel drivers only works with depth testing turned on. + /* #glQueries on Windows+Intel drivers only works with depth testing turned on. * See T62947 for details */ GPU_depth_test(GPU_DEPTH_ALWAYS); GPU_depth_mask(true); @@ -138,10 +141,11 @@ bool gpu_select_query_load_id(uint id) g_query_state.query_issued = true; if (g_query_state.mode == GPU_SELECT_NEAREST_SECOND_PASS) { - /* Second pass should never run if first pass fails, can read past 'bufsize' in this case. */ + /* Second pass should never run if first pass fails, + * can read past `buffer_len` in this case. */ BLI_assert(g_query_state.oldhits != -1); if (g_query_state.index < g_query_state.oldhits) { - if (g_query_state.buffer[g_query_state.index][3] == id) { + if (g_query_state.buffer[g_query_state.index].id == id) { g_query_state.index++; return true; } @@ -154,7 +158,7 @@ bool gpu_select_query_load_id(uint id) uint gpu_select_query_end() { uint hits = 0; - const uint maxhits = g_query_state.bufsize; + const uint maxhits = g_query_state.buffer_len; if (g_query_state.query_issued) { g_query_state.queries->end_query(); @@ -168,10 +172,8 @@ uint gpu_select_query_end() if (result[i] != 0) { if (g_query_state.mode != GPU_SELECT_NEAREST_SECOND_PASS) { if (hits < maxhits) { - g_query_state.buffer[hits][0] = 1; - g_query_state.buffer[hits][1] = 0xFFFF; - g_query_state.buffer[hits][2] = 0xFFFF; - g_query_state.buffer[hits][3] = ids[i]; + g_query_state.buffer[hits].depth = 0xFFFF; + g_query_state.buffer[hits].id = ids[i]; hits++; } else { @@ -183,9 +185,8 @@ uint gpu_select_query_end() int j; /* search in buffer and make selected object first */ for (j = 0; j < g_query_state.oldhits; j++) { - if (g_query_state.buffer[j][3] == ids[i]) { - g_query_state.buffer[j][1] = 0; - g_query_state.buffer[j][2] = 0; + if (g_query_state.buffer[j].id == ids[i]) { + g_query_state.buffer[j].depth = 0; } } break; diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index ef800abc3c9..2e924925ab8 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -249,6 +249,11 @@ GPUShader *GPU_shader_create_compute(const char *computecode, shname); } +const GPUShaderCreateInfo *GPU_shader_create_info_get(const char *info_name) +{ + return gpu_shader_create_info_get(info_name); +} + GPUShader *GPU_shader_create_from_info_name(const char *info_name) { using namespace blender::gpu::shader; @@ -309,9 +314,8 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) } if (!info.vertex_source_.is_empty()) { - uint32_t builtins = 0; + char *code = gpu_shader_dependency_get_resolved_source(info.vertex_source_.c_str()); std::string interface = shader->vertex_interface_declare(info); - char *code = gpu_shader_dependency_get_resolved_source(info.vertex_source_.c_str(), &builtins); Vector<const char *> sources; standard_defines(sources); @@ -336,10 +340,8 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) } if (!info.fragment_source_.is_empty()) { - uint32_t builtins = 0; + char *code = gpu_shader_dependency_get_resolved_source(info.fragment_source_.c_str()); std::string interface = shader->fragment_interface_declare(info); - char *code = gpu_shader_dependency_get_resolved_source(info.fragment_source_.c_str(), - &builtins); Vector<const char *> sources; standard_defines(sources); @@ -364,11 +366,9 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) } if (!info.geometry_source_.is_empty()) { - uint32_t builtins = 0; - std::string interface = shader->geometry_interface_declare(info); + char *code = gpu_shader_dependency_get_resolved_source(info.geometry_source_.c_str()); std::string layout = shader->geometry_layout_declare(info); - char *code = gpu_shader_dependency_get_resolved_source(info.geometry_source_.c_str(), - &builtins); + std::string interface = shader->geometry_interface_declare(info); Vector<const char *> sources; standard_defines(sources); @@ -391,9 +391,7 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) } if (!info.compute_source_.is_empty()) { - uint32_t builtins = 0; - char *code = gpu_shader_dependency_get_resolved_source(info.compute_source_.c_str(), - &builtins); + char *code = gpu_shader_dependency_get_resolved_source(info.compute_source_.c_str()); std::string layout = shader->compute_layout_declare(info); Vector<const char *> sources; diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc index 492b247e192..1464a7ade24 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.cc +++ b/source/blender/gpu/intern/gpu_shader_create_info.cc @@ -34,6 +34,7 @@ #include "gpu_shader_create_info.hh" #include "gpu_shader_create_info_private.hh" +#include "gpu_shader_dependency_private.h" #include "gpu_shader_private.hh" #undef GPU_SHADER_INTERFACE_INFO @@ -167,7 +168,7 @@ void ShaderCreateInfo::validate(const ShaderCreateInfo &other_info) } } { - /* TODO(fclem) Push constant validation. */ + /* TODO(@fclem): Push constant validation. */ } } @@ -209,6 +210,19 @@ void gpu_shader_create_info_init() draw_modelmat = draw_modelmat_legacy; } + for (ShaderCreateInfo *info : g_create_infos->values()) { + if (info->do_static_compilation_) { + info->builtins_ |= static_cast<BuiltinBits>( + gpu_shader_dependency_get_builtins(info->vertex_source_.c_str())); + info->builtins_ |= static_cast<BuiltinBits>( + gpu_shader_dependency_get_builtins(info->fragment_source_.c_str())); + info->builtins_ |= static_cast<BuiltinBits>( + gpu_shader_dependency_get_builtins(info->geometry_source_.c_str())); + info->builtins_ |= static_cast<BuiltinBits>( + gpu_shader_dependency_get_builtins(info->compute_source_.c_str())); + } + } + /* TEST */ // gpu_shader_create_info_compile_all(); } @@ -302,6 +316,7 @@ const GPUShaderCreateInfo *gpu_shader_create_info_get(const char *info_name) { if (g_create_infos->contains(info_name) == false) { printf("Error: Cannot find shader create info named \"%s\"\n", info_name); + return nullptr; } ShaderCreateInfo *info = g_create_infos->lookup(info_name); return reinterpret_cast<const GPUShaderCreateInfo *>(info); diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh index 63c6e94f4c8..736a8ed0590 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.hh +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -63,6 +63,7 @@ enum class Type { }; enum class BuiltinBits { + NONE = 0, /** * Allow getting barycentric coordinates inside the fragment shader. * \note Emulated on OpenGL. @@ -72,6 +73,10 @@ enum class BuiltinBits { FRONT_FACING = (1 << 4), GLOBAL_INVOCATION_ID = (1 << 5), INSTANCE_ID = (1 << 6), + /** + * Allow setting the target layer when the output is a layered framebuffer. + * \note Emulated through geometry shader on older hardware. + */ LAYER = (1 << 7), LOCAL_INVOCATION_ID = (1 << 8), LOCAL_INVOCATION_INDEX = (1 << 9), @@ -125,10 +130,13 @@ enum class ImageType { /* Storage qualifiers. */ enum class Qualifier { - RESTRICT = (1 << 0), - READ_ONLY = (1 << 1), - WRITE_ONLY = (1 << 2), - QUALIFIER_MAX = (WRITE_ONLY << 1) - 1, + /** Restrict flag is set by default. Unless specified otherwise. */ + NO_RESTRICT = (1 << 0), + READ = (1 << 1), + WRITE = (1 << 2), + /** Shorthand version of combined flags. */ + READ_WRITE = READ | WRITE, + QUALIFIER_MAX = (WRITE << 1) - 1, }; ENUM_OPERATORS(Qualifier, Qualifier::QUALIFIER_MAX); @@ -226,6 +234,8 @@ struct ShaderCreateInfo { * Only for names used by gpu::ShaderInterface. */ size_t interface_names_size_ = 0; + /** Manually set builtins. */ + BuiltinBits builtins_ = BuiltinBits::NONE; struct VertIn { int index; @@ -538,6 +548,12 @@ struct ShaderCreateInfo { return *(Self *)this; } + Self &builtins(BuiltinBits builtin) + { + builtins_ |= builtin; + return *(Self *)this; + } + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index 5e03f7d0767..0db03c2b636 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -59,7 +59,6 @@ struct GPUSource { /* Scan for builtins. */ /* FIXME: This can trigger false positive caused by disabled #if blocks. */ /* TODO(fclem): Could be made faster by scanning once. */ - /* TODO(fclem): BARYCENTRIC_COORD. */ if (source.find("gl_FragCoord", 0)) { builtins |= shader::BuiltinBits::FRAG_COORD; } @@ -72,9 +71,6 @@ struct GPUSource { if (source.find("gl_InstanceID", 0)) { builtins |= shader::BuiltinBits::INSTANCE_ID; } - if (source.find("gl_Layer", 0)) { - builtins |= shader::BuiltinBits::LAYER; - } if (source.find("gl_LocalInvocationID", 0)) { builtins |= shader::BuiltinBits::LOCAL_INVOCATION_ID; } @@ -336,13 +332,23 @@ struct GPUSource { } /* Returns the final string with all includes done. */ - void build(std::string &str, shader::BuiltinBits &out_builtins) + std::string build() const { + std::string str; for (auto *dep : dependencies) { - out_builtins |= builtins; str += dep->source; } str += source; + return str; + } + + shader::BuiltinBits builtins_get() const + { + shader::BuiltinBits out_builtins = shader::BuiltinBits::NONE; + for (auto *dep : dependencies) { + out_builtins |= dep->builtins; + } + return out_builtins; } }; @@ -377,14 +383,19 @@ void gpu_shader_dependency_exit() delete g_sources; } -char *gpu_shader_dependency_get_resolved_source(const char *shader_source_name, uint32_t *builtins) +uint32_t gpu_shader_dependency_get_builtins(const char *shader_source_name) +{ + if (shader_source_name[0] == '\0') { + return 0; + } + GPUSource *source = g_sources->lookup(shader_source_name); + return static_cast<uint32_t>(source->builtins_get()); +} + +char *gpu_shader_dependency_get_resolved_source(const char *shader_source_name) { GPUSource *source = g_sources->lookup(shader_source_name); - std::string str; - shader::BuiltinBits out_builtins; - source->build(str, out_builtins); - *builtins |= (uint32_t)out_builtins; - return strdup(str.c_str()); + return strdup(source->build().c_str()); } char *gpu_shader_dependency_get_source(const char *shader_source_name) diff --git a/source/blender/gpu/intern/gpu_shader_dependency_private.h b/source/blender/gpu/intern/gpu_shader_dependency_private.h index b129ca74a48..083c38897ce 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency_private.h +++ b/source/blender/gpu/intern/gpu_shader_dependency_private.h @@ -35,10 +35,11 @@ void gpu_shader_dependency_init(void); void gpu_shader_dependency_exit(void); /* User must free the resulting string using free. */ -char *gpu_shader_dependency_get_resolved_source(const char *shader_source_name, - uint32_t *builtins); +char *gpu_shader_dependency_get_resolved_source(const char *shader_source_name); char *gpu_shader_dependency_get_source(const char *shader_source_name); +uint32_t gpu_shader_dependency_get_builtins(const char *shader_source_name); + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/intern/gpu_shader_log.cc b/source/blender/gpu/intern/gpu_shader_log.cc index 12459b4b721..21973cf976a 100644 --- a/source/blender/gpu/intern/gpu_shader_log.cc +++ b/source/blender/gpu/intern/gpu_shader_log.cc @@ -102,7 +102,7 @@ void Shader::print_log(Span<const char *> sources, found_line_id = true; break; } -/* TODO(fclem) Make this an option to display N lines before error. */ +/* TODO(@fclem): Make this an option to display N lines before error. */ #if 0 /* Uncomment to print shader file up to the error line to have more context. */ BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index); BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line); diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 92d180f1140..2ee7c0503f4 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -248,6 +248,8 @@ static void detect_workarounds() GLContext::direct_state_access_support = false; GLContext::fixed_restart_index_support = false; GLContext::geometry_shader_invocations = false; + GLContext::layered_rendering_support = false; + GLContext::native_barycentric_support = false; GLContext::multi_bind_support = false; GLContext::multi_draw_indirect_support = false; GLContext::shader_draw_parameters_support = false; @@ -445,6 +447,8 @@ bool GLContext::direct_state_access_support = false; bool GLContext::explicit_location_support = false; bool GLContext::geometry_shader_invocations = false; bool GLContext::fixed_restart_index_support = false; +bool GLContext::layered_rendering_support = false; +bool GLContext::native_barycentric_support = false; bool GLContext::multi_bind_support = false; bool GLContext::multi_draw_indirect_support = false; bool GLContext::shader_draw_parameters_support = false; @@ -505,6 +509,8 @@ void GLBackend::capabilities_init() GLContext::explicit_location_support = GLEW_VERSION_4_3; GLContext::geometry_shader_invocations = GLEW_ARB_gpu_shader5; GLContext::fixed_restart_index_support = GLEW_ARB_ES3_compatibility; + GLContext::layered_rendering_support = GLEW_AMD_vertex_shader_layer; + GLContext::native_barycentric_support = GLEW_AMD_shader_explicit_vertex_parameter; GLContext::multi_bind_support = GLEW_ARB_multi_bind; GLContext::multi_draw_indirect_support = GLEW_ARB_multi_draw_indirect; GLContext::shader_draw_parameters_support = GLEW_ARB_shader_draw_parameters; diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh index dd22418972b..b7a74863ac4 100644 --- a/source/blender/gpu/opengl/gl_context.hh +++ b/source/blender/gpu/opengl/gl_context.hh @@ -72,6 +72,8 @@ class GLContext : public Context { static bool explicit_location_support; static bool geometry_shader_invocations; static bool fixed_restart_index_support; + static bool layered_rendering_support; + static bool native_barycentric_support; static bool multi_bind_support; static bool multi_draw_indirect_support; static bool shader_draw_parameters_support; diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index e031047f844..3ab3b11d1f4 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -277,15 +277,15 @@ static void print_image_type(std::ostream &os, static std::ostream &print_qualifier(std::ostream &os, const Qualifier &qualifiers) { - if ((qualifiers & Qualifier::RESTRICT) == Qualifier::RESTRICT) { + if (bool(qualifiers & Qualifier::NO_RESTRICT) == false) { os << "restrict "; } - if ((qualifiers & Qualifier::READ_ONLY) == Qualifier::READ_ONLY) { - os << "readonly "; - } - if ((qualifiers & Qualifier::WRITE_ONLY) == Qualifier::WRITE_ONLY) { + if (bool(qualifiers & Qualifier::READ) == false) { os << "writeonly "; } + if (bool(qualifiers & Qualifier::WRITE) == false) { + os << "readonly "; + } return os; } @@ -370,7 +370,7 @@ static void print_interface(std::ostream &os, const StageInterfaceInfo &iface, const StringRefNull &suffix = "") { - /* TODO(fclem) Move that to interface check. */ + /* TODO(@fclem): Move that to interface check. */ // if (iface.instance_name.is_empty()) { // BLI_assert_msg(0, "Interfaces require an instance name for geometry shader."); // std::cout << iface.name << ": Interfaces require an instance name for geometry shader.\n"; @@ -426,9 +426,28 @@ std::string GLShader::resources_declare(const ShaderCreateInfo &info) const return ss.str(); } +static std::string main_function_wrapper(std::string &pre_main, std::string &post_main) +{ + std::stringstream ss; + /* Prototype for the original main. */ + ss << "\n"; + ss << "void main_function_();\n"; + /* Wrapper to the main function in order to inject code processing on globals. */ + ss << "void main() {\n"; + ss << pre_main; + ss << " main_function_();\n"; + ss << post_main; + ss << "}\n"; + /* Rename the original main. */ + ss << "#define main main_function_\n"; + ss << "\n"; + return ss.str(); +} + std::string GLShader::vertex_interface_declare(const ShaderCreateInfo &info) const { std::stringstream ss; + std::string post_main = ""; ss << "\n/* Inputs. */\n"; for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) { @@ -441,13 +460,35 @@ std::string GLShader::vertex_interface_declare(const ShaderCreateInfo &info) con for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) { print_interface(ss, "out", *iface); } + if (!GLContext::layered_rendering_support && bool(info.builtins_ & BuiltinBits::LAYER)) { + ss << "out int gpu_Layer;\n"; + } + if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) { + if (!GLContext::native_barycentric_support) { + /* Disabled or unsupported. */ + } + else if (GLEW_AMD_shader_explicit_vertex_parameter) { + /* Need this for stable barycentric. */ + ss << "flat out vec4 gpu_pos_flat;\n"; + ss << "out vec4 gpu_pos;\n"; + + post_main += " gpu_pos = gpu_pos_flat = gl_Position;\n"; + } + } ss << "\n"; + + if (post_main.empty() == false) { + std::string pre_main = ""; + ss << main_function_wrapper(pre_main, post_main); + } return ss.str(); } std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) const { std::stringstream ss; + std::string pre_main = ""; + ss << "\n/* Interfaces. */\n"; const Vector<StageInterfaceInfo *> &in_interfaces = (info.geometry_source_.is_empty()) ? info.vertex_out_interfaces_ : @@ -455,6 +496,32 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c for (const StageInterfaceInfo *iface : in_interfaces) { print_interface(ss, "in", *iface); } + if (bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD)) { + if (!GLContext::native_barycentric_support) { + ss << "smooth in vec3 gpu_BaryCoord;\n"; + ss << "noperspective in vec3 gpu_BaryCoordNoPersp;\n"; + } + else if (GLEW_AMD_shader_explicit_vertex_parameter) { + /* NOTE(fclem): This won't work with geometry shader. Hopefully, we don't need geometry + * shader workaround if this extension/feature is detected. */ + ss << "\n/* Stable Barycentric Coordinates. */\n"; + ss << "flat in vec4 gpu_pos_flat;\n"; + ss << "__explicitInterpAMD in vec4 gpu_pos;\n"; + /* Globals. */ + ss << "vec3 gpu_BaryCoord;\n"; + ss << "vec3 gpu_BaryCoordNoPersp;\n"; + ss << "\n"; + ss << "vec2 stable_bary_(vec2 in_bary) {\n"; + ss << " vec3 bary = vec3(in_bary, 1.0 - in_bary.x - in_bary.y);\n"; + ss << " if (interpolateAtVertexAMD(gpu_pos, 0) == gpu_pos_flat) { return bary.zxy; }\n"; + ss << " if (interpolateAtVertexAMD(gpu_pos, 2) == gpu_pos_flat) { return bary.yzx; }\n"; + ss << " return bary.xyz;\n"; + ss << "}\n"; + + pre_main += " gpu_BaryCoord = stable_bary_(gl_BaryCoordSmoothAMD);\n"; + pre_main += " gpu_BaryCoordNoPersp = stable_bary_(gl_BaryCoordNoPerspAMD);\n"; + } + } ss << "\n/* Outputs. */\n"; for (const ShaderCreateInfo::FragOut &output : info.fragment_outputs_) { ss << "layout(location = " << output.index; @@ -472,6 +539,11 @@ std::string GLShader::fragment_interface_declare(const ShaderCreateInfo &info) c ss << "out " << to_string(output.type) << " " << output.name << ";\n"; } ss << "\n"; + + if (pre_main.empty() == false) { + std::string post_main = ""; + ss << main_function_wrapper(pre_main, post_main); + } return ss.str(); } @@ -503,7 +575,7 @@ static StageInterfaceInfo *find_interface_by_name(const Vector<StageInterfaceInf const StringRefNull &name) { for (auto *iface : ifaces) { - if (iface->name == name) { + if (iface->instance_name == name) { return iface; } } @@ -512,8 +584,8 @@ static StageInterfaceInfo *find_interface_by_name(const Vector<StageInterfaceInf std::string GLShader::geometry_interface_declare(const ShaderCreateInfo &info) const { - std::stringstream ss; + ss << "\n/* Interfaces. */\n"; for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) { bool has_matching_output_iface = find_interface_by_name(info.geometry_out_interfaces_, @@ -547,6 +619,76 @@ std::string GLShader::compute_layout_declare(const ShaderCreateInfo &info) const ss << "\n"; return ss.str(); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Passthrough geometry shader emulation + * + * \{ */ + +std::string GLShader::workaround_geometry_shader_source_create( + const shader::ShaderCreateInfo &info) +{ + std::stringstream ss; + + const bool do_layer_workaround = !GLContext::layered_rendering_support && + bool(info.builtins_ & BuiltinBits::LAYER); + const bool do_barycentric_workaround = !GLContext::native_barycentric_support && + bool(info.builtins_ & BuiltinBits::BARYCENTRIC_COORD); + + shader::ShaderCreateInfo info_modified = info; + info_modified.geometry_out_interfaces_ = info_modified.vertex_out_interfaces_; + /** + * NOTE(@fclem): Assuming we will render TRIANGLES. This will not work with other primitive + * types. In this case, it might not trigger an error on some implementations. + */ + info_modified.geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3); + + ss << geometry_layout_declare(info_modified); + ss << geometry_interface_declare(info_modified); + if (do_layer_workaround) { + ss << "in int gpu_Layer[];\n"; + } + if (do_barycentric_workaround) { + ss << "smooth out vec3 gpu_BaryCoord;\n"; + ss << "noperspective out vec3 gpu_BaryCoordNoPersp;\n"; + } + ss << "\n"; + + ss << "void main()\n"; + ss << "{\n"; + if (do_layer_workaround) { + ss << " gl_Layer = gpu_Layer[0];\n"; + } + for (auto i : IndexRange(3)) { + for (auto iface : info_modified.vertex_out_interfaces_) { + for (auto &inout : iface->inouts) { + ss << " " << iface->instance_name << "_out." << inout.name; + ss << " = " << iface->instance_name << "_in[" << i << "]." << inout.name << ";\n"; + } + } + if (do_barycentric_workaround) { + ss << " gpu_BaryCoordNoPersp = gpu_BaryCoord ="; + ss << " vec3(" << int(i == 0) << ", " << int(i == 1) << ", " << int(i == 2) << ");\n"; + } + ss << " gl_Position = gl_in[" << i << "].gl_Position;\n"; + ss << " EmitVertex();\n"; + } + ss << "}\n"; + return ss.str(); +} + +bool GLShader::do_geometry_shader_injection(const shader::ShaderCreateInfo *info) +{ + BuiltinBits builtins = info->builtins_; + if (!GLContext::native_barycentric_support && bool(builtins & BuiltinBits::BARYCENTRIC_COORD)) { + return true; + } + if (!GLContext::layered_rendering_support && bool(builtins & BuiltinBits::LAYER)) { + return true; + } + return false; +} /** \} */ @@ -557,7 +699,7 @@ std::string GLShader::compute_layout_declare(const ShaderCreateInfo &info) const static char *glsl_patch_default_get() { /** Used for shader patching. Init once. */ - static char patch[700] = "\0"; + static char patch[1024] = "\0"; if (patch[0] != '\0') { return patch; } @@ -601,6 +743,13 @@ static char *glsl_patch_default_get() STR_CONCAT(patch, slen, "#extension GL_ARB_shader_image_load_store: enable\n"); STR_CONCAT(patch, slen, "#extension GL_ARB_shading_language_420pack: enable\n"); } + if (GLContext::layered_rendering_support) { + STR_CONCAT(patch, slen, "#extension GL_AMD_vertex_shader_layer: enable\n"); + STR_CONCAT(patch, slen, "#define gpu_Layer gl_Layer\n"); + } + if (GLContext::native_barycentric_support) { + STR_CONCAT(patch, slen, "#extension GL_AMD_shader_explicit_vertex_parameter: enable\n"); + } /* Fallbacks. */ if (!GLContext::shader_draw_parameters_support) { @@ -717,6 +866,14 @@ bool GLShader::finalize(const shader::ShaderCreateInfo *info) return false; } + if (info && do_geometry_shader_injection(info)) { + std::string source = workaround_geometry_shader_source_create(*info); + Vector<const char *> sources; + sources.append("version"); + sources.append(source.c_str()); + geometry_shader_from_glsl(sources); + } + glLinkProgram(shader_program_); GLint status; diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh index a82ab026c16..cc1c93142f8 100644 --- a/source/blender/gpu/opengl/gl_shader.hh +++ b/source/blender/gpu/opengl/gl_shader.hh @@ -94,6 +94,14 @@ class GLShader : public Shader { /** Create, compile and attach the shader stage to the shader program. */ GLuint create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources); + /** + * \brief features available on newer implementation such as native barycentric coordinates + * and layered rendering, necessitate a geometry shader to work on older hardware. + */ + std::string workaround_geometry_shader_source_create(const shader::ShaderCreateInfo &info); + + bool do_geometry_shader_injection(const shader::ShaderCreateInfo *info); + MEM_CXX_CLASS_ALLOC_FUNCS("GLShader"); }; diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc index 71b908665d3..83016fca8a5 100644 --- a/source/blender/gpu/opengl/gl_shader_interface.cc +++ b/source/blender/gpu/opengl/gl_shader_interface.cc @@ -497,7 +497,7 @@ GLShaderInterface::~GLShaderInterface() void GLShaderInterface::ref_add(GLVaoCache *ref) { for (int i = 0; i < refs_.size(); i++) { - if (refs_[i] == NULL) { + if (refs_[i] == nullptr) { refs_[i] = ref; return; } @@ -509,7 +509,7 @@ void GLShaderInterface::ref_remove(GLVaoCache *ref) { for (int i = 0; i < refs_.size(); i++) { if (refs_[i] == ref) { - refs_[i] = NULL; + refs_[i] = nullptr; break; /* cannot have duplicates */ } } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl index 01a16e194ca..ab6024b073d 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_diffuse.glsl @@ -18,7 +18,7 @@ void node_bsdf_diffuse(vec4 color, float roughness, vec3 N, out Closure result) result.radiance = out_Diffuse_0.radiance; - /* TODO(fclem) Try to not use this. */ + /* TODO(@fclem): Try to not use this. */ closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl index bba84c2be52..c97fc090fe2 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl @@ -166,7 +166,7 @@ void node_bsdf_principled(vec4 base_color, float btdf = (do_multiscatter != 0.0) ? 1.0 : btdf_lut(NV, in_Refraction_3.roughness, in_Refraction_3.ior).x; - /* TODO(fclem) This could be going to a transmission render pass instead. */ + /* TODO(@fclem): This could be going to a transmission render pass instead. */ out_Refraction_3.radiance *= btdf; out_Refraction_3.radiance = render_pass_glossy_mask(vec3(1), out_Refraction_3.radiance); out_Refraction_3.radiance *= base_color.rgb; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl index 7cbc7218f5c..8a42a131f43 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_refraction.glsl @@ -21,7 +21,7 @@ void node_bsdf_refraction(vec4 color, float roughness, float ior, vec3 N, out Cl result.radiance = out_Refraction_0.radiance; - /* TODO(fclem) Try to not use this. */ + /* TODO(@fclem): Try to not use this. */ result.ssr_normal = normal_encode(mat3(ViewMatrix) * in_Refraction_0.N, viewCameraVec(viewPosition)); } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl index d0c159cdf37..20b634aa801 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl @@ -22,7 +22,7 @@ void node_subsurface_scattering(vec4 color, closure_load_sss_data(scale, out_Diffuse_0.radiance, color.rgb, int(sss_id), result); - /* TODO(fclem) Try to not use this. */ + /* TODO(@fclem): Try to not use this. */ closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result); } |