diff options
Diffstat (limited to 'source/blender/gpu')
90 files changed, 6965 insertions, 4669 deletions
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 80ea28aca3c..cf0399b776d 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -55,7 +55,6 @@ set(INC_SYS ) set(SRC - intern/gpu_attr_binding.cc intern/gpu_batch.cc intern/gpu_batch_presets.c intern/gpu_batch_utils.c @@ -63,6 +62,7 @@ set(SRC intern/gpu_codegen.c intern/gpu_context.cc intern/gpu_debug.cc + intern/gpu_drawlist.cc intern/gpu_element.cc intern/gpu_extensions.cc intern/gpu_framebuffer.cc @@ -74,7 +74,6 @@ set(SRC intern/gpu_matrix.cc intern/gpu_node_graph.c intern/gpu_platform.cc - intern/gpu_primitive.c intern/gpu_select.c intern/gpu_select_pick.c intern/gpu_select_sample_query.c @@ -83,14 +82,23 @@ set(SRC intern/gpu_shader_interface.cc intern/gpu_state.cc intern/gpu_texture.cc - intern/gpu_uniformbuffer.cc + intern/gpu_uniform_buffer.cc intern/gpu_vertex_buffer.cc intern/gpu_vertex_format.cc intern/gpu_viewport.c + opengl/gl_batch.cc opengl/gl_context.cc + opengl/gl_drawlist.cc + opengl/gl_debug.cc + opengl/gl_framebuffer.cc + opengl/gl_immediate.cc + opengl/gl_shader.cc + opengl/gl_shader_interface.cc + opengl/gl_state.cc + opengl/gl_uniform_buffer.cc + opengl/gl_vertex_array.cc - GPU_attr_binding.h GPU_batch.h GPU_batch_presets.h GPU_batch_utils.h @@ -98,6 +106,7 @@ set(SRC GPU_common.h GPU_context.h GPU_debug.h + GPU_drawlist.h GPU_element.h GPU_extensions.h GPU_framebuffer.h @@ -112,30 +121,45 @@ set(SRC GPU_primitive.h GPU_select.h GPU_shader.h - GPU_shader_interface.h GPU_state.h GPU_texture.h - GPU_uniformbuffer.h + GPU_uniform_buffer.h GPU_vertex_buffer.h GPU_vertex_format.h GPU_viewport.h - intern/gpu_attr_binding_private.h intern/gpu_backend.hh - intern/gpu_batch_private.h + intern/gpu_batch_private.hh intern/gpu_codegen.h intern/gpu_context_private.hh + intern/gpu_drawlist_private.hh + intern/gpu_framebuffer_private.hh + intern/gpu_immediate_private.hh intern/gpu_material_library.h intern/gpu_matrix_private.h intern/gpu_node_graph.h - intern/gpu_primitive_private.h intern/gpu_private.h intern/gpu_select_private.h - intern/gpu_shader_private.h + intern/gpu_shader_private.hh + intern/gpu_shader_interface.hh + intern/gpu_state_private.hh + intern/gpu_uniform_buffer_private.hh intern/gpu_vertex_format_private.h opengl/gl_backend.hh + opengl/gl_batch.hh opengl/gl_context.hh + opengl/gl_debug.hh + opengl/gl_drawlist.hh + opengl/gl_framebuffer.hh + opengl/gl_immediate.hh + opengl/gl_primitive.hh + opengl/gl_shader.hh + opengl/gl_shader_interface.hh + opengl/gl_state.hh + opengl/gl_texture.hh + opengl/gl_uniform_buffer.hh + opengl/gl_vertex_array.hh ) set(LIB diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index 855214c279c..b45898f9c6a 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -26,85 +26,82 @@ #pragma once +#include "BLI_utildefines.h" + #include "GPU_element.h" #include "GPU_shader.h" -#include "GPU_shader_interface.h" #include "GPU_vertex_buffer.h" -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - GPU_BATCH_UNUSED, - GPU_BATCH_READY_TO_FORMAT, - GPU_BATCH_READY_TO_BUILD, - GPU_BATCH_BUILDING, - GPU_BATCH_READY_TO_DRAW, -} GPUBatchPhase; - #define GPU_BATCH_VBO_MAX_LEN 6 #define GPU_BATCH_INST_VBO_MAX_LEN 2 #define GPU_BATCH_VAO_STATIC_LEN 3 #define GPU_BATCH_VAO_DYN_ALLOC_COUNT 16 -typedef struct GPUBatch { - /* geometry */ +typedef enum eGPUBatchFlag { + /** Invalid default state. */ + GPU_BATCH_INVALID = 0, + + /** GPUVertBuf ownership. (One bit per vbo) */ + GPU_BATCH_OWNS_VBO = (1 << 0), + GPU_BATCH_OWNS_VBO_MAX = (GPU_BATCH_OWNS_VBO << (GPU_BATCH_VBO_MAX_LEN - 1)), + GPU_BATCH_OWNS_VBO_ANY = ((GPU_BATCH_OWNS_VBO << GPU_BATCH_VBO_MAX_LEN) - 1), + /** Instance GPUVertBuf ownership. (One bit per vbo) */ + GPU_BATCH_OWNS_INST_VBO = (GPU_BATCH_OWNS_VBO_MAX << 1), + GPU_BATCH_OWNS_INST_VBO_MAX = (GPU_BATCH_OWNS_INST_VBO << (GPU_BATCH_INST_VBO_MAX_LEN - 1)), + GPU_BATCH_OWNS_INST_VBO_ANY = ((GPU_BATCH_OWNS_INST_VBO << GPU_BATCH_INST_VBO_MAX_LEN) - 1) & + ~GPU_BATCH_OWNS_VBO_ANY, + /** GPUIndexBuf ownership. */ + GPU_BATCH_OWNS_INDEX = (GPU_BATCH_OWNS_INST_VBO_MAX << 1), + + /** Has been initialized. At least one VBO is set. */ + GPU_BATCH_INIT = (1 << 16), + /** Batch is initialized but it's VBOs are still being populated. (optional) */ + GPU_BATCH_BUILDING = (1 << 16), + /** Cached data need to be rebuild. (VAO, PSO, ...) */ + GPU_BATCH_DIRTY = (1 << 17), +} eGPUBatchFlag; + +#define GPU_BATCH_OWNS_NONE GPU_BATCH_INVALID + +BLI_STATIC_ASSERT(GPU_BATCH_OWNS_INDEX < GPU_BATCH_INIT, + "eGPUBatchFlag: Error: status flags are shadowed by the ownership bits!") + +ENUM_OPERATORS(eGPUBatchFlag) + +#ifdef __cplusplus +extern "C" { +#endif +/** + * IMPORTANT: Do not allocate manually as the real struct is bigger (i.e: GLBatch). This is only + * the common and "public" part of the struct. Use the provided allocator. + * TODO(fclem) Make the content of this struct hidden and expose getters/setters. + **/ +typedef struct GPUBatch { /** verts[0] is required, others can be NULL */ GPUVertBuf *verts[GPU_BATCH_VBO_MAX_LEN]; /** Instance attributes. */ GPUVertBuf *inst[GPU_BATCH_INST_VBO_MAX_LEN]; /** NULL if element list not needed */ GPUIndexBuf *elem; - uint32_t gl_prim_type; - - /* cached values (avoid dereferencing later) */ - uint32_t vao_id; - uint32_t program; - const struct GPUShaderInterface *interface; - - /* book-keeping */ - uint owns_flag; - /** used to free all vaos. this implies all vaos were created under the same context. */ - struct GPUContext *context; - GPUBatchPhase phase; - bool program_in_use; - - /* Vao management: remembers all geometry state (vertex attribute bindings & element buffer) - * for each shader interface. Start with a static number of vaos and fallback to dynamic count - * if necessary. Once a batch goes dynamic it does not go back. */ - bool is_dynamic_vao_count; - union { - /** Static handle count */ - struct { - const struct GPUShaderInterface *interfaces[GPU_BATCH_VAO_STATIC_LEN]; - uint32_t vao_ids[GPU_BATCH_VAO_STATIC_LEN]; - } static_vaos; - /** Dynamic handle count */ - struct { - uint count; - const struct GPUShaderInterface **interfaces; - uint32_t *vao_ids; - } dynamic_vaos; - }; - - /* XXX This is the only solution if we want to have some data structure using - * batches as key to identify nodes. We must destroy these nodes with this callback. */ - void (*free_callback)(struct GPUBatch *, void *); - void *callback_data; + /** Bookeeping. */ + eGPUBatchFlag flag; + /** Type of geometry to draw. */ + GPUPrimType prim_type; + /** Current assigned shader. DEPRECATED. Here only for uniform binding. */ + struct GPUShader *shader; } GPUBatch; -enum { - GPU_BATCH_OWNS_VBO = (1 << 0), - /* each vbo index gets bit-shifted */ - GPU_BATCH_OWNS_INSTANCES = (1 << 30), - GPU_BATCH_OWNS_INDEX = (1u << 31u), -}; - -GPUBatch *GPU_batch_calloc(uint count); -GPUBatch *GPU_batch_create_ex(GPUPrimType, GPUVertBuf *, GPUIndexBuf *, uint owns_flag); -void GPU_batch_init_ex(GPUBatch *, GPUPrimType, GPUVertBuf *, GPUIndexBuf *, uint owns_flag); +GPUBatch *GPU_batch_calloc(void); +GPUBatch *GPU_batch_create_ex(GPUPrimType prim, + GPUVertBuf *vert, + GPUIndexBuf *elem, + eGPUBatchFlag own_flag); +void GPU_batch_init_ex(GPUBatch *batch, + GPUPrimType prim, + GPUVertBuf *vert, + GPUIndexBuf *elem, + eGPUBatchFlag own_flag); void GPU_batch_copy(GPUBatch *batch_dst, GPUBatch *batch_src); #define GPU_batch_create(prim, verts, elem) GPU_batch_create_ex(prim, verts, elem, 0) @@ -115,10 +112,6 @@ void GPU_batch_clear(GPUBatch *); void GPU_batch_discard(GPUBatch *); /* verts & elem are not discarded */ -void GPU_batch_vao_cache_clear(GPUBatch *); - -void GPU_batch_callback_free_set(GPUBatch *, void (*callback)(GPUBatch *, void *), void *); - void GPU_batch_instbuf_set(GPUBatch *, GPUVertBuf *, bool own_vbo); /* Instancing */ void GPU_batch_elembuf_set(GPUBatch *batch, GPUIndexBuf *elem, bool own_ibo); @@ -128,42 +121,39 @@ int GPU_batch_vertbuf_add_ex(GPUBatch *, GPUVertBuf *, bool own_vbo); #define GPU_batch_vertbuf_add(batch, verts) GPU_batch_vertbuf_add_ex(batch, verts, false) void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader); -void GPU_batch_set_shader_no_bind(GPUBatch *batch, GPUShader *shader); void GPU_batch_program_set_imm_shader(GPUBatch *batch); void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id); void GPU_batch_program_set_builtin_with_config(GPUBatch *batch, eGPUBuiltinShader shader_id, eGPUShaderConfig sh_cfg); -/* Entire batch draws with one shader program, but can be redrawn later with another program. */ -/* Vertex shader's inputs must be compatible with the batch's vertex format. */ - -void GPU_batch_program_use_begin(GPUBatch *); /* call before Batch_Uniform (temp hack?) */ -void GPU_batch_program_use_end(GPUBatch *); - -void GPU_batch_uniform_1ui(GPUBatch *, const char *name, uint value); -void GPU_batch_uniform_1i(GPUBatch *, const char *name, int value); -void GPU_batch_uniform_1b(GPUBatch *, const char *name, bool value); -void GPU_batch_uniform_1f(GPUBatch *, const char *name, float value); -void GPU_batch_uniform_2f(GPUBatch *, const char *name, float x, float y); -void GPU_batch_uniform_3f(GPUBatch *, const char *name, float x, float y, float z); -void GPU_batch_uniform_4f(GPUBatch *, const char *name, float x, float y, float z, float w); -void GPU_batch_uniform_2fv(GPUBatch *, const char *name, const float data[2]); -void GPU_batch_uniform_3fv(GPUBatch *, const char *name, const float data[3]); -void GPU_batch_uniform_4fv(GPUBatch *, const char *name, const float data[4]); -void GPU_batch_uniform_2fv_array(GPUBatch *, const char *name, const int len, const float *data); -void GPU_batch_uniform_4fv_array(GPUBatch *, const char *name, const int len, const float *data); -void GPU_batch_uniform_mat4(GPUBatch *, const char *name, const float data[4][4]); - -void GPU_batch_draw(GPUBatch *); - -/* Needs to be called before GPU_batch_draw_advanced. */ -void GPU_batch_bind(GPUBatch *); + +/* Will only work after setting the batch program. */ +/* TODO(fclem) Theses needs to be replaced by GPU_shader_uniform_* with explicit shader. */ +#define GPU_batch_uniform_1i(batch, name, x) GPU_shader_uniform_1i((batch)->shader, name, x); +#define GPU_batch_uniform_1b(batch, name, x) GPU_shader_uniform_1b((batch)->shader, name, x); +#define GPU_batch_uniform_1f(batch, name, x) GPU_shader_uniform_1f((batch)->shader, name, x); +#define GPU_batch_uniform_2f(batch, name, x, y) GPU_shader_uniform_2f((batch)->shader, name, x, y); +#define GPU_batch_uniform_3f(batch, name, x, y, z) \ + GPU_shader_uniform_3f((batch)->shader, name, x, y, z); +#define GPU_batch_uniform_4f(batch, name, x, y, z, w) \ + GPU_shader_uniform_4f((batch)->shader, name, x, y, z, w); +#define GPU_batch_uniform_2fv(batch, name, val) GPU_shader_uniform_2fv((batch)->shader, name, val); +#define GPU_batch_uniform_3fv(batch, name, val) GPU_shader_uniform_3fv((batch)->shader, name, val); +#define GPU_batch_uniform_4fv(batch, name, val) GPU_shader_uniform_4fv((batch)->shader, name, val); +#define GPU_batch_uniform_2fv_array(batch, name, len, val) \ + GPU_shader_uniform_2fv_array((batch)->shader, name, len, val); +#define GPU_batch_uniform_4fv_array(batch, name, len, val) \ + GPU_shader_uniform_4fv_array((batch)->shader, name, len, val); +#define GPU_batch_uniform_mat4(batch, name, val) \ + GPU_shader_uniform_mat4((batch)->shader, name, val); + +void GPU_batch_draw(GPUBatch *batch); +void GPU_batch_draw_range(GPUBatch *batch, int v_first, int v_count); +void GPU_batch_draw_instanced(GPUBatch *batch, int i_count); + /* This does not bind/unbind shader and does not call GPU_matrix_bind() */ void GPU_batch_draw_advanced(GPUBatch *, int v_first, int v_count, int i_first, int i_count); -/* Does not even need batch */ -void GPU_draw_primitive(GPUPrimType, int v_count); - #if 0 /* future plans */ /* Can multiple batches share a GPUVertBuf? Use ref count? */ @@ -199,19 +189,6 @@ GPUBatch *create_BatchInGeneral(GPUPrimType, VertexBufferStuff, ElementListStuff #endif /* future plans */ -/** - * #GPUDrawList is an API to do lots of similar draw-calls very fast using multi-draw-indirect. - * There is a fallback if the feature is not supported. - */ -typedef struct GPUDrawList GPUDrawList; - -GPUDrawList *GPU_draw_list_create(int length); -void GPU_draw_list_discard(GPUDrawList *list); -void GPU_draw_list_init(GPUDrawList *list, GPUBatch *batch); -void GPU_draw_list_command_add( - GPUDrawList *list, int v_first, int v_count, int i_first, int i_count); -void GPU_draw_list_submit(GPUDrawList *list); - void gpu_batch_init(void); void gpu_batch_exit(void); diff --git a/source/blender/gpu/GPU_batch_presets.h b/source/blender/gpu/GPU_batch_presets.h index 1674cf776db..19f200fecbf 100644 --- a/source/blender/gpu/GPU_batch_presets.h +++ b/source/blender/gpu/GPU_batch_presets.h @@ -43,14 +43,13 @@ struct GPUBatch *GPU_batch_preset_panel_drag_widget(const float pixelsize, const float col_dark[4], const float width) ATTR_WARN_UNUSED_RESULT; +struct GPUBatch *GPU_batch_preset_quad(void); + void gpu_batch_presets_init(void); void gpu_batch_presets_register(struct GPUBatch *preset_batch); bool gpu_batch_presets_unregister(struct GPUBatch *preset_batch); -void gpu_batch_presets_reset(void); void gpu_batch_presets_exit(void); -void GPU_batch_presets_reset(void); - #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/GPU_context.h b/source/blender/gpu/GPU_context.h index e3d47cfe084..be7e604fb96 100644 --- a/source/blender/gpu/GPU_context.h +++ b/source/blender/gpu/GPU_context.h @@ -27,7 +27,6 @@ #include "GPU_batch.h" #include "GPU_common.h" -#include "GPU_shader_interface.h" #ifdef __cplusplus extern "C" { diff --git a/source/blender/gpu/GPU_debug.h b/source/blender/gpu/GPU_debug.h index be822056678..09dc02c0fc6 100644 --- a/source/blender/gpu/GPU_debug.h +++ b/source/blender/gpu/GPU_debug.h @@ -30,9 +30,6 @@ extern "C" { /* prints something if debug mode is active only */ void GPU_print_error_debug(const char *str); -/* inserts a debug marker message for the debug context messaging system */ -void GPU_string_marker(const char *str); - #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/intern/gpu_attr_binding_private.h b/source/blender/gpu/GPU_drawlist.h index 4d359343c38..27f70da8cf8 100644 --- a/source/blender/gpu/intern/gpu_attr_binding_private.h +++ b/source/blender/gpu/GPU_drawlist.h @@ -13,32 +13,33 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * The Original Code is Copyright (C) 2016 by Mike Erwin. + * The Original Code is Copyright (C) 2020 Blender Foundation. * All rights reserved. */ /** \file * \ingroup gpu * - * GPU vertex attribute binding + * GPUDrawList is an API to do lots of similar draw-calls very fast using + * multi-draw-indirect. There is a fallback if the feature is not supported. */ #pragma once -#include "GPU_shader_interface.h" -#include "GPU_vertex_format.h" - #ifdef __cplusplus extern "C" { #endif -/* TODO(fclem) remove, use shaderface directly. */ -void AttrBinding_clear(GPUAttrBinding *binding); +struct GPUBatch; + +typedef void *GPUDrawList; /* Opaque pointer. */ + +/* Create a list with at least length drawcalls. Length can affect performance. */ +GPUDrawList GPU_draw_list_create(int length); +void GPU_draw_list_discard(GPUDrawList list); -void get_attr_locations(const GPUVertFormat *format, - GPUAttrBinding *binding, - const GPUShaderInterface *shaderface); -uint read_attr_location(const GPUAttrBinding *binding, uint a_idx); +void GPU_draw_list_append(GPUDrawList list, GPUBatch *batch, int i_first, int i_count); +void GPU_draw_list_submit(GPUDrawList list); #ifdef __cplusplus } diff --git a/source/blender/gpu/GPU_element.h b/source/blender/gpu/GPU_element.h index 3d5195b12fc..5cf85b4ea0e 100644 --- a/source/blender/gpu/GPU_element.h +++ b/source/blender/gpu/GPU_element.h @@ -54,6 +54,8 @@ typedef struct GPUIndexBuf { }; } GPUIndexBuf; +GPUIndexBuf *GPU_indexbuf_calloc(void); + void GPU_indexbuf_use(GPUIndexBuf *); uint GPU_indexbuf_size_get(const GPUIndexBuf *); diff --git a/source/blender/gpu/GPU_extensions.h b/source/blender/gpu/GPU_extensions.h index 2ce6e458378..18ac2265cc4 100644 --- a/source/blender/gpu/GPU_extensions.h +++ b/source/blender/gpu/GPU_extensions.h @@ -40,7 +40,6 @@ int GPU_max_color_texture_samples(void); int GPU_max_cube_map_size(void); int GPU_max_ubo_binds(void); int GPU_max_ubo_size(void); -float GPU_max_line_width(void); void GPU_get_dfdy_factors(float fac[2]); bool GPU_arb_base_instance_is_supported(void); bool GPU_arb_texture_cube_map_array_is_supported(void); diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h index 9dc07fefd4e..e6648c69de7 100644 --- a/source/blender/gpu/GPU_framebuffer.h +++ b/source/blender/gpu/GPU_framebuffer.h @@ -19,6 +19,13 @@ /** \file * \ingroup gpu + * + * GPU Framebuffer + * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice + * multiple FBO's may be created. + * - actual FBO creation & config is deferred until GPU_framebuffer_bind or + * GPU_framebuffer_check_valid to allow creation & config while another + * opengl context is bound (since FBOs are not shared between ogl contexts). */ #pragma once @@ -41,32 +48,28 @@ typedef enum eGPUFrameBufferBits { } eGPUFrameBufferBits; typedef enum eGPUBackBuffer { - GPU_BACKBUFFER = 0, + GPU_BACKBUFFER_LEFT = 0, GPU_BACKBUFFER_RIGHT, - GPU_BACKBUFFER_LEFT, } eGPUBackBuffer; -typedef struct GPUFrameBuffer GPUFrameBuffer; -typedef struct GPUOffScreen GPUOffScreen; +/** Opaque pointer hiding blender::gpu::FrameBuffer. */ +typedef struct GPUFrameBuffer { + void *dummy; +} GPUFrameBuffer; -/* GPU Framebuffer - * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice - * multiple FBO's may be created, to get around limitations on the number - * of attached textures and the dimension requirements. - * - actual FBO creation & config is deferred until GPU_framebuffer_bind or - * GPU_framebuffer_check_valid to allow creation & config while another - * opengl context is bound (since FBOs are not shared between ogl contexts). - */ +typedef struct GPUOffScreen GPUOffScreen; -GPUFrameBuffer *GPU_framebuffer_create(void); +GPUFrameBuffer *GPU_framebuffer_create(const char *name); void GPU_framebuffer_free(GPUFrameBuffer *fb); void GPU_framebuffer_bind(GPUFrameBuffer *fb); +void GPU_framebuffer_bind_no_srgb(GPUFrameBuffer *fb); void GPU_framebuffer_restore(void); bool GPU_framebuffer_bound(GPUFrameBuffer *fb); bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]); GPUFrameBuffer *GPU_framebuffer_active_get(void); +GPUFrameBuffer *GPU_framebuffer_back_get(void); #define GPU_FRAMEBUFFER_FREE_SAFE(fb) \ do { \ @@ -79,13 +82,10 @@ GPUFrameBuffer *GPU_framebuffer_active_get(void); /* Framebuffer setup : You need to call GPU_framebuffer_bind for these * to be effective. */ -void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int mip); -void GPU_framebuffer_texture_layer_attach( - GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int layer, int mip); -void GPU_framebuffer_texture_cubeface_attach( - GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int face, int mip); +void GPU_framebuffer_texture_attach_ex(GPUFrameBuffer *gpu_fb, + GPUAttachment attachement, + int slot); void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, struct GPUTexture *tex); -void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, struct GPUTexture *tex, int type); /** * How to use #GPU_framebuffer_ensure_config(). @@ -109,7 +109,7 @@ void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, struct GPUTexture * #define GPU_framebuffer_ensure_config(_fb, ...) \ do { \ if (*(_fb) == NULL) { \ - *(_fb) = GPU_framebuffer_create(); \ + *(_fb) = GPU_framebuffer_create(#_fb); \ } \ GPUAttachment config[] = __VA_ARGS__; \ GPU_framebuffer_config_array(*(_fb), config, (sizeof(config) / sizeof(GPUAttachment))); \ @@ -150,9 +150,17 @@ void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *confi _tex, _face, _mip, \ } +void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip); +void GPU_framebuffer_texture_layer_attach( + GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip); +void GPU_framebuffer_texture_cubeface_attach( + GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip); + /* Framebuffer operations */ void GPU_framebuffer_viewport_set(GPUFrameBuffer *fb, int x, int y, int w, int h); +void GPU_framebuffer_viewport_get(GPUFrameBuffer *fb, int r_viewport[4]); +void GPU_framebuffer_viewport_reset(GPUFrameBuffer *fb); void GPU_framebuffer_clear(GPUFrameBuffer *fb, eGPUFrameBufferBits buffers, @@ -224,7 +232,6 @@ void GPU_offscreen_viewport_data_get(GPUOffScreen *ofs, void GPU_clear_color(float red, float green, float blue, float alpha); void GPU_clear_depth(float depth); -void GPU_clear(eGPUFrameBufferBits flags); void GPU_frontbuffer_read_pixels( int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data); diff --git a/source/blender/gpu/GPU_immediate.h b/source/blender/gpu/GPU_immediate.h index 41d4f5d28d3..6057770d2d9 100644 --- a/source/blender/gpu/GPU_immediate.h +++ b/source/blender/gpu/GPU_immediate.h @@ -29,7 +29,6 @@ #include "GPU_immediate_util.h" #include "GPU_primitive.h" #include "GPU_shader.h" -#include "GPU_shader_interface.h" #include "GPU_texture.h" #include "GPU_vertex_format.h" @@ -103,13 +102,11 @@ void immVertex2iv(uint attr_id, const int data[2]); /* Provide uniform values that don't change for the entire draw call. */ void immUniform1i(const char *name, int x); -void immUniform4iv(const char *name, const int data[4]); void immUniform1f(const char *name, float x); void immUniform2f(const char *name, float x, float y); void immUniform2fv(const char *name, const float data[2]); void immUniform3f(const char *name, float x, float y, float z); void immUniform3fv(const char *name, const float data[3]); -void immUniformArray3fv(const char *name, const float *data, int count); void immUniform4f(const char *name, float x, float y, float z, float w); void immUniform4fv(const char *name, const float data[4]); void immUniformArray4fv(const char *bare_name, const float *data, int count); diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index b8957ff1819..680e717e615 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -39,7 +39,7 @@ struct GPUNode; struct GPUNodeLink; struct GPUNodeStack; struct GPUTexture; -struct GPUUniformBuffer; +struct GPUUniformBuf; struct Image; struct ImageUser; struct ListBase; @@ -112,15 +112,6 @@ typedef enum eGPUMatFlag { GPU_MATFLAG_BARYCENTRIC = (1 << 4), } eGPUMatFlag; -typedef enum eGPUBlendMode { - GPU_BLEND_SOLID = 0, - GPU_BLEND_ADD = 1, - GPU_BLEND_ALPHA = 2, - GPU_BLEND_CLIP = 4, - GPU_BLEND_ALPHA_SORT = 8, - GPU_BLEND_ALPHA_TO_COVERAGE = 16, -} eGPUBlendMode; - typedef struct GPUNodeStack { eGPUType type; float vec[4]; @@ -167,10 +158,10 @@ bool GPU_stack_link(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out, ...); -GPUNodeLink *GPU_uniformbuffer_link_out(struct GPUMaterial *mat, - struct bNode *node, - struct GPUNodeStack *stack, - const int index); +GPUNodeLink *GPU_uniformbuf_link_out(struct GPUMaterial *mat, + struct bNode *node, + struct GPUNodeStack *stack, + const int index); void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link); @@ -178,9 +169,9 @@ void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3], const short *falloff_type, const float *sharpness); -struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, - int sample_len, - struct GPUTexture **tex_profile); +struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material, + int sample_len, + struct GPUTexture **tex_profile); /* High level functions to create and use GPU materials */ GPUMaterial *GPU_material_from_nodetree_find(struct ListBase *gpumaterials, @@ -210,9 +201,9 @@ struct GPUShader *GPU_material_get_shader(GPUMaterial *material); struct Material *GPU_material_get_material(GPUMaterial *material); eGPUMaterialStatus GPU_material_status(GPUMaterial *mat); -struct GPUUniformBuffer *GPU_material_uniform_buffer_get(GPUMaterial *material); +struct GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material); void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs); -struct GPUUniformBuffer *GPU_material_create_sss_profile_ubo(void); +struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void); bool GPU_material_has_surface_output(GPUMaterial *mat); bool GPU_material_has_volume_output(GPUMaterial *mat); diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h index 7b94a535a30..aad6ae9e2ba 100644 --- a/source/blender/gpu/GPU_matrix.h +++ b/source/blender/gpu/GPU_matrix.h @@ -29,7 +29,7 @@ extern "C" { #endif -struct GPUShaderInterface; +struct GPUShader; void GPU_matrix_reset(void); /* to Identity transform & empty stack */ @@ -147,7 +147,7 @@ const float (*GPU_matrix_normal_get(float m[3][3]))[3]; const float (*GPU_matrix_normal_inverse_get(float m[3][3]))[3]; /* set uniform values for currently bound shader */ -void GPU_matrix_bind(const struct GPUShaderInterface *); +void GPU_matrix_bind(struct GPUShader *shader); bool GPU_matrix_dirty_get(void); /* since last bind */ /* own working polygon offset */ diff --git a/source/blender/gpu/GPU_primitive.h b/source/blender/gpu/GPU_primitive.h index e910e81fac1..781a10f3636 100644 --- a/source/blender/gpu/GPU_primitive.h +++ b/source/blender/gpu/GPU_primitive.h @@ -56,8 +56,11 @@ typedef enum { GPU_PRIM_CLASS_ANY = GPU_PRIM_CLASS_POINT | GPU_PRIM_CLASS_LINE | GPU_PRIM_CLASS_SURFACE, } GPUPrimClass; -GPUPrimClass GPU_primtype_class(GPUPrimType); -bool GPU_primtype_belongs_to_class(GPUPrimType, GPUPrimClass); +/** + * TODO Improve error checking by validating that the shader is suited for this primitive type. + * GPUPrimClass GPU_primtype_class(GPUPrimType); + * bool GPU_primtype_belongs_to_class(GPUPrimType, GPUPrimClass); + **/ #ifdef __cplusplus } diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index f782742ae53..33fef266c42 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -27,14 +27,12 @@ extern "C" { #endif -typedef struct GPUShader GPUShader; -struct GPUShaderInterface; struct GPUTexture; -struct GPUUniformBuffer; +struct GPUUniformBuf; +struct GPUVertBuf; -/* GPU Shader - * - only for fragment shaders now - * - must call texture bind before setting a texture as uniform! */ +/** Opaque type hidding blender::gpu::Shader */ +typedef struct GPUShader GPUShader; typedef enum eGPUShaderTFBType { GPU_SHADER_TFB_NONE = 0, /* Transform feedback unsupported. */ @@ -63,17 +61,16 @@ GPUShader *GPU_shader_create_ex(const char *vertexcode, const char **tf_names, const int tf_count, const char *shader_name); -GPUShader *GPU_shader_load_from_binary(const char *binary, - const int binary_format, - const int binary_len, - const char *shname); + struct GPU_ShaderCreateFromArray_Params { const char **vert, **geom, **frag, **defs; }; struct GPUShader *GPU_shader_create_from_arrays_impl( - const struct GPU_ShaderCreateFromArray_Params *params); + const struct GPU_ShaderCreateFromArray_Params *params, const char *func, int line); + #define GPU_shader_create_from_arrays(...) \ - GPU_shader_create_from_arrays_impl(&(const struct GPU_ShaderCreateFromArray_Params)__VA_ARGS__) + GPU_shader_create_from_arrays_impl( \ + &(const struct GPU_ShaderCreateFromArray_Params)__VA_ARGS__, __func__, __LINE__) void GPU_shader_free(GPUShader *shader); @@ -81,12 +78,47 @@ void GPU_shader_bind(GPUShader *shader); void GPU_shader_unbind(void); /* Returns true if transform feedback was successfully enabled. */ -bool GPU_shader_transform_feedback_enable(GPUShader *shader, unsigned int vbo_id); +bool GPU_shader_transform_feedback_enable(GPUShader *shader, struct GPUVertBuf *vertbuf); void GPU_shader_transform_feedback_disable(GPUShader *shader); int GPU_shader_get_program(GPUShader *shader); -void GPU_shader_set_srgb_uniform(const struct GPUShaderInterface *interface); +typedef enum { + GPU_UNIFORM_MODEL = 0, /* mat4 ModelMatrix */ + GPU_UNIFORM_VIEW, /* mat4 ViewMatrix */ + GPU_UNIFORM_MODELVIEW, /* mat4 ModelViewMatrix */ + GPU_UNIFORM_PROJECTION, /* mat4 ProjectionMatrix */ + GPU_UNIFORM_VIEWPROJECTION, /* mat4 ViewProjectionMatrix */ + GPU_UNIFORM_MVP, /* mat4 ModelViewProjectionMatrix */ + + GPU_UNIFORM_MODEL_INV, /* mat4 ModelMatrixInverse */ + GPU_UNIFORM_VIEW_INV, /* mat4 ViewMatrixInverse */ + GPU_UNIFORM_MODELVIEW_INV, /* mat4 ModelViewMatrixInverse */ + GPU_UNIFORM_PROJECTION_INV, /* mat4 ProjectionMatrixInverse */ + GPU_UNIFORM_VIEWPROJECTION_INV, /* mat4 ViewProjectionMatrixInverse */ + + GPU_UNIFORM_NORMAL, /* mat3 NormalMatrix */ + GPU_UNIFORM_ORCO, /* vec4 OrcoTexCoFactors[] */ + GPU_UNIFORM_CLIPPLANES, /* vec4 WorldClipPlanes[] */ + + GPU_UNIFORM_COLOR, /* vec4 color */ + GPU_UNIFORM_BASE_INSTANCE, /* int baseInstance */ + GPU_UNIFORM_RESOURCE_CHUNK, /* int resourceChunk */ + GPU_UNIFORM_RESOURCE_ID, /* int resourceId */ + GPU_UNIFORM_SRGB_TRANSFORM, /* bool srgbTarget */ + + GPU_NUM_UNIFORMS, /* Special value, denotes number of builtin uniforms. */ +} GPUUniformBuiltin; + +typedef enum { + GPU_UNIFORM_BLOCK_VIEW = 0, /* viewBlock */ + GPU_UNIFORM_BLOCK_MODEL, /* modelBlock */ + GPU_UNIFORM_BLOCK_INFO, /* infoBlock */ + + GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */ +} GPUUniformBlockBuiltin; + +void GPU_shader_set_srgb_uniform(GPUShader *shader); int GPU_shader_get_uniform(GPUShader *shader, const char *name); int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin); @@ -104,9 +136,20 @@ void GPU_shader_uniform_vector_int( void GPU_shader_uniform_float(GPUShader *shader, int location, float value); void GPU_shader_uniform_int(GPUShader *shader, int location, int value); -int GPU_shader_get_attribute(GPUShader *shader, const char *name); +void GPU_shader_uniform_1i(GPUShader *sh, const char *name, int value); +void GPU_shader_uniform_1b(GPUShader *sh, const char *name, bool value); +void GPU_shader_uniform_1f(GPUShader *sh, const char *name, float value); +void GPU_shader_uniform_2f(GPUShader *sh, const char *name, float x, float y); +void GPU_shader_uniform_3f(GPUShader *sh, const char *name, float x, float y, float z); +void GPU_shader_uniform_4f(GPUShader *sh, const char *name, float x, float y, float z, float w); +void GPU_shader_uniform_2fv(GPUShader *sh, const char *name, const float data[2]); +void GPU_shader_uniform_3fv(GPUShader *sh, const char *name, const float data[3]); +void GPU_shader_uniform_4fv(GPUShader *sh, const char *name, const float data[4]); +void GPU_shader_uniform_mat4(GPUShader *sh, const char *name, const float data[4][4]); +void GPU_shader_uniform_2fv_array(GPUShader *sh, const char *name, int len, const float (*val)[2]); +void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, const float (*val)[4]); -char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len); +int GPU_shader_get_attribute(GPUShader *shader, const char *name); void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear); diff --git a/source/blender/gpu/GPU_shader_interface.h b/source/blender/gpu/GPU_shader_interface.h deleted file mode 100644 index 8aba1236b65..00000000000 --- a/source/blender/gpu/GPU_shader_interface.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 by Mike Erwin. - * All rights reserved. - */ - -/** \file - * \ingroup gpu - * - * GPU shader interface (C --> GLSL) - */ - -#pragma once - -#include "GPU_common.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - GPU_UNIFORM_MODEL = 0, /* mat4 ModelMatrix */ - GPU_UNIFORM_VIEW, /* mat4 ViewMatrix */ - GPU_UNIFORM_MODELVIEW, /* mat4 ModelViewMatrix */ - GPU_UNIFORM_PROJECTION, /* mat4 ProjectionMatrix */ - GPU_UNIFORM_VIEWPROJECTION, /* mat4 ViewProjectionMatrix */ - GPU_UNIFORM_MVP, /* mat4 ModelViewProjectionMatrix */ - - GPU_UNIFORM_MODEL_INV, /* mat4 ModelMatrixInverse */ - GPU_UNIFORM_VIEW_INV, /* mat4 ViewMatrixInverse */ - GPU_UNIFORM_MODELVIEW_INV, /* mat4 ModelViewMatrixInverse */ - GPU_UNIFORM_PROJECTION_INV, /* mat4 ProjectionMatrixInverse */ - GPU_UNIFORM_VIEWPROJECTION_INV, /* mat4 ViewProjectionMatrixInverse */ - - GPU_UNIFORM_NORMAL, /* mat3 NormalMatrix */ - GPU_UNIFORM_ORCO, /* vec4 OrcoTexCoFactors[] */ - GPU_UNIFORM_CLIPPLANES, /* vec4 WorldClipPlanes[] */ - - GPU_UNIFORM_COLOR, /* vec4 color */ - GPU_UNIFORM_BASE_INSTANCE, /* int baseInstance */ - GPU_UNIFORM_RESOURCE_CHUNK, /* int resourceChunk */ - GPU_UNIFORM_RESOURCE_ID, /* int resourceId */ - GPU_UNIFORM_SRGB_TRANSFORM, /* bool srgbTarget */ - - GPU_NUM_UNIFORMS, /* Special value, denotes number of builtin uniforms. */ -} GPUUniformBuiltin; - -typedef enum { - GPU_UNIFORM_BLOCK_VIEW = 0, /* viewBlock */ - GPU_UNIFORM_BLOCK_MODEL, /* modelBlock */ - GPU_UNIFORM_BLOCK_INFO, /* infoBlock */ - - GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */ -} GPUUniformBlockBuiltin; - -typedef struct GPUShaderInput { - uint32_t name_offset; - uint32_t name_hash; - int32_t location; - /** Defined at interface creation or in shader. Only for Samplers, UBOs and Vertex Attribs. */ - int32_t binding; -} GPUShaderInput; - -#define GPU_SHADERINTERFACE_REF_ALLOC_COUNT 16 - -typedef struct GPUShaderInterface { - /** Buffer containing all inputs names separated by '\0'. */ - char *name_buffer; - /** Reference to GPUBatches using this interface */ - struct GPUBatch **batches; - uint batches_len; - /** Input counts. */ - uint attribute_len; - uint ubo_len; - uint uniform_len; - /** Enabled bindpoints that needs to be fed with data. */ - uint16_t enabled_attr_mask; - uint16_t enabled_ubo_mask; - uint64_t enabled_tex_mask; - /** Opengl Location of builtin uniforms. Fast access, no lookup needed. */ - int32_t builtins[GPU_NUM_UNIFORMS]; - int32_t builtin_blocks[GPU_NUM_UNIFORM_BLOCKS]; - /** Flat array. In this order: Attributes, Ubos, Uniforms. */ - GPUShaderInput inputs[0]; -} GPUShaderInterface; - -GPUShaderInterface *GPU_shaderinterface_create(int32_t program_id); -void GPU_shaderinterface_discard(GPUShaderInterface *); - -const GPUShaderInput *GPU_shaderinterface_uniform(const GPUShaderInterface *, const char *name); -int32_t GPU_shaderinterface_uniform_builtin(const GPUShaderInterface *shaderface, - GPUUniformBuiltin builtin); -int32_t GPU_shaderinterface_block_builtin(const GPUShaderInterface *shaderface, - GPUUniformBlockBuiltin builtin); -const GPUShaderInput *GPU_shaderinterface_ubo(const GPUShaderInterface *, const char *name); -const GPUShaderInput *GPU_shaderinterface_attr(const GPUShaderInterface *, const char *name); - -/* keep track of batches using this interface */ -void GPU_shaderinterface_add_batch_ref(GPUShaderInterface *, struct GPUBatch *); -void GPU_shaderinterface_remove_batch_ref(GPUShaderInterface *, struct GPUBatch *); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index 4a2c90e241b..253877bcca0 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -20,90 +20,138 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif +#include "BLI_utildefines.h" -/* These map directly to the GL_ blend functions, to minimize API add as needed*/ -typedef enum eGPUBlendFunction { - GPU_ONE, - GPU_SRC_ALPHA, - GPU_ONE_MINUS_SRC_ALPHA, - GPU_DST_COLOR, - GPU_ZERO, -} eGPUBlendFunction; - -/* These map directly to the GL_ filter functions, to minimize API add as needed*/ -typedef enum eGPUFilterFunction { - GPU_NEAREST, - GPU_LINEAR, -} eGPUFilterFunction; - -typedef enum eGPUFaceCull { +typedef enum eGPUWriteMask { + GPU_WRITE_NONE = 0, + GPU_WRITE_RED = (1 << 0), + GPU_WRITE_GREEN = (1 << 1), + GPU_WRITE_BLUE = (1 << 2), + GPU_WRITE_ALPHA = (1 << 3), + GPU_WRITE_DEPTH = (1 << 4), + GPU_WRITE_STENCIL = (1 << 5), + GPU_WRITE_COLOR = (GPU_WRITE_RED | GPU_WRITE_GREEN | GPU_WRITE_BLUE | GPU_WRITE_ALPHA), +} eGPUWriteMask; + +ENUM_OPERATORS(eGPUWriteMask) + +/** + * Defines the fixed pipeline blending equation. + * SRC is the output color from the shader. + * DST is the color from the framebuffer. + * The blending equation is : + * (SRC * A) + (DST * B). + * The blend mode will modify the A and B parameters. + */ +typedef enum eGPUBlend { + GPU_BLEND_NONE = 0, + /** Premult variants will _NOT_ multiply rgb output by alpha. */ + GPU_BLEND_ALPHA, + GPU_BLEND_ALPHA_PREMULT, + GPU_BLEND_ADDITIVE, + GPU_BLEND_ADDITIVE_PREMULT, + GPU_BLEND_MULTIPLY, + GPU_BLEND_SUBTRACT, + /** Replace logic op: SRC * (1 - DST) + * NOTE: Does not modify alpha. */ + GPU_BLEND_INVERT, + /** Order independent transparency. + * NOTE: Cannot be used as is. Needs special setup (framebuffer, shader ...). */ + GPU_BLEND_OIT, + /** Special blend to add color under and multiply dst color by src alpha. */ + GPU_BLEND_BACKGROUND, + /** Custom blend parameters using dual source blending : SRC0 + SRC1 * DST + * NOTE: Can only be used with _ONE_ Draw Buffer and shader needs to be specialized. */ + GPU_BLEND_CUSTOM, +} eGPUBlend; + +typedef enum eGPUDepthTest { + GPU_DEPTH_NONE = 0, + GPU_DEPTH_ALWAYS, /* Used to draw to the depth buffer without really testing. */ + GPU_DEPTH_LESS, + GPU_DEPTH_LESS_EQUAL, /* Default. */ + GPU_DEPTH_EQUAL, + GPU_DEPTH_GREATER, + GPU_DEPTH_GREATER_EQUAL, +} eGPUDepthTest; + +typedef enum eGPUStencilTest { + GPU_STENCIL_NONE = 0, + GPU_STENCIL_ALWAYS, + GPU_STENCIL_EQUAL, + GPU_STENCIL_NEQUAL, +} eGPUStencilTest; + +typedef enum eGPUStencilOp { + GPU_STENCIL_OP_NONE = 0, + GPU_STENCIL_OP_REPLACE, + /** Special values for stencil shadows. */ + GPU_STENCIL_OP_COUNT_DEPTH_PASS, + GPU_STENCIL_OP_COUNT_DEPTH_FAIL, +} eGPUStencilOp; + +typedef enum eGPUFaceCullTest { GPU_CULL_NONE = 0, /* Culling disabled. */ GPU_CULL_FRONT, GPU_CULL_BACK, -} eGPUFaceCull; +} eGPUFaceCullTest; typedef enum eGPUProvokingVertex { - GPU_VERTEX_FIRST = 0, - GPU_VERTEX_LAST, /* Default */ + GPU_VERTEX_LAST = 0, /* Default. */ + GPU_VERTEX_FIRST = 1, /* Follow Blender loop order. */ } eGPUProvokingVertex; -/* Initialize - * - sets the default Blender opengl state, if in doubt, check - * the contents of this function - * - this is called when starting Blender, for opengl rendering. */ -void GPU_state_init(void); - -void GPU_blend(bool enable); -void GPU_blend_set_func(eGPUBlendFunction sfactor, eGPUBlendFunction dfactor); -void GPU_blend_set_func_separate(eGPUBlendFunction src_rgb, - eGPUBlendFunction dst_rgb, - eGPUBlendFunction src_alpha, - eGPUBlendFunction dst_alpha); -void GPU_face_culling(eGPUFaceCull culling); -void GPU_front_facing(bool invert); +#ifdef __cplusplus +extern "C" { +#endif + +void GPU_blend(eGPUBlend blend); +void GPU_face_culling(eGPUFaceCullTest culling); +void GPU_depth_test(eGPUDepthTest test); +void GPU_stencil_test(eGPUStencilTest test); void GPU_provoking_vertex(eGPUProvokingVertex vert); +void GPU_front_facing(bool invert); void GPU_depth_range(float near, float far); -void GPU_depth_test(bool enable); -bool GPU_depth_test_enabled(void); void GPU_scissor_test(bool enable); void GPU_line_smooth(bool enable); void GPU_line_width(float width); +void GPU_logic_op_xor_set(bool enable); void GPU_point_size(float size); void GPU_polygon_smooth(bool enable); void GPU_program_point_size(bool enable); void GPU_scissor(int x, int y, int width, int height); -void GPU_scissor_get_f(float coords[4]); -void GPU_scissor_get_i(int coords[4]); +void GPU_scissor_get(int coords[4]); void GPU_viewport(int x, int y, int width, int height); void GPU_viewport_size_get_f(float coords[4]); void GPU_viewport_size_get_i(int coords[4]); +void GPU_write_mask(eGPUWriteMask mask); void GPU_color_mask(bool r, bool g, bool b, bool a); void GPU_depth_mask(bool depth); bool GPU_depth_mask_get(void); -void GPU_stencil_mask(uint stencil); void GPU_unpack_row_length_set(uint len); +void GPU_shadow_offset(bool enable); void GPU_clip_distances(int enabled_len); bool GPU_mipmap_enabled(void); +void GPU_state_set(eGPUWriteMask write_mask, + eGPUBlend blend, + eGPUFaceCullTest culling_test, + eGPUDepthTest depth_test, + eGPUStencilTest stencil_test, + eGPUStencilOp stencil_op, + eGPUProvokingVertex provoking_vert); -void GPU_flush(void); -void GPU_finish(void); +void GPU_stencil_reference_set(uint reference); +void GPU_stencil_write_mask_set(uint write_mask); +void GPU_stencil_compare_mask_set(uint compare_mask); -void GPU_logic_op_xor_set(bool enable); +eGPUBlend GPU_blend_get(void); +eGPUDepthTest GPU_depth_test_get(void); +eGPUWriteMask GPU_write_mask_get(void); +uint GPU_stencil_mask_get(void); +eGPUStencilTest GPU_stencil_test_get(void); -/* Attribute push & pop. */ -typedef enum eGPUAttrMask { - GPU_DEPTH_BUFFER_BIT = (1 << 0), - GPU_ENABLE_BIT = (1 << 1), - GPU_SCISSOR_BIT = (1 << 2), - GPU_VIEWPORT_BIT = (1 << 3), - GPU_BLEND_BIT = (1 << 4), -} eGPUAttrMask; - -void gpuPushAttr(eGPUAttrMask mask); -void gpuPopAttr(void); +void GPU_flush(void); +void GPU_finish(void); #ifdef __cplusplus } diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index 7ee7f8fcdec..93865c098b8 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -275,8 +275,10 @@ void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter); void GPU_texture_wrap_mode(GPUTexture *tex, bool use_repeat, bool use_clamp); void GPU_texture_swizzle_set(GPUTexture *tex, const char swizzle[4]); +/* TODO should be private internal functions. */ void GPU_texture_attach_framebuffer(GPUTexture *tex, struct GPUFrameBuffer *fb, int attachment); -int GPU_texture_detach_framebuffer(GPUTexture *tex, struct GPUFrameBuffer *fb); +void GPU_texture_detach_framebuffer(GPUTexture *tex, struct GPUFrameBuffer *fb); +int GPU_texture_framebuffer_attachement_get(GPUTexture *tex, struct GPUFrameBuffer *fb); int GPU_texture_target(const GPUTexture *tex); int GPU_texture_width(const GPUTexture *tex); diff --git a/source/blender/gpu/GPU_uniform_buffer.h b/source/blender/gpu/GPU_uniform_buffer.h new file mode 100644 index 00000000000..4a00dda634d --- /dev/null +++ b/source/blender/gpu/GPU_uniform_buffer.h @@ -0,0 +1,61 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Uniform buffers API. Used to handle many uniforms update at once. + * Make sure that the data structure is compatible with what the implementation expect. + * (see "7.6.2.2 Standard Uniform Block Layout" from the OpenGL spec for more info about std140 + * layout) + * Rule of thumb: Padding to 16bytes, don't use vec3, don't use arrays of anything that is not vec4 + * aligned . + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct ListBase; + +/** Opaque pointer hiding blender::gpu::UniformBuf. */ +typedef struct GPUUniformBuf { + void *dummy; +} GPUUniformBuf; + +GPUUniformBuf *GPU_uniformbuf_create_ex(size_t size, const void *data, const char *name); +GPUUniformBuf *GPU_uniformbuf_create_from_list(struct ListBase *inputs, const char *name); + +#define GPU_uniformbuf_create(size) GPU_uniformbuf_create_ex(size, NULL, __func__); + +void GPU_uniformbuf_free(GPUUniformBuf *ubo); + +void GPU_uniformbuf_update(GPUUniformBuf *ubo, const void *data); + +void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int number); +void GPU_uniformbuf_unbind(GPUUniformBuf *ubo); +void GPU_uniformbuf_unbind_all(void); + +#define GPU_UBO_BLOCK_NAME "nodeTree" + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/GPU_uniformbuffer.h b/source/blender/gpu/GPU_uniformbuffer.h deleted file mode 100644 index e2b2a757fb9..00000000000 --- a/source/blender/gpu/GPU_uniformbuffer.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup gpu - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -struct ListBase; - -typedef struct GPUUniformBuffer GPUUniformBuffer; - -GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_out[256]); -GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(struct ListBase *inputs, char err_out[256]); - -void GPU_uniformbuffer_free(GPUUniformBuffer *ubo); - -void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data); -void GPU_uniformbuffer_dynamic_update(GPUUniformBuffer *ubo_); - -void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number); -void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo); -void GPU_uniformbuffer_unbind_all(void); - -bool GPU_uniformbuffer_is_empty(GPUUniformBuffer *ubo); -bool GPU_uniformbuffer_is_dirty(GPUUniformBuffer *ubo); - -#define GPU_UBO_BLOCK_NAME "nodeTree" - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h index 757255496e0..bd1019bb1f5 100644 --- a/source/blender/gpu/GPU_vertex_buffer.h +++ b/source/blender/gpu/GPU_vertex_buffer.h @@ -59,6 +59,8 @@ typedef struct GPUVertBuf { uint32_t vbo_id; /** Usage hint for GL optimisation. */ GPUUsageType usage; + /** This counter will only avoid freeing the GPUVertBuf, not the data. */ + char handle_refcount; /** Data has been touched and need to be reuploaded to GPU. */ bool dirty; uchar *data; /* NULL indicates data in VRAM (unmapped) */ @@ -73,6 +75,10 @@ GPUVertBuf *GPU_vertbuf_create_with_format_ex(const GPUVertFormat *, GPUUsageTyp void GPU_vertbuf_clear(GPUVertBuf *verts); void GPU_vertbuf_discard(GPUVertBuf *); +/* Avoid GPUVertBuf datablock being free but not its data. */ +void GPU_vertbuf_handle_ref_add(GPUVertBuf *verts); +void GPU_vertbuf_handle_ref_remove(GPUVertBuf *verts); + void GPU_vertbuf_init(GPUVertBuf *, GPUUsageType); void GPU_vertbuf_init_with_format_ex(GPUVertBuf *, const GPUVertFormat *, GPUUsageType); diff --git a/source/blender/gpu/GPU_viewport.h b/source/blender/gpu/GPU_viewport.h index 60b78ecd59b..c3e2f1788b4 100644 --- a/source/blender/gpu/GPU_viewport.h +++ b/source/blender/gpu/GPU_viewport.h @@ -55,8 +55,8 @@ typedef struct ViewportMemoryPool { struct BLI_memblock *views; struct BLI_memblock *passes; struct BLI_memblock *images; - struct GPUUniformBuffer **matrices_ubo; - struct GPUUniformBuffer **obinfos_ubo; + struct GPUUniformBuf **matrices_ubo; + struct GPUUniformBuf **obinfos_ubo; uint ubo_len; } ViewportMemoryPool; diff --git a/source/blender/gpu/intern/gpu_attr_binding.cc b/source/blender/gpu/intern/gpu_attr_binding.cc deleted file mode 100644 index 6cb60884620..00000000000 --- a/source/blender/gpu/intern/gpu_attr_binding.cc +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 by Mike Erwin. - * All rights reserved. - */ - -/** \file - * \ingroup gpu - * - * GPU vertex attribute binding - */ - -#include "GPU_attr_binding.h" -#include "gpu_attr_binding_private.h" -#include <stddef.h> -#include <stdlib.h> - -#if GPU_VERT_ATTR_MAX_LEN != 16 -# error "attribute binding code assumes GPU_VERT_ATTR_MAX_LEN = 16" -#endif - -void AttrBinding_clear(GPUAttrBinding *binding) -{ - binding->loc_bits = 0; - binding->enabled_bits = 0; -} - -uint read_attr_location(const GPUAttrBinding *binding, uint a_idx) -{ -#if TRUST_NO_ONE - assert(a_idx < GPU_VERT_ATTR_MAX_LEN); - assert(binding->enabled_bits & (1 << a_idx)); -#endif - return (binding->loc_bits >> (4 * a_idx)) & 0xF; -} - -static void write_attr_location(GPUAttrBinding *binding, uint a_idx, uint location) -{ -#if TRUST_NO_ONE - assert(a_idx < GPU_VERT_ATTR_MAX_LEN); - assert(location < GPU_VERT_ATTR_MAX_LEN); -#endif - const uint shift = 4 * a_idx; - const uint64_t mask = ((uint64_t)0xF) << shift; - /* overwrite this attr's previous location */ - binding->loc_bits = (binding->loc_bits & ~mask) | (location << shift); - /* mark this attr as enabled */ - binding->enabled_bits |= 1 << a_idx; -} - -void get_attr_locations(const GPUVertFormat *format, - GPUAttrBinding *binding, - const GPUShaderInterface *shaderface) -{ - AttrBinding_clear(binding); - - for (uint a_idx = 0; a_idx < format->attr_len; a_idx++) { - const GPUVertAttr *a = &format->attrs[a_idx]; - for (uint n_idx = 0; n_idx < a->name_len; n_idx++) { - const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); - const GPUShaderInput *input = GPU_shaderinterface_attr(shaderface, name); -#if TRUST_NO_ONE - assert(input != NULL); - /* TODO: make this a recoverable runtime error? - * indicates mismatch between vertex format and program. */ -#endif - write_attr_location(binding, a_idx, input->location); - } - } -} diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh index 24f592f214f..ec60e6b5704 100644 --- a/source/blender/gpu/intern/gpu_backend.hh +++ b/source/blender/gpu/intern/gpu_backend.hh @@ -27,11 +27,30 @@ struct GPUContext; +namespace blender { +namespace gpu { + +class Batch; +class DrawList; +class FrameBuffer; +class Shader; +class UniformBuf; + class GPUBackend { public: virtual ~GPUBackend(){}; + static GPUBackend *get(void); + virtual GPUContext *context_alloc(void *ghost_window) = 0; + + virtual Batch *batch_alloc(void) = 0; + virtual DrawList *drawlist_alloc(int list_length) = 0; + virtual FrameBuffer *framebuffer_alloc(const char *name) = 0; + virtual Shader *shader_alloc(const char *name) = 0; + // virtual Texture *texture_alloc(void) = 0; + virtual UniformBuf *uniformbuf_alloc(int size, const char *name) = 0; }; -GPUBackend *gpu_backend_get(void); +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_batch.cc b/source/blender/gpu/intern/gpu_batch.cc index a6ba4d3d89a..0b0c88a42e2 100644 --- a/source/blender/gpu/intern/gpu_batch.cc +++ b/source/blender/gpu/intern/gpu_batch.cc @@ -26,6 +26,8 @@ #include "MEM_guardedalloc.h" +#include "BLI_math_base.h" + #include "GPU_batch.h" #include "GPU_batch_presets.h" #include "GPU_extensions.h" @@ -33,79 +35,50 @@ #include "GPU_platform.h" #include "GPU_shader.h" -#include "gpu_batch_private.h" +#include "gpu_backend.hh" +#include "gpu_batch_private.hh" #include "gpu_context_private.hh" -#include "gpu_primitive_private.h" -#include "gpu_shader_private.h" +#include "gpu_shader_private.hh" #include "gpu_vertex_format_private.h" +#include "gl_primitive.hh" /* TODO remove */ + #include <limits.h> #include <stdlib.h> #include <string.h> -static GLuint g_default_attr_vbo = 0; - -static void batch_update_program_bindings(GPUBatch *batch, uint i_first); +using namespace blender::gpu; -void GPU_batch_vao_cache_clear(GPUBatch *batch) -{ - if (batch->context == NULL) { - return; - } - if (batch->is_dynamic_vao_count) { - for (int i = 0; i < batch->dynamic_vaos.count; i++) { - if (batch->dynamic_vaos.vao_ids[i]) { - GPU_vao_free(batch->dynamic_vaos.vao_ids[i], batch->context); - } - if (batch->dynamic_vaos.interfaces[i]) { - GPU_shaderinterface_remove_batch_ref( - (GPUShaderInterface *)batch->dynamic_vaos.interfaces[i], batch); - } - } - MEM_freeN((void *)batch->dynamic_vaos.interfaces); - MEM_freeN(batch->dynamic_vaos.vao_ids); - } - else { - for (int i = 0; i < GPU_BATCH_VAO_STATIC_LEN; i++) { - if (batch->static_vaos.vao_ids[i]) { - GPU_vao_free(batch->static_vaos.vao_ids[i], batch->context); - } - if (batch->static_vaos.interfaces[i]) { - GPU_shaderinterface_remove_batch_ref( - (GPUShaderInterface *)batch->static_vaos.interfaces[i], batch); - } - } - } - batch->is_dynamic_vao_count = false; - for (int i = 0; i < GPU_BATCH_VAO_STATIC_LEN; i++) { - batch->static_vaos.vao_ids[i] = 0; - batch->static_vaos.interfaces[i] = NULL; - } - gpu_context_remove_batch(batch->context, batch); - batch->context = NULL; -} +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ -GPUBatch *GPU_batch_calloc(uint count) +GPUBatch *GPU_batch_calloc(void) { - return (GPUBatch *)MEM_callocN(sizeof(GPUBatch) * count, "GPUBatch"); + GPUBatch *batch = GPUBackend::get()->batch_alloc(); + memset(batch, 0, sizeof(*batch)); + return batch; } GPUBatch *GPU_batch_create_ex(GPUPrimType prim_type, GPUVertBuf *verts, GPUIndexBuf *elem, - uint owns_flag) + eGPUBatchFlag owns_flag) { - GPUBatch *batch = GPU_batch_calloc(1); + GPUBatch *batch = GPU_batch_calloc(); GPU_batch_init_ex(batch, prim_type, verts, elem, owns_flag); return batch; } -void GPU_batch_init_ex( - GPUBatch *batch, GPUPrimType prim_type, GPUVertBuf *verts, GPUIndexBuf *elem, uint owns_flag) +void GPU_batch_init_ex(GPUBatch *batch, + GPUPrimType prim_type, + GPUVertBuf *verts, + GPUIndexBuf *elem, + eGPUBatchFlag owns_flag) { -#if TRUST_NO_ONE - assert(verts != NULL); -#endif + BLI_assert(verts != NULL); + /* Do not pass any other flag */ + BLI_assert((owns_flag & ~(GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX)) == 0); batch->verts[0] = verts; for (int v = 1; v < GPU_BATCH_VBO_MAX_LEN; v++) { @@ -115,19 +88,18 @@ void GPU_batch_init_ex( batch->inst[v] = NULL; } batch->elem = elem; - batch->gl_prim_type = convert_prim_type_to_gl(prim_type); - batch->phase = GPU_BATCH_READY_TO_DRAW; - batch->is_dynamic_vao_count = false; - batch->owns_flag = owns_flag; - batch->free_callback = NULL; + batch->prim_type = prim_type; + batch->flag = owns_flag | GPU_BATCH_INIT | GPU_BATCH_DIRTY; + batch->shader = NULL; } /* This will share the VBOs with the new batch. */ void GPU_batch_copy(GPUBatch *batch_dst, GPUBatch *batch_src) { - GPU_batch_init_ex(batch_dst, GPU_PRIM_POINTS, batch_src->verts[0], batch_src->elem, 0); + GPU_batch_init_ex( + batch_dst, GPU_PRIM_POINTS, batch_src->verts[0], batch_src->elem, GPU_BATCH_INVALID); - batch_dst->gl_prim_type = batch_src->gl_prim_type; + batch_dst->prim_type = batch_src->prim_type; for (int v = 1; v < GPU_BATCH_VBO_MAX_LEN; v++) { batch_dst->verts[v] = batch_src->verts[v]; } @@ -135,563 +107,159 @@ void GPU_batch_copy(GPUBatch *batch_dst, GPUBatch *batch_src) void GPU_batch_clear(GPUBatch *batch) { - if (batch->owns_flag & GPU_BATCH_OWNS_INDEX) { + if (batch->flag & GPU_BATCH_OWNS_INDEX) { GPU_indexbuf_discard(batch->elem); } - if (batch->owns_flag & GPU_BATCH_OWNS_INSTANCES) { - GPU_vertbuf_discard(batch->inst[0]); - GPU_VERTBUF_DISCARD_SAFE(batch->inst[1]); - } - if ((batch->owns_flag & ~GPU_BATCH_OWNS_INDEX) != 0) { - for (int v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) { - if (batch->verts[v] == NULL) { - break; + if (batch->flag & GPU_BATCH_OWNS_VBO_ANY) { + for (int v = 0; (v < GPU_BATCH_VBO_MAX_LEN) && batch->verts[v]; v++) { + if (batch->flag & (GPU_BATCH_OWNS_VBO << v)) { + GPU_VERTBUF_DISCARD_SAFE(batch->verts[v]); } - if (batch->owns_flag & (1 << v)) { - GPU_vertbuf_discard(batch->verts[v]); + } + } + if (batch->flag & GPU_BATCH_OWNS_INST_VBO_ANY) { + for (int v = 0; (v < GPU_BATCH_INST_VBO_MAX_LEN) && batch->inst[v]; v++) { + if (batch->flag & (GPU_BATCH_OWNS_INST_VBO << v)) { + GPU_VERTBUF_DISCARD_SAFE(batch->inst[v]); } } } - GPU_batch_vao_cache_clear(batch); - batch->phase = GPU_BATCH_UNUSED; + batch->flag = GPU_BATCH_INVALID; } void GPU_batch_discard(GPUBatch *batch) { - if (batch->free_callback) { - batch->free_callback(batch, batch->callback_data); - } - GPU_batch_clear(batch); - MEM_freeN(batch); -} -void GPU_batch_callback_free_set(GPUBatch *batch, - void (*callback)(GPUBatch *, void *), - void *user_data) -{ - batch->free_callback = callback; - batch->callback_data = user_data; + delete static_cast<Batch *>(batch); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Buffers Management + * \{ */ + +/* NOTE: Override ONLY the first instance vbo (and free them if owned). */ void GPU_batch_instbuf_set(GPUBatch *batch, GPUVertBuf *inst, bool own_vbo) { -#if TRUST_NO_ONE - assert(inst != NULL); -#endif - /* redo the bindings */ - GPU_batch_vao_cache_clear(batch); + BLI_assert(inst); + batch->flag |= GPU_BATCH_DIRTY; - if (batch->inst[0] != NULL && (batch->owns_flag & GPU_BATCH_OWNS_INSTANCES)) { + if (batch->inst[0] && (batch->flag & GPU_BATCH_OWNS_INST_VBO)) { GPU_vertbuf_discard(batch->inst[0]); - GPU_VERTBUF_DISCARD_SAFE(batch->inst[1]); } batch->inst[0] = inst; - if (own_vbo) { - batch->owns_flag |= GPU_BATCH_OWNS_INSTANCES; - } - else { - batch->owns_flag &= ~GPU_BATCH_OWNS_INSTANCES; - } + SET_FLAG_FROM_TEST(batch->flag, own_vbo, GPU_BATCH_OWNS_INST_VBO); } +/* NOTE: Override any previously assigned elem (and free it if owned). */ void GPU_batch_elembuf_set(GPUBatch *batch, GPUIndexBuf *elem, bool own_ibo) { - BLI_assert(elem != NULL); - /* redo the bindings */ - GPU_batch_vao_cache_clear(batch); + BLI_assert(elem); + batch->flag |= GPU_BATCH_DIRTY; - if (batch->elem != NULL && (batch->owns_flag & GPU_BATCH_OWNS_INDEX)) { + if (batch->elem && (batch->flag & GPU_BATCH_OWNS_INDEX)) { GPU_indexbuf_discard(batch->elem); } batch->elem = elem; - if (own_ibo) { - batch->owns_flag |= GPU_BATCH_OWNS_INDEX; - } - else { - batch->owns_flag &= ~GPU_BATCH_OWNS_INDEX; - } + SET_FLAG_FROM_TEST(batch->flag, own_ibo, GPU_BATCH_OWNS_INDEX); } -/* A bit of a quick hack. Should be streamlined as the vbos handling */ int GPU_batch_instbuf_add_ex(GPUBatch *batch, GPUVertBuf *insts, bool own_vbo) { - /* redo the bindings */ - GPU_batch_vao_cache_clear(batch); + BLI_assert(insts); + batch->flag |= GPU_BATCH_DIRTY; for (uint v = 0; v < GPU_BATCH_INST_VBO_MAX_LEN; v++) { if (batch->inst[v] == NULL) { -#if TRUST_NO_ONE /* for now all VertexBuffers must have same vertex_len */ - if (batch->inst[0] != NULL) { - /* Allow for different size of vertex buf (will choose the smallest number of verts). */ - // assert(insts->vertex_len == batch->inst[0]->vertex_len); - assert(own_vbo == ((batch->owns_flag & GPU_BATCH_OWNS_INSTANCES) != 0)); + if (batch->inst[0]) { + /* Allow for different size of vertex buffer (will choose the smallest number of verts). */ + // BLI_assert(insts->vertex_len == batch->inst[0]->vertex_len); } -#endif + batch->inst[v] = insts; - if (own_vbo) { - batch->owns_flag |= GPU_BATCH_OWNS_INSTANCES; - } + SET_FLAG_FROM_TEST(batch->flag, own_vbo, (eGPUBatchFlag)(GPU_BATCH_OWNS_INST_VBO << v)); return v; } } - /* we only make it this far if there is no room for another GPUVertBuf */ -#if TRUST_NO_ONE - assert(false); -#endif + BLI_assert(0 && "Not enough Instance VBO slot in batch"); return -1; } /* Returns the index of verts in the batch. */ int GPU_batch_vertbuf_add_ex(GPUBatch *batch, GPUVertBuf *verts, bool own_vbo) { - /* redo the bindings */ - GPU_batch_vao_cache_clear(batch); + BLI_assert(verts); + batch->flag |= GPU_BATCH_DIRTY; for (uint v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) { if (batch->verts[v] == NULL) { -#if TRUST_NO_ONE /* for now all VertexBuffers must have same vertex_len */ if (batch->verts[0] != NULL) { - assert(verts->vertex_len == batch->verts[0]->vertex_len); + BLI_assert(verts->vertex_len == batch->verts[0]->vertex_len); } -#endif batch->verts[v] = verts; - /* TODO: mark dirty so we can keep attribute bindings up-to-date */ - if (own_vbo) { - batch->owns_flag |= (1 << v); - } + SET_FLAG_FROM_TEST(batch->flag, own_vbo, (eGPUBatchFlag)(GPU_BATCH_OWNS_VBO << v)); return v; } } - /* we only make it this far if there is no room for another GPUVertBuf */ -#if TRUST_NO_ONE - assert(false); -#endif + BLI_assert(0 && "Not enough VBO slot in batch"); return -1; } -static GLuint batch_vao_get(GPUBatch *batch) -{ - /* Search through cache */ - if (batch->is_dynamic_vao_count) { - for (int i = 0; i < batch->dynamic_vaos.count; i++) { - if (batch->dynamic_vaos.interfaces[i] == batch->interface) { - return batch->dynamic_vaos.vao_ids[i]; - } - } - } - else { - for (int i = 0; i < GPU_BATCH_VAO_STATIC_LEN; i++) { - if (batch->static_vaos.interfaces[i] == batch->interface) { - return batch->static_vaos.vao_ids[i]; - } - } - } - - /* Set context of this batch. - * It will be bound to it until GPU_batch_vao_cache_clear is called. - * Until then it can only be drawn with this context. */ - if (batch->context == NULL) { - batch->context = GPU_context_active_get(); - gpu_context_add_batch(batch->context, batch); - } -#if TRUST_NO_ONE - else { - /* Make sure you are not trying to draw this batch in another context. */ - assert(batch->context == GPU_context_active_get()); - } -#endif - - /* Cache miss, time to add a new entry! */ - GLuint new_vao = 0; - if (!batch->is_dynamic_vao_count) { - int i; /* find first unused slot */ - for (i = 0; i < GPU_BATCH_VAO_STATIC_LEN; i++) { - if (batch->static_vaos.vao_ids[i] == 0) { - break; - } - } - - if (i < GPU_BATCH_VAO_STATIC_LEN) { - batch->static_vaos.interfaces[i] = batch->interface; - batch->static_vaos.vao_ids[i] = new_vao = GPU_vao_alloc(); - } - else { - /* Not enough place switch to dynamic. */ - batch->is_dynamic_vao_count = true; - /* Erase previous entries, they will be added back if drawn again. */ - for (int j = 0; j < GPU_BATCH_VAO_STATIC_LEN; j++) { - GPU_shaderinterface_remove_batch_ref( - (GPUShaderInterface *)batch->static_vaos.interfaces[j], batch); - GPU_vao_free(batch->static_vaos.vao_ids[j], batch->context); - } - /* Init dynamic arrays and let the branch below set the values. */ - batch->dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT; - batch->dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_callocN( - batch->dynamic_vaos.count * sizeof(GPUShaderInterface *), "dyn vaos interfaces"); - batch->dynamic_vaos.vao_ids = (GLuint *)MEM_callocN( - batch->dynamic_vaos.count * sizeof(GLuint), "dyn vaos ids"); - } - } - - if (batch->is_dynamic_vao_count) { - int i; /* find first unused slot */ - for (i = 0; i < batch->dynamic_vaos.count; i++) { - if (batch->dynamic_vaos.vao_ids[i] == 0) { - break; - } - } - - if (i == batch->dynamic_vaos.count) { - /* Not enough place, realloc the array. */ - i = batch->dynamic_vaos.count; - batch->dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT; - batch->dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_recallocN( - (void *)batch->dynamic_vaos.interfaces, - sizeof(GPUShaderInterface *) * batch->dynamic_vaos.count); - batch->dynamic_vaos.vao_ids = (GLuint *)MEM_recallocN( - batch->dynamic_vaos.vao_ids, sizeof(GLuint) * batch->dynamic_vaos.count); - } - batch->dynamic_vaos.interfaces[i] = batch->interface; - batch->dynamic_vaos.vao_ids[i] = new_vao = GPU_vao_alloc(); - } - - GPU_shaderinterface_add_batch_ref((GPUShaderInterface *)batch->interface, batch); - -#if TRUST_NO_ONE - assert(new_vao != 0); -#endif - - /* We just got a fresh VAO we need to initialize it. */ - glBindVertexArray(new_vao); - batch_update_program_bindings(batch, 0); - glBindVertexArray(0); - - return new_vao; -} +/** \} */ -void GPU_batch_set_shader_no_bind(GPUBatch *batch, GPUShader *shader) -{ -#if TRUST_NO_ONE - assert(glIsProgram(shader->program)); - assert(batch->program_in_use == 0); -#endif - batch->interface = shader->interface; - batch->program = shader->program; - batch->vao_id = batch_vao_get(batch); -} +/* -------------------------------------------------------------------- */ +/** \name Uniform setters + * + * TODO(fclem) port this to GPUShader. + * \{ */ void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader) { - GPU_batch_set_shader_no_bind(batch, shader); - GPU_batch_program_use_begin(batch); /* hack! to make Batch_Uniform* simpler */ -} - -void gpu_batch_remove_interface_ref(GPUBatch *batch, const GPUShaderInterface *interface) -{ - if (batch->is_dynamic_vao_count) { - for (int i = 0; i < batch->dynamic_vaos.count; i++) { - if (batch->dynamic_vaos.interfaces[i] == interface) { - GPU_vao_free(batch->dynamic_vaos.vao_ids[i], batch->context); - batch->dynamic_vaos.vao_ids[i] = 0; - batch->dynamic_vaos.interfaces[i] = NULL; - break; /* cannot have duplicates */ - } - } - } - else { - int i; - for (i = 0; i < GPU_BATCH_VAO_STATIC_LEN; i++) { - if (batch->static_vaos.interfaces[i] == interface) { - GPU_vao_free(batch->static_vaos.vao_ids[i], batch->context); - batch->static_vaos.vao_ids[i] = 0; - batch->static_vaos.interfaces[i] = NULL; - break; /* cannot have duplicates */ - } - } - } -} - -static void create_bindings(GPUVertBuf *verts, - const GPUShaderInterface *interface, - uint16_t *attr_mask, - uint v_first, - const bool use_instancing) -{ - const GPUVertFormat *format = &verts->format; - - const uint attr_len = format->attr_len; - uint stride = format->stride; - uint offset = 0; - - GPU_vertbuf_use(verts); - - for (uint a_idx = 0; a_idx < attr_len; a_idx++) { - const GPUVertAttr *a = &format->attrs[a_idx]; - - if (format->deinterleaved) { - offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].sz) * verts->vertex_len; - stride = a->sz; - } - else { - offset = a->offset; - } - - const GLvoid *pointer = (const GLubyte *)0 + offset + v_first * stride; - const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type)); - - for (uint n_idx = 0; n_idx < a->name_len; n_idx++) { - const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); - const GPUShaderInput *input = GPU_shaderinterface_attr(interface, name); - - if (input == NULL) { - continue; - } - - *attr_mask &= ~(1 << input->location); - - if (a->comp_len == 16 || a->comp_len == 12 || a->comp_len == 8) { - BLI_assert(a->fetch_mode == GPU_FETCH_FLOAT); - BLI_assert(a->comp_type == GPU_COMP_F32); - for (int i = 0; i < a->comp_len / 4; i++) { - glEnableVertexAttribArray(input->location + i); - glVertexAttribDivisor(input->location + i, (use_instancing) ? 1 : 0); - glVertexAttribPointer( - input->location + i, 4, type, GL_FALSE, stride, (const GLubyte *)pointer + i * 16); - } - } - else { - glEnableVertexAttribArray(input->location); - glVertexAttribDivisor(input->location, (use_instancing) ? 1 : 0); - - switch (a->fetch_mode) { - case GPU_FETCH_FLOAT: - case GPU_FETCH_INT_TO_FLOAT: - glVertexAttribPointer(input->location, a->comp_len, type, GL_FALSE, stride, pointer); - break; - case GPU_FETCH_INT_TO_FLOAT_UNIT: - glVertexAttribPointer(input->location, a->comp_len, type, GL_TRUE, stride, pointer); - break; - case GPU_FETCH_INT: - glVertexAttribIPointer(input->location, a->comp_len, type, stride, pointer); - break; - } - } - } - } -} - -static void batch_update_program_bindings(GPUBatch *batch, uint i_first) -{ - uint16_t attr_mask = batch->interface->enabled_attr_mask; - - /* Reverse order so first VBO'S have more prevalence (in term of attribute override). */ - for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; v--) { - if (batch->verts[v] != NULL) { - create_bindings(batch->verts[v], batch->interface, &attr_mask, 0, false); - } - } - - for (int v = GPU_BATCH_INST_VBO_MAX_LEN - 1; v > -1; v--) { - if (batch->inst[v]) { - create_bindings(batch->inst[v], batch->interface, &attr_mask, i_first, true); - } - } - - if (attr_mask != 0 && GLEW_ARB_vertex_attrib_binding) { - for (uint16_t mask = 1, a = 0; a < 16; a++, mask <<= 1) { - if (attr_mask & mask) { - /* This replaces glVertexAttrib4f(a, 0.0f, 0.0f, 0.0f, 1.0f); with a more modern style. - * Fix issues for some drivers (see T75069). */ - glBindVertexBuffer(a, g_default_attr_vbo, (intptr_t)0, (intptr_t)0); - - glEnableVertexAttribArray(a); - glVertexAttribFormat(a, 4, GL_FLOAT, GL_FALSE, 0); - glVertexAttribBinding(a, a); - } - } - } - - if (batch->elem) { - GPU_indexbuf_use(batch->elem); - } -} - -void GPU_batch_program_use_begin(GPUBatch *batch) -{ - /* NOTE: use_program & done_using_program are fragile, depend on staying in sync with - * the GL context's active program. - * use_program doesn't mark other programs as "not used". */ - /* TODO: make not fragile (somehow) */ - - if (!batch->program_in_use) { - glUseProgram(batch->program); - batch->program_in_use = true; - } -} - -void GPU_batch_program_use_end(GPUBatch *batch) -{ - if (batch->program_in_use) { -#if PROGRAM_NO_OPTI - glUseProgram(0); -#endif - batch->program_in_use = false; - } -} - -#if TRUST_NO_ONE -# define GET_UNIFORM \ - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(batch->interface, name); \ - assert(uniform); -#else -# define GET_UNIFORM \ - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(batch->interface, name); -#endif - -void GPU_batch_uniform_1ui(GPUBatch *batch, const char *name, uint value) -{ - GET_UNIFORM - glUniform1ui(uniform->location, value); -} - -void GPU_batch_uniform_1i(GPUBatch *batch, const char *name, int value) -{ - GET_UNIFORM - glUniform1i(uniform->location, value); -} - -void GPU_batch_uniform_1b(GPUBatch *batch, const char *name, bool value) -{ - GET_UNIFORM - glUniform1i(uniform->location, value ? GL_TRUE : GL_FALSE); -} - -void GPU_batch_uniform_2f(GPUBatch *batch, const char *name, float x, float y) -{ - GET_UNIFORM - glUniform2f(uniform->location, x, y); -} - -void GPU_batch_uniform_3f(GPUBatch *batch, const char *name, float x, float y, float z) -{ - GET_UNIFORM - glUniform3f(uniform->location, x, y, z); -} - -void GPU_batch_uniform_4f(GPUBatch *batch, const char *name, float x, float y, float z, float w) -{ - GET_UNIFORM - glUniform4f(uniform->location, x, y, z, w); -} - -void GPU_batch_uniform_1f(GPUBatch *batch, const char *name, float x) -{ - GET_UNIFORM - glUniform1f(uniform->location, x); -} - -void GPU_batch_uniform_2fv(GPUBatch *batch, const char *name, const float data[2]) -{ - GET_UNIFORM - glUniform2fv(uniform->location, 1, data); -} - -void GPU_batch_uniform_3fv(GPUBatch *batch, const char *name, const float data[3]) -{ - GET_UNIFORM - glUniform3fv(uniform->location, 1, data); -} - -void GPU_batch_uniform_4fv(GPUBatch *batch, const char *name, const float data[4]) -{ - GET_UNIFORM - glUniform4fv(uniform->location, 1, data); -} - -void GPU_batch_uniform_2fv_array(GPUBatch *batch, - const char *name, - const int len, - const float *data) -{ - GET_UNIFORM - glUniform2fv(uniform->location, len, data); + batch->shader = shader; + GPU_shader_bind(batch->shader); } -void GPU_batch_uniform_4fv_array(GPUBatch *batch, - const char *name, - const int len, - const float *data) -{ - GET_UNIFORM - glUniform4fv(uniform->location, len, data); -} +/** \} */ -void GPU_batch_uniform_mat4(GPUBatch *batch, const char *name, const float data[4][4]) -{ - GET_UNIFORM - glUniformMatrix4fv(uniform->location, 1, GL_FALSE, (const float *)data); -} +/* -------------------------------------------------------------------- */ +/** \name Drawing / Drawcall functions + * \{ */ -static void *elem_offset(const GPUIndexBuf *el, int v_first) +void GPU_batch_draw(GPUBatch *batch) { -#if GPU_TRACK_INDEX_RANGE - if (el->index_type == GPU_INDEX_U16) { - return (GLushort *)0 + v_first + el->index_start; - } -#endif - return (GLuint *)0 + v_first + el->index_start; + GPU_shader_bind(batch->shader); + GPU_batch_draw_advanced(batch, 0, 0, 0, 0); + GPU_shader_unbind(); } -/* Use when drawing with GPU_batch_draw_advanced */ -void GPU_batch_bind(GPUBatch *batch) +void GPU_batch_draw_range(GPUBatch *batch, int v_first, int v_count) { - glBindVertexArray(batch->vao_id); - -#if GPU_TRACK_INDEX_RANGE - /* Can be removed if GL 4.3 is required. */ - if (!GLEW_ARB_ES3_compatibility && batch->elem != NULL) { - GLuint restart_index = (batch->elem->index_type == GPU_INDEX_U16) ? (GLuint)0xFFFF : - (GLuint)0xFFFFFFFF; - glPrimitiveRestartIndex(restart_index); - } -#endif + GPU_shader_bind(batch->shader); + GPU_batch_draw_advanced(batch, v_first, v_count, 0, 0); + GPU_shader_unbind(); } -void GPU_batch_draw(GPUBatch *batch) +/* Draw multiple instance of a batch without having any instance attributes. */ +void GPU_batch_draw_instanced(GPUBatch *batch, int i_count) { -#if TRUST_NO_ONE - assert(batch->phase == GPU_BATCH_READY_TO_DRAW); - assert(batch->verts[0]->vbo_id != 0); -#endif - GPU_batch_program_use_begin(batch); - GPU_matrix_bind(batch->interface); // external call. - GPU_shader_set_srgb_uniform(batch->interface); + BLI_assert(batch->inst[0] == NULL); - GPU_batch_bind(batch); - GPU_batch_draw_advanced(batch, 0, 0, 0, 0); - - GPU_batch_program_use_end(batch); + GPU_shader_bind(batch->shader); + GPU_batch_draw_advanced(batch, 0, 0, 0, i_count); + GPU_shader_unbind(); } -#if GPU_TRACK_INDEX_RANGE -# define BASE_INDEX(el) ((el)->base_index) -# define INDEX_TYPE(el) ((el)->gl_index_type) -#else -# define BASE_INDEX(el) 0 -# define INDEX_TYPE(el) GL_UNSIGNED_INT -#endif - void GPU_batch_draw_advanced(GPUBatch *batch, int v_first, int v_count, int i_first, int i_count) { - BLI_assert(batch->program_in_use); - /* TODO could assert that VAO is bound. */ + BLI_assert(GPU_context_active_get()->shader != NULL); if (v_count == 0) { v_count = (batch->elem) ? batch->elem->index_len : batch->verts[0]->vertex_len; @@ -699,8 +267,8 @@ void GPU_batch_draw_advanced(GPUBatch *batch, int v_first, int v_count, int i_fi if (i_count == 0) { i_count = (batch->inst[0]) ? batch->inst[0]->vertex_len : 1; /* Meh. This is to be able to use different numbers of verts in instance vbos. */ - if (batch->inst[1] && i_count > batch->inst[1]->vertex_len) { - i_count = batch->inst[1]->vertex_len; + if (batch->inst[1] != NULL) { + i_count = min_ii(i_count, batch->inst[1]->vertex_len); } } @@ -709,275 +277,7 @@ void GPU_batch_draw_advanced(GPUBatch *batch, int v_first, int v_count, int i_fi return; } - /* Verify there is enough data do draw. */ - /* TODO(fclem) Nice to have but this is invalid when using procedural draw-calls. - * The right assert would be to check if there is an enabled attribute from each VBO - * and check their length. */ - // BLI_assert(i_first + i_count <= (batch->inst ? batch->inst->vertex_len : INT_MAX)); - // BLI_assert(v_first + v_count <= - // (batch->elem ? batch->elem->index_len : batch->verts[0]->vertex_len)); - -#ifdef __APPLE__ - GLuint vao = 0; -#endif - - if (!GPU_arb_base_instance_is_supported()) { - if (i_first > 0) { -#ifdef __APPLE__ - /** - * There seems to be a nasty bug when drawing using the same VAO reconfiguring. (see T71147) - * We just use a throwaway VAO for that. Note that this is likely to degrade performance. - **/ - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); -#else - /* If using offset drawing with instancing, we must - * use the default VAO and redo bindings. */ - glBindVertexArray(GPU_vao_default()); -#endif - batch_update_program_bindings(batch, i_first); - } - else { - /* Previous call could have bind the default vao - * see above. */ - glBindVertexArray(batch->vao_id); - } - } - - if (batch->elem) { - const GPUIndexBuf *el = batch->elem; - GLenum index_type = INDEX_TYPE(el); - GLint base_index = BASE_INDEX(el); - void *v_first_ofs = elem_offset(el, v_first); - - if (GPU_arb_base_instance_is_supported()) { - glDrawElementsInstancedBaseVertexBaseInstance( - batch->gl_prim_type, v_count, index_type, v_first_ofs, i_count, base_index, i_first); - } - else { - glDrawElementsInstancedBaseVertex( - batch->gl_prim_type, v_count, index_type, v_first_ofs, i_count, base_index); - } - } - else { -#ifdef __APPLE__ - glDisable(GL_PRIMITIVE_RESTART); -#endif - if (GPU_arb_base_instance_is_supported()) { - glDrawArraysInstancedBaseInstance(batch->gl_prim_type, v_first, v_count, i_count, i_first); - } - else { - glDrawArraysInstanced(batch->gl_prim_type, v_first, v_count, i_count); - } -#ifdef __APPLE__ - glEnable(GL_PRIMITIVE_RESTART); -#endif - } - -#ifdef __APPLE__ - if (vao != 0) { - glDeleteVertexArrays(1, &vao); - } -#endif -} - -/* just draw some vertices and let shader place them where we want. */ -void GPU_draw_primitive(GPUPrimType prim_type, int v_count) -{ - /* we cannot draw without vao ... annoying ... */ - glBindVertexArray(GPU_vao_default()); - - GLenum type = convert_prim_type_to_gl(prim_type); - glDrawArrays(type, 0, v_count); - - /* Performance hog if you are drawing with the same vao multiple time. - * Only activate for debugging.*/ - // glBindVertexArray(0); -} - -/* -------------------------------------------------------------------- */ -/** \name Indirect Draw Calls - * \{ */ - -#if 0 -# define USE_MULTI_DRAW_INDIRECT 0 -#else -# define USE_MULTI_DRAW_INDIRECT \ - (GL_ARB_multi_draw_indirect && GPU_arb_base_instance_is_supported()) -#endif - -typedef struct GPUDrawCommand { - uint v_count; - uint i_count; - uint v_first; - uint i_first; -} GPUDrawCommand; - -typedef struct GPUDrawCommandIndexed { - uint v_count; - uint i_count; - uint v_first; - uint base_index; - uint i_first; -} GPUDrawCommandIndexed; - -struct GPUDrawList { - GPUBatch *batch; - uint base_index; /* Avoid dereferencing batch. */ - uint cmd_offset; /* in bytes, offset inside indirect command buffer. */ - uint cmd_len; /* Number of used command for the next call. */ - uint buffer_size; /* in bytes, size of indirect command buffer. */ - GLuint buffer_id; /* Draw Indirect Buffer id */ - union { - GPUDrawCommand *commands; - GPUDrawCommandIndexed *commands_indexed; - }; -}; - -GPUDrawList *GPU_draw_list_create(int length) -{ - GPUDrawList *list = (GPUDrawList *)MEM_callocN(sizeof(GPUDrawList), "GPUDrawList"); - /* Alloc the biggest possible command list which is indexed. */ - list->buffer_size = sizeof(GPUDrawCommandIndexed) * length; - if (USE_MULTI_DRAW_INDIRECT) { - list->buffer_id = GPU_buf_alloc(); - glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id); - glBufferData(GL_DRAW_INDIRECT_BUFFER, list->buffer_size, NULL, GL_DYNAMIC_DRAW); - } - else { - list->commands = (GPUDrawCommand *)MEM_mallocN(list->buffer_size, "GPUDrawList data"); - } - return list; -} - -void GPU_draw_list_discard(GPUDrawList *list) -{ - if (list->buffer_id) { - GPU_buf_free(list->buffer_id); - } - else { - MEM_SAFE_FREE(list->commands); - } - MEM_freeN(list); -} - -void GPU_draw_list_init(GPUDrawList *list, GPUBatch *batch) -{ - BLI_assert(batch->phase == GPU_BATCH_READY_TO_DRAW); - list->batch = batch; - list->base_index = batch->elem ? BASE_INDEX(batch->elem) : UINT_MAX; - list->cmd_len = 0; - - if (USE_MULTI_DRAW_INDIRECT) { - if (list->commands == NULL) { - glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id); - if (list->cmd_offset >= list->buffer_size) { - /* Orphan buffer data and start fresh. */ - glBufferData(GL_DRAW_INDIRECT_BUFFER, list->buffer_size, NULL, GL_DYNAMIC_DRAW); - list->cmd_offset = 0; - } - GLenum flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; - list->commands = (GPUDrawCommand *)glMapBufferRange( - GL_DRAW_INDIRECT_BUFFER, list->cmd_offset, list->buffer_size - list->cmd_offset, flags); - } - } - else { - list->cmd_offset = 0; - } -} - -void GPU_draw_list_command_add( - GPUDrawList *list, int v_first, int v_count, int i_first, int i_count) -{ - BLI_assert(list->commands); - - if (v_count == 0 || i_count == 0) { - return; - } - - if (list->base_index != UINT_MAX) { - GPUDrawCommandIndexed *cmd = list->commands_indexed + list->cmd_len; - cmd->v_first = v_first; - cmd->v_count = v_count; - cmd->i_count = i_count; - cmd->base_index = list->base_index; - cmd->i_first = i_first; - } - else { - GPUDrawCommand *cmd = list->commands + list->cmd_len; - cmd->v_first = v_first; - cmd->v_count = v_count; - cmd->i_count = i_count; - cmd->i_first = i_first; - } - - list->cmd_len++; - uint offset = list->cmd_offset + list->cmd_len * sizeof(GPUDrawCommandIndexed); - - if (offset == list->buffer_size) { - GPU_draw_list_submit(list); - GPU_draw_list_init(list, list->batch); - } -} - -void GPU_draw_list_submit(GPUDrawList *list) -{ - GPUBatch *batch = list->batch; - - if (list->cmd_len == 0) { - return; - } - - BLI_assert(list->commands); - BLI_assert(batch->program_in_use); - /* TODO could assert that VAO is bound. */ - - /* TODO We loose a bit of memory here if we only draw arrays. Fix that. */ - uintptr_t offset = list->cmd_offset; - uint cmd_len = list->cmd_len; - size_t bytes_used = cmd_len * sizeof(GPUDrawCommandIndexed); - list->cmd_len = 0; /* Avoid reuse. */ - - /* Only do multi-draw indirect if doing more than 2 drawcall. - * This avoids the overhead of buffer mapping if scene is - * not very instance friendly. - * BUT we also need to take into account the case where only - * a few instances are needed to finish filling a call buffer. */ - const bool do_mdi = (cmd_len > 2) || (list->cmd_offset + bytes_used == list->buffer_size); - - if (USE_MULTI_DRAW_INDIRECT && do_mdi) { - GLenum prim = batch->gl_prim_type; - - glBindBuffer(GL_DRAW_INDIRECT_BUFFER, list->buffer_id); - glFlushMappedBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, bytes_used); - glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER); - list->commands = NULL; /* Unmapped */ - list->cmd_offset += bytes_used; - - if (batch->elem) { - glMultiDrawElementsIndirect(prim, INDEX_TYPE(batch->elem), (void *)offset, cmd_len, 0); - } - else { - glMultiDrawArraysIndirect(prim, (void *)offset, cmd_len, 0); - } - } - else { - /* Fallback */ - if (batch->elem) { - GPUDrawCommandIndexed *cmd = list->commands_indexed; - for (int i = 0; i < cmd_len; i++, cmd++) { - /* Index start was added by Draw manager. Avoid counting it twice. */ - cmd->v_first -= batch->elem->index_start; - GPU_batch_draw_advanced(batch, cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count); - } - } - else { - GPUDrawCommand *cmd = list->commands; - for (int i = 0; i < cmd_len; i++, cmd++) { - GPU_batch_draw_advanced(batch, cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count); - } - } - } + static_cast<Batch *>(batch)->draw(v_first, v_count, i_first, i_count); } /** \} */ @@ -1015,23 +315,11 @@ void GPU_batch_program_set_imm_shader(GPUBatch *batch) void gpu_batch_init(void) { - if (g_default_attr_vbo == 0) { - g_default_attr_vbo = GPU_buf_alloc(); - - float default_attrib_data[4] = {0.0f, 0.0f, 0.0f, 1.0f}; - glBindBuffer(GL_ARRAY_BUFFER, g_default_attr_vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(float[4]), default_attrib_data, GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - gpu_batch_presets_init(); } void gpu_batch_exit(void) { - GPU_buf_free(g_default_attr_vbo); - g_default_attr_vbo = 0; - gpu_batch_presets_exit(); } diff --git a/source/blender/gpu/intern/gpu_batch_presets.c b/source/blender/gpu/intern/gpu_batch_presets.c index 3d9b4326c7e..6a1645a71d8 100644 --- a/source/blender/gpu/intern/gpu_batch_presets.c +++ b/source/blender/gpu/intern/gpu_batch_presets.c @@ -35,7 +35,6 @@ #include "GPU_batch.h" #include "GPU_batch_presets.h" /* own include */ #include "GPU_batch_utils.h" -#include "gpu_shader_private.h" /* -------------------------------------------------------------------- */ /** \name Local Structures @@ -63,6 +62,7 @@ static struct { static struct { struct { GPUBatch *panel_drag_widget; + GPUBatch *quad; } batch; float panel_drag_widget_pixelsize; @@ -331,6 +331,24 @@ GPUBatch *GPU_batch_preset_panel_drag_widget(const float pixelsize, return g_presets_2d.batch.panel_drag_widget; } +/* To be used with procedural placement inside shader. */ +GPUBatch *GPU_batch_preset_quad(void) +{ + if (!g_presets_2d.batch.quad) { + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(preset_2d_format()); + GPU_vertbuf_data_alloc(vbo, 4); + + float pos_data[4][2] = {{0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f}}; + GPU_vertbuf_attr_fill(vbo, g_presets_2d.attr_id.pos, pos_data); + /* Don't fill the color. */ + + g_presets_2d.batch.quad = GPU_batch_create_ex(GPU_PRIM_TRI_FAN, vbo, NULL, GPU_BATCH_OWNS_VBO); + + gpu_batch_presets_register(g_presets_2d.batch.quad); + } + return g_presets_2d.batch.quad; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -380,18 +398,6 @@ bool gpu_batch_presets_unregister(GPUBatch *preset_batch) return false; } -void gpu_batch_presets_reset(void) -{ - BLI_mutex_lock(&g_presets_3d.mutex); - /* Reset vao caches for these every time we switch opengl context. - * This way they will draw correctly for each window. */ - LISTBASE_FOREACH (LinkData *, link, &presets_list) { - GPUBatch *preset = link->data; - GPU_batch_vao_cache_clear(preset); - } - BLI_mutex_unlock(&g_presets_3d.mutex); -} - void gpu_batch_presets_exit(void) { LinkData *link; @@ -404,17 +410,4 @@ void gpu_batch_presets_exit(void) BLI_mutex_end(&g_presets_3d.mutex); } -/** - * This function only needs to be accessed externally because - * we are drawing UI batches with the DRW old context. - * - * And now we use it for drawing the entire area. - * - * XXX (Clément) - to cleanup in the upcoming 2.91 refactor. - **/ -void GPU_batch_presets_reset() -{ - gpu_batch_presets_reset(); -} - /** \} */ diff --git a/source/blender/gpu/intern/gpu_batch_private.h b/source/blender/gpu/intern/gpu_batch_private.hh index 93745b9ca9b..c0444647fe1 100644 --- a/source/blender/gpu/intern/gpu_batch_private.h +++ b/source/blender/gpu/intern/gpu_batch_private.hh @@ -28,14 +28,21 @@ #include "GPU_batch.h" #include "GPU_context.h" -#include "GPU_shader_interface.h" -#ifdef __cplusplus -extern "C" { -#endif +namespace blender { +namespace gpu { -void gpu_batch_remove_interface_ref(GPUBatch *batch, const GPUShaderInterface *interface); +/** + * Base class which is then specialized for each implementation (GL, VK, ...). + * NOTE: Extends GPUBatch as we still needs to expose some of the internals to the outside C code. + **/ +class Batch : public GPUBatch { + public: + Batch(){}; + virtual ~Batch(){}; -#ifdef __cplusplus -} -#endif + virtual void draw(int v_first, int v_count, int i_first, int i_count) = 0; +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_batch_utils.c b/source/blender/gpu/intern/gpu_batch_utils.c index 0660d4a1724..e2d03d27035 100644 --- a/source/blender/gpu/intern/gpu_batch_utils.c +++ b/source/blender/gpu/intern/gpu_batch_utils.c @@ -28,7 +28,6 @@ #include "GPU_batch.h" #include "GPU_batch_utils.h" /* own include */ -#include "gpu_shader_private.h" /* -------------------------------------------------------------------- */ /** \name Polygon Creation (2D) diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index b051d4fe59a..d67ce0be310 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -43,7 +43,7 @@ #include "GPU_extensions.h" #include "GPU_material.h" #include "GPU_shader.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "GPU_vertex_format.h" #include "BLI_sys_types.h" /* for intptr_t support */ @@ -686,7 +686,7 @@ static char *code_generate_vertex(GPUNodeGraph *graph, BLI_dynstr_append(ds, "#define USE_ATTR\n\n"); BLI_dynstr_append(ds, vert_code); - BLI_dynstr_append(ds, "\n"); + BLI_dynstr_append(ds, "\n\n"); BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv) {\n"); @@ -755,15 +755,16 @@ static char *code_generate_geometry(GPUNodeGraph *graph, /* Generate varying assignments. */ BLI_dynstr_append(ds, "#define USE_ATTR\n"); - BLI_dynstr_append(ds, "void pass_attr(const int vert) {\n"); + /* This needs to be a define. Some drivers don't like variable vert index inside dataAttrIn. */ + BLI_dynstr_append(ds, "#define pass_attr(vert) {\\\n"); if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, " dataAttrOut.barycentricTexCo = calc_barycentric_co(vert);\n"); + BLI_dynstr_append(ds, "dataAttrOut.barycentricTexCo = calc_barycentric_co(vert);\\\n"); } LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { /* TODO let shader choose what to do depending on what the attribute is. */ - BLI_dynstr_appendf(ds, " dataAttrOut.var%d = dataAttrIn[vert].var%d;\n", attr->id, attr->id); + BLI_dynstr_appendf(ds, "dataAttrOut.var%d = dataAttrIn[vert].var%d;\\\n", attr->id, attr->id); } BLI_dynstr_append(ds, "}\n\n"); diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index 283784aec20..85e7dffe3e7 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -40,7 +40,7 @@ #include "GHOST_C-api.h" #include "gpu_backend.hh" -#include "gpu_batch_private.h" +#include "gpu_batch_private.hh" #include "gpu_context_private.hh" #include "gpu_matrix_private.h" @@ -70,6 +70,12 @@ GPUContext::GPUContext() GPUContext::~GPUContext() { GPU_matrix_state_discard(matrix_state); + delete state_manager; + delete front_left; + delete back_left; + delete front_right; + delete back_right; + delete imm; } bool GPUContext::is_active_on_thread(void) @@ -83,12 +89,12 @@ bool GPUContext::is_active_on_thread(void) GPUContext *GPU_context_create(void *ghost_window) { - if (gpu_backend_get() == NULL) { + if (GPUBackend::get() == NULL) { /* TODO move where it make sense. */ GPU_backend_init(GPU_BACKEND_OPENGL); } - GPUContext *ctx = gpu_backend_get()->context_alloc(ghost_window); + GPUContext *ctx = GPUBackend::get()->context_alloc(ghost_window); GPU_context_active_set(ctx); return ctx; @@ -120,18 +126,6 @@ GPUContext *GPU_context_active_get(void) return active_ctx; } -GLuint GPU_vao_default(void) -{ - BLI_assert(active_ctx); /* need at least an active context */ - return static_cast<GLContext *>(active_ctx)->default_vao_; -} - -GLuint GPU_framebuffer_default(void) -{ - BLI_assert(active_ctx); /* need at least an active context */ - return static_cast<GLContext *>(active_ctx)->default_framebuffer_; -} - GLuint GPU_vao_alloc(void) { GLuint new_vao_id = 0; @@ -173,63 +167,17 @@ void GPU_fbo_free(GLuint fbo_id, GPUContext *ctx) void GPU_buf_free(GLuint buf_id) { /* TODO avoid using backend */ - GPUBackend *backend = gpu_backend_get(); + GPUBackend *backend = GPUBackend::get(); static_cast<GLBackend *>(backend)->buf_free(buf_id); } void GPU_tex_free(GLuint tex_id) { /* TODO avoid using backend */ - GPUBackend *backend = gpu_backend_get(); + GPUBackend *backend = GPUBackend::get(); static_cast<GLBackend *>(backend)->tex_free(tex_id); } -/* GPUBatch & GPUFrameBuffer contains respectively VAO & FBO indices - * which are not shared across contexts. So we need to keep track of - * ownership. */ - -void gpu_context_add_batch(GPUContext *ctx, GPUBatch *batch) -{ - BLI_assert(ctx); - static_cast<GLContext *>(ctx)->batch_register(batch); -} - -void gpu_context_remove_batch(GPUContext *ctx, GPUBatch *batch) -{ - BLI_assert(ctx); - static_cast<GLContext *>(ctx)->batch_unregister(batch); -} - -void gpu_context_add_framebuffer(GPUContext *ctx, GPUFrameBuffer *fb) -{ -#ifdef DEBUG - BLI_assert(ctx); - static_cast<GLContext *>(ctx)->framebuffer_register(fb); -#else - UNUSED_VARS(ctx, fb); -#endif -} - -void gpu_context_remove_framebuffer(GPUContext *ctx, GPUFrameBuffer *fb) -{ -#ifdef DEBUG - BLI_assert(ctx); - static_cast<GLContext *>(ctx)->framebuffer_unregister(fb); -#else - UNUSED_VARS(ctx, fb); -#endif -} - -void gpu_context_active_framebuffer_set(GPUContext *ctx, GPUFrameBuffer *fb) -{ - ctx->current_fbo = fb; -} - -GPUFrameBuffer *gpu_context_active_framebuffer_get(GPUContext *ctx) -{ - return ctx->current_fbo; -} - struct GPUMatrixState *gpu_context_active_matrix_state_get() { BLI_assert(active_ctx); @@ -285,7 +233,7 @@ void GPU_backend_exit(void) delete g_backend; } -GPUBackend *gpu_backend_get(void) +GPUBackend *GPUBackend::get(void) { return g_backend; } diff --git a/source/blender/gpu/intern/gpu_context_private.hh b/source/blender/gpu/intern/gpu_context_private.hh index d369dbe7402..b72eee13105 100644 --- a/source/blender/gpu/intern/gpu_context_private.hh +++ b/source/blender/gpu/intern/gpu_context_private.hh @@ -29,25 +29,46 @@ #include "GPU_context.h" +#include "gpu_framebuffer_private.hh" +#include "gpu_immediate_private.hh" +#include "gpu_shader_private.hh" +#include "gpu_state_private.hh" + #include <mutex> #include <pthread.h> #include <string.h> #include <unordered_set> #include <vector> -struct GPUFrameBuffer; struct GPUMatrixState; struct GPUContext { public: /** State managment */ - GPUFrameBuffer *current_fbo = NULL; + blender::gpu::Shader *shader = NULL; + blender::gpu::FrameBuffer *active_fb = NULL; GPUMatrixState *matrix_state = NULL; + blender::gpu::GPUStateManager *state_manager = NULL; + blender::gpu::Immediate *imm = NULL; + + /** + * All 4 window framebuffers. + * None of them are valid in an offscreen context. + * Right framebuffers are only available if using stereo rendering. + * Front framebuffers contains (in principle, but not always) the last frame color. + * Default framebuffer is back_left. + */ + blender::gpu::FrameBuffer *back_left = NULL; + blender::gpu::FrameBuffer *front_left = NULL; + blender::gpu::FrameBuffer *back_right = NULL; + blender::gpu::FrameBuffer *front_right = NULL; protected: /** Thread on which this context is active. */ pthread_t thread_; bool is_active_; + /** Avoid including GHOST headers. Can be NULL for offscreen contexts. */ + void *ghost_window_; public: GPUContext(); @@ -61,9 +82,6 @@ struct GPUContext { MEM_CXX_CLASS_ALLOC_FUNCS("GPUContext") }; -GLuint GPU_vao_default(void); -GLuint GPU_framebuffer_default(void); - /* These require a gl ctx bound. */ GLuint GPU_buf_alloc(void); GLuint GPU_tex_alloc(void); @@ -77,12 +95,6 @@ void GPU_tex_free(GLuint tex_id); void GPU_vao_free(GLuint vao_id, GPUContext *ctx); void GPU_fbo_free(GLuint fbo_id, GPUContext *ctx); -void gpu_context_add_batch(GPUContext *ctx, GPUBatch *batch); -void gpu_context_remove_batch(GPUContext *ctx, GPUBatch *batch); - -void gpu_context_add_framebuffer(GPUContext *ctx, struct GPUFrameBuffer *fb); -void gpu_context_remove_framebuffer(GPUContext *ctx, struct GPUFrameBuffer *fb); - void gpu_context_active_framebuffer_set(GPUContext *ctx, struct GPUFrameBuffer *fb); struct GPUFrameBuffer *gpu_context_active_framebuffer_get(GPUContext *ctx); diff --git a/source/blender/gpu/intern/gpu_debug.cc b/source/blender/gpu/intern/gpu_debug.cc index f7d6236071d..f179a241926 100644 --- a/source/blender/gpu/intern/gpu_debug.cc +++ b/source/blender/gpu/intern/gpu_debug.cc @@ -36,226 +36,6 @@ #include <stdlib.h> #include <string.h> -#ifndef __APPLE__ /* only non-Apple systems implement OpenGL debug callbacks */ - -/* control whether we use older AMD_debug_output extension - * some supported GPU + OS combos do not have the newer extensions */ -# define LEGACY_DEBUG 1 - -/* Debug callbacks need the same calling convention as OpenGL functions. */ -# if defined(_WIN32) -# define APIENTRY __stdcall -# else -# define APIENTRY -# endif - -static const char *source_name(GLenum source) -{ - switch (source) { - case GL_DEBUG_SOURCE_API: - return "API"; - case GL_DEBUG_SOURCE_WINDOW_SYSTEM: - return "window system"; - case GL_DEBUG_SOURCE_SHADER_COMPILER: - return "shader compiler"; - case GL_DEBUG_SOURCE_THIRD_PARTY: - return "3rd party"; - case GL_DEBUG_SOURCE_APPLICATION: - return "application"; - case GL_DEBUG_SOURCE_OTHER: - return "other"; - default: - return "???"; - } -} - -static const char *message_type_name(GLenum message) -{ - switch (message) { - case GL_DEBUG_TYPE_ERROR: - return "error"; - case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: - return "deprecated behavior"; - case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: - return "undefined behavior"; - case GL_DEBUG_TYPE_PORTABILITY: - return "portability"; - case GL_DEBUG_TYPE_PERFORMANCE: - return "performance"; - case GL_DEBUG_TYPE_OTHER: - return "other"; - case GL_DEBUG_TYPE_MARKER: - return "marker"; /* KHR has this, ARB does not */ - default: - return "???"; - } -} - -static void APIENTRY gpu_debug_proc(GLenum source, - GLenum type, - GLuint UNUSED(id), - GLenum severity, - GLsizei UNUSED(length), - const GLchar *message, - const GLvoid *UNUSED(userParm)) -{ - bool backtrace = false; - - switch (severity) { - case GL_DEBUG_SEVERITY_HIGH: - backtrace = true; - ATTR_FALLTHROUGH; - case GL_DEBUG_SEVERITY_MEDIUM: - case GL_DEBUG_SEVERITY_LOW: - case GL_DEBUG_SEVERITY_NOTIFICATION: /* KHR has this, ARB does not */ - fprintf(stderr, "GL %s %s: %s\n", source_name(source), message_type_name(type), message); - } - - if (backtrace) { - BLI_system_backtrace(stderr); - fflush(stderr); - } -} - -# if LEGACY_DEBUG - -static const char *category_name_amd(GLenum category) -{ - switch (category) { - case GL_DEBUG_CATEGORY_API_ERROR_AMD: - return "API error"; - case GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD: - return "window system"; - case GL_DEBUG_CATEGORY_DEPRECATION_AMD: - return "deprecated behavior"; - case GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD: - return "undefined behavior"; - case GL_DEBUG_CATEGORY_PERFORMANCE_AMD: - return "performance"; - case GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD: - return "shader compiler"; - case GL_DEBUG_CATEGORY_APPLICATION_AMD: - return "application"; - case GL_DEBUG_CATEGORY_OTHER_AMD: - return "other"; - default: - return "???"; - } -} - -static void APIENTRY gpu_debug_proc_amd(GLuint UNUSED(id), - GLenum category, - GLenum severity, - GLsizei UNUSED(length), - const GLchar *message, - GLvoid *UNUSED(userParm)) -{ - bool backtrace = false; - - switch (severity) { - case GL_DEBUG_SEVERITY_HIGH: - backtrace = true; - ATTR_FALLTHROUGH; - case GL_DEBUG_SEVERITY_MEDIUM: - case GL_DEBUG_SEVERITY_LOW: - fprintf(stderr, "GL %s: %s\n", category_name_amd(category), message); - } - - if (backtrace) { - BLI_system_backtrace(stderr); - fflush(stderr); - } -} -# endif /* LEGACY_DEBUG */ - -# undef APIENTRY -#endif /* not Apple */ - -void gpu_debug_init(void) -{ -#ifdef __APPLE__ - fprintf(stderr, "OpenGL debug callback is not available on Apple.\n"); -#else /* not Apple */ - const char success[] = "Successfully hooked OpenGL debug callback."; - - if (GLEW_VERSION_4_3 || GLEW_KHR_debug) { - fprintf(stderr, - "Using %s\n", - GLEW_VERSION_4_3 ? "OpenGL 4.3 debug facilities" : "KHR_debug extension"); - glEnable(GL_DEBUG_OUTPUT); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - glDebugMessageCallback((GLDEBUGPROC)gpu_debug_proc, NULL); - glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); - GPU_string_marker(success); - } - else if (GLEW_ARB_debug_output) { - fprintf(stderr, "Using ARB_debug_output extension\n"); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - glDebugMessageCallbackARB((GLDEBUGPROCARB)gpu_debug_proc, NULL); - glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); - GPU_string_marker(success); - } -# if LEGACY_DEBUG - else if (GLEW_AMD_debug_output) { - fprintf(stderr, "Using AMD_debug_output extension\n"); - glDebugMessageCallbackAMD(gpu_debug_proc_amd, NULL); - glDebugMessageEnableAMD(GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); - GPU_string_marker(success); - } -# endif - else { - fprintf(stderr, "Failed to hook OpenGL debug callback.\n"); - } -#endif /* not Apple */ -} - -void gpu_debug_exit(void) -{ -#ifndef __APPLE__ - if (GLEW_VERSION_4_3 || GLEW_KHR_debug) { - glDebugMessageCallback(NULL, NULL); - } - else if (GLEW_ARB_debug_output) { - glDebugMessageCallbackARB(NULL, NULL); - } -# if LEGACY_DEBUG - else if (GLEW_AMD_debug_output) { - glDebugMessageCallbackAMD(NULL, NULL); - } -# endif -#endif -} - -void GPU_string_marker(const char *buf) -{ -#ifdef __APPLE__ - UNUSED_VARS(buf); -#else /* not Apple */ - if (GLEW_VERSION_4_3 || GLEW_KHR_debug) { - glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, - GL_DEBUG_TYPE_MARKER, - 0, - GL_DEBUG_SEVERITY_NOTIFICATION, - -1, - buf); - } - else if (GLEW_ARB_debug_output) { - glDebugMessageInsertARB(GL_DEBUG_SOURCE_APPLICATION_ARB, - GL_DEBUG_TYPE_OTHER_ARB, - 0, - GL_DEBUG_SEVERITY_LOW_ARB, - -1, - buf); - } -# if LEGACY_DEBUG - else if (GLEW_AMD_debug_output) { - glDebugMessageInsertAMD( - GL_DEBUG_CATEGORY_APPLICATION_AMD, GL_DEBUG_SEVERITY_LOW_AMD, 0, 0, buf); - } -# endif -#endif /* not Apple */ -} - void GPU_print_error_debug(const char *str) { if (G.debug & G_DEBUG) { diff --git a/source/blender/gpu/intern/gpu_primitive.c b/source/blender/gpu/intern/gpu_drawlist.cc index 3b11b38db87..7b807a2fa80 100644 --- a/source/blender/gpu/intern/gpu_primitive.c +++ b/source/blender/gpu/intern/gpu_drawlist.cc @@ -20,30 +20,40 @@ /** \file * \ingroup gpu * - * GPU geometric primitives + * Implementation of Multi Draw Indirect. */ -#include "GPU_primitive.h" -#include "gpu_primitive_private.h" +#include "MEM_guardedalloc.h" -GLenum convert_prim_type_to_gl(GPUPrimType prim_type) +#include "GPU_batch.h" +#include "GPU_drawlist.h" + +#include "gpu_backend.hh" + +#include "gpu_drawlist_private.hh" + +using namespace blender::gpu; + +GPUDrawList GPU_draw_list_create(int list_length) +{ + DrawList *list_ptr = GPUBackend::get()->drawlist_alloc(list_length); + return reinterpret_cast<DrawList *>(list_ptr); +} + +void GPU_draw_list_discard(GPUDrawList list) +{ + DrawList *list_ptr = reinterpret_cast<DrawList *>(list); + delete list_ptr; +} + +void GPU_draw_list_append(GPUDrawList list, GPUBatch *batch, int i_first, int i_count) +{ + DrawList *list_ptr = reinterpret_cast<DrawList *>(list); + list_ptr->append(batch, i_first, i_count); +} + +void GPU_draw_list_submit(GPUDrawList list) { -#if TRUST_NO_ONE - assert(prim_type != GPU_PRIM_NONE); -#endif - static const GLenum table[] = { - [GPU_PRIM_POINTS] = GL_POINTS, - [GPU_PRIM_LINES] = GL_LINES, - [GPU_PRIM_LINE_STRIP] = GL_LINE_STRIP, - [GPU_PRIM_LINE_LOOP] = GL_LINE_LOOP, - [GPU_PRIM_TRIS] = GL_TRIANGLES, - [GPU_PRIM_TRI_STRIP] = GL_TRIANGLE_STRIP, - [GPU_PRIM_TRI_FAN] = GL_TRIANGLE_FAN, - - [GPU_PRIM_LINES_ADJ] = GL_LINES_ADJACENCY, - [GPU_PRIM_LINE_STRIP_ADJ] = GL_LINE_STRIP_ADJACENCY, - [GPU_PRIM_TRIS_ADJ] = GL_TRIANGLES_ADJACENCY, - }; - - return table[prim_type]; + DrawList *list_ptr = reinterpret_cast<DrawList *>(list); + list_ptr->submit(); } diff --git a/source/blender/gpu/GPU_attr_binding.h b/source/blender/gpu/intern/gpu_drawlist_private.hh index e7c3dcbce05..ddb09fb0c89 100644 --- a/source/blender/gpu/GPU_attr_binding.h +++ b/source/blender/gpu/intern/gpu_drawlist_private.hh @@ -13,31 +13,32 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * The Original Code is Copyright (C) 2016 by Mike Erwin. + * The Original Code is Copyright (C) 2020 Blender Foundation. * All rights reserved. */ /** \file * \ingroup gpu - * - * GPU vertex attribute binding */ #pragma once -#include "GPU_common.h" +#include "MEM_guardedalloc.h" + +namespace blender { +namespace gpu { -#ifdef __cplusplus -extern "C" { -#endif +/** + * Implementation of Multi Draw Indirect. + * Base class which is then specialized for each implementation (GL, VK, ...). + **/ +class DrawList { + public: + virtual ~DrawList(){}; -typedef struct GPUAttrBinding { - /** Store 4 bits for each of the 16 attributes. */ - uint64_t loc_bits; - /** 1 bit for each attribute. */ - uint16_t enabled_bits; -} GPUAttrBinding; + virtual void append(GPUBatch *batch, int i_first, int i_count) = 0; + virtual void submit() = 0; +}; -#ifdef __cplusplus -} -#endif +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_element.cc b/source/blender/gpu/intern/gpu_element.cc index cf7cc1d214c..29c95c725fd 100644 --- a/source/blender/gpu/intern/gpu_element.cc +++ b/source/blender/gpu/intern/gpu_element.cc @@ -326,6 +326,11 @@ static void squeeze_indices_short(GPUIndexBufBuilder *builder, #endif /* GPU_TRACK_INDEX_RANGE */ +GPUIndexBuf *GPU_indexbuf_calloc(void) +{ + return (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), __func__); +} + GPUIndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *builder) { GPUIndexBuf *elem = (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); diff --git a/source/blender/gpu/intern/gpu_extensions.cc b/source/blender/gpu/intern/gpu_extensions.cc index 8074e4b64f0..6fe08d81cda 100644 --- a/source/blender/gpu/intern/gpu_extensions.cc +++ b/source/blender/gpu/intern/gpu_extensions.cc @@ -71,12 +71,10 @@ static struct GPUGlobal { GLint maxubosize; GLint maxubobinds; int samples_color_texture_max; - float line_width_range[2]; /* workaround for different calculation of dfdy factors on GPUs. Some GPUs/drivers * calculate dfdy in shader differently when drawing to an off-screen buffer. First * number is factor on screen and second is off-screen */ float dfdyfactors[2]; - float max_anisotropy; /* Some Intel drivers have limited support for `GLEW_ARB_base_instance` so in * these cases it is best to indicate that it is not supported. See T67951 */ bool glew_arb_base_instance_is_supported; @@ -118,7 +116,7 @@ static void gpu_detect_mip_render_workaround(void) glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, 0); GPU_texture_unbind(tex); - GPUFrameBuffer *fb = GPU_framebuffer_create(); + GPUFrameBuffer *fb = GPU_framebuffer_create(__func__); GPU_framebuffer_texture_attach(fb, tex, 0, 1); GPU_framebuffer_bind(fb); GPU_framebuffer_clear_color(fb, clear_color); @@ -164,11 +162,6 @@ int GPU_max_textures_vert(void) return GG.maxtexturesvert; } -float GPU_max_texture_anisotropy(void) -{ - return GG.max_anisotropy; -} - int GPU_max_color_texture_samples(void) { return GG.samples_color_texture_max; @@ -189,11 +182,6 @@ int GPU_max_ubo_size(void) return GG.maxubosize; } -float GPU_max_line_width(void) -{ - return GG.line_width_range[1]; -} - void GPU_get_dfdy_factors(float fac[2]) { copy_v2_v2(fac, GG.dfdyfactors); @@ -264,18 +252,9 @@ void gpu_extensions_init(void) glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &GG.maxtexlayers); glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &GG.maxcubemapsize); - if (GLEW_EXT_texture_filter_anisotropic) { - glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &GG.max_anisotropy); - } - else { - GG.max_anisotropy = 1.0f; - } - glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, &GG.maxubobinds); glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &GG.maxubosize); - glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, GG.line_width_range); - glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &GG.samples_color_texture_max); const char *vendor = (const char *)glGetString(GL_VENDOR); diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index 5f3089b2ffb..1b6fea56028 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -29,681 +29,396 @@ #include "GPU_batch.h" #include "GPU_extensions.h" -#include "GPU_framebuffer.h" #include "GPU_shader.h" #include "GPU_texture.h" +#include "gpu_backend.hh" #include "gpu_context_private.hh" #include "gpu_private.h" +#include "gpu_texture_private.hh" -typedef enum { - GPU_FB_DEPTH_ATTACHMENT = 0, - GPU_FB_DEPTH_STENCIL_ATTACHMENT, - GPU_FB_COLOR_ATTACHMENT0, - GPU_FB_COLOR_ATTACHMENT1, - GPU_FB_COLOR_ATTACHMENT2, - GPU_FB_COLOR_ATTACHMENT3, - GPU_FB_COLOR_ATTACHMENT4, - GPU_FB_COLOR_ATTACHMENT5, - /* Number of maximum output slots. - * We support 6 outputs for now (usually we wouldn't need more to preserve fill rate). */ - /* Keep in mind that GL max is GL_MAX_DRAW_BUFFERS and is at least 8, corresponding to - * the maximum number of COLOR attachments specified by glDrawBuffers. */ - GPU_FB_MAX_ATTACHEMENT, -} GPUAttachmentType; - -#define FOREACH_ATTACHMENT_RANGE(att, _start, _end) \ - for (GPUAttachmentType att = static_cast<GPUAttachmentType>(_start); att < _end; \ - att = static_cast<GPUAttachmentType>(att + 1)) - -#define GPU_FB_MAX_COLOR_ATTACHMENT (GPU_FB_MAX_ATTACHEMENT - GPU_FB_COLOR_ATTACHMENT0) - -#define GPU_FB_DIRTY_DRAWBUFFER (1 << 15) - -#define GPU_FB_ATTACHEMENT_IS_DIRTY(flag, type) ((flag & (1 << type)) != 0) -#define GPU_FB_ATTACHEMENT_SET_DIRTY(flag, type) (flag |= (1 << type)) - -struct GPUFrameBuffer { - GPUContext *ctx; - GLuint object; - GPUAttachment attachments[GPU_FB_MAX_ATTACHEMENT]; - uint16_t dirty_flag; - int width, height; - bool multisample; - /* TODO Check that we always use the right context when binding - * (FBOs are not shared across ogl contexts). */ - // void *ctx; -}; +#include "gpu_framebuffer_private.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Constructor / Destructor + * \{ */ -static GLenum convert_attachment_type_to_gl(GPUAttachmentType type) +FrameBuffer::FrameBuffer(const char *name) { -#define ATTACHMENT(type) \ - case GPU_FB_##type: { \ - return GL_##type; \ - } \ - ((void)0) - - switch (type) { - ATTACHMENT(DEPTH_ATTACHMENT); - ATTACHMENT(DEPTH_STENCIL_ATTACHMENT); - ATTACHMENT(COLOR_ATTACHMENT0); - ATTACHMENT(COLOR_ATTACHMENT1); - ATTACHMENT(COLOR_ATTACHMENT2); - ATTACHMENT(COLOR_ATTACHMENT3); - ATTACHMENT(COLOR_ATTACHMENT4); - ATTACHMENT(COLOR_ATTACHMENT5); - default: - BLI_assert(0); - return GL_COLOR_ATTACHMENT0; + if (name) { + BLI_strncpy(name_, name, sizeof(name_)); } -} + else { + name_[0] = '\0'; + } + /* Force config on first use. */ + dirty_attachments_ = true; + dirty_state_ = true; -static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot) -{ - switch (GPU_texture_format(tex)) { - case GPU_DEPTH_COMPONENT32F: - case GPU_DEPTH_COMPONENT24: - case GPU_DEPTH_COMPONENT16: - return GPU_FB_DEPTH_ATTACHMENT; - case GPU_DEPTH24_STENCIL8: - case GPU_DEPTH32F_STENCIL8: - return GPU_FB_DEPTH_STENCIL_ATTACHMENT; - default: - return static_cast<GPUAttachmentType>(GPU_FB_COLOR_ATTACHMENT0 + slot); + for (int i = 0; i < ARRAY_SIZE(attachments_); i++) { + attachments_[i].tex = NULL; + attachments_[i].mip = -1; + attachments_[i].layer = -1; } } -static GLenum convert_buffer_bits_to_gl(eGPUFrameBufferBits bits) +FrameBuffer::~FrameBuffer() { - GLbitfield mask = 0; - mask |= (bits & GPU_DEPTH_BIT) ? GL_DEPTH_BUFFER_BIT : 0; - mask |= (bits & GPU_STENCIL_BIT) ? GL_STENCIL_BUFFER_BIT : 0; - mask |= (bits & GPU_COLOR_BIT) ? GL_COLOR_BUFFER_BIT : 0; - return mask; + GPUFrameBuffer *gpu_fb = reinterpret_cast<GPUFrameBuffer *>(this); + for (int i = 0; i < ARRAY_SIZE(attachments_); i++) { + if (attachments_[i].tex != NULL) { + GPU_texture_detach_framebuffer(attachments_[i].tex, gpu_fb); + } + } } -static GPUTexture *framebuffer_get_depth_tex(GPUFrameBuffer *fb) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Attachments managment + * \{ */ + +void FrameBuffer::attachment_set(GPUAttachmentType type, const GPUAttachment &new_attachment) { - if (fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex) { - return fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex; + if (new_attachment.mip == -1) { + return; /* GPU_ATTACHMENT_LEAVE */ } - return fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex; -} + if (type >= GPU_FB_MAX_ATTACHEMENT) { + fprintf(stderr, + "GPUFramebuffer: Error: Trying to attach texture to type %d but maximum slot is %d.\n", + type - GPU_FB_COLOR_ATTACHMENT0, + GPU_FB_MAX_COLOR_ATTACHMENT); + return; + } -static GPUTexture *framebuffer_get_color_tex(GPUFrameBuffer *fb, int slot) -{ - return fb->attachments[GPU_FB_COLOR_ATTACHMENT0 + slot].tex; -} + if (new_attachment.tex) { + if (new_attachment.layer > 0) { + BLI_assert(ELEM(GPU_texture_target(new_attachment.tex), + GL_TEXTURE_2D_ARRAY, + GL_TEXTURE_CUBE_MAP, + GL_TEXTURE_CUBE_MAP_ARRAY_ARB)); + } + if (GPU_texture_stencil(new_attachment.tex)) { + BLI_assert(ELEM(type, GPU_FB_DEPTH_STENCIL_ATTACHMENT)); + } + else if (GPU_texture_depth(new_attachment.tex)) { + BLI_assert(ELEM(type, GPU_FB_DEPTH_ATTACHMENT)); + } + } -static void gpu_print_framebuffer_error(GLenum status, char err_out[256]) -{ - const char *format = "GPUFrameBuffer: framebuffer status %s\n"; - const char *err = "unknown"; - -#define FORMAT_STATUS(X) \ - case GL_FRAMEBUFFER_##X: { \ - err = "GL_FRAMEBUFFER_" #X; \ - break; \ - } \ - ((void)0) - - switch (status) { - /* success */ - FORMAT_STATUS(COMPLETE); - /* errors shared by OpenGL desktop & ES */ - FORMAT_STATUS(INCOMPLETE_ATTACHMENT); - FORMAT_STATUS(INCOMPLETE_MISSING_ATTACHMENT); - FORMAT_STATUS(UNSUPPORTED); -#if 0 /* for OpenGL ES only */ - FORMAT_STATUS(INCOMPLETE_DIMENSIONS); -#else /* for desktop GL only */ - FORMAT_STATUS(INCOMPLETE_DRAW_BUFFER); - FORMAT_STATUS(INCOMPLETE_READ_BUFFER); - FORMAT_STATUS(INCOMPLETE_MULTISAMPLE); - FORMAT_STATUS(UNDEFINED); -#endif + GPUAttachment &attachment = attachments_[type]; + + if (attachment.tex == new_attachment.tex && attachment.layer == new_attachment.layer && + attachment.mip == new_attachment.mip) { + return; /* Exact same texture already bound here. */ + } + /* Unbind previous and bind new. */ + /* TODO(fclem) cleanup the casts. */ + if (attachment.tex) { + GPU_texture_detach_framebuffer(attachment.tex, reinterpret_cast<GPUFrameBuffer *>(this)); } -#undef FORMAT_STATUS + attachment = new_attachment; - if (err_out) { - BLI_snprintf(err_out, 256, format, err); + /* Might be null if this is for unbinding. */ + if (attachment.tex) { + GPU_texture_attach_framebuffer(attachment.tex, reinterpret_cast<GPUFrameBuffer *>(this), type); } else { - fprintf(stderr, format, err); + /* GPU_ATTACHMENT_NONE */ } -} - -void gpu_framebuffer_module_init(void) -{ -} -void gpu_framebuffer_module_exit(void) -{ + dirty_attachments_ = true; } -GPUFrameBuffer *GPU_framebuffer_active_get(void) +void FrameBuffer::recursive_downsample(int max_lvl, + void (*callback)(void *userData, int level), + void *userData) { GPUContext *ctx = GPU_context_active_get(); - if (ctx) { - return gpu_context_active_framebuffer_get(ctx); + /* Bind to make sure the framebuffer is up to date. */ + this->bind(true); + + if (width_ == 1 && height_ == 1) { + return; } + /* HACK: Make the framebuffer appear not bound to avoid assert in GPU_texture_bind. */ + ctx->active_fb = NULL; - return 0; -} + int levels = floor(log2(max_ii(width_, height_))); + max_lvl = min_ii(max_lvl, levels); -static void gpu_framebuffer_current_set(GPUFrameBuffer *fb) -{ - GPUContext *ctx = GPU_context_active_get(); - if (ctx) { - gpu_context_active_framebuffer_set(ctx, fb); + int current_dim[2] = {width_, height_}; + int mip_lvl; + for (mip_lvl = 1; mip_lvl < max_lvl + 1; mip_lvl++) { + /* calculate next viewport size */ + current_dim[0] = max_ii(current_dim[0] / 2, 1); + current_dim[1] = max_ii(current_dim[1] / 2, 1); + /* Replace attaached miplevel for each attachement. */ + for (int att = 0; att < ARRAY_SIZE(attachments_); att++) { + GPUTexture *tex = attachments_[att].tex; + if (tex != NULL) { + /* Some Intel HDXXX have issue with rendering to a mipmap that is below + * the texture GL_TEXTURE_MAX_LEVEL. So even if it not correct, in this case + * we allow GL_TEXTURE_MAX_LEVEL to be one level lower. In practice it does work! */ + int map_lvl = (GPU_mip_render_workaround()) ? mip_lvl : (mip_lvl - 1); + /* Restrict fetches only to previous level. */ + GPU_texture_bind(tex, 0); + glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, mip_lvl - 1); + glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, map_lvl); + GPU_texture_unbind(tex); + /* Bind next level. */ + attachments_[att].mip = mip_lvl; + } + } + /* Update the internal attachments and viewport size. */ + dirty_attachments_ = true; + this->bind(true); + /* HACK: Make the framebuffer appear not bound to avoid assert in GPU_texture_bind. */ + ctx->active_fb = NULL; + + callback(userData, mip_lvl); + + /* This is the last mipmap level. Exit loop without incrementing mip_lvl. */ + if (current_dim[0] == 1 && current_dim[1] == 1) { + break; + } + } + + for (int att = 0; att < ARRAY_SIZE(attachments_); att++) { + if (attachments_[att].tex != NULL) { + /* Reset mipmap level range. */ + GPUTexture *tex = attachments_[att].tex; + GPU_texture_bind(tex, 0); + glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, mip_lvl); + GPU_texture_unbind(tex); + /* Reset base level. NOTE: might not be the one bound at the start of this function. */ + attachments_[att].mip = 0; + } } + dirty_attachments_ = true; } -/* GPUFrameBuffer */ +/** \} */ + +} // namespace blender::gpu -GPUFrameBuffer *GPU_framebuffer_create(void) +/* -------------------------------------------------------------------- */ +/** \name C-API + * \{ */ + +using namespace blender; +using namespace blender::gpu; + +GPUFrameBuffer *GPU_framebuffer_create(const char *name) { /* We generate the FB object later at first use in order to * create the framebuffer in the right opengl context. */ - return (GPUFrameBuffer *)MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer"); + return (GPUFrameBuffer *)GPUBackend::get()->framebuffer_alloc(name); } -static void gpu_framebuffer_init(GPUFrameBuffer *fb) +void GPU_framebuffer_free(GPUFrameBuffer *gpu_fb) { - fb->object = GPU_fbo_alloc(); - fb->ctx = GPU_context_active_get(); - gpu_context_add_framebuffer(fb->ctx, fb); + delete reinterpret_cast<FrameBuffer *>(gpu_fb); } -void GPU_framebuffer_free(GPUFrameBuffer *fb) -{ - for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { - GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); - if (fb->attachments[type].tex != NULL) { - GPU_framebuffer_texture_detach(fb, fb->attachments[type].tex); - } - } - - if (fb->object != 0) { - /* This restores the framebuffer if it was bound */ - GPU_fbo_free(fb->object, fb->ctx); - gpu_context_remove_framebuffer(fb->ctx, fb); - } +/* ---------- Binding ----------- */ - if (GPU_framebuffer_active_get() == fb) { - gpu_framebuffer_current_set(NULL); - } - - MEM_freeN(fb); +void GPU_framebuffer_bind(GPUFrameBuffer *gpu_fb) +{ + FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); + const bool enable_srgb = true; + fb->bind(enable_srgb); } -/* ---------- Attach ----------- */ - -static void gpu_framebuffer_texture_attach_ex( - GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip) +/* Workaround for binding a srgb framebuffer without doing the srgb transform. */ +void GPU_framebuffer_bind_no_srgb(GPUFrameBuffer *gpu_fb) { - if (slot >= GPU_FB_MAX_COLOR_ATTACHMENT) { - fprintf(stderr, - "Attaching to index %d framebuffer slot unsupported. " - "Use at most %d\n", - slot, - GPU_FB_MAX_COLOR_ATTACHMENT); - return; - } + FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); + const bool enable_srgb = false; + fb->bind(enable_srgb); +} - GPUAttachmentType type = attachment_type_from_tex(tex, slot); - GPUAttachment *attachment = &fb->attachments[type]; +/* For stereo rendering. */ +void GPU_backbuffer_bind(eGPUBackBuffer buffer) +{ + GPUContext *ctx = GPU_context_active_get(); - if ((attachment->tex == tex) && (attachment->mip == mip) && (attachment->layer == layer)) { - return; /* Exact same texture already bound here. */ - } - if (attachment->tex != NULL) { - GPU_framebuffer_texture_detach(fb, attachment->tex); + if (buffer == GPU_BACKBUFFER_LEFT) { + ctx->back_left->bind(false); } - - if (attachment->tex == NULL) { - GPU_texture_attach_framebuffer(tex, fb, type); + else { + ctx->back_right->bind(false); } - - attachment->tex = tex; - attachment->mip = mip; - attachment->layer = layer; - GPU_FB_ATTACHEMENT_SET_DIRTY(fb->dirty_flag, type); } -void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip) +void GPU_framebuffer_restore(void) { - gpu_framebuffer_texture_attach_ex(fb, tex, slot, -1, mip); + GPU_context_active_get()->back_left->bind(false); } -void GPU_framebuffer_texture_layer_attach( - GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip) +GPUFrameBuffer *GPU_framebuffer_active_get(void) { - /* NOTE: We could support 1D ARRAY texture. */ - BLI_assert(GPU_texture_target(tex) == GL_TEXTURE_2D_ARRAY); - gpu_framebuffer_texture_attach_ex(fb, tex, slot, layer, mip); + GPUContext *ctx = GPU_context_active_get(); + return reinterpret_cast<GPUFrameBuffer *>(ctx ? ctx->active_fb : NULL); } -void GPU_framebuffer_texture_cubeface_attach( - GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip) +/* Returns the default framebuffer. Will always exists even if it's just a dummy. */ +GPUFrameBuffer *GPU_framebuffer_back_get(void) { - BLI_assert(GPU_texture_cube(tex)); - gpu_framebuffer_texture_attach_ex(fb, tex, slot, face, mip); + GPUContext *ctx = GPU_context_active_get(); + return reinterpret_cast<GPUFrameBuffer *>(ctx ? ctx->back_left : NULL); } -/* ---------- Detach ----------- */ - -void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, GPUTexture *tex, int type) +bool GPU_framebuffer_bound(GPUFrameBuffer *gpu_fb) { - GPUAttachment *attachment = &fb->attachments[type]; + return (gpu_fb == GPU_framebuffer_active_get()); +} - if (attachment->tex != tex) { - fprintf(stderr, - "Warning, attempting to detach Texture %p from framebuffer %p " - "but texture is not attached.\n", - tex, - fb); - return; - } +/* ---------- Attachment Management ----------- */ - attachment->tex = NULL; - GPU_FB_ATTACHEMENT_SET_DIRTY(fb->dirty_flag, type); +bool GPU_framebuffer_check_valid(GPUFrameBuffer *gpu_fb, char err_out[256]) +{ + return reinterpret_cast<FrameBuffer *>(gpu_fb)->check(err_out); } -void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, GPUTexture *tex) +void GPU_framebuffer_texture_attach_ex(GPUFrameBuffer *gpu_fb, GPUAttachment attachement, int slot) { - GPUAttachmentType type = (GPUAttachmentType)GPU_texture_detach_framebuffer(tex, fb); - GPU_framebuffer_texture_detach_slot(fb, tex, type); + GPUAttachmentType type = blender::gpu::Texture::attachment_type(attachement.tex, slot); + reinterpret_cast<FrameBuffer *>(gpu_fb)->attachment_set(type, attachement); } -/* ---------- Config (Attach & Detach) ----------- */ - -/** - * First GPUAttachment in *config is always the depth/depth_stencil buffer. - * Following GPUAttachments are color buffers. - * Setting GPUAttachment.mip to -1 will leave the texture in this slot. - * Setting GPUAttachment.tex to NULL will detach the texture in this slot. - */ -void GPU_framebuffer_config_array(GPUFrameBuffer *fb, const GPUAttachment *config, int config_len) +void GPU_framebuffer_texture_attach(GPUFrameBuffer *fb, GPUTexture *tex, int slot, int mip) { - if (config[0].tex) { - BLI_assert(GPU_texture_depth(config[0].tex)); - gpu_framebuffer_texture_attach_ex(fb, config[0].tex, 0, config[0].layer, config[0].mip); - } - else if (config[0].mip == -1) { - /* Leave texture attached */ - } - else if (fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex != NULL) { - GPU_framebuffer_texture_detach(fb, fb->attachments[GPU_FB_DEPTH_ATTACHMENT].tex); - } - else if (fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex != NULL) { - GPU_framebuffer_texture_detach(fb, fb->attachments[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex); - } - - int slot = 0; - for (int i = 1; i < config_len; i++, slot++) { - if (config[i].tex != NULL) { - BLI_assert(GPU_texture_depth(config[i].tex) == false); - gpu_framebuffer_texture_attach_ex(fb, config[i].tex, slot, config[i].layer, config[i].mip); - } - else if (config[i].mip != -1) { - GPUTexture *tex = framebuffer_get_color_tex(fb, slot); - if (tex != NULL) { - GPU_framebuffer_texture_detach(fb, tex); - } - } - } + GPUAttachment attachement = GPU_ATTACHMENT_TEXTURE_MIP(tex, mip); + GPU_framebuffer_texture_attach_ex(fb, attachement, slot); } -/* ---------- Bind / Restore ----------- */ - -static void gpu_framebuffer_attachment_attach(GPUAttachment *attach, GPUAttachmentType attach_type) +void GPU_framebuffer_texture_layer_attach( + GPUFrameBuffer *fb, GPUTexture *tex, int slot, int layer, int mip) { - int tex_bind = GPU_texture_opengl_bindcode(attach->tex); - GLenum gl_attachment = convert_attachment_type_to_gl(attach_type); - - if (attach->layer > -1) { - if (GPU_texture_cube(attach->tex)) { - glFramebufferTexture2D(GL_FRAMEBUFFER, - gl_attachment, - GL_TEXTURE_CUBE_MAP_POSITIVE_X + attach->layer, - tex_bind, - attach->mip); - } - else { - glFramebufferTextureLayer( - GL_FRAMEBUFFER, gl_attachment, tex_bind, attach->mip, attach->layer); - } - } - else { - glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, tex_bind, attach->mip); - } + GPUAttachment attachement = GPU_ATTACHMENT_TEXTURE_LAYER_MIP(tex, layer, mip); + GPU_framebuffer_texture_attach_ex(fb, attachement, slot); } -static void gpu_framebuffer_attachment_detach(GPUAttachment *UNUSED(attachment), - GPUAttachmentType attach_type) +void GPU_framebuffer_texture_cubeface_attach( + GPUFrameBuffer *fb, GPUTexture *tex, int slot, int face, int mip) { - GLenum gl_attachment = convert_attachment_type_to_gl(attach_type); - glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, 0, 0); + GPUAttachment attachement = GPU_ATTACHMENT_TEXTURE_CUBEFACE_MIP(tex, face, mip); + GPU_framebuffer_texture_attach_ex(fb, attachement, slot); } -static void gpu_framebuffer_update_attachments(GPUFrameBuffer *fb) +void GPU_framebuffer_texture_detach(GPUFrameBuffer *gpu_fb, GPUTexture *tex) { - GLenum gl_attachments[GPU_FB_MAX_COLOR_ATTACHMENT]; - int numslots = 0; - - BLI_assert(GPU_framebuffer_active_get() == fb); - - /* Update attachments */ - FOREACH_ATTACHMENT_RANGE(type, 0, GPU_FB_MAX_ATTACHEMENT) - { - if (type >= GPU_FB_COLOR_ATTACHMENT0) { - if (fb->attachments[type].tex) { - gl_attachments[numslots] = convert_attachment_type_to_gl(type); - } - else { - gl_attachments[numslots] = GL_NONE; - } - numslots++; - } - - if (GPU_FB_ATTACHEMENT_IS_DIRTY(fb->dirty_flag, type) == false) { - continue; - } - if (fb->attachments[type].tex != NULL) { - gpu_framebuffer_attachment_attach(&fb->attachments[type], type); - - fb->multisample = (GPU_texture_samples(fb->attachments[type].tex) > 0); - fb->width = GPU_texture_width(fb->attachments[type].tex); - fb->height = GPU_texture_height(fb->attachments[type].tex); - } - else { - gpu_framebuffer_attachment_detach(&fb->attachments[type], type); - } - } - fb->dirty_flag = 0; - - /* Update draw buffers (color targets) - * This state is saved in the FBO */ - if (numslots) { - glDrawBuffers(numslots, gl_attachments); + GPUAttachment attachement = GPU_ATTACHMENT_NONE; + int type = GPU_texture_framebuffer_attachement_get(tex, gpu_fb); + if (type != -1) { + reinterpret_cast<FrameBuffer *>(gpu_fb)->attachment_set((GPUAttachmentType)type, attachement); } else { - glDrawBuffer(GL_NONE); + BLI_assert(!"Error: Texture: Framebuffer is not attached"); } } /** - * Hack to solve the problem of some bugged AMD GPUs (see `GPU_unused_fb_slot_workaround`). - * If there is an empty color slot between the color slots, - * all textures after this slot are apparently skipped/discarded. + * First GPUAttachment in *config is always the depth/depth_stencil buffer. + * Following GPUAttachments are color buffers. + * Setting GPUAttachment.mip to -1 will leave the texture in this slot. + * Setting GPUAttachment.tex to NULL will detach the texture in this slot. */ -static void gpu_framebuffer_update_attachments_and_fill_empty_slots(GPUFrameBuffer *fb) -{ - GLenum gl_attachments[GPU_FB_MAX_COLOR_ATTACHMENT]; - int dummy_tex = 0; - - BLI_assert(GPU_framebuffer_active_get() == fb); - - /* Update attachments */ - for (int i_type = GPU_FB_MAX_ATTACHEMENT - 1; i_type >= 0; --i_type) { - GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); - GPUTexture *tex = fb->attachments[type].tex; - - if (type >= GPU_FB_COLOR_ATTACHMENT0) { - int slot = type - GPU_FB_COLOR_ATTACHMENT0; - if (tex != NULL || (dummy_tex != 0)) { - gl_attachments[slot] = convert_attachment_type_to_gl(type); - - if (dummy_tex == 0) { - dummy_tex = GPU_texture_opengl_bindcode(tex); - } - } - else { - gl_attachments[slot] = GL_NONE; - } - } - else { - dummy_tex = 0; - } - - if ((dummy_tex != 0) && tex == NULL) { - /* Fill empty slot */ - glFramebufferTexture(GL_FRAMEBUFFER, convert_attachment_type_to_gl(type), dummy_tex, 0); - } - else if (GPU_FB_ATTACHEMENT_IS_DIRTY(fb->dirty_flag, type)) { - if (tex != NULL) { - gpu_framebuffer_attachment_attach(&fb->attachments[type], type); - - fb->multisample = (GPU_texture_samples(tex) > 0); - fb->width = GPU_texture_width(tex); - fb->height = GPU_texture_height(tex); - } - else { - gpu_framebuffer_attachment_detach(&fb->attachments[type], type); - } - } - } - fb->dirty_flag = 0; - - /* Update draw buffers (color targets) - * This state is saved in the FBO */ - glDrawBuffers(GPU_FB_MAX_COLOR_ATTACHMENT, gl_attachments); -} - -#define FRAMEBUFFER_STACK_DEPTH 16 - -static struct { - GPUFrameBuffer *framebuffers[FRAMEBUFFER_STACK_DEPTH]; - uint top; -} FrameBufferStack = {{0}}; - -static void gpuPushFrameBuffer(GPUFrameBuffer *fbo) +void GPU_framebuffer_config_array(GPUFrameBuffer *gpu_fb, + const GPUAttachment *config, + int config_len) { - BLI_assert(FrameBufferStack.top < FRAMEBUFFER_STACK_DEPTH); - FrameBufferStack.framebuffers[FrameBufferStack.top] = fbo; - FrameBufferStack.top++; -} - -static GPUFrameBuffer *gpuPopFrameBuffer(void) -{ - BLI_assert(FrameBufferStack.top > 0); - FrameBufferStack.top--; - return FrameBufferStack.framebuffers[FrameBufferStack.top]; -} + FrameBuffer *fb = reinterpret_cast<FrameBuffer *>(gpu_fb); -#undef FRAMEBUFFER_STACK_DEPTH + const GPUAttachment &depth_attachement = config[0]; + Span<GPUAttachment> color_attachments(config + 1, config_len - 1); -void GPU_framebuffer_bind(GPUFrameBuffer *fb) -{ - if (fb->object == 0) { - gpu_framebuffer_init(fb); + if (depth_attachement.mip == -1) { + /* GPU_ATTACHMENT_LEAVE */ } - - if (GPU_framebuffer_active_get() != fb) { - glBindFramebuffer(GL_FRAMEBUFFER, fb->object); - glEnable(GL_FRAMEBUFFER_SRGB); - - GPUTexture *first_target = fb->attachments[GPU_FB_COLOR_ATTACHMENT0].tex; - const bool is_srgb_target = (first_target && - (GPU_texture_format(first_target) == GPU_SRGB8_A8)); - GPU_shader_set_framebuffer_srgb_target(is_srgb_target); + else if (depth_attachement.tex == NULL) { + /* GPU_ATTACHMENT_NONE: Need to clear both targets. */ + fb->attachment_set(GPU_FB_DEPTH_STENCIL_ATTACHMENT, depth_attachement); + fb->attachment_set(GPU_FB_DEPTH_ATTACHMENT, depth_attachement); } - - gpu_framebuffer_current_set(fb); - - if (fb->dirty_flag != 0) { - if (GPU_unused_fb_slot_workaround()) { - /* XXX: Please AMD, fix this. */ - gpu_framebuffer_update_attachments_and_fill_empty_slots(fb); - } - else { - gpu_framebuffer_update_attachments(fb); - } + else { + GPUAttachmentType type = GPU_texture_stencil(depth_attachement.tex) ? + GPU_FB_DEPTH_STENCIL_ATTACHMENT : + GPU_FB_DEPTH_ATTACHMENT; + fb->attachment_set(type, depth_attachement); } - /* TODO manually check for errors? */ -#if 0 - char err_out[256]; - if (!GPU_framebuffer_check_valid(fb, err_out)) { - printf("Invalid %s\n", err_out); + GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0; + for (const GPUAttachment &attachement : color_attachments) { + fb->attachment_set(type, attachement); + ++type; } -#endif - - glViewport(0, 0, fb->width, fb->height); } -void GPU_framebuffer_restore(void) +/* ---------- Viewport & Scissor Region ----------- */ + +/* Viewport and scissor size is stored per framebuffer. + * It is only reset to its original dimensions explicitely OR when binding the framebuffer after + * modifiying its attachments. */ +void GPU_framebuffer_viewport_set(GPUFrameBuffer *gpu_fb, int x, int y, int width, int height) { - if (GPU_framebuffer_active_get() != NULL) { - glBindFramebuffer(GL_FRAMEBUFFER, GPU_framebuffer_default()); - gpu_framebuffer_current_set(NULL); - glDisable(GL_FRAMEBUFFER_SRGB); - GPU_shader_set_framebuffer_srgb_target(false); - } + int viewport_rect[4] = {x, y, width, height}; + reinterpret_cast<FrameBuffer *>(gpu_fb)->viewport_set(viewport_rect); } -bool GPU_framebuffer_bound(GPUFrameBuffer *fb) +void GPU_framebuffer_viewport_get(GPUFrameBuffer *gpu_fb, int r_viewport[4]) { - return (fb == GPU_framebuffer_active_get()) && (fb->object != 0); + reinterpret_cast<FrameBuffer *>(gpu_fb)->viewport_get(r_viewport); } -bool GPU_framebuffer_check_valid(GPUFrameBuffer *fb, char err_out[256]) +/* Reset to its attachement(s) size. */ +void GPU_framebuffer_viewport_reset(GPUFrameBuffer *gpu_fb) { - if (!GPU_framebuffer_bound(fb)) { - GPU_framebuffer_bind(fb); - } - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - GPU_framebuffer_restore(); - gpu_print_framebuffer_error(status, err_out); - return false; - } - - return true; + reinterpret_cast<FrameBuffer *>(gpu_fb)->viewport_reset(); } /* ---------- Framebuffer Operations ----------- */ -#define CHECK_FRAMEBUFFER_IS_BOUND(_fb) \ - BLI_assert(GPU_framebuffer_bound(_fb)); \ - UNUSED_VARS_NDEBUG(_fb); \ - ((void)0) - -/* Needs to be done after binding. */ -void GPU_framebuffer_viewport_set(GPUFrameBuffer *fb, int x, int y, int w, int h) -{ - CHECK_FRAMEBUFFER_IS_BOUND(fb); - - glViewport(x, y, w, h); -} - -void GPU_framebuffer_clear(GPUFrameBuffer *fb, +void GPU_framebuffer_clear(GPUFrameBuffer *gpu_fb, eGPUFrameBufferBits buffers, const float clear_col[4], float clear_depth, uint clear_stencil) { - CHECK_FRAMEBUFFER_IS_BOUND(fb); - - if (buffers & GPU_COLOR_BIT) { - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glClearColor(clear_col[0], clear_col[1], clear_col[2], clear_col[3]); - } - if (buffers & GPU_DEPTH_BIT) { - glDepthMask(GL_TRUE); - glClearDepth(clear_depth); - } - if (buffers & GPU_STENCIL_BIT) { - glStencilMask(0xFF); - glClearStencil(clear_stencil); - } - - GLbitfield mask = convert_buffer_bits_to_gl(buffers); - glClear(mask); + reinterpret_cast<FrameBuffer *>(gpu_fb)->clear(buffers, clear_col, clear_depth, clear_stencil); } -/* Clear all textures bound to this framebuffer with a different color. */ -void GPU_framebuffer_multi_clear(GPUFrameBuffer *fb, const float (*clear_cols)[4]) +/* Clear all textures attached to this framebuffer with a different color. */ +void GPU_framebuffer_multi_clear(GPUFrameBuffer *gpu_fb, const float (*clear_cols)[4]) { - CHECK_FRAMEBUFFER_IS_BOUND(fb); - - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - int i_type = GPU_FB_COLOR_ATTACHMENT0; - for (int i = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i++, i_type++) { - GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); - if (fb->attachments[type].tex != NULL) { - glClearBufferfv(GL_COLOR, i, clear_cols[i]); - } - } -} - -void GPU_framebuffer_read_depth(GPUFrameBuffer *fb, int x, int y, int w, int h, float *data) -{ - CHECK_FRAMEBUFFER_IS_BOUND(fb); - - GLenum type = GL_DEPTH_COMPONENT; - glReadBuffer(GL_COLOR_ATTACHMENT0); /* This is OK! */ - glReadPixels(x, y, w, h, type, GL_FLOAT, data); + reinterpret_cast<FrameBuffer *>(gpu_fb)->clear_multi(clear_cols); } -static GLenum gpu_get_gl_datatype(eGPUDataFormat format) +void GPU_clear_color(float red, float green, float blue, float alpha) { - switch (format) { - case GPU_DATA_FLOAT: - return GL_FLOAT; - case GPU_DATA_INT: - return GL_INT; - case GPU_DATA_UNSIGNED_INT: - return GL_UNSIGNED_INT; - case GPU_DATA_UNSIGNED_BYTE: - return GL_UNSIGNED_BYTE; - case GPU_DATA_UNSIGNED_INT_24_8: - return GL_UNSIGNED_INT_24_8; - case GPU_DATA_10_11_11_REV: - return GL_UNSIGNED_INT_10F_11F_11F_REV; - default: - BLI_assert(!"Unhandled data format"); - return GL_FLOAT; - } + float clear_col[4] = {red, green, blue, alpha}; + GPU_context_active_get()->active_fb->clear(GPU_COLOR_BIT, clear_col, 0.0f, 0x0); } -static GLenum gpu_get_gl_channel_type(int channels) +void GPU_clear_depth(float depth) { - switch (channels) { - case 1: - return GL_RED; - case 2: - return GL_RG; - case 3: - return GL_RGB; - case 4: - return GL_RGBA; - default: - BLI_assert(!"Wrong number of read channels"); - return GL_RED; - } + float clear_col[4] = {0}; + GPU_context_active_get()->active_fb->clear(GPU_DEPTH_BIT, clear_col, depth, 0x0); } -static void gpu_framebuffer_read_color_ex( - int x, int y, int w, int h, int channels, GLenum readfb, eGPUDataFormat format, float *data) +void GPU_framebuffer_read_depth(GPUFrameBuffer *gpu_fb, int x, int y, int w, int h, float *data) { - GLenum type = gpu_get_gl_channel_type(channels); - GLenum gl_format = gpu_get_gl_datatype(format); - /* TODO: needed for selection buffers to work properly, this should be handled better. */ - if (type == GL_RED && gl_format == GL_UNSIGNED_INT) { - type = GL_RED_INTEGER; - } - glReadBuffer(readfb); - glReadPixels(x, y, w, h, type, gl_format, data); + int rect[4] = {x, y, w, h}; + reinterpret_cast<FrameBuffer *>(gpu_fb)->read(GPU_DEPTH_BIT, GPU_DATA_FLOAT, rect, 1, 1, data); } -void GPU_framebuffer_read_color(GPUFrameBuffer *fb, +void GPU_framebuffer_read_color(GPUFrameBuffer *gpu_fb, int x, int y, int w, @@ -713,90 +428,62 @@ void GPU_framebuffer_read_color(GPUFrameBuffer *fb, eGPUDataFormat format, void *data) { - CHECK_FRAMEBUFFER_IS_BOUND(fb); - gpu_framebuffer_read_color_ex( - x, y, w, h, channels, GL_COLOR_ATTACHMENT0 + slot, format, (float *)data); + int rect[4] = {x, y, w, h}; + reinterpret_cast<FrameBuffer *>(gpu_fb)->read(GPU_COLOR_BIT, format, rect, channels, slot, data); +} + +/* TODO(fclem) rename to read_color. */ +void GPU_frontbuffer_read_pixels( + int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data) +{ + int rect[4] = {x, y, w, h}; + GPU_context_active_get()->front_left->read(GPU_COLOR_BIT, format, rect, channels, 0, data); } /* read_slot and write_slot are only used for color buffers. */ -void GPU_framebuffer_blit(GPUFrameBuffer *fb_read, +/* TODO(fclem) port as texture operation. */ +void GPU_framebuffer_blit(GPUFrameBuffer *gpufb_read, int read_slot, - GPUFrameBuffer *fb_write, + GPUFrameBuffer *gpufb_write, int write_slot, eGPUFrameBufferBits blit_buffers) { + FrameBuffer *fb_read = reinterpret_cast<FrameBuffer *>(gpufb_read); + FrameBuffer *fb_write = reinterpret_cast<FrameBuffer *>(gpufb_write); BLI_assert(blit_buffers != 0); - GPUFrameBuffer *prev_fb = GPU_framebuffer_active_get(); + FrameBuffer *prev_fb = GPU_context_active_get()->active_fb; - /* Framebuffers must be up to date. This simplify this function. */ - if (fb_read->dirty_flag != 0 || fb_read->object == 0) { - GPU_framebuffer_bind(fb_read); +#ifndef NDEBUG + GPUTexture *read_tex, *write_tex; + if (blit_buffers & (GPU_DEPTH_BIT | GPU_STENCIL_BIT)) { + read_tex = fb_read->depth_tex(); + write_tex = fb_write->depth_tex(); } - if (fb_write->dirty_flag != 0 || fb_write->object == 0) { - GPU_framebuffer_bind(fb_write); + else { + read_tex = fb_read->color_tex(read_slot); + write_tex = fb_write->color_tex(write_slot); } - const bool do_color = (blit_buffers & GPU_COLOR_BIT); - const bool do_depth = (blit_buffers & GPU_DEPTH_BIT); - const bool do_stencil = (blit_buffers & GPU_STENCIL_BIT); - - GPUTexture *read_tex = ((do_depth || do_stencil) ? - framebuffer_get_depth_tex(fb_read) : - framebuffer_get_color_tex(fb_read, read_slot)); - GPUTexture *write_tex = ((do_depth || do_stencil) ? - framebuffer_get_depth_tex(fb_write) : - framebuffer_get_color_tex(fb_write, read_slot)); - - if (do_depth) { + if (blit_buffers & GPU_DEPTH_BIT) { BLI_assert(GPU_texture_depth(read_tex) && GPU_texture_depth(write_tex)); BLI_assert(GPU_texture_format(read_tex) == GPU_texture_format(write_tex)); } - if (do_stencil) { + if (blit_buffers & GPU_STENCIL_BIT) { BLI_assert(GPU_texture_stencil(read_tex) && GPU_texture_stencil(write_tex)); BLI_assert(GPU_texture_format(read_tex) == GPU_texture_format(write_tex)); } if (GPU_texture_samples(write_tex) != 0 || GPU_texture_samples(read_tex) != 0) { /* Can only blit multisample textures to another texture of the same size. */ - BLI_assert((fb_read->width == fb_write->width) && (fb_read->height == fb_write->height)); + BLI_assert((GPU_texture_width(write_tex) == GPU_texture_width(read_tex)) && + (GPU_texture_height(write_tex) == GPU_texture_height(read_tex))); } +#endif - glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_read->object); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb_write->object); + fb_read->blit_to(blit_buffers, read_slot, fb_write, write_slot, 0, 0); - if (do_color) { - glReadBuffer(GL_COLOR_ATTACHMENT0 + read_slot); - glDrawBuffer(GL_COLOR_ATTACHMENT0 + write_slot); - /* XXX we messed with the glDrawBuffer, this will reset the - * glDrawBuffers the next time we bind fb_write. */ - fb_write->dirty_flag = GPU_FB_DIRTY_DRAWBUFFER; - } - - GLbitfield mask = convert_buffer_bits_to_gl(blit_buffers); - - glBlitFramebuffer(0, - 0, - fb_read->width, - fb_read->height, - 0, - 0, - fb_write->width, - fb_write->height, - mask, - GL_NEAREST); - - /* Restore previous framebuffer */ - if (fb_write == prev_fb) { - GPU_framebuffer_bind(fb_write); /* To update drawbuffers */ - } - else if (prev_fb) { - glBindFramebuffer(GL_FRAMEBUFFER, prev_fb->object); - gpu_framebuffer_current_set(prev_fb); - } - else { - glBindFramebuffer(GL_FRAMEBUFFER, GPU_framebuffer_default()); - gpu_framebuffer_current_set(NULL); - } + /* FIXME(fclem) sRGB is not saved. */ + prev_fb->bind(true); } /** @@ -804,80 +491,45 @@ void GPU_framebuffer_blit(GPUFrameBuffer *fb_read, * input. This function only takes care of the correct texture handling. It execute the callback * for each texture level. */ -void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb, +void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *gpu_fb, int max_lvl, void (*callback)(void *userData, int level), void *userData) { - /* Framebuffer must be up to date and bound. This simplify this function. */ - if (GPU_framebuffer_active_get() != fb || fb->dirty_flag != 0 || fb->object == 0) { - GPU_framebuffer_bind(fb); - } - /* HACK: We make the framebuffer appear not bound in order to - * not trigger any error in GPU_texture_bind(). */ - GPUFrameBuffer *prev_fb = GPU_framebuffer_active_get(); - gpu_framebuffer_current_set(NULL); - - int levels = floor(log2(max_ii(fb->width, fb->height))); - max_lvl = min_ii(max_lvl, levels); + reinterpret_cast<FrameBuffer *>(gpu_fb)->recursive_downsample(max_lvl, callback, userData); +} - int i; - int current_dim[2] = {fb->width, fb->height}; - for (i = 1; i < max_lvl + 1; i++) { - /* calculate next viewport size */ - current_dim[0] = max_ii(current_dim[0] / 2, 1); - current_dim[1] = max_ii(current_dim[1] / 2, 1); +/** \} */ - for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { - GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); - if (fb->attachments[type].tex != NULL) { - /* Some Intel HDXXX have issue with rendering to a mipmap that is below - * the texture GL_TEXTURE_MAX_LEVEL. So even if it not correct, in this case - * we allow GL_TEXTURE_MAX_LEVEL to be one level lower. In practice it does work! */ - int next_lvl = (GPU_mip_render_workaround()) ? i : i - 1; - /* bind next level for rendering but first restrict fetches only to previous level */ - GPUTexture *tex = fb->attachments[type].tex; - GPU_texture_bind(tex, 0); - glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, i - 1); - glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, next_lvl); - GPU_texture_unbind(tex); - /* copy attachment and replace miplevel. */ - GPUAttachment attachment = fb->attachments[type]; - attachment.mip = i; - gpu_framebuffer_attachment_attach(&attachment, type); - } - } - - BLI_assert(GL_FRAMEBUFFER_COMPLETE == glCheckFramebufferStatus(GL_FRAMEBUFFER)); +/* -------------------------------------------------------------------- */ +/** \name GPUOffScreen + * + * Container that holds a framebuffer and its textures. + * Might be bound to multiple contexts. + * \{ */ - glViewport(0, 0, current_dim[0], current_dim[1]); - callback(userData, i); +#define FRAMEBUFFER_STACK_DEPTH 16 - if (current_dim[0] == 1 && current_dim[1] == 1) { - break; - } - } +static struct { + GPUFrameBuffer *framebuffers[FRAMEBUFFER_STACK_DEPTH]; + uint top; +} FrameBufferStack = {{0}}; - for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { - GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); - if (fb->attachments[type].tex != NULL) { - /* reset mipmap level range */ - GPUTexture *tex = fb->attachments[type].tex; - GPU_texture_bind(tex, 0); - glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_BASE_LEVEL, 0); - glTexParameteri(GPU_texture_target(tex), GL_TEXTURE_MAX_LEVEL, i - 1); - GPU_texture_unbind(tex); - /* Reattach original level */ - /* NOTE: This is not necessary but this makes the FBO config - * remain in sync with the GPUFrameBuffer config. */ - gpu_framebuffer_attachment_attach(&fb->attachments[type], type); - } - } +static void gpuPushFrameBuffer(GPUFrameBuffer *fb) +{ + BLI_assert(FrameBufferStack.top < FRAMEBUFFER_STACK_DEPTH); + FrameBufferStack.framebuffers[FrameBufferStack.top] = fb; + FrameBufferStack.top++; +} - gpu_framebuffer_current_set(prev_fb); +static GPUFrameBuffer *gpuPopFrameBuffer(void) +{ + BLI_assert(FrameBufferStack.top > 0); + FrameBufferStack.top--; + return FrameBufferStack.framebuffers[FrameBufferStack.top]; } -/* GPUOffScreen */ +#undef FRAMEBUFFER_STACK_DEPTH #define MAX_CTX_FB_LEN 3 @@ -952,21 +604,14 @@ GPUOffScreen *GPU_offscreen_create( return NULL; } - gpuPushAttr(GPU_VIEWPORT_BIT); - GPUFrameBuffer *fb = gpu_offscreen_fb_get(ofs); /* check validity at the very end! */ if (!GPU_framebuffer_check_valid(fb, err_out)) { GPU_offscreen_free(ofs); - gpuPopAttr(); return NULL; } - GPU_framebuffer_restore(); - - gpuPopAttr(); - return ofs; } @@ -990,23 +635,16 @@ void GPU_offscreen_free(GPUOffScreen *ofs) void GPU_offscreen_bind(GPUOffScreen *ofs, bool save) { if (save) { - gpuPushAttr((eGPUAttrMask)(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT)); GPUFrameBuffer *fb = GPU_framebuffer_active_get(); - gpuPushFrameBuffer(fb); + gpuPushFrameBuffer(reinterpret_cast<GPUFrameBuffer *>(fb)); } - GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs); - GPU_framebuffer_bind(ofs_fb); - glDisable(GL_FRAMEBUFFER_SRGB); - GPU_scissor_test(false); - GPU_shader_set_framebuffer_srgb_target(false); + reinterpret_cast<FrameBuffer *>(gpu_offscreen_fb_get(ofs))->bind(false); } void GPU_offscreen_unbind(GPUOffScreen *UNUSED(ofs), bool restore) { GPUFrameBuffer *fb = NULL; - if (restore) { - gpuPopAttr(); fb = gpuPopFrameBuffer(); } @@ -1020,33 +658,20 @@ void GPU_offscreen_unbind(GPUOffScreen *UNUSED(ofs), bool restore) void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y) { - const int w = GPU_texture_width(ofs->color); - const int h = GPU_texture_height(ofs->color); - - GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs); - - glBindFramebuffer(GL_READ_FRAMEBUFFER, ofs_fb->object); - GLenum status = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); - - if (status == GL_FRAMEBUFFER_COMPLETE) { - glBlitFramebuffer(0, 0, w, h, x, y, x + w, y + h, GL_COLOR_BUFFER_BIT, GL_NEAREST); - } - else { - gpu_print_framebuffer_error(status, NULL); - } - - glBindFramebuffer(GL_READ_FRAMEBUFFER, GPU_framebuffer_default()); + GPUContext *ctx = GPU_context_active_get(); + FrameBuffer *ofs_fb = reinterpret_cast<FrameBuffer *>(gpu_offscreen_fb_get(ofs)); + ofs_fb->blit_to(GPU_COLOR_BIT, 0, ctx->active_fb, 0, x, y); } -void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat type, void *pixels) +void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat format, void *pixels) { + BLI_assert(ELEM(format, GPU_DATA_UNSIGNED_BYTE, GPU_DATA_FLOAT)); + const int w = GPU_texture_width(ofs->color); const int h = GPU_texture_height(ofs->color); - BLI_assert(ELEM(type, GPU_DATA_UNSIGNED_BYTE, GPU_DATA_FLOAT)); - GLenum gl_type = (type == GPU_DATA_FLOAT) ? GL_FLOAT : GL_UNSIGNED_BYTE; - - glReadPixels(0, 0, w, h, GL_RGBA, gl_type, pixels); + GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs); + GPU_framebuffer_read_color(ofs_fb, 0, 0, w, h, 4, 0, format, pixels); } int GPU_offscreen_width(const GPUOffScreen *ofs) @@ -1075,37 +700,4 @@ void GPU_offscreen_viewport_data_get(GPUOffScreen *ofs, *r_depth = ofs->depth; } -void GPU_clear_color(float red, float green, float blue, float alpha) -{ - glClearColor(red, green, blue, alpha); -} - -void GPU_clear_depth(float depth) -{ - glClearDepth(depth); -} - -void GPU_clear(eGPUFrameBufferBits flags) -{ - glClear(convert_buffer_bits_to_gl(flags)); -} - -void GPU_frontbuffer_read_pixels( - int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data) -{ - gpu_framebuffer_read_color_ex(x, y, w, h, channels, GL_FRONT, format, (float *)data); -} - -/* For stereo rendering. */ -void GPU_backbuffer_bind(eGPUBackBuffer buffer) -{ - if (buffer == GPU_BACKBUFFER) { - glDrawBuffer(GL_BACK); - } - else if (buffer == GPU_BACKBUFFER_LEFT) { - glDrawBuffer(GL_BACK_LEFT); - } - else if (buffer == GPU_BACKBUFFER_RIGHT) { - glDrawBuffer(GL_BACK_RIGHT); - } -} +/** \} */
\ No newline at end of file diff --git a/source/blender/gpu/intern/gpu_framebuffer_private.hh b/source/blender/gpu/intern/gpu_framebuffer_private.hh new file mode 100644 index 00000000000..3fba0c8de92 --- /dev/null +++ b/source/blender/gpu/intern/gpu_framebuffer_private.hh @@ -0,0 +1,211 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GPU Framebuffer + * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice + * multiple FBO's may be created. + * - actual FBO creation & config is deferred until GPU_framebuffer_bind or + * GPU_framebuffer_check_valid to allow creation & config while another + * opengl context is bound (since FBOs are not shared between ogl contexts). + */ + +#pragma once + +#include "BLI_math_vector.h" +#include "BLI_span.hh" + +#include "MEM_guardedalloc.h" + +#include "GPU_framebuffer.h" + +struct GPUTexture; + +typedef enum GPUAttachmentType : int { + GPU_FB_DEPTH_ATTACHMENT = 0, + GPU_FB_DEPTH_STENCIL_ATTACHMENT, + GPU_FB_COLOR_ATTACHMENT0, + GPU_FB_COLOR_ATTACHMENT1, + GPU_FB_COLOR_ATTACHMENT2, + GPU_FB_COLOR_ATTACHMENT3, + GPU_FB_COLOR_ATTACHMENT4, + GPU_FB_COLOR_ATTACHMENT5, + /* Number of maximum output slots. + * We support 6 outputs for now (usually we wouldn't need more to preserve fill rate). */ + /* Keep in mind that GL max is GL_MAX_DRAW_BUFFERS and is at least 8, corresponding to + * the maximum number of COLOR attachments specified by glDrawBuffers. */ + GPU_FB_MAX_ATTACHEMENT, + + GPU_FB_MAX_COLOR_ATTACHMENT = (GPU_FB_MAX_ATTACHEMENT - GPU_FB_COLOR_ATTACHMENT0), +} GPUAttachmentType; + +inline constexpr GPUAttachmentType operator-(GPUAttachmentType a, int b) +{ + return static_cast<GPUAttachmentType>(static_cast<int>(a) - b); +} + +inline constexpr GPUAttachmentType operator+(GPUAttachmentType a, int b) +{ + return static_cast<GPUAttachmentType>(static_cast<int>(a) + b); +} + +inline GPUAttachmentType &operator++(GPUAttachmentType &a) +{ + a = a + 1; + return a; +} + +inline GPUAttachmentType &operator--(GPUAttachmentType &a) +{ + a = a - 1; + return a; +} + +namespace blender { +namespace gpu { + +#ifdef DEBUG +# define DEBUG_NAME_LEN 64 +#else +# define DEBUG_NAME_LEN 16 +#endif + +class FrameBuffer { + protected: + /** Set of texture attachements to render to. DEPTH and DEPTH_STENCIL are mutualy exclusive. */ + GPUAttachment attachments_[GPU_FB_MAX_ATTACHEMENT]; + /** Is true if internal representation need to be updated. */ + bool dirty_attachments_; + /** Size of attachement textures. */ + int width_, height_; + /** Debug name. */ + char name_[DEBUG_NAME_LEN]; + /** Framebuffer state. */ + int viewport_[4]; + int scissor_[4]; + bool scissor_test_ = false; + bool dirty_state_; + + public: + FrameBuffer(const char *name); + virtual ~FrameBuffer(); + + virtual void bind(bool enabled_srgb) = 0; + virtual bool check(char err_out[256]) = 0; + virtual void clear(eGPUFrameBufferBits buffers, + const float clear_col[4], + float clear_depth, + uint clear_stencil) = 0; + virtual void clear_multi(const float (*clear_col)[4]) = 0; + + virtual void read(eGPUFrameBufferBits planes, + eGPUDataFormat format, + const int area[4], + int channel_len, + int slot, + void *r_data) = 0; + + virtual void blit_to(eGPUFrameBufferBits planes, + int src_slot, + FrameBuffer *dst, + int dst_slot, + int dst_offset_x, + int dst_offset_y) = 0; + + void attachment_set(GPUAttachmentType type, const GPUAttachment &new_attachment); + + void recursive_downsample(int max_lvl, + void (*callback)(void *userData, int level), + void *userData); + + inline void size_set(int width, int height) + { + width_ = width; + height_ = height; + dirty_state_ = true; + } + + inline void viewport_set(const int viewport[4]) + { + if (!equals_v4v4_int(viewport_, viewport)) { + copy_v4_v4_int(viewport_, viewport); + dirty_state_ = true; + } + } + + inline void scissor_set(const int scissor[4]) + { + if (!equals_v4v4_int(scissor_, scissor)) { + copy_v4_v4_int(scissor_, scissor); + dirty_state_ = true; + } + } + + inline void scissor_test_set(bool test) + { + scissor_test_ = test; + } + + inline void viewport_get(int r_viewport[4]) const + { + copy_v4_v4_int(r_viewport, viewport_); + } + + inline void scissor_get(int r_scissor[4]) const + { + copy_v4_v4_int(r_scissor, scissor_); + } + + inline bool scissor_test_get(void) const + { + return scissor_test_; + } + + inline void viewport_reset(void) + { + int viewport_rect[4] = {0, 0, width_, height_}; + viewport_set(viewport_rect); + } + + inline void scissor_reset(void) + { + int scissor_rect[4] = {0, 0, width_, height_}; + scissor_set(scissor_rect); + } + + inline GPUTexture *depth_tex(void) const + { + if (attachments_[GPU_FB_DEPTH_ATTACHMENT].tex) { + return attachments_[GPU_FB_DEPTH_ATTACHMENT].tex; + } + return attachments_[GPU_FB_DEPTH_STENCIL_ATTACHMENT].tex; + }; + + inline GPUTexture *color_tex(int slot) const + { + return attachments_[GPU_FB_COLOR_ATTACHMENT0 + slot].tex; + }; +}; + +#undef DEBUG_NAME_LEN + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc index 9cededa54f7..c5dd84ddbd0 100644 --- a/source/blender/gpu/intern/gpu_immediate.cc +++ b/source/blender/gpu/intern/gpu_immediate.cc @@ -20,147 +20,66 @@ /** \file * \ingroup gpu * - * GPU immediate mode work-alike + * Mimics old style opengl immediate mode drawing. */ #ifndef GPU_STANDALONE # include "UI_resources.h" #endif -#include "GPU_attr_binding.h" #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_texture.h" -#include "gpu_attr_binding_private.h" #include "gpu_context_private.hh" -#include "gpu_primitive_private.h" -#include "gpu_shader_private.h" +#include "gpu_immediate_private.hh" +#include "gpu_shader_private.hh" #include "gpu_vertex_format_private.h" -#include <stdlib.h> -#include <string.h> +using namespace blender::gpu; -typedef struct ImmediateDrawBuffer { - GLuint vbo_id; - GLubyte *buffer_data; - uint buffer_offset; - uint buffer_size; -} ImmediateDrawBuffer; - -typedef struct { - /* TODO: organize this struct by frequency of change (run-time) */ - - GPUBatch *batch; - GPUContext *context; - - /* current draw call */ - bool strict_vertex_len; - uint vertex_len; - uint buffer_bytes_mapped; - ImmediateDrawBuffer *active_buffer; - GPUPrimType prim_type; - GPUVertFormat vertex_format; - ImmediateDrawBuffer draw_buffer; - ImmediateDrawBuffer draw_buffer_strict; - - /* current vertex */ - uint vertex_idx; - GLubyte *vertex_data; - uint16_t - unassigned_attr_bits; /* which attributes of current vertex have not been given values? */ - - GLuint vao_id; - - GPUShader *bound_program; - const GPUShaderInterface *shader_interface; - GPUAttrBinding attr_binding; - uint16_t prev_enabled_attr_bits; /* <-- only affects this VAO, so we're ok */ -} Immediate; - -/* size of internal buffer */ -#define DEFAULT_INTERNAL_BUFFER_SIZE (4 * 1024 * 1024) - -static bool initialized = false; -static Immediate imm; +static Immediate *imm = NULL; void immInit(void) { -#if TRUST_NO_ONE - assert(!initialized); -#endif - memset(&imm, 0, sizeof(Immediate)); - - imm.draw_buffer.vbo_id = GPU_buf_alloc(); - imm.draw_buffer.buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE; - glBindBuffer(GL_ARRAY_BUFFER, imm.draw_buffer.vbo_id); - glBufferData(GL_ARRAY_BUFFER, imm.draw_buffer.buffer_size, NULL, GL_DYNAMIC_DRAW); - imm.draw_buffer_strict.vbo_id = GPU_buf_alloc(); - imm.draw_buffer_strict.buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE; - glBindBuffer(GL_ARRAY_BUFFER, imm.draw_buffer_strict.vbo_id); - glBufferData(GL_ARRAY_BUFFER, imm.draw_buffer_strict.buffer_size, NULL, GL_DYNAMIC_DRAW); - - imm.prim_type = GPU_PRIM_NONE; - imm.strict_vertex_len = true; - - glBindBuffer(GL_ARRAY_BUFFER, 0); - initialized = true; + /* TODO Remove */ } void immActivate(void) { -#if TRUST_NO_ONE - assert(initialized); - assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we're not between a Begin/End pair */ - assert(imm.vao_id == 0); -#endif - imm.vao_id = GPU_vao_alloc(); - imm.context = GPU_context_active_get(); + imm = GPU_context_active_get()->imm; } void immDeactivate(void) { -#if TRUST_NO_ONE - assert(initialized); - assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we're not between a Begin/End pair */ - assert(imm.vao_id != 0); -#endif - GPU_vao_free(imm.vao_id, imm.context); - imm.vao_id = 0; - imm.prev_enabled_attr_bits = 0; + imm = NULL; } void immDestroy(void) { - GPU_buf_free(imm.draw_buffer.vbo_id); - GPU_buf_free(imm.draw_buffer_strict.vbo_id); - initialized = false; + /* TODO Remove */ } GPUVertFormat *immVertexFormat(void) { - GPU_vertformat_clear(&imm.vertex_format); - return &imm.vertex_format; + GPU_vertformat_clear(&imm->vertex_format); + return &imm->vertex_format; } void immBindShader(GPUShader *shader) { -#if TRUST_NO_ONE - assert(imm.bound_program == NULL); - assert(glIsProgram(shader->program)); -#endif + BLI_assert(imm->shader == NULL); - imm.bound_program = shader; - imm.shader_interface = shader->interface; + imm->shader = shader; - if (!imm.vertex_format.packed) { - VertexFormat_pack(&imm.vertex_format); + if (!imm->vertex_format.packed) { + VertexFormat_pack(&imm->vertex_format); + imm->enabled_attr_bits = 0xFFFFu & ~(0xFFFFu << imm->vertex_format.attr_len); } GPU_shader_bind(shader); - get_attr_locations(&imm.vertex_format, &imm.attr_binding, imm.shader_interface); - GPU_matrix_bind(imm.shader_interface); - GPU_shader_set_srgb_uniform(imm.shader_interface); + GPU_matrix_bind(shader); + GPU_shader_set_srgb_uniform(shader); } void immBindBuiltinProgram(eGPUBuiltinShader shader_id) @@ -171,22 +90,19 @@ void immBindBuiltinProgram(eGPUBuiltinShader shader_id) void immUnbindProgram(void) { -#if TRUST_NO_ONE - assert(imm.bound_program != NULL); -#endif -#if PROGRAM_NO_OPTI - glUseProgram(0); -#endif - imm.bound_program = NULL; + BLI_assert(imm->shader != NULL); + + GPU_shader_unbind(); + imm->shader = NULL; } /* XXX do not use it. Special hack to use OCIO with batch API. */ GPUShader *immGetShader(void) { - return imm.bound_program; + return imm->shader; } -#if TRUST_NO_ONE +#ifndef NDEBUG static bool vertex_count_makes_sense_for_primitive(uint vertex_len, GPUPrimType prim_type) { /* does vertex_len make sense for this primitive type? */ @@ -217,285 +133,122 @@ static bool vertex_count_makes_sense_for_primitive(uint vertex_len, GPUPrimType void immBegin(GPUPrimType prim_type, uint vertex_len) { -#if TRUST_NO_ONE - assert(initialized); - assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we haven't already begun */ - assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type)); - assert(imm.active_buffer == NULL); -#endif - imm.prim_type = prim_type; - imm.vertex_len = vertex_len; - imm.vertex_idx = 0; - imm.unassigned_attr_bits = imm.attr_binding.enabled_bits; - - /* how many bytes do we need for this draw call? */ - const uint bytes_needed = vertex_buffer_size(&imm.vertex_format, vertex_len); - ImmediateDrawBuffer *active_buffer = imm.strict_vertex_len ? &imm.draw_buffer_strict : - &imm.draw_buffer; - imm.active_buffer = active_buffer; - - glBindBuffer(GL_ARRAY_BUFFER, active_buffer->vbo_id); - - /* does the current buffer have enough room? */ - const uint available_bytes = active_buffer->buffer_size - active_buffer->buffer_offset; - - bool recreate_buffer = false; - if (bytes_needed > active_buffer->buffer_size) { - /* expand the internal buffer */ - active_buffer->buffer_size = bytes_needed; - recreate_buffer = true; - } - else if (bytes_needed < DEFAULT_INTERNAL_BUFFER_SIZE && - active_buffer->buffer_size > DEFAULT_INTERNAL_BUFFER_SIZE) { - /* shrink the internal buffer */ - active_buffer->buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE; - recreate_buffer = true; - } - - /* ensure vertex data is aligned */ - /* Might waste a little space, but it's safe. */ - const uint pre_padding = padding(active_buffer->buffer_offset, imm.vertex_format.stride); - - if (!recreate_buffer && ((bytes_needed + pre_padding) <= available_bytes)) { - active_buffer->buffer_offset += pre_padding; - } - else { - /* orphan this buffer & start with a fresh one */ - /* this method works on all platforms, old & new */ - glBufferData(GL_ARRAY_BUFFER, active_buffer->buffer_size, NULL, GL_DYNAMIC_DRAW); - - active_buffer->buffer_offset = 0; - } - - /* printf("mapping %u to %u\n", imm.buffer_offset, imm.buffer_offset + bytes_needed - 1); */ - -#if TRUST_NO_ONE - { - GLint bufsize; - glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &bufsize); - assert(active_buffer->buffer_offset + bytes_needed <= bufsize); - } -#endif - - active_buffer->buffer_data = (GLubyte *)glMapBufferRange( - GL_ARRAY_BUFFER, - active_buffer->buffer_offset, - bytes_needed, - GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | - (imm.strict_vertex_len ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT)); + BLI_assert(imm->prim_type == GPU_PRIM_NONE); /* Make sure we haven't already begun. */ + BLI_assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type)); -#if TRUST_NO_ONE - assert(active_buffer->buffer_data != NULL); -#endif + imm->prim_type = prim_type; + imm->vertex_len = vertex_len; + imm->vertex_idx = 0; + imm->unassigned_attr_bits = imm->enabled_attr_bits; - imm.buffer_bytes_mapped = bytes_needed; - imm.vertex_data = active_buffer->buffer_data; + imm->vertex_data = imm->begin(); } void immBeginAtMost(GPUPrimType prim_type, uint vertex_len) { -#if TRUST_NO_ONE - assert(vertex_len > 0); -#endif - - imm.strict_vertex_len = false; + BLI_assert(vertex_len > 0); + imm->strict_vertex_len = false; immBegin(prim_type, vertex_len); } GPUBatch *immBeginBatch(GPUPrimType prim_type, uint vertex_len) { -#if TRUST_NO_ONE - assert(initialized); - assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we haven't already begun */ - assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type)); -#endif - imm.prim_type = prim_type; - imm.vertex_len = vertex_len; - imm.vertex_idx = 0; - imm.unassigned_attr_bits = imm.attr_binding.enabled_bits; + BLI_assert(imm->prim_type == GPU_PRIM_NONE); /* Make sure we haven't already begun. */ + BLI_assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type)); + + imm->prim_type = prim_type; + imm->vertex_len = vertex_len; + imm->vertex_idx = 0; + imm->unassigned_attr_bits = imm->enabled_attr_bits; - GPUVertBuf *verts = GPU_vertbuf_create_with_format(&imm.vertex_format); + GPUVertBuf *verts = GPU_vertbuf_create_with_format(&imm->vertex_format); GPU_vertbuf_data_alloc(verts, vertex_len); - imm.buffer_bytes_mapped = GPU_vertbuf_size_get(verts); - imm.vertex_data = verts->data; + imm->vertex_data = verts->data; - imm.batch = GPU_batch_create_ex(prim_type, verts, NULL, GPU_BATCH_OWNS_VBO); - imm.batch->phase = GPU_BATCH_BUILDING; + imm->batch = GPU_batch_create_ex(prim_type, verts, NULL, GPU_BATCH_OWNS_VBO); + imm->batch->flag |= GPU_BATCH_BUILDING; - return imm.batch; + return imm->batch; } GPUBatch *immBeginBatchAtMost(GPUPrimType prim_type, uint vertex_len) { - imm.strict_vertex_len = false; + BLI_assert(vertex_len > 0); + imm->strict_vertex_len = false; return immBeginBatch(prim_type, vertex_len); } -static void immDrawSetup(void) -{ - /* set up VAO -- can be done during Begin or End really */ - glBindVertexArray(imm.vao_id); - - /* Enable/Disable vertex attributes as needed. */ - if (imm.attr_binding.enabled_bits != imm.prev_enabled_attr_bits) { - for (uint loc = 0; loc < GPU_VERT_ATTR_MAX_LEN; loc++) { - bool is_enabled = imm.attr_binding.enabled_bits & (1 << loc); - bool was_enabled = imm.prev_enabled_attr_bits & (1 << loc); - - if (is_enabled && !was_enabled) { - glEnableVertexAttribArray(loc); - } - else if (was_enabled && !is_enabled) { - glDisableVertexAttribArray(loc); - } - } - - imm.prev_enabled_attr_bits = imm.attr_binding.enabled_bits; - } - - const uint stride = imm.vertex_format.stride; - - for (uint a_idx = 0; a_idx < imm.vertex_format.attr_len; a_idx++) { - const GPUVertAttr *a = &imm.vertex_format.attrs[a_idx]; - - const uint offset = imm.active_buffer->buffer_offset + a->offset; - const GLvoid *pointer = (const GLubyte *)0 + offset; - - const uint loc = read_attr_location(&imm.attr_binding, a_idx); - const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type)); - - switch (a->fetch_mode) { - case GPU_FETCH_FLOAT: - case GPU_FETCH_INT_TO_FLOAT: - glVertexAttribPointer(loc, a->comp_len, type, GL_FALSE, stride, pointer); - break; - case GPU_FETCH_INT_TO_FLOAT_UNIT: - glVertexAttribPointer(loc, a->comp_len, type, GL_TRUE, stride, pointer); - break; - case GPU_FETCH_INT: - glVertexAttribIPointer(loc, a->comp_len, type, stride, pointer); - } - } - - if (GPU_matrix_dirty_get()) { - GPU_matrix_bind(imm.shader_interface); - } -} - void immEnd(void) { -#if TRUST_NO_ONE - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ - assert(imm.active_buffer || imm.batch); -#endif + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* Make sure we're between a Begin/End pair. */ + BLI_assert(imm->vertex_data || imm->batch); - uint buffer_bytes_used; - if (imm.strict_vertex_len) { -#if TRUST_NO_ONE - assert(imm.vertex_idx == imm.vertex_len); /* with all vertices defined */ -#endif - buffer_bytes_used = imm.buffer_bytes_mapped; + if (imm->strict_vertex_len) { + BLI_assert(imm->vertex_idx == imm->vertex_len); /* With all vertices defined. */ } else { -#if TRUST_NO_ONE - assert(imm.vertex_idx <= imm.vertex_len); -#endif - if (imm.vertex_idx == imm.vertex_len) { - buffer_bytes_used = imm.buffer_bytes_mapped; - } - else { -#if TRUST_NO_ONE - assert(imm.vertex_idx == 0 || - vertex_count_makes_sense_for_primitive(imm.vertex_idx, imm.prim_type)); -#endif - imm.vertex_len = imm.vertex_idx; - buffer_bytes_used = vertex_buffer_size(&imm.vertex_format, imm.vertex_len); - /* unused buffer bytes are available to the next immBegin */ - } - /* tell OpenGL what range was modified so it doesn't copy the whole mapped range */ - glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, buffer_bytes_used); + BLI_assert(imm->vertex_idx <= imm->vertex_len); + BLI_assert(imm->vertex_idx == 0 || + vertex_count_makes_sense_for_primitive(imm->vertex_idx, imm->prim_type)); } - if (imm.batch) { - if (buffer_bytes_used != imm.buffer_bytes_mapped) { - GPU_vertbuf_data_resize(imm.batch->verts[0], imm.vertex_len); + if (imm->batch) { + if (imm->vertex_idx < imm->vertex_len) { + GPU_vertbuf_data_resize(imm->batch->verts[0], imm->vertex_len); /* TODO: resize only if vertex count is much smaller */ } - GPU_batch_set_shader(imm.batch, imm.bound_program); - imm.batch->phase = GPU_BATCH_READY_TO_DRAW; - imm.batch = NULL; /* don't free, batch belongs to caller */ + GPU_batch_set_shader(imm->batch, imm->shader); + imm->batch->flag &= ~GPU_BATCH_BUILDING; + imm->batch = NULL; /* don't free, batch belongs to caller */ } else { - glUnmapBuffer(GL_ARRAY_BUFFER); - - if (imm.vertex_len > 0) { - immDrawSetup(); -#ifdef __APPLE__ - glDisable(GL_PRIMITIVE_RESTART); -#endif - glDrawArrays(convert_prim_type_to_gl(imm.prim_type), 0, imm.vertex_len); -#ifdef __APPLE__ - glEnable(GL_PRIMITIVE_RESTART); -#endif - } - /* These lines are causing crash on startup on some old GPU + drivers. - * They are not required so just comment them. (T55722) */ - // glBindBuffer(GL_ARRAY_BUFFER, 0); - // glBindVertexArray(0); - /* prep for next immBegin */ - imm.active_buffer->buffer_offset += buffer_bytes_used; + imm->end(); } - /* prep for next immBegin */ - imm.prim_type = GPU_PRIM_NONE; - imm.strict_vertex_len = true; - imm.active_buffer = NULL; + /* Prepare for next immBegin. */ + imm->prim_type = GPU_PRIM_NONE; + imm->strict_vertex_len = true; + imm->vertex_data = NULL; } static void setAttrValueBit(uint attr_id) { uint16_t mask = 1 << attr_id; -#if TRUST_NO_ONE - assert(imm.unassigned_attr_bits & mask); /* not already set */ -#endif - imm.unassigned_attr_bits &= ~mask; + BLI_assert(imm->unassigned_attr_bits & mask); /* not already set */ + imm->unassigned_attr_bits &= ~mask; } /* --- generic attribute functions --- */ void immAttr1f(uint attr_id, float x) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_F32); - assert(attr->comp_len == 1); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_F32); + BLI_assert(attr->comp_len == 1); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - float *data = (float *)(imm.vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */ + float *data = (float *)(imm->vertex_data + attr->offset); + /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ data[0] = x; } void immAttr2f(uint attr_id, float x, float y) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_F32); - assert(attr->comp_len == 2); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_F32); + BLI_assert(attr->comp_len == 2); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - float *data = (float *)(imm.vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */ + float *data = (float *)(imm->vertex_data + attr->offset); + /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ data[0] = x; data[1] = y; @@ -503,18 +256,16 @@ void immAttr2f(uint attr_id, float x, float y) void immAttr3f(uint attr_id, float x, float y, float z) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_F32); - assert(attr->comp_len == 3); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_F32); + BLI_assert(attr->comp_len == 3); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - float *data = (float *)(imm.vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */ + float *data = (float *)(imm->vertex_data + attr->offset); + /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ data[0] = x; data[1] = y; @@ -523,18 +274,16 @@ void immAttr3f(uint attr_id, float x, float y, float z) void immAttr4f(uint attr_id, float x, float y, float z, float w) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_F32); - assert(attr->comp_len == 4); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_F32); + BLI_assert(attr->comp_len == 4); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - float *data = (float *)(imm.vertex_data + attr->offset); - /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */ + float *data = (float *)(imm->vertex_data + attr->offset); + /* printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm->buffer_data, data); */ data[0] = x; data[1] = y; @@ -544,34 +293,30 @@ void immAttr4f(uint attr_id, float x, float y, float z, float w) void immAttr1u(uint attr_id, uint x) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_U32); - assert(attr->comp_len == 1); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_U32); + BLI_assert(attr->comp_len == 1); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - uint *data = (uint *)(imm.vertex_data + attr->offset); + uint *data = (uint *)(imm->vertex_data + attr->offset); data[0] = x; } void immAttr2i(uint attr_id, int x, int y) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_I32); - assert(attr->comp_len == 2); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_I32); + BLI_assert(attr->comp_len == 2); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - int *data = (int *)(imm.vertex_data + attr->offset); + int *data = (int *)(imm->vertex_data + attr->offset); data[0] = x; data[1] = y; @@ -579,17 +324,15 @@ void immAttr2i(uint attr_id, int x, int y) void immAttr2s(uint attr_id, short x, short y) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_I16); - assert(attr->comp_len == 2); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_I16); + BLI_assert(attr->comp_len == 2); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - short *data = (short *)(imm.vertex_data + attr->offset); + short *data = (short *)(imm->vertex_data + attr->offset); data[0] = x; data[1] = y; @@ -612,18 +355,16 @@ void immAttr4fv(uint attr_id, const float data[4]) void immAttr3ub(uint attr_id, uchar r, uchar g, uchar b) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_U8); - assert(attr->comp_len == 3); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_U8); + BLI_assert(attr->comp_len == 3); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - GLubyte *data = imm.vertex_data + attr->offset; - /* printf("%s %td %p\n", __FUNCTION__, data - imm.buffer_data, data); */ + uchar *data = imm->vertex_data + attr->offset; + /* printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); */ data[0] = r; data[1] = g; @@ -632,18 +373,16 @@ void immAttr3ub(uint attr_id, uchar r, uchar g, uchar b) void immAttr4ub(uint attr_id, uchar r, uchar g, uchar b, uchar a) { - GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id]; -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(attr->comp_type == GPU_COMP_U8); - assert(attr->comp_len == 4); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + GPUVertAttr *attr = &imm->vertex_format.attrs[attr_id]; + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(attr->comp_type == GPU_COMP_U8); + BLI_assert(attr->comp_len == 4); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); - GLubyte *data = imm.vertex_data + attr->offset; - /* printf("%s %td %p\n", __FUNCTION__, data - imm.buffer_data, data); */ + uchar *data = imm->vertex_data + attr->offset; + /* printf("%s %td %p\n", __FUNCTION__, data - imm->buffer_data, data); */ data[0] = r; data[1] = g; @@ -663,45 +402,39 @@ void immAttr4ubv(uint attr_id, const uchar data[4]) void immAttrSkip(uint attr_id) { -#if TRUST_NO_ONE - assert(attr_id < imm.vertex_format.attr_len); - assert(imm.vertex_idx < imm.vertex_len); - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ -#endif + BLI_assert(attr_id < imm->vertex_format.attr_len); + BLI_assert(imm->vertex_idx < imm->vertex_len); + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ setAttrValueBit(attr_id); } static void immEndVertex(void) /* and move on to the next vertex */ { -#if TRUST_NO_ONE - assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ - assert(imm.vertex_idx < imm.vertex_len); -#endif + BLI_assert(imm->prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ + BLI_assert(imm->vertex_idx < imm->vertex_len); /* Have all attributes been assigned values? * If not, copy value from previous vertex. */ - if (imm.unassigned_attr_bits) { -#if TRUST_NO_ONE - assert(imm.vertex_idx > 0); /* first vertex must have all attributes specified */ -#endif - for (uint a_idx = 0; a_idx < imm.vertex_format.attr_len; a_idx++) { - if ((imm.unassigned_attr_bits >> a_idx) & 1) { - const GPUVertAttr *a = &imm.vertex_format.attrs[a_idx]; + if (imm->unassigned_attr_bits) { + BLI_assert(imm->vertex_idx > 0); /* first vertex must have all attributes specified */ + for (uint a_idx = 0; a_idx < imm->vertex_format.attr_len; a_idx++) { + if ((imm->unassigned_attr_bits >> a_idx) & 1) { + const GPUVertAttr *a = &imm->vertex_format.attrs[a_idx]; #if 0 - printf("copying %s from vertex %u to %u\n", a->name, imm.vertex_idx - 1, imm.vertex_idx); + printf("copying %s from vertex %u to %u\n", a->name, imm->vertex_idx - 1, imm->vertex_idx); #endif - GLubyte *data = imm.vertex_data + a->offset; - memcpy(data, data - imm.vertex_format.stride, a->sz); + GLubyte *data = imm->vertex_data + a->offset; + memcpy(data, data - imm->vertex_format.stride, a->sz); /* TODO: consolidate copy of adjacent attributes */ } } } - imm.vertex_idx++; - imm.vertex_data += imm.vertex_format.stride; - imm.unassigned_attr_bits = imm.attr_binding.enabled_bits; + imm->vertex_idx++; + imm->vertex_data += imm->vertex_format.stride; + imm->unassigned_attr_bits = imm->enabled_attr_bits; } void immVertex2f(uint attr_id, float x, float y) @@ -754,123 +487,77 @@ void immVertex2iv(uint attr_id, const int data[2]) /* --- generic uniform functions --- */ -#if 0 -# if TRUST_NO_ONE -# define GET_UNIFORM \ - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(imm.shader_interface, name); \ - assert(uniform); -# else -# define GET_UNIFORM \ - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(imm.shader_interface, name); -# endif -#else -/* NOTE: It is possible to have uniform fully optimized out from the shader. - * In this case we can't assert failure or allow NULL-pointer dereference. - * TODO(sergey): How can we detect existing-but-optimized-out uniform but still - * catch typos in uniform names passed to immUniform*() functions? */ -# define GET_UNIFORM \ - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(imm.shader_interface, name); \ - if (uniform == NULL) \ - return; -#endif - void immUniform1f(const char *name, float x) { - GET_UNIFORM - glUniform1f(uniform->location, x); + GPU_shader_uniform_1f(imm->shader, name, x); } void immUniform2f(const char *name, float x, float y) { - GET_UNIFORM - glUniform2f(uniform->location, x, y); + GPU_shader_uniform_2f(imm->shader, name, x, y); } void immUniform2fv(const char *name, const float data[2]) { - GET_UNIFORM - glUniform2fv(uniform->location, 1, data); + GPU_shader_uniform_2fv(imm->shader, name, data); } void immUniform3f(const char *name, float x, float y, float z) { - GET_UNIFORM - glUniform3f(uniform->location, x, y, z); + GPU_shader_uniform_3f(imm->shader, name, x, y, z); } void immUniform3fv(const char *name, const float data[3]) { - GET_UNIFORM - glUniform3fv(uniform->location, 1, data); -} - -/* can increase this limit or move to another file */ -#define MAX_UNIFORM_NAME_LEN 60 - -/* Note array index is not supported for name (i.e: "array[0]"). */ -void immUniformArray3fv(const char *name, const float *data, int count) -{ - GET_UNIFORM - glUniform3fv(uniform->location, count, data); + GPU_shader_uniform_3fv(imm->shader, name, data); } void immUniform4f(const char *name, float x, float y, float z, float w) { - GET_UNIFORM - glUniform4f(uniform->location, x, y, z, w); + GPU_shader_uniform_4f(imm->shader, name, x, y, z, w); } void immUniform4fv(const char *name, const float data[4]) { - GET_UNIFORM - glUniform4fv(uniform->location, 1, data); + GPU_shader_uniform_4fv(imm->shader, name, data); } /* Note array index is not supported for name (i.e: "array[0]"). */ void immUniformArray4fv(const char *name, const float *data, int count) { - GET_UNIFORM - glUniform4fv(uniform->location, count, data); + GPU_shader_uniform_4fv_array(imm->shader, name, count, (float(*)[4])data); } void immUniformMatrix4fv(const char *name, const float data[4][4]) { - GET_UNIFORM - glUniformMatrix4fv(uniform->location, 1, GL_FALSE, (float *)data); + GPU_shader_uniform_mat4(imm->shader, name, data); } void immUniform1i(const char *name, int x) { - GET_UNIFORM - glUniform1i(uniform->location, x); -} - -void immUniform4iv(const char *name, const int data[4]) -{ - GET_UNIFORM - glUniform4iv(uniform->location, 1, data); + GPU_shader_uniform_1i(imm->shader, name, x); } void immBindTexture(const char *name, GPUTexture *tex) { - GET_UNIFORM - GPU_texture_bind(tex, uniform->binding); + int binding = GPU_shader_get_texture_binding(imm->shader, name); + GPU_texture_bind(tex, binding); } void immBindTextureSampler(const char *name, GPUTexture *tex, eGPUSamplerState state) { - GET_UNIFORM - GPU_texture_bind_ex(tex, state, uniform->binding, true); + int binding = GPU_shader_get_texture_binding(imm->shader, name); + GPU_texture_bind_ex(tex, state, binding, true); } /* --- convenience functions for setting "uniform vec4 color" --- */ void immUniformColor4f(float r, float g, float b, float a) { - int32_t uniform_loc = GPU_shaderinterface_uniform_builtin(imm.shader_interface, - GPU_UNIFORM_COLOR); + int32_t uniform_loc = GPU_shader_get_builtin_uniform(imm->shader, GPU_UNIFORM_COLOR); BLI_assert(uniform_loc != -1); - glUniform4f(uniform_loc, r, g, b, a); + float data[4] = {r, g, b, a}; + GPU_shader_uniform_vector(imm->shader, uniform_loc, 4, 1, data); } void immUniformColor4fv(const float rgba[4]) @@ -893,8 +580,6 @@ void immUniformColor3fvAlpha(const float rgb[3], float a) immUniformColor4f(rgb[0], rgb[1], rgb[2], a); } -/* TODO: v-- treat as sRGB? --v */ - void immUniformColor3ub(uchar r, uchar g, uchar b) { const float scale = 1.0f / 255.0f; diff --git a/source/blender/gpu/intern/gpu_immediate_private.hh b/source/blender/gpu/intern/gpu_immediate_private.hh new file mode 100644 index 00000000000..aa99fb9a438 --- /dev/null +++ b/source/blender/gpu/intern/gpu_immediate_private.hh @@ -0,0 +1,66 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Mimics old style opengl immediate mode drawing. + */ + +#pragma once + +#include "GPU_batch.h" +#include "GPU_primitive.h" +#include "GPU_shader.h" +#include "GPU_vertex_format.h" + +namespace blender::gpu { + +class Immediate { + public: + /** Pointer to the mapped buffer data for the currect vertex. */ + uchar *vertex_data = NULL; + /** Current vertex index. */ + uint vertex_idx = 0; + /** Length of the buffer in vertices. */ + uint vertex_len = 0; + /** Which attributes of current vertex have not been given values? */ + uint16_t unassigned_attr_bits = 0; + /** Attributes that needs to be set. One bit per attribute. */ + uint16_t enabled_attr_bits = 0; + + /** Current draw call specification. */ + GPUPrimType prim_type = GPU_PRIM_NONE; + GPUVertFormat vertex_format; + GPUShader *shader = NULL; + /** Enforce strict vertex count (disabled when using immBeginAtMost). */ + bool strict_vertex_len = true; + + /** Batch in construction when using immBeginBatch. */ + GPUBatch *batch = NULL; + + public: + Immediate(){}; + virtual ~Immediate(){}; + + virtual uchar *begin(void) = 0; + virtual void end(void) = 0; +}; + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/intern/gpu_init_exit.c b/source/blender/gpu/intern/gpu_init_exit.c index ba0da95eb9d..4cb43db9bce 100644 --- a/source/blender/gpu/intern/gpu_init_exit.c +++ b/source/blender/gpu/intern/gpu_init_exit.c @@ -53,11 +53,6 @@ void GPU_init(void) gpu_codegen_init(); gpu_material_library_init(); - gpu_framebuffer_module_init(); - - if (G.debug & G_DEBUG_GPU) { - gpu_debug_init(); - } gpu_batch_init(); @@ -82,11 +77,6 @@ void GPU_exit(void) gpu_batch_exit(); - if (G.debug & G_DEBUG_GPU) { - gpu_debug_exit(); - } - - gpu_framebuffer_module_exit(); gpu_material_library_exit(); gpu_codegen_exit(); diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 8df1f94238a..1016e766140 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -47,7 +47,7 @@ #include "GPU_material.h" #include "GPU_shader.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "DRW_engine.h" @@ -88,11 +88,11 @@ struct GPUMaterial { eGPUMatFlag flag; /* Used by 2.8 pipeline */ - GPUUniformBuffer *ubo; /* UBOs for shader uniforms. */ + GPUUniformBuf *ubo; /* UBOs for shader uniforms. */ /* Eevee SSS */ - GPUUniformBuffer *sss_profile; /* UBO containing SSS profile. */ - GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */ + GPUUniformBuf *sss_profile; /* UBO containing SSS profile. */ + GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */ float sss_enabled; float sss_radii[3]; int sss_samples; @@ -174,13 +174,13 @@ static void gpu_material_free_single(GPUMaterial *material) GPU_pass_release(material->pass); } if (material->ubo != NULL) { - GPU_uniformbuffer_free(material->ubo); + GPU_uniformbuf_free(material->ubo); } if (material->sss_tex_profile != NULL) { GPU_texture_free(material->sss_tex_profile); } if (material->sss_profile != NULL) { - GPU_uniformbuffer_free(material->sss_profile); + GPU_uniformbuf_free(material->sss_profile); } if (material->coba_tex != NULL) { GPU_texture_free(material->coba_tex); @@ -220,7 +220,7 @@ Material *GPU_material_get_material(GPUMaterial *material) return material->ma; } -GPUUniformBuffer *GPU_material_uniform_buffer_get(GPUMaterial *material) +GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material) { return material->ubo; } @@ -232,7 +232,12 @@ GPUUniformBuffer *GPU_material_uniform_buffer_get(GPUMaterial *material) */ void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs) { - material->ubo = GPU_uniformbuffer_dynamic_create(inputs, NULL); +#ifndef NDEBUG + const char *name = material->name; +#else + const char *name = "Material"; +#endif + material->ubo = GPU_uniformbuf_create_from_list(inputs, name); } /* Eevee Subsurface scattering. */ @@ -507,13 +512,13 @@ void GPU_material_sss_profile_create(GPUMaterial *material, /* Update / Create UBO */ if (material->sss_profile == NULL) { - material->sss_profile = GPU_uniformbuffer_create(sizeof(GPUSssKernelData), NULL, NULL); + material->sss_profile = GPU_uniformbuf_create(sizeof(GPUSssKernelData)); } } -struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, - int sample_len, - GPUTexture **tex_profile) +struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material, + int sample_len, + GPUTexture **tex_profile) { if (!material->sss_enabled) { return NULL; @@ -530,7 +535,7 @@ struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, compute_sss_kernel(&kd, material->sss_radii, sample_len, material->sss_falloff, sharpness); /* Update / Create UBO */ - GPU_uniformbuffer_update(material->sss_profile, &kd); + GPU_uniformbuf_update(material->sss_profile, &kd); /* Update / Create Tex */ float *translucence_profile; @@ -555,9 +560,9 @@ struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, return material->sss_profile; } -struct GPUUniformBuffer *GPU_material_create_sss_profile_ubo(void) +struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void) { - return GPU_uniformbuffer_create(sizeof(GPUSssKernelData), NULL, NULL); + return GPU_uniformbuf_create(sizeof(GPUSssKernelData)); } #undef SSS_EXPONENT @@ -735,7 +740,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, gpu_node_graph_free(&mat->graph); } - /* Only free after GPU_pass_shader_get where GPUUniformBuffer + /* Only free after GPU_pass_shader_get where GPUUniformBuf * read data from the local tree. */ ntreeFreeLocalTree(localtree); MEM_freeN(localtree); diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc index 5d8d77bbf1c..cdb6d303588 100644 --- a/source/blender/gpu/intern/gpu_matrix.cc +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -21,8 +21,6 @@ * \ingroup gpu */ -#include "GPU_shader_interface.h" - #include "gpu_context_private.hh" #include "gpu_matrix_private.h" @@ -643,47 +641,44 @@ const float (*GPU_matrix_normal_inverse_get(float m[3][3]))[3] return m; } -void GPU_matrix_bind(const GPUShaderInterface *shaderface) +void GPU_matrix_bind(GPUShader *shader) { /* set uniform values to matrix stack values * call this before a draw call if desired matrices are dirty * call glUseProgram before this, as glUniform expects program to be bound */ + int32_t MV = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW); + int32_t P = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_PROJECTION); + int32_t MVP = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MVP); - int32_t MV = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MODELVIEW); - int32_t P = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_PROJECTION); - int32_t MVP = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MVP); - - int32_t N = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_NORMAL); - int32_t MV_inv = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MODELVIEW_INV); - int32_t P_inv = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_PROJECTION_INV); + int32_t N = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_NORMAL); + int32_t MV_inv = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW_INV); + int32_t P_inv = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_PROJECTION_INV); - /* XXX(fclem) this works but this assumes shader is unused inside GPU_shader_uniform_vector. */ - GPUShader *sh = NULL; if (MV != -1) { - GPU_shader_uniform_vector(sh, MV, 16, 1, (const float *)GPU_matrix_model_view_get(NULL)); + GPU_shader_uniform_vector(shader, MV, 16, 1, (const float *)GPU_matrix_model_view_get(NULL)); } if (P != -1) { - GPU_shader_uniform_vector(sh, P, 16, 1, (const float *)GPU_matrix_projection_get(NULL)); + GPU_shader_uniform_vector(shader, P, 16, 1, (const float *)GPU_matrix_projection_get(NULL)); } if (MVP != -1) { GPU_shader_uniform_vector( - sh, MVP, 16, 1, (const float *)GPU_matrix_model_view_projection_get(NULL)); + shader, MVP, 16, 1, (const float *)GPU_matrix_model_view_projection_get(NULL)); } if (N != -1) { - GPU_shader_uniform_vector(sh, N, 9, 1, (const float *)GPU_matrix_normal_get(NULL)); + GPU_shader_uniform_vector(shader, N, 9, 1, (const float *)GPU_matrix_normal_get(NULL)); } if (MV_inv != -1) { Mat4 m; GPU_matrix_model_view_get(m); invert_m4(m); - GPU_shader_uniform_vector(sh, MV_inv, 16, 1, (const float *)m); + GPU_shader_uniform_vector(shader, MV_inv, 16, 1, (const float *)m); } if (P_inv != -1) { Mat4 m; GPU_matrix_projection_get(m); invert_m4(m); - GPU_shader_uniform_vector(sh, P_inv, 16, 1, (const float *)m); + GPU_shader_uniform_vector(shader, P_inv, 16, 1, (const float *)m); } gpu_matrix_state_active_set_dirty(false); @@ -734,8 +729,8 @@ float GPU_polygon_offset_calc(const float (*winmat)[4], float viewdist, float di #else static float depth_fac = 0.0f; if (depth_fac == 0.0f) { - int depthbits; - glGetIntegerv(GL_DEPTH_BITS, &depthbits); + /* Hardcode for 24 bit precision. */ + int depthbits = 24; depth_fac = 1.0f / (float)((1 << depthbits) - 1); } offs = (-1.0 / winmat[2][2]) * dist * depth_fac; diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index 81cf2d69f4d..1b8a5e20240 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -592,10 +592,10 @@ bool GPU_stack_link(GPUMaterial *material, return true; } -GPUNodeLink *GPU_uniformbuffer_link_out(GPUMaterial *mat, - bNode *node, - GPUNodeStack *stack, - const int index) +GPUNodeLink *GPU_uniformbuf_link_out(GPUMaterial *mat, + bNode *node, + GPUNodeStack *stack, + const int index) { return gpu_uniformbuffer_link(mat, node, stack, index, SOCK_OUT); } diff --git a/source/blender/gpu/intern/gpu_private.h b/source/blender/gpu/intern/gpu_private.h index ef96bedae4a..505ac3b0278 100644 --- a/source/blender/gpu/intern/gpu_private.h +++ b/source/blender/gpu/intern/gpu_private.h @@ -32,14 +32,6 @@ void gpu_platform_exit(void); void gpu_extensions_init(void); void gpu_extensions_exit(void); -/* gpu_debug.c */ -void gpu_debug_init(void); -void gpu_debug_exit(void); - -/* gpu_framebuffer.c */ -void gpu_framebuffer_module_init(void); -void gpu_framebuffer_module_exit(void); - /* gpu_pbvh.c */ void gpu_pbvh_init(void); void gpu_pbvh_exit(void); diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c index 0f6f29fab40..c3ccb68a998 100644 --- a/source/blender/gpu/intern/gpu_select_pick.c +++ b/source/blender/gpu/intern/gpu_select_pick.c @@ -27,6 +27,7 @@ #include <stdlib.h> #include <string.h> +#include "GPU_framebuffer.h" #include "GPU_glew.h" #include "GPU_immediate.h" #include "GPU_select.h" @@ -282,6 +283,12 @@ typedef struct GPUPickState { uint *rect_id; } nearest; }; + + /* Previous state to restore after drawing. */ + int viewport[4]; + int scissor[4]; + eGPUWriteMask write_mask; + eGPUDepthTest depth_test; } GPUPickState; static GPUPickState g_pick_state = {0}; @@ -304,17 +311,18 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c /* Restrict OpenGL operations for when we don't have cache */ if (ps->is_cached == false) { - gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_VIEWPORT_BIT); + ps->write_mask = GPU_write_mask_get(); + ps->depth_test = GPU_depth_test_get(); + GPU_scissor_get(ps->scissor); /* disable writing to the framebuffer */ GPU_color_mask(false, false, false, false); - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); + 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. */ - glDepthFunc(GL_LEQUAL); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); float viewport[4]; GPU_viewport_size_get_f(viewport); @@ -331,7 +339,7 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c /* It's possible we don't want to clear depth buffer, * so existing elements are masked by current z-buffer. */ - glClear(GL_DEPTH_BUFFER_BIT); + GPU_clear_depth(1.0f); /* scratch buffer (read new values here) */ ps->gl.rect_depth_test = depth_buf_malloc(rect_len); @@ -510,8 +518,13 @@ bool gpu_select_pick_load_id(uint id, bool end) SWAP(DepthBufCache *, ps->gl.rect_depth, ps->gl.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. */ + bool prev_depth_mask = GPU_depth_mask_get(); /* we want new depths every time */ - glClear(GL_DEPTH_BUFFER_BIT); + GPU_depth_mask(true); + GPU_clear_depth(1.0f); + + GPU_depth_mask(prev_depth_mask); } } } @@ -535,8 +548,9 @@ uint gpu_select_pick_end(void) /* force finishing last pass */ gpu_select_pick_load_id(ps->gl.prev_id, true); } - gpuPopAttr(); - GPU_color_mask(true, true, true, true); + GPU_write_mask(ps->write_mask); + GPU_depth_test(ps->depth_test); + GPU_viewport(UNPACK4(ps->viewport)); } /* assign but never free directly since it may be in cache */ diff --git a/source/blender/gpu/intern/gpu_select_sample_query.c b/source/blender/gpu/intern/gpu_select_sample_query.c index f67c9c36a6b..45d52b22664 100644 --- a/source/blender/gpu/intern/gpu_select_sample_query.c +++ b/source/blender/gpu/intern/gpu_select_sample_query.c @@ -26,6 +26,7 @@ #include <stdlib.h> +#include "GPU_framebuffer.h" #include "GPU_glew.h" #include "GPU_select.h" #include "GPU_state.h" @@ -60,6 +61,12 @@ typedef struct GPUQueryState { char mode; uint index; int oldhits; + + /* Previous state to restore after drawing. */ + int viewport[4]; + int scissor[4]; + eGPUWriteMask write_mask; + eGPUDepthTest depth_test; } GPUQueryState; static GPUQueryState g_query_state = {0}; @@ -67,8 +74,6 @@ static GPUQueryState g_query_state = {0}; void gpu_select_query_begin( uint (*buffer)[4], uint bufsize, const rcti *input, char mode, int oldhits) { - float viewport[4]; - g_query_state.query_issued = false; g_query_state.active_query = 0; g_query_state.num_of_queries = 0; @@ -86,36 +91,42 @@ void gpu_select_query_begin( "gpu selection ids"); glGenQueries(g_query_state.num_of_queries, g_query_state.queries); - gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_VIEWPORT_BIT | GPU_SCISSOR_BIT); - /* disable writing to the framebuffer */ - GPU_color_mask(false, false, false, false); + g_query_state.write_mask = GPU_write_mask_get(); + g_query_state.depth_test = GPU_depth_test_get(); + GPU_scissor_get(g_query_state.scissor); + GPU_viewport_size_get_i(g_query_state.viewport); + + /* Write to color buffer. Seems to fix issues with selecting alpha blended geom (see T7997). */ + GPU_color_mask(true, true, true, true); /* In order to save some fill rate we minimize the viewport using rect. * We need to get the region of the viewport so that our geometry doesn't * get rejected before the depth test. Should probably cull rect against * the viewport but this is a rare case I think */ - GPU_viewport_size_get_f(viewport); - GPU_viewport(viewport[0], viewport[1], BLI_rcti_size_x(input), BLI_rcti_size_y(input)); + + int viewport[4] = { + UNPACK2(g_query_state.viewport), BLI_rcti_size_x(input), BLI_rcti_size_y(input)}; + + GPU_viewport(UNPACK4(viewport)); + GPU_scissor(UNPACK4(viewport)); + GPU_scissor_test(false); /* 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. * See T62947 for details */ - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_ALWAYS); - glDepthMask(GL_TRUE); + GPU_depth_test(GPU_DEPTH_ALWAYS); + GPU_depth_mask(true); } else if (mode == GPU_SELECT_NEAREST_FIRST_PASS) { - glClear(GL_DEPTH_BUFFER_BIT); - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); - glDepthFunc(GL_LEQUAL); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + GPU_depth_mask(true); + GPU_clear_depth(1.0f); } else if (mode == GPU_SELECT_NEAREST_SECOND_PASS) { - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glDepthFunc(GL_EQUAL); + GPU_depth_test(GPU_DEPTH_EQUAL); + GPU_depth_mask(false); } } @@ -204,8 +215,10 @@ uint gpu_select_query_end(void) glDeleteQueries(g_query_state.num_of_queries, g_query_state.queries); MEM_freeN(g_query_state.queries); MEM_freeN(g_query_state.id); - gpuPopAttr(); - GPU_color_mask(true, true, true, true); + + GPU_write_mask(g_query_state.write_mask); + GPU_depth_test(g_query_state.depth_test); + GPU_viewport(UNPACK4(g_query_state.viewport)); return hits; } diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index 03b7d5402f5..360feb9a8c8 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -29,6 +29,7 @@ #include "BLI_string.h" #include "BLI_string_utils.h" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "BKE_appdir.h" #include "BKE_global.h" @@ -40,259 +41,229 @@ #include "GPU_platform.h" #include "GPU_shader.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" -#include "gpu_shader_private.h" +#include "gpu_backend.hh" +#include "gpu_context_private.hh" +#include "gpu_shader_private.hh" extern "C" char datatoc_gpu_shader_colorspace_lib_glsl[]; -/* Adjust these constants as needed. */ -#define MAX_DEFINE_LENGTH 256 -#define MAX_EXT_DEFINE_LENGTH 512 +using namespace blender; +using namespace blender::gpu; -#ifndef NDEBUG -static uint g_shaderid = 0; -#endif +/** Opaque type hidding blender::gpu::Shader */ +struct GPUShader { + char _pad[1]; +}; /* -------------------------------------------------------------------- */ -/** \name Convenience functions +/** \name Debug functions * \{ */ -static void shader_print_errors(const char *task, const char *log, const char **code, int totcode) +void Shader::print_errors(Span<const char *> sources, char *log, const char *stage) { - int line = 1; - - fprintf(stderr, "GPUShader: %s error:\n", task); - - for (int i = 0; i < totcode; i++) { - const char *c, *pos, *end = code[i] + strlen(code[i]); - - if (G.debug & G_DEBUG) { - fprintf(stderr, "===== shader string %d ====\n", i + 1); - - c = code[i]; - while ((c < end) && (pos = strchr(c, '\n'))) { - fprintf(stderr, "%2d ", line); - fwrite(c, (pos + 1) - c, 1, stderr); - c = pos + 1; - line++; + const char line_prefix[] = " | "; + char *sources_combined = BLI_string_join_arrayN((const char **)sources.data(), sources.size()); + + fprintf(stderr, "GPUShader: Compilation Log : %s : %s\n", this->name, stage); + + char *log_line = log, *line_end; + char *error_line_number_end; + int error_line, error_char, last_error_line = -2, last_error_char = -1; + bool found_line_id = false; + while ((line_end = strchr(log_line, '\n'))) { + /* Skip empty lines. */ + if (line_end == log_line) { + log_line++; + continue; + } + /* 0 = error, 1 = warning. */ + int type = -1; + /* Skip ERROR: or WARNING:. */ + const char *prefix[] = {"ERROR", "WARNING"}; + for (int i = 0; i < ARRAY_SIZE(prefix); i++) { + if (STREQLEN(log_line, prefix[i], strlen(prefix[i]))) { + log_line += strlen(prefix[i]); + type = i; + break; } - - fprintf(stderr, "%s", c); } - } - - fprintf(stderr, "%s\n", log); + /* Skip whitespaces and separators. */ + while (ELEM(log_line[0], ':', '(', ' ')) { + log_line++; + } + /* Parse error line & char numbers. */ + error_line = error_char = -1; + if (log_line[0] >= '0' && log_line[0] <= '9') { + error_line = (int)strtol(log_line, &error_line_number_end, 10); + /* Try to fetch the error caracter (not always available). */ + if (ELEM(error_line_number_end[0], '(', ':') && error_line_number_end[1] != ' ') { + error_char = (int)strtol(error_line_number_end + 1, &log_line, 10); + } + else { + log_line = error_line_number_end; + } + /* There can be a 3rd number (case of mesa driver). */ + if (ELEM(log_line[0], '(', ':') && log_line[1] >= '0' && log_line[1] <= '9') { + error_line = error_char; + error_char = (int)strtol(log_line + 1, &error_line_number_end, 10); + log_line = error_line_number_end; + } + } + /* Skip whitespaces and separators. */ + while (ELEM(log_line[0], ':', ')', ' ')) { + log_line++; + } + if (error_line == -1) { + found_line_id = false; + } + const char *src_line = sources_combined; + if ((error_line != -1) && (error_char != -1)) { + if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OFFICIAL)) { + /* source:line */ + int error_source = error_line; + if (error_source < sources.size()) { + src_line = sources[error_source]; + error_line = error_char; + error_char = -1; + } + } + else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_OFFICIAL) || + GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_MAC, GPU_DRIVER_OFFICIAL)) { + /* 0:line */ + error_line = error_char; + error_char = -1; + } + else { + /* line:char */ + } + } + /* Separate from previous block. */ + if (last_error_line != error_line) { + fprintf(stderr, "\033[90m%s\033[39m\n", line_prefix); + } + else if (error_char != last_error_char) { + fprintf(stderr, "%s\n", line_prefix); + } + /* Print line from the source file that is producing the error. */ + if ((error_line != -1) && (error_line != last_error_line || error_char != last_error_char)) { + const char *src_line_end = src_line; + found_line_id = false; + /* error_line is 1 based in this case. */ + int src_line_index = 1; + while ((src_line_end = strchr(src_line, '\n'))) { + if (src_line_index == error_line) { + found_line_id = true; + break; + } + /* Continue to next line. */ + src_line = src_line_end + 1; + src_line_index++; + } + /* Print error source. */ + if (found_line_id) { + if (error_line != last_error_line) { + fprintf(stderr, "%5d | ", src_line_index); + } + else { + fprintf(stderr, line_prefix); + } + fwrite(src_line, (src_line_end + 1) - src_line, 1, stderr); + /* Print char offset. */ + fprintf(stderr, line_prefix); + if (error_char != -1) { + for (int i = 0; i < error_char; i++) { + fprintf(stderr, " "); + } + fprintf(stderr, "^"); + } + fprintf(stderr, "\n"); + } + } + fprintf(stderr, line_prefix); + /* Skip to message. Avoid redundant info. */ + const char *keywords[] = {"error", "warning"}; + for (int i = 0; i < ARRAY_SIZE(prefix); i++) { + if (STREQLEN(log_line, keywords[i], strlen(keywords[i]))) { + log_line += strlen(keywords[i]); + type = i; + break; + } + } + /* Skip and separators. */ + while (ELEM(log_line[0], ':', ')')) { + log_line++; + } + if (type == 0) { + fprintf(stderr, "\033[31;1mError\033[0;2m: "); + } + else if (type == 1) { + fprintf(stderr, "\033[33;1mWarning\033[0;2m: "); + } + /* Print the error itself. */ + fprintf(stderr, "\033[2m"); + fwrite(log_line, (line_end + 1) - log_line, 1, stderr); + fprintf(stderr, "\033[0m"); + /* Continue to next line. */ + log_line = line_end + 1; + last_error_line = error_line; + last_error_char = error_char; + } + fprintf(stderr, "\n"); + MEM_freeN(sources_combined); } -static const char *gpu_shader_version(void) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Creation / Destruction + * \{ */ + +Shader::Shader(const char *sh_name) { - return "#version 330\n"; + BLI_strncpy(this->name, sh_name, sizeof(this->name)); } -static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH]) +Shader::~Shader() { - /* enable extensions for features that are not part of our base GLSL version - * don't use an extension for something already available! - */ - - if (GLEW_ARB_texture_gather) { - /* There is a bug on older Nvidia GPU where GL_ARB_texture_gather - * is reported to be supported but yield a compile error (see T55802). */ - if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) || GLEW_VERSION_4_0) { - strcat(defines, "#extension GL_ARB_texture_gather: enable\n"); - - /* Some drivers don't agree on GLEW_ARB_texture_gather and the actual support in the - * shader so double check the preprocessor define (see T56544). */ - if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) && !GLEW_VERSION_4_0) { - strcat(defines, "#ifdef GL_ARB_texture_gather\n"); - strcat(defines, "# define GPU_ARB_texture_gather\n"); - strcat(defines, "#endif\n"); - } - else { - strcat(defines, "#define GPU_ARB_texture_gather\n"); - } - } - } - if (GLEW_ARB_texture_query_lod) { - /* a #version 400 feature, but we use #version 330 maximum so use extension */ - strcat(defines, "#extension GL_ARB_texture_query_lod: enable\n"); - } - if (GLEW_ARB_shader_draw_parameters) { - strcat(defines, "#extension GL_ARB_shader_draw_parameters : enable\n"); - strcat(defines, "#define GPU_ARB_shader_draw_parameters\n"); - } - if (GPU_arb_texture_cube_map_array_is_supported()) { - strcat(defines, "#extension GL_ARB_texture_cube_map_array : enable\n"); - strcat(defines, "#define GPU_ARB_texture_cube_map_array\n"); - } + delete interface; } -static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH]) +static void standard_defines(Vector<const char *> &sources) { + BLI_assert(sources.size() == 0); + /* Version needs to be first. Exact values will be added by implementation. */ + sources.append("version"); /* some useful defines to detect GPU type */ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) { - strcat(defines, "#define GPU_ATI\n"); - if (GPU_crappy_amd_driver()) { - strcat(defines, "#define GPU_DEPRECATED_AMD_DRIVER\n"); - } + sources.append("#define GPU_ATI\n"); } else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) { - strcat(defines, "#define GPU_NVIDIA\n"); + sources.append("#define GPU_NVIDIA\n"); } else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) { - strcat(defines, "#define GPU_INTEL\n"); + sources.append("#define GPU_INTEL\n"); } - /* some useful defines to detect OS type */ if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_WIN, GPU_DRIVER_ANY)) { - strcat(defines, "#define OS_WIN\n"); + sources.append("#define OS_WIN\n"); } else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) { - strcat(defines, "#define OS_MAC\n"); + sources.append("#define OS_MAC\n"); } else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_UNIX, GPU_DRIVER_ANY)) { - strcat(defines, "#define OS_UNIX\n"); - } - - float derivatives_factors[2]; - GPU_get_dfdy_factors(derivatives_factors); - if (derivatives_factors[0] == 1.0f) { - strcat(defines, "#define DFDX_SIGN 1.0\n"); - } - else { - strcat(defines, "#define DFDX_SIGN -1.0\n"); - } - - if (derivatives_factors[1] == 1.0f) { - strcat(defines, "#define DFDY_SIGN 1.0\n"); - } - else { - strcat(defines, "#define DFDY_SIGN -1.0\n"); - } -} - -#define DEBUG_SHADER_NONE "" -#define DEBUG_SHADER_VERTEX "vert" -#define DEBUG_SHADER_FRAGMENT "frag" -#define DEBUG_SHADER_GEOMETRY "geom" - -/** - * Dump GLSL shaders to disk - * - * This is used for profiling shader performance externally and debug if shader code is correct. - * If called with no code, it simply bumps the shader index, so different shaders for the same - * program share the same index. - */ -static void gpu_dump_shaders(const char **code, const int num_shaders, const char *extension) -{ - if ((G.debug & G_DEBUG_GPU_SHADERS) == 0) { - return; - } - - /* We use the same shader index for shaders in the same program. - * So we call this function once before calling for the individual shaders. */ - static int shader_index = 0; - if (code == NULL) { - shader_index++; - BLI_assert(STREQ(DEBUG_SHADER_NONE, extension)); - return; - } - - /* Determine the full path of the new shader. */ - char shader_path[FILE_MAX]; - - char file_name[512] = {'\0'}; - sprintf(file_name, "%04d.%s", shader_index, extension); - - BLI_join_dirfile(shader_path, sizeof(shader_path), BKE_tempdir_session(), file_name); - - /* Write shader to disk. */ - FILE *f = fopen(shader_path, "w"); - if (f == NULL) { - printf("Error writing to file: %s\n", shader_path); - } - for (int j = 0; j < num_shaders; j++) { - fprintf(f, "%s", code[j]); - } - fclose(f); - printf("Shader file written to disk: %s\n", shader_path); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Creation / Destruction - * \{ */ - -GPUShader *GPU_shader_create(const char *vertexcode, - const char *fragcode, - const char *geocode, - const char *libcode, - const char *defines, - const char *shname) -{ - return GPU_shader_create_ex( - vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, shname); -} - -GPUShader *GPU_shader_create_from_python(const char *vertexcode, - const char *fragcode, - const char *geocode, - const char *libcode, - const char *defines) -{ - char *libcodecat = NULL; - - if (libcode == NULL) { - libcode = datatoc_gpu_shader_colorspace_lib_glsl; - } - else { - libcode = libcodecat = BLI_strdupcat(libcode, datatoc_gpu_shader_colorspace_lib_glsl); + sources.append("#define OS_UNIX\n"); } - GPUShader *sh = GPU_shader_create_ex( - vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, NULL); - - MEM_SAFE_FREE(libcodecat); - return sh; -} - -GPUShader *GPU_shader_load_from_binary(const char *binary, - const int binary_format, - const int binary_len, - const char *shname) -{ - BLI_assert(GL_ARB_get_program_binary); - int success; - int program = glCreateProgram(); - - glProgramBinary(program, binary_format, binary, binary_len); - glGetProgramiv(program, GL_LINK_STATUS, &success); - - if (success) { - glUseProgram(program); - - GPUShader *shader = (GPUShader *)MEM_callocN(sizeof(*shader), __func__); - shader->interface = GPU_shaderinterface_create(program); - shader->program = program; - -#ifndef NDEBUG - BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++); -#else - UNUSED_VARS(shname); -#endif - - return shader; + if (GPU_crappy_amd_driver()) { + sources.append("#define GPU_DEPRECATED_AMD_DRIVER\n"); } - - glDeleteProgram(program); - return NULL; } -GPUShader *GPU_shader_create_ex(const char *vertexcode, +GPUShader *GPU_shader_create_ex(const char *vertcode, const char *fragcode, - const char *geocode, + const char *geomcode, const char *libcode, const char *defines, const eGPUShaderTFBType tf_type, @@ -300,223 +271,113 @@ GPUShader *GPU_shader_create_ex(const char *vertexcode, const int tf_count, const char *shname) { - GLint status; - GLchar log[5000]; - GLsizei length = 0; - GPUShader *shader; - char standard_defines[MAX_DEFINE_LENGTH] = ""; - char standard_extensions[MAX_EXT_DEFINE_LENGTH] = ""; - - shader = (GPUShader *)MEM_callocN(sizeof(GPUShader), "GPUShader"); - gpu_dump_shaders(NULL, 0, DEBUG_SHADER_NONE); - -#ifndef NDEBUG - BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++); -#else - UNUSED_VARS(shname); -#endif - /* At least a vertex shader and a fragment shader are required. */ - BLI_assert((fragcode != NULL) && (vertexcode != NULL)); - - if (vertexcode) { - shader->vertex = glCreateShader(GL_VERTEX_SHADER); - } - if (fragcode) { - shader->fragment = glCreateShader(GL_FRAGMENT_SHADER); - } - if (geocode) { - shader->geometry = glCreateShader(GL_GEOMETRY_SHADER); - } - - shader->program = glCreateProgram(); - - if (!shader->program || (vertexcode && !shader->vertex) || (fragcode && !shader->fragment) || - (geocode && !shader->geometry)) { - fprintf(stderr, "GPUShader, object creation failed.\n"); - GPU_shader_free(shader); - return NULL; - } + BLI_assert((fragcode != NULL) && (vertcode != NULL)); - gpu_shader_standard_defines(standard_defines); - gpu_shader_standard_extensions(standard_extensions); + Shader *shader = GPUBackend::get()->shader_alloc(shname); - if (vertexcode) { - const char *source[7]; - /* custom limit, may be too small, beware */ - int num_source = 0; - - source[num_source++] = gpu_shader_version(); - source[num_source++] = - "#define GPU_VERTEX_SHADER\n" - "#define IN_OUT out\n"; - source[num_source++] = standard_extensions; - source[num_source++] = standard_defines; - - if (geocode) { - source[num_source++] = "#define USE_GEOMETRY_SHADER\n"; + if (vertcode) { + Vector<const char *> sources; + standard_defines(sources); + sources.append("#define GPU_VERTEX_SHADER\n"); + sources.append("#define IN_OUT out\n"); + if (geomcode) { + sources.append("#define USE_GEOMETRY_SHADER\n"); } if (defines) { - source[num_source++] = defines; + sources.append(defines); } - source[num_source++] = vertexcode; - - gpu_dump_shaders(source, num_source, DEBUG_SHADER_VERTEX); - - glAttachShader(shader->program, shader->vertex); - glShaderSource(shader->vertex, num_source, source, NULL); + sources.append(vertcode); - glCompileShader(shader->vertex); - glGetShaderiv(shader->vertex, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader->vertex, sizeof(log), &length, log); - shader_print_errors("compile", log, source, num_source); - - GPU_shader_free(shader); - return NULL; - } + shader->vertex_shader_from_glsl(sources); } if (fragcode) { - const char *source[8]; - int num_source = 0; - - source[num_source++] = gpu_shader_version(); - source[num_source++] = - "#define GPU_FRAGMENT_SHADER\n" - "#define IN_OUT in\n"; - source[num_source++] = standard_extensions; - source[num_source++] = standard_defines; - - if (geocode) { - source[num_source++] = "#define USE_GEOMETRY_SHADER\n"; + Vector<const char *> sources; + standard_defines(sources); + sources.append("#define GPU_FRAGMENT_SHADER\n"); + sources.append("#define IN_OUT in\n"); + if (geomcode) { + sources.append("#define USE_GEOMETRY_SHADER\n"); } if (defines) { - source[num_source++] = defines; + sources.append(defines); } if (libcode) { - source[num_source++] = libcode; + sources.append(libcode); } - source[num_source++] = fragcode; - - gpu_dump_shaders(source, num_source, DEBUG_SHADER_FRAGMENT); + sources.append(fragcode); - glAttachShader(shader->program, shader->fragment); - glShaderSource(shader->fragment, num_source, source, NULL); - - glCompileShader(shader->fragment); - glGetShaderiv(shader->fragment, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader->fragment, sizeof(log), &length, log); - shader_print_errors("compile", log, source, num_source); - - GPU_shader_free(shader); - return NULL; - } + shader->fragment_shader_from_glsl(sources); } - if (geocode) { - const char *source[6]; - int num_source = 0; - - source[num_source++] = gpu_shader_version(); - source[num_source++] = "#define GPU_GEOMETRY_SHADER\n"; - source[num_source++] = standard_extensions; - source[num_source++] = standard_defines; - + if (geomcode) { + Vector<const char *> sources; + standard_defines(sources); + sources.append("#define GPU_GEOMETRY_SHADER\n"); if (defines) { - source[num_source++] = defines; + sources.append(defines); } - source[num_source++] = geocode; - - gpu_dump_shaders(source, num_source, DEBUG_SHADER_GEOMETRY); - - glAttachShader(shader->program, shader->geometry); - glShaderSource(shader->geometry, num_source, source, NULL); + sources.append(geomcode); - glCompileShader(shader->geometry); - glGetShaderiv(shader->geometry, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader->geometry, sizeof(log), &length, log); - shader_print_errors("compile", log, source, num_source); - - GPU_shader_free(shader); - return NULL; - } + shader->geometry_shader_from_glsl(sources); } - if (tf_names != NULL) { - glTransformFeedbackVaryings(shader->program, tf_count, tf_names, GL_INTERLEAVED_ATTRIBS); - /* Primitive type must be setup */ + if (tf_names != NULL && tf_count > 0) { BLI_assert(tf_type != GPU_SHADER_TFB_NONE); - shader->feedback_transform_type = tf_type; + shader->transform_feedback_names_set(Span<const char *>(tf_names, tf_count), tf_type); } - glLinkProgram(shader->program); - glGetProgramiv(shader->program, GL_LINK_STATUS, &status); - if (!status) { - glGetProgramInfoLog(shader->program, sizeof(log), &length, log); - /* print attached shaders in pipeline order */ - if (defines) { - shader_print_errors("linking", log, &defines, 1); - } - if (vertexcode) { - shader_print_errors("linking", log, &vertexcode, 1); - } - if (geocode) { - shader_print_errors("linking", log, &geocode, 1); - } - if (libcode) { - shader_print_errors("linking", log, &libcode, 1); - } - if (fragcode) { - shader_print_errors("linking", log, &fragcode, 1); - } - - GPU_shader_free(shader); + if (!shader->finalize()) { + delete shader; return NULL; - } + }; - glUseProgram(shader->program); - shader->interface = GPU_shaderinterface_create(shader->program); + return reinterpret_cast<GPUShader *>(shader); +} - return shader; +void GPU_shader_free(GPUShader *shader) +{ + delete reinterpret_cast<Shader *>(shader); } -#undef DEBUG_SHADER_GEOMETRY -#undef DEBUG_SHADER_FRAGMENT -#undef DEBUG_SHADER_VERTEX -#undef DEBUG_SHADER_NONE +/** \} */ -void GPU_shader_free(GPUShader *shader) +/* -------------------------------------------------------------------- */ +/** \name Creation utils + * \{ */ + +GPUShader *GPU_shader_create(const char *vertcode, + const char *fragcode, + const char *geomcode, + const char *libcode, + const char *defines, + const char *shname) { -#if 0 /* Would be nice to have, but for now the Deferred compilation \ - * does not have a GPUContext. */ - BLI_assert(GPU_context_active_get() != NULL); -#endif - BLI_assert(shader); + return GPU_shader_create_ex( + vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, shname); +} - if (shader->vertex) { - glDeleteShader(shader->vertex); - } - if (shader->geometry) { - glDeleteShader(shader->geometry); - } - if (shader->fragment) { - glDeleteShader(shader->fragment); +GPUShader *GPU_shader_create_from_python(const char *vertcode, + const char *fragcode, + const char *geomcode, + const char *libcode, + const char *defines) +{ + char *libcodecat = NULL; + + if (libcode == NULL) { + libcode = datatoc_gpu_shader_colorspace_lib_glsl; } - if (shader->program) { - glDeleteProgram(shader->program); + else { + libcode = libcodecat = BLI_strdupcat(libcode, datatoc_gpu_shader_colorspace_lib_glsl); } - if (shader->interface) { - GPU_shaderinterface_discard(shader->interface); - } + GPUShader *sh = GPU_shader_create_ex( + vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, "pyGPUShader"); - MEM_freeN(shader); + MEM_SAFE_FREE(libcodecat); + return sh; } static const char *string_join_array_maybe_alloc(const char **str_arr, bool *r_is_alloc) @@ -565,7 +426,7 @@ static const char *string_join_array_maybe_alloc(const char **str_arr, bool *r_i * \endcode */ struct GPUShader *GPU_shader_create_from_arrays_impl( - const struct GPU_ShaderCreateFromArray_Params *params) + const struct GPU_ShaderCreateFromArray_Params *params, const char *func, int line) { struct { const char *str; @@ -577,8 +438,11 @@ struct GPUShader *GPU_shader_create_from_arrays_impl( str_dst[i].str = string_join_array_maybe_alloc(str_src[i], &str_dst[i].is_alloc); } + char name[64]; + BLI_snprintf(name, sizeof(name), "%s_%d", func, line); + GPUShader *sh = GPU_shader_create( - str_dst[0].str, str_dst[1].str, str_dst[2].str, NULL, str_dst[3].str, __func__); + str_dst[0].str, str_dst[1].str, str_dst[2].str, NULL, str_dst[3].str, name); for (int i = 0; i < ARRAY_SIZE(str_dst); i++) { if (str_dst[i].is_alloc) { @@ -594,52 +458,51 @@ struct GPUShader *GPU_shader_create_from_arrays_impl( /** \name Binding * \{ */ -void GPU_shader_bind(GPUShader *shader) +void GPU_shader_bind(GPUShader *gpu_shader) { - BLI_assert(shader && shader->program); + Shader *shader = reinterpret_cast<Shader *>(gpu_shader); + + GPUContext *ctx = GPU_context_active_get(); + + if (ctx->shader != shader) { + ctx->shader = shader; + shader->bind(); + GPU_matrix_bind(gpu_shader); + GPU_shader_set_srgb_uniform(gpu_shader); + } - glUseProgram(shader->program); - GPU_matrix_bind(shader->interface); - GPU_shader_set_srgb_uniform(shader->interface); + if (GPU_matrix_dirty_get()) { + GPU_matrix_bind(gpu_shader); + } } void GPU_shader_unbind(void) { - glUseProgram(0); +#ifndef NDEBUG + GPUContext *ctx = GPU_context_active_get(); + if (ctx->shader) { + reinterpret_cast<Shader *>(ctx->shader)->unbind(); + } + ctx->shader = NULL; +#endif } /** \} */ /* -------------------------------------------------------------------- */ /** \name Transform feedback + * + * TODO(fclem) Should be replaced by compute shaders. * \{ */ -bool GPU_shader_transform_feedback_enable(GPUShader *shader, uint vbo_id) +bool GPU_shader_transform_feedback_enable(GPUShader *shader, GPUVertBuf *vertbuf) { - if (shader->feedback_transform_type == GPU_SHADER_TFB_NONE) { - return false; - } - - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo_id); - - switch (shader->feedback_transform_type) { - case GPU_SHADER_TFB_POINTS: - glBeginTransformFeedback(GL_POINTS); - return true; - case GPU_SHADER_TFB_LINES: - glBeginTransformFeedback(GL_LINES); - return true; - case GPU_SHADER_TFB_TRIANGLES: - glBeginTransformFeedback(GL_TRIANGLES); - return true; - default: - return false; - } + return reinterpret_cast<Shader *>(shader)->transform_feedback_enable(vertbuf); } -void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader)) +void GPU_shader_transform_feedback_disable(GPUShader *shader) { - glEndTransformFeedback(); + reinterpret_cast<Shader *>(shader)->transform_feedback_disable(); } /** \} */ @@ -650,50 +513,49 @@ void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader)) int GPU_shader_get_uniform(GPUShader *shader, const char *name) { - BLI_assert(shader && shader->program); - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(shader->interface, name); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + const ShaderInput *uniform = interface->uniform_get(name); return uniform ? uniform->location : -1; } int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin) { - BLI_assert(shader && shader->program); - return GPU_shaderinterface_uniform_builtin(shader->interface, - static_cast<GPUUniformBuiltin>(builtin)); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + return interface->uniform_builtin((GPUUniformBuiltin)builtin); } int GPU_shader_get_builtin_block(GPUShader *shader, int builtin) { - BLI_assert(shader && shader->program); - return GPU_shaderinterface_block_builtin(shader->interface, - static_cast<GPUUniformBlockBuiltin>(builtin)); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + return interface->ubo_builtin((GPUUniformBlockBuiltin)builtin); } +/* DEPRECATED. */ int GPU_shader_get_uniform_block(GPUShader *shader, const char *name) { - BLI_assert(shader && shader->program); - const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + const ShaderInput *ubo = interface->ubo_get(name); return ubo ? ubo->location : -1; } int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name) { - BLI_assert(shader && shader->program); - const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + const ShaderInput *ubo = interface->ubo_get(name); return ubo ? ubo->binding : -1; } int GPU_shader_get_texture_binding(GPUShader *shader, const char *name) { - BLI_assert(shader && shader->program); - const GPUShaderInput *tex = GPU_shaderinterface_uniform(shader->interface, name); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + const ShaderInput *tex = interface->uniform_get(name); return tex ? tex->binding : -1; } int GPU_shader_get_attribute(GPUShader *shader, const char *name) { - BLI_assert(shader && shader->program); - const GPUShaderInput *attr = GPU_shaderinterface_attr(shader->interface, name); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + const ShaderInput *attr = interface->attr_get(name); return attr ? attr->location : -1; } @@ -704,108 +566,109 @@ int GPU_shader_get_attribute(GPUShader *shader, const char *name) * \{ */ /* Clement : Temp */ -int GPU_shader_get_program(GPUShader *shader) +int GPU_shader_get_program(GPUShader *UNUSED(shader)) { - return (int)shader->program; + /* TODO fixme */ + return (int)0; } -char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len) +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Uniforms setters + * \{ */ + +void GPU_shader_uniform_vector( + GPUShader *shader, int loc, int len, int arraysize, const float *value) { - BLI_assert(GLEW_ARB_get_program_binary); - char *r_binary; - int binary_len = 0; + reinterpret_cast<Shader *>(shader)->uniform_float(loc, len, arraysize, value); +} - glGetProgramiv(shader->program, GL_PROGRAM_BINARY_LENGTH, &binary_len); - r_binary = (char *)MEM_mallocN(binary_len, __func__); - glGetProgramBinary(shader->program, binary_len, NULL, r_binary_format, r_binary); +void GPU_shader_uniform_vector_int( + GPUShader *shader, int loc, int len, int arraysize, const int *value) +{ + reinterpret_cast<Shader *>(shader)->uniform_int(loc, len, arraysize, value); +} - if (r_binary_len) { - *r_binary_len = binary_len; - } +void GPU_shader_uniform_int(GPUShader *shader, int location, int value) +{ + GPU_shader_uniform_vector_int(shader, location, 1, 1, &value); +} - return r_binary; +void GPU_shader_uniform_float(GPUShader *shader, int location, float value) +{ + GPU_shader_uniform_vector(shader, location, 1, 1, &value); } -/** \} */ +void GPU_shader_uniform_1i(GPUShader *sh, const char *name, int value) +{ + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_int(sh, loc, value); +} -/* -------------------------------------------------------------------- */ -/** \name Uniforms setters - * \{ */ +void GPU_shader_uniform_1b(GPUShader *sh, const char *name, bool value) +{ + GPU_shader_uniform_1i(sh, name, value ? 1 : 0); +} -void GPU_shader_uniform_float(GPUShader *UNUSED(shader), int location, float value) +void GPU_shader_uniform_2f(GPUShader *sh, const char *name, float x, float y) { - if (location == -1) { - return; - } + const float data[2] = {x, y}; + GPU_shader_uniform_2fv(sh, name, data); +} - glUniform1f(location, value); +void GPU_shader_uniform_3f(GPUShader *sh, const char *name, float x, float y, float z) +{ + const float data[3] = {x, y, z}; + GPU_shader_uniform_3fv(sh, name, data); } -void GPU_shader_uniform_vector( - GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value) +void GPU_shader_uniform_4f(GPUShader *sh, const char *name, float x, float y, float z, float w) { - if (location == -1 || value == NULL) { - return; - } + const float data[4] = {x, y, z, w}; + GPU_shader_uniform_4fv(sh, name, data); +} - switch (length) { - case 1: - glUniform1fv(location, arraysize, value); - break; - case 2: - glUniform2fv(location, arraysize, value); - break; - case 3: - glUniform3fv(location, arraysize, value); - break; - case 4: - glUniform4fv(location, arraysize, value); - break; - case 9: - glUniformMatrix3fv(location, arraysize, 0, value); - break; - case 16: - glUniformMatrix4fv(location, arraysize, 0, value); - break; - default: - BLI_assert(0); - break; - } +void GPU_shader_uniform_1f(GPUShader *sh, const char *name, float x) +{ + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_float(sh, loc, x); } -void GPU_shader_uniform_int(GPUShader *UNUSED(shader), int location, int value) +void GPU_shader_uniform_2fv(GPUShader *sh, const char *name, const float data[2]) { - if (location == -1) { - return; - } + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 2, 1, data); +} - glUniform1i(location, value); +void GPU_shader_uniform_3fv(GPUShader *sh, const char *name, const float data[3]) +{ + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 3, 1, data); } -void GPU_shader_uniform_vector_int( - GPUShader *UNUSED(shader), int location, int length, int arraysize, const int *value) +void GPU_shader_uniform_4fv(GPUShader *sh, const char *name, const float data[4]) { - if (location == -1) { - return; - } + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 4, 1, data); +} - switch (length) { - case 1: - glUniform1iv(location, arraysize, value); - break; - case 2: - glUniform2iv(location, arraysize, value); - break; - case 3: - glUniform3iv(location, arraysize, value); - break; - case 4: - glUniform4iv(location, arraysize, value); - break; - default: - BLI_assert(0); - break; - } +void GPU_shader_uniform_mat4(GPUShader *sh, const char *name, const float data[4][4]) +{ + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 16, 1, (const float *)data); +} + +void GPU_shader_uniform_2fv_array(GPUShader *sh, const char *name, int len, const float (*val)[2]) +{ + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 2, len, (const float *)val); +} + +void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, const float (*val)[4]) +{ + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 4, len, (const float *)val); } /** \} */ @@ -823,11 +686,11 @@ void GPU_shader_uniform_vector_int( static int g_shader_builtin_srgb_transform = 0; -void GPU_shader_set_srgb_uniform(const GPUShaderInterface *interface) +void GPU_shader_set_srgb_uniform(GPUShader *shader) { - int32_t loc = GPU_shaderinterface_uniform_builtin(interface, GPU_UNIFORM_SRGB_TRANSFORM); + int32_t loc = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_SRGB_TRANSFORM); if (loc != -1) { - glUniform1i(loc, g_shader_builtin_srgb_transform); + GPU_shader_uniform_vector_int(shader, loc, 1, 1, &g_shader_builtin_srgb_transform); } } diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c index 9c0692b76e2..ed95a236da5 100644 --- a/source/blender/gpu/intern/gpu_shader_builtin.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -40,9 +40,7 @@ #include "GPU_platform.h" #include "GPU_shader.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" - -#include "gpu_shader_private.h" +#include "GPU_uniform_buffer.h" /* Adjust these constants as needed. */ #define MAX_DEFINE_LENGTH 256 diff --git a/source/blender/gpu/intern/gpu_shader_interface.cc b/source/blender/gpu/intern/gpu_shader_interface.cc index 4511d4a199d..dc59dca9f78 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.cc +++ b/source/blender/gpu/intern/gpu_shader_interface.cc @@ -23,157 +23,41 @@ * GPU shader interface (C --> GLSL) */ -#include "BKE_global.h" - -#include "BLI_bitmap.h" -#include "BLI_math_base.h" - #include "MEM_guardedalloc.h" -#include "GPU_shader_interface.h" - -#include "gpu_batch_private.h" -#include "gpu_context_private.hh" - -#include <stddef.h> -#include <stdlib.h> -#include <string.h> +#include "BLI_span.hh" +#include "BLI_vector.hh" -#define DEBUG_SHADER_INTERFACE 0 - -#if DEBUG_SHADER_INTERFACE -# include <stdio.h> -#endif - -static const char *BuiltinUniform_name(GPUUniformBuiltin u) -{ - switch (u) { - case GPU_UNIFORM_MODEL: - return "ModelMatrix"; - case GPU_UNIFORM_VIEW: - return "ViewMatrix"; - case GPU_UNIFORM_MODELVIEW: - return "ModelViewMatrix"; - case GPU_UNIFORM_PROJECTION: - return "ProjectionMatrix"; - case GPU_UNIFORM_VIEWPROJECTION: - return "ViewProjectionMatrix"; - case GPU_UNIFORM_MVP: - return "ModelViewProjectionMatrix"; - - case GPU_UNIFORM_MODEL_INV: - return "ModelMatrixInverse"; - case GPU_UNIFORM_VIEW_INV: - return "ViewMatrixInverse"; - case GPU_UNIFORM_MODELVIEW_INV: - return "ModelViewMatrixInverse"; - case GPU_UNIFORM_PROJECTION_INV: - return "ProjectionMatrixInverse"; - case GPU_UNIFORM_VIEWPROJECTION_INV: - return "ViewProjectionMatrixInverse"; - - case GPU_UNIFORM_NORMAL: - return "NormalMatrix"; - case GPU_UNIFORM_ORCO: - return "OrcoTexCoFactors"; - case GPU_UNIFORM_CLIPPLANES: - return "WorldClipPlanes"; - - case GPU_UNIFORM_COLOR: - return "color"; - case GPU_UNIFORM_BASE_INSTANCE: - return "baseInstance"; - case GPU_UNIFORM_RESOURCE_CHUNK: - return "resourceChunk"; - case GPU_UNIFORM_RESOURCE_ID: - return "resourceId"; - case GPU_UNIFORM_SRGB_TRANSFORM: - return "srgbTarget"; - - default: - return NULL; - } -} +#include "gpu_shader_interface.hh" -static const char *BuiltinUniformBlock_name(GPUUniformBlockBuiltin u) -{ - switch (u) { - case GPU_UNIFORM_BLOCK_VIEW: - return "viewBlock"; - case GPU_UNIFORM_BLOCK_MODEL: - return "modelBlock"; - case GPU_UNIFORM_BLOCK_INFO: - return "infoBlock"; - default: - return NULL; - } -} +namespace blender::gpu { -GPU_INLINE bool match(const char *a, const char *b) +ShaderInterface::ShaderInterface(void) { - return STREQ(a, b); + /* TODO(fclem) add unique ID for debugging. */ } -GPU_INLINE uint hash_string(const char *str) +ShaderInterface::~ShaderInterface(void) { - uint i = 0, c; - while ((c = *str++)) { - i = i * 37 + c; - } - return i; + /* Free memory used by name_buffer. */ + MEM_freeN(name_buffer_); + MEM_freeN(inputs_); } -GPU_INLINE uint32_t set_input_name(GPUShaderInterface *shaderface, - GPUShaderInput *input, - char *name, - uint32_t name_len) +static void sort_input_list(MutableSpan<ShaderInput> dst) { - /* remove "[0]" from array name */ - if (name[name_len - 1] == ']') { - name[name_len - 3] = '\0'; - name_len -= 3; + if (dst.size() == 0) { + return; } - input->name_offset = (uint32_t)(name - shaderface->name_buffer); - input->name_hash = hash_string(name); - return name_len + 1; /* include NULL terminator */ -} - -GPU_INLINE const GPUShaderInput *input_lookup(const GPUShaderInterface *shaderface, - const GPUShaderInput *const inputs, - const uint inputs_len, - const char *name) -{ - const uint name_hash = hash_string(name); - /* Simple linear search for now. */ - for (int i = inputs_len - 1; i >= 0; i--) { - if (inputs[i].name_hash == name_hash) { - if ((i > 0) && UNLIKELY(inputs[i - 1].name_hash == name_hash)) { - /* Hash colision resolve. */ - for (; i >= 0 && inputs[i].name_hash == name_hash; i--) { - if (match(name, shaderface->name_buffer + inputs[i].name_offset)) { - return inputs + i; /* not found */ - } - } - return NULL; /* not found */ - } - - /* This is a bit dangerous since we could have a hash collision. - * where the asked uniform that does not exist has the same hash - * as a real uniform. */ - BLI_assert(match(name, shaderface->name_buffer + inputs[i].name_offset)); - return inputs + i; - } - } - return NULL; /* not found */ -} + Vector<ShaderInput> inputs_vec = Vector<ShaderInput>(dst.size()); + MutableSpan<ShaderInput> src = inputs_vec.as_mutable_span(); + src.copy_from(dst); -/* Note that this modify the src array. */ -GPU_INLINE void sort_input_list(GPUShaderInput *dst, GPUShaderInput *src, const uint input_len) -{ - for (uint i = 0; i < input_len; i++) { - GPUShaderInput *input_src = &src[0]; - for (uint j = 1; j < input_len; j++) { + /* Simple sorting by going through the array and selecting the biggest element each time. */ + for (uint i = 0; i < dst.size(); i++) { + ShaderInput *input_src = &src[0]; + for (uint j = 1; j < src.size(); j++) { if (src[j].name_hash > input_src->name_hash) { input_src = &src[j]; } @@ -183,358 +67,60 @@ GPU_INLINE void sort_input_list(GPUShaderInput *dst, GPUShaderInput *src, const } } -static int block_binding(int32_t program, uint32_t block_index) +/* Sorts all inputs inside their respective array. + * This is to allow fast hash collision detection. + * See ShaderInterface::input_lookup for more details. */ +void ShaderInterface::sort_inputs(void) { - /* For now just assign a consecutive index. In the future, we should set it in - * the shader using layout(binding = i) and query its value. */ - glUniformBlockBinding(program, block_index, block_index); - return block_index; + sort_input_list(MutableSpan<ShaderInput>(inputs_, attr_len_)); + sort_input_list(MutableSpan<ShaderInput>(inputs_ + attr_len_, ubo_len_)); + sort_input_list(MutableSpan<ShaderInput>(inputs_ + attr_len_ + ubo_len_, uniform_len_)); } -static int sampler_binding(int32_t program, - uint32_t uniform_index, - int32_t uniform_location, - int *sampler_len) +void ShaderInterface::debug_print(void) { - /* Identify sampler uniforms and asign sampler units to them. */ - GLint type; - glGetActiveUniformsiv(program, 1, &uniform_index, GL_UNIFORM_TYPE, &type); + Span<ShaderInput> attrs = Span<ShaderInput>(inputs_, attr_len_); + Span<ShaderInput> ubos = Span<ShaderInput>(inputs_ + attr_len_, ubo_len_); + Span<ShaderInput> uniforms = Span<ShaderInput>(inputs_ + attr_len_ + ubo_len_, uniform_len_); + char *name_buf = name_buffer_; + const char format[] = " | %.8x : %4d : %s\n"; - switch (type) { - case GL_SAMPLER_1D: - case GL_SAMPLER_2D: - case GL_SAMPLER_3D: - case GL_SAMPLER_CUBE: - case GL_SAMPLER_CUBE_MAP_ARRAY_ARB: /* OpenGL 4.0 */ - case GL_SAMPLER_1D_SHADOW: - case GL_SAMPLER_2D_SHADOW: - case GL_SAMPLER_1D_ARRAY: - case GL_SAMPLER_2D_ARRAY: - case GL_SAMPLER_1D_ARRAY_SHADOW: - case GL_SAMPLER_2D_ARRAY_SHADOW: - case GL_SAMPLER_2D_MULTISAMPLE: - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_SAMPLER_CUBE_SHADOW: - case GL_SAMPLER_BUFFER: - case GL_INT_SAMPLER_1D: - case GL_INT_SAMPLER_2D: - case GL_INT_SAMPLER_3D: - case GL_INT_SAMPLER_CUBE: - case GL_INT_SAMPLER_1D_ARRAY: - case GL_INT_SAMPLER_2D_ARRAY: - case GL_INT_SAMPLER_2D_MULTISAMPLE: - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_INT_SAMPLER_BUFFER: - case GL_UNSIGNED_INT_SAMPLER_1D: - case GL_UNSIGNED_INT_SAMPLER_2D: - case GL_UNSIGNED_INT_SAMPLER_3D: - case GL_UNSIGNED_INT_SAMPLER_CUBE: - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_BUFFER: { - /* For now just assign a consecutive index. In the future, we should set it in - * the shader using layout(binding = i) and query its value. */ - int binding = *sampler_len; - glUniform1i(uniform_location, binding); - (*sampler_len)++; - return binding; - } - default: - return -1; + printf(" \033[1mGPUShaderInterface : \033[0m\n"); + if (attrs.size() > 0) { + printf("\n Attributes :\n"); } -} - -GPUShaderInterface *GPU_shaderinterface_create(int32_t program) -{ -#ifndef NDEBUG - GLint curr_program; - glGetIntegerv(GL_CURRENT_PROGRAM, &curr_program); - BLI_assert(curr_program == program); -#endif - - GLint max_attr_name_len = 0, attr_len = 0; - glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_attr_name_len); - glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attr_len); - - GLint max_ubo_name_len = 0, ubo_len = 0; - glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_ubo_name_len); - glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &ubo_len); - - GLint max_uniform_name_len = 0, active_uniform_len = 0, uniform_len = 0; - glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_len); - glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len); - uniform_len = active_uniform_len; - - /* Work around driver bug with Intel HD 4600 on Windows 7/8, where - * GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH does not work. */ - if (attr_len > 0 && max_attr_name_len == 0) { - max_attr_name_len = 256; - } - if (ubo_len > 0 && max_ubo_name_len == 0) { - max_ubo_name_len = 256; - } - if (uniform_len > 0 && max_uniform_name_len == 0) { - max_uniform_name_len = 256; + for (const ShaderInput &attr : attrs) { + printf(format, attr.name_hash, attr.location, name_buf + attr.name_offset); } - /* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before - * allocating the uniform array. */ - GLint max_ubo_uni_len = 0; - for (int i = 0; i < ubo_len; i++) { - GLint ubo_uni_len; - glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len); - max_ubo_uni_len = max_ii(max_ubo_uni_len, ubo_uni_len); - uniform_len -= ubo_uni_len; + if (uniforms.size() > 0) { + printf("\n Uniforms :\n"); } - /* Bit set to true if uniform comes from a uniform block. */ - BLI_bitmap *uniforms_from_blocks = BLI_BITMAP_NEW(active_uniform_len, __func__); - /* Set uniforms from block for exclusion. */ - GLint *ubo_uni_ids = (GLint *)MEM_mallocN(sizeof(GLint) * max_ubo_uni_len, __func__); - for (int i = 0; i < ubo_len; i++) { - GLint ubo_uni_len; - glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len); - glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, ubo_uni_ids); - for (int u = 0; u < ubo_uni_len; u++) { - BLI_BITMAP_ENABLE(uniforms_from_blocks, ubo_uni_ids[u]); + for (const ShaderInput &uni : uniforms) { + /* Bypass samplers. */ + if (uni.binding == -1) { + printf(format, uni.name_hash, uni.location, name_buf + uni.name_offset); } } - MEM_freeN(ubo_uni_ids); - - uint32_t name_buffer_offset = 0; - const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len + - uniform_len * max_uniform_name_len; - - int input_tot_len = attr_len + ubo_len + uniform_len; - size_t interface_size = sizeof(GPUShaderInterface) + sizeof(GPUShaderInput) * input_tot_len; - GPUShaderInterface *shaderface = (GPUShaderInterface *)MEM_callocN(interface_size, - "GPUShaderInterface"); - shaderface->attribute_len = attr_len; - shaderface->ubo_len = ubo_len; - shaderface->uniform_len = uniform_len; - shaderface->name_buffer = (char *)MEM_mallocN(name_buffer_len, "name_buffer"); - GPUShaderInput *inputs = shaderface->inputs; - - /* Temp buffer. */ - int input_tmp_len = max_iii(attr_len, ubo_len, uniform_len); - GPUShaderInput *inputs_tmp = (GPUShaderInput *)MEM_mallocN( - sizeof(GPUShaderInput) * input_tmp_len, "name_buffer"); - - /* Attributes */ - shaderface->enabled_attr_mask = 0; - for (int i = 0, idx = 0; i < attr_len; i++) { - char *name = shaderface->name_buffer + name_buffer_offset; - GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; - GLsizei name_len = 0; - GLenum type; - GLint size; - - glGetActiveAttrib(program, i, remaining_buffer, &name_len, &size, &type, name); - GLint location = glGetAttribLocation(program, name); - /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */ - if (location == -1) { - shaderface->attribute_len--; - continue; - } - - GPUShaderInput *input = &inputs_tmp[idx++]; - input->location = input->binding = location; - - name_buffer_offset += set_input_name(shaderface, input, name, name_len); - shaderface->enabled_attr_mask |= (1 << input->location); + if (ubos.size() > 0) { + printf("\n Uniform Buffer Objects :\n"); } - sort_input_list(inputs, inputs_tmp, shaderface->attribute_len); - inputs += shaderface->attribute_len; - - /* Uniform Blocks */ - for (int i = 0, idx = 0; i < ubo_len; i++) { - char *name = shaderface->name_buffer + name_buffer_offset; - GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; - GLsizei name_len = 0; - - glGetActiveUniformBlockName(program, i, remaining_buffer, &name_len, name); - - GPUShaderInput *input = &inputs_tmp[idx++]; - input->binding = input->location = block_binding(program, i); - - name_buffer_offset += set_input_name(shaderface, input, name, name_len); - shaderface->enabled_ubo_mask |= (1 << input->binding); + for (const ShaderInput &ubo : ubos) { + printf(format, ubo.name_hash, ubo.binding, name_buf + ubo.name_offset); } - sort_input_list(inputs, inputs_tmp, shaderface->ubo_len); - inputs += shaderface->ubo_len; - - /* Uniforms */ - for (int i = 0, idx = 0, sampler = 0; i < active_uniform_len; i++) { - if (BLI_BITMAP_TEST(uniforms_from_blocks, i)) { - continue; - } - char *name = shaderface->name_buffer + name_buffer_offset; - GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; - GLsizei name_len = 0; - glGetActiveUniformName(program, i, remaining_buffer, &name_len, name); - - GPUShaderInput *input = &inputs_tmp[idx++]; - input->location = glGetUniformLocation(program, name); - input->binding = sampler_binding(program, i, input->location, &sampler); - - name_buffer_offset += set_input_name(shaderface, input, name, name_len); - shaderface->enabled_tex_mask |= (input->binding != -1) ? (1lu << input->binding) : 0lu; - } - sort_input_list(inputs, inputs_tmp, shaderface->uniform_len); - - /* Builtin Uniforms */ - for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) { - GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int); - shaderface->builtins[u] = glGetUniformLocation(program, BuiltinUniform_name(u)); - } - - /* Builtin Uniforms Blocks */ - for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) { - GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int); - const GPUShaderInput *block = GPU_shaderinterface_ubo(shaderface, BuiltinUniformBlock_name(u)); - shaderface->builtin_blocks[u] = (block != NULL) ? block->binding : -1; - } - - /* Batches ref buffer */ - shaderface->batches_len = GPU_SHADERINTERFACE_REF_ALLOC_COUNT; - shaderface->batches = (GPUBatch **)MEM_callocN(shaderface->batches_len * sizeof(GPUBatch *), - "GPUShaderInterface batches"); - - MEM_freeN(uniforms_from_blocks); - MEM_freeN(inputs_tmp); - - /* Resize name buffer to save some memory. */ - if (name_buffer_offset < name_buffer_len) { - shaderface->name_buffer = (char *)MEM_reallocN(shaderface->name_buffer, name_buffer_offset); - } - -#if DEBUG_SHADER_INTERFACE - char *name_buf = shaderface->name_buffer; - printf("--- GPUShaderInterface %p, program %d ---\n", shaderface, program); - if (shaderface->attribute_len > 0) { - printf("Attributes {\n"); - for (int i = 0; i < shaderface->attribute_len; i++) { - GPUShaderInput *input = shaderface->inputs + i; - printf("\t(location = %d) %s;\n", input->location, name_buf + input->name_offset); - } - printf("};\n"); + if (enabled_tex_mask_ > 0) { + printf("\n Samplers :\n"); } - if (shaderface->ubo_len > 0) { - printf("Uniform Buffer Objects {\n"); - for (int i = 0; i < shaderface->ubo_len; i++) { - GPUShaderInput *input = shaderface->inputs + shaderface->attribute_len + i; - printf("\t(binding = %d) %s;\n", input->binding, name_buf + input->name_offset); - } - printf("};\n"); - } - if (shaderface->enabled_tex_mask > 0) { - printf("Samplers {\n"); - for (int i = 0; i < shaderface->uniform_len; i++) { - GPUShaderInput *input = shaderface->inputs + shaderface->attribute_len + - shaderface->ubo_len + i; - if (input->binding != -1) { - printf("\t(location = %d, binding = %d) %s;\n", - input->location, - input->binding, - name_buf + input->name_offset); - } - } - printf("};\n"); - } - if (shaderface->uniform_len > 0) { - printf("Uniforms {\n"); - for (int i = 0; i < shaderface->uniform_len; i++) { - GPUShaderInput *input = shaderface->inputs + shaderface->attribute_len + - shaderface->ubo_len + i; - if (input->binding == -1) { - printf("\t(location = %d) %s;\n", input->location, name_buf + input->name_offset); - } - } - printf("};\n"); - } - printf("--- GPUShaderInterface end ---\n\n"); -#endif - - return shaderface; -} - -void GPU_shaderinterface_discard(GPUShaderInterface *shaderface) -{ - /* Free memory used by name_buffer. */ - MEM_freeN(shaderface->name_buffer); - /* Remove this interface from all linked Batches vao cache. */ - for (int i = 0; i < shaderface->batches_len; i++) { - if (shaderface->batches[i] != NULL) { - gpu_batch_remove_interface_ref(shaderface->batches[i], shaderface); + for (const ShaderInput &samp : uniforms) { + /* Bypass uniforms. */ + if (samp.binding != -1) { + printf(format, samp.name_hash, samp.binding, name_buf + samp.name_offset); } } - MEM_freeN(shaderface->batches); - /* Free memory used by shader interface by its self. */ - MEM_freeN(shaderface); -} -const GPUShaderInput *GPU_shaderinterface_attr(const GPUShaderInterface *shaderface, - const char *name) -{ - uint ofs = 0; - return input_lookup(shaderface, shaderface->inputs + ofs, shaderface->attribute_len, name); + printf("\n"); } -const GPUShaderInput *GPU_shaderinterface_ubo(const GPUShaderInterface *shaderface, - const char *name) -{ - uint ofs = shaderface->attribute_len; - return input_lookup(shaderface, shaderface->inputs + ofs, shaderface->ubo_len, name); -} - -const GPUShaderInput *GPU_shaderinterface_uniform(const GPUShaderInterface *shaderface, - const char *name) -{ - uint ofs = shaderface->attribute_len + shaderface->ubo_len; - return input_lookup(shaderface, shaderface->inputs + ofs, shaderface->uniform_len, name); -} - -int32_t GPU_shaderinterface_uniform_builtin(const GPUShaderInterface *shaderface, - GPUUniformBuiltin builtin) -{ - BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORMS); - return shaderface->builtins[builtin]; -} - -int32_t GPU_shaderinterface_block_builtin(const GPUShaderInterface *shaderface, - GPUUniformBlockBuiltin builtin) -{ - BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORM_BLOCKS); - return shaderface->builtin_blocks[builtin]; -} - -void GPU_shaderinterface_add_batch_ref(GPUShaderInterface *shaderface, GPUBatch *batch) -{ - int i; /* find first unused slot */ - for (i = 0; i < shaderface->batches_len; i++) { - if (shaderface->batches[i] == NULL) { - break; - } - } - if (i == shaderface->batches_len) { - /* Not enough place, realloc the array. */ - i = shaderface->batches_len; - shaderface->batches_len += GPU_SHADERINTERFACE_REF_ALLOC_COUNT; - shaderface->batches = (GPUBatch **)MEM_recallocN(shaderface->batches, - sizeof(GPUBatch *) * shaderface->batches_len); - } - shaderface->batches[i] = batch; -} - -void GPU_shaderinterface_remove_batch_ref(GPUShaderInterface *shaderface, GPUBatch *batch) -{ - for (int i = 0; i < shaderface->batches_len; i++) { - if (shaderface->batches[i] == batch) { - shaderface->batches[i] = NULL; - break; /* cannot have duplicates */ - } - } -} +} // namespace blender::gpu diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh new file mode 100644 index 00000000000..265fe90fc76 --- /dev/null +++ b/source/blender/gpu/intern/gpu_shader_interface.hh @@ -0,0 +1,255 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GPU shader interface (C --> GLSL) + * + * Structure detailing needed vertex inputs and resources for a specific shader. + * A shader interface can be shared between two similar shaders. + */ + +#pragma once + +#include <cstring> /* required for STREQ later on. */ + +#include "BLI_hash.h" +#include "BLI_utildefines.h" + +#include "GPU_shader.h" + +namespace blender::gpu { + +typedef struct ShaderInput { + uint32_t name_offset; + uint32_t name_hash; + int32_t location; + /** Defined at interface creation or in shader. Only for Samplers, UBOs and Vertex Attribs. */ + int32_t binding; +} ShaderInput; + +/** + * Implementation of Shader interface. + * Base class which is then specialized for each implementation (GL, VK, ...). + **/ +class ShaderInterface { + /* TODO(fclem) should be protected. */ + public: + /** Flat array. In this order: Attributes, Ubos, Uniforms. */ + ShaderInput *inputs_ = NULL; + /** Buffer containing all inputs names separated by '\0'. */ + char *name_buffer_ = NULL; + /** Input counts inside input array. */ + uint attr_len_ = 0; + uint ubo_len_ = 0; + uint uniform_len_ = 0; + /** Enabled bindpoints that needs to be fed with data. */ + uint16_t enabled_attr_mask_ = 0; + uint16_t enabled_ubo_mask_ = 0; + uint64_t enabled_tex_mask_ = 0; + /** Location of builtin uniforms. Fast access, no lookup needed. */ + int32_t builtins_[GPU_NUM_UNIFORMS]; + int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS]; + + public: + ShaderInterface(); + virtual ~ShaderInterface(); + + void debug_print(void); + + inline const ShaderInput *attr_get(const char *name) const + { + return input_lookup(inputs_, attr_len_, name); + } + + inline const ShaderInput *ubo_get(const char *name) const + { + return input_lookup(inputs_ + attr_len_, ubo_len_, name); + } + inline const ShaderInput *ubo_get(const int binding) const + { + return input_lookup(inputs_ + attr_len_, ubo_len_, binding); + } + + inline const ShaderInput *uniform_get(const char *name) const + { + return input_lookup(inputs_ + attr_len_ + ubo_len_, uniform_len_, name); + } + + inline const char *input_name_get(const ShaderInput *input) const + { + return name_buffer_ + input->name_offset; + } + + /* Returns uniform location. */ + inline int32_t uniform_builtin(const GPUUniformBuiltin builtin) const + { + BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORMS); + return builtins_[builtin]; + } + + /* Returns binding position. */ + inline int32_t ubo_builtin(const GPUUniformBlockBuiltin builtin) const + { + BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORM_BLOCKS); + return builtin_blocks_[builtin]; + } + + protected: + static inline const char *builtin_uniform_name(GPUUniformBuiltin u); + static inline const char *builtin_uniform_block_name(GPUUniformBlockBuiltin u); + + inline uint32_t set_input_name(ShaderInput *input, char *name, uint32_t name_len) const; + + /* Finalize interface construction by sorting the ShaderInputs for faster lookups. */ + void sort_inputs(void); + + private: + inline const ShaderInput *input_lookup(const ShaderInput *const inputs, + const uint inputs_len, + const char *name) const; + + inline const ShaderInput *input_lookup(const ShaderInput *const inputs, + const uint inputs_len, + const int binding) const; +}; + +inline const char *ShaderInterface::builtin_uniform_name(GPUUniformBuiltin u) +{ + switch (u) { + case GPU_UNIFORM_MODEL: + return "ModelMatrix"; + case GPU_UNIFORM_VIEW: + return "ViewMatrix"; + case GPU_UNIFORM_MODELVIEW: + return "ModelViewMatrix"; + case GPU_UNIFORM_PROJECTION: + return "ProjectionMatrix"; + case GPU_UNIFORM_VIEWPROJECTION: + return "ViewProjectionMatrix"; + case GPU_UNIFORM_MVP: + return "ModelViewProjectionMatrix"; + + case GPU_UNIFORM_MODEL_INV: + return "ModelMatrixInverse"; + case GPU_UNIFORM_VIEW_INV: + return "ViewMatrixInverse"; + case GPU_UNIFORM_MODELVIEW_INV: + return "ModelViewMatrixInverse"; + case GPU_UNIFORM_PROJECTION_INV: + return "ProjectionMatrixInverse"; + case GPU_UNIFORM_VIEWPROJECTION_INV: + return "ViewProjectionMatrixInverse"; + + case GPU_UNIFORM_NORMAL: + return "NormalMatrix"; + case GPU_UNIFORM_ORCO: + return "OrcoTexCoFactors"; + case GPU_UNIFORM_CLIPPLANES: + return "WorldClipPlanes"; + + case GPU_UNIFORM_COLOR: + return "color"; + case GPU_UNIFORM_BASE_INSTANCE: + return "baseInstance"; + case GPU_UNIFORM_RESOURCE_CHUNK: + return "resourceChunk"; + case GPU_UNIFORM_RESOURCE_ID: + return "resourceId"; + case GPU_UNIFORM_SRGB_TRANSFORM: + return "srgbTarget"; + + default: + return NULL; + } +} + +inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBuiltin u) +{ + switch (u) { + case GPU_UNIFORM_BLOCK_VIEW: + return "viewBlock"; + case GPU_UNIFORM_BLOCK_MODEL: + return "modelBlock"; + case GPU_UNIFORM_BLOCK_INFO: + return "infoBlock"; + default: + return NULL; + } +} + +/* Returns string length including '\0' terminator. */ +inline uint32_t ShaderInterface::set_input_name(ShaderInput *input, + char *name, + uint32_t name_len) const +{ + /* remove "[0]" from array name */ + if (name[name_len - 1] == ']') { + name[name_len - 3] = '\0'; + name_len -= 3; + } + + input->name_offset = (uint32_t)(name - name_buffer_); + input->name_hash = BLI_hash_string(name); + return name_len + 1; /* include NULL terminator */ +} + +inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const inputs, + const uint inputs_len, + const char *name) const +{ + const uint name_hash = BLI_hash_string(name); + /* Simple linear search for now. */ + for (int i = inputs_len - 1; i >= 0; i--) { + if (inputs[i].name_hash == name_hash) { + if ((i > 0) && UNLIKELY(inputs[i - 1].name_hash == name_hash)) { + /* Hash colision resolve. */ + for (; i >= 0 && inputs[i].name_hash == name_hash; i--) { + if (STREQ(name, name_buffer_ + inputs[i].name_offset)) { + return inputs + i; /* not found */ + } + } + return NULL; /* not found */ + } + + /* This is a bit dangerous since we could have a hash collision. + * where the asked uniform that does not exist has the same hash + * as a real uniform. */ + BLI_assert(STREQ(name, name_buffer_ + inputs[i].name_offset)); + return inputs + i; + } + } + return NULL; /* not found */ +} + +inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const inputs, + const uint inputs_len, + const int binding) const +{ + /* Simple linear search for now. */ + for (int i = inputs_len - 1; i >= 0; i--) { + if (inputs[i].binding == binding) { + return inputs + i; + } + } + return NULL; /* not found */ +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/intern/gpu_shader_private.h b/source/blender/gpu/intern/gpu_shader_private.h deleted file mode 100644 index 0f89fbda737..00000000000 --- a/source/blender/gpu/intern/gpu_shader_private.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup gpu - */ - -#pragma once - -#include "GPU_shader_interface.h" - -#ifdef __cplusplus -extern "C" { -#endif - -struct GPUShader { - /** Handle for full program (links shader stages below). */ - GLuint program; - - /** Handle for vertex shader. */ - GLuint vertex; - /** Handle for geometry shader. */ - GLuint geometry; - /** Handle for fragment shader. */ - GLuint fragment; - - /** Cached uniform & attribute interface for shader. */ - GPUShaderInterface *interface; - - int feedback_transform_type; -#ifndef NDEBUG - char name[64]; -#endif -}; - -/* XXX do not use it. Special hack to use OCIO with batch API. */ -GPUShader *immGetShader(void); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/gpu/intern/gpu_shader_private.hh b/source/blender/gpu/intern/gpu_shader_private.hh new file mode 100644 index 00000000000..9c9aa835b97 --- /dev/null +++ b/source/blender/gpu/intern/gpu_shader_private.hh @@ -0,0 +1,80 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "BLI_span.hh" + +#include "GPU_shader.h" +#include "GPU_vertex_buffer.h" +#include "gpu_shader_interface.hh" + +namespace blender { +namespace gpu { + +/** + * Implementation of shader compilation and uniforms handling. + * Base class which is then specialized for each implementation (GL, VK, ...). + **/ +class Shader { + public: + /** Uniform & attribute locations for shader. */ + ShaderInterface *interface = nullptr; + + protected: + /** For debugging purpose. */ + char name[64]; + + public: + Shader(const char *name); + virtual ~Shader(); + + virtual void vertex_shader_from_glsl(MutableSpan<const char *> sources) = 0; + virtual void geometry_shader_from_glsl(MutableSpan<const char *> sources) = 0; + virtual void fragment_shader_from_glsl(MutableSpan<const char *> sources) = 0; + virtual bool finalize(void) = 0; + + virtual void transform_feedback_names_set(Span<const char *> name_list, + const eGPUShaderTFBType geom_type) = 0; + virtual bool transform_feedback_enable(GPUVertBuf *) = 0; + virtual void transform_feedback_disable(void) = 0; + + virtual void bind(void) = 0; + virtual void unbind(void) = 0; + + virtual void uniform_float(int location, int comp_len, int array_size, const float *data) = 0; + virtual void uniform_int(int location, int comp_len, int array_size, const int *data) = 0; + + virtual void vertformat_from_shader(GPUVertFormat *) const = 0; + + inline const char *const name_get(void) const + { + return name; + }; + + protected: + void print_errors(Span<const char *> sources, char *log, const char *stage); +}; + +} // namespace gpu +} // namespace blender + +/* XXX do not use it. Special hack to use OCIO with batch API. */ +GPUShader *immGetShader(void); diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc index 794c7a3eb97..478fd639cdd 100644 --- a/source/blender/gpu/intern/gpu_state.cc +++ b/source/blender/gpu/intern/gpu_state.cc @@ -25,6 +25,7 @@ # define PIXELSIZE (1.0f) #endif +#include "BLI_math_vector.h" #include "BLI_utildefines.h" #include "BKE_global.h" @@ -33,239 +34,252 @@ #include "GPU_glew.h" #include "GPU_state.h" -static GLenum gpu_get_gl_blendfunction(eGPUBlendFunction blend) -{ - switch (blend) { - case GPU_ONE: - return GL_ONE; - case GPU_SRC_ALPHA: - return GL_SRC_ALPHA; - case GPU_ONE_MINUS_SRC_ALPHA: - return GL_ONE_MINUS_SRC_ALPHA; - case GPU_DST_COLOR: - return GL_DST_COLOR; - case GPU_ZERO: - return GL_ZERO; - default: - BLI_assert(!"Unhandled blend mode"); - return GL_ZERO; - } +#include "gpu_context_private.hh" + +#include "gpu_state_private.hh" + +using namespace blender::gpu; + +#define SET_STATE(_prefix, _state, _value) \ + do { \ + GPUStateManager *stack = GPU_context_active_get()->state_manager; \ + auto &state_object = stack->_prefix##state; \ + state_object._state = (_value); \ + } while (0) + +#define SET_IMMUTABLE_STATE(_state, _value) SET_STATE(, _state, _value) +#define SET_MUTABLE_STATE(_state, _value) SET_STATE(mutable_, _state, _value) + +/* -------------------------------------------------------------------- */ +/** \name Immutable state Setters + * \{ */ + +void GPU_blend(eGPUBlend blend) +{ + SET_IMMUTABLE_STATE(blend, blend); } -void GPU_blend(bool enable) +void GPU_face_culling(eGPUFaceCullTest culling) { - if (enable) { - glEnable(GL_BLEND); - } - else { - glDisable(GL_BLEND); - } + SET_IMMUTABLE_STATE(culling_test, culling); } -void GPU_blend_set_func(eGPUBlendFunction sfactor, eGPUBlendFunction dfactor) +void GPU_front_facing(bool invert) { - glBlendFunc(gpu_get_gl_blendfunction(sfactor), gpu_get_gl_blendfunction(dfactor)); + SET_IMMUTABLE_STATE(invert_facing, invert); } -void GPU_blend_set_func_separate(eGPUBlendFunction src_rgb, - eGPUBlendFunction dst_rgb, - eGPUBlendFunction src_alpha, - eGPUBlendFunction dst_alpha) +void GPU_provoking_vertex(eGPUProvokingVertex vert) { - glBlendFuncSeparate(gpu_get_gl_blendfunction(src_rgb), - gpu_get_gl_blendfunction(dst_rgb), - gpu_get_gl_blendfunction(src_alpha), - gpu_get_gl_blendfunction(dst_alpha)); + SET_IMMUTABLE_STATE(provoking_vert, vert); } -void GPU_face_culling(eGPUFaceCull culling) +void GPU_depth_test(eGPUDepthTest test) { - if (culling == GPU_CULL_NONE) { - glDisable(GL_CULL_FACE); - } - else { - glEnable(GL_CULL_FACE); - glCullFace((culling == GPU_CULL_FRONT) ? GL_FRONT : GL_BACK); - } + SET_IMMUTABLE_STATE(depth_test, test); } -void GPU_front_facing(bool invert) +void GPU_stencil_test(eGPUStencilTest test) { - glFrontFace((invert) ? GL_CW : GL_CCW); + SET_IMMUTABLE_STATE(stencil_test, test); } -void GPU_provoking_vertex(eGPUProvokingVertex vert) +void GPU_line_smooth(bool enable) { - glProvokingVertex((vert == GPU_VERTEX_FIRST) ? GL_FIRST_VERTEX_CONVENTION : - GL_LAST_VERTEX_CONVENTION); + SET_IMMUTABLE_STATE(line_smooth, enable); } -void GPU_depth_range(float near, float far) +void GPU_polygon_smooth(bool enable) { - /* glDepthRangef is only for OpenGL 4.1 or higher */ - glDepthRange(near, far); + SET_IMMUTABLE_STATE(polygon_smooth, enable); } -void GPU_depth_test(bool enable) +void GPU_logic_op_xor_set(bool enable) { - if (enable) { - glEnable(GL_DEPTH_TEST); - } - else { - glDisable(GL_DEPTH_TEST); - } + SET_IMMUTABLE_STATE(logic_op_xor, enable); } -bool GPU_depth_test_enabled() +void GPU_write_mask(eGPUWriteMask mask) { - return glIsEnabled(GL_DEPTH_TEST); + SET_IMMUTABLE_STATE(write_mask, mask); } -void GPU_line_smooth(bool enable) +void GPU_color_mask(bool r, bool g, bool b, bool a) { - if (enable && ((G.debug & G_DEBUG_GPU) == 0)) { - glEnable(GL_LINE_SMOOTH); - } - else { - glDisable(GL_LINE_SMOOTH); - } + GPUStateManager *stack = GPU_context_active_get()->state_manager; + auto &state = stack->state; + uint32_t write_mask = state.write_mask; + SET_FLAG_FROM_TEST(write_mask, r, (uint32_t)GPU_WRITE_RED); + SET_FLAG_FROM_TEST(write_mask, g, (uint32_t)GPU_WRITE_GREEN); + SET_FLAG_FROM_TEST(write_mask, b, (uint32_t)GPU_WRITE_BLUE); + SET_FLAG_FROM_TEST(write_mask, a, (uint32_t)GPU_WRITE_ALPHA); + state.write_mask = write_mask; } -void GPU_line_width(float width) +void GPU_depth_mask(bool depth) { - float max_size = GPU_max_line_width(); - float final_size = width * PIXELSIZE; - /* Fix opengl errors on certain platform / drivers. */ - CLAMP(final_size, 1.0f, max_size); - glLineWidth(final_size); + GPUStateManager *stack = GPU_context_active_get()->state_manager; + auto &state = stack->state; + uint32_t write_mask = state.write_mask; + SET_FLAG_FROM_TEST(write_mask, depth, (uint32_t)GPU_WRITE_DEPTH); + state.write_mask = write_mask; } -void GPU_point_size(float size) +void GPU_shadow_offset(bool enable) { - glPointSize(size * PIXELSIZE); + SET_IMMUTABLE_STATE(shadow_bias, enable); } -void GPU_polygon_smooth(bool enable) +void GPU_clip_distances(int distances_enabled) { - if (enable && ((G.debug & G_DEBUG_GPU) == 0)) { - glEnable(GL_POLYGON_SMOOTH); - } - else { - glDisable(GL_POLYGON_SMOOTH); - } + SET_IMMUTABLE_STATE(clip_distances, distances_enabled); +} + +void GPU_state_set(eGPUWriteMask write_mask, + eGPUBlend blend, + eGPUFaceCullTest culling_test, + eGPUDepthTest depth_test, + eGPUStencilTest stencil_test, + eGPUStencilOp stencil_op, + eGPUProvokingVertex provoking_vert) +{ + GPUStateManager *stack = GPU_context_active_get()->state_manager; + auto &state = stack->state; + state.write_mask = (uint32_t)write_mask; + state.blend = (uint32_t)blend; + state.culling_test = (uint32_t)culling_test; + state.depth_test = (uint32_t)depth_test; + state.stencil_test = (uint32_t)stencil_test; + state.stencil_op = (uint32_t)stencil_op; + state.provoking_vert = (uint32_t)provoking_vert; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mutable State Setters + * \{ */ + +void GPU_depth_range(float near, float far) +{ + GPUStateManager *stack = GPU_context_active_get()->state_manager; + auto &state = stack->mutable_state; + copy_v2_fl2(state.depth_range, near, far); +} + +void GPU_line_width(float width) +{ + SET_MUTABLE_STATE(line_width, width * PIXELSIZE); +} + +void GPU_point_size(float size) +{ + SET_MUTABLE_STATE(point_size, size * PIXELSIZE); } /* Programmable point size * - shaders set their own point size when enabled * - use glPointSize when disabled */ +/* TODO remove and use program point size everywhere */ void GPU_program_point_size(bool enable) { - if (enable) { - glEnable(GL_PROGRAM_POINT_SIZE); - } - else { - glDisable(GL_PROGRAM_POINT_SIZE); - } + GPUStateManager *stack = GPU_context_active_get()->state_manager; + auto &state = stack->mutable_state; + /* Set point size sign negative to disable. */ + state.point_size = fabsf(state.point_size) * (enable ? 1 : -1); } void GPU_scissor_test(bool enable) { - if (enable) { - glEnable(GL_SCISSOR_TEST); - } - else { - glDisable(GL_SCISSOR_TEST); - } + GPU_context_active_get()->active_fb->scissor_test_set(enable); } void GPU_scissor(int x, int y, int width, int height) { - glScissor(x, y, width, height); + int scissor_rect[4] = {x, y, width, height}; + GPU_context_active_get()->active_fb->scissor_set(scissor_rect); } void GPU_viewport(int x, int y, int width, int height) { - glViewport(x, y, width, height); + int viewport_rect[4] = {x, y, width, height}; + GPU_context_active_get()->active_fb->viewport_set(viewport_rect); } -void GPU_scissor_get_f(float coords[4]) +void GPU_stencil_reference_set(uint reference) { - glGetFloatv(GL_SCISSOR_BOX, coords); + SET_MUTABLE_STATE(stencil_reference, (uint8_t)reference); } -void GPU_scissor_get_i(int coords[4]) +void GPU_stencil_write_mask_set(uint write_mask) { - glGetIntegerv(GL_SCISSOR_BOX, coords); + SET_MUTABLE_STATE(stencil_write_mask, (uint8_t)write_mask); } -void GPU_viewport_size_get_f(float coords[4]) +void GPU_stencil_compare_mask_set(uint compare_mask) { - glGetFloatv(GL_VIEWPORT, coords); + SET_MUTABLE_STATE(stencil_compare_mask, (uint8_t)compare_mask); } -void GPU_viewport_size_get_i(int coords[4]) -{ - glGetIntegerv(GL_VIEWPORT, coords); -} +/** \} */ -void GPU_flush(void) +/* -------------------------------------------------------------------- */ +/** \name State Getters + * \{ */ + +eGPUBlend GPU_blend_get() { - glFlush(); + GPUState &state = GPU_context_active_get()->state_manager->state; + return (eGPUBlend)state.blend; } -void GPU_finish(void) +eGPUWriteMask GPU_write_mask_get() { - glFinish(); + GPUState &state = GPU_context_active_get()->state_manager->state; + return (eGPUWriteMask)state.write_mask; } -void GPU_unpack_row_length_set(uint len) +uint GPU_stencil_mask_get() { - glPixelStorei(GL_UNPACK_ROW_LENGTH, len); + GPUStateMutable &state = GPU_context_active_get()->state_manager->mutable_state; + return state.stencil_write_mask; } -void GPU_logic_op_xor_set(bool enable) +eGPUDepthTest GPU_depth_test_get() { - if (enable) { - glLogicOp(GL_XOR); - glEnable(GL_COLOR_LOGIC_OP); - } - else { - glDisable(GL_COLOR_LOGIC_OP); - } + GPUState &state = GPU_context_active_get()->state_manager->state; + return (eGPUDepthTest)state.depth_test; } -void GPU_color_mask(bool r, bool g, bool b, bool a) +eGPUStencilTest GPU_stencil_test_get() { - glColorMask(r, g, b, a); + GPUState &state = GPU_context_active_get()->state_manager->state; + return (eGPUStencilTest)state.stencil_test; } -void GPU_depth_mask(bool depth) +void GPU_scissor_get(int coords[4]) { - glDepthMask(depth); + GPU_context_active_get()->active_fb->scissor_get(coords); } -bool GPU_depth_mask_get(void) +void GPU_viewport_size_get_f(float coords[4]) { - GLint mask; - glGetIntegerv(GL_DEPTH_WRITEMASK, &mask); - return mask == GL_TRUE; + int viewport[4]; + GPU_context_active_get()->active_fb->viewport_get(viewport); + for (int i = 0; i < 4; i++) { + coords[i] = viewport[i]; + } } -void GPU_stencil_mask(uint stencil) +void GPU_viewport_size_get_i(int coords[4]) { - glStencilMask(stencil); + GPU_context_active_get()->active_fb->viewport_get(coords); } -void GPU_clip_distances(int distances_new) +bool GPU_depth_mask_get(void) { - static int distances_enabled = 0; - for (int i = 0; i < distances_new; i++) { - glEnable(GL_CLIP_DISTANCE0 + i); - } - for (int i = distances_new; i < distances_enabled; i++) { - glDisable(GL_CLIP_DISTANCE0 + i); - } - distances_enabled = distances_new; + GPUState &state = GPU_context_active_get()->state_manager->state; + return (state.write_mask & GPU_WRITE_DEPTH) != 0; } bool GPU_mipmap_enabled(void) @@ -274,163 +288,61 @@ bool GPU_mipmap_enabled(void) return true; } -/** \name GPU Push/Pop State - * \{ */ - -#define STATE_STACK_DEPTH 16 - -typedef struct { - eGPUAttrMask mask; - - /* GL_BLEND_BIT */ - uint is_blend : 1; - - /* GL_DEPTH_BUFFER_BIT */ - uint is_depth_test : 1; - int depth_func; - double depth_clear_value; - bool depth_write_mask; - - /* GL_SCISSOR_BIT */ - int scissor_box[4]; - uint is_scissor_test : 1; - - /* GL_VIEWPORT_BIT */ - int viewport[4]; - double near_far[2]; -} GPUAttrValues; - -typedef struct { - GPUAttrValues attr_stack[STATE_STACK_DEPTH]; - uint top; -} GPUAttrStack; - -static GPUAttrStack state = { - {}, - 0, -}; +/** \} */ -#define AttrStack state -#define Attr state.attr_stack[state.top] +/* -------------------------------------------------------------------- */ +/** \name Context Utils + * \{ */ -/** - * Replacement for glPush/PopAttributes - * - * We don't need to cover all the options of legacy OpenGL - * but simply the ones used by Blender. - */ -void gpuPushAttr(eGPUAttrMask mask) +void GPU_flush(void) { - Attr.mask = mask; - - if ((mask & GPU_DEPTH_BUFFER_BIT) != 0) { - Attr.is_depth_test = glIsEnabled(GL_DEPTH_TEST); - glGetIntegerv(GL_DEPTH_FUNC, &Attr.depth_func); - glGetDoublev(GL_DEPTH_CLEAR_VALUE, &Attr.depth_clear_value); - glGetBooleanv(GL_DEPTH_WRITEMASK, (GLboolean *)&Attr.depth_write_mask); - } - - if ((mask & GPU_SCISSOR_BIT) != 0) { - Attr.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST); - glGetIntegerv(GL_SCISSOR_BOX, (GLint *)&Attr.scissor_box); - } - - if ((mask & GPU_VIEWPORT_BIT) != 0) { - glGetDoublev(GL_DEPTH_RANGE, (GLdouble *)&Attr.near_far); - glGetIntegerv(GL_VIEWPORT, (GLint *)&Attr.viewport); - } - - if ((mask & GPU_BLEND_BIT) != 0) { - Attr.is_blend = glIsEnabled(GL_BLEND); - } - - BLI_assert(AttrStack.top < STATE_STACK_DEPTH); - AttrStack.top++; + glFlush(); } -static void restore_mask(GLenum cap, const bool value) +void GPU_finish(void) { - if (value) { - glEnable(cap); - } - else { - glDisable(cap); - } + glFinish(); } -void gpuPopAttr(void) +void GPU_unpack_row_length_set(uint len) { - BLI_assert(AttrStack.top > 0); - AttrStack.top--; - - GLint mask = Attr.mask; - - if ((mask & GPU_DEPTH_BUFFER_BIT) != 0) { - restore_mask(GL_DEPTH_TEST, Attr.is_depth_test); - glDepthFunc(Attr.depth_func); - glClearDepth(Attr.depth_clear_value); - glDepthMask(Attr.depth_write_mask); - } - - if ((mask & GPU_VIEWPORT_BIT) != 0) { - glViewport(Attr.viewport[0], Attr.viewport[1], Attr.viewport[2], Attr.viewport[3]); - glDepthRange(Attr.near_far[0], Attr.near_far[1]); - } - - if ((mask & GPU_SCISSOR_BIT) != 0) { - restore_mask(GL_SCISSOR_TEST, Attr.is_scissor_test); - glScissor(Attr.scissor_box[0], Attr.scissor_box[1], Attr.scissor_box[2], Attr.scissor_box[3]); - } - - if ((mask & GPU_BLEND_BIT) != 0) { - restore_mask(GL_BLEND, Attr.is_blend); - } + glPixelStorei(GL_UNPACK_ROW_LENGTH, len); } -#undef Attr -#undef AttrStack +/** \} */ -/* Default OpenGL State +/* -------------------------------------------------------------------- */ +/** \name Default OpenGL State * * This is called on startup, for opengl offscreen render. * Generally we should always return to this state when * temporarily modifying the state for drawing, though that are (undocumented) - * exceptions that we should try to get rid of. */ - -void GPU_state_init(void) -{ - GPU_program_point_size(false); - - glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); - - glDisable(GL_BLEND); - glDisable(GL_DEPTH_TEST); - glDisable(GL_COLOR_LOGIC_OP); - glDisable(GL_STENCIL_TEST); - glDisable(GL_DITHER); - - glDepthFunc(GL_LEQUAL); - glDepthRange(0.0, 1.0); - - glFrontFace(GL_CCW); - glCullFace(GL_BACK); - glDisable(GL_CULL_FACE); - - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - - /* Is default but better be explicit. */ - glEnable(GL_MULTISAMPLE); - - /* This is a bit dangerous since addons could change this. */ - glEnable(GL_PRIMITIVE_RESTART); - glPrimitiveRestartIndex((GLuint)0xFFFFFFFF); + * exceptions that we should try to get rid of. + * \{ */ - /* TODO: Should become default. But needs at least GL 4.3 */ - if (GLEW_ARB_ES3_compatibility) { - /* Takes predecence over GL_PRIMITIVE_RESTART */ - glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); - } +GPUStateManager::GPUStateManager(void) +{ + /* Set default state. */ + state.write_mask = GPU_WRITE_COLOR; + state.blend = GPU_BLEND_NONE; + state.culling_test = GPU_CULL_NONE; + state.depth_test = GPU_DEPTH_NONE; + state.stencil_test = GPU_STENCIL_NONE; + state.stencil_op = GPU_STENCIL_OP_NONE; + state.provoking_vert = GPU_VERTEX_LAST; + state.logic_op_xor = false; + state.invert_facing = false; + state.shadow_bias = false; + state.polygon_smooth = false; + state.clip_distances = 0; + + mutable_state.depth_range[0] = 0.0f; + mutable_state.depth_range[1] = 1.0f; + mutable_state.point_size = 1.0f; + mutable_state.line_width = 1.0f; + mutable_state.stencil_write_mask = 0x00; + mutable_state.stencil_compare_mask = 0x00; + mutable_state.stencil_reference = 0x00; } /** \} */ diff --git a/source/blender/gpu/intern/gpu_state_private.hh b/source/blender/gpu/intern/gpu_state_private.hh new file mode 100644 index 00000000000..61234c4612c --- /dev/null +++ b/source/blender/gpu/intern/gpu_state_private.hh @@ -0,0 +1,166 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "BLI_utildefines.h" + +#include "GPU_state.h" + +#include <cstring> + +namespace blender { +namespace gpu { + +/* Encapsulate all pipeline state that we need to track. + * Try to keep small to reduce validation time. */ +union GPUState { + struct { + /** eGPUWriteMask */ + uint32_t write_mask : 13; + /** eGPUBlend */ + uint32_t blend : 4; + /** eGPUFaceCullTest */ + uint32_t culling_test : 2; + /** eGPUDepthTest */ + uint32_t depth_test : 3; + /** eGPUStencilTest */ + uint32_t stencil_test : 3; + /** eGPUStencilOp */ + uint32_t stencil_op : 3; + /** eGPUProvokingVertex */ + uint32_t provoking_vert : 1; + /** Enable bits. */ + uint32_t logic_op_xor : 1; + uint32_t invert_facing : 1; + uint32_t shadow_bias : 1; + /** Number of clip distances enabled. */ + /* TODO(fclem) This should be a shader property. */ + uint32_t clip_distances : 3; + /* TODO(fclem) remove, old opengl features. */ + uint32_t polygon_smooth : 1; + uint32_t line_smooth : 1; + }; + /* Here to allow fast bitwise ops. */ + uint64_t data; +}; + +BLI_STATIC_ASSERT(sizeof(GPUState) == sizeof(uint64_t), "GPUState is too big."); + +inline bool operator==(const GPUState &a, const GPUState &b) +{ + return a.data == b.data; +} + +inline bool operator!=(const GPUState &a, const GPUState &b) +{ + return !(a == b); +} + +inline GPUState operator^(const GPUState &a, const GPUState &b) +{ + GPUState r; + r.data = a.data ^ b.data; + return r; +} + +inline GPUState operator~(const GPUState &a) +{ + GPUState r; + r.data = ~a.data; + return r; +} + +/* Mutable state that does not require pipeline change. */ +union GPUStateMutable { + struct { + /* Viewport State */ + /** TODO remove */ + float depth_range[2]; + /** TODO remove, use explicit clear calls. */ + float clear_color[4]; + float clear_depth; + /** Negative if using program point size. */ + /* TODO(fclem) should be passed as uniform to all shaders. */ + float point_size; + /** Not supported on every platform. Prefer using wideline shader. */ + float line_width; + /** Mutable stencil states. */ + uint8_t stencil_write_mask; + uint8_t stencil_compare_mask; + uint8_t stencil_reference; + uint8_t _pad0; + /* IMPORTANT: ensure x64 stuct alignment. */ + }; + /* Here to allow fast bitwise ops. */ + uint64_t data[9]; +}; + +BLI_STATIC_ASSERT(sizeof(GPUStateMutable) == sizeof(GPUStateMutable::data), + "GPUStateMutable is too big."); + +inline bool operator==(const GPUStateMutable &a, const GPUStateMutable &b) +{ + return memcmp(&a, &b, sizeof(GPUStateMutable)) == 0; +} + +inline bool operator!=(const GPUStateMutable &a, const GPUStateMutable &b) +{ + return !(a == b); +} + +inline GPUStateMutable operator^(const GPUStateMutable &a, const GPUStateMutable &b) +{ + GPUStateMutable r; + for (int i = 0; i < ARRAY_SIZE(a.data); i++) { + r.data[i] = a.data[i] ^ b.data[i]; + } + return r; +} + +inline GPUStateMutable operator~(const GPUStateMutable &a) +{ + GPUStateMutable r; + for (int i = 0; i < ARRAY_SIZE(a.data); i++) { + r.data[i] = ~a.data[i]; + } + return r; +} + +/** + * State manager keeping track of the draw state and applying it before drawing. + * Base class which is then specialized for each implementation (GL, VK, ...). + **/ +class GPUStateManager { + public: + GPUState state; + GPUStateMutable mutable_state; + + public: + GPUStateManager(); + virtual ~GPUStateManager(){}; + + virtual void apply_state(void) = 0; +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index a45bd222664..1b7e1d4fd6a 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -44,6 +44,7 @@ #include "GPU_texture.h" #include "gpu_context_private.hh" +#include "gpu_framebuffer_private.hh" #define WARN_NOT_BOUND(_tex) \ { \ @@ -109,6 +110,9 @@ struct GPUTexture { GPUContext *copy_fb_ctx; }; +using namespace blender; +using namespace blender::gpu; + static uint gpu_get_bytesize(eGPUTextureFormat data_type); static void gpu_texture_framebuffer_ensure(GPUTexture *tex); @@ -615,7 +619,7 @@ static bool gpu_texture_check_capacity( GPUTexture *tex, GLenum proxy, GLenum internalformat, GLenum data_format, GLenum data_type) { if (proxy == GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB && - GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_MAC, GPU_DRIVER_ANY)) { + GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) { /* Special fix for T79703. */ /* Depth has already been checked. */ return tex->w <= GPU_max_cube_map_size(); @@ -1158,9 +1162,9 @@ static GLenum convert_target_to_gl(int dimension, bool is_array) { switch (dimension) { case 1: - return is_array ? GL_TEXTURE_1D : GL_TEXTURE_1D_ARRAY; + return is_array ? GL_TEXTURE_1D_ARRAY : GL_TEXTURE_1D; case 2: - return is_array ? GL_TEXTURE_2D : GL_TEXTURE_2D_ARRAY; + return is_array ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; case 3: return GL_TEXTURE_3D; default: @@ -1611,6 +1615,9 @@ void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat gpu_data_format, const vo /* This means that this function can only be used in one context for each texture. */ BLI_assert(tex->copy_fb_ctx == GPU_context_active_get()); + int viewport[4]; + GPU_viewport_size_get_i(viewport); + glBindFramebuffer(GL_FRAMEBUFFER, tex->copy_fb); glViewport(0, 0, tex->w, tex->h); @@ -1675,6 +1682,8 @@ void GPU_texture_clear(GPUTexture *tex, eGPUDataFormat gpu_data_format, const vo glClear(GL_COLOR_BUFFER_BIT); } + glViewport(UNPACK4(viewport)); + if (prev_fb) { GPU_framebuffer_bind(prev_fb); } @@ -2015,7 +2024,8 @@ void GPU_texture_free(GPUTexture *tex) if (tex->refcount == 0) { for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; i++) { if (tex->fb[i] != NULL) { - GPU_framebuffer_texture_detach_slot(tex->fb[i], tex, tex->fb_attachment[i]); + FrameBuffer *framebuffer = reinterpret_cast<FrameBuffer *>(tex->fb[i]); + framebuffer->attachment_set((GPUAttachmentType)tex->fb_attachment[i], GPU_ATTACHMENT_NONE); } } @@ -2127,17 +2137,26 @@ void GPU_texture_attach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb, int att } /* Return previous attachment point */ -int GPU_texture_detach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb) +void GPU_texture_detach_framebuffer(GPUTexture *tex, GPUFrameBuffer *fb) { for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; i++) { if (tex->fb[i] == fb) { tex->fb[i] = NULL; - return tex->fb_attachment[i]; + return; } } - BLI_assert(!"Error: Texture: Framebuffer is not attached"); - return 0; +} + +/* Return attachment type for the given framebuffer or -1 if not attached. */ +int GPU_texture_framebuffer_attachement_get(GPUTexture *tex, GPUFrameBuffer *fb) +{ + for (int i = 0; i < GPU_TEX_MAX_FBO_ATTACHED; i++) { + if (tex->fb[i] == fb) { + return tex->fb_attachment[i]; + } + } + return -1; } void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size) @@ -2174,6 +2193,11 @@ void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size) void GPU_samplers_init(void) { + float max_anisotropy = 1.0f; + if (GLEW_EXT_texture_filter_anisotropic) { + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy); + } + glGenSamplers(GPU_SAMPLER_MAX, GG.samplers); for (int i = 0; i < GPU_SAMPLER_MAX; i++) { eGPUSamplerState state = static_cast<eGPUSamplerState>(i); @@ -2188,7 +2212,7 @@ void GPU_samplers_init(void) GLenum compare_mode = (state & GPU_SAMPLER_COMPARE) ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE; /* TODO(fclem) Anisotropic level should be a render engine parameter. */ float aniso_filter = ((state & GPU_SAMPLER_MIPMAP) && (state & GPU_SAMPLER_ANISO)) ? - U.anisotropic_filter : + max_ff(max_anisotropy, U.anisotropic_filter) : 1.0f; glSamplerParameteri(GG.samplers[i], GL_TEXTURE_WRAP_S, wrap_s); diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh new file mode 100644 index 00000000000..6aa2a39046e --- /dev/null +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -0,0 +1,53 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "BLI_assert.h" + +namespace blender { +namespace gpu { + +class Texture { + public: + /** TODO(fclem): make it a non-static function. */ + static GPUAttachmentType attachment_type(GPUTexture *tex, int slot) + { + switch (GPU_texture_format(tex)) { + case GPU_DEPTH_COMPONENT32F: + case GPU_DEPTH_COMPONENT24: + case GPU_DEPTH_COMPONENT16: + BLI_assert(slot == 0); + return GPU_FB_DEPTH_ATTACHMENT; + case GPU_DEPTH24_STENCIL8: + case GPU_DEPTH32F_STENCIL8: + BLI_assert(slot == 0); + return GPU_FB_DEPTH_STENCIL_ATTACHMENT; + default: + return static_cast<GPUAttachmentType>(GPU_FB_COLOR_ATTACHMENT0 + slot); + } + } +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_uniformbuffer.cc b/source/blender/gpu/intern/gpu_uniform_buffer.cc index e203ffd848f..94aa6bd76ab 100644 --- a/source/blender/gpu/intern/gpu_uniformbuffer.cc +++ b/source/blender/gpu/intern/gpu_uniform_buffer.cc @@ -13,7 +13,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * The Original Code is Copyright (C) 2005 Blender Foundation. + * The Original Code is Copyright (C) 2020 Blender Foundation. * All rights reserved. */ @@ -27,58 +27,46 @@ #include "BLI_blenlib.h" #include "BLI_math_base.h" -#include "gpu_context_private.hh" +#include "gpu_backend.hh" #include "gpu_node_graph.h" -#include "GPU_extensions.h" -#include "GPU_glew.h" #include "GPU_material.h" -#include "GPU_uniformbuffer.h" - -typedef struct GPUUniformBuffer { - /** Data size in bytes. */ - int size; - /** GL handle for UBO. */ - GLuint bindcode; - /** Current binding point. */ - int bindpoint; - /** Continuous memory block to copy to GPU. Is own by the GPUUniformBuffer. */ - void *data; -} GPUUniformBuffer; - -GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_out[256]) + +#include "GPU_extensions.h" + +#include "GPU_uniform_buffer.h" +#include "gpu_uniform_buffer_private.hh" + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ + +namespace blender::gpu { + +UniformBuf::UniformBuf(size_t size, const char *name) { /* Make sure that UBO is padded to size of vec4 */ BLI_assert((size % 16) == 0); + BLI_assert(size <= GPU_max_ubo_size()); - if (size > GPU_max_ubo_size()) { - if (err_out) { - BLI_strncpy(err_out, "GPUUniformBuffer: UBO too big", 256); - } - return NULL; - } - - GPUUniformBuffer *ubo = (GPUUniformBuffer *)MEM_mallocN(sizeof(GPUUniformBuffer), __func__); - ubo->size = size; - ubo->data = NULL; - ubo->bindcode = 0; - ubo->bindpoint = -1; - - /* Direct init. */ - if (data != NULL) { - GPU_uniformbuffer_update(ubo, data); - } + size_in_bytes_ = size; - return ubo; + BLI_strncpy(name_, name, sizeof(name_)); } -void GPU_uniformbuffer_free(GPUUniformBuffer *ubo) +UniformBuf::~UniformBuf() { - MEM_SAFE_FREE(ubo->data); - GPU_buf_free(ubo->bindcode); - MEM_freeN(ubo); + MEM_SAFE_FREE(data_); } +} // namespace blender::gpu + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Uniform buffer from GPUInput list + * \{ */ + /** * We need to pad some data types (vec3) on the C side * To match the GPU expected memory block alignment. @@ -111,10 +99,10 @@ static int inputs_cmp(const void *a, const void *b) * Make sure we respect the expected alignment of UBOs. * mat4, vec4, pad vec3 as vec4, then vec2, then floats. */ -static void gpu_uniformbuffer_inputs_sort(ListBase *inputs) +static void buffer_from_list_inputs_sort(ListBase *inputs) { -/* Only support up to this type, if you want to extend it, make sure the - * padding logic is correct for the new types. */ +/* Only support up to this type, if you want to extend it, make sure static void + * inputs_sobuffer_size_compute *inputs) padding logic is correct for the new types. */ #define MAX_UBO_GPU_TYPE GPU_MAT4 /* Order them as mat4, vec4, vec3, vec2, float. */ @@ -173,23 +161,9 @@ static void gpu_uniformbuffer_inputs_sort(ListBase *inputs) #undef MAX_UBO_GPU_TYPE } -/** - * Create dynamic UBO from parameters - * Return NULL if failed to create or if \param inputs: is empty. - * - * \param inputs: ListBase of #BLI_genericNodeN(#GPUInput). - */ -GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_out[256]) +static inline size_t buffer_size_from_list(ListBase *inputs) { - /* There is no point on creating an UBO if there is no arguments. */ - if (BLI_listbase_is_empty(inputs)) { - return NULL; - } - /* Make sure we comply to the ubo alignment requirements. */ - gpu_uniformbuffer_inputs_sort(inputs); - size_t buffer_size = 0; - LISTBASE_FOREACH (LinkData *, link, inputs) { const eGPUType gputype = get_padded_gpu_type(link); buffer_size += gputype * sizeof(float); @@ -197,8 +171,12 @@ GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_ou /* Round up to size of vec4. (Opengl Requirement) */ size_t alignment = sizeof(float[4]); buffer_size = divide_ceil_u(buffer_size, alignment) * alignment; - void *data = MEM_mallocN(buffer_size, __func__); + return buffer_size; +} + +static inline void buffer_fill_from_list(void *data, ListBase *inputs) +{ /* Now that we know the total ubo size we can start populating it. */ float *offset = (float *)data; LISTBASE_FOREACH (LinkData *, link, inputs) { @@ -206,71 +184,73 @@ GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_ou memcpy(offset, input->vec, input->type * sizeof(float)); offset += get_padded_gpu_type(link); } - - /* Pass data as NULL for late init. */ - GPUUniformBuffer *ubo = GPU_uniformbuffer_create(buffer_size, NULL, err_out); - /* Data will be update just before binding. */ - ubo->data = data; - return ubo; } -static void gpu_uniformbuffer_init(GPUUniformBuffer *ubo) -{ - BLI_assert(ubo->bindcode == 0); - ubo->bindcode = GPU_buf_alloc(); +/** \} */ - if (ubo->bindcode == 0) { - fprintf(stderr, "GPUUniformBuffer: UBO create failed"); - BLI_assert(0); - return; - } +/* -------------------------------------------------------------------- */ +/** \name C-API + * \{ */ - glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); - glBufferData(GL_UNIFORM_BUFFER, ubo->size, NULL, GL_DYNAMIC_DRAW); -} +using namespace blender::gpu; -void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) +GPUUniformBuf *GPU_uniformbuf_create_ex(size_t size, const void *data, const char *name) { - if (ubo->bindcode == 0) { - gpu_uniformbuffer_init(ubo); + UniformBuf *ubo = GPUBackend::get()->uniformbuf_alloc(size, name); + /* Direct init. */ + if (data != NULL) { + ubo->update(data); } - - glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); - glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo->size, data); - glBindBuffer(GL_UNIFORM_BUFFER, 0); + return reinterpret_cast<GPUUniformBuf *>(ubo); } -void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number) +/** + * Create UBO from inputs list. + * Return NULL if failed to create or if \param inputs: is empty. + * + * \param inputs: ListBase of #BLI_genericNodeN(#GPUInput). + */ +GPUUniformBuf *GPU_uniformbuf_create_from_list(ListBase *inputs, const char *name) { - if (number >= GPU_max_ubo_binds()) { - fprintf(stderr, "Not enough UBO slots.\n"); - return; + /* There is no point on creating an UBO if there is no arguments. */ + if (BLI_listbase_is_empty(inputs)) { + return NULL; } - if (ubo->bindcode == 0) { - gpu_uniformbuffer_init(ubo); - } + buffer_from_list_inputs_sort(inputs); + size_t buffer_size = buffer_size_from_list(inputs); + void *data = MEM_mallocN(buffer_size, __func__); + buffer_fill_from_list(data, inputs); - if (ubo->data != NULL) { - GPU_uniformbuffer_update(ubo, ubo->data); - MEM_SAFE_FREE(ubo->data); - } + UniformBuf *ubo = GPUBackend::get()->uniformbuf_alloc(buffer_size, name); + /* Defer data upload. */ + ubo->attach_data(data); + return reinterpret_cast<GPUUniformBuf *>(ubo); +} - glBindBufferBase(GL_UNIFORM_BUFFER, number, ubo->bindcode); - ubo->bindpoint = number; +void GPU_uniformbuf_free(GPUUniformBuf *ubo) +{ + delete reinterpret_cast<UniformBuf *>(ubo); } -void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo) +void GPU_uniformbuf_update(GPUUniformBuf *ubo, const void *data) { -#ifndef NDEBUG - glBindBufferBase(GL_UNIFORM_BUFFER, ubo->bindpoint, 0); -#endif - ubo->bindpoint = 0; + reinterpret_cast<UniformBuf *>(ubo)->update(data); } -void GPU_uniformbuffer_unbind_all(void) +void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int slot) { - for (int i = 0; i < GPU_max_ubo_binds(); i++) { - glBindBufferBase(GL_UNIFORM_BUFFER, i, 0); - } + reinterpret_cast<UniformBuf *>(ubo)->bind(slot); } + +void GPU_uniformbuf_unbind(GPUUniformBuf *ubo) +{ + reinterpret_cast<UniformBuf *>(ubo)->unbind(); +} + +void GPU_uniformbuf_unbind_all(void) +{ + /* FIXME */ +} + +/** \} */ diff --git a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh new file mode 100644 index 00000000000..cf6447ccd37 --- /dev/null +++ b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh @@ -0,0 +1,69 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "BLI_sys_types.h" + +namespace blender { +namespace gpu { + +#ifdef DEBUG +# define DEBUG_NAME_LEN 64 +#else +# define DEBUG_NAME_LEN 8 +#endif + +/** + * Implementation of Uniform Buffers. + * Base class which is then specialized for each implementation (GL, VK, ...). + **/ +class UniformBuf { + protected: + /** Data size in bytes. */ + size_t size_in_bytes_; + /** Continuous memory block to copy to GPU. This data is owned by the UniformBuf. */ + void *data_ = NULL; + /** Debugging name */ + char name_[DEBUG_NAME_LEN]; + + public: + UniformBuf(size_t size, const char *name); + virtual ~UniformBuf(); + + virtual void update(const void *data) = 0; + virtual void bind(int slot) = 0; + virtual void unbind(void) = 0; + + /** Used to defer data upload at drawing time. + * This is useful if the thread has no context bound. + * This transfers ownership to this UniformBuf. */ + void attach_data(void *data) + { + data_ = data; + } +}; + +#undef DEBUG_NAME_LEN + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc index 67ad8835b6a..debf9835c90 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer.cc +++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc @@ -77,6 +77,7 @@ void GPU_vertbuf_init(GPUVertBuf *verts, GPUUsageType usage) memset(verts, 0, sizeof(GPUVertBuf)); verts->usage = usage; verts->dirty = true; + verts->handle_refcount = 1; } void GPU_vertbuf_init_with_format_ex(GPUVertBuf *verts, @@ -137,7 +138,23 @@ void GPU_vertbuf_clear(GPUVertBuf *verts) void GPU_vertbuf_discard(GPUVertBuf *verts) { GPU_vertbuf_clear(verts); - MEM_freeN(verts); + GPU_vertbuf_handle_ref_remove(verts); +} + +void GPU_vertbuf_handle_ref_add(GPUVertBuf *verts) +{ + verts->handle_refcount++; +} + +void GPU_vertbuf_handle_ref_remove(GPUVertBuf *verts) +{ + BLI_assert(verts->handle_refcount > 0); + verts->handle_refcount--; + if (verts->handle_refcount == 0) { + /* Should already have been cleared. */ + BLI_assert(verts->vbo_id == 0 && verts->data == NULL); + MEM_freeN(verts); + } } uint GPU_vertbuf_size_get(const GPUVertBuf *verts) diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc index 2e8017660d0..ed317b3a0df 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.cc +++ b/source/blender/gpu/intern/gpu_vertex_format.cc @@ -24,7 +24,9 @@ */ #include "GPU_vertex_format.h" +#include "gpu_shader_private.hh" #include "gpu_vertex_format_private.h" + #include <stddef.h> #include <string.h> @@ -32,15 +34,14 @@ #include "BLI_string.h" #include "BLI_utildefines.h" -#include "GPU_shader.h" -#include "gpu_shader_private.h" - #define PACK_DEBUG 0 #if PACK_DEBUG # include <stdio.h> #endif +using namespace blender::gpu; + void GPU_vertformat_clear(GPUVertFormat *format) { #if TRUST_NO_ONE @@ -404,112 +405,8 @@ void VertexFormat_pack(GPUVertFormat *format) format->packed = true; } -static uint calc_component_size(const GLenum gl_type) +void GPU_vertformat_from_shader(GPUVertFormat *format, const struct GPUShader *gpushader) { - switch (gl_type) { - case GL_FLOAT_VEC2: - case GL_INT_VEC2: - case GL_UNSIGNED_INT_VEC2: - return 2; - case GL_FLOAT_VEC3: - case GL_INT_VEC3: - case GL_UNSIGNED_INT_VEC3: - return 3; - case GL_FLOAT_VEC4: - case GL_FLOAT_MAT2: - case GL_INT_VEC4: - case GL_UNSIGNED_INT_VEC4: - return 4; - case GL_FLOAT_MAT3: - return 9; - case GL_FLOAT_MAT4: - return 16; - case GL_FLOAT_MAT2x3: - case GL_FLOAT_MAT3x2: - return 6; - case GL_FLOAT_MAT2x4: - case GL_FLOAT_MAT4x2: - return 8; - case GL_FLOAT_MAT3x4: - case GL_FLOAT_MAT4x3: - return 12; - default: - return 1; - } -} - -static void get_fetch_mode_and_comp_type(int gl_type, - GPUVertCompType *r_comp_type, - GPUVertFetchMode *r_fetch_mode) -{ - switch (gl_type) { - case GL_FLOAT: - case GL_FLOAT_VEC2: - case GL_FLOAT_VEC3: - case GL_FLOAT_VEC4: - case GL_FLOAT_MAT2: - case GL_FLOAT_MAT3: - case GL_FLOAT_MAT4: - case GL_FLOAT_MAT2x3: - case GL_FLOAT_MAT2x4: - case GL_FLOAT_MAT3x2: - case GL_FLOAT_MAT3x4: - case GL_FLOAT_MAT4x2: - case GL_FLOAT_MAT4x3: - *r_comp_type = GPU_COMP_F32; - *r_fetch_mode = GPU_FETCH_FLOAT; - break; - case GL_INT: - case GL_INT_VEC2: - case GL_INT_VEC3: - case GL_INT_VEC4: - *r_comp_type = GPU_COMP_I32; - *r_fetch_mode = GPU_FETCH_INT; - break; - case GL_UNSIGNED_INT: - case GL_UNSIGNED_INT_VEC2: - case GL_UNSIGNED_INT_VEC3: - case GL_UNSIGNED_INT_VEC4: - *r_comp_type = GPU_COMP_U32; - *r_fetch_mode = GPU_FETCH_INT; - break; - default: - BLI_assert(0); - } -} - -void GPU_vertformat_from_shader(GPUVertFormat *format, const GPUShader *shader) -{ - GPU_vertformat_clear(format); - GPUVertAttr *attr = &format->attrs[0]; - - GLint attr_len; - glGetProgramiv(shader->program, GL_ACTIVE_ATTRIBUTES, &attr_len); - - for (int i = 0; i < attr_len; i++) { - char name[256]; - GLenum gl_type; - GLint size; - glGetActiveAttrib(shader->program, i, sizeof(name), NULL, &size, &gl_type, name); - - /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */ - if (glGetAttribLocation(shader->program, name) == -1) { - continue; - } - - format->name_len++; /* multiname support */ - format->attr_len++; - - GPUVertCompType comp_type; - GPUVertFetchMode fetch_mode; - get_fetch_mode_and_comp_type(gl_type, &comp_type, &fetch_mode); - - attr->names[attr->name_len++] = copy_attr_name(format, name); - attr->offset = 0; /* offsets & stride are calculated later (during pack) */ - attr->comp_len = calc_component_size(gl_type) * size; - attr->sz = attr->comp_len * 4; - attr->fetch_mode = fetch_mode; - attr->comp_type = comp_type; - attr += 1; - } + const Shader *shader = reinterpret_cast<const Shader *>(gpushader); + shader->vertformat_from_shader(format); } diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index ba938349761..fd1265dc2a6 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -41,7 +41,7 @@ #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "GPU_viewport.h" #include "DRW_engine.h" @@ -1020,8 +1020,8 @@ void GPU_viewport_free(GPUViewport *viewport) } for (int i = 0; i < viewport->vmempool.ubo_len; i++) { - GPU_uniformbuffer_free(viewport->vmempool.matrices_ubo[i]); - GPU_uniformbuffer_free(viewport->vmempool.obinfos_ubo[i]); + GPU_uniformbuf_free(viewport->vmempool.matrices_ubo[i]); + GPU_uniformbuf_free(viewport->vmempool.obinfos_ubo[i]); } MEM_SAFE_FREE(viewport->vmempool.matrices_ubo); MEM_SAFE_FREE(viewport->vmempool.obinfos_ubo); diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh index f7c01b2f184..332350e47b5 100644 --- a/source/blender/gpu/opengl/gl_backend.hh +++ b/source/blender/gpu/opengl/gl_backend.hh @@ -27,7 +27,12 @@ #include "BLI_vector.hh" +#include "gl_batch.hh" #include "gl_context.hh" +#include "gl_drawlist.hh" +#include "gl_framebuffer.hh" +#include "gl_shader.hh" +#include "gl_uniform_buffer.hh" namespace blender { namespace gpu { @@ -37,11 +42,41 @@ class GLBackend : public GPUBackend { GLSharedOrphanLists shared_orphan_list_; public: + static GLBackend *get(void) + { + return static_cast<GLBackend *>(GPUBackend::get()); + } + GPUContext *context_alloc(void *ghost_window) { return new GLContext(ghost_window, shared_orphan_list_); }; + Batch *batch_alloc(void) + { + return new GLBatch(); + }; + + DrawList *drawlist_alloc(int list_length) + { + return new GLDrawList(list_length); + }; + + FrameBuffer *framebuffer_alloc(const char *name) + { + return new GLFrameBuffer(name); + }; + + Shader *shader_alloc(const char *name) + { + return new GLShader(name); + }; + + UniformBuf *uniformbuf_alloc(int size, const char *name) + { + return new GLUniformBuf(size, name); + }; + /* TODO remove */ void buf_free(GLuint buf_id); void tex_free(GLuint tex_id); diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc new file mode 100644 index 00000000000..bb7d5654efd --- /dev/null +++ b/source/blender/gpu/opengl/gl_batch.cc @@ -0,0 +1,381 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GL implementation of GPUBatch. + * The only specificity of GL here is that it caches a list of + * Vertex Array Objects based on the bound shader interface. + */ + +#include "BLI_assert.h" + +#include "glew-mx.h" + +#include "GPU_extensions.h" + +#include "gpu_batch_private.hh" +#include "gpu_shader_private.hh" + +#include "gl_batch.hh" +#include "gl_context.hh" +#include "gl_debug.hh" +#include "gl_primitive.hh" +#include "gl_vertex_array.hh" + +using namespace blender::gpu; + +/* -------------------------------------------------------------------- */ +/** \name Vao cache + * + * Each GLBatch has a small cache of VAO objects that are used to avoid VAO reconfiguration. + * TODO(fclem) Could be revisited to avoid so much cross references. + * \{ */ + +GLVaoCache::GLVaoCache(void) +{ + init(); +} + +GLVaoCache::~GLVaoCache() +{ + this->clear(); +} + +void GLVaoCache::init(void) +{ + context_ = NULL; + interface_ = NULL; + is_dynamic_vao_count = false; + for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) { + static_vaos.interfaces[i] = NULL; + static_vaos.vao_ids[i] = 0; + } + vao_base_instance_ = 0; + base_instance_ = 0; + vao_id_ = 0; +} + +/* Create a new VAO object and store it in the cache. */ +void GLVaoCache::insert(const GLShaderInterface *interface, GLuint vao) +{ + /* Now insert the cache. */ + if (!is_dynamic_vao_count) { + int i; /* find first unused slot */ + for (i = 0; i < GPU_VAO_STATIC_LEN; i++) { + if (static_vaos.vao_ids[i] == 0) { + break; + } + } + + if (i < GPU_VAO_STATIC_LEN) { + static_vaos.interfaces[i] = interface; + static_vaos.vao_ids[i] = vao; + } + else { + /* Erase previous entries, they will be added back if drawn again. */ + for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) { + if (static_vaos.interfaces[i] != NULL) { + const_cast<GLShaderInterface *>(static_vaos.interfaces[i])->ref_remove(this); + context_->vao_free(static_vaos.vao_ids[i]); + } + } + /* Not enough place switch to dynamic. */ + is_dynamic_vao_count = true; + /* Init dynamic arrays and let the branch below set the values. */ + dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT; + dynamic_vaos.interfaces = (const GLShaderInterface **)MEM_callocN( + dynamic_vaos.count * sizeof(GLShaderInterface *), "dyn vaos interfaces"); + dynamic_vaos.vao_ids = (GLuint *)MEM_callocN(dynamic_vaos.count * sizeof(GLuint), + "dyn vaos ids"); + } + } + + if (is_dynamic_vao_count) { + int i; /* find first unused slot */ + for (i = 0; i < dynamic_vaos.count; i++) { + if (dynamic_vaos.vao_ids[i] == 0) { + break; + } + } + + if (i == dynamic_vaos.count) { + /* Not enough place, realloc the array. */ + i = dynamic_vaos.count; + dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT; + dynamic_vaos.interfaces = (const GLShaderInterface **)MEM_recallocN( + (void *)dynamic_vaos.interfaces, sizeof(GLShaderInterface *) * dynamic_vaos.count); + dynamic_vaos.vao_ids = (GLuint *)MEM_recallocN(dynamic_vaos.vao_ids, + sizeof(GLuint) * dynamic_vaos.count); + } + dynamic_vaos.interfaces[i] = interface; + dynamic_vaos.vao_ids[i] = vao; + } + + const_cast<GLShaderInterface *>(interface)->ref_add(this); +} + +void GLVaoCache::remove(const GLShaderInterface *interface) +{ + const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN; + GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids; + const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : + static_vaos.interfaces; + for (int i = 0; i < count; i++) { + if (interfaces[i] == interface) { + context_->vao_free(vaos[i]); + vaos[i] = 0; + interfaces[i] = NULL; + break; /* cannot have duplicates */ + } + } +} + +void GLVaoCache::clear(void) +{ + GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get()); + const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN; + GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids; + const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : + static_vaos.interfaces; + /* Early out, nothing to free. */ + if (context_ == NULL) { + return; + } + + if (context_ == ctx) { + glDeleteVertexArrays(count, vaos); + glDeleteVertexArrays(1, &vao_base_instance_); + } + else { + /* TODO(fclem) Slow way. Could avoid multiple mutex lock here */ + for (int i = 0; i < count; i++) { + context_->vao_free(vaos[i]); + } + context_->vao_free(vao_base_instance_); + } + + for (int i = 0; i < count; i++) { + if (interfaces[i] != NULL) { + const_cast<GLShaderInterface *>(interfaces[i])->ref_remove(this); + } + } + + if (is_dynamic_vao_count) { + MEM_freeN((void *)dynamic_vaos.interfaces); + MEM_freeN(dynamic_vaos.vao_ids); + } + + if (context_) { + context_->vao_cache_unregister(this); + } + /* Reinit. */ + this->init(); +} + +/* Return 0 on cache miss (invalid VAO) */ +GLuint GLVaoCache::lookup(const GLShaderInterface *interface) +{ + const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN; + const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : + static_vaos.interfaces; + for (int i = 0; i < count; i++) { + if (interfaces[i] == interface) { + return (is_dynamic_vao_count) ? dynamic_vaos.vao_ids[i] : static_vaos.vao_ids[i]; + } + } + return 0; +} + +/* The GLVaoCache object is only valid for one GLContext. + * Reset the cache if trying to draw in another context; */ +void GLVaoCache::context_check(void) +{ + GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get()); + BLI_assert(ctx); + + if (context_ != ctx) { + if (context_ != NULL) { + /* IMPORTANT: Trying to draw a batch in multiple different context will trash the VAO cache. + * This has major performance impact and should be avoided in most cases. */ + context_->vao_cache_unregister(this); + } + this->clear(); + context_ = ctx; + context_->vao_cache_register(this); + } +} + +GLuint GLVaoCache::base_instance_vao_get(GPUBatch *batch, int i_first) +{ + this->context_check(); + /* Make sure the interface is up to date. */ + Shader *shader = GPU_context_active_get()->shader; + GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface); + if (interface_ != interface) { + vao_get(batch); + /* Trigger update. */ + base_instance_ = 0; + } + /** + * There seems to be a nasty bug when drawing using the same VAO reconfiguring (T71147). + * We just use a throwaway VAO for that. Note that this is likely to degrade performance. + **/ +#ifdef __APPLE__ + glDeleteVertexArrays(1, &vao_base_instance_); + vao_base_instance_ = 0; + base_instance_ = 0; +#endif + + if (vao_base_instance_ == 0) { + glGenVertexArrays(1, &vao_base_instance_); + } + + if (base_instance_ != i_first) { + base_instance_ = i_first; + GLVertArray::update_bindings(vao_base_instance_, batch, interface_, i_first); + } + return vao_base_instance_; +} + +GLuint GLVaoCache::vao_get(GPUBatch *batch) +{ + this->context_check(); + + Shader *shader = GPU_context_active_get()->shader; + GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface); + if (interface_ != interface) { + interface_ = interface; + vao_id_ = this->lookup(interface_); + + if (vao_id_ == 0) { + /* Cache miss, create a new VAO. */ + glGenVertexArrays(1, &vao_id_); + this->insert(interface_, vao_id_); + GLVertArray::update_bindings(vao_id_, batch, interface_, 0); + } + } + + return vao_id_; +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ + +GLBatch::GLBatch(void) +{ +} + +GLBatch::~GLBatch() +{ +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Drawing + * \{ */ + +#if GPU_TRACK_INDEX_RANGE +# define BASE_INDEX(el) ((el)->base_index) +# define INDEX_TYPE(el) ((el)->gl_index_type) +#else +# define BASE_INDEX(el) 0 +# define INDEX_TYPE(el) GL_UNSIGNED_INT +#endif + +void GLBatch::bind(int i_first) +{ + GPU_context_active_get()->state_manager->apply_state(); + + if (flag & GPU_BATCH_DIRTY) { + flag &= ~GPU_BATCH_DIRTY; + vao_cache_.clear(); + } + +#if GPU_TRACK_INDEX_RANGE + /* Can be removed if GL 4.3 is required. */ + if (!GLEW_ARB_ES3_compatibility && (elem != NULL)) { + glPrimitiveRestartIndex((elem->index_type == GPU_INDEX_U16) ? 0xFFFFu : 0xFFFFFFFFu); + } +#endif + + /* Can be removed if GL 4.2 is required. */ + if (!GPU_arb_base_instance_is_supported() && (i_first > 0)) { + glBindVertexArray(vao_cache_.base_instance_vao_get(this, i_first)); + } + else { + glBindVertexArray(vao_cache_.vao_get(this)); + } +} + +void GLBatch::draw(int v_first, int v_count, int i_first, int i_count) +{ + GL_CHECK_RESOURCES("Batch"); + GL_CHECK_ERROR("Batch Pre drawing"); + + this->bind(i_first); + + BLI_assert(v_count > 0 && i_count > 0); + + GLenum gl_type = to_gl(prim_type); + + if (elem) { + const GPUIndexBuf *el = elem; + GLenum index_type = INDEX_TYPE(el); + GLint base_index = BASE_INDEX(el); + void *v_first_ofs = (GLuint *)0 + v_first + el->index_start; + +#if GPU_TRACK_INDEX_RANGE + if (el->index_type == GPU_INDEX_U16) { + v_first_ofs = (GLushort *)0 + v_first + el->index_start; + } +#endif + + if (GPU_arb_base_instance_is_supported()) { + glDrawElementsInstancedBaseVertexBaseInstance( + gl_type, v_count, index_type, v_first_ofs, i_count, base_index, i_first); + } + else { + glDrawElementsInstancedBaseVertex( + gl_type, v_count, index_type, v_first_ofs, i_count, base_index); + } + GL_CHECK_ERROR("Batch Post-drawing Indexed"); + } + else { +#ifdef __APPLE__ + glDisable(GL_PRIMITIVE_RESTART); +#endif + if (GPU_arb_base_instance_is_supported()) { + glDrawArraysInstancedBaseInstance(gl_type, v_first, v_count, i_count, i_first); + } + else { + glDrawArraysInstanced(gl_type, v_first, v_count, i_count); + } +#ifdef __APPLE__ + glEnable(GL_PRIMITIVE_RESTART); +#endif + GL_CHECK_ERROR("Batch Post-drawing Non-indexed"); + } +} + +/** \} */ diff --git a/source/blender/gpu/opengl/gl_batch.hh b/source/blender/gpu/opengl/gl_batch.hh new file mode 100644 index 00000000000..9399148c68d --- /dev/null +++ b/source/blender/gpu/opengl/gl_batch.hh @@ -0,0 +1,106 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2020, Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GPU geometry batch + * Contains VAOs + VBOs + Shader representing a drawable entity. + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "gpu_batch_private.hh" + +#include "glew-mx.h" + +namespace blender { +namespace gpu { + +class GLContext; +class GLShaderInterface; + +#define GPU_VAO_STATIC_LEN 3 + +/* Vao management: remembers all geometry state (vertex attribute bindings & element buffer) + * for each shader interface. Start with a static number of vaos and fallback to dynamic count + * if necessary. Once a batch goes dynamic it does not go back. */ +class GLVaoCache { + private: + /** Context for which the vao_cache_ was generated. */ + GLContext *context_ = NULL; + /** Last interface this batch was drawn with. */ + GLShaderInterface *interface_ = NULL; + /** Cached vao for the last interface. */ + GLuint vao_id_ = 0; + /** Used whend arb_base_instance is not supported. */ + GLuint vao_base_instance_ = 0; + int base_instance_ = 0; + + bool is_dynamic_vao_count = false; + union { + /** Static handle count */ + struct { + const GLShaderInterface *interfaces[GPU_VAO_STATIC_LEN]; + GLuint vao_ids[GPU_VAO_STATIC_LEN]; + } static_vaos; + /** Dynamic handle count */ + struct { + uint count; + const GLShaderInterface **interfaces; + GLuint *vao_ids; + } dynamic_vaos; + }; + + public: + GLVaoCache(); + ~GLVaoCache(); + + GLuint vao_get(GPUBatch *batch); + GLuint base_instance_vao_get(GPUBatch *batch, int i_first); + + GLuint lookup(const GLShaderInterface *interface); + void insert(const GLShaderInterface *interface, GLuint vao_id); + void remove(const GLShaderInterface *interface); + void clear(void); + + private: + void init(void); + void context_check(void); +}; + +class GLBatch : public Batch { + public: + /** All vaos corresponding to all the GPUShaderInterface this batch was drawn with. */ + GLVaoCache vao_cache_; + + public: + GLBatch(); + ~GLBatch(); + + void draw(int v_first, int v_count, int i_first, int i_count) override; + void bind(int i_first); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLBatch"); +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_context.cc b/source/blender/gpu/opengl/gl_context.cc index 37c84abaa7f..1495e665aa8 100644 --- a/source/blender/gpu/opengl/gl_context.cc +++ b/source/blender/gpu/opengl/gl_context.cc @@ -22,14 +22,22 @@ */ #include "BLI_assert.h" +#include "BLI_system.h" #include "BLI_utildefines.h" +#include "BKE_global.h" + #include "GPU_framebuffer.h" #include "GHOST_C-api.h" #include "gpu_context_private.hh" +#include "gl_debug.hh" +#include "gl_immediate.hh" +#include "gl_state.hh" +#include "gl_uniform_buffer.hh" + #include "gl_backend.hh" /* TODO remove */ #include "gl_context.hh" @@ -43,17 +51,50 @@ using namespace blender::gpu; GLContext::GLContext(void *ghost_window, GLSharedOrphanLists &shared_orphan_list) : shared_orphan_list_(shared_orphan_list) { - default_framebuffer_ = ghost_window ? - GHOST_GetDefaultOpenGLFramebuffer((GHOST_WindowHandle)ghost_window) : - 0; - - glGenVertexArrays(1, &default_vao_); + if (G.debug & G_DEBUG_GPU) { + debug::init_gl_callbacks(); + } float data[4] = {0.0f, 0.0f, 0.0f, 1.0f}; glGenBuffers(1, &default_attr_vbo_); glBindBuffer(GL_ARRAY_BUFFER, default_attr_vbo_); glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); + + state_manager = new GLStateManager(); + imm = new GLImmediate(); + ghost_window_ = ghost_window; + + if (ghost_window) { + GLuint default_fbo = GHOST_GetDefaultOpenGLFramebuffer((GHOST_WindowHandle)ghost_window); + GHOST_RectangleHandle bounds = GHOST_GetClientBounds((GHOST_WindowHandle)ghost_window); + int w = GHOST_GetWidthRectangle(bounds); + int h = GHOST_GetHeightRectangle(bounds); + GHOST_DisposeRectangle(bounds); + + if (default_fbo != 0) { + front_left = new GLFrameBuffer("front_left", this, GL_COLOR_ATTACHMENT0, default_fbo, w, h); + back_left = new GLFrameBuffer("back_left", this, GL_COLOR_ATTACHMENT0, default_fbo, w, h); + } + else { + front_left = new GLFrameBuffer("front_left", this, GL_FRONT_LEFT, 0, w, h); + back_left = new GLFrameBuffer("back_left", this, GL_BACK_LEFT, 0, w, h); + } + /* TODO(fclem) enable is supported. */ + const bool supports_stereo_quad_buffer = false; + if (supports_stereo_quad_buffer) { + front_right = new GLFrameBuffer("front_right", this, GL_FRONT_RIGHT, 0, w, h); + back_right = new GLFrameBuffer("back_right", this, GL_BACK_RIGHT, 0, w, h); + } + } + else { + /* For offscreen contexts. Default framebuffer is NULL. */ + back_left = new GLFrameBuffer("back_left", this, GL_NONE, 0, 0, 0); + } + + active_fb = back_left; + static_cast<GLStateManager *>(state_manager)->active_fb = static_cast<GLFrameBuffer *>( + back_left); } GLContext::~GLContext() @@ -63,10 +104,9 @@ GLContext::~GLContext() /* For now don't allow GPUFrameBuffers to be reuse in another context. */ BLI_assert(framebuffers_.is_empty()); /* Delete vaos so the batch can be reused in another context. */ - for (GPUBatch *batch : batches_) { - GPU_batch_vao_cache_clear(batch); + for (GLVaoCache *cache : vao_caches_) { + cache->clear(); } - glDeleteVertexArrays(1, &default_vao_); glDeleteBuffers(1, &default_attr_vbo_); } @@ -86,6 +126,31 @@ void GLContext::activate(void) /* Clear accumulated orphans. */ orphans_clear(); + + if (ghost_window_) { + /* Get the correct framebuffer size for the internal framebuffers. */ + GHOST_RectangleHandle bounds = GHOST_GetClientBounds((GHOST_WindowHandle)ghost_window_); + int w = GHOST_GetWidthRectangle(bounds); + int h = GHOST_GetHeightRectangle(bounds); + GHOST_DisposeRectangle(bounds); + + if (front_left) { + front_left->size_set(w, h); + } + if (back_left) { + back_left->size_set(w, h); + } + if (front_right) { + front_right->size_set(w, h); + } + if (back_right) { + back_right->size_set(w, h); + } + } + + /* Not really following the state but we should consider + * no ubo bound when activating a context. */ + bound_ubo_slots = 0; } void GLContext::deactivate(void) @@ -193,47 +258,22 @@ void GLBackend::tex_free(GLuint tex_id) /** \name Linked object deletion * * These objects contain data that are stored per context. We - * need to do some cleanup if they are used accross context or if context + * need to do some cleanup if they are used across context or if context * is discarded. * \{ */ -void GLContext::batch_register(struct GPUBatch *batch) -{ - lists_mutex_.lock(); - batches_.add(batch); - lists_mutex_.unlock(); -} - -void GLContext::batch_unregister(struct GPUBatch *batch) -{ - /* vao_cache_clear() can acquire lists_mutex_ so avoid deadlock. */ - // reinterpret_cast<GLBatch *>(batch)->vao_cache_clear(); - - lists_mutex_.lock(); - batches_.remove(batch); - lists_mutex_.unlock(); -} - -void GLContext::framebuffer_register(struct GPUFrameBuffer *fb) +void GLContext::vao_cache_register(GLVaoCache *cache) { -#ifdef DEBUG lists_mutex_.lock(); - framebuffers_.add(fb); + vao_caches_.add(cache); lists_mutex_.unlock(); -#else - UNUSED_VARS(fb); -#endif } -void GLContext::framebuffer_unregister(struct GPUFrameBuffer *fb) +void GLContext::vao_cache_unregister(GLVaoCache *cache) { -#ifdef DEBUG lists_mutex_.lock(); - framebuffers_.remove(fb); + vao_caches_.remove(cache); lists_mutex_.unlock(); -#else - UNUSED_VARS(fb); -#endif } /** \} */ diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh index 3b55965b9d1..bc7e2060804 100644 --- a/source/blender/gpu/opengl/gl_context.hh +++ b/source/blender/gpu/opengl/gl_context.hh @@ -25,19 +25,20 @@ #include "gpu_context_private.hh" +#include "GPU_framebuffer.h" + #include "BLI_set.hh" #include "BLI_vector.hh" #include "glew-mx.h" -#include <iostream> #include <mutex> -#include <unordered_set> -#include <vector> namespace blender { namespace gpu { +class GLVaoCache; + class GLSharedOrphanLists { public: /** Mutex for the bellow structures. */ @@ -51,19 +52,19 @@ class GLSharedOrphanLists { }; class GLContext : public GPUContext { + public: + /** Used for debugging purpose. Bitflags of all bound slots. */ + uint16_t bound_ubo_slots; + /* TODO(fclem) these needs to become private. */ public: - /** Default VAO for procedural draw calls. */ - GLuint default_vao_; - /** Default framebuffer object for some GL implementation. */ - GLuint default_framebuffer_; /** VBO for missing vertex attrib binding. Avoid undefined behavior on some implementation. */ GLuint default_attr_vbo_; /** * GPUBatch & GPUFramebuffer have references to the context they are from, in the case the * context is destroyed, we need to remove any reference to it. */ - Set<GPUBatch *> batches_; + Set<GLVaoCache *> vao_caches_; Set<GPUFrameBuffer *> framebuffers_; /** Mutex for the bellow structures. */ std::mutex lists_mutex_; @@ -77,6 +78,8 @@ class GLContext : public GPUContext { GLContext(void *ghost_window, GLSharedOrphanLists &shared_orphan_list); ~GLContext(); + static void check_error(const char *info); + void activate(void) override; void deactivate(void) override; @@ -87,10 +90,8 @@ class GLContext : public GPUContext { void vao_free(GLuint vao_id); void fbo_free(GLuint fbo_id); - void batch_register(struct GPUBatch *batch); - void batch_unregister(struct GPUBatch *batch); - void framebuffer_register(struct GPUFrameBuffer *fb); - void framebuffer_unregister(struct GPUFrameBuffer *fb); + void vao_cache_register(GLVaoCache *cache); + void vao_cache_unregister(GLVaoCache *cache); }; } // namespace gpu diff --git a/source/blender/gpu/opengl/gl_debug.cc b/source/blender/gpu/opengl/gl_debug.cc new file mode 100644 index 00000000000..c1a3780bb51 --- /dev/null +++ b/source/blender/gpu/opengl/gl_debug.cc @@ -0,0 +1,207 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Debug features of OpenGL. + */ + +#include "BLI_compiler_attrs.h" +#include "BLI_string.h" +#include "BLI_system.h" +#include "BLI_utildefines.h" + +#include "glew-mx.h" + +#include "gl_context.hh" +#include "gl_uniform_buffer.hh" + +#include "gl_debug.hh" + +#include <stdio.h> + +namespace blender::gpu::debug { + +/* -------------------------------------------------------------------- */ +/** \name Debug Callbacks + * + * Hooks up debug callbacks to a debug OpenGL context using extensions or 4.3 core debug + * capabiliities. + * \{ */ + +/* Debug callbacks need the same calling convention as OpenGL functions. */ +#if defined(_WIN32) +# define APIENTRY __stdcall +#else +# define APIENTRY +#endif + +#define VERBOSE 1 + +static void APIENTRY debug_callback(GLenum UNUSED(source), + GLenum type, + GLuint UNUSED(id), + GLenum severity, + GLsizei UNUSED(length), + const GLchar *message, + const GLvoid *UNUSED(userParm)) +{ + const char format[] = "GPUDebug: %s%s\033[0m\n"; + + if (ELEM(severity, GL_DEBUG_SEVERITY_LOW, GL_DEBUG_SEVERITY_NOTIFICATION)) { + if (VERBOSE) { + fprintf(stderr, format, "\033[2m", message); + } + } + else { + switch (type) { + case GL_DEBUG_TYPE_ERROR: + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: + fprintf(stderr, format, "\033[31;1mError\033[39m: ", message); + break; + case GL_DEBUG_TYPE_PORTABILITY: + case GL_DEBUG_TYPE_PERFORMANCE: + case GL_DEBUG_TYPE_OTHER: + case GL_DEBUG_TYPE_MARKER: /* KHR has this, ARB does not */ + default: + fprintf(stderr, format, "\033[33;1mWarning\033[39m: ", message); + break; + } + + if (VERBOSE && severity == GL_DEBUG_SEVERITY_HIGH) { + /* Focus on error message. */ + fprintf(stderr, "\033[2m"); + BLI_system_backtrace(stderr); + fprintf(stderr, "\033[0m\n"); + fflush(stderr); + } + } +} + +#undef APIENTRY + +void init_gl_callbacks(void) +{ +#ifdef __APPLE__ + fprintf(stderr, "GPUDebug: OpenGL debug callback is not available on Apple\n"); + return; +#endif /* not Apple */ + + char msg[256] = ""; + const char format[] = "Successfully hooked OpenGL debug callback using %s"; + + if (GLEW_VERSION_4_3 || GLEW_KHR_debug) { + SNPRINTF(msg, format, GLEW_VERSION_4_3 ? "OpenGL 4.3" : "KHR_debug extension"); + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback((GLDEBUGPROC)debug_callback, NULL); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); + glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, + GL_DEBUG_TYPE_MARKER, + 0, + GL_DEBUG_SEVERITY_NOTIFICATION, + -1, + msg); + } + else if (GLEW_ARB_debug_output) { + SNPRINTF(msg, format, "ARB_debug_output"); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallbackARB((GLDEBUGPROCARB)debug_callback, NULL); + glDebugMessageControlARB(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); + glDebugMessageInsertARB(GL_DEBUG_SOURCE_APPLICATION_ARB, + GL_DEBUG_TYPE_OTHER_ARB, + 0, + GL_DEBUG_SEVERITY_LOW_ARB, + -1, + msg); + } + else { + fprintf(stderr, "GPUDebug: Failed to hook OpenGL debug callback\n"); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Error Checking + * + * This is only useful for implementation that does not support the KHR_debug extension OR when the + * implementations do not report any errors even when clearly doing shady things. + * \{ */ + +void check_gl_error(const char *info) +{ + GLenum error = glGetError(); + +#define ERROR_CASE(err) \ + case err: { \ + char msg[256]; \ + SNPRINTF(msg, "%s : %s", #err, info); \ + debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, NULL); \ + break; \ + } + + switch (error) { + ERROR_CASE(GL_INVALID_ENUM) + ERROR_CASE(GL_INVALID_VALUE) + ERROR_CASE(GL_INVALID_OPERATION) + ERROR_CASE(GL_INVALID_FRAMEBUFFER_OPERATION) + ERROR_CASE(GL_OUT_OF_MEMORY) + ERROR_CASE(GL_STACK_UNDERFLOW) + ERROR_CASE(GL_STACK_OVERFLOW) + case GL_NO_ERROR: + break; + default: + char msg[256]; + SNPRINTF(msg, "Unknown GL error: %x : %s", error, info); + debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, NULL); + break; + } +} + +void check_gl_resources(const char *info) +{ + GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get()); + ShaderInterface *interface = ctx->shader->interface; + /* NOTE: This only check binding. To be valid, the bound ubo needs to + * be big enough to feed the data range the shader awaits. */ + uint16_t ubo_needed = interface->enabled_ubo_mask_; + ubo_needed &= ~ctx->bound_ubo_slots; + + if (ubo_needed == 0) { + return; + } + + for (int i = 0; ubo_needed != 0; i++, ubo_needed >>= 1) { + if ((ubo_needed & 1) != 0) { + const ShaderInput *ubo_input = interface->ubo_get(i); + const char *ubo_name = interface->input_name_get(ubo_input); + const char *sh_name = ctx->shader->name_get(); + char msg[256]; + SNPRINTF(msg, "Missing UBO bind at slot %d : %s > %s : %s", i, sh_name, ubo_name, info); + debug_callback(0, GL_DEBUG_TYPE_ERROR, 0, GL_DEBUG_SEVERITY_HIGH, 0, msg, NULL); + } + } +} + +/** \} */ + +} // namespace blender::gpu::debug
\ No newline at end of file diff --git a/source/blender/gpu/intern/gpu_primitive_private.h b/source/blender/gpu/opengl/gl_debug.hh index e91eec18786..dd98505ebc1 100644 --- a/source/blender/gpu/intern/gpu_primitive_private.h +++ b/source/blender/gpu/opengl/gl_debug.hh @@ -12,26 +12,35 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 by Mike Erwin. - * All rights reserved. */ /** \file * \ingroup gpu - * - * GPU geometric primitives */ #pragma once -#ifdef __cplusplus -extern "C" { -#endif +namespace blender { +namespace gpu { +namespace debug { -/* TODO(fclem) move to OGL backend */ -GLenum convert_prim_type_to_gl(GPUPrimType); +/* Enabled on MacOS by default since there is no support for debug callbacks. */ +#if defined(DEBUG) && defined(__APPLE__) +# define GL_CHECK_ERROR(info) debug::check_gl_error(info) +#else +# define GL_CHECK_ERROR(info) +#endif -#ifdef __cplusplus -} +#ifdef DEBUG +# define GL_CHECK_RESOURCES(info) debug::check_gl_resources(info) +#else +# define GL_CHECK_RESOURCES(info) #endif + +void check_gl_error(const char *info); +void check_gl_resources(const char *info); +void init_gl_callbacks(void); + +} // namespace debug +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_drawlist.cc b/source/blender/gpu/opengl/gl_drawlist.cc new file mode 100644 index 00000000000..35fecc859b8 --- /dev/null +++ b/source/blender/gpu/opengl/gl_drawlist.cc @@ -0,0 +1,247 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Implementation of Multi Draw Indirect using OpenGL. + * Fallback if the needed extensions are not supported. + */ + +#include "BLI_assert.h" + +#include "GPU_batch.h" +#include "GPU_extensions.h" + +#include "glew-mx.h" + +#include "gpu_context_private.hh" +#include "gpu_drawlist_private.hh" + +#include "gl_backend.hh" +#include "gl_drawlist.hh" +#include "gl_primitive.hh" + +#include <limits.h> + +#define USE_MULTI_DRAW_INDIRECT 1 + +/* TODO remove. */ +#if GPU_TRACK_INDEX_RANGE +# define BASE_INDEX(el) ((el)->base_index) +# define INDEX_TYPE(el) ((el)->gl_index_type) +#else +# define BASE_INDEX(el) 0 +# define INDEX_TYPE(el) GL_UNSIGNED_INT +#endif + +using namespace blender::gpu; + +typedef struct GLDrawCommand { + GLuint v_count; + GLuint i_count; + GLuint v_first; + GLuint i_first; +} GLDrawCommand; + +typedef struct GLDrawCommandIndexed { + GLuint v_count; + GLuint i_count; + GLuint v_first; + GLuint base_index; + GLuint i_first; +} GLDrawCommandIndexed; + +#define MDI_ENABLED (buffer_size_ != 0) +#define MDI_DISABLED (buffer_size_ == 0) +#define MDI_INDEXED (base_index_ != UINT_MAX) + +GLDrawList::GLDrawList(int length) +{ + BLI_assert(length > 0); + batch_ = NULL; + buffer_id_ = 0; + command_len_ = 0; + command_offset_ = 0; + data_offset_ = 0; + data_size_ = 0; + data_ = NULL; + + if (USE_MULTI_DRAW_INDIRECT && GLEW_ARB_multi_draw_indirect && + GPU_arb_base_instance_is_supported()) { + /* Alloc the biggest possible command list, which is indexed. */ + buffer_size_ = sizeof(GLDrawCommandIndexed) * length; + } + else { + /* Indicates MDI is not supported. */ + buffer_size_ = 0; + } +} + +GLDrawList::~GLDrawList() +{ + /* TODO This ... */ + static_cast<GLBackend *>(GPUBackend::get())->buf_free(buffer_id_); + /* ... should be this. */ + // context_->buf_free(buffer_id_) +} + +void GLDrawList::init(void) +{ + BLI_assert(GPU_context_active_get()); + BLI_assert(MDI_ENABLED); + BLI_assert(data_ == NULL); + batch_ = NULL; + command_len_ = 0; + + if (buffer_id_ == 0) { + /* Allocate on first use. */ + glGenBuffers(1, &buffer_id_); + context_ = static_cast<GLContext *>(GPU_context_active_get()); + } + + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_); + /* If buffer is full, orphan buffer data and start fresh. */ + // if (command_offset_ >= data_size_) { + glBufferData(GL_DRAW_INDIRECT_BUFFER, buffer_size_, NULL, GL_DYNAMIC_DRAW); + data_offset_ = 0; + // } + /* Map the remaining range. */ + GLbitfield flag = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; + data_size_ = buffer_size_ - data_offset_; + data_ = (GLbyte *)glMapBufferRange(GL_DRAW_INDIRECT_BUFFER, data_offset_, data_size_, flag); + command_offset_ = 0; +} + +void GLDrawList::append(GPUBatch *batch, int i_first, int i_count) +{ + /* Fallback when MultiDrawIndirect is not supported/enabled. */ + if (MDI_DISABLED) { + GPU_batch_draw_advanced(batch, 0, 0, i_first, i_count); + return; + } + + if (data_ == NULL) { + this->init(); + } + + if (batch != batch_) { + // BLI_assert(batch->flag | GPU_BATCH_INIT); + this->submit(); + batch_ = batch; + /* Cached for faster access. */ + base_index_ = batch->elem ? BASE_INDEX(batch->elem) : UINT_MAX; + v_first_ = batch->elem ? batch->elem->index_start : 0; + v_count_ = batch->elem ? batch->elem->index_len : batch->verts[0]->vertex_len; + } + + if (v_count_ == 0) { + /* Nothing to draw. */ + return; + } + + if (MDI_INDEXED) { + GLDrawCommandIndexed *cmd = reinterpret_cast<GLDrawCommandIndexed *>(data_ + command_offset_); + cmd->v_first = v_first_; + cmd->v_count = v_count_; + cmd->i_count = i_count; + cmd->base_index = base_index_; + cmd->i_first = i_first; + command_offset_ += sizeof(GLDrawCommandIndexed); + } + else { + GLDrawCommand *cmd = reinterpret_cast<GLDrawCommand *>(data_ + command_offset_); + cmd->v_first = v_first_; + cmd->v_count = v_count_; + cmd->i_count = i_count; + cmd->i_first = i_first; + command_offset_ += sizeof(GLDrawCommand); + } + + command_len_++; + + if (command_offset_ >= data_size_) { + this->submit(); + } +} + +void GLDrawList::submit(void) +{ + if (command_len_ == 0) { + return; + } + /* Something's wrong if we get here without MDI support. */ + BLI_assert(MDI_ENABLED); + BLI_assert(data_); + BLI_assert(GPU_context_active_get()->shader != NULL); + + GLBatch *batch = static_cast<GLBatch *>(batch_); + + /* Only do multi-draw indirect if doing more than 2 drawcall. This avoids the overhead of + * buffer mapping if scene is not very instance friendly. BUT we also need to take into + * account the + * case where only a few instances are needed to finish filling a call buffer. */ + const bool is_finishing_a_buffer = (command_offset_ >= data_size_); + if (command_len_ > 2 || is_finishing_a_buffer) { + GLenum prim = to_gl(batch_->prim_type); + void *offset = (void *)data_offset_; + + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_); + glFlushMappedBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, command_offset_); + glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER); + data_ = NULL; /* Unmapped */ + data_offset_ += command_offset_; + + batch->bind(0); + + if (MDI_INDEXED) { + glMultiDrawElementsIndirect(prim, INDEX_TYPE(batch_->elem), offset, command_len_, 0); + } + else { + glMultiDrawArraysIndirect(prim, offset, command_len_, 0); + } + } + else { + /* Fallback do simple drawcalls, and don't unmap the buffer. */ + if (MDI_INDEXED) { + GLDrawCommandIndexed *cmd = (GLDrawCommandIndexed *)data_; + for (int i = 0; i < command_len_; i++, cmd++) { + /* Index start was already added. Avoid counting it twice. */ + cmd->v_first -= batch->elem->index_start; + batch->draw(cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count); + } + /* Reuse the same data. */ + command_offset_ -= command_len_ * sizeof(GLDrawCommandIndexed); + } + else { + GLDrawCommand *cmd = (GLDrawCommand *)data_; + for (int i = 0; i < command_len_; i++, cmd++) { + batch->draw(cmd->v_first, cmd->v_count, cmd->i_first, cmd->i_count); + } + /* Reuse the same data. */ + command_offset_ -= command_len_ * sizeof(GLDrawCommand); + } + } + /* Do not submit this buffer again. */ + command_len_ = 0; + /* Avoid keeping reference to the batch. */ + batch_ = NULL; +} + +/** \} */ diff --git a/source/blender/gpu/opengl/gl_drawlist.hh b/source/blender/gpu/opengl/gl_drawlist.hh new file mode 100644 index 00000000000..b690b8f8a98 --- /dev/null +++ b/source/blender/gpu/opengl/gl_drawlist.hh @@ -0,0 +1,86 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Implementation of Multi Draw Indirect using OpenGL. + * Fallback if the needed extensions are not supported. + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "BLI_sys_types.h" + +#include "GPU_batch.h" +#include "GPU_glew.h" + +#include "gpu_drawlist_private.hh" + +#include "gl_context.hh" + +namespace blender { +namespace gpu { + +/** + * Implementation of Multi Draw Indirect using OpenGL. + **/ +class GLDrawList : public DrawList { + public: + GLDrawList(int length); + ~GLDrawList(); + + void append(GPUBatch *batch, int i_first, int i_count) override; + void submit(void) override; + + private: + void init(void); + + /** Batch for which we are recording commands for. */ + GPUBatch *batch_; + /** Mapped memory bounds. */ + GLbyte *data_; + /** Length of the mapped buffer (in byte). */ + GLsizeiptr data_size_; + /** Current offset inside the mapped buffer (in byte). */ + GLintptr command_offset_; + /** Current number of command recorded inside the mapped buffer. */ + uint command_len_; + /** Is UINT_MAX if not drawing indexed geom. Also Avoid dereferencing batch. */ + GLuint base_index_; + /** Also Avoid dereferencing batch. */ + GLuint v_first_, v_count_; + + /** GL Indirect Buffer id. 0 means MultiDrawIndirect is not supported/enabled. */ + GLuint buffer_id_; + /** Length of whole the buffer (in byte). */ + GLsizeiptr buffer_size_; + /** Offset of data_ inside the whole buffer (in byte). */ + GLintptr data_offset_; + + /** To free the buffer_id_. */ + GLContext *context_; + + MEM_CXX_CLASS_ALLOC_FUNCS("GLDrawList"); +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_framebuffer.cc b/source/blender/gpu/opengl/gl_framebuffer.cc new file mode 100644 index 00000000000..8d48c9f8de3 --- /dev/null +++ b/source/blender/gpu/opengl/gl_framebuffer.cc @@ -0,0 +1,460 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#include "BKE_global.h" + +#include "GPU_extensions.h" + +#include "gl_backend.hh" +#include "gl_framebuffer.hh" +#include "gl_state.hh" +#include "gl_texture.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ + +GLFrameBuffer::GLFrameBuffer(const char *name) : FrameBuffer(name) +{ + /* Just-In-Time init. See GLFrameBuffer::init(). */ + immutable_ = false; + fbo_id_ = 0; +} + +GLFrameBuffer::GLFrameBuffer( + const char *name, GLContext *ctx, GLenum target, GLuint fbo, int w, int h) + : FrameBuffer(name) +{ + context_ = ctx; + state_manager_ = static_cast<GLStateManager *>(ctx->state_manager); + immutable_ = true; + fbo_id_ = fbo; + gl_attachments_[0] = target; + /* Never update an internal framebuffer. */ + dirty_attachments_ = false; + width_ = w; + height_ = h; + srgb_ = false; + + viewport_[0] = scissor_[0] = 0; + viewport_[1] = scissor_[1] = 0; + viewport_[2] = scissor_[2] = w; + viewport_[3] = scissor_[3] = h; + +#ifndef __APPLE__ + if (fbo_id_ && (G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { + char sh_name[32]; + SNPRINTF(sh_name, "FrameBuffer-%s", name); + glObjectLabel(GL_FRAMEBUFFER, fbo_id_, -1, sh_name); + } +#endif +} + +GLFrameBuffer::~GLFrameBuffer() +{ + if (context_ == NULL) { + return; + } + + if (context_ == GPU_context_active_get()) { + /* Context might be partially freed. This happens when destroying the window framebuffers. */ + glDeleteFramebuffers(1, &fbo_id_); + } + else { + context_->fbo_free(fbo_id_); + } + /* Restore default framebuffer if this framebuffer was bound. */ + if (context_->active_fb == this && context_->back_left != this) { + /* If this assert triggers it means the framebuffer is being freed while in use by another + * context which, by the way, is TOTALLY UNSAFE!!! */ + BLI_assert(context_ == GPU_context_active_get()); + GPU_framebuffer_restore(); + } +} + +void GLFrameBuffer::init(void) +{ + context_ = static_cast<GLContext *>(GPU_context_active_get()); + state_manager_ = static_cast<GLStateManager *>(context_->state_manager); + glGenFramebuffers(1, &fbo_id_); + +#ifndef __APPLE__ + if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { + char sh_name[64]; + SNPRINTF(sh_name, "FrameBuffer-%s", name_); + /* Binding before setting the label is needed on some drivers. */ + glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_); + glObjectLabel(GL_FRAMEBUFFER, fbo_id_, -1, sh_name); + } +#endif +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Config + * \{ */ + +/* This is a rather slow operation. Don't check in normal cases. */ +bool GLFrameBuffer::check(char err_out[256]) +{ + this->bind(true); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + +#define FORMAT_STATUS(X) \ + case X: { \ + err = #X; \ + break; \ + } + + const char *err; + switch (status) { + FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT); + FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT); + FORMAT_STATUS(GL_FRAMEBUFFER_UNSUPPORTED); + FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER); + FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER); + FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE); + FORMAT_STATUS(GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS); + FORMAT_STATUS(GL_FRAMEBUFFER_UNDEFINED); + case GL_FRAMEBUFFER_COMPLETE: + return true; + default: + err = "unknown"; + break; + } + +#undef FORMAT_STATUS + + const char *format = "GPUFrameBuffer: framebuffer status %s\n"; + + if (err_out) { + BLI_snprintf(err_out, 256, format, err); + } + else { + fprintf(stderr, format, err); + } + + return false; +} + +void GLFrameBuffer::update_attachments(void) +{ + /* Default framebuffers cannot have attachements. */ + BLI_assert(immutable_ == false); + + /* First color texture OR the depth texture if no color is attached. + * Used to determine framebuffer colorspace and dimensions. */ + GPUAttachmentType first_attachment = GPU_FB_MAX_ATTACHEMENT; + /* NOTE: Inverse iteration to get the first color texture. */ + for (GPUAttachmentType type = GPU_FB_MAX_ATTACHEMENT - 1; type >= 0; --type) { + GPUAttachment &attach = attachments_[type]; + GLenum gl_attachment = to_gl(type); + + if (type >= GPU_FB_COLOR_ATTACHMENT0) { + gl_attachments_[type - GPU_FB_COLOR_ATTACHMENT0] = (attach.tex) ? gl_attachment : GL_NONE; + first_attachment = (attach.tex) ? type : first_attachment; + } + else if (first_attachment == GPU_FB_MAX_ATTACHEMENT) { + /* Only use depth texture to get infos if there is no color attachment. */ + first_attachment = (attach.tex) ? type : first_attachment; + } + + if (attach.tex == NULL) { + glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, 0, 0); + continue; + } + GLuint gl_tex = GPU_texture_opengl_bindcode(attach.tex); + if (attach.layer > -1 && GPU_texture_cube(attach.tex) && !GPU_texture_array(attach.tex)) { + /* Could be avoided if ARB_direct_state_access is required. In this case + * glFramebufferTextureLayer would bind the correct face. */ + GLenum gl_target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + attach.layer; + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attachment, gl_target, gl_tex, attach.mip); + } + else if (attach.layer > -1) { + glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_attachment, gl_tex, attach.mip, attach.layer); + } + else { + /* The whole texture level is attached. The framebuffer is potentially layered. */ + glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, gl_tex, attach.mip); + } + /* We found one depth buffer type. Stop here, otherwise we would + * override it by setting GPU_FB_DEPTH_ATTACHMENT */ + if (type == GPU_FB_DEPTH_STENCIL_ATTACHMENT) { + break; + } + } + + if (GPU_unused_fb_slot_workaround()) { + /* Fill normally un-occupied slots to avoid rendering artifacts on some hardware. */ + GLuint gl_tex = 0; + /* NOTE: Inverse iteration to get the first color texture. */ + for (int i = ARRAY_SIZE(gl_attachments_) - 1; i >= 0; --i) { + GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0 + i; + GPUAttachment &attach = attachments_[type]; + if (attach.tex != NULL) { + gl_tex = GPU_texture_opengl_bindcode(attach.tex); + } + else if (gl_tex != 0) { + GLenum gl_attachment = to_gl(type); + gl_attachments_[i] = gl_attachment; + glFramebufferTexture(GL_FRAMEBUFFER, gl_attachment, gl_tex, 0); + } + } + } + + if (first_attachment != GPU_FB_MAX_ATTACHEMENT) { + GPUAttachment &attach = attachments_[first_attachment]; + int size[3]; + GPU_texture_get_mipmap_size(attach.tex, attach.mip, size); + this->size_set(size[0], size[1]); + srgb_ = (GPU_texture_format(attach.tex) == GPU_SRGB8_A8); + } + + dirty_attachments_ = false; + + glDrawBuffers(ARRAY_SIZE(gl_attachments_), gl_attachments_); + + if (G.debug & G_DEBUG_GPU) { + BLI_assert(this->check(NULL)); + } +} + +void GLFrameBuffer::apply_state(void) +{ + if (dirty_state_ == false) { + return; + } + + glViewport(UNPACK4(viewport_)); + glScissor(UNPACK4(scissor_)); + + if (scissor_test_) { + glEnable(GL_SCISSOR_TEST); + } + else { + glDisable(GL_SCISSOR_TEST); + } + + dirty_state_ = false; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Binding + * \{ */ + +void GLFrameBuffer::bind(bool enabled_srgb) +{ + if (!immutable_ && fbo_id_ == 0) { + this->init(); + } + + if (context_ != GPU_context_active_get()) { + BLI_assert(!"Trying to use the same framebuffer in multiple context"); + return; + } + + if (context_->active_fb != this) { + glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_); + /* Internal framebuffers have only one color output and needs to be set everytime. */ + if (immutable_ && fbo_id_ == 0) { + glDrawBuffer(gl_attachments_[0]); + } + } + + if (dirty_attachments_) { + this->update_attachments(); + this->viewport_reset(); + this->scissor_reset(); + } + + if (context_->active_fb != this) { + context_->active_fb = this; + state_manager_->active_fb = this; + dirty_state_ = true; + + if (enabled_srgb) { + glEnable(GL_FRAMEBUFFER_SRGB); + } + else { + glDisable(GL_FRAMEBUFFER_SRGB); + } + + GPU_shader_set_framebuffer_srgb_target(enabled_srgb && srgb_); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Operations. + * \{ */ + +void GLFrameBuffer::clear(eGPUFrameBufferBits buffers, + const float clear_col[4], + float clear_depth, + uint clear_stencil) +{ + BLI_assert(GPU_context_active_get() == context_); + BLI_assert(context_->active_fb == this); + + /* Save and restore the state. */ + eGPUWriteMask write_mask = GPU_write_mask_get(); + uint stencil_mask = GPU_stencil_mask_get(); + eGPUStencilTest stencil_test = GPU_stencil_test_get(); + + if (buffers & GPU_COLOR_BIT) { + GPU_color_mask(true, true, true, true); + glClearColor(clear_col[0], clear_col[1], clear_col[2], clear_col[3]); + } + if (buffers & GPU_DEPTH_BIT) { + GPU_depth_mask(true); + glClearDepth(clear_depth); + } + if (buffers & GPU_STENCIL_BIT) { + GPU_stencil_write_mask_set(0xFFu); + GPU_stencil_test(GPU_STENCIL_ALWAYS); + glClearStencil(clear_stencil); + } + + context_->state_manager->apply_state(); + + GLbitfield mask = to_gl(buffers); + glClear(mask); + + if (buffers & (GPU_COLOR_BIT | GPU_DEPTH_BIT)) { + GPU_write_mask(write_mask); + } + if (buffers & GPU_STENCIL_BIT) { + GPU_stencil_write_mask_set(stencil_mask); + GPU_stencil_test(stencil_test); + } +} + +void GLFrameBuffer::clear_multi(const float (*clear_cols)[4]) +{ + BLI_assert(GPU_context_active_get() == context_); + BLI_assert(context_->active_fb == this); + + /* Save and restore the state. */ + eGPUWriteMask write_mask = GPU_write_mask_get(); + GPU_color_mask(true, true, true, true); + + context_->state_manager->apply_state(); + + /* WATCH: This can easilly access clear_cols out of bounds it clear_cols is not big enough for + * all attachments. + * TODO(fclem) fix this insecurity? */ + int type = GPU_FB_COLOR_ATTACHMENT0; + for (int i = 0; type < GPU_FB_MAX_ATTACHEMENT; i++, type++) { + if (attachments_[type].tex != NULL) { + glClearBufferfv(GL_COLOR, i, clear_cols[i]); + } + } + + GPU_write_mask(write_mask); +} + +void GLFrameBuffer::read(eGPUFrameBufferBits plane, + eGPUDataFormat data_format, + const int area[4], + int channel_len, + int slot, + void *r_data) +{ + GLenum format, type, mode; + mode = gl_attachments_[slot]; + type = to_gl(data_format); + + switch (plane) { + case GPU_DEPTH_BIT: + format = GL_DEPTH_COMPONENT; + break; + case GPU_COLOR_BIT: + format = channel_len_to_gl(channel_len); + /* TODO: needed for selection buffers to work properly, this should be handled better. */ + if (format == GL_RED && type == GL_UNSIGNED_INT) { + format = GL_RED_INTEGER; + } + break; + case GPU_STENCIL_BIT: + fprintf(stderr, "GPUFramebuffer: Error: Trying to read stencil bit. Unsupported."); + return; + default: + fprintf(stderr, "GPUFramebuffer: Error: Trying to read more than one framebuffer plane."); + return; + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_id_); + glReadBuffer(mode); + glReadPixels(UNPACK4(area), format, type, r_data); +} + +/* Copy src at the give offset inside dst. */ +void GLFrameBuffer::blit_to( + eGPUFrameBufferBits planes, int src_slot, FrameBuffer *dst_, int dst_slot, int x, int y) +{ + GLFrameBuffer *src = this; + GLFrameBuffer *dst = static_cast<GLFrameBuffer *>(dst_); + + /* Framebuffers must be up to date. This simplify this function. */ + if (src->dirty_attachments_) { + src->bind(true); + } + if (dst->dirty_attachments_) { + dst->bind(true); + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, src->fbo_id_); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->fbo_id_); + + if (planes & GPU_COLOR_BIT) { + BLI_assert(src->immutable_ == false || src_slot == 0); + BLI_assert(dst->immutable_ == false || dst_slot == 0); + BLI_assert(src->gl_attachments_[src_slot] != GL_NONE); + BLI_assert(dst->gl_attachments_[dst_slot] != GL_NONE); + glReadBuffer(src->gl_attachments_[src_slot]); + glDrawBuffer(dst->gl_attachments_[dst_slot]); + } + + context_->state_manager->apply_state(); + + int w = src->width_; + int h = src->height_; + GLbitfield mask = to_gl(planes); + glBlitFramebuffer(0, 0, w, h, x, y, x + w, y + h, mask, GL_NEAREST); + + if (!dst->immutable_) { + /* Restore the draw buffers. */ + glDrawBuffers(ARRAY_SIZE(dst->gl_attachments_), dst->gl_attachments_); + } +} + +/** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_framebuffer.hh b/source/blender/gpu/opengl/gl_framebuffer.hh new file mode 100644 index 00000000000..8d386116159 --- /dev/null +++ b/source/blender/gpu/opengl/gl_framebuffer.hh @@ -0,0 +1,148 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Encapsulation of Framebuffer states (attached textures, viewport, scissors). + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "glew-mx.h" + +#include "gpu_framebuffer_private.hh" + +namespace blender::gpu { + +class GLStateManager; + +/** + * Implementation of FrameBuffer object using OpenGL. + **/ +class GLFrameBuffer : public FrameBuffer { + private: + /** OpenGL handle. */ + GLuint fbo_id_ = 0; + /** Context the handle is from. Framebuffers are not shared accros contexts. */ + GLContext *context_ = NULL; + /** State Manager of the same contexts. */ + GLStateManager *state_manager_ = NULL; + /** Copy of the GL state. Contains ONLY color attachments enums for slot binding. */ + GLenum gl_attachments_[GPU_FB_MAX_COLOR_ATTACHMENT]; + /** Internal framebuffers are immutable. */ + bool immutable_; + /** True is the framebuffer has it's first color target using the GPU_SRGB8_A8 format. */ + bool srgb_; + + public: + /** + * Create a conventional framebuffer to attach texture to. + **/ + GLFrameBuffer(const char *name); + + /** + * Special Framebuffer encapsulating internal window framebuffer. + * (i.e.: GL_FRONT_LEFT, GL_BACK_RIGHT, ...) + * @param ctx context the handle is from. + * @param target the internal GL name (i.e: GL_BACK_LEFT). + * @param fbo the (optional) already created object for some implementation. Default is 0. + * @param w buffer width. + * @param h buffer height. + **/ + GLFrameBuffer(const char *name, GLContext *ctx, GLenum target, GLuint fbo, int w, int h); + + ~GLFrameBuffer(); + + void bind(bool enabled_srgb) override; + + bool check(char err_out[256]) override; + + void clear(eGPUFrameBufferBits buffers, + const float clear_col[4], + float clear_depth, + uint clear_stencil) override; + void clear_multi(const float (*clear_cols)[4]) override; + + void read(eGPUFrameBufferBits planes, + eGPUDataFormat format, + const int area[4], + int channel_len, + int slot, + void *r_data) override; + + void blit_to(eGPUFrameBufferBits planes, + int src_slot, + FrameBuffer *dst, + int dst_slot, + int dst_offset_x, + int dst_offset_y) override; + + void apply_state(void); + + private: + void init(void); + void update_attachments(void); + void update_drawbuffers(void); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLFrameBuffer"); +}; + +/* -------------------------------------------------------------------- */ +/** \name Enums Conversion + * \{ */ + +static inline GLenum to_gl(const GPUAttachmentType type) +{ +#define ATTACHMENT(X) \ + case GPU_FB_##X: { \ + return GL_##X; \ + } \ + ((void)0) + + switch (type) { + ATTACHMENT(DEPTH_ATTACHMENT); + ATTACHMENT(DEPTH_STENCIL_ATTACHMENT); + ATTACHMENT(COLOR_ATTACHMENT0); + ATTACHMENT(COLOR_ATTACHMENT1); + ATTACHMENT(COLOR_ATTACHMENT2); + ATTACHMENT(COLOR_ATTACHMENT3); + ATTACHMENT(COLOR_ATTACHMENT4); + ATTACHMENT(COLOR_ATTACHMENT5); + default: + BLI_assert(0); + return GL_COLOR_ATTACHMENT0; + } +#undef ATTACHMENT +} + +static inline GLbitfield to_gl(const eGPUFrameBufferBits bits) +{ + GLbitfield mask = 0; + mask |= (bits & GPU_DEPTH_BIT) ? GL_DEPTH_BUFFER_BIT : 0; + mask |= (bits & GPU_STENCIL_BIT) ? GL_STENCIL_BUFFER_BIT : 0; + mask |= (bits & GPU_COLOR_BIT) ? GL_COLOR_BUFFER_BIT : 0; + return mask; +} + +/** \} */ + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_immediate.cc b/source/blender/gpu/opengl/gl_immediate.cc new file mode 100644 index 00000000000..7f12f41a598 --- /dev/null +++ b/source/blender/gpu/opengl/gl_immediate.cc @@ -0,0 +1,194 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Mimics old style opengl immediate mode drawing. + */ + +#include "BKE_global.h" + +#include "gpu_context_private.hh" +#include "gpu_shader_private.hh" +#include "gpu_vertex_format_private.h" + +#include "gl_context.hh" +#include "gl_debug.hh" +#include "gl_primitive.hh" +#include "gl_vertex_array.hh" + +#include "gl_immediate.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ + +GLImmediate::GLImmediate() +{ + glGenVertexArrays(1, &vao_id_); + glBindVertexArray(vao_id_); /* Necessary for glObjectLabel. */ + + buffer.buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE; + glGenBuffers(1, &buffer.vbo_id); + glBindBuffer(GL_ARRAY_BUFFER, buffer.vbo_id); + glBufferData(GL_ARRAY_BUFFER, buffer.buffer_size, NULL, GL_DYNAMIC_DRAW); + + buffer_strict.buffer_size = DEFAULT_INTERNAL_BUFFER_SIZE; + glGenBuffers(1, &buffer_strict.vbo_id); + glBindBuffer(GL_ARRAY_BUFFER, buffer_strict.vbo_id); + glBufferData(GL_ARRAY_BUFFER, buffer_strict.buffer_size, NULL, GL_DYNAMIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + +#ifndef __APPLE__ + if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { + glObjectLabel(GL_VERTEX_ARRAY, vao_id_, -1, "VAO-Immediate"); + glObjectLabel(GL_BUFFER, buffer.vbo_id, -1, "VBO-ImmediateBuffer"); + glObjectLabel(GL_BUFFER, buffer_strict.vbo_id, -1, "VBO-ImmediateBufferStrict"); + } +#endif +} + +GLImmediate::~GLImmediate() +{ + glDeleteVertexArrays(1, &vao_id_); + + glDeleteBuffers(1, &buffer.vbo_id); + glDeleteBuffers(1, &buffer_strict.vbo_id); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Buffer management + * \{ */ + +uchar *GLImmediate::begin() +{ + /* How many bytes do we need for this draw call? */ + const size_t bytes_needed = vertex_buffer_size(&vertex_format, vertex_len); + /* Does the current buffer have enough room? */ + const size_t available_bytes = buffer_size() - buffer_offset(); + + GL_CHECK_RESOURCES("Immediate"); + GL_CHECK_ERROR("Immediate Pre-Begin"); + + glBindBuffer(GL_ARRAY_BUFFER, vbo_id()); + + bool recreate_buffer = false; + if (bytes_needed > buffer_size()) { + /* expand the internal buffer */ + buffer_size() = bytes_needed; + recreate_buffer = true; + } + else if (bytes_needed < DEFAULT_INTERNAL_BUFFER_SIZE && + buffer_size() > DEFAULT_INTERNAL_BUFFER_SIZE) { + /* shrink the internal buffer */ + buffer_size() = DEFAULT_INTERNAL_BUFFER_SIZE; + recreate_buffer = true; + } + + /* ensure vertex data is aligned */ + /* Might waste a little space, but it's safe. */ + const uint pre_padding = padding(buffer_offset(), vertex_format.stride); + + if (!recreate_buffer && ((bytes_needed + pre_padding) <= available_bytes)) { + buffer_offset() += pre_padding; + } + else { + /* orphan this buffer & start with a fresh one */ + glBufferData(GL_ARRAY_BUFFER, buffer_size(), NULL, GL_DYNAMIC_DRAW); + buffer_offset() = 0; + } + +#ifndef NDEBUG + { + GLint bufsize; + glGetBufferParameteriv(GL_ARRAY_BUFFER, GL_BUFFER_SIZE, &bufsize); + BLI_assert(buffer_offset() + bytes_needed <= bufsize); + } +#endif + + GLbitfield access = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT; + if (!strict_vertex_len) { + access |= GL_MAP_FLUSH_EXPLICIT_BIT; + } + void *data = glMapBufferRange(GL_ARRAY_BUFFER, buffer_offset(), bytes_needed, access); + BLI_assert(data != NULL); + GL_CHECK_ERROR("Immediate Post-Begin"); + + bytes_mapped_ = bytes_needed; + return (uchar *)data; +} + +void GLImmediate::end(void) +{ + BLI_assert(prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ + + uint buffer_bytes_used = bytes_mapped_; + if (!strict_vertex_len) { + if (vertex_idx != vertex_len) { + vertex_len = vertex_idx; + buffer_bytes_used = vertex_buffer_size(&vertex_format, vertex_len); + /* unused buffer bytes are available to the next immBegin */ + } + /* tell OpenGL what range was modified so it doesn't copy the whole mapped range */ + glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, buffer_bytes_used); + } + glUnmapBuffer(GL_ARRAY_BUFFER); + + GL_CHECK_ERROR("Immediate Post-Unmap"); + + if (vertex_len > 0) { + GPU_context_active_get()->state_manager->apply_state(); + + /* We convert the offset in vertex offset from the buffer's start. + * This works because we added some padding to align the first vertex vertex. */ + uint v_first = buffer_offset() / vertex_format.stride; + GLVertArray::update_bindings( + vao_id_, v_first, &vertex_format, reinterpret_cast<Shader *>(shader)->interface); + + /* Update matrices. */ + GPU_shader_bind(shader); + +#ifdef __APPLE__ + glDisable(GL_PRIMITIVE_RESTART); +#endif + glDrawArrays(to_gl(prim_type), 0, vertex_len); +#ifdef __APPLE__ + glEnable(GL_PRIMITIVE_RESTART); +#endif + /* These lines are causing crash on startup on some old GPU + drivers. + * They are not required so just comment them. (T55722) */ + // glBindBuffer(GL_ARRAY_BUFFER, 0); + // glBindVertexArray(0); + + GL_CHECK_ERROR("Immediate Post-drawing"); + } + + buffer_offset() += buffer_bytes_used; +} + +/** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_immediate.hh b/source/blender/gpu/opengl/gl_immediate.hh new file mode 100644 index 00000000000..2b9b90d692b --- /dev/null +++ b/source/blender/gpu/opengl/gl_immediate.hh @@ -0,0 +1,81 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Mimics old style opengl immediate mode drawing. + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "glew-mx.h" + +#include "gpu_immediate_private.hh" + +namespace blender::gpu { + +/* size of internal buffer */ +#define DEFAULT_INTERNAL_BUFFER_SIZE (4 * 1024 * 1024) + +class GLImmediate : public Immediate { + private: + /* Use two buffers for strict and unstrict vertex count to + * avoid some huge driver slowdown (see T70922). + * Use accessor functions to get / modify. */ + struct { + /** Opengl Handle for this buffer. */ + GLuint vbo_id = 0; + /** Offset of the mapped data in data. */ + size_t buffer_offset = 0; + /** Size of the whole buffer in bytes. */ + size_t buffer_size = 0; + } buffer, buffer_strict; + /** Size in bytes of the mapped region. */ + size_t bytes_mapped_ = 0; + /** Vertex array for this immediate mode instance. */ + GLuint vao_id_ = 0; + + public: + GLImmediate(); + ~GLImmediate(); + + uchar *begin(void) override; + void end(void) override; + + private: + GLuint &vbo_id(void) + { + return strict_vertex_len ? buffer_strict.vbo_id : buffer.vbo_id; + }; + + size_t &buffer_offset(void) + { + return strict_vertex_len ? buffer_strict.buffer_offset : buffer.buffer_offset; + }; + + size_t &buffer_size(void) + { + return strict_vertex_len ? buffer_strict.buffer_size : buffer.buffer_size; + }; +}; + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_primitive.hh b/source/blender/gpu/opengl/gl_primitive.hh new file mode 100644 index 00000000000..7cd0654bc2c --- /dev/null +++ b/source/blender/gpu/opengl/gl_primitive.hh @@ -0,0 +1,65 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Encapsulation of Framebuffer states (attached textures, viewport, scissors). + */ + +#pragma once + +#include "BLI_assert.h" + +#include "GPU_primitive.h" + +#include "glew-mx.h" + +namespace blender::gpu { + +static inline GLenum to_gl(GPUPrimType prim_type) +{ + BLI_assert(prim_type != GPU_PRIM_NONE); + switch (prim_type) { + default: + case GPU_PRIM_POINTS: + return GL_POINTS; + case GPU_PRIM_LINES: + return GL_LINES; + case GPU_PRIM_LINE_STRIP: + return GL_LINE_STRIP; + case GPU_PRIM_LINE_LOOP: + return GL_LINE_LOOP; + case GPU_PRIM_TRIS: + return GL_TRIANGLES; + case GPU_PRIM_TRI_STRIP: + return GL_TRIANGLE_STRIP; + case GPU_PRIM_TRI_FAN: + return GL_TRIANGLE_FAN; + + case GPU_PRIM_LINES_ADJ: + return GL_LINES_ADJACENCY; + case GPU_PRIM_LINE_STRIP_ADJ: + return GL_LINE_STRIP_ADJACENCY; + case GPU_PRIM_TRIS_ADJ: + return GL_TRIANGLES_ADJACENCY; + }; +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc new file mode 100644 index 00000000000..17058a6a5a7 --- /dev/null +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -0,0 +1,456 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#include "BKE_global.h" + +#include "BLI_string.h" + +#include "GPU_extensions.h" +#include "GPU_platform.h" + +#include "gl_shader.hh" +#include "gl_shader_interface.hh" + +using namespace blender; +using namespace blender::gpu; + +/* -------------------------------------------------------------------- */ +/** \name Creation / Destruction + * \{ */ + +GLShader::GLShader(const char *name) : Shader(name) +{ +#if 0 /* Would be nice to have, but for now the Deferred compilation \ + * does not have a GPUContext. */ + BLI_assert(GPU_context_active_get() != NULL); +#endif + shader_program_ = glCreateProgram(); + +#ifndef __APPLE__ + if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { + char sh_name[64]; + SNPRINTF(sh_name, "ShaderProgram-%s", name); + glObjectLabel(GL_PROGRAM, shader_program_, -1, sh_name); + } +#endif +} + +GLShader::~GLShader(void) +{ +#if 0 /* Would be nice to have, but for now the Deferred compilation \ + * does not have a GPUContext. */ + BLI_assert(GPU_context_active_get() != NULL); +#endif + /* Invalid handles are silently ignored. */ + glDeleteShader(vert_shader_); + glDeleteShader(geom_shader_); + glDeleteShader(frag_shader_); + glDeleteProgram(shader_program_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Shader stage creation + * \{ */ + +char *GLShader::glsl_patch_get(void) +{ + /** Used for shader patching. Init once. */ + static char patch[512] = "\0"; + if (patch[0] != '\0') { + return patch; + } + + size_t slen = 0; + /* Version need to go first. */ + STR_CONCAT(patch, slen, "#version 330\n"); + + /* Enable extensions for features that are not part of our base GLSL version + * don't use an extension for something already available! */ + if (GLEW_ARB_texture_gather) { + /* There is a bug on older Nvidia GPU where GL_ARB_texture_gather + * is reported to be supported but yield a compile error (see T55802). */ + if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) || GLEW_VERSION_4_0) { + STR_CONCAT(patch, slen, "#extension GL_ARB_texture_gather: enable\n"); + + /* Some drivers don't agree on GLEW_ARB_texture_gather and the actual support in the + * shader so double check the preprocessor define (see T56544). */ + if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) && !GLEW_VERSION_4_0) { + STR_CONCAT(patch, slen, "#ifdef GL_ARB_texture_gather\n"); + STR_CONCAT(patch, slen, "# define GPU_ARB_texture_gather\n"); + STR_CONCAT(patch, slen, "#endif\n"); + } + else { + STR_CONCAT(patch, slen, "#define GPU_ARB_texture_gather\n"); + } + } + } + if (GLEW_ARB_shader_draw_parameters) { + STR_CONCAT(patch, slen, "#extension GL_ARB_shader_draw_parameters : enable\n"); + STR_CONCAT(patch, slen, "#define GPU_ARB_shader_draw_parameters\n"); + } + if (GPU_arb_texture_cube_map_array_is_supported()) { + STR_CONCAT(patch, slen, "#extension GL_ARB_texture_cube_map_array : enable\n"); + STR_CONCAT(patch, slen, "#define GPU_ARB_texture_cube_map_array\n"); + } + + /* Derivative sign can change depending on implementation. */ + float derivatives[2]; + GPU_get_dfdy_factors(derivatives); + STR_CONCATF(patch, slen, "#define DFDX_SIGN %1.1f\n", derivatives[0]); + STR_CONCATF(patch, slen, "#define DFDY_SIGN %1.1f\n", derivatives[1]); + + BLI_assert(slen < sizeof(patch)); + return patch; +} + +/* Create, compile and attach the shader stage to the shader program. */ +GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources) +{ + GLuint shader = glCreateShader(gl_stage); + if (shader == 0) { + fprintf(stderr, "GLShader: Error: Could not create shader object."); + return 0; + } + + /* Patch the shader code using the first source slot. */ + sources[0] = glsl_patch_get(); + + glShaderSource(shader, sources.size(), sources.data(), NULL); + glCompileShader(shader); + + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (!status || (G.debug & G_DEBUG_GPU)) { + char log[5000] = ""; + glGetShaderInfoLog(shader, sizeof(log), NULL, log); + if (log[0] != '\0') { + switch (gl_stage) { + case GL_VERTEX_SHADER: + this->print_errors(sources, log, "VertShader"); + break; + case GL_GEOMETRY_SHADER: + this->print_errors(sources, log, "GeomShader"); + break; + case GL_FRAGMENT_SHADER: + this->print_errors(sources, log, "FragShader"); + break; + } + } + } + if (!status) { + glDeleteShader(shader); + compilation_failed_ = true; + return 0; + } + +#ifndef __APPLE__ + if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { + char sh_name[64]; + switch (gl_stage) { + case GL_VERTEX_SHADER: + BLI_snprintf(sh_name, sizeof(sh_name), "VertShader-%s", name); + break; + case GL_GEOMETRY_SHADER: + BLI_snprintf(sh_name, sizeof(sh_name), "GeomShader-%s", name); + break; + case GL_FRAGMENT_SHADER: + BLI_snprintf(sh_name, sizeof(sh_name), "FragShader-%s", name); + break; + } + glObjectLabel(GL_SHADER, shader, -1, sh_name); + } +#endif + + glAttachShader(shader_program_, shader); + return shader; +} + +void GLShader::vertex_shader_from_glsl(MutableSpan<const char *> sources) +{ + vert_shader_ = this->create_shader_stage(GL_VERTEX_SHADER, sources); +} + +void GLShader::geometry_shader_from_glsl(MutableSpan<const char *> sources) +{ + geom_shader_ = this->create_shader_stage(GL_GEOMETRY_SHADER, sources); +} + +void GLShader::fragment_shader_from_glsl(MutableSpan<const char *> sources) +{ + frag_shader_ = this->create_shader_stage(GL_FRAGMENT_SHADER, sources); +} + +bool GLShader::finalize(void) +{ + if (compilation_failed_) { + return false; + } + + glLinkProgram(shader_program_); + + GLint status; + glGetProgramiv(shader_program_, GL_LINK_STATUS, &status); + if (!status) { + char log[5000]; + glGetProgramInfoLog(shader_program_, sizeof(log), NULL, log); + fprintf(stderr, "\nLinking Error:\n\n%s", log); + return false; + } + + interface = new GLShaderInterface(shader_program_); + + return true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Binding + * \{ */ + +void GLShader::bind(void) +{ + BLI_assert(shader_program_ != 0); + glUseProgram(shader_program_); +} + +void GLShader::unbind(void) +{ +#ifndef NDEBUG + glUseProgram(0); +#endif +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform feedback + * + * TODO(fclem) Should be replaced by compute shaders. + * \{ */ + +/* Should be called before linking. */ +void GLShader::transform_feedback_names_set(Span<const char *> name_list, + const eGPUShaderTFBType geom_type) +{ + glTransformFeedbackVaryings( + shader_program_, name_list.size(), name_list.data(), GL_INTERLEAVED_ATTRIBS); + transform_feedback_type_ = geom_type; +} + +bool GLShader::transform_feedback_enable(GPUVertBuf *buf) +{ + if (transform_feedback_type_ == GPU_SHADER_TFB_NONE) { + return false; + } + + BLI_assert(buf->vbo_id != 0); + + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf->vbo_id); + + switch (transform_feedback_type_) { + case GPU_SHADER_TFB_POINTS: + glBeginTransformFeedback(GL_POINTS); + break; + case GPU_SHADER_TFB_LINES: + glBeginTransformFeedback(GL_LINES); + break; + case GPU_SHADER_TFB_TRIANGLES: + glBeginTransformFeedback(GL_TRIANGLES); + break; + default: + return false; + } + return true; +} + +void GLShader::transform_feedback_disable(void) +{ + glEndTransformFeedback(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Uniforms setters + * \{ */ + +void GLShader::uniform_float(int location, int comp_len, int array_size, const float *data) +{ + switch (comp_len) { + case 1: + glUniform1fv(location, array_size, data); + break; + case 2: + glUniform2fv(location, array_size, data); + break; + case 3: + glUniform3fv(location, array_size, data); + break; + case 4: + glUniform4fv(location, array_size, data); + break; + case 9: + glUniformMatrix3fv(location, array_size, 0, data); + break; + case 16: + glUniformMatrix4fv(location, array_size, 0, data); + break; + default: + BLI_assert(0); + break; + } +} + +void GLShader::uniform_int(int location, int comp_len, int array_size, const int *data) +{ + switch (comp_len) { + case 1: + glUniform1iv(location, array_size, data); + break; + case 2: + glUniform2iv(location, array_size, data); + break; + case 3: + glUniform3iv(location, array_size, data); + break; + case 4: + glUniform4iv(location, array_size, data); + break; + default: + BLI_assert(0); + break; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GPUVertFormat from Shader + * \{ */ + +static uint calc_component_size(const GLenum gl_type) +{ + switch (gl_type) { + case GL_FLOAT_VEC2: + case GL_INT_VEC2: + case GL_UNSIGNED_INT_VEC2: + return 2; + case GL_FLOAT_VEC3: + case GL_INT_VEC3: + case GL_UNSIGNED_INT_VEC3: + return 3; + case GL_FLOAT_VEC4: + case GL_FLOAT_MAT2: + case GL_INT_VEC4: + case GL_UNSIGNED_INT_VEC4: + return 4; + case GL_FLOAT_MAT3: + return 9; + case GL_FLOAT_MAT4: + return 16; + case GL_FLOAT_MAT2x3: + case GL_FLOAT_MAT3x2: + return 6; + case GL_FLOAT_MAT2x4: + case GL_FLOAT_MAT4x2: + return 8; + case GL_FLOAT_MAT3x4: + case GL_FLOAT_MAT4x3: + return 12; + default: + return 1; + } +} + +static void get_fetch_mode_and_comp_type(int gl_type, + GPUVertCompType *r_comp_type, + GPUVertFetchMode *r_fetch_mode) +{ + switch (gl_type) { + case GL_FLOAT: + case GL_FLOAT_VEC2: + case GL_FLOAT_VEC3: + case GL_FLOAT_VEC4: + case GL_FLOAT_MAT2: + case GL_FLOAT_MAT3: + case GL_FLOAT_MAT4: + case GL_FLOAT_MAT2x3: + case GL_FLOAT_MAT2x4: + case GL_FLOAT_MAT3x2: + case GL_FLOAT_MAT3x4: + case GL_FLOAT_MAT4x2: + case GL_FLOAT_MAT4x3: + *r_comp_type = GPU_COMP_F32; + *r_fetch_mode = GPU_FETCH_FLOAT; + break; + case GL_INT: + case GL_INT_VEC2: + case GL_INT_VEC3: + case GL_INT_VEC4: + *r_comp_type = GPU_COMP_I32; + *r_fetch_mode = GPU_FETCH_INT; + break; + case GL_UNSIGNED_INT: + case GL_UNSIGNED_INT_VEC2: + case GL_UNSIGNED_INT_VEC3: + case GL_UNSIGNED_INT_VEC4: + *r_comp_type = GPU_COMP_U32; + *r_fetch_mode = GPU_FETCH_INT; + break; + default: + BLI_assert(0); + } +} + +void GLShader::vertformat_from_shader(GPUVertFormat *format) const +{ + GPU_vertformat_clear(format); + + GLint attr_len; + glGetProgramiv(shader_program_, GL_ACTIVE_ATTRIBUTES, &attr_len); + + for (int i = 0; i < attr_len; i++) { + char name[256]; + GLenum gl_type; + GLint size; + glGetActiveAttrib(shader_program_, i, sizeof(name), NULL, &size, &gl_type, name); + + /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */ + if (glGetAttribLocation(shader_program_, name) == -1) { + continue; + } + + GPUVertCompType comp_type; + GPUVertFetchMode fetch_mode; + get_fetch_mode_and_comp_type(gl_type, &comp_type, &fetch_mode); + + int comp_len = calc_component_size(gl_type) * size; + + GPU_vertformat_attr_add(format, name, comp_type, comp_len, fetch_mode); + } +} + +/** \} */ diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh new file mode 100644 index 00000000000..a686014f4c5 --- /dev/null +++ b/source/blender/gpu/opengl/gl_shader.hh @@ -0,0 +1,83 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "glew-mx.h" + +#include "gpu_shader_private.hh" + +namespace blender { +namespace gpu { + +/** + * Implementation of shader compilation and uniforms handling using OpenGL. + **/ +class GLShader : public Shader { + private: + /** Handle for full program (links shader stages below). */ + GLuint shader_program_ = 0; + /** Individual shader stages. */ + GLuint vert_shader_ = 0; + GLuint geom_shader_ = 0; + GLuint frag_shader_ = 0; + /** True if any shader failed to compile. */ + bool compilation_failed_ = false; + + eGPUShaderTFBType transform_feedback_type_ = GPU_SHADER_TFB_NONE; + + public: + GLShader(const char *name); + ~GLShader(); + + /* Return true on success. */ + void vertex_shader_from_glsl(MutableSpan<const char *> sources) override; + void geometry_shader_from_glsl(MutableSpan<const char *> sources) override; + void fragment_shader_from_glsl(MutableSpan<const char *> sources) override; + bool finalize(void) override; + + void transform_feedback_names_set(Span<const char *> name_list, + const eGPUShaderTFBType geom_type) override; + bool transform_feedback_enable(GPUVertBuf *buf) override; + void transform_feedback_disable(void) override; + + void bind(void) override; + void unbind(void) override; + + void uniform_float(int location, int comp_len, int array_size, const float *data) override; + void uniform_int(int location, int comp_len, int array_size, const int *data) override; + + void vertformat_from_shader(GPUVertFormat *format) const override; + + private: + char *glsl_patch_get(void); + + GLuint create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLShader"); +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc new file mode 100644 index 00000000000..d611efcd975 --- /dev/null +++ b/source/blender/gpu/opengl/gl_shader_interface.cc @@ -0,0 +1,299 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GPU shader interface (C --> GLSL) + */ + +#include "BLI_bitmap.h" + +#include "gl_batch.hh" + +#include "gl_shader_interface.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Binding assignment + * + * To mimic vulkan, we assign binding at shader creation to avoid shader recompilation. + * In the future, we should set it in the shader using layout(binding = i) and query its value. + * \{ */ + +static inline int block_binding(int32_t program, uint32_t block_index) +{ + /* For now just assign a consecutive index. In the future, we should set it in + * the shader using layout(binding = i) and query its value. */ + glUniformBlockBinding(program, block_index, block_index); + return block_index; +} + +static inline int sampler_binding(int32_t program, + uint32_t uniform_index, + int32_t uniform_location, + int *sampler_len) +{ + /* Identify sampler uniforms and asign sampler units to them. */ + GLint type; + glGetActiveUniformsiv(program, 1, &uniform_index, GL_UNIFORM_TYPE, &type); + + switch (type) { + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_CUBE_MAP_ARRAY_ARB: /* OpenGL 4.0 */ + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_BUFFER: + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: { + /* For now just assign a consecutive index. In the future, we should set it in + * the shader using layout(binding = i) and query its value. */ + int binding = *sampler_len; + glUniform1i(uniform_location, binding); + (*sampler_len)++; + return binding; + } + default: + return -1; + } +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Creation / Destruction + * \{ */ + +GLShaderInterface::GLShaderInterface(GLuint program) +{ + /* Necessary to make glUniform works. */ + glUseProgram(program); + + GLint max_attr_name_len = 0, attr_len = 0; + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_attr_name_len); + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attr_len); + + GLint max_ubo_name_len = 0, ubo_len = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_ubo_name_len); + glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &ubo_len); + + GLint max_uniform_name_len = 0, active_uniform_len = 0, uniform_len = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_len); + glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len); + uniform_len = active_uniform_len; + + BLI_assert(ubo_len <= 16 && "enabled_ubo_mask_ is uint16_t"); + + /* Work around driver bug with Intel HD 4600 on Windows 7/8, where + * GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH does not work. */ + if (attr_len > 0 && max_attr_name_len == 0) { + max_attr_name_len = 256; + } + if (ubo_len > 0 && max_ubo_name_len == 0) { + max_ubo_name_len = 256; + } + if (uniform_len > 0 && max_uniform_name_len == 0) { + max_uniform_name_len = 256; + } + + /* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before + * allocating the uniform array. */ + GLint max_ubo_uni_len = 0; + for (int i = 0; i < ubo_len; i++) { + GLint ubo_uni_len; + glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len); + max_ubo_uni_len = max_ii(max_ubo_uni_len, ubo_uni_len); + uniform_len -= ubo_uni_len; + } + /* Bit set to true if uniform comes from a uniform block. */ + BLI_bitmap *uniforms_from_blocks = BLI_BITMAP_NEW(active_uniform_len, __func__); + /* Set uniforms from block for exclusion. */ + GLint *ubo_uni_ids = (GLint *)MEM_mallocN(sizeof(GLint) * max_ubo_uni_len, __func__); + for (int i = 0; i < ubo_len; i++) { + GLint ubo_uni_len; + glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len); + glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, ubo_uni_ids); + for (int u = 0; u < ubo_uni_len; u++) { + BLI_BITMAP_ENABLE(uniforms_from_blocks, ubo_uni_ids[u]); + } + } + MEM_freeN(ubo_uni_ids); + + int input_tot_len = attr_len + ubo_len + uniform_len; + inputs_ = (ShaderInput *)MEM_callocN(sizeof(ShaderInput) * input_tot_len, __func__); + + const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len + + uniform_len * max_uniform_name_len; + name_buffer_ = (char *)MEM_mallocN(name_buffer_len, "name_buffer"); + uint32_t name_buffer_offset = 0; + + /* Attributes */ + enabled_attr_mask_ = 0; + for (int i = 0; i < attr_len; i++) { + char *name = name_buffer_ + name_buffer_offset; + GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; + GLsizei name_len = 0; + GLenum type; + GLint size; + + glGetActiveAttrib(program, i, remaining_buffer, &name_len, &size, &type, name); + GLint location = glGetAttribLocation(program, name); + /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */ + if (location == -1) { + continue; + } + + ShaderInput *input = &inputs_[attr_len_++]; + input->location = input->binding = location; + + name_buffer_offset += set_input_name(input, name, name_len); + enabled_attr_mask_ |= (1 << input->location); + } + + /* Uniform Blocks */ + for (int i = 0; i < ubo_len; i++) { + char *name = name_buffer_ + name_buffer_offset; + GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; + GLsizei name_len = 0; + + glGetActiveUniformBlockName(program, i, remaining_buffer, &name_len, name); + + ShaderInput *input = &inputs_[attr_len_ + ubo_len_++]; + input->binding = input->location = block_binding(program, i); + + name_buffer_offset += this->set_input_name(input, name, name_len); + enabled_ubo_mask_ |= (1 << input->binding); + } + + /* Uniforms */ + for (int i = 0, sampler = 0; i < active_uniform_len; i++) { + if (BLI_BITMAP_TEST(uniforms_from_blocks, i)) { + continue; + } + char *name = name_buffer_ + name_buffer_offset; + GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; + GLsizei name_len = 0; + + glGetActiveUniformName(program, i, remaining_buffer, &name_len, name); + + ShaderInput *input = &inputs_[attr_len_ + ubo_len_ + uniform_len_++]; + input->location = glGetUniformLocation(program, name); + input->binding = sampler_binding(program, i, input->location, &sampler); + + name_buffer_offset += this->set_input_name(input, name, name_len); + enabled_tex_mask_ |= (input->binding != -1) ? (1lu << input->binding) : 0lu; + } + + /* Builtin Uniforms */ + for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) { + GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int); + builtins_[u] = glGetUniformLocation(program, builtin_uniform_name(u)); + } + + /* Builtin Uniforms Blocks */ + for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) { + GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int); + const ShaderInput *block = this->ubo_get(builtin_uniform_block_name(u)); + builtin_blocks_[u] = (block != NULL) ? block->binding : -1; + } + + MEM_freeN(uniforms_from_blocks); + + /* Resize name buffer to save some memory. */ + if (name_buffer_offset < name_buffer_len) { + name_buffer_ = (char *)MEM_reallocN(name_buffer_, name_buffer_offset); + } + + // this->debug_print(); + + this->sort_inputs(); +} + +GLShaderInterface::~GLShaderInterface() +{ + for (auto *ref : refs_) { + if (ref != NULL) { + ref->remove(this); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Batch Reference + * \{ */ + +void GLShaderInterface::ref_add(GLVaoCache *ref) +{ + for (int i = 0; i < refs_.size(); i++) { + if (refs_[i] == NULL) { + refs_[i] = ref; + return; + } + } + refs_.append(ref); +} + +void GLShaderInterface::ref_remove(GLVaoCache *ref) +{ + for (int i = 0; i < refs_.size(); i++) { + if (refs_[i] == ref) { + refs_[i] = NULL; + break; /* cannot have duplicates */ + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Validation + * TODO + * \{ */ + +/** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_shader_interface.hh b/source/blender/gpu/opengl/gl_shader_interface.hh new file mode 100644 index 00000000000..8cac84a5990 --- /dev/null +++ b/source/blender/gpu/opengl/gl_shader_interface.hh @@ -0,0 +1,61 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GPU shader interface (C --> GLSL) + * + * Structure detailing needed vertex inputs and resources for a specific shader. + * A shader interface can be shared between two similar shaders. + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "BLI_vector.hh" + +#include "glew-mx.h" + +#include "gpu_shader_interface.hh" + +namespace blender::gpu { + +class GLVaoCache; + +/** + * Implementation of Shader interface using OpenGL. + **/ +class GLShaderInterface : public ShaderInterface { + private: + /** Reference to VaoCaches using this interface */ + Vector<GLVaoCache *> refs_; + + public: + GLShaderInterface(GLuint program); + ~GLShaderInterface(); + + void ref_add(GLVaoCache *ref); + void ref_remove(GLVaoCache *ref); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLShaderInterface"); +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_state.cc b/source/blender/gpu/opengl/gl_state.cc new file mode 100644 index 00000000000..8f01ff13486 --- /dev/null +++ b/source/blender/gpu/opengl/gl_state.cc @@ -0,0 +1,421 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup gpu + */ + +#include "BLI_math_base.h" + +#include "GPU_extensions.h" + +#include "glew-mx.h" + +#include "gl_context.hh" +#include "gl_framebuffer.hh" +#include "gl_state.hh" + +using namespace blender::gpu; + +/* -------------------------------------------------------------------- */ +/** \name GLStateManager + * \{ */ + +GLStateManager::GLStateManager(void) : GPUStateManager() +{ + /* Set other states that never change. */ + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); + glEnable(GL_MULTISAMPLE); + glEnable(GL_PRIMITIVE_RESTART); + + glDisable(GL_DITHER); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + glPrimitiveRestartIndex((GLuint)0xFFFFFFFF); + /* TODO: Should become default. But needs at least GL 4.3 */ + if (GLEW_ARB_ES3_compatibility) { + /* Takes predecence over GL_PRIMITIVE_RESTART */ + glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); + } + + /* Limits. */ + glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, line_width_range_); + + /* Force update using default state. */ + current_ = ~state; + current_mutable_ = ~mutable_state; + set_state(state); + set_mutable_state(mutable_state); +} + +void GLStateManager::apply_state(void) +{ + this->set_state(this->state); + this->set_mutable_state(this->mutable_state); + active_fb->apply_state(); +}; + +void GLStateManager::set_state(const GPUState &state) +{ + GPUState changed = state ^ current_; + + if (changed.blend != 0) { + set_blend((eGPUBlend)state.blend); + } + if (changed.write_mask != 0) { + set_write_mask((eGPUWriteMask)state.write_mask); + } + if (changed.depth_test != 0) { + set_depth_test((eGPUDepthTest)state.depth_test); + } + if (changed.stencil_test != 0 || changed.stencil_op != 0) { + set_stencil_test((eGPUStencilTest)state.stencil_test, (eGPUStencilOp)state.stencil_op); + set_stencil_mask((eGPUStencilTest)state.stencil_test, mutable_state); + } + if (changed.clip_distances != 0) { + set_clip_distances(state.clip_distances, current_.clip_distances); + } + if (changed.culling_test != 0) { + set_backface_culling((eGPUFaceCullTest)state.culling_test); + } + if (changed.logic_op_xor != 0) { + set_logic_op(state.logic_op_xor); + } + if (changed.invert_facing != 0) { + set_facing(state.invert_facing); + } + if (changed.provoking_vert != 0) { + set_provoking_vert((eGPUProvokingVertex)state.provoking_vert); + } + if (changed.shadow_bias != 0) { + set_shadow_bias(state.shadow_bias); + } + + /* TODO remove */ + if (changed.polygon_smooth) { + if (state.polygon_smooth) { + glEnable(GL_POLYGON_SMOOTH); + } + else { + glDisable(GL_POLYGON_SMOOTH); + } + } + if (changed.line_smooth) { + if (state.line_smooth) { + glEnable(GL_LINE_SMOOTH); + } + else { + glDisable(GL_LINE_SMOOTH); + } + } + + current_ = state; +} + +void GLStateManager::set_mutable_state(const GPUStateMutable &state) +{ + GPUStateMutable changed = state ^ current_mutable_; + + /* TODO remove, should be uniform. */ + if (changed.point_size != 0) { + if (state.point_size > 0.0f) { + glEnable(GL_PROGRAM_POINT_SIZE); + glPointSize(state.point_size); + } + else { + glDisable(GL_PROGRAM_POINT_SIZE); + } + } + + if (changed.line_width != 0) { + /* TODO remove, should use wide line shader. */ + glLineWidth(clamp_f(state.line_width, line_width_range_[0], line_width_range_[1])); + } + + if (changed.depth_range[0] != 0 || changed.depth_range[1] != 0) { + /* TODO remove, should modify the projection matrix instead. */ + glDepthRange(UNPACK2(state.depth_range)); + } + + if (changed.stencil_compare_mask != 0 || changed.stencil_reference != 0 || + changed.stencil_write_mask != 0) { + set_stencil_mask((eGPUStencilTest)current_.stencil_test, state); + } + + current_mutable_ = state; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name State set functions + * \{ */ + +void GLStateManager::set_write_mask(const eGPUWriteMask value) +{ + glDepthMask((value & GPU_WRITE_DEPTH) != 0); + glColorMask((value & GPU_WRITE_RED) != 0, + (value & GPU_WRITE_GREEN) != 0, + (value & GPU_WRITE_BLUE) != 0, + (value & GPU_WRITE_ALPHA) != 0); + + if (value == GPU_WRITE_NONE) { + glEnable(GL_RASTERIZER_DISCARD); + } + else { + glDisable(GL_RASTERIZER_DISCARD); + } +} + +void GLStateManager::set_depth_test(const eGPUDepthTest value) +{ + GLenum func; + switch (value) { + case GPU_DEPTH_LESS: + func = GL_LESS; + break; + case GPU_DEPTH_LESS_EQUAL: + func = GL_LEQUAL; + break; + case GPU_DEPTH_EQUAL: + func = GL_EQUAL; + break; + case GPU_DEPTH_GREATER: + func = GL_GREATER; + break; + case GPU_DEPTH_GREATER_EQUAL: + func = GL_GEQUAL; + break; + case GPU_DEPTH_ALWAYS: + default: + func = GL_ALWAYS; + break; + } + + if (value != GPU_DEPTH_NONE) { + glEnable(GL_DEPTH_TEST); + glDepthFunc(func); + } + else { + glDisable(GL_DEPTH_TEST); + } +} + +void GLStateManager::set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation) +{ + switch (operation) { + case GPU_STENCIL_OP_REPLACE: + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + break; + case GPU_STENCIL_OP_COUNT_DEPTH_PASS: + glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_INCR_WRAP); + glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_DECR_WRAP); + break; + case GPU_STENCIL_OP_COUNT_DEPTH_FAIL: + glStencilOpSeparate(GL_BACK, GL_KEEP, GL_DECR_WRAP, GL_KEEP); + glStencilOpSeparate(GL_FRONT, GL_KEEP, GL_INCR_WRAP, GL_KEEP); + break; + case GPU_STENCIL_OP_NONE: + default: + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + } + + if (test != GPU_STENCIL_NONE) { + glEnable(GL_STENCIL_TEST); + } + else { + glDisable(GL_STENCIL_TEST); + } +} + +void GLStateManager::set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state) +{ + GLenum func; + switch (test) { + case GPU_STENCIL_NEQUAL: + func = GL_NOTEQUAL; + break; + case GPU_STENCIL_EQUAL: + func = GL_EQUAL; + break; + case GPU_STENCIL_ALWAYS: + func = GL_ALWAYS; + break; + case GPU_STENCIL_NONE: + default: + glStencilMask(0x00); + glStencilFunc(GL_ALWAYS, 0x00, 0x00); + return; + } + + glStencilMask(state.stencil_write_mask); + glStencilFunc(func, state.stencil_reference, state.stencil_compare_mask); +} + +void GLStateManager::set_clip_distances(const int new_dist_len, const int old_dist_len) +{ + for (int i = 0; i < new_dist_len; i++) { + glEnable(GL_CLIP_DISTANCE0 + i); + } + for (int i = new_dist_len; i < old_dist_len; i++) { + glDisable(GL_CLIP_DISTANCE0 + i); + } +} + +void GLStateManager::set_logic_op(const bool enable) +{ + if (enable) { + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(GL_XOR); + } + else { + glDisable(GL_COLOR_LOGIC_OP); + } +} + +void GLStateManager::set_facing(const bool invert) +{ + glFrontFace((invert) ? GL_CW : GL_CCW); +} + +void GLStateManager::set_backface_culling(const eGPUFaceCullTest test) +{ + if (test != GPU_CULL_NONE) { + glEnable(GL_CULL_FACE); + glCullFace((test == GPU_CULL_FRONT) ? GL_FRONT : GL_BACK); + } + else { + glDisable(GL_CULL_FACE); + } +} + +void GLStateManager::set_provoking_vert(const eGPUProvokingVertex vert) +{ + GLenum value = (vert == GPU_VERTEX_FIRST) ? GL_FIRST_VERTEX_CONVENTION : + GL_LAST_VERTEX_CONVENTION; + glProvokingVertex(value); +} + +void GLStateManager::set_shadow_bias(const bool enable) +{ + if (enable) { + glEnable(GL_POLYGON_OFFSET_FILL); + glEnable(GL_POLYGON_OFFSET_LINE); + /* 2.0 Seems to be the lowest possible slope bias that works in every case. */ + glPolygonOffset(2.0f, 1.0f); + } + else { + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_POLYGON_OFFSET_LINE); + } +} + +void GLStateManager::set_blend(const eGPUBlend value) +{ + /** + * Factors to the equation. + * SRC is fragment shader output. + * DST is framebuffer color. + * final.rgb = SRC.rgb * src_rgb + DST.rgb * dst_rgb; + * final.a = SRC.a * src_alpha + DST.a * dst_alpha; + **/ + GLenum src_rgb, src_alpha, dst_rgb, dst_alpha; + switch (value) { + default: + case GPU_BLEND_ALPHA: { + src_rgb = GL_SRC_ALPHA; + dst_rgb = GL_ONE_MINUS_SRC_ALPHA; + src_alpha = GL_ONE; + dst_alpha = GL_ONE_MINUS_SRC_ALPHA; + break; + } + case GPU_BLEND_ALPHA_PREMULT: { + src_rgb = GL_ONE; + dst_rgb = GL_ONE_MINUS_SRC_ALPHA; + src_alpha = GL_ONE; + dst_alpha = GL_ONE_MINUS_SRC_ALPHA; + break; + } + case GPU_BLEND_ADDITIVE: { + /* Do not let alpha accumulate but premult the source RGB by it. */ + src_rgb = GL_SRC_ALPHA; + dst_rgb = GL_ONE; + src_alpha = GL_ZERO; + dst_alpha = GL_ONE; + break; + } + case GPU_BLEND_SUBTRACT: + case GPU_BLEND_ADDITIVE_PREMULT: { + /* Let alpha accumulate. */ + src_rgb = GL_ONE; + dst_rgb = GL_ONE; + src_alpha = GL_ONE; + dst_alpha = GL_ONE; + break; + } + case GPU_BLEND_MULTIPLY: { + src_rgb = GL_DST_COLOR; + dst_rgb = GL_ZERO; + src_alpha = GL_DST_ALPHA; + dst_alpha = GL_ZERO; + break; + } + case GPU_BLEND_INVERT: { + src_rgb = GL_ONE_MINUS_DST_COLOR; + dst_rgb = GL_ZERO; + src_alpha = GL_ZERO; + dst_alpha = GL_ONE; + break; + } + case GPU_BLEND_OIT: { + src_rgb = GL_ONE; + dst_rgb = GL_ONE; + src_alpha = GL_ZERO; + dst_alpha = GL_ONE_MINUS_SRC_ALPHA; + break; + } + case GPU_BLEND_BACKGROUND: { + src_rgb = GL_ONE_MINUS_DST_ALPHA; + dst_rgb = GL_SRC_ALPHA; + src_alpha = GL_ZERO; + dst_alpha = GL_SRC_ALPHA; + break; + } + case GPU_BLEND_CUSTOM: { + src_rgb = GL_ONE; + dst_rgb = GL_SRC1_COLOR; + src_alpha = GL_ONE; + dst_alpha = GL_SRC1_ALPHA; + break; + } + } + + /* Always set the blend function. This avoid a rendering error when blending is disabled but + * GPU_BLEND_CUSTOM was used just before and the framebuffer is using more than 1 color targe */ + glBlendFuncSeparate(src_rgb, dst_rgb, src_alpha, dst_alpha); + if (value != GPU_BLEND_NONE) { + glEnable(GL_BLEND); + } + else { + glDisable(GL_BLEND); + } +} + +/** \} */ diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh new file mode 100644 index 00000000000..c25e384fcd7 --- /dev/null +++ b/source/blender/gpu/opengl/gl_state.hh @@ -0,0 +1,77 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup gpu + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" + +#include "gpu_state_private.hh" + +#include "glew-mx.h" + +namespace blender { +namespace gpu { + +class GLFrameBuffer; + +/** + * State manager keeping track of the draw state and applying it before drawing. + * Opengl Implementation. + **/ +class GLStateManager : public GPUStateManager { + public: + /** Anothter reference to tje active framebuffer. */ + GLFrameBuffer *active_fb; + + private: + /** Current state of the GL implementation. Avoids resetting the whole state for every change. */ + GPUState current_; + GPUStateMutable current_mutable_; + /** Limits. */ + float line_width_range_[2]; + + public: + GLStateManager(); + + void apply_state(void) override; + + private: + static void set_write_mask(const eGPUWriteMask value); + static void set_depth_test(const eGPUDepthTest value); + static void set_stencil_test(const eGPUStencilTest test, const eGPUStencilOp operation); + static void set_stencil_mask(const eGPUStencilTest test, const GPUStateMutable state); + static void set_clip_distances(const int new_dist_len, const int old_dist_len); + static void set_logic_op(const bool enable); + static void set_facing(const bool invert); + static void set_backface_culling(const eGPUFaceCullTest test); + static void set_provoking_vert(const eGPUProvokingVertex vert); + static void set_shadow_bias(const bool enable); + static void set_blend(const eGPUBlend value); + + void set_state(const GPUState &state); + void set_mutable_state(const GPUStateMutable &state); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLStateManager") +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh new file mode 100644 index 00000000000..c1194941038 --- /dev/null +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -0,0 +1,81 @@ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GPU Framebuffer + * - this is a wrapper for an OpenGL framebuffer object (FBO). in practice + * multiple FBO's may be created. + * - actual FBO creation & config is deferred until GPU_framebuffer_bind or + * GPU_framebuffer_check_valid to allow creation & config while another + * opengl context is bound (since FBOs are not shared between ogl contexts). + */ + +#pragma once + +#include "BLI_assert.h" + +#include "glew-mx.h" + +namespace blender { +namespace gpu { + +static GLenum to_gl(eGPUDataFormat format) +{ + switch (format) { + case GPU_DATA_FLOAT: + return GL_FLOAT; + case GPU_DATA_INT: + return GL_INT; + case GPU_DATA_UNSIGNED_INT: + return GL_UNSIGNED_INT; + case GPU_DATA_UNSIGNED_BYTE: + return GL_UNSIGNED_BYTE; + case GPU_DATA_UNSIGNED_INT_24_8: + return GL_UNSIGNED_INT_24_8; + case GPU_DATA_10_11_11_REV: + return GL_UNSIGNED_INT_10F_11F_11F_REV; + default: + BLI_assert(!"Unhandled data format"); + return GL_FLOAT; + } +} + +/* Assume Unorm / Float target. Used with glReadPixels. */ +static GLenum channel_len_to_gl(int channel_len) +{ + switch (channel_len) { + case 1: + return GL_RED; + case 2: + return GL_RG; + case 3: + return GL_RGB; + case 4: + return GL_RGBA; + default: + BLI_assert(!"Wrong number of texture channels"); + return GL_RED; + } +} + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_uniform_buffer.cc b/source/blender/gpu/opengl/gl_uniform_buffer.cc new file mode 100644 index 00000000000..0e0c64e5c60 --- /dev/null +++ b/source/blender/gpu/opengl/gl_uniform_buffer.cc @@ -0,0 +1,133 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#include "BKE_global.h" + +#include "BLI_string.h" + +#include "GPU_extensions.h" + +#include "gpu_backend.hh" +#include "gpu_context_private.hh" + +#include "gl_backend.hh" +#include "gl_uniform_buffer.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ + +GLUniformBuf::GLUniformBuf(size_t size, const char *name) : UniformBuf(size, name) +{ + /* Do not create ubo GL buffer here to allow allocation from any thread. */ +} + +GLUniformBuf::~GLUniformBuf() +{ + GLBackend::get()->buf_free(ubo_id_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Data upload / update + * \{ */ + +void GLUniformBuf::init(void) +{ + BLI_assert(GPU_context_active_get()); + + glGenBuffers(1, &ubo_id_); + glBindBuffer(GL_UNIFORM_BUFFER, ubo_id_); + glBufferData(GL_UNIFORM_BUFFER, size_in_bytes_, NULL, GL_DYNAMIC_DRAW); + +#ifndef __APPLE__ + if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { + char sh_name[64]; + SNPRINTF(sh_name, "UBO-%s", name_); + glObjectLabel(GL_BUFFER, ubo_id_, -1, sh_name); + } +#endif +} + +void GLUniformBuf::update(const void *data) +{ + if (ubo_id_ == 0) { + this->init(); + } + glBindBuffer(GL_UNIFORM_BUFFER, ubo_id_); + glBufferSubData(GL_UNIFORM_BUFFER, 0, size_in_bytes_, data); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Usage + * \{ */ + +void GLUniformBuf::bind(int slot) +{ + if (slot >= GPU_max_ubo_binds()) { + fprintf(stderr, + "Error: Trying to bind \"%s\" ubo to slot %d which is above the reported limit of %d.", + name_, + slot, + GPU_max_ubo_binds()); + return; + } + + if (ubo_id_ == 0) { + this->init(); + } + + if (data_ != NULL) { + this->update(data_); + MEM_SAFE_FREE(data_); + } + + slot_ = slot; + glBindBufferBase(GL_UNIFORM_BUFFER, slot_, ubo_id_); + +#ifdef DEBUG + BLI_assert(slot < 16); + static_cast<GLContext *>(GPU_context_active_get())->bound_ubo_slots |= 1 << slot; +#endif +} + +void GLUniformBuf::unbind(void) +{ +#ifdef DEBUG + /* NOTE: This only unbinds the last bound slot. */ + glBindBufferBase(GL_UNIFORM_BUFFER, slot_, 0); + /* Hope that the context did not change. */ + static_cast<GLContext *>(GPU_context_active_get())->bound_ubo_slots &= ~(1 << slot_); +#endif + slot_ = 0; +} + +/** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_uniform_buffer.hh b/source/blender/gpu/opengl/gl_uniform_buffer.hh new file mode 100644 index 00000000000..8cd2ab91be9 --- /dev/null +++ b/source/blender/gpu/opengl/gl_uniform_buffer.hh @@ -0,0 +1,60 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "gpu_uniform_buffer_private.hh" + +#include "glew-mx.h" + +namespace blender { +namespace gpu { + +/** + * Implementation of Uniform Buffers using OpenGL. + **/ +class GLUniformBuf : public UniformBuf { + private: + /** Slot to which this UBO is currently bound. -1 if not bound. */ + int slot_ = -1; + /** OpenGL Object handle. */ + GLuint ubo_id_ = 0; + + public: + GLUniformBuf(size_t size, const char *name); + ~GLUniformBuf(); + + void update(const void *data) override; + void bind(int slot) override; + void unbind(void) override; + + private: + void init(void); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLUniformBuf"); +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc new file mode 100644 index 00000000000..64d44c39587 --- /dev/null +++ b/source/blender/gpu/opengl/gl_vertex_array.cc @@ -0,0 +1,171 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#include "GPU_glew.h" + +#include "GPU_vertex_buffer.h" + +#include "gpu_shader_interface.hh" +#include "gpu_vertex_format_private.h" + +#include "gl_batch.hh" +#include "gl_context.hh" + +#include "gl_vertex_array.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Vertex Array Bindings + * \{ */ + +/* Returns enabled vertex pointers as a bitflag (one bit per attrib). */ +static uint16_t vbo_bind(const ShaderInterface *interface, + const GPUVertFormat *format, + uint v_first, + uint v_len, + const bool use_instancing) +{ + uint16_t enabled_attrib = 0; + const uint attr_len = format->attr_len; + uint stride = format->stride; + uint offset = 0; + GLuint divisor = (use_instancing) ? 1 : 0; + + for (uint a_idx = 0; a_idx < attr_len; a_idx++) { + const GPUVertAttr *a = &format->attrs[a_idx]; + + if (format->deinterleaved) { + offset += ((a_idx == 0) ? 0 : format->attrs[a_idx - 1].sz) * v_len; + stride = a->sz; + } + else { + offset = a->offset; + } + + const GLvoid *pointer = (const GLubyte *)0 + offset + v_first * stride; + const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type)); + + for (uint n_idx = 0; n_idx < a->name_len; n_idx++) { + const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); + const ShaderInput *input = interface->attr_get(name); + + if (input == NULL) { + continue; + } + + enabled_attrib |= (1 << input->location); + + if (a->comp_len == 16 || a->comp_len == 12 || a->comp_len == 8) { + BLI_assert(a->fetch_mode == GPU_FETCH_FLOAT); + BLI_assert(a->comp_type == GPU_COMP_F32); + for (int i = 0; i < a->comp_len / 4; i++) { + glEnableVertexAttribArray(input->location + i); + glVertexAttribDivisor(input->location + i, divisor); + glVertexAttribPointer( + input->location + i, 4, type, GL_FALSE, stride, (const GLubyte *)pointer + i * 16); + } + } + else { + glEnableVertexAttribArray(input->location); + glVertexAttribDivisor(input->location, divisor); + + switch (a->fetch_mode) { + case GPU_FETCH_FLOAT: + case GPU_FETCH_INT_TO_FLOAT: + glVertexAttribPointer(input->location, a->comp_len, type, GL_FALSE, stride, pointer); + break; + case GPU_FETCH_INT_TO_FLOAT_UNIT: + glVertexAttribPointer(input->location, a->comp_len, type, GL_TRUE, stride, pointer); + break; + case GPU_FETCH_INT: + glVertexAttribIPointer(input->location, a->comp_len, type, stride, pointer); + break; + } + } + } + } + return enabled_attrib; +} + +/* Update the Attrib Binding of the currently bound VAO. */ +void GLVertArray::update_bindings(const GLuint vao, + const GPUBatch *batch, + const ShaderInterface *interface, + const int base_instance) +{ + uint16_t attr_mask = interface->enabled_attr_mask_; + + glBindVertexArray(vao); + + /* Reverse order so first VBO'S have more prevalence (in term of attribute override). */ + for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; v--) { + GPUVertBuf *vbo = batch->verts[v]; + if (vbo) { + GPU_vertbuf_use(vbo); + attr_mask &= ~vbo_bind(interface, &vbo->format, 0, vbo->vertex_len, false); + } + } + + for (int v = GPU_BATCH_INST_VBO_MAX_LEN - 1; v > -1; v--) { + GPUVertBuf *vbo = batch->inst[v]; + if (vbo) { + GPU_vertbuf_use(vbo); + attr_mask &= ~vbo_bind(interface, &vbo->format, base_instance, vbo->vertex_len, true); + } + } + + if (attr_mask != 0 && GLEW_ARB_vertex_attrib_binding) { + for (uint16_t mask = 1, a = 0; a < 16; a++, mask <<= 1) { + if (attr_mask & mask) { + GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get()); + /* This replaces glVertexAttrib4f(a, 0.0f, 0.0f, 0.0f, 1.0f); with a more modern style. + * Fix issues for some drivers (see T75069). */ + glBindVertexBuffer(a, ctx->default_attr_vbo_, (intptr_t)0, (intptr_t)0); + glEnableVertexAttribArray(a); + glVertexAttribFormat(a, 4, GL_FLOAT, GL_FALSE, 0); + glVertexAttribBinding(a, a); + } + } + } + + if (batch->elem) { + /* Binds the index buffer. This state is also saved in the VAO. */ + GPU_indexbuf_use(batch->elem); + } +} + +/* Another version of update_bindings for Immediate mode. */ +void GLVertArray::update_bindings(const GLuint vao, + const uint v_first, + const GPUVertFormat *format, + const ShaderInterface *interface) +{ + glBindVertexArray(vao); + + vbo_bind(interface, format, v_first, 0, false); +} + +/** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_vertex_array.hh b/source/blender/gpu/opengl/gl_vertex_array.hh new file mode 100644 index 00000000000..7037986e31e --- /dev/null +++ b/source/blender/gpu/opengl/gl_vertex_array.hh @@ -0,0 +1,49 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "glew-mx.h" + +#include "GPU_batch.h" +#include "gl_shader_interface.hh" + +namespace blender { +namespace gpu { + +namespace GLVertArray { + +void update_bindings(const GLuint vao, + const GPUBatch *batch, + const ShaderInterface *interface, + const int base_instance); + +void update_bindings(const GLuint vao, + const uint v_first, + const GPUVertFormat *format, + const ShaderInterface *interface); + +} // namespace GLVertArray + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl index d25cd586e65..640ceb97e5b 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_image_multi_rect_vert.glsl @@ -13,34 +13,19 @@ flat out vec4 finalColor; void main() { - /* Rendering 2 triangle per icon. */ - int i = gl_VertexID / 6; - int v = gl_VertexID % 6; + vec4 pos = calls_data[gl_InstanceID * 3]; + vec4 tex = calls_data[gl_InstanceID * 3 + 1]; + finalColor = calls_data[gl_InstanceID * 3 + 2]; - vec4 pos = calls_data[i * 3]; - vec4 tex = calls_data[i * 3 + 1]; - finalColor = calls_data[i * 3 + 2]; - - /* TODO Remove this */ - if (v == 2) { - v = 4; - } - else if (v == 3) { - v = 0; - } - else if (v == 5) { - v = 2; + if (gl_VertexID == 0) { + pos.xy = pos.xz; + tex.xy = tex.xz; } - - if (v == 0) { + else if (gl_VertexID == 1) { pos.xy = pos.xw; tex.xy = tex.xw; } - else if (v == 1) { - pos.xy = pos.xz; - tex.xy = tex.xz; - } - else if (v == 2) { + else if (gl_VertexID == 2) { pos.xy = pos.yw; tex.xy = tex.yw; } diff --git a/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl index fcd877a37eb..ab9c30505c2 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_image_rect_vert.glsl @@ -14,13 +14,13 @@ void main() vec2 uv; vec2 co; if (gl_VertexID == 0) { - co = rect_geom.xw; - uv = rect_icon.xw; - } - else if (gl_VertexID == 1) { co = rect_geom.xy; uv = rect_icon.xy; } + else if (gl_VertexID == 1) { + co = rect_geom.xw; + uv = rect_icon.xw; + } else if (gl_VertexID == 2) { co = rect_geom.zw; uv = rect_icon.zw; diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl index d15f48c8f8a..fb512a1f00e 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_vert.glsl @@ -57,9 +57,14 @@ in float dummy; vec2 do_widget(void) { + /* Offset to avoid loosing pixels (mimics conservative rasterization). */ + const vec2 ofs = vec2(0.5, -0.5); lineWidth = abs(rect.x - recti.x); vec2 emboss_ofs = vec2(0.0, -lineWidth); - vec2 v_pos[4] = vec2[4](rect.xz + emboss_ofs, rect.xw, rect.yz + emboss_ofs, rect.yw); + vec2 v_pos[4] = vec2[4](rect.xz + emboss_ofs + ofs.yy, + rect.xw + ofs.yx, + rect.yz + emboss_ofs + ofs.xy, + rect.yw + ofs.xx); vec2 pos = v_pos[gl_VertexID]; uvInterp = pos - rect.xz; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl index 27ca96501ae..5eb853a4c1a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_output_world.glsl @@ -6,7 +6,8 @@ void node_output_world(Closure surface, Closure volume, out Closure result) float alpha = renderPassEnvironment ? 1.0 : backgroundAlpha; result = CLOSURE_DEFAULT; result.radiance = surface.radiance * alpha; - result.transmittance = vec3(1.0 - alpha); + result.transmittance = vec3(0.0); + result.holdout = (1.0 - alpha); #else result = volume; #endif /* VOLUMETRICS */ |