diff options
Diffstat (limited to 'source/blender/gpu')
74 files changed, 1624 insertions, 376 deletions
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 2ff72266a64..b7dc3210c41 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -62,6 +62,7 @@ set(SRC intern/gpu_buffers.c intern/gpu_capabilities.cc intern/gpu_codegen.c + intern/gpu_compute.cc intern/gpu_context.cc intern/gpu_debug.cc intern/gpu_drawlist.cc @@ -82,6 +83,7 @@ set(SRC intern/gpu_shader.cc intern/gpu_shader_builtin.c intern/gpu_shader_interface.cc + intern/gpu_shader_log.cc intern/gpu_state.cc intern/gpu_texture.cc intern/gpu_uniform_buffer.cc @@ -91,6 +93,7 @@ set(SRC opengl/gl_backend.cc opengl/gl_batch.cc + opengl/gl_compute.cc opengl/gl_context.cc opengl/gl_debug.cc opengl/gl_debug_layer.cc @@ -101,6 +104,7 @@ set(SRC opengl/gl_query.cc opengl/gl_shader.cc opengl/gl_shader_interface.cc + opengl/gl_shader_log.cc opengl/gl_state.cc opengl/gl_texture.cc opengl/gl_uniform_buffer.cc @@ -113,6 +117,7 @@ set(SRC GPU_buffers.h GPU_capabilities.h GPU_common.h + GPU_compute.h GPU_context.h GPU_debug.h GPU_drawlist.h @@ -163,6 +168,7 @@ set(SRC opengl/gl_backend.hh opengl/gl_batch.hh + opengl/gl_compute.hh opengl/gl_context.hh opengl/gl_debug.hh opengl/gl_drawlist.hh @@ -275,6 +281,7 @@ data_to_c_simple(shaders/material/gpu_shader_material_anisotropic.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_attribute.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_background.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_bevel.glsl SRC) +data_to_c_simple(shaders/material/gpu_shader_material_wavelength.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_blackbody.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_bright_contrast.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_bump.glsl SRC) @@ -390,6 +397,10 @@ if(WITH_GTESTS) set(TEST_SRC tests/gpu_testing.cc + tests/gpu_index_buffer_test.cc + tests/gpu_shader_builtin_test.cc + tests/gpu_shader_test.cc + tests/gpu_testing.hh ) set(TEST_INC diff --git a/source/blender/gpu/GPU_capabilities.h b/source/blender/gpu/GPU_capabilities.h index f54ecece659..0c054d4f264 100644 --- a/source/blender/gpu/GPU_capabilities.h +++ b/source/blender/gpu/GPU_capabilities.h @@ -37,6 +37,8 @@ int GPU_max_textures(void); int GPU_max_textures_vert(void); int GPU_max_textures_geom(void); int GPU_max_textures_frag(void); +int GPU_max_work_group_count(int index); +int GPU_max_work_group_size(int index); int GPU_max_uniforms_vert(void); int GPU_max_uniforms_frag(void); int GPU_max_batch_indices(void); @@ -55,6 +57,8 @@ bool GPU_use_main_context_workaround(void); bool GPU_use_hq_normals_workaround(void); bool GPU_crappy_amd_driver(void); +bool GPU_compute_shader_support(void); +bool GPU_shader_storage_buffer_objects_support(void); bool GPU_shader_image_load_store_support(void); bool GPU_mem_stats_supported(void); diff --git a/source/blender/gpu/GPU_compute.h b/source/blender/gpu/GPU_compute.h new file mode 100644 index 00000000000..a048f72c0a0 --- /dev/null +++ b/source/blender/gpu/GPU_compute.h @@ -0,0 +1,38 @@ +/* + * 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_sys_types.h" + +#include "GPU_shader.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void GPU_compute_dispatch(GPUShader *shader, + uint groups_x_len, + uint groups_y_len, + uint groups_z_len); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/GPU_index_buffer.h b/source/blender/gpu/GPU_index_buffer.h index 76aab3c196b..197077cf76c 100644 --- a/source/blender/gpu/GPU_index_buffer.h +++ b/source/blender/gpu/GPU_index_buffer.h @@ -40,6 +40,8 @@ typedef struct GPUIndexBufBuilder { uint max_allowed_index; uint max_index_len; uint index_len; + uint index_min; + uint index_max; GPUPrimType prim_type; uint32_t *data; } GPUIndexBufBuilder; @@ -49,6 +51,14 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *, GPUPrimType, uint index_len, uin /* supports only GPU_PRIM_POINTS, GPU_PRIM_LINES and GPU_PRIM_TRIS. */ void GPU_indexbuf_init(GPUIndexBufBuilder *, GPUPrimType, uint prim_len, uint vertex_len); +GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len); + +/* + * Thread safe. + * + * Function inspired by the reduction directives of multithread work APIs.. + */ +void GPU_indexbuf_join(GPUIndexBufBuilder *builder, const GPUIndexBufBuilder *builder_from); void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *, uint v); void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *); @@ -70,6 +80,8 @@ void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem); GPUIndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *); void GPU_indexbuf_build_in_place(GPUIndexBufBuilder *, GPUIndexBuf *); +void GPU_indexbuf_bind_as_ssbo(GPUIndexBuf *elem, int binding); + /* Create a sub-range of an existing index-buffer. */ GPUIndexBuf *GPU_indexbuf_create_subrange(GPUIndexBuf *elem_src, uint start, uint length); void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem, @@ -77,6 +89,15 @@ void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem, uint start, uint length); +/** + * (Download and) return a pointer containing the data of an index buffer. + * + * Note that the returned pointer is still owned by the driver. To get an + * local copy, use `GPU_indexbuf_unmap` after calling `GPU_indexbuf_read`. + */ +const uint32_t *GPU_indexbuf_read(GPUIndexBuf *elem); +uint32_t *GPU_indexbuf_unmap(const GPUIndexBuf *elem, const uint32_t *mapped_buffer); + void GPU_indexbuf_discard(GPUIndexBuf *elem); bool GPU_indexbuf_is_init(GPUIndexBuf *elem); diff --git a/source/blender/gpu/GPU_matrix.h b/source/blender/gpu/GPU_matrix.h index aad6ae9e2ba..e073263f352 100644 --- a/source/blender/gpu/GPU_matrix.h +++ b/source/blender/gpu/GPU_matrix.h @@ -118,21 +118,27 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *unproj_prec const float proj[4][4], const int view[4]); -void GPU_matrix_project(const float world[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float r_win[3]); - -bool GPU_matrix_unproject(const float win[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float r_world[3]); - -void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc, - const float win[3], - float r_world[3]); +void GPU_matrix_project_3fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_win[3]); + +void GPU_matrix_project_2fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_win[2]); + +bool GPU_matrix_unproject_3fv(const float win[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_world[3]); + +void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *unproj_precalc, + const float win[3], + float r_world[3]); /* 2D Projection Matrix */ diff --git a/source/blender/gpu/GPU_primitive.h b/source/blender/gpu/GPU_primitive.h index b1d70326b45..f64a673d461 100644 --- a/source/blender/gpu/GPU_primitive.h +++ b/source/blender/gpu/GPU_primitive.h @@ -57,7 +57,7 @@ typedef enum { } GPUPrimClass; /** - * TODO Improve error checking by validating that the shader is suited for this primitive type. + * 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); */ diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 9824c7016dc..f834ee5b234 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -27,6 +27,7 @@ extern "C" { #endif +struct GPUIndexBuf; struct GPUVertBuf; /** Opaque type hiding #blender::gpu::Shader */ @@ -45,6 +46,10 @@ GPUShader *GPU_shader_create(const char *vertcode, const char *libcode, const char *defines, const char *shname); +GPUShader *GPU_shader_create_compute(const char *computecode, + const char *libcode, + const char *defines, + const char *shname); GPUShader *GPU_shader_create_from_python(const char *vertcode, const char *fragcode, const char *geomcode, @@ -53,6 +58,7 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode, GPUShader *GPU_shader_create_ex(const char *vertcode, const char *fragcode, const char *geomcode, + const char *computecode, const char *libcode, const char *defines, const eGPUShaderTFBType tf_type, @@ -126,6 +132,7 @@ int GPU_shader_get_uniform(GPUShader *shader, const char *name); int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin); int GPU_shader_get_builtin_block(GPUShader *shader, int builtin); int GPU_shader_get_uniform_block(GPUShader *shader, const char *name); +int GPU_shader_get_ssbo(GPUShader *shader, const char *name); int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name); int GPU_shader_get_texture_binding(GPUShader *shader, const char *name); @@ -359,7 +366,6 @@ typedef enum eGPUBuiltinShader { GPU_SHADER_INSTANCE_VARIYING_COLOR_VARIYING_SIZE, /* Uniformly scaled */ /* grease pencil drawing */ GPU_SHADER_GPENCIL_STROKE, - GPU_SHADER_GPENCIL_FILL, /* specialized for widget drawing */ GPU_SHADER_2D_AREA_EDGES, GPU_SHADER_2D_WIDGET_BASE, diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index 0687f271670..a338728804c 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -39,6 +39,7 @@ typedef enum eGPUBarrier { GPU_BARRIER_NONE = 0, GPU_BARRIER_SHADER_IMAGE_ACCESS = (1 << 0), GPU_BARRIER_TEXTURE_FETCH = (1 << 1), + GPU_BARRIER_SHADER_STORAGE = (1 << 2), } eGPUBarrier; ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_TEXTURE_FETCH) diff --git a/source/blender/gpu/GPU_vertex_buffer.h b/source/blender/gpu/GPU_vertex_buffer.h index aae58de533b..2c54016daa7 100644 --- a/source/blender/gpu/GPU_vertex_buffer.h +++ b/source/blender/gpu/GPU_vertex_buffer.h @@ -59,6 +59,7 @@ typedef enum { GPU_USAGE_STREAM, GPU_USAGE_STATIC, /* do not keep data in memory */ GPU_USAGE_DYNAMIC, + GPU_USAGE_DEVICE_ONLY, /* Do not do host->device data transfers. */ } GPUUsageType; /** Opaque type hiding blender::gpu::VertBuf. */ @@ -70,6 +71,14 @@ GPUVertBuf *GPU_vertbuf_create_with_format_ex(const GPUVertFormat *, GPUUsageTyp #define GPU_vertbuf_create_with_format(format) \ GPU_vertbuf_create_with_format_ex(format, GPU_USAGE_STATIC) +/** + * (Download and) return a pointer containing the data of a vertex buffer. + * + * Note that the returned pointer is still owned by the driver. To get an + * local copy, use `GPU_vertbuf_unmap` after calling `GPU_vertbuf_read`. + */ +const void *GPU_vertbuf_read(GPUVertBuf *verts); +void *GPU_vertbuf_unmap(const GPUVertBuf *verts, const void *mapped_data); void GPU_vertbuf_clear(GPUVertBuf *verts); void GPU_vertbuf_discard(GPUVertBuf *); @@ -138,6 +147,7 @@ uint GPU_vertbuf_get_vertex_len(const GPUVertBuf *verts); GPUVertBufStatus GPU_vertbuf_get_status(const GPUVertBuf *verts); void GPU_vertbuf_use(GPUVertBuf *); +void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding); /* XXX do not use. */ void GPU_vertbuf_update_sub(GPUVertBuf *verts, uint start, uint len, void *data); diff --git a/source/blender/gpu/GPU_viewport.h b/source/blender/gpu/GPU_viewport.h index 095e17f344e..8b2a14a1a96 100644 --- a/source/blender/gpu/GPU_viewport.h +++ b/source/blender/gpu/GPU_viewport.h @@ -43,7 +43,7 @@ typedef struct GPUViewport GPUViewport; struct GPUFrameBuffer; -/* Contains memory pools information */ +/* Contains memory pools information. */ typedef struct ViewportMemoryPool { struct BLI_memblock *commands; struct BLI_memblock *commands_small; @@ -62,21 +62,21 @@ typedef struct ViewportMemoryPool { uint ubo_len; } ViewportMemoryPool; -/* All FramebufferLists are just the same pointers with different names */ +/* All FramebufferLists are just the same pointers with different names. */ typedef struct FramebufferList { - struct GPUFrameBuffer *framebuffers[0]; + struct GPUFrameBuffer *framebuffers[1]; } FramebufferList; typedef struct TextureList { - struct GPUTexture *textures[0]; + struct GPUTexture *textures[1]; } TextureList; typedef struct PassList { - struct DRWPass *passes[0]; + struct DRWPass *passes[1]; } PassList; typedef struct StorageList { - void *storage[0]; /* custom structs from the engine */ + void *storage[1]; /* Custom structs from the engine. */ } StorageList; typedef struct ViewportEngineData { @@ -90,10 +90,10 @@ typedef struct ViewportEngineData { TextureList *txl_stereo; StorageList *stl_stereo; - /* we may want to put this elsewhere */ + /* We may want to put this elsewhere. */ struct DRWTextStore *text_draw_cache; - /* Profiling data */ + /* Profiling data. */ double init_time; double render_time; double background_time; @@ -141,7 +141,7 @@ void GPU_viewport_size_get(const GPUViewport *viewport, int size[2]); void GPU_viewport_size_set(GPUViewport *viewport, const int size[2]); void GPU_viewport_active_view_set(GPUViewport *viewport, int view); -/* Profiling */ +/* Profiling. */ double *GPU_viewport_cache_time_get(GPUViewport *viewport); void GPU_viewport_tag_update(GPUViewport *viewport); @@ -149,7 +149,7 @@ bool GPU_viewport_do_update(GPUViewport *viewport); GPUTexture *GPU_viewport_color_texture(GPUViewport *viewport, int view); -/* Texture pool */ +/* Texture pool. */ GPUTexture *GPU_viewport_texture_pool_query( GPUViewport *viewport, void *engine, int width, int height, int format); diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh index 04ec82a9213..73792215569 100644 --- a/source/blender/gpu/intern/gpu_backend.hh +++ b/source/blender/gpu/intern/gpu_backend.hh @@ -47,6 +47,7 @@ class GPUBackend { static GPUBackend *get(void); virtual void samplers_update(void) = 0; + virtual void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) = 0; virtual Context *context_alloc(void *ghost_window) = 0; diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index 13d0139e406..43483916236 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -707,7 +707,7 @@ void GPU_pbvh_grid_buffers_update(GPU_PBVH_Buffers *buffers, float fno[3]; short no_short[3]; - /* Note: Clockwise indices ordering, that's why we invert order here. */ + /* NOTE: Clockwise indices ordering, that's why we invert order here. */ normal_quad_v3(fno, co[3], co[2], co[1], co[0]); normal_float_to_short_v3(no_short, fno); @@ -916,7 +916,7 @@ void GPU_pbvh_bmesh_buffers_update(GPU_PBVH_Buffers *buffers, return; } - /* TODO, make mask layer optional for bmesh buffer */ + /* TODO: make mask layer optional for bmesh buffer. */ const int cd_vert_mask_offset = CustomData_get_offset(&bm->vdata, CD_PAINT_MASK); /* Fill vertex buffer */ diff --git a/source/blender/gpu/intern/gpu_capabilities.cc b/source/blender/gpu/intern/gpu_capabilities.cc index d8764502800..c6e9dc210cb 100644 --- a/source/blender/gpu/intern/gpu_capabilities.cc +++ b/source/blender/gpu/intern/gpu_capabilities.cc @@ -82,6 +82,16 @@ int GPU_max_textures(void) return GCaps.max_textures; } +int GPU_max_work_group_count(int index) +{ + return GCaps.max_work_group_count[index]; +} + +int GPU_max_work_group_size(int index) +{ + return GCaps.max_work_group_size[index]; +} + int GPU_max_uniforms_vert(void) { return GCaps.max_uniforms_vert; @@ -148,6 +158,16 @@ bool GPU_use_hq_normals_workaround(void) return GCaps.use_hq_normals_workaround; } +bool GPU_compute_shader_support(void) +{ + return GCaps.compute_shader_support; +} + +bool GPU_shader_storage_buffer_objects_support(void) +{ + return GCaps.shader_storage_buffer_objects_support; +} + bool GPU_shader_image_load_store_support(void) { return GCaps.shader_image_load_store_support; diff --git a/source/blender/gpu/intern/gpu_capabilities_private.hh b/source/blender/gpu/intern/gpu_capabilities_private.hh index 7c1d4590ce8..95cf7fd335d 100644 --- a/source/blender/gpu/intern/gpu_capabilities_private.hh +++ b/source/blender/gpu/intern/gpu_capabilities_private.hh @@ -41,6 +41,8 @@ struct GPUCapabilities { int max_textures_vert = 0; int max_textures_geom = 0; int max_textures_frag = 0; + int max_work_group_count[3] = {0, 0, 0}; + int max_work_group_size[3] = {0, 0, 0}; int max_uniforms_vert = 0; int max_uniforms_frag = 0; int max_batch_indices = 0; @@ -51,6 +53,8 @@ struct GPUCapabilities { const char *(*extension_get)(int); bool mem_stats_support = false; + bool compute_shader_support = false; + bool shader_storage_buffer_objects_support = false; bool shader_image_load_store_support = false; /* OpenGL related workarounds. */ bool mip_render_workaround = false; diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index a2072e504fd..d12cecd129e 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -810,7 +810,7 @@ static char *code_generate_geometry(GPUNodeGraph *graph, } LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - /* TODO let shader choose what to do depending on what the attribute is. */ + /* 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_append(ds, "}\n\n"); @@ -938,9 +938,9 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, return pass; } -static int count_active_texture_sampler(GPUShader *shader, char *source) +static int count_active_texture_sampler(GPUShader *shader, const char *source) { - char *code = source; + const char *code = source; /* Remember this is per stage. */ GSet *sampler_ids = BLI_gset_int_new(__func__); diff --git a/source/blender/gpu/intern/gpu_compute.cc b/source/blender/gpu/intern/gpu_compute.cc new file mode 100644 index 00000000000..7a8ae2acf9a --- /dev/null +++ b/source/blender/gpu/intern/gpu_compute.cc @@ -0,0 +1,41 @@ +/* + * 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 + */ + +#include "GPU_compute.h" + +#include "gpu_backend.hh" + +#ifdef __cplusplus +extern "C" { +#endif + +void GPU_compute_dispatch(GPUShader *shader, + uint groups_x_len, + uint groups_y_len, + uint groups_z_len) +{ + blender::gpu::GPUBackend &gpu_backend = *blender::gpu::GPUBackend::get(); + GPU_shader_bind(shader); + gpu_backend.compute_dispatch(groups_x_len, groups_y_len, groups_z_len); +} + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/intern/gpu_context.cc b/source/blender/gpu/intern/gpu_context.cc index b5a437b46f7..943a6151ced 100644 --- a/source/blender/gpu/intern/gpu_context.cc +++ b/source/blender/gpu/intern/gpu_context.cc @@ -28,7 +28,7 @@ * - free can be called from any thread */ -/* TODO Create cmake option. */ +/* TODO: Create cmake option. */ #define WITH_OPENGL_BACKEND 1 #include "BLI_assert.h" @@ -99,7 +99,7 @@ Context *Context::get() GPUContext *GPU_context_create(void *ghost_window) { if (GPUBackend::get() == nullptr) { - /* TODO move where it make sense. */ + /* TODO: move where it make sense. */ GPU_backend_init(GPU_BACKEND_OPENGL); } @@ -182,7 +182,7 @@ void GPU_backend_init(eGPUBackendType backend_type) void GPU_backend_exit(void) { - /* TODO assert no resource left. Currently UI textures are still not freed in their context + /* TODO: assert no resource left. Currently UI textures are still not freed in their context * correctly. */ delete g_backend; g_backend = nullptr; diff --git a/source/blender/gpu/intern/gpu_index_buffer.cc b/source/blender/gpu/intern/gpu_index_buffer.cc index 65932d2dbf4..be7470e79c1 100644 --- a/source/blender/gpu/intern/gpu_index_buffer.cc +++ b/source/blender/gpu/intern/gpu_index_buffer.cc @@ -25,12 +25,15 @@ #include "MEM_guardedalloc.h" +#include "BLI_math_base.h" #include "BLI_utildefines.h" #include "gpu_backend.hh" #include "gpu_index_buffer_private.hh" +#include <cstring> + #define KEEP_SINGLE_COPY 1 #define RESTART_INDEX 0xFFFFFFFF @@ -50,6 +53,8 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *builder, builder->max_allowed_index = vertex_len - 1; builder->max_index_len = index_len; builder->index_len = 0; // start empty + builder->index_min = UINT32_MAX; + builder->index_max = 0; builder->prim_type = prim_type; builder->data = (uint *)MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data"); } @@ -66,6 +71,22 @@ void GPU_indexbuf_init(GPUIndexBufBuilder *builder, GPU_indexbuf_init_ex(builder, prim_type, prim_len * (uint)verts_per_prim, vertex_len); } +GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len) +{ + GPUIndexBuf *elem_ = GPU_indexbuf_calloc(); + IndexBuf *elem = unwrap(elem_); + elem->init_build_on_device(index_len); + return elem_; +} + +void GPU_indexbuf_join(GPUIndexBufBuilder *builder_to, const GPUIndexBufBuilder *builder_from) +{ + BLI_assert(builder_to->data == builder_from->data); + builder_to->index_len = max_uu(builder_to->index_len, builder_from->index_len); + builder_to->index_min = min_uu(builder_to->index_min, builder_from->index_min); + builder_to->index_max = max_uu(builder_to->index_max, builder_from->index_max); +} + void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *builder, uint v) { #if TRUST_NO_ONE @@ -74,6 +95,8 @@ void GPU_indexbuf_add_generic_vert(GPUIndexBufBuilder *builder, uint v) assert(v <= builder->max_allowed_index); #endif builder->data[builder->index_len++] = v; + builder->index_min = MIN2(builder->index_min, v); + builder->index_max = MAX2(builder->index_max, v); } void GPU_indexbuf_add_primitive_restart(GPUIndexBufBuilder *builder) @@ -132,9 +155,9 @@ void GPU_indexbuf_set_point_vert(GPUIndexBufBuilder *builder, uint elem, uint v1 BLI_assert(builder->prim_type == GPU_PRIM_POINTS); BLI_assert(elem < builder->max_index_len); builder->data[elem++] = v1; - if (builder->index_len < elem) { - builder->index_len = elem; - } + builder->index_min = MIN2(builder->index_min, v1); + builder->index_max = MAX2(builder->index_max, v1); + builder->index_len = MAX2(builder->index_len, elem); } void GPU_indexbuf_set_line_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2) @@ -147,9 +170,9 @@ void GPU_indexbuf_set_line_verts(GPUIndexBufBuilder *builder, uint elem, uint v1 uint idx = elem * 2; builder->data[idx++] = v1; builder->data[idx++] = v2; - if (builder->index_len < idx) { - builder->index_len = idx; - } + builder->index_min = MIN3(builder->index_min, v1, v2); + builder->index_max = MAX3(builder->index_max, v1, v2); + builder->index_len = MAX2(builder->index_len, idx); } void GPU_indexbuf_set_tri_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, uint v2, uint v3) @@ -164,9 +187,10 @@ void GPU_indexbuf_set_tri_verts(GPUIndexBufBuilder *builder, uint elem, uint v1, builder->data[idx++] = v1; builder->data[idx++] = v2; builder->data[idx++] = v3; - if (builder->index_len < idx) { - builder->index_len = idx; - } + + builder->index_min = MIN4(builder->index_min, v1, v2, v3); + builder->index_max = MAX4(builder->index_max, v1, v2, v3); + builder->index_len = MAX2(builder->index_len, idx); } void GPU_indexbuf_set_point_restart(GPUIndexBufBuilder *builder, uint elem) @@ -174,9 +198,7 @@ void GPU_indexbuf_set_point_restart(GPUIndexBufBuilder *builder, uint elem) BLI_assert(builder->prim_type == GPU_PRIM_POINTS); BLI_assert(elem < builder->max_index_len); builder->data[elem++] = RESTART_INDEX; - if (builder->index_len < elem) { - builder->index_len = elem; - } + builder->index_len = MAX2(builder->index_len, elem); } void GPU_indexbuf_set_line_restart(GPUIndexBufBuilder *builder, uint elem) @@ -186,9 +208,7 @@ void GPU_indexbuf_set_line_restart(GPUIndexBufBuilder *builder, uint elem) uint idx = elem * 2; builder->data[idx++] = RESTART_INDEX; builder->data[idx++] = RESTART_INDEX; - if (builder->index_len < idx) { - builder->index_len = idx; - } + builder->index_len = MAX2(builder->index_len, idx); } void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem) @@ -199,9 +219,7 @@ void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem) builder->data[idx++] = RESTART_INDEX; builder->data[idx++] = RESTART_INDEX; builder->data[idx++] = RESTART_INDEX; - if (builder->index_len < idx) { - builder->index_len = idx; - } + builder->index_len = MAX2(builder->index_len, idx); } /** \} */ @@ -219,7 +237,7 @@ IndexBuf::~IndexBuf() } } -void IndexBuf::init(uint indices_len, uint32_t *indices) +void IndexBuf::init(uint indices_len, uint32_t *indices, uint min_index, uint max_index) { is_init_ = true; data_ = indices; @@ -229,8 +247,7 @@ void IndexBuf::init(uint indices_len, uint32_t *indices) #if GPU_TRACK_INDEX_RANGE /* Everything remains 32 bit while building to keep things simple. * Find min/max after, then convert to smallest index type possible. */ - uint min_index, max_index; - uint range = this->index_range(&min_index, &max_index); + uint range = min_index < max_index ? max_index - min_index : 0; /* count the primitive restart index. */ range += 1; @@ -241,6 +258,15 @@ void IndexBuf::init(uint indices_len, uint32_t *indices) #endif } +void IndexBuf::init_build_on_device(uint index_len) +{ + is_init_ = true; + index_start_ = 0; + index_len_ = index_len; + index_type_ = GPU_INDEX_U32; + data_ = nullptr; +} + void IndexBuf::init_subrange(IndexBuf *elem_src, uint start, uint length) { /* We don't support nested subranges. */ @@ -307,6 +333,14 @@ void IndexBuf::squeeze_indices_short(uint min_idx, uint max_idx) } } +uint32_t *IndexBuf::unmap(const uint32_t *mapped_memory) const +{ + size_t size = size_get(); + uint32_t *result = static_cast<uint32_t *>(MEM_mallocN(size, __func__)); + memcpy(result, mapped_memory, size); + return result; +} + } // namespace blender::gpu /** \} */ @@ -339,7 +373,7 @@ void GPU_indexbuf_build_in_place(GPUIndexBufBuilder *builder, GPUIndexBuf *elem) BLI_assert(builder->data != nullptr); /* Transfer data ownership to GPUIndexBuf. * It will be uploaded upon first use. */ - unwrap(elem)->init(builder->index_len, builder->data); + unwrap(elem)->init(builder->index_len, builder->data, builder->index_min, builder->index_max); builder->data = nullptr; } @@ -351,6 +385,16 @@ void GPU_indexbuf_create_subrange_in_place(GPUIndexBuf *elem, unwrap(elem)->init_subrange(unwrap(elem_src), start, length); } +const uint32_t *GPU_indexbuf_read(GPUIndexBuf *elem) +{ + return unwrap(elem)->read(); +} + +uint32_t *GPU_indexbuf_unmap(const GPUIndexBuf *elem, const uint32_t *mapped_buffer) +{ + return unwrap(elem)->unmap(mapped_buffer); +} + void GPU_indexbuf_discard(GPUIndexBuf *elem) { delete unwrap(elem); @@ -366,4 +410,9 @@ int GPU_indexbuf_primitive_len(GPUPrimType prim_type) return indices_per_primitive(prim_type); } +void GPU_indexbuf_bind_as_ssbo(GPUIndexBuf *elem, int binding) +{ + unwrap(elem)->bind_as_ssbo(binding); +} + /** \} */ diff --git a/source/blender/gpu/intern/gpu_index_buffer_private.hh b/source/blender/gpu/intern/gpu_index_buffer_private.hh index 2405db8664a..ed7dd830c8c 100644 --- a/source/blender/gpu/intern/gpu_index_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_index_buffer_private.hh @@ -51,7 +51,7 @@ class IndexBuf { protected: /** Type of indices used inside this buffer. */ GPUIndexBufType index_type_ = GPU_INDEX_U32; - /** Offset in this buffer to the first index to render. Is 0 if not a subrange. */ + /** Offset in this buffer to the first index to render. Is 0 if not a subrange. */ uint32_t index_start_ = 0; /** Number of indices to render. */ uint32_t index_len_ = 0; @@ -73,15 +73,16 @@ class IndexBuf { IndexBuf(){}; virtual ~IndexBuf(); - void init(uint indices_len, uint32_t *indices); + void init(uint indices_len, uint32_t *indices, uint min_index, uint max_index); void init_subrange(IndexBuf *elem_src, uint start, uint length); + void init_build_on_device(uint index_len); uint32_t index_len_get(void) const { return index_len_; } /* Return size in byte of the drawable data buffer range. Actual buffer size might be bigger. */ - size_t size_get(void) + size_t size_get(void) const { return index_len_ * to_bytesize(index_type_); }; @@ -91,6 +92,11 @@ class IndexBuf { return is_init_; }; + virtual void bind_as_ssbo(uint binding) = 0; + + virtual const uint32_t *read() const = 0; + uint32_t *unmap(const uint32_t *mapped_memory) const; + private: inline void squeeze_indices_short(uint min_idx, uint max_idx); inline uint index_range(uint *r_min, uint *r_max); @@ -105,6 +111,10 @@ static inline IndexBuf *unwrap(GPUIndexBuf *indexbuf) { return reinterpret_cast<IndexBuf *>(indexbuf); } +static inline const IndexBuf *unwrap(const GPUIndexBuf *indexbuf) +{ + return reinterpret_cast<const IndexBuf *>(indexbuf); +} static inline int indices_per_primitive(GPUPrimType prim_type) { diff --git a/source/blender/gpu/intern/gpu_material_library.c b/source/blender/gpu/intern/gpu_material_library.c index 175facc0a8d..73a80c62bdc 100644 --- a/source/blender/gpu/intern/gpu_material_library.c +++ b/source/blender/gpu/intern/gpu_material_library.c @@ -47,6 +47,7 @@ extern char datatoc_gpu_shader_material_anisotropic_glsl[]; extern char datatoc_gpu_shader_material_attribute_glsl[]; extern char datatoc_gpu_shader_material_background_glsl[]; extern char datatoc_gpu_shader_material_bevel_glsl[]; +extern char datatoc_gpu_shader_material_wavelength_glsl[]; extern char datatoc_gpu_shader_material_blackbody_glsl[]; extern char datatoc_gpu_shader_material_bright_contrast_glsl[]; extern char datatoc_gpu_shader_material_bump_glsl[]; @@ -191,6 +192,11 @@ static GPUMaterialLibrary gpu_shader_material_bevel_library = { .dependencies = {NULL}, }; +static GPUMaterialLibrary gpu_shader_material_wavelength_library = { + .code = datatoc_gpu_shader_material_wavelength_glsl, + .dependencies = {NULL}, +}; + static GPUMaterialLibrary gpu_shader_material_blackbody_library = { .code = datatoc_gpu_shader_material_blackbody_glsl, .dependencies = {NULL}, @@ -593,6 +599,7 @@ static GPUMaterialLibrary *gpu_material_libraries[] = { &gpu_shader_material_attribute_library, &gpu_shader_material_background_library, &gpu_shader_material_bevel_library, + &gpu_shader_material_wavelength_library, &gpu_shader_material_blackbody_library, &gpu_shader_material_bright_contrast_library, &gpu_shader_material_bump_library, @@ -677,7 +684,7 @@ static GPUMaterialLibrary *gpu_material_libraries[] = { static GHash *FUNCTION_HASH = NULL; -char *gpu_str_skip_token(char *str, char *token, int max) +const char *gpu_str_skip_token(const char *str, char *token, int max) { int len = 0; @@ -745,7 +752,7 @@ static void gpu_parse_material_library(GHash *hash, GPUMaterialLibrary *library) eGPUType type; GPUFunctionQual qual; int i; - char *code = library->code; + const char *code = library->code; while ((code = strstr(code, "void "))) { function = MEM_callocN(sizeof(GPUFunction), "GPUFunction"); diff --git a/source/blender/gpu/intern/gpu_material_library.h b/source/blender/gpu/intern/gpu_material_library.h index da7b1636fa3..782d89d6f2a 100644 --- a/source/blender/gpu/intern/gpu_material_library.h +++ b/source/blender/gpu/intern/gpu_material_library.h @@ -62,5 +62,5 @@ char *gpu_material_library_generate_code(struct GSet *used_libraries, const char /* Code Parsing */ -char *gpu_str_skip_token(char *str, char *token, int max); +const char *gpu_str_skip_token(const char *str, char *token, int max); const char *gpu_data_type_to_string(const eGPUType type); diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc index 569b51a407a..6eb9cb823d5 100644 --- a/source/blender/gpu/intern/gpu_matrix.cc +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -474,11 +474,11 @@ void GPU_matrix_look_at(float eyeX, GPU_matrix_translate_3f(-eyeX, -eyeY, -eyeZ); } -void GPU_matrix_project(const float world[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float win[3]) +void GPU_matrix_project_3fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float win[3]) { float v[4]; @@ -494,6 +494,25 @@ void GPU_matrix_project(const float world[3], win[2] = (v[2] + 1) * 0.5f; } +void GPU_matrix_project_2fv(const float world[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float win[2]) +{ + float v[4]; + + mul_v4_m4v3(v, model, world); + mul_m4_v4(proj, v); + + if (v[3] != 0.0f) { + mul_v2_fl(v, 1.0f / v[3]); + } + + win[0] = view[0] + (view[2] * (v[0] + 1)) * 0.5f; + win[1] = view[1] + (view[3] * (v[1] + 1)) * 0.5f; +} + /** * The same result could be obtained as follows: * @@ -556,9 +575,9 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc, return true; } -void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc, - const float win[3], - float r_world[3]) +void GPU_matrix_unproject_3fv_with_precalc(const struct GPUMatrixUnproject_Precalc *precalc, + const float win[3], + float r_world[3]) { float in[3] = { (win[0] - precalc->view[0]) / precalc->view[2], @@ -569,18 +588,18 @@ void GPU_matrix_unproject_with_precalc(const struct GPUMatrixUnproject_Precalc * mul_v3_m4v3(r_world, precalc->model_inverted, in); } -bool GPU_matrix_unproject(const float win[3], - const float model[4][4], - const float proj[4][4], - const int view[4], - float r_world[3]) +bool GPU_matrix_unproject_3fv(const float win[3], + const float model[4][4], + const float proj[4][4], + const int view[4], + float r_world[3]) { struct GPUMatrixUnproject_Precalc precalc; if (!GPU_matrix_unproject_precalc(&precalc, model, proj, view)) { zero_v3(r_world); return false; } - GPU_matrix_unproject_with_precalc(&precalc, win, r_world); + GPU_matrix_unproject_3fv_with_precalc(&precalc, win, r_world); return true; } diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index b220c60e979..7646cce2a3e 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -174,7 +174,7 @@ static const char *gpu_uniform_set_function_from_type(eNodeSocketDatatype type) case SOCK_RGBA: return "set_rgba"; default: - BLI_assert(!"No gpu function for non-supported eNodeSocketDatatype"); + BLI_assert_msg(0, "No gpu function for non-supported eNodeSocketDatatype"); return NULL; } } @@ -259,7 +259,7 @@ static void gpu_node_output(GPUNode *node, const eGPUType type, GPUNodeLink **li output->link->link_type = GPU_NODE_LINK_OUTPUT; output->link->output = output; - /* note: the caller owns the reference to the link, GPUOutput + /* NOTE: the caller owns the reference to the link, GPUOutput * merely points to it, and if the node is destroyed it will * set that pointer to NULL */ } diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c index 89c94fd3ba5..7fb704c29dd 100644 --- a/source/blender/gpu/intern/gpu_select_pick.c +++ b/source/blender/gpu/intern/gpu_select_pick.c @@ -55,7 +55,7 @@ * SubRectStride */ -/* For looping over a sub-region of a rect, could be moved into 'rct.c'*/ +/* For looping over a sub-region of a rect, could be moved into 'rct.c'. */ typedef struct SubRectStride { uint start; /* start here */ uint span; /* read these */ @@ -230,7 +230,7 @@ typedef struct GPUPickState { /* cache on initialization */ uint (*buffer)[4]; - /* buffer size (stores number of integers, for actual size multiply by sizeof integer)*/ + /* Buffer size (stores number of integers, for actual size multiply by sizeof integer). */ uint bufsize; /* mode of operation */ char mode; @@ -484,7 +484,7 @@ bool gpu_select_pick_load_id(uint id, bool end) GPUFrameBuffer *fb = GPU_framebuffer_active_get(); GPU_framebuffer_read_depth( fb, UNPACK4(ps->gl.clip_readpixels), GPU_DATA_UINT, ps->gl.rect_depth_test->buf); - /* perform initial check since most cases the array remains unchanged */ + /* Perform initial check since most cases the array remains unchanged. */ bool do_pass = false; if (g_pick_state.mode == GPU_SELECT_PICK_ALL) { diff --git a/source/blender/gpu/intern/gpu_select_sample_query.cc b/source/blender/gpu/intern/gpu_select_sample_query.cc index 5d8689c0d6a..fc755568687 100644 --- a/source/blender/gpu/intern/gpu_select_sample_query.cc +++ b/source/blender/gpu/intern/gpu_select_sample_query.cc @@ -48,17 +48,17 @@ using namespace blender; using namespace blender::gpu; struct GPUSelectQueryState { - /* Tracks whether a query has been issued so that gpu_load_id can end the previous one */ + /* Tracks whether a query has been issued so that gpu_load_id can end the previous one. */ bool query_issued; /* GPU queries abstraction. Contains an array of queries. */ QueryPool *queries; /* Array holding the id corresponding id to each query. */ Vector<uint> *ids; - /* cache on initialization */ + /* Cache on initialization. */ uint (*buffer)[4]; - /* buffer size (stores number of integers, for actual size multiply by sizeof integer)*/ + /* Buffer size (stores number of integers, for actual size multiply by sizeof integer). */ uint bufsize; - /* mode of operation */ + /* Mode of operation. */ char mode; uint index; int oldhits; diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index aea27756708..c754a649924 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -23,226 +23,24 @@ #include "MEM_guardedalloc.h" -#include "BLI_dynstr.h" -#include "BLI_math_base.h" -#include "BLI_math_vector.h" -#include "BLI_path_util.h" -#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" - -#include "DNA_space_types.h" #include "GPU_capabilities.h" #include "GPU_matrix.h" #include "GPU_platform.h" -#include "GPU_shader.h" -#include "GPU_texture.h" -#include "GPU_uniform_buffer.h" #include "gpu_backend.hh" #include "gpu_context_private.hh" #include "gpu_shader_private.hh" -#include "CLG_log.h" - extern "C" char datatoc_gpu_shader_colorspace_lib_glsl[]; -static CLG_LogRef LOG = {"gpu.shader"}; - using namespace blender; using namespace blender::gpu; static bool gpu_shader_srgb_uniform_dirty_get(); /* -------------------------------------------------------------------- */ -/** \name Debug functions - * \{ */ - -void Shader::print_log(Span<const char *> sources, char *log, const char *stage, const bool error) -{ - const char line_prefix[] = " | "; - char err_col[] = "\033[31;1m"; - char warn_col[] = "\033[33;1m"; - char info_col[] = "\033[0;2m"; - char reset_col[] = "\033[0;0m"; - char *sources_combined = BLI_string_join_arrayN((const char **)sources.data(), sources.size()); - DynStr *dynstr = BLI_dynstr_new(); - - if (!CLG_color_support_get(&LOG)) { - err_col[0] = warn_col[0] = info_col[0] = reset_col[0] = '\0'; - } - - BLI_dynstr_appendf(dynstr, "\n"); - - 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; - } - } - /* 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 character (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) { - BLI_dynstr_appendf(dynstr, "%s%s%s\n", info_col, line_prefix, reset_col); - } - else if (error_char != last_error_char) { - BLI_dynstr_appendf(dynstr, "%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; - } -/* TODO(fclem) Make this an option to display N lines before error. */ -#if 0 /* Uncomment to print shader file up to the error line to have more context. */ - BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index); - BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line); -#endif - /* 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) { - BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index); - } - else { - BLI_dynstr_appendf(dynstr, line_prefix); - } - BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line); - /* Print char offset. */ - BLI_dynstr_appendf(dynstr, line_prefix); - if (error_char != -1) { - for (int i = 0; i < error_char; i++) { - BLI_dynstr_appendf(dynstr, " "); - } - BLI_dynstr_appendf(dynstr, "^"); - } - BLI_dynstr_appendf(dynstr, "\n"); - } - } - BLI_dynstr_appendf(dynstr, 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) { - BLI_dynstr_appendf(dynstr, "%s%s%s: ", err_col, "Error", info_col); - } - else if (type == 1) { - BLI_dynstr_appendf(dynstr, "%s%s%s: ", warn_col, "Warning", info_col); - } - /* Print the error itself. */ - BLI_dynstr_append(dynstr, info_col); - BLI_dynstr_nappend(dynstr, log_line, (line_end + 1) - log_line); - BLI_dynstr_append(dynstr, reset_col); - /* Continue to next line. */ - log_line = line_end + 1; - last_error_line = error_line; - last_error_char = error_char; - } - MEM_freeN(sources_combined); - - CLG_Severity severity = error ? CLG_SEVERITY_ERROR : CLG_SEVERITY_WARN; - - if (((LOG.type->flag & CLG_FLAG_USE) && (LOG.type->level >= 0)) || - (severity >= CLG_SEVERITY_WARN)) { - const char *_str = BLI_dynstr_get_cstring(dynstr); - CLG_log_str(LOG.type, severity, this->name, stage, _str); - MEM_freeN((void *)_str); - } - - BLI_dynstr_free(dynstr); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Creation / Destruction * \{ */ @@ -290,6 +88,7 @@ static void standard_defines(Vector<const char *> &sources) GPUShader *GPU_shader_create_ex(const char *vertcode, const char *fragcode, const char *geomcode, + const char *computecode, const char *libcode, const char *defines, const eGPUShaderTFBType tf_type, @@ -297,8 +96,10 @@ GPUShader *GPU_shader_create_ex(const char *vertcode, const int tf_count, const char *shname) { - /* At least a vertex shader and a fragment shader are required. */ - BLI_assert((fragcode != nullptr) && (vertcode != nullptr)); + /* At least a vertex shader and a fragment shader are required, or only a compute shader. */ + BLI_assert(((fragcode != nullptr) && (vertcode != nullptr) && (computecode == nullptr)) || + ((fragcode == nullptr) && (vertcode == nullptr) && (geomcode == nullptr) && + (computecode != nullptr))); Shader *shader = GPUBackend::get()->shader_alloc(shname); @@ -349,6 +150,21 @@ GPUShader *GPU_shader_create_ex(const char *vertcode, shader->geometry_shader_from_glsl(sources); } + if (computecode) { + Vector<const char *> sources; + standard_defines(sources); + sources.append("#define GPU_COMPUTE_SHADER\n"); + if (defines) { + sources.append(defines); + } + if (libcode) { + sources.append(libcode); + } + sources.append(computecode); + + shader->compute_shader_from_glsl(sources); + } + if (tf_names != nullptr && tf_count > 0) { BLI_assert(tf_type != GPU_SHADER_TFB_NONE); shader->transform_feedback_names_set(Span<const char *>(tf_names, tf_count), tf_type); @@ -380,8 +196,33 @@ GPUShader *GPU_shader_create(const char *vertcode, const char *defines, const char *shname) { - return GPU_shader_create_ex( - vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, nullptr, 0, shname); + return GPU_shader_create_ex(vertcode, + fragcode, + geomcode, + nullptr, + libcode, + defines, + GPU_SHADER_TFB_NONE, + nullptr, + 0, + shname); +} + +GPUShader *GPU_shader_create_compute(const char *computecode, + const char *libcode, + const char *defines, + const char *shname) +{ + return GPU_shader_create_ex(nullptr, + nullptr, + nullptr, + computecode, + libcode, + defines, + GPU_SHADER_TFB_NONE, + nullptr, + 0, + shname); } GPUShader *GPU_shader_create_from_python(const char *vertcode, @@ -402,6 +243,7 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode, GPUShader *sh = GPU_shader_create_ex(vertcode, fragcode, geomcode, + nullptr, libcode, defines, GPU_SHADER_TFB_NONE, @@ -567,6 +409,13 @@ int GPU_shader_get_builtin_block(GPUShader *shader, int builtin) return interface->ubo_builtin((GPUUniformBlockBuiltin)builtin); } +int GPU_shader_get_ssbo(GPUShader *shader, const char *name) +{ + ShaderInterface *interface = unwrap(shader)->interface; + const ShaderInput *ssbo = interface->ssbo_get(name); + return ssbo ? ssbo->location : -1; +} + /* DEPRECATED. */ int GPU_shader_get_uniform_block(GPUShader *shader, const char *name) { diff --git a/source/blender/gpu/intern/gpu_shader_interface.cc b/source/blender/gpu/intern/gpu_shader_interface.cc index c584c40eca8..ae94112b17b 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.cc +++ b/source/blender/gpu/intern/gpu_shader_interface.cc @@ -80,6 +80,8 @@ void ShaderInterface::debug_print() 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_); + Span<ShaderInput> ssbos = Span<ShaderInput>(inputs_ + attr_len_ + ubo_len_ + uniform_len_, + ssbo_len_); char *name_buf = name_buffer_; const char format[] = " | %.8x : %4d : %s\n"; @@ -117,6 +119,13 @@ void ShaderInterface::debug_print() } } + if (ssbos.size() > 0) { + printf("\n Shader Storage Objects :\n"); + } + for (const ShaderInput &ssbo : ssbos) { + printf(format, ssbo.name_hash, ssbo.binding, name_buf + ssbo.name_offset); + } + printf("\n"); } diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh index aec58544111..ebed7b15170 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.hh +++ b/source/blender/gpu/intern/gpu_shader_interface.hh @@ -60,6 +60,7 @@ class ShaderInterface { uint attr_len_ = 0; uint ubo_len_ = 0; uint uniform_len_ = 0; + uint ssbo_len_ = 0; /** Enabled bind-points that needs to be fed with data. */ uint16_t enabled_attr_mask_ = 0; uint16_t enabled_ubo_mask_ = 0; @@ -99,6 +100,11 @@ class ShaderInterface { return input_lookup(inputs_ + attr_len_ + ubo_len_, uniform_len_, binding); } + inline const ShaderInput *ssbo_get(const char *name) const + { + return input_lookup(inputs_ + attr_len_ + ubo_len_ + uniform_len_, ssbo_len_, name); + } + inline const char *input_name_get(const ShaderInput *input) const { return name_buffer_ + input->name_offset; diff --git a/source/blender/gpu/intern/gpu_shader_log.cc b/source/blender/gpu/intern/gpu_shader_log.cc new file mode 100644 index 00000000000..12459b4b721 --- /dev/null +++ b/source/blender/gpu/intern/gpu_shader_log.cc @@ -0,0 +1,217 @@ +/* + * 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) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_dynstr.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" + +#include "gpu_shader_private.hh" + +#include "GPU_platform.h" + +#include "CLG_log.h" + +static CLG_LogRef LOG = {"gpu.shader"}; + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Debug functions + * \{ */ + +void Shader::print_log(Span<const char *> sources, + char *log, + const char *stage, + const bool error, + GPULogParser *parser) +{ + const char line_prefix[] = " | "; + char err_col[] = "\033[31;1m"; + char warn_col[] = "\033[33;1m"; + char info_col[] = "\033[0;2m"; + char reset_col[] = "\033[0;0m"; + char *sources_combined = BLI_string_join_arrayN((const char **)sources.data(), sources.size()); + DynStr *dynstr = BLI_dynstr_new(); + + if (!CLG_color_support_get(&LOG)) { + err_col[0] = warn_col[0] = info_col[0] = reset_col[0] = '\0'; + } + + BLI_dynstr_appendf(dynstr, "\n"); + + char *log_line = log, *line_end; + + LogCursor previous_location; + + bool found_line_id = false; + while ((line_end = strchr(log_line, '\n'))) { + /* Skip empty lines. */ + if (line_end == log_line) { + log_line++; + continue; + } + + GPULogItem log_item; + log_line = parser->parse_line(log_line, log_item); + + if (log_item.cursor.row == -1) { + found_line_id = false; + } + + const char *src_line = sources_combined; + + /* Separate from previous block. */ + if (previous_location.source != log_item.cursor.source || + previous_location.row != log_item.cursor.row) { + BLI_dynstr_appendf(dynstr, "%s%s%s\n", info_col, line_prefix, reset_col); + } + else if (log_item.cursor.column != previous_location.column) { + BLI_dynstr_appendf(dynstr, "%s\n", line_prefix); + } + /* Print line from the source file that is producing the error. */ + if ((log_item.cursor.row != -1) && (log_item.cursor.row != previous_location.row || + log_item.cursor.column != previous_location.column)) { + const char *src_line_end; + 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 == log_item.cursor.row) { + found_line_id = true; + break; + } +/* TODO(fclem) Make this an option to display N lines before error. */ +#if 0 /* Uncomment to print shader file up to the error line to have more context. */ + BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index); + BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line); +#endif + /* Continue to next line. */ + src_line = src_line_end + 1; + src_line_index++; + } + /* Print error source. */ + if (found_line_id) { + if (log_item.cursor.row != previous_location.row) { + BLI_dynstr_appendf(dynstr, "%5d | ", src_line_index); + } + else { + BLI_dynstr_appendf(dynstr, line_prefix); + } + BLI_dynstr_nappend(dynstr, src_line, (src_line_end + 1) - src_line); + /* Print char offset. */ + BLI_dynstr_appendf(dynstr, line_prefix); + if (log_item.cursor.column != -1) { + for (int i = 0; i < log_item.cursor.column; i++) { + BLI_dynstr_appendf(dynstr, " "); + } + BLI_dynstr_appendf(dynstr, "^"); + } + BLI_dynstr_appendf(dynstr, "\n"); + } + } + BLI_dynstr_appendf(dynstr, line_prefix); + + if (log_item.severity == Severity::Error) { + BLI_dynstr_appendf(dynstr, "%s%s%s: ", err_col, "Error", info_col); + } + else if (log_item.severity == Severity::Error) { + BLI_dynstr_appendf(dynstr, "%s%s%s: ", warn_col, "Warning", info_col); + } + /* Print the error itself. */ + BLI_dynstr_append(dynstr, info_col); + BLI_dynstr_nappend(dynstr, log_line, (line_end + 1) - log_line); + BLI_dynstr_append(dynstr, reset_col); + /* Continue to next line. */ + log_line = line_end + 1; + previous_location = log_item.cursor; + } + MEM_freeN(sources_combined); + + CLG_Severity severity = error ? CLG_SEVERITY_ERROR : CLG_SEVERITY_WARN; + + if (((LOG.type->flag & CLG_FLAG_USE) && (LOG.type->level >= 0)) || + (severity >= CLG_SEVERITY_WARN)) { + const char *_str = BLI_dynstr_get_cstring(dynstr); + CLG_log_str(LOG.type, severity, this->name, stage, _str); + MEM_freeN((void *)_str); + } + + BLI_dynstr_free(dynstr); +} + +char *GPULogParser::skip_severity(char *log_line, + GPULogItem &log_item, + const char *error_msg, + const char *warning_msg) const +{ + if (STREQLEN(log_line, error_msg, strlen(error_msg))) { + log_line += strlen(error_msg); + log_item.severity = Severity::Error; + } + else if (STREQLEN(log_line, warning_msg, strlen(warning_msg))) { + log_line += strlen(warning_msg); + log_item.severity = Severity::Warning; + } + return log_line; +} + +char *GPULogParser::skip_separators(char *log_line, const StringRef separators) const +{ + while (at_any(log_line, separators)) { + log_line++; + } + return log_line; +} + +char *GPULogParser::skip_until(char *log_line, char stop_char) const +{ + char *cursor = log_line; + while (!ELEM(cursor[0], '\n', '\0')) { + if (cursor[0] == stop_char) { + return cursor; + } + cursor++; + } + return log_line; +} + +bool GPULogParser::at_number(const char *log_line) const +{ + return log_line[0] >= '0' && log_line[0] <= '9'; +} + +bool GPULogParser::at_any(const char *log_line, const StringRef chars) const +{ + return chars.find(log_line[0]) != StringRef::not_found; +} + +int GPULogParser::parse_number(const char *log_line, char **r_new_position) const +{ + return (int)strtol(log_line, r_new_position, 10); +} + +/** \} */ + +} // namespace blender::gpu diff --git a/source/blender/gpu/intern/gpu_shader_private.hh b/source/blender/gpu/intern/gpu_shader_private.hh index d9327bbc0f4..65720e457d8 100644 --- a/source/blender/gpu/intern/gpu_shader_private.hh +++ b/source/blender/gpu/intern/gpu_shader_private.hh @@ -21,6 +21,7 @@ #pragma once #include "BLI_span.hh" +#include "BLI_string_ref.hh" #include "GPU_shader.h" #include "gpu_shader_interface.hh" @@ -29,6 +30,8 @@ namespace blender { namespace gpu { +class GPULogParser; + /** * Implementation of shader compilation and uniforms handling. * Base class which is then specialized for each implementation (GL, VK, ...). @@ -49,6 +52,7 @@ class 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 void compute_shader_from_glsl(MutableSpan<const char *> sources) = 0; virtual bool finalize(void) = 0; virtual void transform_feedback_names_set(Span<const char *> name_list, @@ -73,7 +77,11 @@ class Shader { }; protected: - void print_log(Span<const char *> sources, char *log, const char *stage, const bool error); + void print_log(Span<const char *> sources, + char *log, + const char *stage, + const bool error, + GPULogParser *parser); }; /* Syntactic sugar. */ @@ -90,6 +98,41 @@ static inline const Shader *unwrap(const GPUShader *vert) return reinterpret_cast<const Shader *>(vert); } +enum class Severity { + Unknown, + Warning, + Error, +}; + +struct LogCursor { + int source = -1; + int row = -1; + int column = -1; +}; + +struct GPULogItem { + LogCursor cursor; + Severity severity = Severity::Unknown; +}; + +class GPULogParser { + public: + virtual char *parse_line(char *log_line, GPULogItem &log_item) = 0; + + protected: + char *skip_severity(char *log_line, + GPULogItem &log_item, + const char *error_msg, + const char *warning_msg) const; + char *skip_separators(char *log_line, const StringRef separators) const; + char *skip_until(char *log_line, char stop_char) const; + bool at_number(const char *log_line) const; + bool at_any(const char *log_line, const StringRef chars) const; + int parse_number(const char *log_line, char **r_new_position) const; + + MEM_CXX_CLASS_ALLOC_FUNCS("GPULogParser"); +}; + } // namespace gpu } // namespace blender diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc index 5c33066c720..9ee8a8b8d32 100644 --- a/source/blender/gpu/intern/gpu_state.cc +++ b/source/blender/gpu/intern/gpu_state.cc @@ -187,7 +187,7 @@ void GPU_point_size(float size) /* Programmable point size * - shaders set their own point size when enabled * - use GPU_point_size when disabled */ -/* TODO remove and use program point size everywhere */ +/* TODO: remove and use program point size everywhere. */ void GPU_program_point_size(bool enable) { StateManager *stack = Context::get()->state_manager; diff --git a/source/blender/gpu/intern/gpu_state_private.hh b/source/blender/gpu/intern/gpu_state_private.hh index b79350a6506..b96b71a7ac4 100644 --- a/source/blender/gpu/intern/gpu_state_private.hh +++ b/source/blender/gpu/intern/gpu_state_private.hh @@ -96,7 +96,7 @@ inline GPUState operator~(const GPUState &a) union GPUStateMutable { struct { /* Viewport State */ - /** TODO remove */ + /** TODO: remove. */ float depth_range[2]; /** Positive if using program point size. */ /* TODO(fclem): should be passed as uniform to all shaders. */ diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index 997064e82a2..6564cbda694 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -154,7 +154,7 @@ void Texture::attach_to(FrameBuffer *fb, GPUAttachmentType type) return; } } - BLI_assert(!"GPU: Error: Texture: Not enough attachment"); + BLI_assert_msg(0, "GPU: Error: Texture: Not enough attachment"); } void Texture::detach_from(FrameBuffer *fb) @@ -166,7 +166,7 @@ void Texture::detach_from(FrameBuffer *fb) return; } } - BLI_assert(!"GPU: Error: Texture: Framebuffer is not attached"); + BLI_assert_msg(0, "GPU: Error: Texture: Framebuffer is not attached"); } void Texture::update(eGPUDataFormat format, const void *data) @@ -400,7 +400,7 @@ void GPU_texture_update(GPUTexture *tex, eGPUDataFormat data_format, const void } /* Makes data interpretation aware of the source layout. - * Skipping pixels correctly when changing rows when doing partial update.*/ + * Skipping pixels correctly when changing rows when doing partial update. */ void GPU_unpack_row_length_set(uint len) { Context::get()->state_manager->texture_unpack_row_length_set(len); @@ -600,7 +600,7 @@ void GPU_texture_py_reference_set(GPUTexture *tex, void **py_ref) } #endif -/* TODO remove */ +/* TODO: remove. */ int GPU_texture_opengl_bindcode(const GPUTexture *tex) { return reinterpret_cast<const Texture *>(tex)->gl_bindcode_get(); diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh index a8f2e482bdd..2b8a5a5cc12 100644 --- a/source/blender/gpu/intern/gpu_texture_private.hh +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -155,7 +155,7 @@ class Texture { void mip_size_get(int mip, int r_size[3]) const { - /* TODO assert if lvl is below the limit of 1px in each dimension. */ + /* TODO: assert if lvl is below the limit of 1px in each dimension. */ int div = 1 << mip; r_size[0] = max_ii(1, w_ / div); @@ -559,7 +559,7 @@ static inline eGPUTextureFormat to_texture_format(const GPUVertFormat *format) case GPU_COMP_I16: return GPU_RGBA16I; case GPU_COMP_U16: - /* Note: Checking the fetch mode to select the right GPU texture format. This can be + /* NOTE: Checking the fetch mode to select the right GPU texture format. This can be * added to other formats as well. */ switch (format->attrs[0].fetch_mode) { case GPU_FETCH_INT: diff --git a/source/blender/gpu/intern/gpu_uniform_buffer.cc b/source/blender/gpu/intern/gpu_uniform_buffer.cc index 3edb090d81c..3a9269d1753 100644 --- a/source/blender/gpu/intern/gpu_uniform_buffer.cc +++ b/source/blender/gpu/intern/gpu_uniform_buffer.cc @@ -114,11 +114,11 @@ static void buffer_from_list_inputs_sort(ListBase *inputs) if (input->type == GPU_MAT3) { /* Alignment for mat3 is not handled currently, so not supported */ - BLI_assert(!"mat3 not supported in UBO"); + BLI_assert_msg(0, "mat3 not supported in UBO"); continue; } if (input->type > MAX_UBO_GPU_TYPE) { - BLI_assert(!"GPU type not supported in UBO"); + BLI_assert_msg(0, "GPU type not supported in UBO"); continue; } diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.cc b/source/blender/gpu/intern/gpu_vertex_buffer.cc index 09b9eba9f95..7ff68242c17 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer.cc +++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc @@ -28,8 +28,8 @@ #include "gpu_backend.hh" #include "gpu_vertex_format_private.h" -#include "gl_vertex_buffer.hh" /* TODO remove */ -#include "gpu_context_private.hh" /* TODO remove */ +#include "gl_vertex_buffer.hh" /* TODO: remove. */ +#include "gpu_context_private.hh" /* TODO: remove. */ #include "gpu_vertex_buffer_private.hh" @@ -149,6 +149,16 @@ GPUVertBuf *GPU_vertbuf_duplicate(GPUVertBuf *verts_) return wrap(unwrap(verts_)->duplicate()); } +const void *GPU_vertbuf_read(GPUVertBuf *verts) +{ + return unwrap(verts)->read(); +} + +void *GPU_vertbuf_unmap(const GPUVertBuf *verts, const void *mapped_data) +{ + return unwrap(verts)->unmap(mapped_data); +} + /** Same as discard but does not free. */ void GPU_vertbuf_clear(GPUVertBuf *verts) { @@ -219,7 +229,7 @@ void GPU_vertbuf_attr_fill(GPUVertBuf *verts_, uint a_idx, const void *data) GPU_vertbuf_attr_fill_stride(verts_, a_idx, stride, data); } -/** Fills a whole vertex (all attributes). Data must match packed layout. */ +/** Fills a whole vertex (all attributes). Data must match packed layout. */ void GPU_vertbuf_vert_set(GPUVertBuf *verts_, uint v_idx, const void *data) { VertBuf *verts = unwrap(verts_); @@ -277,7 +287,7 @@ void GPU_vertbuf_attr_get_raw_data(GPUVertBuf *verts_, uint a_idx, GPUVertBufRaw /* NOTE: Be careful when using this. The data needs to match the expected format. */ void *GPU_vertbuf_get_data(const GPUVertBuf *verts) { - /* TODO Assert that the format has no padding. */ + /* TODO: Assert that the format has no padding. */ return unwrap(verts)->data; } @@ -286,7 +296,7 @@ void *GPU_vertbuf_get_data(const GPUVertBuf *verts) void *GPU_vertbuf_steal_data(GPUVertBuf *verts_) { VertBuf *verts = unwrap(verts_); - /* TODO Assert that the format has no padding. */ + /* TODO: Assert that the format has no padding. */ BLI_assert(verts->data); void *data = verts->data; verts->data = nullptr; @@ -324,6 +334,11 @@ void GPU_vertbuf_use(GPUVertBuf *verts) unwrap(verts)->upload(); } +void GPU_vertbuf_bind_as_ssbo(struct GPUVertBuf *verts, int binding) +{ + unwrap(verts)->bind_as_ssbo(binding); +} + /* XXX this is just a wrapper for the use of the Hair refine workaround. * To be used with GPU_vertbuf_use(). */ void GPU_vertbuf_update_sub(GPUVertBuf *verts, uint start, uint len, void *data) diff --git a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh index 67a09f6f83c..9531c2c1a5f 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer_private.hh +++ b/source/blender/gpu/intern/gpu_vertex_buffer_private.hh @@ -66,6 +66,7 @@ class VertBuf { void allocate(uint vert_len); void resize(uint vert_len); void upload(void); + virtual void bind_as_ssbo(uint binding) = 0; VertBuf *duplicate(void); @@ -96,6 +97,8 @@ class VertBuf { } virtual void update_sub(uint start, uint len, void *data) = 0; + virtual const void *read() const = 0; + virtual void *unmap(const void *mapped_data) const = 0; protected: virtual void acquire_data(void) = 0; diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc index 8498da11507..78a119bcec8 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.cc +++ b/source/blender/gpu/intern/gpu_vertex_format.cc @@ -284,7 +284,7 @@ void GPU_vertformat_safe_attr_name(const char *attr_name, char *r_safe_name, uin data[i] = attr_name[i]; } /* We use a hash to identify each data layer based on its name. - * NOTE: This is still prone to hash collision but the risks are very low.*/ + * NOTE: This is still prone to hash collision but the risks are very low. */ /* Start hashing after the first 2 chars. */ *(uint *)&data[4] = BLI_ghashutil_strhash_p_murmur(attr_name + 4); } @@ -306,7 +306,8 @@ void GPU_vertformat_safe_attr_name(const char *attr_name, char *r_safe_name, uin #endif } -/* Make attribute layout non-interleaved. +/** + * Make attribute layout non-interleaved. * Warning! This does not change data layout! * Use direct buffer access to fill the data. * This is for advanced usage. @@ -314,11 +315,11 @@ void GPU_vertformat_safe_attr_name(const char *attr_name, char *r_safe_name, uin * De-interleaved data means all attribute data for each attribute * is stored continuously like this: * 000011112222 - * instead of : + * instead of: * 012012012012 * - * Note this is per attribute de-interleaving, NOT per component. - * */ + * \note This is per attribute de-interleaving, NOT per component. + */ void GPU_vertformat_deinterleave(GPUVertFormat *format) { /* Ideally we should change the stride and offset here. This would allow diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index c118145ebd6..96bf1ec40b0 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -248,7 +248,7 @@ void *GPU_viewport_engine_data_create(GPUViewport *viewport, void *engine_type) } } - BLI_assert(!"Too many draw engines enabled at the same time"); + BLI_assert_msg(0, "Too many draw engines enabled at the same time"); return NULL; } @@ -284,7 +284,7 @@ static void gpu_viewport_engines_data_free(GPUViewport *viewport) MEM_freeN(data); - /* Mark as unused*/ + /* Mark as unused. */ viewport->engine_data[i].handle = NULL; } diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index e7d67be6d0e..f90c37e5c50 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -38,6 +38,17 @@ namespace blender::gpu { /** \name Platform * \{ */ +static bool match_renderer(StringRef renderer, const Vector<std::string> &items) +{ + for (const std::string &item : items) { + const std::string wrapped = " " + item + " "; + if (renderer.endswith(item) || renderer.find(wrapped) != StringRef::not_found) { + return true; + } + } + return false; +} + void GLBackend::platform_init() { BLI_assert(!GPG.initialized); @@ -288,14 +299,25 @@ static void detect_workarounds() * The work around uses `GPU_RGBA16I`. */ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL)) { - if (strstr(renderer, " RX 460 ") || strstr(renderer, " RX 470 ") || - strstr(renderer, " RX 480 ") || strstr(renderer, " RX 490 ") || - strstr(renderer, " RX 560 ") || strstr(renderer, " RX 560X ") || - strstr(renderer, " RX 570 ") || strstr(renderer, " RX 580 ") || - strstr(renderer, " RX 580X ") || strstr(renderer, " RX 590 ") || - strstr(renderer, " RX550/550 ") || strstr(renderer, " (TM) 520 ") || - strstr(renderer, " (TM) 530 ") || strstr(renderer, " R5 ") || strstr(renderer, " R7 ") || - strstr(renderer, " R9 ")) { + const Vector<std::string> matches = {"RX 460", + "RX 470", + "RX 480", + "RX 490", + "RX 560", + "RX 560X", + "RX 570", + "RX 580", + "RX 580X", + "RX 590", + "RX550/550", + "(TM) 520", + "(TM) 530", + "(TM) 535", + "R5", + "R7", + "R9"}; + + if (match_renderer(renderer, matches)) { GCaps.use_hq_normals_workaround = true; } } @@ -343,8 +365,8 @@ static void detect_workarounds() (strstr(version, "Build 20.19.15.4285"))) { GCaps.use_main_context_workaround = true; } - /* See T70187: merging vertices fail. This has been tested from 18.2.2 till 19.3.0~dev of the - * Mesa driver */ + /* See T70187: merging vertices fail. This has been tested from `18.2.2` till `19.3.0~dev` + * of the Mesa driver */ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE) && (strstr(version, "Mesa 18.") || strstr(version, "Mesa 19.0") || strstr(version, "Mesa 19.1") || strstr(version, "Mesa 19.2"))) { @@ -437,6 +459,16 @@ void GLBackend::capabilities_init() GCaps.mem_stats_support = GLEW_NVX_gpu_memory_info || GLEW_ATI_meminfo; GCaps.shader_image_load_store_support = GLEW_ARB_shader_image_load_store; + GCaps.compute_shader_support = GLEW_ARB_compute_shader; + if (GCaps.compute_shader_support) { + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &GCaps.max_work_group_count[0]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &GCaps.max_work_group_count[1]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &GCaps.max_work_group_count[2]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0, &GCaps.max_work_group_size[0]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1, &GCaps.max_work_group_size[1]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2, &GCaps.max_work_group_size[2]); + } + GCaps.shader_storage_buffer_objects_support = GLEW_ARB_shader_storage_buffer_object; /* GL specific capabilities. */ glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &GLContext::max_texture_3d_size); glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &GLContext::max_cubemap_size); diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh index 231e5811b45..e9dcdffced0 100644 --- a/source/blender/gpu/opengl/gl_backend.hh +++ b/source/blender/gpu/opengl/gl_backend.hh @@ -28,6 +28,7 @@ #include "BLI_vector.hh" #include "gl_batch.hh" +#include "gl_compute.hh" #include "gl_context.hh" #include "gl_drawlist.hh" #include "gl_framebuffer.hh" @@ -126,6 +127,12 @@ class GLBackend : public GPUBackend { return shared_orphan_list_; }; + void compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) override + { + GLContext::get()->state_manager_active_get()->apply_state(); + GLCompute::dispatch(groups_x_len, groups_y_len, groups_z_len); + } + private: static void platform_init(void); static void platform_exit(void); diff --git a/source/blender/gpu/opengl/gl_compute.cc b/source/blender/gpu/opengl/gl_compute.cc new file mode 100644 index 00000000000..fa8317dde4a --- /dev/null +++ b/source/blender/gpu/opengl/gl_compute.cc @@ -0,0 +1,35 @@ +/* + * 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 + */ + +#include "gl_compute.hh" + +#include "gl_debug.hh" + +#include "glew-mx.h" + +namespace blender::gpu { + +void GLCompute::dispatch(int group_x_len, int group_y_len, int group_z_len) +{ + glDispatchCompute(group_x_len, group_y_len, group_z_len); + debug::check_gl_error("Dispatch Compute"); +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_compute.hh b/source/blender/gpu/opengl/gl_compute.hh new file mode 100644 index 00000000000..2fd918ddd10 --- /dev/null +++ b/source/blender/gpu/opengl/gl_compute.hh @@ -0,0 +1,30 @@ +/* + * 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 + +namespace blender::gpu { + +class GLCompute { + public: + static void dispatch(int group_x_len, int group_y_len, int group_z_len); +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_context.cc b/source/blender/gpu/opengl/gl_context.cc index 6c9c6e10774..23654cb96f3 100644 --- a/source/blender/gpu/opengl/gl_context.cc +++ b/source/blender/gpu/opengl/gl_context.cc @@ -39,7 +39,7 @@ #include "gl_state.hh" #include "gl_uniform_buffer.hh" -#include "gl_backend.hh" /* TODO remove */ +#include "gl_backend.hh" /* TODO: remove. */ #include "gl_context.hh" using namespace blender; diff --git a/source/blender/gpu/opengl/gl_debug.cc b/source/blender/gpu/opengl/gl_debug.cc index ac42a950945..3e259235515 100644 --- a/source/blender/gpu/opengl/gl_debug.cc +++ b/source/blender/gpu/opengl/gl_debug.cc @@ -81,9 +81,11 @@ static void APIENTRY debug_callback(GLenum UNUSED(source), return; } - if (TRIM_NVIDIA_BUFFER_INFO && - GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_OFFICIAL) && - STRPREFIX(message, "Buffer detailed info")) { + /* NOTE: callback function can be triggered during before the platform is initialized. + * In this case invoking `GPU_type_matches` would fail and + * therefore the message is checked before the platform matching. */ + if (TRIM_NVIDIA_BUFFER_INFO && STRPREFIX(message, "Buffer detailed info") && + GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_OFFICIAL)) { /** Suppress buffer infos flooding the output. */ return; } diff --git a/source/blender/gpu/opengl/gl_drawlist.hh b/source/blender/gpu/opengl/gl_drawlist.hh index db4b9c03c3c..6f80fdd5a8a 100644 --- a/source/blender/gpu/opengl/gl_drawlist.hh +++ b/source/blender/gpu/opengl/gl_drawlist.hh @@ -72,7 +72,7 @@ class GLDrawList : public DrawList { GLuint buffer_id_; /** Length of whole the buffer (in byte). */ GLsizeiptr buffer_size_; - /** Offset of data_ inside the whole buffer (in byte). */ + /** Offset of `data_` inside the whole buffer (in byte). */ GLintptr data_offset_; /** To free the buffer_id_. */ diff --git a/source/blender/gpu/opengl/gl_framebuffer.cc b/source/blender/gpu/opengl/gl_framebuffer.cc index 920dac407d7..8da114d9270 100644 --- a/source/blender/gpu/opengl/gl_framebuffer.cc +++ b/source/blender/gpu/opengl/gl_framebuffer.cc @@ -86,7 +86,7 @@ GLFrameBuffer::~GLFrameBuffer() /* Restore default frame-buffer if this frame-buffer was bound. */ if (context_->active_fb == this && context_->back_left != this) { /* If this assert triggers it means the frame-buffer is being freed while in use by another - * context which, by the way, is TOTALLY UNSAFE!!! */ + * context which, by the way, is TOTALLY UNSAFE! */ BLI_assert(context_ == Context::get()); GPU_framebuffer_restore(); } @@ -268,7 +268,7 @@ void GLFrameBuffer::bind(bool enabled_srgb) } if (context_ != GLContext::get()) { - BLI_assert(!"Trying to use the same frame-buffer in multiple context"); + BLI_assert_msg(0, "Trying to use the same frame-buffer in multiple context"); return; } @@ -379,7 +379,7 @@ void GLFrameBuffer::clear_attachment(GPUAttachmentType type, glClearBufferfv(GL_DEPTH, 0, &depth); } else { - BLI_assert(!"Unhandled data format"); + BLI_assert_msg(0, "Unhandled data format"); } } else { @@ -395,7 +395,7 @@ void GLFrameBuffer::clear_attachment(GPUAttachmentType type, glClearBufferiv(GL_COLOR, slot, (GLint *)clear_value); break; default: - BLI_assert(!"Unhandled data format"); + BLI_assert_msg(0, "Unhandled data format"); break; } } diff --git a/source/blender/gpu/opengl/gl_immediate.cc b/source/blender/gpu/opengl/gl_immediate.cc index 63e3162944d..2f7fa5a78fc 100644 --- a/source/blender/gpu/opengl/gl_immediate.cc +++ b/source/blender/gpu/opengl/gl_immediate.cc @@ -155,7 +155,7 @@ void GLImmediate::end() GLContext::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. */ + * 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); diff --git a/source/blender/gpu/opengl/gl_index_buffer.cc b/source/blender/gpu/opengl/gl_index_buffer.cc index e2c18c5d0b9..e305f765ad9 100644 --- a/source/blender/gpu/opengl/gl_index_buffer.cc +++ b/source/blender/gpu/opengl/gl_index_buffer.cc @@ -40,17 +40,14 @@ void GLIndexBuf::bind() return; } - if (ibo_id_ == 0) { + const bool allocate_on_device = ibo_id_ == 0; + if (allocate_on_device) { glGenBuffers(1, &ibo_id_); - - if (data_ == nullptr) { - debug::raise_gl_error("Trying to use Index Buffer but the buffer contains no data"); - } } glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_id_); - if (data_ != nullptr) { + if (data_ != nullptr || allocate_on_device) { size_t size = this->size_get(); /* Sends data to GPU. */ glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, data_, GL_STATIC_DRAW); @@ -59,4 +56,29 @@ void GLIndexBuf::bind() } } +void GLIndexBuf::bind_as_ssbo(uint binding) +{ + bind(); + BLI_assert(ibo_id_ != 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, ibo_id_); +} + +const uint32_t *GLIndexBuf::read() const +{ + BLI_assert(is_active()); + void *data = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_READ_ONLY); + uint32_t *result = static_cast<uint32_t *>(data); + return result; +} + +bool GLIndexBuf::is_active() const +{ + if (!ibo_id_) { + return false; + } + int active_ibo_id = 0; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &active_ibo_id); + return ibo_id_ == active_ibo_id; +} + } // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_index_buffer.hh b/source/blender/gpu/opengl/gl_index_buffer.hh index b84934bb77f..0dbdaa6d398 100644 --- a/source/blender/gpu/opengl/gl_index_buffer.hh +++ b/source/blender/gpu/opengl/gl_index_buffer.hh @@ -34,6 +34,7 @@ namespace blender::gpu { class GLIndexBuf : public IndexBuf { friend class GLBatch; friend class GLDrawList; + friend class GLShader; /* For compute shaders. */ private: GLuint ibo_id_ = 0; @@ -42,6 +43,9 @@ class GLIndexBuf : public IndexBuf { ~GLIndexBuf(); void bind(void); + void bind_as_ssbo(uint binding) override; + + const uint32_t *read() const override; void *offset_ptr(uint additional_vertex_offset) const { @@ -57,6 +61,9 @@ class GLIndexBuf : public IndexBuf { return (index_type_ == GPU_INDEX_U16) ? 0xFFFFu : 0xFFFFFFFFu; } + private: + bool is_active() const; + MEM_CXX_CLASS_ALLOC_FUNCS("GLIndexBuf") }; diff --git a/source/blender/gpu/opengl/gl_query.cc b/source/blender/gpu/opengl/gl_query.cc index 8a42719c665..da9770b4cc1 100644 --- a/source/blender/gpu/opengl/gl_query.cc +++ b/source/blender/gpu/opengl/gl_query.cc @@ -41,7 +41,7 @@ void GLQueryPool::init(GPUQueryType type) query_issued_ = 0; } -#if 0 /* TODO to avoid realloc of permanent query pool. */ +#if 0 /* TODO: to avoid realloc of permanent query pool. */ void GLQueryPool::reset(GPUQueryType type) { initialized_ = false; @@ -50,7 +50,7 @@ void GLQueryPool::reset(GPUQueryType type) void GLQueryPool::begin_query() { - /* TODO add assert about expected usage. */ + /* TODO: add assert about expected usage. */ while (query_issued_ >= query_ids_.size()) { int64_t prev_size = query_ids_.size(); query_ids_.resize(prev_size + QUERY_CHUNCK_LEN); @@ -61,7 +61,7 @@ void GLQueryPool::begin_query() void GLQueryPool::end_query() { - /* TODO add assert about expected usage. */ + /* TODO: add assert about expected usage. */ glEndQuery(gl_type_); } @@ -70,7 +70,7 @@ void GLQueryPool::get_occlusion_result(MutableSpan<uint32_t> r_values) BLI_assert(r_values.size() == query_issued_); for (int i = 0; i < query_issued_; i++) { - /* Note: This is a sync point. */ + /* NOTE: This is a sync point. */ glGetQueryObjectuiv(query_ids_[i], GL_QUERY_RESULT, &r_values[i]); } } diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index dd08a67517e..66a1bd5ceb7 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -26,6 +26,7 @@ #include "BLI_string.h" #include "BLI_vector.hh" +#include "GPU_capabilities.h" #include "GPU_platform.h" #include "gl_backend.hh" @@ -63,6 +64,7 @@ GLShader::~GLShader() glDeleteShader(vert_shader_); glDeleteShader(geom_shader_); glDeleteShader(frag_shader_); + glDeleteShader(compute_shader_); glDeleteProgram(shader_program_); } @@ -72,7 +74,7 @@ GLShader::~GLShader() /** \name Shader stage creation * \{ */ -char *GLShader::glsl_patch_get() +static char *glsl_patch_default_get() { /** Used for shader patching. Init once. */ static char patch[512] = "\0"; @@ -111,6 +113,30 @@ char *GLShader::glsl_patch_get() return patch; } +static char *glsl_patch_compute_get() +{ + /** 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 430\n"); + STR_CONCAT(patch, slen, "#extension GL_ARB_compute_shader :enable\n"); + BLI_assert(slen < sizeof(patch)); + return patch; +} + +char *GLShader::glsl_patch_get(GLenum gl_stage) +{ + if (gl_stage == GL_COMPUTE_SHADER) { + return glsl_patch_compute_get(); + } + return glsl_patch_default_get(); +} + /* Create, compile and attach the shader stage to the shader program. */ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources) { @@ -121,7 +147,7 @@ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *> } /* Patch the shader code using the first source slot. */ - sources[0] = glsl_patch_get(); + sources[0] = glsl_patch_get(gl_stage); glShaderSource(shader, sources.size(), sources.data(), nullptr); glCompileShader(shader); @@ -132,15 +158,19 @@ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *> char log[5000] = ""; glGetShaderInfoLog(shader, sizeof(log), nullptr, log); if (log[0] != '\0') { + GLLogParser parser; switch (gl_stage) { case GL_VERTEX_SHADER: - this->print_log(sources, log, "VertShader", !status); + this->print_log(sources, log, "VertShader", !status, &parser); break; case GL_GEOMETRY_SHADER: - this->print_log(sources, log, "GeomShader", !status); + this->print_log(sources, log, "GeomShader", !status, &parser); break; case GL_FRAGMENT_SHADER: - this->print_log(sources, log, "FragShader", !status); + this->print_log(sources, log, "FragShader", !status, &parser); + break; + case GL_COMPUTE_SHADER: + this->print_log(sources, log, "ComputeShader", !status, &parser); break; } } @@ -172,6 +202,11 @@ void GLShader::fragment_shader_from_glsl(MutableSpan<const char *> sources) frag_shader_ = this->create_shader_stage(GL_FRAGMENT_SHADER, sources); } +void GLShader::compute_shader_from_glsl(MutableSpan<const char *> sources) +{ + compute_shader_ = this->create_shader_stage(GL_COMPUTE_SHADER, sources); +} + bool GLShader::finalize() { if (compilation_failed_) { @@ -186,7 +221,8 @@ bool GLShader::finalize() char log[5000]; glGetProgramInfoLog(shader_program_, sizeof(log), nullptr, log); Span<const char *> sources; - this->print_log(sources, log, "Linking", true); + GLLogParser parser; + this->print_log(sources, log, "Linking", true, &parser); return false; } diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh index 152eb2f068a..770bc29747e 100644 --- a/source/blender/gpu/opengl/gl_shader.hh +++ b/source/blender/gpu/opengl/gl_shader.hh @@ -43,6 +43,7 @@ class GLShader : public Shader { GLuint vert_shader_ = 0; GLuint geom_shader_ = 0; GLuint frag_shader_ = 0; + GLuint compute_shader_ = 0; /** True if any shader failed to compile. */ bool compilation_failed_ = false; @@ -56,6 +57,7 @@ class GLShader : public Shader { 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; + void compute_shader_from_glsl(MutableSpan<const char *> sources) override; bool finalize(void) override; void transform_feedback_names_set(Span<const char *> name_list, @@ -75,12 +77,23 @@ class GLShader : public Shader { int program_handle_get(void) const override; private: - char *glsl_patch_get(void); + char *glsl_patch_get(GLenum gl_stage); GLuint create_shader_stage(GLenum gl_stage, MutableSpan<const char *> sources); MEM_CXX_CLASS_ALLOC_FUNCS("GLShader"); }; +class GLLogParser : public GPULogParser { + public: + char *parse_line(char *log_line, GPULogItem &log_item) override; + + protected: + char *skip_severity_prefix(char *log_line, GPULogItem &log_item); + char *skip_severity_keyword(char *log_line, GPULogItem &log_item); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLLogParser"); +}; + } // namespace gpu } // namespace blender diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc index 5870c645bf4..9cf072b2e8a 100644 --- a/source/blender/gpu/opengl/gl_shader_interface.cc +++ b/source/blender/gpu/opengl/gl_shader_interface.cc @@ -29,6 +29,8 @@ #include "gl_shader_interface.hh" +#include "GPU_capabilities.h" + namespace blender::gpu { /* -------------------------------------------------------------------- */ @@ -125,6 +127,18 @@ static inline int image_binding(int32_t program, return -1; } } + +static inline int ssbo_binding(int32_t program, uint32_t ssbo_index) +{ + GLint binding = -1; + GLenum property = GL_BUFFER_BINDING; + GLint values_written = 0; + glGetProgramResourceiv( + program, GL_SHADER_STORAGE_BLOCK, ssbo_index, 1, &property, 1, &values_written, &binding); + + return binding; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -149,6 +163,13 @@ GLShaderInterface::GLShaderInterface(GLuint program) glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len); uniform_len = active_uniform_len; + GLint max_ssbo_name_len = 0, ssbo_len = 0; + if (GPU_shader_storage_buffer_objects_support()) { + glGetProgramInterfaceiv(program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &ssbo_len); + glGetProgramInterfaceiv( + program, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &max_ssbo_name_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 @@ -162,6 +183,9 @@ GLShaderInterface::GLShaderInterface(GLuint program) if (uniform_len > 0 && max_uniform_name_len == 0) { max_uniform_name_len = 256; } + if (ssbo_len > 0 && max_ssbo_name_len == 0) { + max_ssbo_name_len = 256; + } /* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before * allocating the uniform array. */ @@ -186,11 +210,12 @@ GLShaderInterface::GLShaderInterface(GLuint program) } MEM_freeN(ubo_uni_ids); - int input_tot_len = attr_len + ubo_len + uniform_len; + int input_tot_len = attr_len + ubo_len + uniform_len + ssbo_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; + uniform_len * max_uniform_name_len + + ssbo_len * max_ssbo_name_len; name_buffer_ = (char *)MEM_mallocN(name_buffer_len, "name_buffer"); uint32_t name_buffer_offset = 0; @@ -257,6 +282,22 @@ GLShaderInterface::GLShaderInterface(GLuint program) } } + /* SSBOs */ + for (int i = 0; i < ssbo_len; i++) { + char *name = name_buffer_ + name_buffer_offset; + GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; + GLsizei name_len = 0; + glGetProgramResourceName( + program, GL_SHADER_STORAGE_BLOCK, i, remaining_buffer, &name_len, name); + + const GLint binding = ssbo_binding(program, i); + + ShaderInput *input = &inputs_[attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_++]; + input->binding = input->location = binding; + + name_buffer_offset += this->set_input_name(input, name, name_len); + } + /* Builtin Uniforms */ for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) { GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int); diff --git a/source/blender/gpu/opengl/gl_shader_log.cc b/source/blender/gpu/opengl/gl_shader_log.cc new file mode 100644 index 00000000000..174cc63ad81 --- /dev/null +++ b/source/blender/gpu/opengl/gl_shader_log.cc @@ -0,0 +1,87 @@ +/* + * 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) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#include "gl_shader.hh" + +#include "GPU_platform.h" + +namespace blender::gpu { + +char *GLLogParser::parse_line(char *log_line, GPULogItem &log_item) +{ + /* Skip ERROR: or WARNING:. */ + log_line = skip_severity_prefix(log_line, log_item); + log_line = skip_separators(log_line, "(: "); + + /* Parse error line & char numbers. */ + if (at_number(log_line)) { + char *error_line_number_end; + log_item.cursor.row = parse_number(log_line, &error_line_number_end); + /* Try to fetch the error character (not always available). */ + if (at_any(error_line_number_end, "(:") && at_number(&error_line_number_end[1])) { + log_item.cursor.column = parse_number(error_line_number_end + 1, &log_line); + } + else { + log_line = error_line_number_end; + } + /* There can be a 3rd number (case of mesa driver). */ + if (at_any(log_line, "(:") && at_number(&log_line[1])) { + log_item.cursor.source = log_item.cursor.row; + log_item.cursor.row = log_item.cursor.column; + log_item.cursor.column = parse_number(log_line + 1, &error_line_number_end); + log_line = error_line_number_end; + } + } + + if ((log_item.cursor.row != -1) && (log_item.cursor.column != -1)) { + 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 */ + log_item.cursor.row = log_item.cursor.column; + log_item.cursor.column = -1; + } + else { + /* line:char */ + } + } + + log_line = skip_separators(log_line, ":) "); + + /* Skip to message. Avoid redundant info. */ + log_line = skip_severity_keyword(log_line, log_item); + log_line = skip_separators(log_line, ":) "); + + return log_line; +} + +char *GLLogParser::skip_severity_prefix(char *log_line, GPULogItem &log_item) +{ + return skip_severity(log_line, log_item, "ERROR", "WARNING"); +} + +char *GLLogParser::skip_severity_keyword(char *log_line, GPULogItem &log_item) +{ + return skip_severity(log_line, log_item, "error", "warning"); +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_state.cc b/source/blender/gpu/opengl/gl_state.cc index b837eae4871..1106e3dab50 100644 --- a/source/blender/gpu/opengl/gl_state.cc +++ b/source/blender/gpu/opengl/gl_state.cc @@ -131,7 +131,7 @@ void GLStateManager::set_state(const GPUState &state) set_shadow_bias(state.shadow_bias); } - /* TODO remove */ + /* TODO: remove. */ if (changed.polygon_smooth) { if (state.polygon_smooth) { glEnable(GL_POLYGON_SMOOTH); @@ -156,7 +156,7 @@ void GLStateManager::set_mutable_state(const GPUStateMutable &state) { GPUStateMutable changed = state ^ current_mutable_; - /* TODO remove, should be uniform. */ + /* TODO: remove, should be uniform. */ if (float_as_uint(changed.point_size) != 0) { if (state.point_size > 0.0f) { glEnable(GL_PROGRAM_POINT_SIZE); @@ -168,12 +168,12 @@ void GLStateManager::set_mutable_state(const GPUStateMutable &state) } if (changed.line_width != 0) { - /* TODO remove, should use wide line shader. */ + /* 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. */ + /* TODO: remove, should modify the projection matrix instead. */ glDepthRange(UNPACK2(state.depth_range)); } diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh index 651c3c22afa..3b4b40b1d10 100644 --- a/source/blender/gpu/opengl/gl_state.hh +++ b/source/blender/gpu/opengl/gl_state.hh @@ -121,6 +121,9 @@ static inline GLbitfield to_gl(eGPUBarrier barrier_bits) if (barrier_bits & GPU_BARRIER_TEXTURE_FETCH) { barrier |= GL_TEXTURE_FETCH_BARRIER_BIT; } + if (barrier_bits & GPU_BARRIER_SHADER_STORAGE) { + barrier |= GL_SHADER_STORAGE_BARRIER_BIT; + } return barrier; } diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc index b65686165d9..db1fda63c28 100644 --- a/source/blender/gpu/opengl/gl_texture.cc +++ b/source/blender/gpu/opengl/gl_texture.cc @@ -32,7 +32,7 @@ #include "gl_backend.hh" #include "gl_debug.hh" #include "gl_state.hh" -#include "gpu_vertex_buffer_private.hh" /* TODO should be `gl_vertex_buffer.hh` */ +#include "gpu_vertex_buffer_private.hh" /* TODO: should be `gl_vertex_buffer.hh`. */ #include "gl_texture.hh" @@ -79,7 +79,7 @@ bool GLTexture::init_internal() target_ = to_gl_target(type_); - /* We need to bind once to define the texture type. */ + /* We need to bind once to define the texture type. */ GLContext::state_manager_active_get()->texture_bind_temp(this); if (!this->proxy_check(0)) { @@ -106,7 +106,7 @@ bool GLTexture::init_internal(GPUVertBuf *vbo) GLVertBuf *gl_vbo = static_cast<GLVertBuf *>(unwrap(vbo)); target_ = to_gl_target(type_); - /* We need to bind once to define the texture type. */ + /* We need to bind once to define the texture type. */ GLContext::state_manager_active_get()->texture_bind_temp(this); GLenum internal_format = to_gl_internal_format(format_); @@ -347,7 +347,7 @@ void GLTexture::copy_to(Texture *dst_) BLI_assert((dst->w_ == src->w_) && (dst->h_ == src->h_) && (dst->d_ == src->d_)); BLI_assert(dst->format_ == src->format_); BLI_assert(dst->type_ == src->type_); - /* TODO support array / 3D textures. */ + /* TODO: support array / 3D textures. */ BLI_assert(dst->d_ == 0); if (GLContext::copy_image_support) { @@ -368,7 +368,7 @@ void GLTexture::copy_to(Texture *dst_) void *GLTexture::read(int mip, eGPUDataFormat type) { BLI_assert(!(format_flag_ & GPU_FORMAT_COMPRESSED)); - BLI_assert(mip <= mipmaps_); + BLI_assert(mip <= mipmaps_ || mip == 0); BLI_assert(validate_data_format(format_, type)); /* NOTE: mip_size_get() won't override any dimension that is equal to 0. */ diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh index 9da27056269..dc048ea05c0 100644 --- a/source/blender/gpu/opengl/gl_texture.hh +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -44,7 +44,7 @@ class GLTexture : public Texture { /** All samplers states. */ static GLuint samplers_[GPU_SAMPLER_MAX]; - /** Target to bind the texture to (GL_TEXTURE_1D, GL_TEXTURE_2D, etc...)*/ + /** Target to bind the texture to (#GL_TEXTURE_1D, #GL_TEXTURE_2D, etc...). */ GLenum target_ = -1; /** opengl identifier for texture. */ GLuint tex_id_ = 0; diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.cc b/source/blender/gpu/opengl/gl_vertex_buffer.cc index a56d5269fde..ce16a491528 100644 --- a/source/blender/gpu/opengl/gl_vertex_buffer.cc +++ b/source/blender/gpu/opengl/gl_vertex_buffer.cc @@ -29,6 +29,10 @@ namespace blender::gpu { void GLVertBuf::acquire_data() { + if (usage_ == GPU_USAGE_DEVICE_ONLY) { + return; + } + /* Discard previous data if any. */ MEM_SAFE_FREE(data); data = (uchar *)MEM_mallocN(sizeof(uchar) * this->size_alloc_get(), __func__); @@ -36,6 +40,10 @@ void GLVertBuf::acquire_data() void GLVertBuf::resize_data() { + if (usage_ == GPU_USAGE_DEVICE_ONLY) { + return; + } + data = (uchar *)MEM_reallocN(data, sizeof(uchar) * this->size_alloc_get()); } @@ -94,8 +102,10 @@ void GLVertBuf::bind() vbo_size_ = this->size_used_get(); /* Orphan the vbo to avoid sync then upload data. */ glBufferData(GL_ARRAY_BUFFER, vbo_size_, nullptr, to_gl(usage_)); - glBufferSubData(GL_ARRAY_BUFFER, 0, vbo_size_, data); - + /* Do not transfer data from host to device when buffer is device only. */ + if (usage_ != GPU_USAGE_DEVICE_ONLY) { + glBufferSubData(GL_ARRAY_BUFFER, 0, vbo_size_, data); + } memory_usage += vbo_size_; if (usage_ == GPU_USAGE_STATIC) { @@ -106,6 +116,37 @@ void GLVertBuf::bind() } } +void GLVertBuf::bind_as_ssbo(uint binding) +{ + bind(); + BLI_assert(vbo_id_ != 0); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, vbo_id_); +} + +const void *GLVertBuf::read() const +{ + BLI_assert(is_active()); + void *result = glMapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); + return result; +} + +void *GLVertBuf::unmap(const void *mapped_data) const +{ + void *result = MEM_mallocN(vbo_size_, __func__); + memcpy(result, mapped_data, vbo_size_); + return result; +} + +bool GLVertBuf::is_active() const +{ + if (!vbo_id_) { + return false; + } + int active_vbo_id = 0; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &active_vbo_id); + return vbo_id_ == active_vbo_id; +} + void GLVertBuf::update_sub(uint start, uint len, void *data) { glBufferSubData(GL_ARRAY_BUFFER, start, len, data); diff --git a/source/blender/gpu/opengl/gl_vertex_buffer.hh b/source/blender/gpu/opengl/gl_vertex_buffer.hh index e2bf6cd00e8..6c38a2225b3 100644 --- a/source/blender/gpu/opengl/gl_vertex_buffer.hh +++ b/source/blender/gpu/opengl/gl_vertex_buffer.hh @@ -47,12 +47,19 @@ class GLVertBuf : public VertBuf { void update_sub(uint start, uint len, void *data) override; + const void *read() const override; + void *unmap(const void *mapped_data) const override; + protected: void acquire_data(void) override; void resize_data(void) override; void release_data(void) override; void upload_data(void) override; void duplicate_data(VertBuf *dst) override; + void bind_as_ssbo(uint binding) override; + + private: + bool is_active() const; MEM_CXX_CLASS_ALLOC_FUNCS("GLVertBuf"); }; @@ -65,6 +72,7 @@ static inline GLenum to_gl(GPUUsageType type) case GPU_USAGE_DYNAMIC: return GL_DYNAMIC_DRAW; case GPU_USAGE_STATIC: + case GPU_USAGE_DEVICE_ONLY: return GL_STATIC_DRAW; default: BLI_assert(0); diff --git a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl index 21c7f79a57c..6dd0201535d 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_widget_base_frag.glsl @@ -36,6 +36,9 @@ vec3 compute_masks(vec2 uv) corner_rad = right_half ? outRoundCorners.y : outRoundCorners.x; } + /* Fade emboss at the border. */ + float emboss_size = upper_half ? 0.0 : min(1.0, uv_sdf.x / (corner_rad * ratio)); + /* Signed distance field from the corner (in pixel). * inner_sdf is sharp and outer_sdf is rounded. */ uv_sdf -= corner_rad; @@ -43,9 +46,6 @@ vec3 compute_masks(vec2 uv) float outer_sdf = -length(min(uv_sdf, 0.0)); float sdf = inner_sdf + outer_sdf + corner_rad; - /* Fade emboss at the border. */ - float emboss_size = clamp((upper_half) ? 0.0 : (uv.x / corner_rad), 0.0, 1.0); - /* Clamp line width to be at least 1px wide. This can happen if the projection matrix * has been scaled (i.e: Node editor)... */ float line_width = (lineWidth > 0.0) ? max(fwidth(uv.y), lineWidth) : 0.0; diff --git a/source/blender/gpu/shaders/gpu_shader_gpencil_stroke_geom.glsl b/source/blender/gpu/shaders/gpu_shader_gpencil_stroke_geom.glsl index da6e6502e62..b937323f62a 100644 --- a/source/blender/gpu/shaders/gpu_shader_gpencil_stroke_geom.glsl +++ b/source/blender/gpu/shaders/gpu_shader_gpencil_stroke_geom.glsl @@ -170,7 +170,7 @@ void main(void) length_b = finalThickness[2]; } - /* generate the start endcap (alpha < 0 used as endcap flag)*/ + /* Generate the start end-cap (alpha < 0 used as end-cap flag). */ float extend = (fill_stroke > 0) ? 2 : 1; if ((caps_start != GPENCIL_FLATCAP) && is_equal(P0, P2)) { mTexCoord = vec2(1, 0.5); @@ -211,7 +211,7 @@ void main(void) gl_Position = vec4((sp2 - length_b * miter_b) / Viewport, getZdepth(P2), 1.0); EmitVertex(); - /* generate the end endcap (alpha < 0 used as endcap flag)*/ + /* Generate the end end-cap (alpha < 0 used as end-cap flag). */ if ((caps_end != GPENCIL_FLATCAP) && is_equal(P1, P3)) { mTexCoord = vec2(0, 1); mColor = vec4(finalColor[2].rgb, finalColor[2].a * -1.0); diff --git a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl index d85884e0a25..2568cd74445 100644 --- a/source/blender/gpu/shaders/gpu_shader_text_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_text_frag.glsl @@ -7,7 +7,7 @@ flat in int interp_size; out vec4 fragColor; -uniform sampler1DArray glyph; +uniform sampler2D glyph; const vec2 offsets4[4] = vec2[4]( vec2(-0.5, 0.5), vec2(0.5, 0.5), vec2(-0.5, -0.5), vec2(-0.5, -0.5)); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl index 4db27c3049d..a14ff5021bf 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_geometry.glsl @@ -37,8 +37,13 @@ void node_geometry(vec3 I, normal = (toworld * vec4(N, 0.0)).xyz; true_normal = normal; # endif + +# ifdef HAIR_SHADER + tangent = -hairTangent; +# else tangent_orco_z(orco, orco); node_tangent(N, orco, objmat, tangent); +# endif parametric = vec3(barycentric, 0.0); backfacing = (gl_FrontFacing) ? 0.0 : 1.0; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl index 5a68f802659..5129bf71903 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl @@ -23,8 +23,8 @@ void node_subsurface_scattering(vec4 color, /* Not perfect for texture_blur values between 0.0 and 1.0. * Interpolate between separated color and color applied on irradiance. */ float one_minus_texture_blur = 1.0 - texture_blur; - vec3 sss_albedo = color.rgb * texture_blur + one_minus_texture_blur; - vec3 radiance_tint = color.rgb * one_minus_texture_blur + texture_blur; + vec3 sss_albedo = color.rgb * one_minus_texture_blur + texture_blur; + vec3 radiance_tint = color.rgb * texture_blur + one_minus_texture_blur; /* Consider output radiance as irradiance. */ out_Diffuse_0.radiance *= radiance_tint; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl index d8d9ecdf287..5745f11ede4 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_noise.glsl @@ -2,7 +2,7 @@ * coordinates to act as a seed since the noise functions don't have seed values. * A seed value is needed for generating distortion textures and color outputs. * The offset's components are in the range [100, 200], not too high to cause - * bad precision and not to small to be noticeable. We use float seed because + * bad precision and not too small to be noticeable. We use float seed because * OSL only support float hashes. */ diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl index 60ed098beb3..4ad5d4232de 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_vector_math.glsl @@ -150,3 +150,9 @@ void vector_math_faceforward( { outVector = faceforward(a, b, c); } + +void vector_math_multiply_add( + vec3 a, vec3 b, vec3 c, float scale, out vec3 outVector, out float outValue) +{ + outVector = a * b + c; +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl new file mode 100644 index 00000000000..2c5d38eabbe --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_wavelength.glsl @@ -0,0 +1,15 @@ +void node_wavelength(float wavelength, + sampler1DArray spectrummap, + float layer, + vec3 xyz_to_r, + vec3 xyz_to_g, + vec3 xyz_to_b, + out vec4 color) +{ + mat3 xyz_to_rgb = mat3(xyz_to_r, xyz_to_g, xyz_to_b); + float t = (wavelength - 380.0) / (780.0 - 380.0); + vec3 xyz = texture(spectrummap, vec2(t, layer)).rgb; + vec3 rgb = xyz * xyz_to_rgb; + rgb *= 1.0 / 2.52; /* Empirical scale from lg to make all comps <= 1. */ + color = vec4(clamp(rgb, 0.0, 1.0), 1.0); +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl index d33465fa846..40e46bc250c 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_world_normals.glsl @@ -1,4 +1,4 @@ -/* TODO : clean this ifdef mess */ +/* TODO: clean this `ifdef` mess. */ void world_normals_get(out vec3 N) { #ifndef VOLUMETRICS diff --git a/source/blender/gpu/tests/gpu_index_buffer_test.cc b/source/blender/gpu/tests/gpu_index_buffer_test.cc new file mode 100644 index 00000000000..9d767b58a7b --- /dev/null +++ b/source/blender/gpu/tests/gpu_index_buffer_test.cc @@ -0,0 +1,49 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "MEM_guardedalloc.h" + +#include "GPU_index_buffer.h" + +#include "gpu_testing.hh" + +namespace blender::gpu::tests { + +static void test_gpu_index_buffer_subbuilders() +{ + const uint num_subbuilders = 10; + const uint verts_per_subbuilders = 100; + const uint vertex_len = num_subbuilders * verts_per_subbuilders; + + GPUIndexBufBuilder builder; + GPU_indexbuf_init(&builder, GPU_PRIM_POINTS, vertex_len, vertex_len); + + GPUIndexBufBuilder subbuilders[num_subbuilders]; + for (int subbuilder_index = 0; subbuilder_index < num_subbuilders; subbuilder_index++) { + memcpy(&subbuilders[subbuilder_index], &builder, sizeof(builder)); + } + + for (int subbuilder_index = 0; subbuilder_index < num_subbuilders; subbuilder_index++) { + GPUIndexBufBuilder &subbuilder = subbuilders[subbuilder_index]; + for (int subbuilder_vert_index = 0; subbuilder_vert_index < verts_per_subbuilders; + subbuilder_vert_index++) { + int vert_index_to_update = subbuilder_index * verts_per_subbuilders + subbuilder_vert_index; + GPU_indexbuf_set_point_vert(&subbuilder, vert_index_to_update, vert_index_to_update); + } + } + + for (int subbuilder_index = 0; subbuilder_index < num_subbuilders; subbuilder_index++) { + EXPECT_EQ(builder.index_len, subbuilder_index * verts_per_subbuilders); + GPU_indexbuf_join(&builder, &subbuilders[subbuilder_index]); + EXPECT_EQ(builder.index_len, (subbuilder_index + 1) * verts_per_subbuilders); + } + + GPUIndexBuf *index_buffer = GPU_indexbuf_build(&builder); + EXPECT_NE(index_buffer, nullptr); + GPU_INDEXBUF_DISCARD_SAFE(index_buffer); +} + +GPU_TEST(gpu_index_buffer_subbuilders) + +} // namespace blender::gpu::tests diff --git a/source/blender/gpu/tests/gpu_shader_builtin_test.cc b/source/blender/gpu/tests/gpu_shader_builtin_test.cc new file mode 100644 index 00000000000..f0061a6bf5c --- /dev/null +++ b/source/blender/gpu/tests/gpu_shader_builtin_test.cc @@ -0,0 +1,94 @@ +/* Apache License, Version 2.0 */ + +#include "gpu_testing.hh" + +#include "GPU_shader.h" + +namespace blender::gpu::tests { + +static void test_compile_builtin_shader(eGPUBuiltinShader shader_type, eGPUShaderConfig sh_cfg) +{ + GPUShader *sh = GPU_shader_get_builtin_shader_with_config(shader_type, sh_cfg); + EXPECT_NE(sh, nullptr); +} + +static void test_compile_builtin_shader(eGPUBuiltinShader shader_type) +{ + test_compile_builtin_shader(shader_type, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(shader_type, GPU_SHADER_CFG_CLIPPED); +} + +static void test_shader_builtin() +{ + GPU_shader_free_builtin_shaders(); + + test_compile_builtin_shader(GPU_SHADER_3D_UNIFORM_COLOR); + test_compile_builtin_shader(GPU_SHADER_3D_SMOOTH_COLOR); + test_compile_builtin_shader(GPU_SHADER_3D_DEPTH_ONLY); + test_compile_builtin_shader(GPU_SHADER_3D_FLAT_COLOR); + test_compile_builtin_shader(GPU_SHADER_INSTANCE_VARIYING_COLOR_VARIYING_SIZE); + test_compile_builtin_shader(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR); + test_compile_builtin_shader(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + test_compile_builtin_shader(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA); + + test_compile_builtin_shader(GPU_SHADER_TEXT, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_KEYFRAME_DIAMOND, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_SIMPLE_LIGHTING, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_FLAT_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_SMOOTH_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_IMAGE, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_DESATURATE_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_MULTI_RECT_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_CHECKER, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_DIAG_STRIPES, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_3D_CLIPPED_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_3D_POLYLINE_CLIPPED_UNIFORM_COLOR, + GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_3D_POLYLINE_FLAT_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_OVERLAYS_MERGE, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_OVERLAYS_STEREO_MERGE, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_POINT_FIXED_SIZE_UNIFORM_COLOR, + GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_POINT_UNIFORM_SIZE_VARYING_COLOR_OUTLINE_AA, + GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_POINT_VARYING_SIZE_VARYING_COLOR, + GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_3D_POINT_FIXED_SIZE_UNIFORM_COLOR, + GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA, + GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA, + GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_3D_POINT_FIXED_SIZE_VARYING_COLOR, + GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_3D_POINT_VARYING_SIZE_UNIFORM_COLOR, + GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR, + GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_GPENCIL_STROKE, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_AREA_EDGES, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_WIDGET_BASE, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_WIDGET_BASE_INST, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_WIDGET_SHADOW, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_NODELINK, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_NODELINK_INST, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_UV_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_UV_VERTS, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_UV_FACEDOTS, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_UV_EDGES, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_UV_EDGES_SMOOTH, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_UV_FACES, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_UV_FACES_STRETCH_AREA, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_2D_UV_FACES_STRETCH_ANGLE, GPU_SHADER_CFG_DEFAULT); +} + +GPU_TEST(shader_builtin) + +} // namespace blender::gpu::tests diff --git a/source/blender/gpu/tests/gpu_shader_test.cc b/source/blender/gpu/tests/gpu_shader_test.cc new file mode 100644 index 00000000000..43ff86ebbd8 --- /dev/null +++ b/source/blender/gpu/tests/gpu_shader_test.cc @@ -0,0 +1,306 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "GPU_capabilities.h" +#include "GPU_compute.h" +#include "GPU_index_buffer.h" +#include "GPU_shader.h" +#include "GPU_texture.h" +#include "GPU_vertex_buffer.h" +#include "GPU_vertex_format.h" + +#include "MEM_guardedalloc.h" + +#include "gpu_testing.hh" + +#include "GPU_glew.h" + +namespace blender::gpu::tests { + +static void test_gpu_shader_compute_2d() +{ + + if (!GPU_compute_shader_support()) { + /* We can't test as a the platform does not support compute shaders. */ + std::cout << "Skipping compute shader test: platform not supported"; + return; + } + + static constexpr uint SIZE = 512; + + /* Build compute shader. */ + const char *compute_glsl = R"( + +layout(local_size_x = 1, local_size_y = 1) in; +layout(rgba32f, binding = 0) uniform image2D img_output; + +void main() { + vec4 pixel = vec4(1.0, 0.5, 0.2, 1.0); + imageStore(img_output, ivec2(gl_GlobalInvocationID.xy), pixel); +} + +)"; + + GPUShader *shader = GPU_shader_create_compute( + compute_glsl, nullptr, nullptr, "gpu_shader_compute_2d"); + EXPECT_NE(shader, nullptr); + + /* Create texture to store result and attach to shader. */ + GPUTexture *texture = GPU_texture_create_2d( + "gpu_shader_compute_2d", SIZE, SIZE, 0, GPU_RGBA32F, nullptr); + EXPECT_NE(texture, nullptr); + + GPU_shader_bind(shader); + GPU_texture_image_bind(texture, GPU_shader_get_texture_binding(shader, "img_output")); + + /* Dispatch compute task. */ + GPU_compute_dispatch(shader, SIZE, SIZE, 1); + + /* Check if compute has been done. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + float *data = static_cast<float *>(GPU_texture_read(texture, GPU_DATA_FLOAT, 0)); + EXPECT_NE(data, nullptr); + for (int index = 0; index < SIZE * SIZE; index++) { + EXPECT_FLOAT_EQ(data[index * 4 + 0], 1.0f); + EXPECT_FLOAT_EQ(data[index * 4 + 1], 0.5f); + EXPECT_FLOAT_EQ(data[index * 4 + 2], 0.2f); + EXPECT_FLOAT_EQ(data[index * 4 + 3], 1.0f); + } + MEM_freeN(data); + + /* Cleanup. */ + GPU_shader_unbind(); + GPU_texture_unbind(texture); + GPU_texture_free(texture); + GPU_shader_free(shader); +} +GPU_TEST(gpu_shader_compute_2d) + +static void test_gpu_shader_compute_1d() +{ + + if (!GPU_compute_shader_support()) { + /* We can't test as a the platform does not support compute shaders. */ + std::cout << "Skipping compute shader test: platform not supported"; + return; + } + + static constexpr uint SIZE = 10; + + /* Build compute shader. */ + const char *compute_glsl = R"( + +layout(local_size_x = 1) in; + +layout(rgba32f, binding = 1) uniform image1D outputVboData; + +void main() { + int index = int(gl_GlobalInvocationID.x); + vec4 pos = vec4(gl_GlobalInvocationID.x); + imageStore(outputVboData, index, pos); +} + +)"; + + GPUShader *shader = GPU_shader_create_compute( + compute_glsl, nullptr, nullptr, "gpu_shader_compute_1d"); + EXPECT_NE(shader, nullptr); + + /* Construct Texture. */ + GPUTexture *texture = GPU_texture_create_1d("gpu_shader_compute_1d", SIZE, 0, GPU_RGBA32F, NULL); + EXPECT_NE(texture, nullptr); + + GPU_shader_bind(shader); + GPU_texture_image_bind(texture, GPU_shader_get_texture_binding(shader, "outputVboData")); + + /* Dispatch compute task. */ + GPU_compute_dispatch(shader, SIZE, 1, 1); + + /* Check if compute has been done. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + + /* Create texture to load back result. */ + float *data = static_cast<float *>(GPU_texture_read(texture, GPU_DATA_FLOAT, 0)); + EXPECT_NE(data, nullptr); + for (int index = 0; index < SIZE; index++) { + float expected_value = index; + EXPECT_FLOAT_EQ(data[index * 4 + 0], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 1], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 2], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 3], expected_value); + } + MEM_freeN(data); + + /* Cleanup. */ + GPU_shader_unbind(); + GPU_texture_unbind(texture); + GPU_texture_free(texture); + GPU_shader_free(shader); +} +GPU_TEST(gpu_shader_compute_1d) + +static void test_gpu_shader_compute_vbo() +{ + + if (!GPU_compute_shader_support()) { + /* We can't test as a the platform does not support compute shaders. */ + std::cout << "Skipping compute shader test: platform not supported"; + return; + } + + static constexpr uint SIZE = 128; + + /* Build compute shader. */ + const char *compute_glsl = R"( + +layout(local_size_x = 1) in; + +layout(std430, binding = 0) writeonly buffer outputVboData +{ + vec4 out_positions[]; +}; + +void main() { + uint index = gl_GlobalInvocationID.x; + vec4 pos = vec4(gl_GlobalInvocationID.x); + out_positions[index] = pos; +} + +)"; + + GPUShader *shader = GPU_shader_create_compute( + compute_glsl, nullptr, nullptr, "gpu_shader_compute_vbo"); + EXPECT_NE(shader, nullptr); + GPU_shader_bind(shader); + + /* Construct VBO. */ + static GPUVertFormat format = {0}; + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + GPUVertBuf *vbo = GPU_vertbuf_create_with_format_ex(&format, GPU_USAGE_DEVICE_ONLY); + GPU_vertbuf_data_alloc(vbo, SIZE); + GPU_vertbuf_bind_as_ssbo(vbo, GPU_shader_get_ssbo(shader, "outputVboData")); + + /* Dispatch compute task. */ + GPU_compute_dispatch(shader, SIZE, 1, 1); + + /* Check if compute has been done. */ + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + + /* Download the vertex buffer. */ + const float *data = static_cast<const float *>(GPU_vertbuf_read(vbo)); + ASSERT_NE(data, nullptr); + for (int index = 0; index < SIZE; index++) { + float expected_value = index; + EXPECT_FLOAT_EQ(data[index * 4 + 0], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 1], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 2], expected_value); + EXPECT_FLOAT_EQ(data[index * 4 + 3], expected_value); + } + + /* Cleanup. */ + GPU_shader_unbind(); + GPU_vertbuf_discard(vbo); + GPU_shader_free(shader); +} +GPU_TEST(gpu_shader_compute_vbo) + +static void test_gpu_shader_compute_ibo() +{ + + if (!GPU_compute_shader_support()) { + /* We can't test as a the platform does not support compute shaders. */ + std::cout << "Skipping compute shader test: platform not supported"; + return; + } + + static constexpr uint SIZE = 128; + + /* Build compute shader. */ + const char *compute_glsl = R"( + +layout(local_size_x = 1) in; + +layout(std430, binding = 1) writeonly buffer outputIboData +{ + uint out_indexes[]; +}; + +void main() { + uint store_index = int(gl_GlobalInvocationID.x); + out_indexes[store_index] = store_index; +} + +)"; + + GPUShader *shader = GPU_shader_create_compute( + compute_glsl, nullptr, nullptr, "gpu_shader_compute_vbo"); + EXPECT_NE(shader, nullptr); + GPU_shader_bind(shader); + + /* Construct IBO. */ + GPUIndexBuf *ibo = GPU_indexbuf_build_on_device(SIZE); + GPU_indexbuf_bind_as_ssbo(ibo, GPU_shader_get_ssbo(shader, "outputIboData")); + + /* Dispatch compute task. */ + GPU_compute_dispatch(shader, SIZE, 1, 1); + + /* Check if compute has been done. */ + GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE); + + /* Download the index buffer. */ + const uint32_t *data = GPU_indexbuf_read(ibo); + ASSERT_NE(data, nullptr); + for (int index = 0; index < SIZE; index++) { + uint32_t expected = index; + EXPECT_EQ(data[index], expected); + } + + /* Cleanup. */ + GPU_shader_unbind(); + GPU_indexbuf_discard(ibo); + GPU_shader_free(shader); +} +GPU_TEST(gpu_shader_compute_ibo) + +static void test_gpu_shader_ssbo_binding() +{ + if (!GPU_compute_shader_support()) { + /* We can't test as a the platform does not support compute shaders. */ + std::cout << "Skipping compute shader test: platform not supported"; + return; + } + + /* Build compute shader. */ + const char *compute_glsl = R"( + +layout(local_size_x = 1) in; + +layout(std430, binding = 0) buffer ssboBinding0 +{ + int data0[]; +}; +layout(std430, binding = 1) buffer ssboBinding1 +{ + int data1[]; +}; + +void main() { +} + +)"; + + GPUShader *shader = GPU_shader_create_compute(compute_glsl, nullptr, nullptr, "gpu_shader_ssbo"); + EXPECT_NE(shader, nullptr); + GPU_shader_bind(shader); + + EXPECT_EQ(0, GPU_shader_get_ssbo(shader, "ssboBinding0")); + EXPECT_EQ(1, GPU_shader_get_ssbo(shader, "ssboBinding1")); + + /* Cleanup. */ + GPU_shader_unbind(); + GPU_shader_free(shader); +} +GPU_TEST(gpu_shader_ssbo_binding) + +} // namespace blender::gpu::tests diff --git a/source/blender/gpu/tests/gpu_testing.hh b/source/blender/gpu/tests/gpu_testing.hh index cf902a91264..c45770cb94e 100644 --- a/source/blender/gpu/tests/gpu_testing.hh +++ b/source/blender/gpu/tests/gpu_testing.hh @@ -15,13 +15,31 @@ namespace blender::gpu { */ class GPUTest : public ::testing::Test { private: + GHOST_TDrawingContextType draw_context_type = GHOST_kDrawingContextTypeNone; GHOST_SystemHandle ghost_system; GHOST_ContextHandle ghost_context; struct GPUContext *context; protected: + GPUTest(GHOST_TDrawingContextType draw_context_type) : draw_context_type(draw_context_type) + { + } + void SetUp() override; void TearDown() override; }; +class GPUOpenGLTest : public GPUTest { + public: + GPUOpenGLTest() : GPUTest(GHOST_kDrawingContextTypeOpenGL) + { + } +}; + +#define GPU_TEST(test_name) \ + TEST_F(GPUOpenGLTest, test_name) \ + { \ + test_##test_name(); \ + } + } // namespace blender::gpu |