diff options
Diffstat (limited to 'source/blender/gpu/intern')
34 files changed, 1940 insertions, 3772 deletions
diff --git a/source/blender/gpu/intern/gpu_attr_binding.c b/source/blender/gpu/intern/gpu_attr_binding.cc index 6cb60884620..6cb60884620 100644 --- a/source/blender/gpu/intern/gpu_attr_binding.c +++ b/source/blender/gpu/intern/gpu_attr_binding.cc diff --git a/source/blender/gpu/intern/gpu_attr_binding_private.h b/source/blender/gpu/intern/gpu_attr_binding_private.h index 301ec3333dd..7df403a3ea5 100644 --- a/source/blender/gpu/intern/gpu_attr_binding_private.h +++ b/source/blender/gpu/intern/gpu_attr_binding_private.h @@ -29,6 +29,11 @@ #include "GPU_shader_interface.h" #include "GPU_vertex_format.h" +#ifdef __cplusplus +extern "C" { +#endif + +/* TODO(fclem) remove, use shaderface directly. */ void AttrBinding_clear(GPUAttrBinding *binding); void get_attr_locations(const GPUVertFormat *format, @@ -36,4 +41,8 @@ void get_attr_locations(const GPUVertFormat *format, const GPUShaderInterface *shaderface); uint read_attr_location(const GPUAttrBinding *binding, uint a_idx); +#ifdef __cplusplus +} +#endif + #endif /* __GPU_ATTR_BINDING_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_batch.c b/source/blender/gpu/intern/gpu_batch.cc index 5f77f13c135..9f9adcacfa6 100644 --- a/source/blender/gpu/intern/gpu_batch.c +++ b/source/blender/gpu/intern/gpu_batch.cc @@ -37,6 +37,7 @@ #include "gpu_context_private.h" #include "gpu_primitive_private.h" #include "gpu_shader_private.h" +#include "gpu_vertex_format_private.h" #include <limits.h> #include <stdlib.h> @@ -89,7 +90,7 @@ GPUBatch *GPU_batch_create_ex(GPUPrimType prim_type, GPUIndexBuf *elem, uint owns_flag) { - GPUBatch *batch = MEM_callocN(sizeof(GPUBatch), "GPUBatch"); + GPUBatch *batch = (GPUBatch *)MEM_callocN(sizeof(GPUBatch), "GPUBatch"); GPU_batch_init_ex(batch, prim_type, verts, elem, owns_flag); return batch; } @@ -327,10 +328,10 @@ static GLuint batch_vao_get(GPUBatch *batch) } /* Init dynamic arrays and let the branch below set the values. */ batch->dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT; - batch->dynamic_vaos.interfaces = MEM_callocN( + batch->dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_callocN( batch->dynamic_vaos.count * sizeof(GPUShaderInterface *), "dyn vaos interfaces"); - batch->dynamic_vaos.vao_ids = MEM_callocN(batch->dynamic_vaos.count * sizeof(GLuint), - "dyn vaos ids"); + batch->dynamic_vaos.vao_ids = (GLuint *)MEM_callocN( + batch->dynamic_vaos.count * sizeof(GLuint), "dyn vaos ids"); } } @@ -346,11 +347,11 @@ static GLuint batch_vao_get(GPUBatch *batch) /* Not enough place, realloc the array. */ i = batch->dynamic_vaos.count; batch->dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT; - batch->dynamic_vaos.interfaces = MEM_recallocN((void *)batch->dynamic_vaos.interfaces, - sizeof(GPUShaderInterface *) * - batch->dynamic_vaos.count); - batch->dynamic_vaos.vao_ids = MEM_recallocN(batch->dynamic_vaos.vao_ids, - sizeof(GLuint) * batch->dynamic_vaos.count); + batch->dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_recallocN( + (void *)batch->dynamic_vaos.interfaces, + sizeof(GPUShaderInterface *) * batch->dynamic_vaos.count); + batch->dynamic_vaos.vao_ids = (GLuint *)MEM_recallocN( + batch->dynamic_vaos.vao_ids, sizeof(GLuint) * batch->dynamic_vaos.count); } batch->dynamic_vaos.interfaces[i] = batch->interface; batch->dynamic_vaos.vao_ids[i] = new_vao = GPU_vao_alloc(); @@ -370,22 +371,20 @@ static GLuint batch_vao_get(GPUBatch *batch) return new_vao; } -void GPU_batch_program_set_no_use(GPUBatch *batch, - uint32_t program, - const GPUShaderInterface *shaderface) +void GPU_batch_set_shader_no_bind(GPUBatch *batch, GPUShader *shader) { #if TRUST_NO_ONE - assert(glIsProgram(program)); + assert(glIsProgram(shader->program)); assert(batch->program_in_use == 0); #endif - batch->interface = shaderface; - batch->program = program; + batch->interface = shader->interface; + batch->program = shader->program; batch->vao_id = batch_vao_get(batch); } -void GPU_batch_program_set(GPUBatch *batch, uint32_t program, const GPUShaderInterface *shaderface) +void GPU_batch_set_shader(GPUBatch *batch, GPUShader *shader) { - GPU_batch_program_set_no_use(batch, program, shaderface); + GPU_batch_set_shader_no_bind(batch, shader); GPU_batch_program_use_begin(batch); /* hack! to make Batch_Uniform* simpler */ } @@ -440,6 +439,7 @@ static void create_bindings(GPUVertBuf *verts, } const GLvoid *pointer = (const GLubyte *)0 + offset + v_first * stride; + const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type)); for (uint n_idx = 0; n_idx < a->name_len; n_idx++) { const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); @@ -452,19 +452,13 @@ static void create_bindings(GPUVertBuf *verts, *attr_mask &= ~(1 << input->location); if (a->comp_len == 16 || a->comp_len == 12 || a->comp_len == 8) { -#if TRUST_NO_ONE - assert(a->fetch_mode == GPU_FETCH_FLOAT); - assert(a->gl_comp_type == GL_FLOAT); -#endif + BLI_assert(a->fetch_mode == GPU_FETCH_FLOAT); + BLI_assert(a->comp_type == GPU_COMP_F32); for (int i = 0; i < a->comp_len / 4; i++) { glEnableVertexAttribArray(input->location + i); glVertexAttribDivisor(input->location + i, (use_instancing) ? 1 : 0); - glVertexAttribPointer(input->location + i, - 4, - a->gl_comp_type, - GL_FALSE, - stride, - (const GLubyte *)pointer + i * 16); + glVertexAttribPointer( + input->location + i, 4, type, GL_FALSE, stride, (const GLubyte *)pointer + i * 16); } } else { @@ -474,15 +468,13 @@ static void create_bindings(GPUVertBuf *verts, switch (a->fetch_mode) { case GPU_FETCH_FLOAT: case GPU_FETCH_INT_TO_FLOAT: - glVertexAttribPointer( - input->location, a->comp_len, a->gl_comp_type, GL_FALSE, stride, pointer); + glVertexAttribPointer(input->location, a->comp_len, type, GL_FALSE, stride, pointer); break; case GPU_FETCH_INT_TO_FLOAT_UNIT: - glVertexAttribPointer( - input->location, a->comp_len, a->gl_comp_type, GL_TRUE, stride, pointer); + glVertexAttribPointer(input->location, a->comp_len, type, GL_TRUE, stride, pointer); break; case GPU_FETCH_INT: - glVertexAttribIPointer(input->location, a->comp_len, a->gl_comp_type, stride, pointer); + glVertexAttribIPointer(input->location, a->comp_len, type, stride, pointer); break; } } @@ -839,7 +831,7 @@ struct GPUDrawList { GPUDrawList *GPU_draw_list_create(int length) { - GPUDrawList *list = MEM_callocN(sizeof(GPUDrawList), "GPUDrawList"); + GPUDrawList *list = (GPUDrawList *)MEM_callocN(sizeof(GPUDrawList), "GPUDrawList"); /* Alloc the biggest possible command list which is indexed. */ list->buffer_size = sizeof(GPUDrawCommandIndexed) * length; if (USE_MULTI_DRAW_INDIRECT) { @@ -848,7 +840,7 @@ GPUDrawList *GPU_draw_list_create(int length) glBufferData(GL_DRAW_INDIRECT_BUFFER, list->buffer_size, NULL, GL_DYNAMIC_DRAW); } else { - list->commands = MEM_mallocN(list->buffer_size, "GPUDrawList data"); + list->commands = (GPUDrawCommand *)MEM_mallocN(list->buffer_size, "GPUDrawList data"); } return list; } @@ -880,7 +872,7 @@ void GPU_draw_list_init(GPUDrawList *list, GPUBatch *batch) list->cmd_offset = 0; } GLenum flags = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT; - list->commands = glMapBufferRange( + list->commands = (GPUDrawCommand *)glMapBufferRange( GL_DRAW_INDIRECT_BUFFER, list->cmd_offset, list->buffer_size - list->cmd_offset, flags); } } @@ -989,17 +981,12 @@ void GPU_draw_list_submit(GPUDrawList *list) /** \name Utilities * \{ */ -void GPU_batch_program_set_shader(GPUBatch *batch, GPUShader *shader) -{ - GPU_batch_program_set(batch, shader->program, shader->interface); -} - void GPU_batch_program_set_builtin_with_config(GPUBatch *batch, eGPUBuiltinShader shader_id, eGPUShaderConfig sh_cfg) { GPUShader *shader = GPU_shader_get_builtin_shader_with_config(shader_id, sh_cfg); - GPU_batch_program_set(batch, shader->program, shader->interface); + GPU_batch_set_shader(batch, shader); } void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id) @@ -1012,10 +999,7 @@ void GPU_batch_program_set_builtin(GPUBatch *batch, eGPUBuiltinShader shader_id) * DO NOT DRAW WITH THE BATCH BEFORE CALLING immUnbindProgram. */ void GPU_batch_program_set_imm_shader(GPUBatch *batch) { - GLuint program; - GPUShaderInterface *interface; - immGetProgram(&program, &interface); - GPU_batch_program_set(batch, program, interface); + GPU_batch_set_shader(batch, immGetShader()); } /** \} */ diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index cef90d57ef5..10d5a860f6a 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -34,9 +34,11 @@ #include "BLI_hash.h" #include "BLI_math.h" #include "BLI_math_color.h" +#include "BLI_math_color_blend.h" #include "BLI_utildefines.h" #include "DNA_meshdata_types.h" +#include "DNA_userdef_types.h" #include "BKE_DerivedMesh.h" #include "BKE_ccg.h" @@ -227,12 +229,14 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers, const int *sculpt_face_sets, const int face_sets_color_seed, const int face_sets_color_default, + const MPropCol *vtcol, const int update_flags) { const bool show_mask = vmask && (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0; - const bool show_vcol = vcol && (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0; const bool show_face_sets = sculpt_face_sets && (update_flags & GPU_PBVH_BUFFERS_SHOW_SCULPT_FACE_SETS) != 0; + const bool show_vcol = (vcol || (vtcol && U.experimental.use_sculpt_vertex_colors)) && + (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) != 0; bool empty_mask = true; bool default_face_set = true; @@ -244,8 +248,8 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers, GPUVertBufRaw pos_step = {0}; GPUVertBufRaw nor_step = {0}; GPUVertBufRaw msk_step = {0}; - GPUVertBufRaw col_step = {0}; GPUVertBufRaw fset_step = {0}; + GPUVertBufRaw col_step = {0}; GPU_vertbuf_attr_get_raw_data(buffers->vert_buf, g_vbo_id.pos, &pos_step); GPU_vertbuf_attr_get_raw_data(buffers->vert_buf, g_vbo_id.nor, &nor_step); @@ -312,25 +316,33 @@ void GPU_pbvh_mesh_buffers_update(GPU_PBVH_Buffers *buffers, *(uchar *)GPU_vertbuf_raw_step(&msk_step) = cmask; empty_mask = empty_mask && (cmask == 0); - + /* Vertex Colors. */ if (show_vcol) { - const uint loop_index = lt->tri[j]; - const MLoopCol *mcol = &vcol[loop_index]; - ushort scol[4]; - scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]); - scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]); - scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]); - scol[3] = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f)); - memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol)); + ushort scol[4] = {USHRT_MAX, USHRT_MAX, USHRT_MAX, USHRT_MAX}; + if (vtcol && U.experimental.use_sculpt_vertex_colors) { + scol[0] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[0]); + scol[1] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[1]); + scol[2] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[2]); + scol[3] = unit_float_to_ushort_clamp(vtcol[vtri[j]].color[3]); + memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol)); + } + else { + const uint loop_index = lt->tri[j]; + const MLoopCol *mcol = &vcol[loop_index]; + scol[0] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->r]); + scol[1] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->g]); + scol[2] = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mcol->b]); + scol[3] = unit_float_to_ushort_clamp(mcol->a * (1.0f / 255.0f)); + memcpy(GPU_vertbuf_raw_step(&col_step), scol, sizeof(scol)); + } } - /* Face Sets. */ memcpy(GPU_vertbuf_raw_step(&fset_step), face_set_color, sizeof(uchar) * 3); } } - - gpu_pbvh_batch_init(buffers, GPU_PRIM_TRIS); } + + gpu_pbvh_batch_init(buffers, GPU_PRIM_TRIS); } /* Get material index from the first face of this buffer. */ @@ -440,7 +452,7 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const MPoly *mpoly, static void gpu_pbvh_grid_fill_index_buffers(GPU_PBVH_Buffers *buffers, SubdivCCG *UNUSED(subdiv_ccg), const int *UNUSED(face_sets), - int *grid_indices, + const int *grid_indices, uint visible_quad_len, int totgrid, int gridsize) @@ -571,7 +583,7 @@ static void gpu_pbvh_grid_fill_index_buffers(GPU_PBVH_Buffers *buffers, void GPU_pbvh_grid_buffers_update_free(GPU_PBVH_Buffers *buffers, const struct DMFlagMat *grid_flag_mats, - int *grid_indices) + const int *grid_indices) { const bool smooth = grid_flag_mats[grid_indices[0]].flag & ME_SMOOTH; diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index c1e7933d7ba..e15e4e08159 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -41,7 +41,6 @@ #include "BKE_material.h" #include "GPU_extensions.h" -#include "GPU_glew.h" #include "GPU_material.h" #include "GPU_shader.h" #include "GPU_uniformbuffer.h" @@ -56,8 +55,8 @@ #include <stdarg.h> #include <string.h> +extern char datatoc_gpu_shader_codegen_lib_glsl[]; extern char datatoc_gpu_shader_common_obinfos_lib_glsl[]; -extern char datatoc_common_view_lib_glsl[]; /* -------------------- GPUPass Cache ------------------ */ /** @@ -282,18 +281,15 @@ static const char *gpu_builtin_name(eGPUBuiltin builtin) static void codegen_set_unique_ids(GPUNodeGraph *graph) { - GPUNode *node; - GPUInput *input; - GPUOutput *output; int id = 1; - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { /* set id for unique names of uniform variables */ input->id = id++; } - for (output = node->outputs.first; output; output = output->next) { + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { /* set id for unique names of tmp variables storing output */ output->id = id++; } @@ -307,17 +303,10 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, DynStr *ds, GPUNodeGraph *graph) { - GPUNode *node; - GPUInput *input; const char *name; int builtins = 0; ListBase ubo_inputs = {NULL, NULL}; - /* Attributes */ - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - BLI_dynstr_appendf(ds, "in %s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); - } - /* Textures */ LISTBASE_FOREACH (GPUMaterialTexture *, tex, &graph->textures) { if (tex->colorband) { @@ -339,8 +328,9 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, } /* Print other uniforms */ - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { + + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { if (input->source == GPU_SOURCE_BUILTIN) { /* only define each builtin uniform/varying once */ if (!(builtins & input->builtin)) { @@ -382,8 +372,8 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, BLI_dynstr_appendf(ds, "\nlayout (std140) uniform %s {\n", GPU_UBO_BLOCK_NAME); LISTBASE_FOREACH (LinkData *, link, &ubo_inputs) { - input = link->data; - BLI_dynstr_appendf(ds, "\t%s unf%d;\n", gpu_data_type_to_string(input->type), input->id); + GPUInput *input = (GPUInput *)(link->data); + BLI_dynstr_appendf(ds, " %s unf%d;\n", gpu_data_type_to_string(input->type), input->id); } BLI_dynstr_append(ds, "};\n"); BLI_freelistN(&ubo_inputs); @@ -396,34 +386,26 @@ static int codegen_process_uniforms_functions(GPUMaterial *material, static void codegen_declare_tmps(DynStr *ds, GPUNodeGraph *graph) { - GPUNode *node; - GPUOutput *output; - - for (node = graph->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { /* declare temporary variables for node output storage */ - for (output = node->outputs.first; output; output = output->next) { + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { if (output->type == GPU_CLOSURE) { - BLI_dynstr_appendf(ds, "\tClosure tmp%d;\n", output->id); + BLI_dynstr_appendf(ds, " Closure tmp%d;\n", output->id); } else { - BLI_dynstr_appendf(ds, "\t%s tmp%d;\n", gpu_data_type_to_string(output->type), output->id); + BLI_dynstr_appendf(ds, " %s tmp%d;\n", gpu_data_type_to_string(output->type), output->id); } } } - BLI_dynstr_append(ds, "\n"); } static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *finaloutput) { - GPUNode *node; - GPUInput *input; - GPUOutput *output; - - for (node = graph->nodes.first; node; node = node->next) { - BLI_dynstr_appendf(ds, "\t%s(", node->name); + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + BLI_dynstr_appendf(ds, " %s(", node->name); - for (input = node->inputs.first; input; input = input->next) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { if (input->source == GPU_SOURCE_TEX) { BLI_dynstr_append(ds, input->texture->sampler_name); } @@ -504,7 +486,7 @@ static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *f BLI_dynstr_append(ds, ", "); } - for (output = node->outputs.first; output; output = output->next) { + LISTBASE_FOREACH (GPUOutput *, output, &node->outputs) { BLI_dynstr_appendf(ds, "tmp%d", output->id); if (output->next) { BLI_dynstr_append(ds, ", "); @@ -514,21 +496,24 @@ static void codegen_call_functions(DynStr *ds, GPUNodeGraph *graph, GPUOutput *f BLI_dynstr_append(ds, ");\n"); } - BLI_dynstr_appendf(ds, "\n\treturn tmp%d", finaloutput->id); - BLI_dynstr_append(ds, ";\n"); + BLI_dynstr_appendf(ds, "\n return tmp%d;\n", finaloutput->id); } -static char *code_generate_fragment(GPUMaterial *material, GPUNodeGraph *graph) +static char *code_generate_fragment(GPUMaterial *material, + GPUNodeGraph *graph, + const char *interface_str) { DynStr *ds = BLI_dynstr_new(); char *code; int builtins; -#if 0 - BLI_dynstr_append(ds, FUNCTION_PROTOTYPES); -#endif - codegen_set_unique_ids(graph); + + /* Attributes, Shader stage interface. */ + if (interface_str) { + BLI_dynstr_appendf(ds, "in codegenInterface {%s};\n\n", interface_str); + } + builtins = codegen_process_uniforms_functions(material, ds, graph); if (builtins & (GPU_OBJECT_INFO | GPU_OBJECT_COLOR)) { @@ -536,73 +521,61 @@ static char *code_generate_fragment(GPUMaterial *material, GPUNodeGraph *graph) } if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "in vec2 barycentricTexCo;\n"); - } - - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "flat in vec3 barycentricDist;\n"); + BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); } BLI_dynstr_append(ds, "Closure nodetree_exec(void)\n{\n"); if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, - "\tvec2 barytexco = vec2((fract(barycentricTexCo.y) != 0.0)\n" - "\t ? barycentricTexCo.x\n" - "\t : 1.0 - barycentricTexCo.x,\n" - "\t 0.0);\n"); - BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, "\tvec2 barytexco = barycentricTexCo;\n"); - BLI_dynstr_append(ds, "#endif\n"); + BLI_dynstr_append(ds, " vec2 barytexco = barycentric_resolve(barycentricTexCo);\n"); } /* TODO(fclem) get rid of that. */ if (builtins & GPU_VIEW_MATRIX) { - BLI_dynstr_append(ds, "\t#define viewmat ViewMatrix\n"); + BLI_dynstr_append(ds, " #define viewmat ViewMatrix\n"); } if (builtins & GPU_CAMERA_TEXCO_FACTORS) { - BLI_dynstr_append(ds, "\t#define camtexfac CameraTexCoFactors\n"); + BLI_dynstr_append(ds, " #define camtexfac CameraTexCoFactors\n"); } if (builtins & GPU_OBJECT_MATRIX) { - BLI_dynstr_append(ds, "\t#define objmat ModelMatrix\n"); + BLI_dynstr_append(ds, " #define objmat ModelMatrix\n"); } if (builtins & GPU_INVERSE_OBJECT_MATRIX) { - BLI_dynstr_append(ds, "\t#define objinv ModelMatrixInverse\n"); + BLI_dynstr_append(ds, " #define objinv ModelMatrixInverse\n"); } if (builtins & GPU_INVERSE_VIEW_MATRIX) { - BLI_dynstr_append(ds, "\t#define viewinv ViewMatrixInverse\n"); + BLI_dynstr_append(ds, " #define viewinv ViewMatrixInverse\n"); } if (builtins & GPU_LOC_TO_VIEW_MATRIX) { - BLI_dynstr_append(ds, "\t#define localtoviewmat (ViewMatrix * ModelMatrix)\n"); + BLI_dynstr_append(ds, " #define localtoviewmat (ViewMatrix * ModelMatrix)\n"); } if (builtins & GPU_INVERSE_LOC_TO_VIEW_MATRIX) { BLI_dynstr_append(ds, - "\t#define invlocaltoviewmat (ModelMatrixInverse * ViewMatrixInverse)\n"); + " #define invlocaltoviewmat (ModelMatrixInverse * ViewMatrixInverse)\n"); } if (builtins & GPU_VIEW_NORMAL) { BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, "\tvec3 n;\n"); - BLI_dynstr_append(ds, "\tworld_normals_get(n);\n"); - BLI_dynstr_append(ds, "\tvec3 facingnormal = transform_direction(ViewMatrix, n);\n"); + BLI_dynstr_append(ds, " vec3 n;\n"); + BLI_dynstr_append(ds, " world_normals_get(n);\n"); + BLI_dynstr_append(ds, " vec3 facingnormal = transform_direction(ViewMatrix, n);\n"); BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, "\tvec3 facingnormal = gl_FrontFacing ? viewNormal: -viewNormal;\n"); + BLI_dynstr_append(ds, " vec3 facingnormal = gl_FrontFacing ? viewNormal: -viewNormal;\n"); BLI_dynstr_append(ds, "#endif\n"); } if (builtins & GPU_WORLD_NORMAL) { - BLI_dynstr_append(ds, "\tvec3 facingwnormal;\n"); + BLI_dynstr_append(ds, " vec3 facingwnormal;\n"); if (builtins & GPU_VIEW_NORMAL) { BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, "\tfacingwnormal = n;\n"); + BLI_dynstr_append(ds, " facingwnormal = n;\n"); BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, "\tworld_normals_get(facingwnormal);\n"); + BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n"); BLI_dynstr_append(ds, "#endif\n"); } else { - BLI_dynstr_append(ds, "\tworld_normals_get(facingwnormal);\n"); + BLI_dynstr_append(ds, " world_normals_get(facingwnormal);\n"); } } if (builtins & GPU_VIEW_POSITION) { - BLI_dynstr_append(ds, "\t#define viewposition viewPosition\n"); + BLI_dynstr_append(ds, " #define viewposition viewPosition\n"); } codegen_declare_tmps(ds, graph); @@ -610,21 +583,6 @@ static char *code_generate_fragment(GPUMaterial *material, GPUNodeGraph *graph) BLI_dynstr_append(ds, "}\n"); - /* XXX This cannot go into gpu_shader_material.glsl because main() - * would be parsed and generate error */ - /* Old glsl mode compat. */ - /* TODO(fclem) This is only used by world shader now. get rid of it? */ - BLI_dynstr_append(ds, "#ifndef NODETREE_EXEC\n"); - BLI_dynstr_append(ds, "out vec4 fragColor;\n"); - BLI_dynstr_append(ds, "void main()\n"); - BLI_dynstr_append(ds, "{\n"); - BLI_dynstr_append(ds, "\tClosure cl = nodetree_exec();\n"); - BLI_dynstr_append(ds, - "\tfragColor = vec4(cl.radiance, " - "saturate(1.0 - avg(cl.transmittance)));\n"); - BLI_dynstr_append(ds, "}\n"); - BLI_dynstr_append(ds, "#endif\n\n"); - /* create shader */ code = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); @@ -649,6 +607,8 @@ static const char *attr_prefix_get(CustomDataType type) return "t"; case CD_MCOL: return "c"; + case CD_PROP_COLOR: + return "c"; case CD_AUTO_FROM_NAME: return "a"; default: @@ -657,23 +617,48 @@ static const char *attr_prefix_get(CustomDataType type) } } -static char *code_generate_vertex(GPUNodeGraph *graph, const char *vert_code, bool use_geom) +/* We talk about shader stage interface, not to be mistaken with GPUShaderInterface. */ +static char *code_generate_interface(GPUNodeGraph *graph, int builtins) { + if (BLI_listbase_is_empty(&graph->attributes) && + (builtins & (GPU_BARYCENTRIC_DIST | GPU_BARYCENTRIC_TEXCO)) == 0) { + return NULL; + } + DynStr *ds = BLI_dynstr_new(); - GPUNode *node; - GPUInput *input; - char *code; - int builtins = 0; - /* Hairs uv and col attributes are passed by bufferTextures. */ - BLI_dynstr_append(ds, - "#ifdef HAIR_SHADER\n" - "#define DEFINE_ATTR(type, attr) uniform samplerBuffer attr\n" - "#else\n" - "#define DEFINE_ATTR(type, attr) in type attr\n" - "#endif\n"); + BLI_dynstr_append(ds, "\n"); LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { + BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); + } + if (builtins & GPU_BARYCENTRIC_TEXCO) { + BLI_dynstr_append(ds, "vec2 barycentricTexCo;\n"); + } + if (builtins & GPU_BARYCENTRIC_DIST) { + BLI_dynstr_append(ds, "vec3 barycentricDist;\n"); + } + + char *code = BLI_dynstr_get_cstring(ds); + + BLI_dynstr_free(ds); + + return code; +} + +static char *code_generate_vertex(GPUNodeGraph *graph, + const char *interface_str, + const char *vert_code, + int builtins) +{ + DynStr *ds = BLI_dynstr_new(); + + BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); + + /* Inputs */ + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { + const char *type_str = gpu_data_type_to_string(attr->gputype); + const char *prefix = attr_prefix_get(attr->type); /* XXX FIXME : see notes in mesh_render_data_create() */ /* NOTE : Replicate changes to mesh_render_data_create() in draw_cache_impl_mesh.c */ if (attr->type == CD_ORCO) { @@ -682,188 +667,58 @@ static char *code_generate_vertex(GPUNodeGraph *graph, const char *vert_code, bo BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n"); } else if (attr->name[0] == '\0') { - BLI_dynstr_appendf(ds, - "DEFINE_ATTR(%s, %s);\n", - gpu_data_type_to_string(attr->gputype), - attr_prefix_get(attr->type)); - BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, attr_prefix_get(attr->type)); + BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s);\n", type_str, prefix); + BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, prefix); } else { char attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; GPU_vertformat_safe_attr_name(attr->name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); - BLI_dynstr_appendf(ds, - "DEFINE_ATTR(%s, %s%s);\n", - gpu_data_type_to_string(attr->gputype), - attr_prefix_get(attr->type), - attr_safe_name); - BLI_dynstr_appendf( - ds, "#define att%d %s%s\n", attr->id, attr_prefix_get(attr->type), attr_safe_name); + BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s%s);\n", type_str, prefix, attr_safe_name); + BLI_dynstr_appendf(ds, "#define att%d %s%s\n", attr->id, prefix, attr_safe_name); } - BLI_dynstr_appendf(ds, - "out %s var%d%s;\n", - gpu_data_type_to_string(attr->gputype), - attr->id, - use_geom ? "g" : ""); - } - - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { - if (input->source == GPU_SOURCE_BUILTIN) { - builtins |= input->builtin; - } - } - } - - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_appendf(ds, "out vec2 barycentricTexCo%s;\n", use_geom ? "g" : ""); - BLI_dynstr_append(ds, "#endif\n"); } - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "out vec3 barycentricPosg;\n"); + /* Outputs interface */ + if (interface_str) { + BLI_dynstr_appendf(ds, "out codegenInterface {%s};\n\n", interface_str); } - BLI_dynstr_append(ds, "\n#define USE_ATTR\n"); - - /* Prototype, defined later (this is because of matrices definition). */ - BLI_dynstr_append(ds, "void pass_attr(in vec3 position);\n"); - - BLI_dynstr_append(ds, "\n"); - - if (use_geom) { - /* XXX HACK: Eevee specific. */ - char *vert_new, *vert_new2; - vert_new = BLI_str_replaceN(vert_code, "worldPosition", "worldPositiong"); - vert_new2 = vert_new; - vert_new = BLI_str_replaceN(vert_new2, "viewPosition", "viewPositiong"); - MEM_freeN(vert_new2); - vert_new2 = vert_new; - vert_new = BLI_str_replaceN(vert_new2, "worldNormal", "worldNormalg"); - MEM_freeN(vert_new2); - vert_new2 = vert_new; - vert_new = BLI_str_replaceN(vert_new2, "viewNormal", "viewNormalg"); - MEM_freeN(vert_new2); - - BLI_dynstr_append(ds, vert_new); - - MEM_freeN(vert_new); - } - else { - BLI_dynstr_append(ds, vert_code); - } + /* Prototype. Needed for hair functions. */ + BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv);\n"); + BLI_dynstr_append(ds, "#define USE_ATTR\n\n"); + BLI_dynstr_append(ds, vert_code); BLI_dynstr_append(ds, "\n"); - BLI_dynstr_append(ds, use_geom ? "RESOURCE_ID_VARYING_GEOM\n" : "RESOURCE_ID_VARYING\n"); - - /* Prototype because defined later. */ - BLI_dynstr_append(ds, - "vec2 hair_get_customdata_vec2(const samplerBuffer);\n" - "vec3 hair_get_customdata_vec3(const samplerBuffer);\n" - "vec4 hair_get_customdata_vec4(const samplerBuffer);\n" - "vec3 hair_get_strand_pos(void);\n" - "int hair_get_base_id(void);\n" - "\n"); - - BLI_dynstr_append(ds, "void pass_attr(in vec3 position) {\n"); - - BLI_dynstr_append(ds, use_geom ? "\tPASS_RESOURCE_ID_GEOM\n" : "\tPASS_RESOURCE_ID\n"); - - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - - if (builtins & GPU_BARYCENTRIC_TEXCO) { - /* To match cycles without breaking into individual segment we encode if we need to invert - * the first component into the second component. We invert if the barycentricTexCo.y - * is NOT 0.0 or 1.0. */ - BLI_dynstr_append(ds, "\tint _base_id = hair_get_base_id();\n"); - BLI_dynstr_appendf( - ds, "\tbarycentricTexCo%s.x = float((_base_id %% 2) == 1);\n", use_geom ? "g" : ""); - BLI_dynstr_appendf( - ds, "\tbarycentricTexCo%s.y = float(((_base_id %% 4) %% 3) > 0);\n", use_geom ? "g" : ""); - } - - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "\tbarycentricPosg = position;\n"); - } - - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - if (attr->type == CD_TANGENT) { - /* Not supported by hairs */ - BLI_dynstr_appendf(ds, "\tvar%d%s = vec4(0.0);\n", attr->id, use_geom ? "g" : ""); - } - else if (attr->type == CD_ORCO) { - BLI_dynstr_appendf(ds, - "\tvar%d%s = OrcoTexCoFactors[0].xyz + (ModelMatrixInverse * " - "vec4(hair_get_strand_pos(), 1.0)).xyz * OrcoTexCoFactors[1].xyz;\n", - attr->id, - use_geom ? "g" : ""); - /* TODO: fix ORCO with modifiers. */ - } - else { - BLI_dynstr_appendf(ds, - "\tvar%d%s = hair_get_customdata_%s(att%d);\n", - attr->id, - use_geom ? "g" : "", - gpu_data_type_to_string(attr->gputype), - attr->id); - } - } - - BLI_dynstr_append(ds, "#else /* MESH_SHADER */\n"); + BLI_dynstr_append(ds, "void pass_attr(vec3 position, mat3 normalmat, mat4 modelmatinv) {\n"); /* GPU_BARYCENTRIC_TEXCO cannot be computed based on gl_VertexID * for MESH_SHADER because of indexed drawing. In this case a * geometry shader is needed. */ - + if (builtins & GPU_BARYCENTRIC_TEXCO) { + BLI_dynstr_appendf(ds, " barycentricTexCo = barycentric_get();\n"); + } if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "\tbarycentricPosg = (ModelMatrix * vec4(position, 1.0)).xyz;\n"); + BLI_dynstr_appendf(ds, " barycentricDist = vec3(0);\n"); } LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { if (attr->type == CD_TANGENT) { /* silly exception */ - BLI_dynstr_appendf(ds, - "\tvar%d%s.xyz = transpose(mat3(ModelMatrixInverse)) * att%d.xyz;\n", - attr->id, - use_geom ? "g" : "", - attr->id); - BLI_dynstr_appendf(ds, "\tvar%d%s.w = att%d.w;\n", attr->id, use_geom ? "g" : "", attr->id); - /* Normalize only if vector is not null. */ - BLI_dynstr_appendf(ds, - "\tfloat lvar%d = dot(var%d%s.xyz, var%d%s.xyz);\n", - attr->id, - attr->id, - use_geom ? "g" : "", - attr->id, - use_geom ? "g" : ""); - BLI_dynstr_appendf(ds, - "\tvar%d%s.xyz *= (lvar%d > 0.0) ? inversesqrt(lvar%d) : 1.0;\n", - attr->id, - use_geom ? "g" : "", - attr->id, - attr->id); + BLI_dynstr_appendf(ds, " var%d = tangent_get(att%d, normalmat);\n", attr->id, attr->id); } else if (attr->type == CD_ORCO) { - BLI_dynstr_appendf(ds, - "\tvar%d%s = OrcoTexCoFactors[0].xyz + position *" - " OrcoTexCoFactors[1].xyz;\n", - attr->id, - use_geom ? "g" : ""); - /* See mesh_create_loop_orco() for explanation. */ - BLI_dynstr_appendf(ds, - "\tif (orco.w == 0.0) { var%d%s = orco.xyz * 0.5 + 0.5; }\n", - attr->id, - use_geom ? "g" : ""); + BLI_dynstr_appendf( + ds, " var%d = orco_get(position, modelmatinv, OrcoTexCoFactors, orco);\n", attr->id); } else { - BLI_dynstr_appendf(ds, "\tvar%d%s = att%d;\n", attr->id, use_geom ? "g" : "", attr->id); + const char *type_str = gpu_data_type_to_string(attr->gputype); + BLI_dynstr_appendf(ds, " var%d = GET_ATTR(%s, att%d);\n", attr->id, type_str, attr->id); } } - BLI_dynstr_append(ds, "#endif /* HAIR_SHADER */\n"); BLI_dynstr_append(ds, "}\n"); - code = BLI_dynstr_get_cstring(ds); + char *code = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); @@ -877,146 +732,46 @@ static char *code_generate_vertex(GPUNodeGraph *graph, const char *vert_code, bo } static char *code_generate_geometry(GPUNodeGraph *graph, + const char *interface_str, const char *geom_code, - const char *defines) + int builtins) { - DynStr *ds = BLI_dynstr_new(); - GPUNode *node; - GPUInput *input; - char *code; - int builtins = 0; - - /* XXX we should not make specific eevee cases here. */ - bool is_hair_shader = (strstr(defines, "HAIR_SHADER") != NULL); - - /* Create prototype because attributes cannot be declared before layout. */ - BLI_dynstr_append(ds, "void pass_attr(in int vert);\n"); - BLI_dynstr_append(ds, "void calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2);\n"); - BLI_dynstr_append(ds, "#define USE_ATTR\n"); - - /* Generate varying declarations. */ - for (node = graph->nodes.first; node; node = node->next) { - for (input = node->inputs.first; input; input = input->next) { - if (input->source == GPU_SOURCE_BUILTIN) { - builtins |= input->builtin; - } - } - } - - LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - BLI_dynstr_appendf(ds, "in %s var%dg[];\n", gpu_data_type_to_string(attr->gputype), attr->id); - BLI_dynstr_appendf(ds, "out %s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); + if (!geom_code) { + return NULL; } - if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, "in vec2 barycentricTexCog[];\n"); - BLI_dynstr_append(ds, "#endif\n"); - - BLI_dynstr_append(ds, "out vec2 barycentricTexCo;\n"); - } + DynStr *ds = BLI_dynstr_new(); - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "in vec3 barycentricPosg[];\n"); - BLI_dynstr_append(ds, "flat out vec3 barycentricDist;\n"); + /* Attributes, Shader interface; */ + if (interface_str) { + BLI_dynstr_appendf(ds, "in codegenInterface {%s} dataAttrIn[];\n\n", interface_str); + BLI_dynstr_appendf(ds, "out codegenInterface {%s} dataAttrOut;\n\n", interface_str); } - if (geom_code == NULL) { - /* Force geometry usage if GPU_BARYCENTRIC_DIST or GPU_BARYCENTRIC_TEXCO are used. - * Note: GPU_BARYCENTRIC_TEXCO only requires it if the shader is not drawing hairs. */ - if ((builtins & (GPU_BARYCENTRIC_DIST | GPU_BARYCENTRIC_TEXCO)) == 0 || is_hair_shader) { - /* Early out */ - BLI_dynstr_free(ds); - return NULL; - } - else { - /* Force geom shader usage */ - /* TODO put in external file. */ - BLI_dynstr_append(ds, "layout(triangles) in;\n"); - BLI_dynstr_append(ds, "layout(triangle_strip, max_vertices=3) out;\n"); - - BLI_dynstr_append(ds, "in vec3 worldPositiong[];\n"); - BLI_dynstr_append(ds, "in vec3 viewPositiong[];\n"); - BLI_dynstr_append(ds, "in vec3 worldNormalg[];\n"); - BLI_dynstr_append(ds, "in vec3 viewNormalg[];\n"); - - BLI_dynstr_append(ds, "out vec3 worldPosition;\n"); - BLI_dynstr_append(ds, "out vec3 viewPosition;\n"); - BLI_dynstr_append(ds, "out vec3 worldNormal;\n"); - BLI_dynstr_append(ds, "out vec3 viewNormal;\n"); - - BLI_dynstr_append(ds, datatoc_common_view_lib_glsl); - - BLI_dynstr_append(ds, "void main(){\n"); - - if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, - "\tcalc_barycentric_distances(barycentricPosg[0], barycentricPosg[1], " - "barycentricPosg[2]);\n"); - } - - for (int i = 0; i < 3; i++) { - BLI_dynstr_appendf(ds, "\tgl_Position = gl_in[%d].gl_Position;\n", i); - BLI_dynstr_appendf(ds, "\tgl_ClipDistance[0] = gl_in[%d].gl_ClipDistance[0];\n", i); - BLI_dynstr_appendf(ds, "\tpass_attr(%d);\n", i); - BLI_dynstr_append(ds, "\tEmitVertex();\n"); - } - BLI_dynstr_append(ds, "}\n"); - } - } - else { - BLI_dynstr_append(ds, geom_code); - } + BLI_dynstr_append(ds, datatoc_gpu_shader_codegen_lib_glsl); if (builtins & GPU_BARYCENTRIC_DIST) { - BLI_dynstr_append(ds, "void calc_barycentric_distances(vec3 pos0, vec3 pos1, vec3 pos2) {\n"); - BLI_dynstr_append(ds, "\tvec3 edge21 = pos2 - pos1;\n"); - BLI_dynstr_append(ds, "\tvec3 edge10 = pos1 - pos0;\n"); - BLI_dynstr_append(ds, "\tvec3 edge02 = pos0 - pos2;\n"); - BLI_dynstr_append(ds, "\tvec3 d21 = normalize(edge21);\n"); - BLI_dynstr_append(ds, "\tvec3 d10 = normalize(edge10);\n"); - BLI_dynstr_append(ds, "\tvec3 d02 = normalize(edge02);\n"); - - BLI_dynstr_append(ds, "\tfloat d = dot(d21, edge02);\n"); - BLI_dynstr_append(ds, "\tbarycentricDist.x = sqrt(dot(edge02, edge02) - d * d);\n"); - BLI_dynstr_append(ds, "\td = dot(d02, edge10);\n"); - BLI_dynstr_append(ds, "\tbarycentricDist.y = sqrt(dot(edge10, edge10) - d * d);\n"); - BLI_dynstr_append(ds, "\td = dot(d10, edge21);\n"); - BLI_dynstr_append(ds, "\tbarycentricDist.z = sqrt(dot(edge21, edge21) - d * d);\n"); - BLI_dynstr_append(ds, "}\n"); + /* geom_code should do something with this, but may not. */ + BLI_dynstr_append(ds, "#define DO_BARYCENTRIC_DISTANCES\n"); } - BLI_dynstr_append(ds, "RESOURCE_ID_VARYING\n"); - /* Generate varying assignments. */ - BLI_dynstr_append(ds, "void pass_attr(in int vert) {\n"); - - BLI_dynstr_append(ds, "\tPASS_RESOURCE_ID(vert)\n"); - - /* XXX HACK: Eevee specific. */ - if (geom_code == NULL) { - BLI_dynstr_append(ds, "\tworldPosition = worldPositiong[vert];\n"); - BLI_dynstr_append(ds, "\tviewPosition = viewPositiong[vert];\n"); - BLI_dynstr_append(ds, "\tworldNormal = worldNormalg[vert];\n"); - BLI_dynstr_append(ds, "\tviewNormal = viewNormalg[vert];\n"); - } + BLI_dynstr_append(ds, "#define USE_ATTR\n"); + BLI_dynstr_append(ds, "void pass_attr(const int vert) {\n"); if (builtins & GPU_BARYCENTRIC_TEXCO) { - BLI_dynstr_append(ds, "#ifdef HAIR_SHADER\n"); - BLI_dynstr_append(ds, "\tbarycentricTexCo = barycentricTexCog[vert];\n"); - BLI_dynstr_append(ds, "#else\n"); - BLI_dynstr_append(ds, "\tbarycentricTexCo.x = float((vert % 3) == 0);\n"); - BLI_dynstr_append(ds, "\tbarycentricTexCo.y = float((vert % 3) == 1);\n"); - BLI_dynstr_append(ds, "#endif\n"); + BLI_dynstr_append(ds, " dataAttrOut.barycentricTexCo = calc_barycentric_co(vert);\n"); } LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { /* TODO let shader choose what to do depending on what the attribute is. */ - BLI_dynstr_appendf(ds, "\tvar%d = var%dg[vert];\n", attr->id, attr->id); + BLI_dynstr_appendf(ds, " dataAttrOut.var%d = dataAttrIn[vert].var%d;\n", attr->id, attr->id); } - BLI_dynstr_append(ds, "}\n"); + BLI_dynstr_append(ds, "}\n\n"); - code = BLI_dynstr_get_cstring(ds); + BLI_dynstr_append(ds, geom_code); + + char *code = BLI_dynstr_get_cstring(ds); BLI_dynstr_free(ds); return code; @@ -1046,8 +801,17 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, * generated VBOs are ready to accept the future shader. */ gpu_node_graph_prune_unused(graph); + int builtins = 0; + LISTBASE_FOREACH (GPUNode *, node, &graph->nodes) { + LISTBASE_FOREACH (GPUInput *, input, &node->inputs) { + if (input->source == GPU_SOURCE_BUILTIN) { + builtins |= input->builtin; + } + } + } /* generate code */ - char *fragmentgen = code_generate_fragment(material, graph); + char *interface_str = code_generate_interface(graph, builtins); + char *fragmentgen = code_generate_fragment(material, graph, interface_str); /* Cache lookup: Reuse shaders already compiled */ uint32_t hash = gpu_pass_hash(fragmentgen, defines, &graph->attributes); @@ -1055,6 +819,7 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, if (pass_hash && (pass_hash->next == NULL || pass_hash->next->hash != hash)) { /* No collision, just return the pass. */ + MEM_SAFE_FREE(interface_str); MEM_freeN(fragmentgen); if (!gpu_pass_is_valid(pass_hash)) { /* Shader has already been created but failed to compile. */ @@ -1069,10 +834,11 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, GSet *used_libraries = gpu_material_used_libraries(material); char *tmp = gpu_material_library_generate_code(used_libraries, frag_lib); - char *geometrycode = code_generate_geometry(graph, geom_code, defines); - char *vertexcode = code_generate_vertex(graph, vert_code, (geometrycode != NULL)); + char *geometrycode = code_generate_geometry(graph, interface_str, geom_code, builtins); + char *vertexcode = code_generate_vertex(graph, interface_str, vert_code, builtins); char *fragmentcode = BLI_strdupcat(tmp, fragmentgen); + MEM_SAFE_FREE(interface_str); MEM_freeN(fragmentgen); MEM_freeN(tmp); diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h index ce20f495ba3..18ff4c3241d 100644 --- a/source/blender/gpu/intern/gpu_codegen.h +++ b/source/blender/gpu/intern/gpu_codegen.h @@ -26,6 +26,10 @@ #ifndef __GPU_CODEGEN_H__ #define __GPU_CODEGEN_H__ +#ifdef __cplusplus +extern "C" { +#endif + struct GPUMaterial; struct GPUNodeGraph; struct GPUOutput; @@ -68,4 +72,8 @@ void GPU_pass_release(GPUPass *pass); void gpu_codegen_init(void); void gpu_codegen_exit(void); +#ifdef __cplusplus +} +#endif + #endif /* __GPU_CODEGEN_H__ */ diff --git a/source/blender/gpu/intern/gpu_context.cpp b/source/blender/gpu/intern/gpu_context.cc index 0b9104e5349..0b9104e5349 100644 --- a/source/blender/gpu/intern/gpu_context.cpp +++ b/source/blender/gpu/intern/gpu_context.cc diff --git a/source/blender/gpu/intern/gpu_debug.c b/source/blender/gpu/intern/gpu_debug.cc index f7d6236071d..f7d6236071d 100644 --- a/source/blender/gpu/intern/gpu_debug.c +++ b/source/blender/gpu/intern/gpu_debug.cc diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c deleted file mode 100644 index 1c346217e9f..00000000000 --- a/source/blender/gpu/intern/gpu_draw.c +++ /dev/null @@ -1,1452 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup gpu - * - * Utility functions for dealing with OpenGL texture & material context, - * mipmap generation and light objects. - * - * These are some obscure rendering functions shared between the game engine (not anymore) - * and the blender, in this module to avoid duplication - * and abstract them away from the rest a bit. - */ - -#include <string.h> - -#include "BLI_blenlib.h" -#include "BLI_boxpack_2d.h" -#include "BLI_linklist.h" -#include "BLI_math.h" -#include "BLI_threads.h" -#include "BLI_utildefines.h" - -#include "DNA_image_types.h" -#include "DNA_movieclip_types.h" -#include "DNA_userdef_types.h" - -#include "MEM_guardedalloc.h" - -#include "IMB_colormanagement.h" -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" - -#include "BKE_global.h" -#include "BKE_image.h" -#include "BKE_main.h" -#include "BKE_movieclip.h" - -#include "GPU_draw.h" -#include "GPU_extensions.h" -#include "GPU_glew.h" -#include "GPU_platform.h" -#include "GPU_texture.h" - -#include "PIL_time.h" - -static void gpu_free_image_immediate(Image *ima); - -//* Checking powers of two for images since OpenGL ES requires it */ -#ifdef WITH_DDS -static bool is_power_of_2_resolution(int w, int h) -{ - return is_power_of_2_i(w) && is_power_of_2_i(h); -} -#endif - -static bool is_over_resolution_limit(GLenum textarget, int w, int h) -{ - int size = (textarget == GL_TEXTURE_CUBE_MAP) ? GPU_max_cube_map_size() : GPU_max_texture_size(); - int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size; - - return (w > reslimit || h > reslimit); -} - -static int smaller_power_of_2_limit(int num) -{ - int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, GPU_max_texture_size()) : - GPU_max_texture_size(); - /* take texture clamping into account */ - if (num > reslimit) { - return reslimit; - } - - return power_of_2_min_i(num); -} - -/* Current OpenGL state caching for GPU_set_tpage */ - -static struct GPUTextureState { - /* also controls min/mag filtering */ - bool domipmap; - /* only use when 'domipmap' is set */ - bool linearmipmap; - /* store this so that new images created while texture painting won't be set to mipmapped */ - bool texpaint; - - float anisotropic; -} GTS = {1, 0, 0, 1.0f}; - -/* Mipmap settings */ - -void GPU_set_mipmap(Main *bmain, bool mipmap) -{ - if (GTS.domipmap != mipmap) { - GPU_free_images(bmain); - GTS.domipmap = mipmap; - } -} - -void GPU_set_linear_mipmap(bool linear) -{ - if (GTS.linearmipmap != linear) { - GTS.linearmipmap = linear; - } -} - -bool GPU_get_mipmap(void) -{ - return GTS.domipmap && !GTS.texpaint; -} - -bool GPU_get_linear_mipmap(void) -{ - return GTS.linearmipmap; -} - -static GLenum gpu_get_mipmap_filter(bool mag) -{ - /* linearmipmap is off by default *when mipmapping is off, - * use unfiltered display */ - if (mag) { - if (GTS.domipmap) { - return GL_LINEAR; - } - else { - return GL_NEAREST; - } - } - else { - if (GTS.domipmap) { - if (GTS.linearmipmap) { - return GL_LINEAR_MIPMAP_LINEAR; - } - else { - return GL_LINEAR_MIPMAP_NEAREST; - } - } - else { - return GL_NEAREST; - } - } -} - -/* Anisotropic filtering settings */ -void GPU_set_anisotropic(float value) -{ - if (GTS.anisotropic != value) { - GPU_samplers_free(); - - /* Clamp value to the maximum value the graphics card supports */ - const float max = GPU_max_texture_anisotropy(); - if (value > max) { - value = max; - } - - GTS.anisotropic = value; - - GPU_samplers_init(); - } -} - -float GPU_get_anisotropic(void) -{ - return GTS.anisotropic; -} - -/* Set OpenGL state for an MTFace */ - -static GPUTexture **gpu_get_image_gputexture(Image *ima, GLenum textarget, const int multiview_eye) -{ - if (textarget == GL_TEXTURE_2D) { - return &(ima->gputexture[TEXTARGET_TEXTURE_2D][multiview_eye]); - } - else if (textarget == GL_TEXTURE_CUBE_MAP) { - return &(ima->gputexture[TEXTARGET_TEXTURE_CUBE_MAP][multiview_eye]); - } - else if (textarget == GL_TEXTURE_2D_ARRAY) { - return &(ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][multiview_eye]); - } - else if (textarget == GL_TEXTURE_1D_ARRAY) { - return &(ima->gputexture[TEXTARGET_TEXTURE_TILE_MAPPING][multiview_eye]); - } - - return NULL; -} - -static uint gpu_texture_create_tile_mapping(Image *ima, const int multiview_eye) -{ - GPUTexture *tilearray = ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][multiview_eye]; - - if (tilearray == NULL) { - return 0; - } - - float array_w = GPU_texture_width(tilearray); - float array_h = GPU_texture_height(tilearray); - - ImageTile *last_tile = ima->tiles.last; - /* Tiles are sorted by number. */ - int max_tile = last_tile->tile_number - 1001; - - /* create image */ - int bindcode; - glGenTextures(1, (GLuint *)&bindcode); - glBindTexture(GL_TEXTURE_1D_ARRAY, bindcode); - - int width = max_tile + 1; - float *data = MEM_callocN(width * 8 * sizeof(float), __func__); - for (int i = 0; i < width; i++) { - data[4 * i] = -1.0f; - } - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - int i = tile->tile_number - 1001; - data[4 * i] = tile->runtime.tilearray_layer; - - float *tile_info = &data[4 * width + 4 * i]; - tile_info[0] = tile->runtime.tilearray_offset[0] / array_w; - tile_info[1] = tile->runtime.tilearray_offset[1] / array_h; - tile_info[2] = tile->runtime.tilearray_size[0] / array_w; - tile_info[3] = tile->runtime.tilearray_size[1] / array_h; - } - - glTexImage2D(GL_TEXTURE_1D_ARRAY, 0, GL_RGBA32F, width, 2, 0, GL_RGBA, GL_FLOAT, data); - MEM_freeN(data); - - glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_1D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glBindTexture(GL_TEXTURE_1D_ARRAY, 0); - - return bindcode; -} - -typedef struct PackTile { - FixedSizeBoxPack boxpack; - ImageTile *tile; - float pack_score; -} PackTile; - -static int compare_packtile(const void *a, const void *b) -{ - const PackTile *tile_a = a; - const PackTile *tile_b = b; - - return tile_a->pack_score < tile_b->pack_score; -} - -static uint gpu_texture_create_tile_array(Image *ima, ImBuf *main_ibuf) -{ - int arraywidth = 0, arrayheight = 0; - - ListBase boxes = {NULL}; - - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - ImageUser iuser; - BKE_imageuser_default(&iuser); - iuser.tile = tile->tile_number; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); - - if (ibuf) { - PackTile *packtile = MEM_callocN(sizeof(PackTile), __func__); - packtile->tile = tile; - packtile->boxpack.w = ibuf->x; - packtile->boxpack.h = ibuf->y; - - if (is_over_resolution_limit( - GL_TEXTURE_2D_ARRAY, packtile->boxpack.w, packtile->boxpack.h)) { - packtile->boxpack.w = smaller_power_of_2_limit(packtile->boxpack.w); - packtile->boxpack.h = smaller_power_of_2_limit(packtile->boxpack.h); - } - arraywidth = max_ii(arraywidth, packtile->boxpack.w); - arrayheight = max_ii(arrayheight, packtile->boxpack.h); - - /* We sort the tiles by decreasing size, with an additional penalty term - * for high aspect ratios. This improves packing efficiency. */ - float w = packtile->boxpack.w, h = packtile->boxpack.h; - packtile->pack_score = max_ff(w, h) / min_ff(w, h) * w * h; - - BKE_image_release_ibuf(ima, ibuf, NULL); - BLI_addtail(&boxes, packtile); - } - } - - BLI_assert(arraywidth > 0 && arrayheight > 0); - - BLI_listbase_sort(&boxes, compare_packtile); - int arraylayers = 0; - /* Keep adding layers until all tiles are packed. */ - while (boxes.first != NULL) { - ListBase packed = {NULL}; - BLI_box_pack_2d_fixedarea(&boxes, arraywidth, arrayheight, &packed); - BLI_assert(packed.first != NULL); - - LISTBASE_FOREACH (PackTile *, packtile, &packed) { - ImageTile *tile = packtile->tile; - int *tileoffset = tile->runtime.tilearray_offset; - int *tilesize = tile->runtime.tilearray_size; - - tileoffset[0] = packtile->boxpack.x; - tileoffset[1] = packtile->boxpack.y; - tilesize[0] = packtile->boxpack.w; - tilesize[1] = packtile->boxpack.h; - tile->runtime.tilearray_layer = arraylayers; - } - - BLI_freelistN(&packed); - arraylayers++; - } - - /* create image */ - int bindcode; - glGenTextures(1, (GLuint *)&bindcode); - glBindTexture(GL_TEXTURE_2D_ARRAY, bindcode); - - GLenum data_type, internal_format; - if (main_ibuf->rect_float) { - data_type = GL_FLOAT; - internal_format = (!(main_ibuf->flags & IB_halffloat) && (ima->flag & IMA_HIGH_BITDEPTH)) ? - GL_RGBA32F : - GL_RGBA16F; - } - else { - data_type = GL_UNSIGNED_BYTE; - internal_format = GL_RGBA8; - if (!IMB_colormanagement_space_is_data(main_ibuf->rect_colorspace) && - !IMB_colormanagement_space_is_scene_linear(main_ibuf->rect_colorspace)) { - internal_format = GL_SRGB8_ALPHA8; - } - } - - glTexImage3D(GL_TEXTURE_2D_ARRAY, - 0, - internal_format, - arraywidth, - arrayheight, - arraylayers, - 0, - GL_RGBA, - data_type, - NULL); - - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - int tilelayer = tile->runtime.tilearray_layer; - int *tileoffset = tile->runtime.tilearray_offset; - int *tilesize = tile->runtime.tilearray_size; - - if (tilesize[0] == 0 || tilesize[1] == 0) { - continue; - } - - ImageUser iuser; - BKE_imageuser_default(&iuser); - iuser.tile = tile->tile_number; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); - - if (ibuf) { - bool needs_scale = (ibuf->x != tilesize[0] || ibuf->y != tilesize[1]); - - ImBuf *scale_ibuf = NULL; - if (ibuf->rect_float) { - float *rect_float = ibuf->rect_float; - - const bool store_premultiplied = ima->alpha_mode != IMA_ALPHA_STRAIGHT; - if (ibuf->channels != 4 || !store_premultiplied) { - rect_float = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); - IMB_colormanagement_imbuf_to_float_texture( - rect_float, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); - } - - float *pixeldata = rect_float; - if (needs_scale) { - scale_ibuf = IMB_allocFromBuffer(NULL, rect_float, ibuf->x, ibuf->y, 4); - IMB_scaleImBuf(scale_ibuf, tilesize[0], tilesize[1]); - pixeldata = scale_ibuf->rect_float; - } - - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - tileoffset[0], - tileoffset[1], - tilelayer, - tilesize[0], - tilesize[1], - 1, - GL_RGBA, - GL_FLOAT, - pixeldata); - - if (rect_float != ibuf->rect_float) { - MEM_freeN(rect_float); - } - } - else { - unsigned int *rect = ibuf->rect; - - if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { - rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); - IMB_colormanagement_imbuf_to_byte_texture((uchar *)rect, - 0, - 0, - ibuf->x, - ibuf->y, - ibuf, - internal_format == GL_SRGB8_ALPHA8, - ima->alpha_mode == IMA_ALPHA_PREMUL); - } - - unsigned int *pixeldata = rect; - if (needs_scale) { - scale_ibuf = IMB_allocFromBuffer(rect, NULL, ibuf->x, ibuf->y, 4); - IMB_scaleImBuf(scale_ibuf, tilesize[0], tilesize[1]); - pixeldata = scale_ibuf->rect; - } - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - tileoffset[0], - tileoffset[1], - tilelayer, - tilesize[0], - tilesize[1], - 1, - GL_RGBA, - GL_UNSIGNED_BYTE, - pixeldata); - - if (rect != ibuf->rect) { - MEM_freeN(rect); - } - } - if (scale_ibuf != NULL) { - IMB_freeImBuf(scale_ibuf); - } - } - - BKE_image_release_ibuf(ima, ibuf, NULL); - } - - if (GPU_get_mipmap()) { - glGenerateMipmap(GL_TEXTURE_2D_ARRAY); - if (ima) { - ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; - } - } - - glBindTexture(GL_TEXTURE_2D_ARRAY, 0); - - return bindcode; -} - -static uint gpu_texture_create_from_ibuf(Image *ima, ImBuf *ibuf, int textarget) -{ - uint bindcode = 0; - const bool mipmap = GPU_get_mipmap(); - const bool half_float = (ibuf->flags & IB_halffloat) != 0; - -#ifdef WITH_DDS - if (ibuf->ftype == IMB_FTYPE_DDS) { - /* DDS is loaded directly in compressed form. */ - GPU_create_gl_tex_compressed(&bindcode, textarget, ima, ibuf); - return bindcode; - } -#endif - - /* Regular uncompressed texture. */ - float *rect_float = ibuf->rect_float; - uchar *rect = (uchar *)ibuf->rect; - bool compress_as_srgb = false; - - if (rect_float == NULL) { - /* Byte image is in original colorspace from the file. If the file is sRGB - * scene linear, or non-color data no conversion is needed. Otherwise we - * compress as scene linear + sRGB transfer function to avoid precision loss - * in common cases. - * - * We must also convert to premultiplied for correct texture interpolation - * and consistency with float images. */ - if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { - compress_as_srgb = !IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace); - - rect = MEM_mallocN(sizeof(uchar) * 4 * ibuf->x * ibuf->y, __func__); - if (rect == NULL) { - return bindcode; - } - - /* Texture storage of images is defined by the alpha mode of the image. The - * downside of this is that there can be artifacts near alpha edges. However, - * this allows us to use sRGB texture formats and preserves color values in - * zero alpha areas, and appears generally closer to what game engines that we - * want to be compatible with do. */ - const bool store_premultiplied = ima ? (ima->alpha_mode == IMA_ALPHA_PREMUL) : true; - IMB_colormanagement_imbuf_to_byte_texture( - rect, 0, 0, ibuf->x, ibuf->y, ibuf, compress_as_srgb, store_premultiplied); - } - } - else { - /* Float image is already in scene linear colorspace or non-color data by - * convention, no colorspace conversion needed. But we do require 4 channels - * currently. */ - const bool store_premultiplied = ima ? (ima->alpha_mode != IMA_ALPHA_STRAIGHT) : false; - - if (ibuf->channels != 4 || !store_premultiplied) { - rect_float = MEM_mallocN(sizeof(float) * 4 * ibuf->x * ibuf->y, __func__); - if (rect_float == NULL) { - return bindcode; - } - IMB_colormanagement_imbuf_to_float_texture( - rect_float, 0, 0, ibuf->x, ibuf->y, ibuf, store_premultiplied); - } - } - - /* Create OpenGL texture. */ - GPU_create_gl_tex(&bindcode, - (uint *)rect, - rect_float, - ibuf->x, - ibuf->y, - textarget, - mipmap, - half_float, - compress_as_srgb, - ima); - - /* Free buffers if needed. */ - if (rect && rect != (uchar *)ibuf->rect) { - MEM_freeN(rect); - } - if (rect_float && rect_float != ibuf->rect_float) { - MEM_freeN(rect_float); - } - - return bindcode; -} - -static GPUTexture **gpu_get_movieclip_gputexture(MovieClip *clip, - MovieClipUser *cuser, - GLenum textarget) -{ - MovieClip_RuntimeGPUTexture *tex; - for (tex = clip->runtime.gputextures.first; tex; tex = tex->next) { - if (memcmp(&tex->user, cuser, sizeof(MovieClipUser)) == 0) { - break; - } - } - - if (tex == NULL) { - tex = MEM_mallocN(sizeof(MovieClip_RuntimeGPUTexture), __func__); - - for (int i = 0; i < TEXTARGET_COUNT; i++) { - tex->gputexture[i] = NULL; - } - - memcpy(&tex->user, cuser, sizeof(MovieClipUser)); - BLI_addtail(&clip->runtime.gputextures, tex); - } - - if (textarget == GL_TEXTURE_2D) { - return &tex->gputexture[TEXTARGET_TEXTURE_2D]; - } - else if (textarget == GL_TEXTURE_CUBE_MAP) { - return &tex->gputexture[TEXTARGET_TEXTURE_CUBE_MAP]; - } - - return NULL; -} - -static ImBuf *update_do_scale(uchar *rect, - float *rect_float, - int *x, - int *y, - int *w, - int *h, - int limit_w, - int limit_h, - int full_w, - int full_h) -{ - /* Partial update with scaling. */ - float xratio = limit_w / (float)full_w; - float yratio = limit_h / (float)full_h; - - int part_w = *w, part_h = *h; - - /* Find sub coordinates in scaled image. Take ceiling because we will be - * losing 1 pixel due to rounding errors in x,y. */ - *x *= xratio; - *y *= yratio; - *w = (int)ceil(xratio * (*w)); - *h = (int)ceil(yratio * (*h)); - - /* ...but take back if we are over the limit! */ - if (*x + *w > limit_w) { - (*w)--; - } - if (*y + *h > limit_h) { - (*h)--; - } - - /* Scale pixels. */ - ImBuf *ibuf = IMB_allocFromBuffer((uint *)rect, rect_float, part_w, part_h, 4); - IMB_scaleImBuf(ibuf, *w, *h); - - return ibuf; -} - -static void gpu_texture_update_scaled_array(uchar *rect, - float *rect_float, - int full_w, - int full_h, - int x, - int y, - int layer, - const int *tile_offset, - const int *tile_size, - int w, - int h) -{ - ImBuf *ibuf = update_do_scale( - rect, rect_float, &x, &y, &w, &h, tile_size[0], tile_size[1], full_w, full_h); - - /* Shift to account for tile packing. */ - x += tile_offset[0]; - y += tile_offset[1]; - - if (ibuf->rect_float) { - glTexSubImage3D( - GL_TEXTURE_2D_ARRAY, 0, x, y, layer, w, h, 1, GL_RGBA, GL_FLOAT, ibuf->rect_float); - } - else { - glTexSubImage3D( - GL_TEXTURE_2D_ARRAY, 0, x, y, layer, w, h, 1, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); - } - - IMB_freeImBuf(ibuf); -} - -static void gpu_texture_update_scaled( - uchar *rect, float *rect_float, int full_w, int full_h, int x, int y, int w, int h) -{ - /* Partial update with scaling. */ - int limit_w = smaller_power_of_2_limit(full_w); - int limit_h = smaller_power_of_2_limit(full_h); - - ImBuf *ibuf = update_do_scale( - rect, rect_float, &x, &y, &w, &h, limit_w, limit_h, full_w, full_h); - - if (ibuf->rect_float) { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, ibuf->rect_float); - } - else { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); - } - - IMB_freeImBuf(ibuf); -} - -static void gpu_texture_update_unscaled(uchar *rect, - float *rect_float, - int x, - int y, - int layer, - int w, - int h, - GLint tex_stride, - GLint tex_offset) -{ - /* Partial update without scaling. Stride and offset are used to copy only a - * subset of a possible larger buffer than what we are updating. */ - GLint row_length; - glGetIntegerv(GL_UNPACK_ROW_LENGTH, &row_length); - glPixelStorei(GL_UNPACK_ROW_LENGTH, tex_stride); - - if (layer >= 0) { - if (rect_float == NULL) { - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - x, - y, - layer, - w, - h, - 1, - GL_RGBA, - GL_UNSIGNED_BYTE, - rect + tex_offset); - } - else { - glTexSubImage3D(GL_TEXTURE_2D_ARRAY, - 0, - x, - y, - layer, - w, - h, - 1, - GL_RGBA, - GL_FLOAT, - rect_float + tex_offset); - } - } - else { - if (rect_float == NULL) { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rect + tex_offset); - } - else { - glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, rect_float + tex_offset); - } - } - - glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length); -} - -static void gpu_texture_update_from_ibuf( - GPUTexture *tex, Image *ima, ImBuf *ibuf, ImageTile *tile, int x, int y, int w, int h) -{ - /* Partial update of texture for texture painting. This is often much - * quicker than fully updating the texture for high resolution images. */ - GPU_texture_bind(tex, 0); - - bool scaled; - if (tile != NULL) { - int *tilesize = tile->runtime.tilearray_size; - scaled = (ibuf->x != tilesize[0]) || (ibuf->y != tilesize[1]); - } - else { - scaled = is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y); - } - - if (scaled) { - /* Extra padding to account for bleed from neighboring pixels. */ - const int padding = 4; - const int xmax = min_ii(x + w + padding, ibuf->x); - const int ymax = min_ii(y + h + padding, ibuf->y); - x = max_ii(x - padding, 0); - y = max_ii(y - padding, 0); - w = xmax - x; - h = ymax - y; - } - - /* Get texture data pointers. */ - float *rect_float = ibuf->rect_float; - uchar *rect = (uchar *)ibuf->rect; - GLint tex_stride = ibuf->x; - GLint tex_offset = ibuf->channels * (y * ibuf->x + x); - - if (rect_float == NULL) { - /* Byte pixels. */ - if (!IMB_colormanagement_space_is_data(ibuf->rect_colorspace)) { - const bool compress_as_srgb = !IMB_colormanagement_space_is_scene_linear( - ibuf->rect_colorspace); - - rect = MEM_mallocN(sizeof(uchar) * 4 * w * h, __func__); - if (rect == NULL) { - return; - } - - tex_stride = w; - tex_offset = 0; - - /* Convert to scene linear with sRGB compression, and premultiplied for - * correct texture interpolation. */ - const bool store_premultiplied = (ima->alpha_mode == IMA_ALPHA_PREMUL); - IMB_colormanagement_imbuf_to_byte_texture( - rect, x, y, w, h, ibuf, compress_as_srgb, store_premultiplied); - } - } - else { - /* Float pixels. */ - const bool store_premultiplied = (ima->alpha_mode != IMA_ALPHA_STRAIGHT); - - if (ibuf->channels != 4 || scaled || !store_premultiplied) { - rect_float = MEM_mallocN(sizeof(float) * 4 * w * h, __func__); - if (rect_float == NULL) { - return; - } - - tex_stride = w; - tex_offset = 0; - - IMB_colormanagement_imbuf_to_float_texture( - rect_float, x, y, w, h, ibuf, store_premultiplied); - } - } - - if (scaled) { - /* Slower update where we first have to scale the input pixels. */ - if (tile != NULL) { - int *tileoffset = tile->runtime.tilearray_offset; - int *tilesize = tile->runtime.tilearray_size; - int tilelayer = tile->runtime.tilearray_layer; - gpu_texture_update_scaled_array( - rect, rect_float, ibuf->x, ibuf->y, x, y, tilelayer, tileoffset, tilesize, w, h); - } - else { - gpu_texture_update_scaled(rect, rect_float, ibuf->x, ibuf->y, x, y, w, h); - } - } - else { - /* Fast update at same resolution. */ - if (tile != NULL) { - int *tileoffset = tile->runtime.tilearray_offset; - int tilelayer = tile->runtime.tilearray_layer; - gpu_texture_update_unscaled(rect, - rect_float, - x + tileoffset[0], - y + tileoffset[1], - tilelayer, - w, - h, - tex_stride, - tex_offset); - } - else { - gpu_texture_update_unscaled(rect, rect_float, x, y, -1, w, h, tex_stride, tex_offset); - } - } - - /* Free buffers if needed. */ - if (rect && rect != (uchar *)ibuf->rect) { - MEM_freeN(rect); - } - if (rect_float && rect_float != ibuf->rect_float) { - MEM_freeN(rect_float); - } - - if (GPU_get_mipmap()) { - glGenerateMipmap((tile != NULL) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D); - } - else { - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; - } - - GPU_texture_unbind(tex); -} - -/* Get the GPUTexture for a given `Image`. - * - * `iuser` and `ibuf` are mutual exclusive parameters. The caller can pass the `ibuf` when already - * available. It is also required when requesting the GPUTexture for a render result. */ -GPUTexture *GPU_texture_from_blender(Image *ima, ImageUser *iuser, ImBuf *ibuf, int textarget) -{ -#ifndef GPU_STANDALONE - if (ima == NULL) { - return NULL; - } - - /* currently, gpu refresh tagging is used by ima sequences */ - if (ima->gpuflag & IMA_GPU_REFRESH) { - gpu_free_image_immediate(ima); - ima->gpuflag &= ~IMA_GPU_REFRESH; - } - - /* Tag as in active use for garbage collector. */ - BKE_image_tag_time(ima); - - /* Test if we already have a texture. */ - GPUTexture **tex = gpu_get_image_gputexture(ima, textarget, iuser ? iuser->multiview_eye : 0); - if (*tex) { - return *tex; - } - - /* Check if we have a valid image. If not, we return a dummy - * texture with zero bindcode so we don't keep trying. */ - uint bindcode = 0; - ImageTile *tile = BKE_image_get_tile(ima, 0); - if (tile == NULL || tile->ok == 0) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; - } - - /* check if we have a valid image buffer */ - ImBuf *ibuf_intern = ibuf; - if (ibuf_intern == NULL) { - ibuf_intern = BKE_image_acquire_ibuf(ima, iuser, NULL); - if (ibuf_intern == NULL) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; - } - } - - if (textarget == GL_TEXTURE_2D_ARRAY) { - bindcode = gpu_texture_create_tile_array(ima, ibuf_intern); - } - else if (textarget == GL_TEXTURE_1D_ARRAY) { - bindcode = gpu_texture_create_tile_mapping(ima, iuser ? iuser->multiview_eye : 0); - } - else { - bindcode = gpu_texture_create_from_ibuf(ima, ibuf_intern, textarget); - } - - /* if `ibuf` was given, we don't own the `ibuf_intern` */ - if (ibuf == NULL) { - BKE_image_release_ibuf(ima, ibuf_intern, NULL); - } - - *tex = GPU_texture_from_bindcode(textarget, bindcode); - - GPU_texture_orig_size_set(*tex, ibuf_intern->x, ibuf_intern->y); - - return *tex; -#endif - return NULL; -} - -GPUTexture *GPU_texture_from_movieclip(MovieClip *clip, MovieClipUser *cuser, int textarget) -{ -#ifndef GPU_STANDALONE - if (clip == NULL) { - return NULL; - } - - GPUTexture **tex = gpu_get_movieclip_gputexture(clip, cuser, textarget); - if (*tex) { - return *tex; - } - - /* check if we have a valid image buffer */ - uint bindcode = 0; - ImBuf *ibuf = BKE_movieclip_get_ibuf(clip, cuser); - if (ibuf == NULL) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; - } - - bindcode = gpu_texture_create_from_ibuf(NULL, ibuf, textarget); - IMB_freeImBuf(ibuf); - - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; -#else - return NULL; -#endif -} - -void GPU_free_texture_movieclip(struct MovieClip *clip) -{ - /* number of gpu textures to keep around as cache - * We don't want to keep too many GPU textures for - * movie clips around, as they can be large.*/ - const int MOVIECLIP_NUM_GPUTEXTURES = 1; - - while (BLI_listbase_count(&clip->runtime.gputextures) > MOVIECLIP_NUM_GPUTEXTURES) { - MovieClip_RuntimeGPUTexture *tex = BLI_pophead(&clip->runtime.gputextures); - for (int i = 0; i < TEXTARGET_COUNT; i++) { - /* free glsl image binding */ - if (tex->gputexture[i]) { - GPU_texture_free(tex->gputexture[i]); - tex->gputexture[i] = NULL; - } - } - MEM_freeN(tex); - } -} - -static void **gpu_gen_cube_map(uint *rect, float *frect, int rectw, int recth) -{ - size_t block_size = frect ? sizeof(float[4]) : sizeof(uchar[4]); - void **sides = NULL; - int h = recth / 2; - int w = rectw / 3; - - if (w != h) { - return sides; - } - - /* PosX, NegX, PosY, NegY, PosZ, NegZ */ - sides = MEM_mallocN(sizeof(void *) * 6, ""); - for (int i = 0; i < 6; i++) { - sides[i] = MEM_mallocN(block_size * w * h, ""); - } - - /* divide image into six parts */ - /* ______________________ - * | | | | - * | NegX | NegY | PosX | - * |______|______|______| - * | | | | - * | NegZ | PosZ | PosY | - * |______|______|______| - */ - if (frect) { - float(*frectb)[4] = (float(*)[4])frect; - float(**fsides)[4] = (float(**)[4])sides; - - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - memcpy(&fsides[0][x * h + y], &frectb[(recth - y - 1) * rectw + 2 * w + x], block_size); - memcpy(&fsides[1][x * h + y], &frectb[(y + h) * rectw + w - 1 - x], block_size); - memcpy( - &fsides[3][y * w + x], &frectb[(recth - y - 1) * rectw + 2 * w - 1 - x], block_size); - memcpy(&fsides[5][y * w + x], &frectb[(h - y - 1) * rectw + w - 1 - x], block_size); - } - memcpy(&fsides[2][y * w], frectb[y * rectw + 2 * w], block_size * w); - memcpy(&fsides[4][y * w], frectb[y * rectw + w], block_size * w); - } - } - else { - uint **isides = (uint **)sides; - - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - isides[0][x * h + y] = rect[(recth - y - 1) * rectw + 2 * w + x]; - isides[1][x * h + y] = rect[(y + h) * rectw + w - 1 - x]; - isides[3][y * w + x] = rect[(recth - y - 1) * rectw + 2 * w - 1 - x]; - isides[5][y * w + x] = rect[(h - y - 1) * rectw + w - 1 - x]; - } - memcpy(&isides[2][y * w], &rect[y * rectw + 2 * w], block_size * w); - memcpy(&isides[4][y * w], &rect[y * rectw + w], block_size * w); - } - } - - return sides; -} - -static void gpu_del_cube_map(void **cube_map) -{ - int i; - if (cube_map == NULL) { - return; - } - for (i = 0; i < 6; i++) { - MEM_freeN(cube_map[i]); - } - MEM_freeN(cube_map); -} - -/* Image *ima can be NULL */ -void GPU_create_gl_tex(uint *bind, - uint *rect, - float *frect, - int rectw, - int recth, - int textarget, - bool mipmap, - bool half_float, - bool use_srgb, - Image *ima) -{ - ImBuf *ibuf = NULL; - - if (textarget == GL_TEXTURE_2D && is_over_resolution_limit(textarget, rectw, recth)) { - int tpx = rectw; - int tpy = recth; - rectw = smaller_power_of_2_limit(rectw); - recth = smaller_power_of_2_limit(recth); - - if (frect) { - ibuf = IMB_allocFromBuffer(NULL, frect, tpx, tpy, 4); - IMB_scaleImBuf(ibuf, rectw, recth); - - frect = ibuf->rect_float; - } - else { - ibuf = IMB_allocFromBuffer(rect, NULL, tpx, tpy, 4); - IMB_scaleImBuf(ibuf, rectw, recth); - - rect = ibuf->rect; - } - } - - /* create image */ - glGenTextures(1, (GLuint *)bind); - glBindTexture(textarget, *bind); - - GLenum float_format = (!half_float && (ima && (ima->flag & IMA_HIGH_BITDEPTH))) ? GL_RGBA32F : - GL_RGBA16F; - GLenum internal_format = (frect) ? float_format : (use_srgb) ? GL_SRGB8_ALPHA8 : GL_RGBA8; - - if (textarget == GL_TEXTURE_2D) { - if (frect) { - glTexImage2D(GL_TEXTURE_2D, 0, internal_format, rectw, recth, 0, GL_RGBA, GL_FLOAT, frect); - } - else { - glTexImage2D( - GL_TEXTURE_2D, 0, internal_format, rectw, recth, 0, GL_RGBA, GL_UNSIGNED_BYTE, rect); - } - - if (GPU_get_mipmap() && mipmap) { - glGenerateMipmap(GL_TEXTURE_2D); - if (ima) { - ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; - } - } - } - else if (textarget == GL_TEXTURE_CUBE_MAP) { - int w = rectw / 3, h = recth / 2; - - if (h == w && is_power_of_2_i(h) && !is_over_resolution_limit(textarget, h, w)) { - void **cube_map = gpu_gen_cube_map(rect, frect, rectw, recth); - GLenum type = frect ? GL_FLOAT : GL_UNSIGNED_BYTE; - - if (cube_map) { - for (int i = 0; i < 6; i++) { - glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, - 0, - internal_format, - w, - h, - 0, - GL_RGBA, - type, - cube_map[i]); - } - } - - if (GPU_get_mipmap() && mipmap) { - glGenerateMipmap(GL_TEXTURE_CUBE_MAP); - - if (ima) { - ima->gpuflag |= IMA_GPU_MIPMAP_COMPLETE; - } - } - - gpu_del_cube_map(cube_map); - } - else { - printf("Incorrect envmap size\n"); - } - } - - glBindTexture(textarget, 0); - - if (ibuf) { - IMB_freeImBuf(ibuf); - } -} - -/** - * GPU_upload_dxt_texture() assumes that the texture is already bound and ready to go. - * This is so the viewport and the BGE can share some code. - * Returns false if the provided ImBuf doesn't have a supported DXT compression format - */ -bool GPU_upload_dxt_texture(ImBuf *ibuf, bool use_srgb) -{ -#ifdef WITH_DDS - GLint format = 0; - int blocksize, height, width, i, size, offset = 0; - - width = ibuf->x; - height = ibuf->y; - - if (GLEW_EXT_texture_compression_s3tc) { - if (ibuf->dds_data.fourcc == FOURCC_DXT1) { - format = (use_srgb) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT : - GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - } - else if (ibuf->dds_data.fourcc == FOURCC_DXT3) { - format = (use_srgb) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : - GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - } - else if (ibuf->dds_data.fourcc == FOURCC_DXT5) { - format = (use_srgb) ? GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : - GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - } - } - - if (format == 0) { - fprintf(stderr, "Unable to find a suitable DXT compression, falling back to uncompressed\n"); - return false; - } - - if (!is_power_of_2_resolution(width, height)) { - fprintf( - stderr, - "Unable to load non-power-of-two DXT image resolution, falling back to uncompressed\n"); - return false; - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - - blocksize = (ibuf->dds_data.fourcc == FOURCC_DXT1) ? 8 : 16; - for (i = 0; i < ibuf->dds_data.nummipmaps && (width || height); i++) { - if (width == 0) { - width = 1; - } - if (height == 0) { - height = 1; - } - - size = ((width + 3) / 4) * ((height + 3) / 4) * blocksize; - - glCompressedTexImage2D( - GL_TEXTURE_2D, i, format, width, height, 0, size, ibuf->dds_data.data + offset); - - offset += size; - width >>= 1; - height >>= 1; - } - - /* set number of mipmap levels we have, needed in case they don't go down to 1x1 */ - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i - 1); - - return true; -#else - UNUSED_VARS(ibuf, use_srgb); - return false; -#endif -} - -void GPU_create_gl_tex_compressed(unsigned int *bind, int textarget, Image *ima, ImBuf *ibuf) -{ - /* For DDS we only support data, scene linear and sRGB. Converting to - * different colorspace would break the compression. */ - const bool use_srgb = !(IMB_colormanagement_space_is_data(ibuf->rect_colorspace) || - IMB_colormanagement_space_is_scene_linear(ibuf->rect_colorspace)); - const bool mipmap = GPU_get_mipmap(); - const bool half_float = (ibuf->flags & IB_halffloat) != 0; - -#ifndef WITH_DDS - (void)ibuf; - /* Fall back to uncompressed if DDS isn't enabled */ - GPU_create_gl_tex( - bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, half_float, use_srgb, ima); -#else - glGenTextures(1, (GLuint *)bind); - glBindTexture(textarget, *bind); - - if (textarget == GL_TEXTURE_2D && GPU_upload_dxt_texture(ibuf, use_srgb) == 0) { - glDeleteTextures(1, (GLuint *)bind); - GPU_create_gl_tex( - bind, ibuf->rect, NULL, ibuf->x, ibuf->y, textarget, mipmap, half_float, use_srgb, ima); - } - - glBindTexture(textarget, 0); -#endif -} - -/* these two functions are called on entering and exiting texture paint mode, - * temporary disabling/enabling mipmapping on all images for quick texture - * updates with glTexSubImage2D. images that didn't change don't have to be - * re-uploaded to OpenGL */ -void GPU_paint_set_mipmap(Main *bmain, bool mipmap) -{ -#ifndef GPU_STANDALONE - if (!GTS.domipmap) { - return; - } - - GTS.texpaint = !mipmap; - - if (mipmap) { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { - if (BKE_image_has_opengl_texture(ima)) { - if (ima->gpuflag & IMA_GPU_MIPMAP_COMPLETE) { - for (int eye = 0; eye < 2; eye++) { - for (int a = 0; a < TEXTARGET_COUNT; a++) { - if (ELEM(a, TEXTARGET_TEXTURE_2D, TEXTARGET_TEXTURE_2D_ARRAY)) { - GPUTexture *tex = ima->gputexture[a][eye]; - if (tex != NULL) { - GPU_texture_bind(tex, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0)); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - GPU_texture_unbind(tex); - } - } - } - } - } - else { - GPU_free_image(ima); - } - } - else { - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; - } - } - } - else { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { - if (BKE_image_has_opengl_texture(ima)) { - for (int eye = 0; eye < 2; eye++) { - for (int a = 0; a < TEXTARGET_COUNT; a++) { - if (ELEM(a, TEXTARGET_TEXTURE_2D, TEXTARGET_TEXTURE_2D_ARRAY)) { - GPUTexture *tex = ima->gputexture[a][eye]; - if (tex != NULL) { - GPU_texture_bind(tex, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - GPU_texture_unbind(tex); - } - } - } - } - } - else { - ima->gpuflag &= ~IMA_GPU_MIPMAP_COMPLETE; - } - } - } -#endif /* GPU_STANDALONE */ -} - -void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, int h) -{ -#ifndef GPU_STANDALONE - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - - if ((ibuf == NULL) || (w == 0) || (h == 0)) { - /* Full reload of texture. */ - GPU_free_image(ima); - } - - GPUTexture *tex = ima->gputexture[TEXTARGET_TEXTURE_2D][0]; - /* Check if we need to update the main gputexture. */ - if (tex != NULL && tile == ima->tiles.first) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, NULL, x, y, w, h); - } - - /* Check if we need to update the array gputexture. */ - tex = ima->gputexture[TEXTARGET_TEXTURE_2D_ARRAY][0]; - if (tex != NULL) { - gpu_texture_update_from_ibuf(tex, ima, ibuf, tile, x, y, w, h); - } - - BKE_image_release_ibuf(ima, ibuf, NULL); -#endif -} - -static LinkNode *image_free_queue = NULL; -static ThreadMutex img_queue_mutex = BLI_MUTEX_INITIALIZER; - -static void gpu_queue_image_for_free(Image *ima) -{ - BLI_mutex_lock(&img_queue_mutex); - BLI_linklist_prepend(&image_free_queue, ima); - BLI_mutex_unlock(&img_queue_mutex); -} - -void GPU_free_unused_buffers(Main *bmain) -{ - if (!BLI_thread_is_main()) { - return; - } - - BLI_mutex_lock(&img_queue_mutex); - - /* images */ - for (LinkNode *node = image_free_queue; node; node = node->next) { - Image *ima = node->link; - - /* check in case it was freed in the meantime */ - if (bmain && BLI_findindex(&bmain->images, ima) != -1) { - GPU_free_image(ima); - } - } - - BLI_linklist_free(image_free_queue, NULL); - image_free_queue = NULL; - - BLI_mutex_unlock(&img_queue_mutex); -} - -static void gpu_free_image_immediate(Image *ima) -{ - for (int eye = 0; eye < 2; eye++) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - /* free glsl image binding */ - if (ima->gputexture[i][eye] != NULL) { - GPU_texture_free(ima->gputexture[i][eye]); - ima->gputexture[i][eye] = NULL; - } - } - } - - ima->gpuflag &= ~(IMA_GPU_MIPMAP_COMPLETE); -} - -void GPU_free_image(Image *ima) -{ - if (!BLI_thread_is_main()) { - gpu_queue_image_for_free(ima); - return; - } - - gpu_free_image_immediate(ima); -} - -void GPU_free_images(Main *bmain) -{ - if (bmain) { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { - GPU_free_image(ima); - } - } -} - -/* same as above but only free animated images */ -void GPU_free_images_anim(Main *bmain) -{ - if (bmain) { - for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { - if (BKE_image_is_animated(ima)) { - GPU_free_image(ima); - } - } - } -} - -void GPU_free_images_old(Main *bmain) -{ - static int lasttime = 0; - int ctime = (int)PIL_check_seconds_timer(); - - /* - * Run garbage collector once for every collecting period of time - * if textimeout is 0, that's the option to NOT run the collector - */ - if (U.textimeout == 0 || ctime % U.texcollectrate || ctime == lasttime) { - return; - } - - /* of course not! */ - if (G.is_rendering) { - return; - } - - lasttime = ctime; - - Image *ima = bmain->images.first; - while (ima) { - if ((ima->flag & IMA_NOCOLLECT) == 0 && ctime - ima->lastused > U.textimeout) { - /* If it's in GL memory, deallocate and set time tag to current time - * This gives textures a "second chance" to be used before dying. */ - if (BKE_image_has_opengl_texture(ima)) { - GPU_free_image(ima); - ima->lastused = ctime; - } - /* Otherwise, just kill the buffers */ - else { - BKE_image_free_buffers(ima); - } - } - ima = ima->id.next; - } -} diff --git a/source/blender/gpu/intern/gpu_draw_smoke.c b/source/blender/gpu/intern/gpu_draw_smoke.c deleted file mode 100644 index 80c59ed47c9..00000000000 --- a/source/blender/gpu/intern/gpu_draw_smoke.c +++ /dev/null @@ -1,422 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup gpu - * - * GPU fluid drawing functions. - */ - -#include <string.h> - -#include "BLI_math.h" -#include "BLI_utildefines.h" - -#include "DNA_fluid_types.h" -#include "DNA_modifier_types.h" - -#include "MEM_guardedalloc.h" - -#include "BKE_colorband.h" - -#include "GPU_draw.h" -#include "GPU_glew.h" -#include "GPU_texture.h" - -#ifdef WITH_FLUID -# include "manta_fluid_API.h" -#endif - -/* -------------------------------------------------------------------- */ -/** \name Private API - * \{ */ - -#ifdef WITH_FLUID - -enum { - TFUNC_FLAME_SPECTRUM = 0, - TFUNC_COLOR_RAMP = 1, -}; - -# define TFUNC_WIDTH 256 - -static void create_flame_spectrum_texture(float *data) -{ -# define FIRE_THRESH 7 -# define MAX_FIRE_ALPHA 0.06f -# define FULL_ON_FIRE 100 - - float *spec_pixels = MEM_mallocN(TFUNC_WIDTH * 4 * 16 * 16 * sizeof(float), "spec_pixels"); - - blackbody_temperature_to_rgb_table(data, TFUNC_WIDTH, 1500, 3000); - - for (int i = 0; i < 16; i++) { - for (int j = 0; j < 16; j++) { - for (int k = 0; k < TFUNC_WIDTH; k++) { - int index = (j * TFUNC_WIDTH * 16 + i * TFUNC_WIDTH + k) * 4; - if (k >= FIRE_THRESH) { - spec_pixels[index] = (data[k * 4]); - spec_pixels[index + 1] = (data[k * 4 + 1]); - spec_pixels[index + 2] = (data[k * 4 + 2]); - spec_pixels[index + 3] = MAX_FIRE_ALPHA * - ((k > FULL_ON_FIRE) ? - 1.0f : - (k - FIRE_THRESH) / ((float)FULL_ON_FIRE - FIRE_THRESH)); - } - else { - zero_v4(&spec_pixels[index]); - } - } - } - } - - memcpy(data, spec_pixels, sizeof(float) * 4 * TFUNC_WIDTH); - - MEM_freeN(spec_pixels); - -# undef FIRE_THRESH -# undef MAX_FIRE_ALPHA -# undef FULL_ON_FIRE -} - -static void create_color_ramp(const struct ColorBand *coba, float *data) -{ - for (int i = 0; i < TFUNC_WIDTH; i++) { - BKE_colorband_evaluate(coba, (float)i / TFUNC_WIDTH, &data[i * 4]); - } -} - -static GPUTexture *create_transfer_function(int type, const struct ColorBand *coba) -{ - float *data = MEM_mallocN(sizeof(float) * 4 * TFUNC_WIDTH, __func__); - - switch (type) { - case TFUNC_FLAME_SPECTRUM: - create_flame_spectrum_texture(data); - break; - case TFUNC_COLOR_RAMP: - create_color_ramp(coba, data); - break; - } - - GPUTexture *tex = GPU_texture_create_1d(TFUNC_WIDTH, GPU_RGBA8, data, NULL); - - MEM_freeN(data); - - return tex; -} - -static void swizzle_texture_channel_single(GPUTexture *tex) -{ - /* Swizzle texture channels so that we get useful RGBA values when sampling - * a texture with fewer channels, e.g. when using density as color. */ - GPU_texture_bind(tex, 0); - GPU_texture_swizzle_channel_auto(tex, 1); - GPU_texture_unbind(tex); -} - -static GPUTexture *create_field_texture(FluidDomainSettings *mds) -{ - float *field = NULL; - - switch (mds->coba_field) { - case FLUID_DOMAIN_FIELD_DENSITY: - field = manta_smoke_get_density(mds->fluid); - break; - case FLUID_DOMAIN_FIELD_HEAT: - field = manta_smoke_get_heat(mds->fluid); - break; - case FLUID_DOMAIN_FIELD_FUEL: - field = manta_smoke_get_fuel(mds->fluid); - break; - case FLUID_DOMAIN_FIELD_REACT: - field = manta_smoke_get_react(mds->fluid); - break; - case FLUID_DOMAIN_FIELD_FLAME: - field = manta_smoke_get_flame(mds->fluid); - break; - case FLUID_DOMAIN_FIELD_VELOCITY_X: - field = manta_get_velocity_x(mds->fluid); - break; - case FLUID_DOMAIN_FIELD_VELOCITY_Y: - field = manta_get_velocity_y(mds->fluid); - break; - case FLUID_DOMAIN_FIELD_VELOCITY_Z: - field = manta_get_velocity_z(mds->fluid); - break; - case FLUID_DOMAIN_FIELD_COLOR_R: - field = manta_smoke_get_color_r(mds->fluid); - break; - case FLUID_DOMAIN_FIELD_COLOR_G: - field = manta_smoke_get_color_g(mds->fluid); - break; - case FLUID_DOMAIN_FIELD_COLOR_B: - field = manta_smoke_get_color_b(mds->fluid); - break; - case FLUID_DOMAIN_FIELD_FORCE_X: - field = manta_get_force_x(mds->fluid); - break; - case FLUID_DOMAIN_FIELD_FORCE_Y: - field = manta_get_force_y(mds->fluid); - break; - case FLUID_DOMAIN_FIELD_FORCE_Z: - field = manta_get_force_z(mds->fluid); - break; - default: - return NULL; - } - - GPUTexture *tex = GPU_texture_create_nD( - mds->res[0], mds->res[1], mds->res[2], 3, field, GPU_R8, GPU_DATA_FLOAT, 0, true, NULL); - - swizzle_texture_channel_single(tex); - return tex; -} - -static GPUTexture *create_density_texture(FluidDomainSettings *mds, int highres) -{ - int *dim = (highres) ? mds->res_noise : mds->res; - - float *data; - if (highres) { - data = manta_smoke_turbulence_get_density(mds->fluid); - } - else { - data = manta_smoke_get_density(mds->fluid); - } - - GPUTexture *tex = GPU_texture_create_nD( - dim[0], dim[1], dim[2], 3, data, GPU_R8, GPU_DATA_FLOAT, 0, true, NULL); - - swizzle_texture_channel_single(tex); - - return tex; -} - -static GPUTexture *create_color_texture(FluidDomainSettings *mds, int highres) -{ - const bool has_color = (highres) ? manta_smoke_turbulence_has_colors(mds->fluid) : - manta_smoke_has_colors(mds->fluid); - - if (!has_color) { - return NULL; - } - - int cell_count = (highres) ? manta_smoke_turbulence_get_cells(mds->fluid) : mds->total_cells; - int *dim = (highres) ? mds->res_noise : mds->res; - float *data = MEM_callocN(sizeof(float) * cell_count * 4, "smokeColorTexture"); - - if (data == NULL) { - return NULL; - } - - if (highres) { - manta_smoke_turbulence_get_rgba(mds->fluid, data, 0); - } - else { - manta_smoke_get_rgba(mds->fluid, data, 0); - } - - GPUTexture *tex = GPU_texture_create_nD( - dim[0], dim[1], dim[2], 3, data, GPU_RGBA8, GPU_DATA_FLOAT, 0, true, NULL); - - MEM_freeN(data); - - return tex; -} - -static GPUTexture *create_flame_texture(FluidDomainSettings *mds, int highres) -{ - float *source = NULL; - const bool has_fuel = (highres) ? manta_smoke_turbulence_has_fuel(mds->fluid) : - manta_smoke_has_fuel(mds->fluid); - int *dim = (highres) ? mds->res_noise : mds->res; - - if (!has_fuel) { - return NULL; - } - - if (highres) { - source = manta_smoke_turbulence_get_flame(mds->fluid); - } - else { - source = manta_smoke_get_flame(mds->fluid); - } - - GPUTexture *tex = GPU_texture_create_nD( - dim[0], dim[1], dim[2], 3, source, GPU_R8, GPU_DATA_FLOAT, 0, true, NULL); - - swizzle_texture_channel_single(tex); - - return tex; -} - -#endif /* WITH_FLUID */ - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Public API - * \{ */ - -void GPU_free_smoke(FluidModifierData *mmd) -{ - if (mmd->type & MOD_FLUID_TYPE_DOMAIN && mmd->domain) { - if (mmd->domain->tex_density) { - GPU_texture_free(mmd->domain->tex_density); - mmd->domain->tex_density = NULL; - } - - if (mmd->domain->tex_color) { - GPU_texture_free(mmd->domain->tex_color); - mmd->domain->tex_color = NULL; - } - - if (mmd->domain->tex_shadow) { - GPU_texture_free(mmd->domain->tex_shadow); - mmd->domain->tex_shadow = NULL; - } - - if (mmd->domain->tex_flame) { - GPU_texture_free(mmd->domain->tex_flame); - mmd->domain->tex_flame = NULL; - } - - if (mmd->domain->tex_flame_coba) { - GPU_texture_free(mmd->domain->tex_flame_coba); - mmd->domain->tex_flame_coba = NULL; - } - - if (mmd->domain->tex_coba) { - GPU_texture_free(mmd->domain->tex_coba); - mmd->domain->tex_coba = NULL; - } - - if (mmd->domain->tex_field) { - GPU_texture_free(mmd->domain->tex_field); - mmd->domain->tex_field = NULL; - } - } -} - -void GPU_create_smoke_coba_field(FluidModifierData *mmd) -{ -#ifndef WITH_FLUID - UNUSED_VARS(mmd); -#else - if (mmd->type & MOD_FLUID_TYPE_DOMAIN) { - FluidDomainSettings *mds = mmd->domain; - - if (!mds->tex_field) { - mds->tex_field = create_field_texture(mds); - } - if (!mds->tex_coba) { - mds->tex_coba = create_transfer_function(TFUNC_COLOR_RAMP, mds->coba); - } - } -#endif -} - -void GPU_create_smoke(FluidModifierData *mmd, int highres) -{ -#ifndef WITH_FLUID - UNUSED_VARS(mmd, highres); -#else - if (mmd->type & MOD_FLUID_TYPE_DOMAIN) { - FluidDomainSettings *mds = mmd->domain; - - if (!mds->tex_density) { - mds->tex_density = create_density_texture(mds, highres); - } - if (!mds->tex_color) { - mds->tex_color = create_color_texture(mds, highres); - } - if (!mds->tex_flame) { - mds->tex_flame = create_flame_texture(mds, highres); - } - if (!mds->tex_flame_coba && mds->tex_flame) { - mds->tex_flame_coba = create_transfer_function(TFUNC_FLAME_SPECTRUM, NULL); - } - if (!mds->tex_shadow) { - mds->tex_shadow = GPU_texture_create_nD(mds->res[0], - mds->res[1], - mds->res[2], - 3, - manta_smoke_get_shadow(mds->fluid), - GPU_R8, - GPU_DATA_FLOAT, - 0, - true, - NULL); - } - } -#endif /* WITH_FLUID */ -} - -void GPU_create_smoke_velocity(FluidModifierData *mmd) -{ -#ifndef WITH_FLUID - UNUSED_VARS(mmd); -#else - if (mmd->type & MOD_FLUID_TYPE_DOMAIN) { - FluidDomainSettings *mds = mmd->domain; - - const float *vel_x = manta_get_velocity_x(mds->fluid); - const float *vel_y = manta_get_velocity_y(mds->fluid); - const float *vel_z = manta_get_velocity_z(mds->fluid); - - if (ELEM(NULL, vel_x, vel_y, vel_z)) { - return; - } - - if (!mds->tex_velocity_x) { - mds->tex_velocity_x = GPU_texture_create_3d( - mds->res[0], mds->res[1], mds->res[2], GPU_R16F, vel_x, NULL); - mds->tex_velocity_y = GPU_texture_create_3d( - mds->res[0], mds->res[1], mds->res[2], GPU_R16F, vel_y, NULL); - mds->tex_velocity_z = GPU_texture_create_3d( - mds->res[0], mds->res[1], mds->res[2], GPU_R16F, vel_z, NULL); - } - } -#endif /* WITH_FLUID */ -} - -/* TODO Unify with the other GPU_free_smoke. */ -void GPU_free_smoke_velocity(FluidModifierData *mmd) -{ - if (mmd->type & MOD_FLUID_TYPE_DOMAIN && mmd->domain) { - if (mmd->domain->tex_velocity_x) { - GPU_texture_free(mmd->domain->tex_velocity_x); - } - - if (mmd->domain->tex_velocity_y) { - GPU_texture_free(mmd->domain->tex_velocity_y); - } - - if (mmd->domain->tex_velocity_z) { - GPU_texture_free(mmd->domain->tex_velocity_z); - } - - mmd->domain->tex_velocity_x = NULL; - mmd->domain->tex_velocity_y = NULL; - mmd->domain->tex_velocity_z = NULL; - } -} - -/** \} */ diff --git a/source/blender/gpu/intern/gpu_element.c b/source/blender/gpu/intern/gpu_element.cc index 036588b4a48..9f104ab3fec 100644 --- a/source/blender/gpu/intern/gpu_element.c +++ b/source/blender/gpu/intern/gpu_element.cc @@ -26,6 +26,7 @@ #include "MEM_guardedalloc.h" #include "GPU_element.h" +#include "GPU_glew.h" #include "gpu_context_private.h" @@ -37,21 +38,18 @@ static GLenum convert_index_type_to_gl(GPUIndexBufType type) { - static const GLenum table[] = { - [GPU_INDEX_U16] = GL_UNSIGNED_SHORT, - [GPU_INDEX_U32] = GL_UNSIGNED_INT, - }; - return table[type]; +#if GPU_TRACK_INDEX_RANGE + return (type == GPU_INDEX_U32) ? GL_UNSIGNED_INT : GL_UNSIGNED_SHORT; +#else + return GL_UNSIGNED_INT; +#endif } uint GPU_indexbuf_size_get(const GPUIndexBuf *elem) { #if GPU_TRACK_INDEX_RANGE - static const uint table[] = { - [GPU_INDEX_U16] = sizeof(GLushort), - [GPU_INDEX_U32] = sizeof(GLuint), - }; - return elem->index_len * table[elem->index_type]; + return elem->index_len * + ((elem->index_type == GPU_INDEX_U32) ? sizeof(GLuint) : sizeof(GLshort)); #else return elem->index_len * sizeof(GLuint); #endif @@ -86,7 +84,7 @@ void GPU_indexbuf_init_ex(GPUIndexBufBuilder *builder, builder->max_index_len = index_len; builder->index_len = 0; // start empty builder->prim_type = prim_type; - builder->data = MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data"); + builder->data = (uint *)MEM_callocN(builder->max_index_len * sizeof(uint), "GPUIndexBuf data"); } void GPU_indexbuf_init(GPUIndexBufBuilder *builder, @@ -241,7 +239,7 @@ void GPU_indexbuf_set_tri_restart(GPUIndexBufBuilder *builder, uint elem) GPUIndexBuf *GPU_indexbuf_create_subrange(GPUIndexBuf *elem_src, uint start, uint length) { - GPUIndexBuf *elem = MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); + GPUIndexBuf *elem = (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); GPU_indexbuf_create_subrange_in_place(elem, elem_src, start, length); return elem; } @@ -331,7 +329,7 @@ static void squeeze_indices_short(GPUIndexBufBuilder *builder, GPUIndexBuf *GPU_indexbuf_build(GPUIndexBufBuilder *builder) { - GPUIndexBuf *elem = MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); + GPUIndexBuf *elem = (GPUIndexBuf *)MEM_callocN(sizeof(GPUIndexBuf), "GPUIndexBuf"); GPU_indexbuf_build_in_place(builder, elem); return elem; } diff --git a/source/blender/gpu/intern/gpu_extensions.c b/source/blender/gpu/intern/gpu_extensions.cc index 469abefca68..32c1bf6e2d3 100644 --- a/source/blender/gpu/intern/gpu_extensions.c +++ b/source/blender/gpu/intern/gpu_extensions.cc @@ -31,6 +31,8 @@ #include "BKE_global.h" #include "MEM_guardedalloc.h" +#include "DNA_userdef_types.h" + #include "GPU_extensions.h" #include "GPU_framebuffer.h" #include "GPU_glew.h" @@ -71,7 +73,7 @@ static struct GPUGlobal { int samples_color_texture_max; float line_width_range[2]; /* workaround for different calculation of dfdy factors on GPUs. Some GPUs/drivers - * calculate dfdy in shader differently when drawing to an offscreen buffer. First + * calculate dfdy in shader differently when drawing to an off-screen buffer. First * number is factor on screen and second is off-screen */ float dfdyfactors[2]; float max_anisotropy; @@ -84,9 +86,9 @@ static struct GPUGlobal { * GL_TEXTURE_MAX_LEVEL is higher than the target mip. * We need a workaround in this cases. */ bool mip_render_workaround; - /* There is an issue with the glBlitFramebuffer on MacOS with radeon pro graphics. - * Blitting depth with GL_DEPTH24_STENCIL8 is buggy so the workaround is to use - * GPU_DEPTH32F_STENCIL8. Then Blitting depth will work but blitting stencil will + /* There is an issue with the #glBlitFramebuffer on MacOS with radeon pro graphics. + * Blitting depth with#GL_DEPTH24_STENCIL8 is buggy so the workaround is to use + * #GPU_DEPTH32F_STENCIL8. Then Blitting depth will work but blitting stencil will * still be broken. */ bool depth_blitting_workaround; /* Crappy driver don't know how to map framebuffer slot to output vars... @@ -96,7 +98,7 @@ static struct GPUGlobal { /* Some crappy Intel drivers don't work well with shaders created in different * rendering contexts. */ bool context_local_shaders_workaround; - /* Intel drivers exhibit artifacts when using glCopyImageSubData & workbench antialiasing. + /* Intel drivers exhibit artifacts when using #glCopyImageSubData & workbench anti-aliasing. * (see T76273) */ bool texture_copy_workaround; } GG = {1, 0}; @@ -104,7 +106,8 @@ static struct GPUGlobal { static void gpu_detect_mip_render_workaround(void) { int cube_size = 2; - float *source_pix = MEM_callocN(sizeof(float) * 4 * 6 * cube_size * cube_size, __func__); + float *source_pix = (float *)MEM_callocN(sizeof(float) * 4 * 6 * cube_size * cube_size, + __func__); float clear_color[4] = {1.0f, 0.5f, 0.0f, 0.0f}; GPUTexture *tex = GPU_texture_create_cube(cube_size, GPU_RGBA16F, source_pix, NULL); @@ -123,7 +126,7 @@ static void gpu_detect_mip_render_workaround(void) GPU_framebuffer_restore(); GPU_framebuffer_free(fb); - float *data = GPU_texture_read(tex, GPU_DATA_FLOAT, 1); + float *data = (float *)GPU_texture_read(tex, GPU_DATA_FLOAT, 1); GG.mip_render_workaround = !equals_v4v4(clear_color, data); MEM_freeN(data); @@ -238,6 +241,13 @@ bool GPU_crappy_amd_driver(void) return GG.broken_amd_driver; } +int GPU_texture_size_with_limit(int res) +{ + int size = GPU_max_texture_size(); + int reslimit = (U.glreslimit != 0) ? min_ii(U.glreslimit, size) : size; + return min_ii(reslimit, res); +} + void gpu_extensions_init(void) { /* during 2.8 development each platform has its own OpenGL minimum requirements @@ -288,6 +298,19 @@ void gpu_extensions_init(void) } } + if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE) && + strstr(renderer, "AMD VERDE")) { + /* We have issues with this specific renderer. (see T74024) */ + GG.unused_fb_slot_workaround = true; + GG.broken_amd_driver = true; + } + + if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE) && + strstr(version, "Mesa 19.3.4")) { + /* Fix slowdown on this particular driver. (see T77641) */ + GG.broken_amd_driver = true; + } + if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_MAC, GPU_DRIVER_OFFICIAL)) { if (strstr(renderer, "AMD Radeon Pro") || strstr(renderer, "AMD Radeon R9") || strstr(renderer, "AMD Radeon RX")) { @@ -298,13 +321,20 @@ void gpu_extensions_init(void) if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_OFFICIAL)) { /* Limit this fix to older hardware with GL < 4.5. This means Broadwell GPUs are * covered since they only support GL 4.4 on windows. - * This fixes some issues with workbench antialiasing on Win + Intel GPU. (see T76273) */ + * This fixes some issues with workbench anti-aliasing on Win + Intel GPU. (see T76273) */ if (!GLEW_VERSION_4_5) { GG.texture_copy_workaround = true; } } - GG.glew_arb_base_instance_is_supported = GLEW_ARB_base_instance; + /* Limit support for GLEW_ARB_base_instance to OpenGL 4.0 and higher. NVIDIA Quadro FX 4800 + * (TeraScale) report that they support GLEW_ARB_base_instance, but the driver does not support + * GLEW_ARB_draw_indirect as it has an OpenGL3 context what also matches the minimum needed + * requirements. + * + * We use it as a target for glMapBuffer(Range) what is part of the OpenGL 4 API. So better + * disable it when we don't have an OpenGL4 context (See T77657) */ + GG.glew_arb_base_instance_is_supported = GLEW_ARB_base_instance && GLEW_VERSION_4_0; GG.glew_arb_texture_cube_map_array_is_supported = GLEW_ARB_texture_cube_map_array; gpu_detect_mip_render_workaround(); @@ -319,7 +349,6 @@ void gpu_extensions_init(void) GG.depth_blitting_workaround = true; GG.unused_fb_slot_workaround = true; GG.texture_copy_workaround = true; - GG.context_local_shaders_workaround = GLEW_ARB_get_program_binary; } /* Special fix for theses specific GPUs. @@ -327,7 +356,13 @@ void gpu_extensions_init(void) if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_OFFICIAL) && (strstr(renderer, "HD Graphics 620") || strstr(renderer, "HD Graphics 630"))) { GG.mip_render_workaround = true; - GG.context_local_shaders_workaround = GLEW_ARB_get_program_binary; + } + + /* Intel Ivy Bridge GPU's seems to have buggy cube-map array support. (see T75943) */ + if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_WIN, GPU_DRIVER_OFFICIAL) && + (strstr(renderer, "HD Graphics 4000") || strstr(renderer, "HD Graphics 4400") || + strstr(renderer, "HD Graphics 2500"))) { + GG.glew_arb_texture_cube_map_array_is_supported = false; } /* df/dy calculation factors, those are dependent on driver */ @@ -385,7 +420,7 @@ void gpu_extensions_exit(void) bool GPU_mem_stats_supported(void) { #ifndef GPU_STANDALONE - return (GLEW_NVX_gpu_memory_info || GLEW_ATI_meminfo) && (G.debug & G_DEBUG_GPU_MEM); + return (GLEW_NVX_gpu_memory_info || GLEW_ATI_meminfo); #else return false; #endif @@ -413,3 +448,11 @@ void GPU_mem_stats_get(int *totalmem, int *freemem) *freemem = 0; } } + +/* Return support for the active context + window. */ +bool GPU_stereo_quadbuffer_support(void) +{ + GLboolean stereo = GL_FALSE; + glGetBooleanv(GL_STEREO, &stereo); + return stereo == GL_TRUE; +} diff --git a/source/blender/gpu/intern/gpu_framebuffer.c b/source/blender/gpu/intern/gpu_framebuffer.cc index 5af9364b92c..056a449ac6d 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.c +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -28,7 +28,6 @@ #include "BLI_utildefines.h" #include "GPU_batch.h" -#include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_framebuffer.h" #include "GPU_shader.h" @@ -53,6 +52,10 @@ typedef enum { GPU_FB_MAX_ATTACHEMENT, } GPUAttachmentType; +#define FOREACH_ATTACHMENT_RANGE(att, _start, _end) \ + for (GPUAttachmentType att = static_cast<GPUAttachmentType>(_start); att < _end; \ + att = static_cast<GPUAttachmentType>(att + 1)) + #define GPU_FB_MAX_COLOR_ATTACHMENT (GPU_FB_MAX_ATTACHEMENT - GPU_FB_COLOR_ATTACHMENT0) #define GPU_FB_DIRTY_DRAWBUFFER (1 << 15) @@ -74,17 +77,25 @@ struct GPUFrameBuffer { static GLenum convert_attachment_type_to_gl(GPUAttachmentType type) { - static const GLenum table[] = { - [GPU_FB_DEPTH_ATTACHMENT] = GL_DEPTH_ATTACHMENT, - [GPU_FB_DEPTH_STENCIL_ATTACHMENT] = GL_DEPTH_STENCIL_ATTACHMENT, - [GPU_FB_COLOR_ATTACHMENT0] = GL_COLOR_ATTACHMENT0, - [GPU_FB_COLOR_ATTACHMENT1] = GL_COLOR_ATTACHMENT1, - [GPU_FB_COLOR_ATTACHMENT2] = GL_COLOR_ATTACHMENT2, - [GPU_FB_COLOR_ATTACHMENT3] = GL_COLOR_ATTACHMENT3, - [GPU_FB_COLOR_ATTACHMENT4] = GL_COLOR_ATTACHMENT4, - [GPU_FB_COLOR_ATTACHMENT5] = GL_COLOR_ATTACHMENT5, - }; - return table[type]; +#define ATTACHMENT(type) \ + case GPU_FB_##type: { \ + return GL_##type; \ + } \ + ((void)0) + + switch (type) { + ATTACHMENT(DEPTH_ATTACHMENT); + ATTACHMENT(DEPTH_STENCIL_ATTACHMENT); + ATTACHMENT(COLOR_ATTACHMENT0); + ATTACHMENT(COLOR_ATTACHMENT1); + ATTACHMENT(COLOR_ATTACHMENT2); + ATTACHMENT(COLOR_ATTACHMENT3); + ATTACHMENT(COLOR_ATTACHMENT4); + ATTACHMENT(COLOR_ATTACHMENT5); + default: + BLI_assert(0); + return GL_COLOR_ATTACHMENT0; + } } static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot) @@ -98,7 +109,7 @@ static GPUAttachmentType attachment_type_from_tex(GPUTexture *tex, int slot) case GPU_DEPTH32F_STENCIL8: return GPU_FB_DEPTH_STENCIL_ATTACHMENT; default: - return GPU_FB_COLOR_ATTACHMENT0 + slot; + return static_cast<GPUAttachmentType>(GPU_FB_COLOR_ATTACHMENT0 + slot); } } @@ -198,7 +209,7 @@ GPUFrameBuffer *GPU_framebuffer_create(void) { /* We generate the FB object later at first use in order to * create the framebuffer in the right opengl context. */ - return MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer"); + return (GPUFrameBuffer *)MEM_callocN(sizeof(GPUFrameBuffer), "GPUFrameBuffer"); } static void gpu_framebuffer_init(GPUFrameBuffer *fb) @@ -210,7 +221,8 @@ static void gpu_framebuffer_init(GPUFrameBuffer *fb) void GPU_framebuffer_free(GPUFrameBuffer *fb) { - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { + for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { + GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); if (fb->attachments[type].tex != NULL) { GPU_framebuffer_texture_detach(fb, fb->attachments[type].tex); } @@ -304,7 +316,7 @@ void GPU_framebuffer_texture_detach_slot(GPUFrameBuffer *fb, GPUTexture *tex, in void GPU_framebuffer_texture_detach(GPUFrameBuffer *fb, GPUTexture *tex) { - GPUAttachmentType type = GPU_texture_detach_framebuffer(tex, fb); + GPUAttachmentType type = (GPUAttachmentType)GPU_texture_detach_framebuffer(tex, fb); GPU_framebuffer_texture_detach_slot(fb, tex, type); } @@ -387,8 +399,8 @@ static void gpu_framebuffer_update_attachments(GPUFrameBuffer *fb) BLI_assert(GPU_framebuffer_active_get() == fb); /* Update attachments */ - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { - + FOREACH_ATTACHMENT_RANGE(type, 0, GPU_FB_MAX_ATTACHEMENT) + { if (type >= GPU_FB_COLOR_ATTACHMENT0) { if (fb->attachments[type].tex) { gl_attachments[numslots] = convert_attachment_type_to_gl(type); @@ -438,7 +450,8 @@ static void gpu_framebuffer_update_attachments_and_fill_empty_slots(GPUFrameBuff BLI_assert(GPU_framebuffer_active_get() == fb); /* Update attachments */ - for (GPUAttachmentType type = GPU_FB_MAX_ATTACHEMENT; type--;) { + for (int i_type = GPU_FB_MAX_ATTACHEMENT - 1; i_type >= 0; --i_type) { + GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); GPUTexture *tex = fb->attachments[type].tex; if (type >= GPU_FB_COLOR_ATTACHMENT0) { @@ -623,8 +636,9 @@ void GPU_framebuffer_multi_clear(GPUFrameBuffer *fb, const float (*clear_cols)[4 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - GPUAttachmentType type = GPU_FB_COLOR_ATTACHMENT0; - for (int i = 0; type < GPU_FB_MAX_ATTACHEMENT; i++, type++) { + int i_type = GPU_FB_COLOR_ATTACHMENT0; + for (int i = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i++, i_type++) { + GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); if (fb->attachments[type].tex != NULL) { glClearBufferfv(GL_COLOR, i, clear_cols[i]); } @@ -640,31 +654,70 @@ void GPU_framebuffer_read_depth(GPUFrameBuffer *fb, int x, int y, int w, int h, glReadPixels(x, y, w, h, type, GL_FLOAT, data); } -void GPU_framebuffer_read_color( - GPUFrameBuffer *fb, int x, int y, int w, int h, int channels, int slot, float *data) +static GLenum gpu_get_gl_datatype(eGPUDataFormat format) { - CHECK_FRAMEBUFFER_IS_BOUND(fb); + switch (format) { + case GPU_DATA_FLOAT: + return GL_FLOAT; + case GPU_DATA_INT: + return GL_INT; + case GPU_DATA_UNSIGNED_INT: + return GL_UNSIGNED_INT; + case GPU_DATA_UNSIGNED_BYTE: + return GL_UNSIGNED_BYTE; + case GPU_DATA_UNSIGNED_INT_24_8: + return GL_UNSIGNED_INT_24_8; + case GPU_DATA_10_11_11_REV: + return GL_UNSIGNED_INT_10F_11F_11F_REV; + default: + BLI_assert(!"Unhandled data format"); + return GL_FLOAT; + } +} - GLenum type; +static GLenum gpu_get_gl_channel_type(int channels) +{ switch (channels) { case 1: - type = GL_RED; - break; + return GL_RED; case 2: - type = GL_RG; - break; + return GL_RG; case 3: - type = GL_RGB; - break; + return GL_RGB; case 4: - type = GL_RGBA; - break; + return GL_RGBA; default: - BLI_assert(false && "wrong number of read channels"); - return; + BLI_assert(!"Wrong number of read channels"); + return GL_RED; } - glReadBuffer(GL_COLOR_ATTACHMENT0 + slot); - glReadPixels(x, y, w, h, type, GL_FLOAT, data); +} + +static void gpu_framebuffer_read_color_ex( + int x, int y, int w, int h, int channels, GLenum readfb, eGPUDataFormat format, float *data) +{ + GLenum type = gpu_get_gl_channel_type(channels); + GLenum gl_format = gpu_get_gl_datatype(format); + /* TODO: needed for selection buffers to work properly, this should be handled better. */ + if (type == GL_RED && gl_format == GL_UNSIGNED_INT) { + type = GL_RED_INTEGER; + } + glReadBuffer(readfb); + glReadPixels(x, y, w, h, type, gl_format, data); +} + +void GPU_framebuffer_read_color(GPUFrameBuffer *fb, + int x, + int y, + int w, + int h, + int channels, + int slot, + eGPUDataFormat format, + void *data) +{ + CHECK_FRAMEBUFFER_IS_BOUND(fb); + gpu_framebuffer_read_color_ex( + x, y, w, h, channels, GL_COLOR_ATTACHMENT0 + slot, format, (float *)data); } /* read_slot and write_slot are only used for color buffers. */ @@ -749,9 +802,9 @@ void GPU_framebuffer_blit(GPUFrameBuffer *fb_read, } /** - * Use this if you need to custom down-sample your texture and use the previous mip level as input. - * This function only takes care of the correct texture handling. - * It execute the callback for each texture level. + * Use this if you need to custom down-sample your texture and use the previous mip level as + * input. This function only takes care of the correct texture handling. It execute the callback + * for each texture level. */ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb, int max_lvl, @@ -777,7 +830,8 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb, current_dim[0] = max_ii(current_dim[0] / 2, 1); current_dim[1] = max_ii(current_dim[1] / 2, 1); - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { + for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { + GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); if (fb->attachments[type].tex != NULL) { /* Some Intel HDXXX have issue with rendering to a mipmap that is below * the texture GL_TEXTURE_MAX_LEVEL. So even if it not correct, in this case @@ -806,7 +860,8 @@ void GPU_framebuffer_recursive_downsample(GPUFrameBuffer *fb, } } - for (GPUAttachmentType type = 0; type < GPU_FB_MAX_ATTACHEMENT; type++) { + for (int i_type = 0; i_type < GPU_FB_MAX_ATTACHEMENT; i_type++) { + GPUAttachmentType type = static_cast<GPUAttachmentType>(i_type); if (fb->attachments[type].tex != NULL) { /* reset mipmap level range */ GPUTexture *tex = fb->attachments[type].tex; @@ -847,9 +902,11 @@ static GPUFrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs) for (int i = 0; i < MAX_CTX_FB_LEN; i++) { if (ofs->framebuffers[i].fb == NULL) { ofs->framebuffers[i].ctx = ctx; - GPU_framebuffer_ensure_config( - &ofs->framebuffers[i].fb, - {GPU_ATTACHMENT_TEXTURE(ofs->depth), GPU_ATTACHMENT_TEXTURE(ofs->color)}); + GPU_framebuffer_ensure_config(&ofs->framebuffers[i].fb, + { + GPU_ATTACHMENT_TEXTURE(ofs->depth), + GPU_ATTACHMENT_TEXTURE(ofs->color), + }); } if (ofs->framebuffers[i].ctx == ctx) { @@ -876,23 +933,20 @@ static GPUFrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs) } GPUOffScreen *GPU_offscreen_create( - int width, int height, int samples, bool depth, bool high_bitdepth, char err_out[256]) + int width, int height, bool depth, bool high_bitdepth, char err_out[256]) { - GPUOffScreen *ofs; - - ofs = MEM_callocN(sizeof(GPUOffScreen), "GPUOffScreen"); + GPUOffScreen *ofs = (GPUOffScreen *)MEM_callocN(sizeof(GPUOffScreen), __func__); /* Sometimes areas can have 0 height or width and this will * create a 1D texture which we don't want. */ height = max_ii(1, height); width = max_ii(1, width); - ofs->color = GPU_texture_create_2d_multisample( - width, height, (high_bitdepth) ? GPU_RGBA16F : GPU_RGBA8, NULL, samples, err_out); + ofs->color = GPU_texture_create_2d( + width, height, (high_bitdepth) ? GPU_RGBA16F : GPU_RGBA8, NULL, err_out); if (depth) { - ofs->depth = GPU_texture_create_2d_multisample( - width, height, GPU_DEPTH24_STENCIL8, NULL, samples, err_out); + ofs->depth = GPU_texture_create_2d(width, height, GPU_DEPTH24_STENCIL8, NULL, err_out); } if ((depth && !ofs->depth) || !ofs->color) { @@ -938,14 +992,14 @@ void GPU_offscreen_free(GPUOffScreen *ofs) void GPU_offscreen_bind(GPUOffScreen *ofs, bool save) { if (save) { - gpuPushAttr(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT); + gpuPushAttr((eGPUAttrMask)(GPU_SCISSOR_BIT | GPU_VIEWPORT_BIT)); GPUFrameBuffer *fb = GPU_framebuffer_active_get(); gpuPushFrameBuffer(fb); } - glDisable(GL_SCISSOR_TEST); GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs); GPU_framebuffer_bind(ofs_fb); glDisable(GL_FRAMEBUFFER_SRGB); + GPU_scissor_test(false); GPU_shader_set_framebuffer_srgb_target(false); } @@ -986,55 +1040,15 @@ void GPU_offscreen_draw_to_screen(GPUOffScreen *ofs, int x, int y) glBindFramebuffer(GL_READ_FRAMEBUFFER, GPU_framebuffer_default()); } -void GPU_offscreen_read_pixels(GPUOffScreen *ofs, int type, void *pixels) +void GPU_offscreen_read_pixels(GPUOffScreen *ofs, eGPUDataFormat type, void *pixels) { const int w = GPU_texture_width(ofs->color); const int h = GPU_texture_height(ofs->color); - BLI_assert(type == GL_UNSIGNED_BYTE || type == GL_FLOAT); - - if (GPU_texture_target(ofs->color) == GL_TEXTURE_2D_MULTISAMPLE) { - /* For a multi-sample texture, - * we need to create an intermediate buffer to blit to, - * before its copied using 'glReadPixels' */ - GLuint fbo_blit = 0; - GLuint tex_blit = 0; - - /* create texture for new 'fbo_blit' */ - glGenTextures(1, &tex_blit); - glBindTexture(GL_TEXTURE_2D, tex_blit); - glTexImage2D( - GL_TEXTURE_2D, 0, (type == GL_FLOAT) ? GL_RGBA16F : GL_RGBA8, w, h, 0, GL_RGBA, type, 0); - - /* write into new single-sample buffer */ - glGenFramebuffers(1, &fbo_blit); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_blit); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex_blit, 0); - - GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - goto finally; - } - - /* perform the copy */ - glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); - - /* read the results */ - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_blit); - glReadPixels(0, 0, w, h, GL_RGBA, type, pixels); - - /* restore the original frame-bufer */ - GPUFrameBuffer *ofs_fb = gpu_offscreen_fb_get(ofs); - glBindFramebuffer(GL_FRAMEBUFFER, ofs_fb->object); + BLI_assert(ELEM(type, GPU_DATA_UNSIGNED_BYTE, GPU_DATA_FLOAT)); + GLenum gl_type = (type == GPU_DATA_FLOAT) ? GL_FLOAT : GL_UNSIGNED_BYTE; - finally: - /* cleanup */ - glDeleteTextures(1, &tex_blit); - glDeleteFramebuffers(1, &fbo_blit); - } - else { - glReadPixels(0, 0, w, h, GL_RGBA, type, pixels); - } + glReadPixels(0, 0, w, h, GL_RGBA, gl_type, pixels); } int GPU_offscreen_width(const GPUOffScreen *ofs) @@ -1077,3 +1091,23 @@ void GPU_clear(eGPUFrameBufferBits flags) { glClear(convert_buffer_bits_to_gl(flags)); } + +void GPU_frontbuffer_read_pixels( + int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data) +{ + gpu_framebuffer_read_color_ex(x, y, w, h, channels, GL_FRONT, format, (float *)data); +} + +/* For stereo rendering. */ +void GPU_backbuffer_bind(eGPUBackBuffer buffer) +{ + if (buffer == GPU_BACKBUFFER) { + glDrawBuffer(GL_BACK); + } + else if (buffer == GPU_BACKBUFFER_LEFT) { + glDrawBuffer(GL_BACK_LEFT); + } + else if (buffer == GPU_BACKBUFFER_RIGHT) { + glDrawBuffer(GL_BACK_RIGHT); + } +} diff --git a/source/blender/gpu/intern/gpu_immediate.c b/source/blender/gpu/intern/gpu_immediate.cc index 9ea273f33cf..ac3e653c9ff 100644 --- a/source/blender/gpu/intern/gpu_immediate.c +++ b/source/blender/gpu/intern/gpu_immediate.cc @@ -29,6 +29,8 @@ #include "GPU_attr_binding.h" #include "GPU_immediate.h" +#include "GPU_matrix.h" +#include "GPU_texture.h" #include "gpu_attr_binding_private.h" #include "gpu_context_private.h" @@ -39,10 +41,6 @@ #include <stdlib.h> #include <string.h> -/* necessary functions from matrix API */ -extern void GPU_matrix_bind(const GPUShaderInterface *); -extern bool GPU_matrix_dirty_get(void); - typedef struct ImmediateDrawBuffer { GLuint vbo_id; GLubyte *buffer_data; @@ -74,7 +72,7 @@ typedef struct { GLuint vao_id; - GLuint bound_program; + GPUShader *bound_program; const GPUShaderInterface *shader_interface; GPUAttrBinding attr_binding; uint16_t prev_enabled_attr_bits; /* <-- only affects this VAO, so we're ok */ @@ -145,48 +143,47 @@ GPUVertFormat *immVertexFormat(void) return &imm.vertex_format; } -void immBindProgram(GLuint program, const GPUShaderInterface *shaderface) +void immBindShader(GPUShader *shader) { #if TRUST_NO_ONE - assert(imm.bound_program == 0); - assert(glIsProgram(program)); + assert(imm.bound_program == NULL); + assert(glIsProgram(shader->program)); #endif - imm.bound_program = program; - imm.shader_interface = shaderface; + imm.bound_program = shader; + imm.shader_interface = shader->interface; if (!imm.vertex_format.packed) { VertexFormat_pack(&imm.vertex_format); } - glUseProgram(program); - get_attr_locations(&imm.vertex_format, &imm.attr_binding, shaderface); - GPU_matrix_bind(shaderface); - GPU_shader_set_srgb_uniform(shaderface); + GPU_shader_bind(shader); + get_attr_locations(&imm.vertex_format, &imm.attr_binding, imm.shader_interface); + GPU_matrix_bind(imm.shader_interface); + GPU_shader_set_srgb_uniform(imm.shader_interface); } void immBindBuiltinProgram(eGPUBuiltinShader shader_id) { GPUShader *shader = GPU_shader_get_builtin_shader(shader_id); - immBindProgram(shader->program, shader->interface); + immBindShader(shader); } void immUnbindProgram(void) { #if TRUST_NO_ONE - assert(imm.bound_program != 0); + assert(imm.bound_program != NULL); #endif #if PROGRAM_NO_OPTI glUseProgram(0); #endif - imm.bound_program = 0; + imm.bound_program = NULL; } /* XXX do not use it. Special hack to use OCIO with batch API. */ -void immGetProgram(GLuint *program, GPUShaderInterface **shaderface) +GPUShader *immGetShader(void) { - *program = imm.bound_program; - *shaderface = (GPUShaderInterface *)imm.shader_interface; + return imm.bound_program; } #if TRUST_NO_ONE @@ -280,7 +277,7 @@ void immBegin(GPUPrimType prim_type, uint vertex_len) } #endif - active_buffer->buffer_data = glMapBufferRange( + active_buffer->buffer_data = (GLubyte *)glMapBufferRange( GL_ARRAY_BUFFER, active_buffer->buffer_offset, bytes_needed, @@ -366,17 +363,18 @@ static void immDrawSetup(void) const GLvoid *pointer = (const GLubyte *)0 + offset; const uint loc = read_attr_location(&imm.attr_binding, a_idx); + const GLenum type = convert_comp_type_to_gl(static_cast<GPUVertCompType>(a->comp_type)); switch (a->fetch_mode) { case GPU_FETCH_FLOAT: case GPU_FETCH_INT_TO_FLOAT: - glVertexAttribPointer(loc, a->comp_len, a->gl_comp_type, GL_FALSE, stride, pointer); + glVertexAttribPointer(loc, a->comp_len, type, GL_FALSE, stride, pointer); break; case GPU_FETCH_INT_TO_FLOAT_UNIT: - glVertexAttribPointer(loc, a->comp_len, a->gl_comp_type, GL_TRUE, stride, pointer); + glVertexAttribPointer(loc, a->comp_len, type, GL_TRUE, stride, pointer); break; case GPU_FETCH_INT: - glVertexAttribIPointer(loc, a->comp_len, a->gl_comp_type, stride, pointer); + glVertexAttribIPointer(loc, a->comp_len, type, stride, pointer); } } @@ -424,7 +422,7 @@ void immEnd(void) GPU_vertbuf_data_resize(imm.batch->verts[0], imm.vertex_len); /* TODO: resize only if vertex count is much smaller */ } - GPU_batch_program_set(imm.batch, imm.bound_program, imm.shader_interface); + GPU_batch_set_shader(imm.batch, imm.bound_program); imm.batch->phase = GPU_BATCH_READY_TO_DRAW; imm.batch = NULL; /* don't free, batch belongs to caller */ } @@ -853,6 +851,18 @@ void immUniform4iv(const char *name, const int data[4]) glUniform4iv(uniform->location, 1, data); } +void immBindTexture(const char *name, GPUTexture *tex) +{ + GET_UNIFORM + GPU_texture_bind(tex, uniform->binding); +} + +void immBindTextureSampler(const char *name, GPUTexture *tex, eGPUSamplerState state) +{ + GET_UNIFORM + GPU_texture_bind_ex(tex, state, uniform->binding, true); +} + /* --- convenience functions for setting "uniform vec4 color" --- */ void immUniformColor4f(float r, float g, float b, float a) diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index d2384b9c065..f3477b6f3a4 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -85,7 +85,7 @@ struct GPUMaterial { bool has_surface_output; /* Only used by Eevee to know which bsdf are used. */ - int flag; + eGPUMatFlag flag; /* Used by 2.8 pipeline */ GPUUniformBuffer *ubo; /* UBOs for shader uniforms. */ @@ -497,8 +497,8 @@ static void compute_sss_translucence_kernel(const GPUSssKernelData *kd, void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3], - short *falloff_type, - float *sharpness) + const short *falloff_type, + const float *sharpness) { copy_v3_v3(material->sss_radii, radii); material->sss_falloff = (falloff_type) ? *falloff_type : 0.0; @@ -659,7 +659,8 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, const char *geom_code, const char *frag_lib, const char *defines, - const char *name) + const char *name, + GPUMaterialEvalCallbackFn callback) { LinkData *link; bool has_volume_output, has_surface_output; @@ -696,6 +697,9 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, mat->has_volume_output = has_volume_output; if (mat->graph.outlink) { + if (callback) { + callback(mat, options, &vert_code, &geom_code, &frag_lib, &defines); + } /* HACK: this is only for eevee. We add the define here after the nodetree evaluation. */ if (GPU_material_flag_get(mat, GPU_MATFLAG_SSS)) { defines = BLI_string_joinN(defines, diff --git a/source/blender/gpu/intern/gpu_matrix.c b/source/blender/gpu/intern/gpu_matrix.c index 2687f56ad27..669bf56b726 100644 --- a/source/blender/gpu/intern/gpu_matrix.c +++ b/source/blender/gpu/intern/gpu_matrix.c @@ -536,13 +536,13 @@ bool GPU_matrix_unproject_precalc(struct GPUMatrixUnproject_Precalc *precalc, const int view[4]) { precalc->is_persp = proj[3][3] == 0.0f; - projmat_dimensions(proj, - &precalc->dims.xmin, - &precalc->dims.xmax, - &precalc->dims.ymin, - &precalc->dims.ymax, - &precalc->dims.zmin, - &precalc->dims.zmax); + projmat_dimensions_db(proj, + &precalc->dims.xmin, + &precalc->dims.xmax, + &precalc->dims.ymin, + &precalc->dims.ymax, + &precalc->dims.zmin, + &precalc->dims.zmax); if (isinf(precalc->dims.zmax)) { /* We cannot retrieve the actual value of the clip_end. * Use `FLT_MAX` to avoid nans. */ @@ -738,3 +738,69 @@ int GPU_matrix_stack_level_get_projection(void) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Polygon Offset Hack + * + * Workaround the fact that polygon-offset is implementation dependent. + * We modify the projection matrix \a winmat in order to change the final depth a tiny amount. + * \{ */ + +float GPU_polygon_offset_calc(const float (*winmat)[4], float viewdist, float dist) +{ + /* Seems like we have a factor of 2 more offset than 2.79 for some reason. Correct for this. */ + dist *= 0.5f; + + if (winmat[3][3] > 0.5f) { +#if 1 + return 0.00001f * dist * viewdist; // ortho tweaking +#else + static float depth_fac = 0.0f; + if (depth_fac == 0.0f) { + int depthbits; + glGetIntegerv(GL_DEPTH_BITS, &depthbits); + depth_fac = 1.0f / (float)((1 << depthbits) - 1); + } + offs = (-1.0 / winmat[2][2]) * dist * depth_fac; + + UNUSED_VARS(viewdist); +#endif + } + + /* This adjustment effectively results in reducing the Z value by 0.25%. + * + * winmat[4][3] actually evaluates to `-2 * far * near / (far - near)`, + * is very close to -0.2 with default clip range, + * and is used as the coefficient multiplied by `w / z`, + * thus controlling the z dependent part of the depth value. + */ + return winmat[3][2] * -0.0025f * dist; +} + +/** + * \note \a viewdist is only for ortho at the moment. + */ +void GPU_polygon_offset(float viewdist, float dist) +{ + static float winmat[4][4], offset = 0.0f; + + if (dist != 0.0f) { + /* hack below is to mimic polygon offset */ + GPU_matrix_projection_get(winmat); + + /* dist is from camera to center point */ + + float offs = GPU_polygon_offset_calc(winmat, viewdist, dist); + + winmat[3][2] -= offs; + offset += offs; + } + else { + winmat[3][2] += offset; + offset = 0.0; + } + + GPU_matrix_projection_set(winmat); +} + +/** \} */ diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index 3067be1c485..a133dd1ccdc 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -29,7 +29,6 @@ #include "DNA_customdata_types.h" #include "DNA_listBase.h" -#include "GPU_glew.h" #include "GPU_material.h" #include "GPU_shader.h" @@ -116,7 +115,7 @@ typedef struct GPUInput { struct GPUInput *next, *prev; GPUNode *node; - eGPUType type; /* datatype */ + eGPUType type; /* data-type. */ GPUNodeLink *link; int id; /* unique id as created by code generator */ diff --git a/source/blender/gpu/intern/gpu_platform.c b/source/blender/gpu/intern/gpu_platform.cc index a758787466f..5cabde61bc3 100644 --- a/source/blender/gpu/intern/gpu_platform.c +++ b/source/blender/gpu/intern/gpu_platform.cc @@ -44,6 +44,8 @@ static struct GPUPlatformGlobal { char *gpu_name; } GPG = {false}; +/* Remove this? */ +#if 0 typedef struct GPUPlatformSupportTest { eGPUSupportLevel support_level; eGPUDeviceType device; @@ -53,6 +55,7 @@ typedef struct GPUPlatformSupportTest { const char *renderer; const char *version; } GPUPlatformSupportTest; +#endif eGPUSupportLevel GPU_platform_support_level(void) { diff --git a/source/blender/gpu/intern/gpu_primitive.c b/source/blender/gpu/intern/gpu_primitive.c index 2285c1ab95b..3b11b38db87 100644 --- a/source/blender/gpu/intern/gpu_primitive.c +++ b/source/blender/gpu/intern/gpu_primitive.c @@ -26,35 +26,6 @@ #include "GPU_primitive.h" #include "gpu_primitive_private.h" -GPUPrimClass GPU_primtype_class(GPUPrimType prim_type) -{ - static const GPUPrimClass classes[] = { - [GPU_PRIM_POINTS] = GPU_PRIM_CLASS_POINT, - [GPU_PRIM_LINES] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_LINE_STRIP] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_LINE_LOOP] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_TRIS] = GPU_PRIM_CLASS_SURFACE, - [GPU_PRIM_TRI_STRIP] = GPU_PRIM_CLASS_SURFACE, - [GPU_PRIM_TRI_FAN] = GPU_PRIM_CLASS_SURFACE, - - [GPU_PRIM_LINES_ADJ] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_LINE_STRIP_ADJ] = GPU_PRIM_CLASS_LINE, - [GPU_PRIM_TRIS_ADJ] = GPU_PRIM_CLASS_SURFACE, - - [GPU_PRIM_NONE] = GPU_PRIM_CLASS_NONE, - }; - - return classes[prim_type]; -} - -bool GPU_primtype_belongs_to_class(GPUPrimType prim_type, GPUPrimClass prim_class) -{ - if (prim_class == GPU_PRIM_CLASS_NONE && prim_type == GPU_PRIM_NONE) { - return true; - } - return prim_class & GPU_primtype_class(prim_type); -} - GLenum convert_prim_type_to_gl(GPUPrimType prim_type) { #if TRUST_NO_ONE diff --git a/source/blender/gpu/intern/gpu_primitive_private.h b/source/blender/gpu/intern/gpu_primitive_private.h index abefa6abd20..b7d7b262128 100644 --- a/source/blender/gpu/intern/gpu_primitive_private.h +++ b/source/blender/gpu/intern/gpu_primitive_private.h @@ -26,6 +26,15 @@ #ifndef __GPU_PRIMITIVE_PRIVATE_H__ #define __GPU_PRIMITIVE_PRIVATE_H__ +#ifdef __cplusplus +extern "C" { +#endif + +/* TODO(fclem) move to OGL backend */ GLenum convert_prim_type_to_gl(GPUPrimType); +#ifdef __cplusplus +} +#endif + #endif /* __GPU_PRIMITIVE_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_private.h b/source/blender/gpu/intern/gpu_private.h index 7846bff87f4..ed729dd399c 100644 --- a/source/blender/gpu/intern/gpu_private.h +++ b/source/blender/gpu/intern/gpu_private.h @@ -21,6 +21,10 @@ #ifndef __GPU_PRIVATE_H__ #define __GPU_PRIVATE_H__ +#ifdef __cplusplus +extern "C" { +#endif + /* call this before running any of the functions below */ void gpu_platform_init(void); void gpu_platform_exit(void); @@ -41,4 +45,8 @@ void gpu_framebuffer_module_exit(void); void gpu_pbvh_init(void); void gpu_pbvh_exit(void); +#ifdef __cplusplus +} +#endif + #endif /* __GPU_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_select.c b/source/blender/gpu/intern/gpu_select.c index 5766a176a96..c069cbe012f 100644 --- a/source/blender/gpu/intern/gpu_select.c +++ b/source/blender/gpu/intern/gpu_select.c @@ -26,7 +26,6 @@ #include <stdlib.h> #include <string.h> -#include "GPU_glew.h" #include "GPU_select.h" #include "MEM_guardedalloc.h" diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c index 4b38cd333a1..3025b5d66da 100644 --- a/source/blender/gpu/intern/gpu_select_pick.c +++ b/source/blender/gpu/intern/gpu_select_pick.c @@ -27,7 +27,6 @@ #include <stdlib.h> #include <string.h> -#include "GPU_draw.h" #include "GPU_glew.h" #include "GPU_immediate.h" #include "GPU_select.h" @@ -310,7 +309,7 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_VIEWPORT_BIT); /* disable writing to the framebuffer */ - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + GPU_color_mask(false, false, false, false); glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); @@ -320,7 +319,7 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c glDepthFunc(GL_LEQUAL); float viewport[4]; - glGetFloatv(GL_VIEWPORT, viewport); + GPU_viewport_size_get_f(viewport); ps->src.clip_rect = *input; ps->src.rect_len = rect_len; @@ -330,7 +329,7 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c ps->gl.clip_readpixels[2] = BLI_rcti_size_x(&ps->src.clip_rect); ps->gl.clip_readpixels[3] = BLI_rcti_size_y(&ps->src.clip_rect); - glViewport(UNPACK4(ps->gl.clip_readpixels)); + GPU_viewport(UNPACK4(ps->gl.clip_readpixels)); /* It's possible we don't want to clear depth buffer, * so existing elements are masked by current z-buffer. */ @@ -539,7 +538,7 @@ uint gpu_select_pick_end(void) gpu_select_pick_load_id(ps->gl.prev_id, true); } gpuPopAttr(); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + GPU_color_mask(true, true, true, true); } /* assign but never free directly since it may be in cache */ diff --git a/source/blender/gpu/intern/gpu_select_sample_query.c b/source/blender/gpu/intern/gpu_select_sample_query.c index c82d1e17c66..70ad2f6759e 100644 --- a/source/blender/gpu/intern/gpu_select_sample_query.c +++ b/source/blender/gpu/intern/gpu_select_sample_query.c @@ -88,14 +88,14 @@ void gpu_select_query_begin( gpuPushAttr(GPU_DEPTH_BUFFER_BIT | GPU_VIEWPORT_BIT | GPU_SCISSOR_BIT); /* disable writing to the framebuffer */ - glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + GPU_color_mask(false, false, false, false); /* In order to save some fill rate we minimize the viewport using rect. * We need to get the region of the viewport so that our geometry doesn't * get rejected before the depth test. Should probably cull rect against * the viewport but this is a rare case I think */ - glGetFloatv(GL_VIEWPORT, viewport); - glViewport(viewport[0], viewport[1], BLI_rcti_size_x(input), BLI_rcti_size_y(input)); + GPU_viewport_size_get_f(viewport); + GPU_viewport(viewport[0], viewport[1], BLI_rcti_size_x(input), BLI_rcti_size_y(input)); /* occlusion queries operates on fragments that pass tests and since we are interested on all * objects in the view frustum independently of their order, we need to disable the depth test */ @@ -206,7 +206,7 @@ uint gpu_select_query_end(void) MEM_freeN(g_query_state.queries); MEM_freeN(g_query_state.id); gpuPopAttr(); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + GPU_color_mask(true, true, true, true); return hits; } diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc new file mode 100644 index 00000000000..2b54e733bf5 --- /dev/null +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -0,0 +1,839 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#include "MEM_guardedalloc.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 "BKE_appdir.h" +#include "BKE_global.h" + +#include "DNA_space_types.h" + +#include "GPU_extensions.h" +#include "GPU_matrix.h" +#include "GPU_platform.h" +#include "GPU_shader.h" +#include "GPU_texture.h" +#include "GPU_uniformbuffer.h" + +#include "gpu_shader_private.h" + +extern "C" char datatoc_gpu_shader_colorspace_lib_glsl[]; + +/* Adjust these constants as needed. */ +#define MAX_DEFINE_LENGTH 256 +#define MAX_EXT_DEFINE_LENGTH 512 + +#ifndef NDEBUG +static uint g_shaderid = 0; +#endif + +/* -------------------------------------------------------------------- */ +/** \name Convenience functions + * \{ */ + +static void shader_print_errors(const char *task, const char *log, const char **code, int totcode) +{ + int line = 1; + + fprintf(stderr, "GPUShader: %s error:\n", task); + + for (int i = 0; i < totcode; i++) { + const char *c, *pos, *end = code[i] + strlen(code[i]); + + if (G.debug & G_DEBUG) { + fprintf(stderr, "===== shader string %d ====\n", i + 1); + + c = code[i]; + while ((c < end) && (pos = strchr(c, '\n'))) { + fprintf(stderr, "%2d ", line); + fwrite(c, (pos + 1) - c, 1, stderr); + c = pos + 1; + line++; + } + + fprintf(stderr, "%s", c); + } + } + + fprintf(stderr, "%s\n", log); +} + +static const char *gpu_shader_version(void) +{ + return "#version 330\n"; +} + +static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH]) +{ + /* enable extensions for features that are not part of our base GLSL version + * don't use an extension for something already available! + */ + + if (GLEW_ARB_texture_gather) { + /* There is a bug on older Nvidia GPU where GL_ARB_texture_gather + * is reported to be supported but yield a compile error (see T55802). */ + if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) || GLEW_VERSION_4_0) { + strcat(defines, "#extension GL_ARB_texture_gather: enable\n"); + + /* Some drivers don't agree on GLEW_ARB_texture_gather and the actual support in the + * shader so double check the preprocessor define (see T56544). */ + if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) && !GLEW_VERSION_4_0) { + strcat(defines, "#ifdef GL_ARB_texture_gather\n"); + strcat(defines, "# define GPU_ARB_texture_gather\n"); + strcat(defines, "#endif\n"); + } + else { + strcat(defines, "#define GPU_ARB_texture_gather\n"); + } + } + } + if (GLEW_ARB_texture_query_lod) { + /* a #version 400 feature, but we use #version 330 maximum so use extension */ + strcat(defines, "#extension GL_ARB_texture_query_lod: enable\n"); + } + if (GLEW_ARB_shader_draw_parameters) { + strcat(defines, "#extension GL_ARB_shader_draw_parameters : enable\n"); + } + if (GPU_arb_texture_cube_map_array_is_supported()) { + strcat(defines, "#extension GL_ARB_texture_cube_map_array : enable\n"); + strcat(defines, "#define GPU_ARB_texture_cube_map_array\n"); + } +} + +static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH]) +{ + /* some useful defines to detect GPU type */ + if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) { + strcat(defines, "#define GPU_ATI\n"); + if (GPU_crappy_amd_driver()) { + strcat(defines, "#define GPU_DEPRECATED_AMD_DRIVER\n"); + } + } + else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) { + strcat(defines, "#define GPU_NVIDIA\n"); + } + else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) { + strcat(defines, "#define GPU_INTEL\n"); + } + + /* some useful defines to detect OS type */ + if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_WIN, GPU_DRIVER_ANY)) { + strcat(defines, "#define OS_WIN\n"); + } + else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) { + strcat(defines, "#define OS_MAC\n"); + } + else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_UNIX, GPU_DRIVER_ANY)) { + strcat(defines, "#define OS_UNIX\n"); + } + + float derivatives_factors[2]; + GPU_get_dfdy_factors(derivatives_factors); + if (derivatives_factors[0] == 1.0f) { + strcat(defines, "#define DFDX_SIGN 1.0\n"); + } + else { + strcat(defines, "#define DFDX_SIGN -1.0\n"); + } + + if (derivatives_factors[1] == 1.0f) { + strcat(defines, "#define DFDY_SIGN 1.0\n"); + } + else { + strcat(defines, "#define DFDY_SIGN -1.0\n"); + } +} + +#define DEBUG_SHADER_NONE "" +#define DEBUG_SHADER_VERTEX "vert" +#define DEBUG_SHADER_FRAGMENT "frag" +#define DEBUG_SHADER_GEOMETRY "geom" + +/** + * Dump GLSL shaders to disk + * + * This is used for profiling shader performance externally and debug if shader code is correct. + * If called with no code, it simply bumps the shader index, so different shaders for the same + * program share the same index. + */ +static void gpu_dump_shaders(const char **code, const int num_shaders, const char *extension) +{ + if ((G.debug & G_DEBUG_GPU_SHADERS) == 0) { + return; + } + + /* We use the same shader index for shaders in the same program. + * So we call this function once before calling for the individual shaders. */ + static int shader_index = 0; + if (code == NULL) { + shader_index++; + BLI_assert(STREQ(DEBUG_SHADER_NONE, extension)); + return; + } + + /* Determine the full path of the new shader. */ + char shader_path[FILE_MAX]; + + char file_name[512] = {'\0'}; + sprintf(file_name, "%04d.%s", shader_index, extension); + + BLI_join_dirfile(shader_path, sizeof(shader_path), BKE_tempdir_session(), file_name); + + /* Write shader to disk. */ + FILE *f = fopen(shader_path, "w"); + if (f == NULL) { + printf("Error writing to file: %s\n", shader_path); + } + for (int j = 0; j < num_shaders; j++) { + fprintf(f, "%s", code[j]); + } + fclose(f); + printf("Shader file written to disk: %s\n", shader_path); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Creation / Destruction + * \{ */ + +GPUShader *GPU_shader_create(const char *vertexcode, + const char *fragcode, + const char *geocode, + const char *libcode, + const char *defines, + const char *shname) +{ + return GPU_shader_create_ex( + vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, shname); +} + +GPUShader *GPU_shader_create_from_python(const char *vertexcode, + const char *fragcode, + const char *geocode, + const char *libcode, + const char *defines) +{ + char *libcodecat = NULL; + + if (libcode == NULL) { + libcode = datatoc_gpu_shader_colorspace_lib_glsl; + } + else { + libcode = libcodecat = BLI_strdupcat(libcode, datatoc_gpu_shader_colorspace_lib_glsl); + } + + GPUShader *sh = GPU_shader_create_ex( + vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, NULL); + + MEM_SAFE_FREE(libcodecat); + return sh; +} + +GPUShader *GPU_shader_load_from_binary(const char *binary, + const int binary_format, + const int binary_len, + const char *shname) +{ + BLI_assert(GL_ARB_get_program_binary); + int success; + int program = glCreateProgram(); + + glProgramBinary(program, binary_format, binary, binary_len); + glGetProgramiv(program, GL_LINK_STATUS, &success); + + if (success) { + glUseProgram(program); + + GPUShader *shader = (GPUShader *)MEM_callocN(sizeof(*shader), __func__); + shader->interface = GPU_shaderinterface_create(program); + shader->program = program; + +#ifndef NDEBUG + BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++); +#else + UNUSED_VARS(shname); +#endif + + return shader; + } + + glDeleteProgram(program); + return NULL; +} + +GPUShader *GPU_shader_create_ex(const char *vertexcode, + const char *fragcode, + const char *geocode, + const char *libcode, + const char *defines, + const eGPUShaderTFBType tf_type, + const char **tf_names, + const int tf_count, + const char *shname) +{ + GLint status; + GLchar log[5000]; + GLsizei length = 0; + GPUShader *shader; + char standard_defines[MAX_DEFINE_LENGTH] = ""; + char standard_extensions[MAX_EXT_DEFINE_LENGTH] = ""; + + shader = (GPUShader *)MEM_callocN(sizeof(GPUShader), "GPUShader"); + gpu_dump_shaders(NULL, 0, DEBUG_SHADER_NONE); + +#ifndef NDEBUG + BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++); +#else + UNUSED_VARS(shname); +#endif + + /* At least a vertex shader and a fragment shader are required. */ + BLI_assert((fragcode != NULL) && (vertexcode != NULL)); + + if (vertexcode) { + shader->vertex = glCreateShader(GL_VERTEX_SHADER); + } + if (fragcode) { + shader->fragment = glCreateShader(GL_FRAGMENT_SHADER); + } + if (geocode) { + shader->geometry = glCreateShader(GL_GEOMETRY_SHADER); + } + + shader->program = glCreateProgram(); + + if (!shader->program || (vertexcode && !shader->vertex) || (fragcode && !shader->fragment) || + (geocode && !shader->geometry)) { + fprintf(stderr, "GPUShader, object creation failed.\n"); + GPU_shader_free(shader); + return NULL; + } + + gpu_shader_standard_defines(standard_defines); + gpu_shader_standard_extensions(standard_extensions); + + if (vertexcode) { + const char *source[7]; + /* custom limit, may be too small, beware */ + int num_source = 0; + + source[num_source++] = gpu_shader_version(); + source[num_source++] = + "#define GPU_VERTEX_SHADER\n" + "#define IN_OUT out\n"; + source[num_source++] = standard_extensions; + source[num_source++] = standard_defines; + + if (geocode) { + source[num_source++] = "#define USE_GEOMETRY_SHADER\n"; + } + if (defines) { + source[num_source++] = defines; + } + source[num_source++] = vertexcode; + + gpu_dump_shaders(source, num_source, DEBUG_SHADER_VERTEX); + + glAttachShader(shader->program, shader->vertex); + glShaderSource(shader->vertex, num_source, source, NULL); + + glCompileShader(shader->vertex); + glGetShaderiv(shader->vertex, GL_COMPILE_STATUS, &status); + + if (!status) { + glGetShaderInfoLog(shader->vertex, sizeof(log), &length, log); + shader_print_errors("compile", log, source, num_source); + + GPU_shader_free(shader); + return NULL; + } + } + + if (fragcode) { + const char *source[8]; + int num_source = 0; + + source[num_source++] = gpu_shader_version(); + source[num_source++] = + "#define GPU_FRAGMENT_SHADER\n" + "#define IN_OUT in\n"; + source[num_source++] = standard_extensions; + source[num_source++] = standard_defines; + + if (geocode) { + source[num_source++] = "#define USE_GEOMETRY_SHADER\n"; + } + if (defines) { + source[num_source++] = defines; + } + if (libcode) { + source[num_source++] = libcode; + } + source[num_source++] = fragcode; + + gpu_dump_shaders(source, num_source, DEBUG_SHADER_FRAGMENT); + + glAttachShader(shader->program, shader->fragment); + glShaderSource(shader->fragment, num_source, source, NULL); + + glCompileShader(shader->fragment); + glGetShaderiv(shader->fragment, GL_COMPILE_STATUS, &status); + + if (!status) { + glGetShaderInfoLog(shader->fragment, sizeof(log), &length, log); + shader_print_errors("compile", log, source, num_source); + + GPU_shader_free(shader); + return NULL; + } + } + + if (geocode) { + const char *source[6]; + int num_source = 0; + + source[num_source++] = gpu_shader_version(); + source[num_source++] = "#define GPU_GEOMETRY_SHADER\n"; + source[num_source++] = standard_extensions; + source[num_source++] = standard_defines; + + if (defines) { + source[num_source++] = defines; + } + source[num_source++] = geocode; + + gpu_dump_shaders(source, num_source, DEBUG_SHADER_GEOMETRY); + + glAttachShader(shader->program, shader->geometry); + glShaderSource(shader->geometry, num_source, source, NULL); + + glCompileShader(shader->geometry); + glGetShaderiv(shader->geometry, GL_COMPILE_STATUS, &status); + + if (!status) { + glGetShaderInfoLog(shader->geometry, sizeof(log), &length, log); + shader_print_errors("compile", log, source, num_source); + + GPU_shader_free(shader); + return NULL; + } + } + + if (tf_names != NULL) { + glTransformFeedbackVaryings(shader->program, tf_count, tf_names, GL_INTERLEAVED_ATTRIBS); + /* Primitive type must be setup */ + BLI_assert(tf_type != GPU_SHADER_TFB_NONE); + shader->feedback_transform_type = tf_type; + } + + glLinkProgram(shader->program); + glGetProgramiv(shader->program, GL_LINK_STATUS, &status); + if (!status) { + glGetProgramInfoLog(shader->program, sizeof(log), &length, log); + /* print attached shaders in pipeline order */ + if (defines) { + shader_print_errors("linking", log, &defines, 1); + } + if (vertexcode) { + shader_print_errors("linking", log, &vertexcode, 1); + } + if (geocode) { + shader_print_errors("linking", log, &geocode, 1); + } + if (libcode) { + shader_print_errors("linking", log, &libcode, 1); + } + if (fragcode) { + shader_print_errors("linking", log, &fragcode, 1); + } + + GPU_shader_free(shader); + return NULL; + } + + glUseProgram(shader->program); + shader->interface = GPU_shaderinterface_create(shader->program); + + return shader; +} + +#undef DEBUG_SHADER_GEOMETRY +#undef DEBUG_SHADER_FRAGMENT +#undef DEBUG_SHADER_VERTEX +#undef DEBUG_SHADER_NONE + +void GPU_shader_free(GPUShader *shader) +{ +#if 0 /* Would be nice to have, but for now the Deferred compilation \ + * does not have a GPUContext. */ + BLI_assert(GPU_context_active_get() != NULL); +#endif + BLI_assert(shader); + + if (shader->vertex) { + glDeleteShader(shader->vertex); + } + if (shader->geometry) { + glDeleteShader(shader->geometry); + } + if (shader->fragment) { + glDeleteShader(shader->fragment); + } + if (shader->program) { + glDeleteProgram(shader->program); + } + + if (shader->interface) { + GPU_shaderinterface_discard(shader->interface); + } + + MEM_freeN(shader); +} + +static const char *string_join_array_maybe_alloc(const char **str_arr, bool *r_is_alloc) +{ + bool is_alloc = false; + if (str_arr == NULL) { + *r_is_alloc = false; + return NULL; + } + /* Skip empty strings (avoid alloc if we can). */ + while (str_arr[0] && str_arr[0][0] == '\0') { + str_arr++; + } + int i; + for (i = 0; str_arr[i]; i++) { + if (i != 0 && str_arr[i][0] != '\0') { + is_alloc = true; + } + } + *r_is_alloc = is_alloc; + if (is_alloc) { + return BLI_string_join_arrayN(str_arr, i); + } + else { + return str_arr[0]; + } +} + +/** + * Use via #GPU_shader_create_from_arrays macro (avoids passing in param). + * + * Similar to #DRW_shader_create_with_lib with the ability to include libs for each type of shader. + * + * It has the advantage that each item can be conditionally included + * without having to build the string inline, then free it. + * + * \param params: NULL terminated arrays of strings. + * + * Example: + * \code{.c} + * sh = GPU_shader_create_from_arrays({ + * .vert = (const char *[]){shader_lib_glsl, shader_vert_glsl, NULL}, + * .geom = (const char *[]){shader_geom_glsl, NULL}, + * .frag = (const char *[]){shader_frag_glsl, NULL}, + * .defs = (const char *[]){"#define DEFINE\n", test ? "#define OTHER_DEFINE\n" : "", NULL}, + * }); + * \endcode + */ +struct GPUShader *GPU_shader_create_from_arrays_impl( + const struct GPU_ShaderCreateFromArray_Params *params) +{ + struct { + const char *str; + bool is_alloc; + } str_dst[4] = {{0}}; + const char **str_src[4] = {params->vert, params->frag, params->geom, params->defs}; + + for (int i = 0; i < ARRAY_SIZE(str_src); i++) { + str_dst[i].str = string_join_array_maybe_alloc(str_src[i], &str_dst[i].is_alloc); + } + + GPUShader *sh = GPU_shader_create( + str_dst[0].str, str_dst[1].str, str_dst[2].str, NULL, str_dst[3].str, __func__); + + for (int i = 0; i < ARRAY_SIZE(str_dst); i++) { + if (str_dst[i].is_alloc) { + MEM_freeN((void *)str_dst[i].str); + } + } + return sh; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Binding + * \{ */ + +void GPU_shader_bind(GPUShader *shader) +{ + BLI_assert(shader && shader->program); + + glUseProgram(shader->program); + GPU_matrix_bind(shader->interface); + GPU_shader_set_srgb_uniform(shader->interface); +} + +void GPU_shader_unbind(void) +{ + glUseProgram(0); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform feedback + * \{ */ + +bool GPU_shader_transform_feedback_enable(GPUShader *shader, uint vbo_id) +{ + if (shader->feedback_transform_type == GPU_SHADER_TFB_NONE) { + return false; + } + + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo_id); + + switch (shader->feedback_transform_type) { + case GPU_SHADER_TFB_POINTS: + glBeginTransformFeedback(GL_POINTS); + return true; + case GPU_SHADER_TFB_LINES: + glBeginTransformFeedback(GL_LINES); + return true; + case GPU_SHADER_TFB_TRIANGLES: + glBeginTransformFeedback(GL_TRIANGLES); + return true; + default: + return false; + } +} + +void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader)) +{ + glEndTransformFeedback(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Uniforms / Resource location + * \{ */ + +int GPU_shader_get_uniform(GPUShader *shader, const char *name) +{ + BLI_assert(shader && shader->program); + const GPUShaderInput *uniform = GPU_shaderinterface_uniform(shader->interface, name); + return uniform ? uniform->location : -1; +} + +int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin) +{ + BLI_assert(shader && shader->program); + return GPU_shaderinterface_uniform_builtin(shader->interface, + static_cast<GPUUniformBuiltin>(builtin)); +} + +int GPU_shader_get_builtin_block(GPUShader *shader, int builtin) +{ + BLI_assert(shader && shader->program); + return GPU_shaderinterface_block_builtin(shader->interface, + static_cast<GPUUniformBlockBuiltin>(builtin)); +} + +int GPU_shader_get_uniform_block(GPUShader *shader, const char *name) +{ + BLI_assert(shader && shader->program); + const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name); + return ubo ? ubo->location : -1; +} + +int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name) +{ + BLI_assert(shader && shader->program); + const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name); + return ubo ? ubo->binding : -1; +} + +int GPU_shader_get_texture_binding(GPUShader *shader, const char *name) +{ + BLI_assert(shader && shader->program); + const GPUShaderInput *tex = GPU_shaderinterface_uniform(shader->interface, name); + return tex ? tex->binding : -1; +} + +int GPU_shader_get_attribute(GPUShader *shader, const char *name) +{ + BLI_assert(shader && shader->program); + const GPUShaderInput *attr = GPU_shaderinterface_attr(shader->interface, name); + return attr ? attr->location : -1; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Getters + * \{ */ + +/* Clement : Temp */ +int GPU_shader_get_program(GPUShader *shader) +{ + return (int)shader->program; +} + +char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len) +{ + BLI_assert(GLEW_ARB_get_program_binary); + char *r_binary; + int binary_len = 0; + + glGetProgramiv(shader->program, GL_PROGRAM_BINARY_LENGTH, &binary_len); + r_binary = (char *)MEM_mallocN(binary_len, __func__); + glGetProgramBinary(shader->program, binary_len, NULL, r_binary_format, r_binary); + + if (r_binary_len) { + *r_binary_len = binary_len; + } + + return r_binary; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Uniforms setters + * \{ */ + +void GPU_shader_uniform_float(GPUShader *UNUSED(shader), int location, float value) +{ + if (location == -1) { + return; + } + + glUniform1f(location, value); +} + +void GPU_shader_uniform_vector( + GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value) +{ + if (location == -1 || value == NULL) { + return; + } + + switch (length) { + case 1: + glUniform1fv(location, arraysize, value); + break; + case 2: + glUniform2fv(location, arraysize, value); + break; + case 3: + glUniform3fv(location, arraysize, value); + break; + case 4: + glUniform4fv(location, arraysize, value); + break; + case 9: + glUniformMatrix3fv(location, arraysize, 0, value); + break; + case 16: + glUniformMatrix4fv(location, arraysize, 0, value); + break; + default: + BLI_assert(0); + break; + } +} + +void GPU_shader_uniform_int(GPUShader *UNUSED(shader), int location, int value) +{ + if (location == -1) { + return; + } + + glUniform1i(location, value); +} + +void GPU_shader_uniform_vector_int( + GPUShader *UNUSED(shader), int location, int length, int arraysize, const int *value) +{ + if (location == -1) { + return; + } + + switch (length) { + case 1: + glUniform1iv(location, arraysize, value); + break; + case 2: + glUniform2iv(location, arraysize, value); + break; + case 3: + glUniform3iv(location, arraysize, value); + break; + case 4: + glUniform4iv(location, arraysize, value); + break; + default: + BLI_assert(0); + break; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name sRGB Rendering Workaround + * + * The viewport overlay frame-buffer is sRGB and will expect shaders to output display referred + * Linear colors. But other frame-buffers (i.e: the area frame-buffers) are not sRGB and require + * the shader output color to be in sRGB space + * (assumed display encoded color-space as the time of writing). + * For this reason we have a uniform to switch the transform on and off depending on the current + * frame-buffer color-space. + * \{ */ + +static int g_shader_builtin_srgb_transform = 0; + +void GPU_shader_set_srgb_uniform(const GPUShaderInterface *interface) +{ + int32_t loc = GPU_shaderinterface_uniform_builtin(interface, GPU_UNIFORM_SRGB_TRANSFORM); + if (loc != -1) { + glUniform1i(loc, g_shader_builtin_srgb_transform); + } +} + +void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear) +{ + g_shader_builtin_srgb_transform = use_srgb_to_linear; +} + +/** \} */ diff --git a/source/blender/gpu/intern/gpu_shader.c b/source/blender/gpu/intern/gpu_shader_builtin.c index 8c03567b95f..9c0692b76e2 100644 --- a/source/blender/gpu/intern/gpu_shader.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -81,10 +81,7 @@ extern char datatoc_gpu_shader_image_overlays_stereo_merge_frag_glsl[]; extern char datatoc_gpu_shader_image_color_frag_glsl[]; extern char datatoc_gpu_shader_image_desaturate_frag_glsl[]; extern char datatoc_gpu_shader_image_varying_color_frag_glsl[]; -extern char datatoc_gpu_shader_image_alpha_color_frag_glsl[]; extern char datatoc_gpu_shader_image_shuffle_color_frag_glsl[]; -extern char datatoc_gpu_shader_image_mask_uniform_color_frag_glsl[]; -extern char datatoc_gpu_shader_image_modulate_alpha_frag_glsl[]; extern char datatoc_gpu_shader_3D_vert_glsl[]; extern char datatoc_gpu_shader_3D_normal_vert_glsl[]; extern char datatoc_gpu_shader_3D_flat_color_vert_glsl[]; @@ -117,6 +114,7 @@ extern char datatoc_gpu_shader_2D_point_uniform_size_varying_color_outline_aa_ve extern char datatoc_gpu_shader_2D_edituvs_points_vert_glsl[]; extern char datatoc_gpu_shader_2D_edituvs_facedots_vert_glsl[]; extern char datatoc_gpu_shader_2D_edituvs_edges_vert_glsl[]; +extern char datatoc_gpu_shader_2D_edituvs_edges_frag_glsl[]; extern char datatoc_gpu_shader_2D_edituvs_faces_vert_glsl[]; extern char datatoc_gpu_shader_2D_edituvs_stretch_vert_glsl[]; @@ -155,11 +153,6 @@ const struct GPUShaderConfigData GPU_shader_cfg_data[GPU_SHADER_CFG_LEN] = { /* cache of built-in shaders (each is created on first use) */ static GPUShader *builtin_shaders[GPU_SHADER_CFG_LEN][GPU_SHADER_BUILTIN_LEN] = {{NULL}}; -static int g_shader_builtin_srgb_transform = 0; - -#ifndef NDEBUG -static uint g_shaderid = 0; -#endif typedef struct { const char *vert; @@ -170,725 +163,6 @@ typedef struct { const char *defs; } GPUShaderStages; -static void shader_print_errors(const char *task, const char *log, const char **code, int totcode) -{ - int line = 1; - - fprintf(stderr, "GPUShader: %s error:\n", task); - - for (int i = 0; i < totcode; i++) { - const char *c, *pos, *end = code[i] + strlen(code[i]); - - if (G.debug & G_DEBUG) { - fprintf(stderr, "===== shader string %d ====\n", i + 1); - - c = code[i]; - while ((c < end) && (pos = strchr(c, '\n'))) { - fprintf(stderr, "%2d ", line); - fwrite(c, (pos + 1) - c, 1, stderr); - c = pos + 1; - line++; - } - - fprintf(stderr, "%s", c); - } - } - - fprintf(stderr, "%s\n", log); -} - -static const char *gpu_shader_version(void) -{ - return "#version 330\n"; -} - -static void gpu_shader_standard_extensions(char defines[MAX_EXT_DEFINE_LENGTH]) -{ - /* enable extensions for features that are not part of our base GLSL version - * don't use an extension for something already available! - */ - - if (GLEW_ARB_texture_gather) { - /* There is a bug on older Nvidia GPU where GL_ARB_texture_gather - * is reported to be supported but yield a compile error (see T55802). */ - if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) || GLEW_VERSION_4_0) { - strcat(defines, "#extension GL_ARB_texture_gather: enable\n"); - - /* Some drivers don't agree on GLEW_ARB_texture_gather and the actual support in the - * shader so double check the preprocessor define (see T56544). */ - if (!GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY) && !GLEW_VERSION_4_0) { - strcat(defines, "#ifdef GL_ARB_texture_gather\n"); - strcat(defines, "# define GPU_ARB_texture_gather\n"); - strcat(defines, "#endif\n"); - } - else { - strcat(defines, "#define GPU_ARB_texture_gather\n"); - } - } - } - if (GLEW_ARB_texture_query_lod) { - /* a #version 400 feature, but we use #version 330 maximum so use extension */ - strcat(defines, "#extension GL_ARB_texture_query_lod: enable\n"); - } - if (GLEW_ARB_shader_draw_parameters) { - strcat(defines, "#extension GL_ARB_shader_draw_parameters : enable\n"); - } - if (GPU_arb_texture_cube_map_array_is_supported()) { - strcat(defines, "#extension GL_ARB_texture_cube_map_array : enable\n"); - strcat(defines, "#define GPU_ARB_texture_cube_map_array\n"); - } -} - -static void gpu_shader_standard_defines(char defines[MAX_DEFINE_LENGTH]) -{ - /* some useful defines to detect GPU type */ - if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) { - strcat(defines, "#define GPU_ATI\n"); - if (GPU_crappy_amd_driver()) { - strcat(defines, "#define GPU_DEPRECATED_AMD_DRIVER\n"); - } - } - else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) { - strcat(defines, "#define GPU_NVIDIA\n"); - } - else if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) { - strcat(defines, "#define GPU_INTEL\n"); - } - - /* some useful defines to detect OS type */ - if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_WIN, GPU_DRIVER_ANY)) { - strcat(defines, "#define OS_WIN\n"); - } - else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_MAC, GPU_DRIVER_ANY)) { - strcat(defines, "#define OS_MAC\n"); - } - else if (GPU_type_matches(GPU_DEVICE_ANY, GPU_OS_UNIX, GPU_DRIVER_ANY)) { - strcat(defines, "#define OS_UNIX\n"); - } - - float derivatives_factors[2]; - GPU_get_dfdy_factors(derivatives_factors); - if (derivatives_factors[0] == 1.0f) { - strcat(defines, "#define DFDX_SIGN 1.0\n"); - } - else { - strcat(defines, "#define DFDX_SIGN -1.0\n"); - } - - if (derivatives_factors[1] == 1.0f) { - strcat(defines, "#define DFDY_SIGN 1.0\n"); - } - else { - strcat(defines, "#define DFDY_SIGN -1.0\n"); - } - - return; -} - -GPUShader *GPU_shader_create(const char *vertexcode, - const char *fragcode, - const char *geocode, - const char *libcode, - const char *defines, - const char *shname) -{ - return GPU_shader_create_ex( - vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, shname); -} - -GPUShader *GPU_shader_create_from_python(const char *vertexcode, - const char *fragcode, - const char *geocode, - const char *libcode, - const char *defines) -{ - char *libcodecat = NULL; - - if (libcode == NULL) { - libcode = datatoc_gpu_shader_colorspace_lib_glsl; - } - else { - libcode = libcodecat = BLI_strdupcat(libcode, datatoc_gpu_shader_colorspace_lib_glsl); - } - - GPUShader *sh = GPU_shader_create_ex( - vertexcode, fragcode, geocode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, NULL); - - MEM_SAFE_FREE(libcodecat); - return sh; -} - -GPUShader *GPU_shader_load_from_binary(const char *binary, - const int binary_format, - const int binary_len, - const char *shname) -{ - BLI_assert(GL_ARB_get_program_binary); - int success; - int program = glCreateProgram(); - - glProgramBinary(program, binary_format, binary, binary_len); - glGetProgramiv(program, GL_LINK_STATUS, &success); - - if (success) { - glUseProgram(program); - - GPUShader *shader = MEM_callocN(sizeof(*shader), __func__); - shader->interface = GPU_shaderinterface_create(program); - shader->program = program; - -#ifndef NDEBUG - BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++); -#else - UNUSED_VARS(shname); -#endif - - return shader; - } - - glDeleteProgram(program); - return NULL; -} - -#define DEBUG_SHADER_NONE "" -#define DEBUG_SHADER_VERTEX "vert" -#define DEBUG_SHADER_FRAGMENT "frag" -#define DEBUG_SHADER_GEOMETRY "geom" - -/** - * Dump GLSL shaders to disk - * - * This is used for profiling shader performance externally and debug if shader code is correct. - * If called with no code, it simply bumps the shader index, so different shaders for the same - * program share the same index. - */ -static void gpu_dump_shaders(const char **code, const int num_shaders, const char *extension) -{ - if ((G.debug & G_DEBUG_GPU_SHADERS) == 0) { - return; - } - - /* We use the same shader index for shaders in the same program. - * So we call this function once before calling for the individual shaders. */ - static int shader_index = 0; - if (code == NULL) { - shader_index++; - BLI_assert(STREQ(DEBUG_SHADER_NONE, extension)); - return; - } - - /* Determine the full path of the new shader. */ - char shader_path[FILE_MAX]; - - char file_name[512] = {'\0'}; - sprintf(file_name, "%04d.%s", shader_index, extension); - - BLI_join_dirfile(shader_path, sizeof(shader_path), BKE_tempdir_session(), file_name); - - /* Write shader to disk. */ - FILE *f = fopen(shader_path, "w"); - if (f == NULL) { - printf("Error writing to file: %s\n", shader_path); - } - for (int j = 0; j < num_shaders; j++) { - fprintf(f, "%s", code[j]); - } - fclose(f); - printf("Shader file written to disk: %s\n", shader_path); -} - -GPUShader *GPU_shader_create_ex(const char *vertexcode, - const char *fragcode, - const char *geocode, - const char *libcode, - const char *defines, - const eGPUShaderTFBType tf_type, - const char **tf_names, - const int tf_count, - const char *shname) -{ - GLint status; - GLchar log[5000]; - GLsizei length = 0; - GPUShader *shader; - char standard_defines[MAX_DEFINE_LENGTH] = ""; - char standard_extensions[MAX_EXT_DEFINE_LENGTH] = ""; - - shader = MEM_callocN(sizeof(GPUShader), "GPUShader"); - gpu_dump_shaders(NULL, 0, DEBUG_SHADER_NONE); - -#ifndef NDEBUG - BLI_snprintf(shader->name, sizeof(shader->name), "%s_%u", shname, g_shaderid++); -#else - UNUSED_VARS(shname); -#endif - - /* At least a vertex shader and a fragment shader are required. */ - BLI_assert((fragcode != NULL) && (vertexcode != NULL)); - - if (vertexcode) { - shader->vertex = glCreateShader(GL_VERTEX_SHADER); - } - if (fragcode) { - shader->fragment = glCreateShader(GL_FRAGMENT_SHADER); - } - if (geocode) { - shader->geometry = glCreateShader(GL_GEOMETRY_SHADER); - } - - shader->program = glCreateProgram(); - - if (!shader->program || (vertexcode && !shader->vertex) || (fragcode && !shader->fragment) || - (geocode && !shader->geometry)) { - fprintf(stderr, "GPUShader, object creation failed.\n"); - GPU_shader_free(shader); - return NULL; - } - - gpu_shader_standard_defines(standard_defines); - gpu_shader_standard_extensions(standard_extensions); - - if (vertexcode) { - const char *source[6]; - /* custom limit, may be too small, beware */ - int num_source = 0; - - source[num_source++] = gpu_shader_version(); - source[num_source++] = "#define GPU_VERTEX_SHADER\n"; - source[num_source++] = standard_extensions; - source[num_source++] = standard_defines; - - if (defines) { - source[num_source++] = defines; - } - source[num_source++] = vertexcode; - - gpu_dump_shaders(source, num_source, DEBUG_SHADER_VERTEX); - - glAttachShader(shader->program, shader->vertex); - glShaderSource(shader->vertex, num_source, source, NULL); - - glCompileShader(shader->vertex); - glGetShaderiv(shader->vertex, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader->vertex, sizeof(log), &length, log); - shader_print_errors("compile", log, source, num_source); - - GPU_shader_free(shader); - return NULL; - } - } - - if (fragcode) { - const char *source[7]; - int num_source = 0; - - source[num_source++] = gpu_shader_version(); - source[num_source++] = "#define GPU_FRAGMENT_SHADER\n"; - source[num_source++] = standard_extensions; - source[num_source++] = standard_defines; - - if (defines) { - source[num_source++] = defines; - } - if (libcode) { - source[num_source++] = libcode; - } - source[num_source++] = fragcode; - - gpu_dump_shaders(source, num_source, DEBUG_SHADER_FRAGMENT); - - glAttachShader(shader->program, shader->fragment); - glShaderSource(shader->fragment, num_source, source, NULL); - - glCompileShader(shader->fragment); - glGetShaderiv(shader->fragment, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader->fragment, sizeof(log), &length, log); - shader_print_errors("compile", log, source, num_source); - - GPU_shader_free(shader); - return NULL; - } - } - - if (geocode) { - const char *source[6]; - int num_source = 0; - - source[num_source++] = gpu_shader_version(); - source[num_source++] = "#define GPU_GEOMETRY_SHADER\n"; - source[num_source++] = standard_extensions; - source[num_source++] = standard_defines; - - if (defines) { - source[num_source++] = defines; - } - source[num_source++] = geocode; - - gpu_dump_shaders(source, num_source, DEBUG_SHADER_GEOMETRY); - - glAttachShader(shader->program, shader->geometry); - glShaderSource(shader->geometry, num_source, source, NULL); - - glCompileShader(shader->geometry); - glGetShaderiv(shader->geometry, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader->geometry, sizeof(log), &length, log); - shader_print_errors("compile", log, source, num_source); - - GPU_shader_free(shader); - return NULL; - } - } - - if (tf_names != NULL) { - glTransformFeedbackVaryings(shader->program, tf_count, tf_names, GL_INTERLEAVED_ATTRIBS); - /* Primitive type must be setup */ - BLI_assert(tf_type != GPU_SHADER_TFB_NONE); - shader->feedback_transform_type = tf_type; - } - - glLinkProgram(shader->program); - glGetProgramiv(shader->program, GL_LINK_STATUS, &status); - if (!status) { - glGetProgramInfoLog(shader->program, sizeof(log), &length, log); - /* print attached shaders in pipeline order */ - if (vertexcode) { - shader_print_errors("linking", log, &vertexcode, 1); - } - if (geocode) { - shader_print_errors("linking", log, &geocode, 1); - } - if (libcode) { - shader_print_errors("linking", log, &libcode, 1); - } - if (fragcode) { - shader_print_errors("linking", log, &fragcode, 1); - } - - GPU_shader_free(shader); - return NULL; - } - - glUseProgram(shader->program); - shader->interface = GPU_shaderinterface_create(shader->program); - - return shader; -} - -#undef DEBUG_SHADER_GEOMETRY -#undef DEBUG_SHADER_FRAGMENT -#undef DEBUG_SHADER_VERTEX -#undef DEBUG_SHADER_NONE - -static const char *string_join_array_maybe_alloc(const char **str_arr, bool *r_is_alloc) -{ - bool is_alloc = false; - if (str_arr == NULL) { - *r_is_alloc = false; - return NULL; - } - /* Skip empty strings (avoid alloc if we can). */ - while (str_arr[0] && str_arr[0][0] == '\0') { - str_arr++; - } - int i; - for (i = 0; str_arr[i]; i++) { - if (i != 0 && str_arr[i][0] != '\0') { - is_alloc = true; - } - } - *r_is_alloc = is_alloc; - if (is_alloc) { - return BLI_string_join_arrayN(str_arr, i); - } - else { - return str_arr[0]; - } -} - -/** - * Use via #GPU_shader_create_from_arrays macro (avoids passing in param). - * - * Similar to #DRW_shader_create_with_lib with the ability to include libs for each type of shader. - * - * It has the advantage that each item can be conditionally included - * without having to build the string inline, then free it. - * - * \param params: NULL terminated arrays of strings. - * - * Example: - * \code{.c} - * sh = GPU_shader_create_from_arrays({ - * .vert = (const char *[]){shader_lib_glsl, shader_vert_glsl, NULL}, - * .geom = (const char *[]){shader_geom_glsl, NULL}, - * .frag = (const char *[]){shader_frag_glsl, NULL}, - * .defs = (const char *[]){"#define DEFINE\n", test ? "#define OTHER_DEFINE\n" : "", NULL}, - * }); - * \endcode - */ -struct GPUShader *GPU_shader_create_from_arrays_impl( - const struct GPU_ShaderCreateFromArray_Params *params) -{ - struct { - const char *str; - bool is_alloc; - } str_dst[4] = {{0}}; - const char **str_src[4] = {params->vert, params->frag, params->geom, params->defs}; - - for (int i = 0; i < ARRAY_SIZE(str_src); i++) { - str_dst[i].str = string_join_array_maybe_alloc(str_src[i], &str_dst[i].is_alloc); - } - - GPUShader *sh = GPU_shader_create( - str_dst[0].str, str_dst[1].str, str_dst[2].str, NULL, str_dst[3].str, __func__); - - for (int i = 0; i < ARRAY_SIZE(str_dst); i++) { - if (str_dst[i].is_alloc) { - MEM_freeN((void *)str_dst[i].str); - } - } - return sh; -} - -void GPU_shader_bind(GPUShader *shader) -{ - BLI_assert(shader && shader->program); - - glUseProgram(shader->program); - GPU_matrix_bind(shader->interface); - GPU_shader_set_srgb_uniform(shader->interface); -} - -void GPU_shader_unbind(void) -{ - glUseProgram(0); -} - -bool GPU_shader_transform_feedback_enable(GPUShader *shader, uint vbo_id) -{ - if (shader->feedback_transform_type == GPU_SHADER_TFB_NONE) { - return false; - } - - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, vbo_id); - - switch (shader->feedback_transform_type) { - case GPU_SHADER_TFB_POINTS: - glBeginTransformFeedback(GL_POINTS); - return true; - case GPU_SHADER_TFB_LINES: - glBeginTransformFeedback(GL_LINES); - return true; - case GPU_SHADER_TFB_TRIANGLES: - glBeginTransformFeedback(GL_TRIANGLES); - return true; - default: - return false; - } -} - -void GPU_shader_transform_feedback_disable(GPUShader *UNUSED(shader)) -{ - glEndTransformFeedback(); -} - -void GPU_shader_free(GPUShader *shader) -{ -#if 0 /* Would be nice to have, but for now the Deferred compilation \ - * does not have a GPUContext. */ - BLI_assert(GPU_context_active_get() != NULL); -#endif - BLI_assert(shader); - - if (shader->vertex) { - glDeleteShader(shader->vertex); - } - if (shader->geometry) { - glDeleteShader(shader->geometry); - } - if (shader->fragment) { - glDeleteShader(shader->fragment); - } - if (shader->program) { - glDeleteProgram(shader->program); - } - - if (shader->interface) { - GPU_shaderinterface_discard(shader->interface); - } - - MEM_freeN(shader); -} - -int GPU_shader_get_uniform(GPUShader *shader, const char *name) -{ - BLI_assert(shader && shader->program); - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(shader->interface, name); - return uniform ? uniform->location : -1; -} - -int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin) -{ - BLI_assert(shader && shader->program); - return GPU_shaderinterface_uniform_builtin(shader->interface, builtin); -} - -int GPU_shader_get_builtin_block(GPUShader *shader, int builtin) -{ - BLI_assert(shader && shader->program); - return GPU_shaderinterface_block_builtin(shader->interface, builtin); -} - -int GPU_shader_get_uniform_block(GPUShader *shader, const char *name) -{ - BLI_assert(shader && shader->program); - const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name); - return ubo ? ubo->location : -1; -} - -int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name) -{ - BLI_assert(shader && shader->program); - const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name); - return ubo ? ubo->binding : -1; -} - -int GPU_shader_get_texture_binding(GPUShader *shader, const char *name) -{ - BLI_assert(shader && shader->program); - const GPUShaderInput *tex = GPU_shaderinterface_uniform(shader->interface, name); - return tex ? tex->binding : -1; -} - -void *GPU_shader_get_interface(GPUShader *shader) -{ - return shader->interface; -} - -/* Clement : Temp */ -int GPU_shader_get_program(GPUShader *shader) -{ - return (int)shader->program; -} - -void GPU_shader_uniform_float(GPUShader *UNUSED(shader), int location, float value) -{ - if (location == -1) { - return; - } - - glUniform1f(location, value); -} - -void GPU_shader_uniform_vector( - GPUShader *UNUSED(shader), int location, int length, int arraysize, const float *value) -{ - if (location == -1 || value == NULL) { - return; - } - - switch (length) { - case 1: - glUniform1fv(location, arraysize, value); - break; - case 2: - glUniform2fv(location, arraysize, value); - break; - case 3: - glUniform3fv(location, arraysize, value); - break; - case 4: - glUniform4fv(location, arraysize, value); - break; - case 9: - glUniformMatrix3fv(location, arraysize, 0, value); - break; - case 16: - glUniformMatrix4fv(location, arraysize, 0, value); - break; - default: - BLI_assert(0); - break; - } -} - -void GPU_shader_uniform_vector_int( - GPUShader *UNUSED(shader), int location, int length, int arraysize, const int *value) -{ - if (location == -1) { - return; - } - - switch (length) { - case 1: - glUniform1iv(location, arraysize, value); - break; - case 2: - glUniform2iv(location, arraysize, value); - break; - case 3: - glUniform3iv(location, arraysize, value); - break; - case 4: - glUniform4iv(location, arraysize, value); - break; - default: - BLI_assert(0); - break; - } -} - -void GPU_shader_uniform_int(GPUShader *UNUSED(shader), int location, int value) -{ - if (location == -1) { - return; - } - - glUniform1i(location, value); -} - -void GPU_shader_set_srgb_uniform(const GPUShaderInterface *interface) -{ - int32_t loc = GPU_shaderinterface_uniform_builtin(interface, GPU_UNIFORM_SRGB_TRANSFORM); - if (loc != -1) { - glUniform1i(loc, g_shader_builtin_srgb_transform); - } -} - -int GPU_shader_get_attribute(GPUShader *shader, const char *name) -{ - BLI_assert(shader && shader->program); - const GPUShaderInput *attr = GPU_shaderinterface_attr(shader->interface, name); - return attr ? attr->location : -1; -} - -char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len) -{ - BLI_assert(GLEW_ARB_get_program_binary); - char *r_binary; - int binary_len = 0; - - glGetProgramiv(shader->program, GL_PROGRAM_BINARY_LENGTH, &binary_len); - r_binary = MEM_mallocN(binary_len, __func__); - glGetProgramBinary(shader->program, binary_len, NULL, r_binary_format, r_binary); - - if (r_binary_len) { - *r_binary_len = binary_len; - } - - return r_binary; -} - -void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear) -{ - g_shader_builtin_srgb_transform = use_srgb_to_linear; -} - static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = { [GPU_SHADER_TEXT] = { @@ -906,16 +180,6 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = { .frag = datatoc_gpu_shader_simple_lighting_frag_glsl, }, - [GPU_SHADER_2D_IMAGE_MASK_UNIFORM_COLOR] = - { - .vert = datatoc_gpu_shader_3D_image_vert_glsl, - .frag = datatoc_gpu_shader_image_mask_uniform_color_frag_glsl, - }, - [GPU_SHADER_3D_IMAGE_MODULATE_ALPHA] = - { - .vert = datatoc_gpu_shader_3D_image_vert_glsl, - .frag = datatoc_gpu_shader_image_modulate_alpha_frag_glsl, - }, [GPU_SHADER_2D_CHECKER] = { .vert = datatoc_gpu_shader_2D_vert_glsl, @@ -968,11 +232,6 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = { .vert = datatoc_gpu_shader_2D_image_vert_glsl, .frag = datatoc_gpu_shader_image_desaturate_frag_glsl, }, - [GPU_SHADER_2D_IMAGE_ALPHA_COLOR] = - { - .vert = datatoc_gpu_shader_2D_image_vert_glsl, - .frag = datatoc_gpu_shader_image_alpha_color_frag_glsl, - }, [GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR] = { .vert = datatoc_gpu_shader_2D_image_vert_glsl, @@ -1171,12 +430,12 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = { [GPU_SHADER_2D_UV_EDGES] = { .vert = datatoc_gpu_shader_2D_edituvs_edges_vert_glsl, - .frag = datatoc_gpu_shader_flat_color_frag_glsl, + .frag = datatoc_gpu_shader_2D_edituvs_edges_frag_glsl, }, [GPU_SHADER_2D_UV_EDGES_SMOOTH] = { .vert = datatoc_gpu_shader_2D_edituvs_edges_vert_glsl, - .frag = datatoc_gpu_shader_2D_smooth_color_frag_glsl, + .frag = datatoc_gpu_shader_2D_edituvs_edges_frag_glsl, .defs = "#define SMOOTH_COLOR\n", }, [GPU_SHADER_2D_UV_FACES] = @@ -1257,6 +516,7 @@ GPUShader *GPU_shader_get_builtin_shader_with_config(eGPUBuiltinShader shader, return *sh_p; } + GPUShader *GPU_shader_get_builtin_shader(eGPUBuiltinShader shader) { return GPU_shader_get_builtin_shader_with_config(shader, GPU_SHADER_CFG_DEFAULT); diff --git a/source/blender/gpu/intern/gpu_shader_private.h b/source/blender/gpu/intern/gpu_shader_private.h index e4443e79a8d..2c52568ac25 100644 --- a/source/blender/gpu/intern/gpu_shader_private.h +++ b/source/blender/gpu/intern/gpu_shader_private.h @@ -21,9 +21,12 @@ #ifndef __GPU_SHADER_PRIVATE_H__ #define __GPU_SHADER_PRIVATE_H__ -#include "GPU_glew.h" #include "GPU_shader_interface.h" +#ifdef __cplusplus +extern "C" { +#endif + struct GPUShader { /** Handle for full program (links shader stages below). */ GLuint program; @@ -45,6 +48,10 @@ struct GPUShader { }; /* XXX do not use it. Special hack to use OCIO with batch API. */ -void immGetProgram(GLuint *program, GPUShaderInterface **shaderface); +GPUShader *immGetShader(void); + +#ifdef __cplusplus +} +#endif #endif /* __GPU_SHADER_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_state.c b/source/blender/gpu/intern/gpu_state.cc index 908f5fa5771..794c7a3eb97 100644 --- a/source/blender/gpu/intern/gpu_state.c +++ b/source/blender/gpu/intern/gpu_state.cc @@ -78,6 +78,28 @@ void GPU_blend_set_func_separate(eGPUBlendFunction src_rgb, gpu_get_gl_blendfunction(dst_alpha)); } +void GPU_face_culling(eGPUFaceCull culling) +{ + if (culling == GPU_CULL_NONE) { + glDisable(GL_CULL_FACE); + } + else { + glEnable(GL_CULL_FACE); + glCullFace((culling == GPU_CULL_FRONT) ? GL_FRONT : GL_BACK); + } +} + +void GPU_front_facing(bool invert) +{ + glFrontFace((invert) ? GL_CW : GL_CCW); +} + +void GPU_provoking_vertex(eGPUProvokingVertex vert) +{ + glProvokingVertex((vert == GPU_VERTEX_FIRST) ? GL_FIRST_VERTEX_CONVENTION : + GL_LAST_VERTEX_CONVENTION); +} + void GPU_depth_range(float near, float far) { /* glDepthRangef is only for OpenGL 4.1 or higher */ @@ -146,11 +168,26 @@ void GPU_program_point_size(bool enable) } } +void GPU_scissor_test(bool enable) +{ + if (enable) { + glEnable(GL_SCISSOR_TEST); + } + else { + glDisable(GL_SCISSOR_TEST); + } +} + void GPU_scissor(int x, int y, int width, int height) { glScissor(x, y, width, height); } +void GPU_viewport(int x, int y, int width, int height) +{ + glViewport(x, y, width, height); +} + void GPU_scissor_get_f(float coords[4]) { glGetFloatv(GL_SCISSOR_BOX, coords); @@ -181,20 +218,62 @@ void GPU_finish(void) glFinish(); } -void GPU_logic_op_invert_set(bool enable) +void GPU_unpack_row_length_set(uint len) +{ + glPixelStorei(GL_UNPACK_ROW_LENGTH, len); +} + +void GPU_logic_op_xor_set(bool enable) { if (enable) { - glLogicOp(GL_INVERT); + glLogicOp(GL_XOR); glEnable(GL_COLOR_LOGIC_OP); - glDisable(GL_DITHER); } else { - glLogicOp(GL_COPY); glDisable(GL_COLOR_LOGIC_OP); - glEnable(GL_DITHER); } } +void GPU_color_mask(bool r, bool g, bool b, bool a) +{ + glColorMask(r, g, b, a); +} + +void GPU_depth_mask(bool depth) +{ + glDepthMask(depth); +} + +bool GPU_depth_mask_get(void) +{ + GLint mask; + glGetIntegerv(GL_DEPTH_WRITEMASK, &mask); + return mask == GL_TRUE; +} + +void GPU_stencil_mask(uint stencil) +{ + glStencilMask(stencil); +} + +void GPU_clip_distances(int distances_new) +{ + static int distances_enabled = 0; + for (int i = 0; i < distances_new; i++) { + glEnable(GL_CLIP_DISTANCE0 + i); + } + for (int i = distances_new; i < distances_enabled; i++) { + glDisable(GL_CLIP_DISTANCE0 + i); + } + distances_enabled = distances_new; +} + +bool GPU_mipmap_enabled(void) +{ + /* TODO(fclem) this used to be a userdef option. */ + return true; +} + /** \name GPU Push/Pop State * \{ */ @@ -203,34 +282,18 @@ void GPU_logic_op_invert_set(bool enable) typedef struct { eGPUAttrMask mask; - /* GL_ENABLE_BIT */ + /* GL_BLEND_BIT */ uint is_blend : 1; - uint is_cull_face : 1; - uint is_depth_test : 1; - uint is_dither : 1; - uint is_lighting : 1; - uint is_line_smooth : 1; - uint is_color_logic_op : 1; - uint is_multisample : 1; - uint is_polygon_offset_line : 1; - uint is_polygon_offset_fill : 1; - uint is_polygon_smooth : 1; - uint is_sample_alpha_to_coverage : 1; - uint is_scissor_test : 1; - uint is_stencil_test : 1; - uint is_framebuffer_srgb : 1; - - bool is_clip_plane[6]; /* GL_DEPTH_BUFFER_BIT */ - /* uint is_depth_test : 1; */ + uint is_depth_test : 1; int depth_func; double depth_clear_value; bool depth_write_mask; /* GL_SCISSOR_BIT */ int scissor_box[4]; - /* uint is_scissor_test : 1; */ + uint is_scissor_test : 1; /* GL_VIEWPORT_BIT */ int viewport[4]; @@ -243,7 +306,8 @@ typedef struct { } GPUAttrStack; static GPUAttrStack state = { - .top = 0, + {}, + 0, }; #define AttrStack state @@ -266,27 +330,6 @@ void gpuPushAttr(eGPUAttrMask mask) glGetBooleanv(GL_DEPTH_WRITEMASK, (GLboolean *)&Attr.depth_write_mask); } - if ((mask & GPU_ENABLE_BIT) != 0) { - Attr.is_blend = glIsEnabled(GL_BLEND); - - for (int i = 0; i < 6; i++) { - Attr.is_clip_plane[i] = glIsEnabled(GL_CLIP_PLANE0 + i); - } - - Attr.is_cull_face = glIsEnabled(GL_CULL_FACE); - Attr.is_depth_test = glIsEnabled(GL_DEPTH_TEST); - Attr.is_dither = glIsEnabled(GL_DITHER); - Attr.is_line_smooth = glIsEnabled(GL_LINE_SMOOTH); - Attr.is_color_logic_op = glIsEnabled(GL_COLOR_LOGIC_OP); - Attr.is_multisample = glIsEnabled(GL_MULTISAMPLE); - Attr.is_polygon_offset_line = glIsEnabled(GL_POLYGON_OFFSET_LINE); - Attr.is_polygon_offset_fill = glIsEnabled(GL_POLYGON_OFFSET_FILL); - Attr.is_polygon_smooth = glIsEnabled(GL_POLYGON_SMOOTH); - Attr.is_sample_alpha_to_coverage = glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE); - Attr.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST); - Attr.is_stencil_test = glIsEnabled(GL_STENCIL_TEST); - } - if ((mask & GPU_SCISSOR_BIT) != 0) { Attr.is_scissor_test = glIsEnabled(GL_SCISSOR_TEST); glGetIntegerv(GL_SCISSOR_BOX, (GLint *)&Attr.scissor_box); @@ -295,7 +338,6 @@ void gpuPushAttr(eGPUAttrMask mask) if ((mask & GPU_VIEWPORT_BIT) != 0) { glGetDoublev(GL_DEPTH_RANGE, (GLdouble *)&Attr.near_far); glGetIntegerv(GL_VIEWPORT, (GLint *)&Attr.viewport); - Attr.is_framebuffer_srgb = glIsEnabled(GL_FRAMEBUFFER_SRGB); } if ((mask & GPU_BLEND_BIT) != 0) { @@ -330,31 +372,9 @@ void gpuPopAttr(void) glDepthMask(Attr.depth_write_mask); } - if ((mask & GPU_ENABLE_BIT) != 0) { - restore_mask(GL_BLEND, Attr.is_blend); - - for (int i = 0; i < 6; i++) { - restore_mask(GL_CLIP_PLANE0 + i, Attr.is_clip_plane[i]); - } - - restore_mask(GL_CULL_FACE, Attr.is_cull_face); - restore_mask(GL_DEPTH_TEST, Attr.is_depth_test); - restore_mask(GL_DITHER, Attr.is_dither); - restore_mask(GL_LINE_SMOOTH, Attr.is_line_smooth); - restore_mask(GL_COLOR_LOGIC_OP, Attr.is_color_logic_op); - restore_mask(GL_MULTISAMPLE, Attr.is_multisample); - restore_mask(GL_POLYGON_OFFSET_LINE, Attr.is_polygon_offset_line); - restore_mask(GL_POLYGON_OFFSET_FILL, Attr.is_polygon_offset_fill); - restore_mask(GL_POLYGON_SMOOTH, Attr.is_polygon_smooth); - restore_mask(GL_SAMPLE_ALPHA_TO_COVERAGE, Attr.is_sample_alpha_to_coverage); - restore_mask(GL_SCISSOR_TEST, Attr.is_scissor_test); - restore_mask(GL_STENCIL_TEST, Attr.is_stencil_test); - } - if ((mask & GPU_VIEWPORT_BIT) != 0) { glViewport(Attr.viewport[0], Attr.viewport[1], Attr.viewport[2], Attr.viewport[3]); glDepthRange(Attr.near_far[0], Attr.near_far[1]); - restore_mask(GL_FRAMEBUFFER_SRGB, Attr.is_framebuffer_srgb); } if ((mask & GPU_SCISSOR_BIT) != 0) { @@ -396,6 +416,9 @@ void GPU_state_init(void) glCullFace(GL_BACK); glDisable(GL_CULL_FACE); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + /* Is default but better be explicit. */ glEnable(GL_MULTISAMPLE); diff --git a/source/blender/gpu/intern/gpu_texture.c b/source/blender/gpu/intern/gpu_texture.cc index a985d45162c..8a3a8a382f8 100644 --- a/source/blender/gpu/intern/gpu_texture.c +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -26,6 +26,7 @@ #include "MEM_guardedalloc.h" #include "DNA_image_types.h" +#include "DNA_userdef_types.h" #include "BLI_blenlib.h" #include "BLI_math_base.h" @@ -36,7 +37,6 @@ #include "GPU_batch.h" #include "GPU_context.h" #include "GPU_debug.h" -#include "GPU_draw.h" #include "GPU_extensions.h" #include "GPU_framebuffer.h" #include "GPU_glew.h" @@ -45,6 +45,16 @@ #include "gpu_context_private.h" +#define WARN_NOT_BOUND(_tex) \ + { \ + if (_tex->number == -1) { \ + fprintf(stderr, "Warning : Trying to set parameter on a texture not bound.\n"); \ + BLI_assert(0); \ + return; \ + } \ + } \ + ((void)0) + static struct GPUTextureGlobal { /** Texture used in place of invalid textures (not loaded correctly, missing). */ GPUTexture *invalid_tex_1D; @@ -52,6 +62,7 @@ static struct GPUTextureGlobal { GPUTexture *invalid_tex_3D; /** Sampler objects used to replace internal texture parameters. */ GLuint samplers[GPU_SAMPLER_MAX]; + GLuint icon_sampler; } GG = {NULL}; /* Maximum number of FBOs a texture can be attached to. */ @@ -70,6 +81,8 @@ typedef enum eGPUTextureFormatFlag { GPU_FORMAT_ARRAY = (1 << 14), } eGPUTextureFormatFlag; +ENUM_OPERATORS(eGPUTextureFormatFlag) + /* GPUTexture */ struct GPUTexture { int w, h, d; /* width/height/depth */ @@ -102,7 +115,7 @@ static void gpu_texture_framebuffer_ensure(GPUTexture *tex); /* ------ Memory Management ------- */ /* Records every texture allocation / free * to estimate the Texture Pool Memory consumption */ -static uint memory_usage; +static uint memory_usage = 0; static uint gpu_texture_memory_footprint_compute(GPUTexture *tex) { @@ -160,54 +173,58 @@ uint GPU_texture_memory_usage_get(void) static const char *gl_enum_to_str(GLenum e) { -#define ENUM_TO_STRING(e) [GL_##e] = STRINGIFY_ARG(e) - static const char *enum_strings[] = { - ENUM_TO_STRING(TEXTURE_CUBE_MAP), - ENUM_TO_STRING(TEXTURE_CUBE_MAP_ARRAY), - ENUM_TO_STRING(TEXTURE_2D), - ENUM_TO_STRING(TEXTURE_2D_ARRAY), - ENUM_TO_STRING(TEXTURE_1D), - ENUM_TO_STRING(TEXTURE_1D_ARRAY), - ENUM_TO_STRING(TEXTURE_3D), - ENUM_TO_STRING(TEXTURE_2D_MULTISAMPLE), - ENUM_TO_STRING(RGBA32F), - ENUM_TO_STRING(RGBA16F), - ENUM_TO_STRING(RGBA16UI), - ENUM_TO_STRING(RGBA16I), - ENUM_TO_STRING(RGBA16), - ENUM_TO_STRING(RGBA8UI), - ENUM_TO_STRING(RGBA8I), - ENUM_TO_STRING(RGBA8), - ENUM_TO_STRING(RGB16F), - ENUM_TO_STRING(RG32F), - ENUM_TO_STRING(RG16F), - ENUM_TO_STRING(RG16UI), - ENUM_TO_STRING(RG16I), - ENUM_TO_STRING(RG16), - ENUM_TO_STRING(RG8UI), - ENUM_TO_STRING(RG8I), - ENUM_TO_STRING(RG8), - ENUM_TO_STRING(R8UI), - ENUM_TO_STRING(R8I), - ENUM_TO_STRING(R8), - ENUM_TO_STRING(R32F), - ENUM_TO_STRING(R32UI), - ENUM_TO_STRING(R32I), - ENUM_TO_STRING(R16F), - ENUM_TO_STRING(R16UI), - ENUM_TO_STRING(R16I), - ENUM_TO_STRING(R16), - ENUM_TO_STRING(R11F_G11F_B10F), - ENUM_TO_STRING(SRGB8_ALPHA8), - ENUM_TO_STRING(DEPTH24_STENCIL8), - ENUM_TO_STRING(DEPTH32F_STENCIL8), - ENUM_TO_STRING(DEPTH_COMPONENT32F), - ENUM_TO_STRING(DEPTH_COMPONENT24), - ENUM_TO_STRING(DEPTH_COMPONENT16), +#define ENUM_TO_STRING(e) \ + case GL_##e: { \ + return STRINGIFY_ARG(e); \ + } + + switch (e) { + ENUM_TO_STRING(TEXTURE_CUBE_MAP); + ENUM_TO_STRING(TEXTURE_CUBE_MAP_ARRAY); + ENUM_TO_STRING(TEXTURE_2D); + ENUM_TO_STRING(TEXTURE_2D_ARRAY); + ENUM_TO_STRING(TEXTURE_1D); + ENUM_TO_STRING(TEXTURE_1D_ARRAY); + ENUM_TO_STRING(TEXTURE_3D); + ENUM_TO_STRING(TEXTURE_2D_MULTISAMPLE); + ENUM_TO_STRING(RGBA32F); + ENUM_TO_STRING(RGBA16F); + ENUM_TO_STRING(RGBA16UI); + ENUM_TO_STRING(RGBA16I); + ENUM_TO_STRING(RGBA16); + ENUM_TO_STRING(RGBA8UI); + ENUM_TO_STRING(RGBA8I); + ENUM_TO_STRING(RGBA8); + ENUM_TO_STRING(RGB16F); + ENUM_TO_STRING(RG32F); + ENUM_TO_STRING(RG16F); + ENUM_TO_STRING(RG16UI); + ENUM_TO_STRING(RG16I); + ENUM_TO_STRING(RG16); + ENUM_TO_STRING(RG8UI); + ENUM_TO_STRING(RG8I); + ENUM_TO_STRING(RG8); + ENUM_TO_STRING(R8UI); + ENUM_TO_STRING(R8I); + ENUM_TO_STRING(R8); + ENUM_TO_STRING(R32F); + ENUM_TO_STRING(R32UI); + ENUM_TO_STRING(R32I); + ENUM_TO_STRING(R16F); + ENUM_TO_STRING(R16UI); + ENUM_TO_STRING(R16I); + ENUM_TO_STRING(R16); + ENUM_TO_STRING(R11F_G11F_B10F); + ENUM_TO_STRING(SRGB8_ALPHA8); + ENUM_TO_STRING(DEPTH24_STENCIL8); + ENUM_TO_STRING(DEPTH32F_STENCIL8); + ENUM_TO_STRING(DEPTH_COMPONENT32F); + ENUM_TO_STRING(DEPTH_COMPONENT24); + ENUM_TO_STRING(DEPTH_COMPONENT16); + default: + return "Unkown enum"; }; #undef ENUM_TO_STRING - - return enum_strings[e]; } static int gpu_get_component_count(eGPUTextureFormat format) @@ -422,6 +439,13 @@ static uint gpu_get_bytesize(eGPUTextureFormat data_type) case GPU_R8: case GPU_R8UI: return 1; + case GPU_SRGB8_A8_DXT1: + case GPU_SRGB8_A8_DXT3: + case GPU_SRGB8_A8_DXT5: + case GPU_RGBA8_DXT1: + case GPU_RGBA8_DXT3: + case GPU_RGBA8_DXT5: + return 1; /* Incorrect but actual size is fractional. */ default: BLI_assert(!"Texture format incorrect or unsupported\n"); return 0; @@ -507,7 +531,18 @@ static GLenum gpu_format_to_gl_internalformat(eGPUTextureFormat format) case GPU_RGB16F: return GL_RGB16F; /* Special formats texture only */ - /* ** Add Format here */ + case GPU_SRGB8_A8_DXT1: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; + case GPU_SRGB8_A8_DXT3: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; + case GPU_SRGB8_A8_DXT5: + return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + case GPU_RGBA8_DXT1: + return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case GPU_RGBA8_DXT3: + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case GPU_RGBA8_DXT5: + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; /* Depth Formats */ case GPU_DEPTH_COMPONENT32F: return GL_DEPTH_COMPONENT32F; @@ -521,99 +556,6 @@ static GLenum gpu_format_to_gl_internalformat(eGPUTextureFormat format) } } -static eGPUTextureFormat gl_internalformat_to_gpu_format(const GLint glformat) -{ - /* You can add any of the available type to this list - * For available types see GPU_texture.h */ - switch (glformat) { - /* Formats texture & renderbuffer */ - case GL_RGBA8UI: - return GPU_RGBA8UI; - case GL_RGBA8I: - return GPU_RGBA8I; - case GL_RGBA8: - return GPU_RGBA8; - case GL_RGBA32UI: - return GPU_RGBA32UI; - case GL_RGBA32I: - return GPU_RGBA32I; - case GL_RGBA32F: - return GPU_RGBA32F; - case GL_RGBA16UI: - return GPU_RGBA16UI; - case GL_RGBA16I: - return GPU_RGBA16I; - case GL_RGBA16F: - return GPU_RGBA16F; - case GL_RGBA16: - return GPU_RGBA16; - case GL_RG8UI: - return GPU_RG8UI; - case GL_RG8I: - return GPU_RG8I; - case GL_RG8: - return GPU_RG8; - case GL_RG32UI: - return GPU_RG32UI; - case GL_RG32I: - return GPU_RG32I; - case GL_RG32F: - return GPU_RG32F; - case GL_RG16UI: - return GPU_RG16UI; - case GL_RG16I: - return GPU_RG16I; - case GL_RG16F: - return GPU_RGBA32F; - case GL_RG16: - return GPU_RG16; - case GL_R8UI: - return GPU_R8UI; - case GL_R8I: - return GPU_R8I; - case GL_R8: - return GPU_R8; - case GL_R32UI: - return GPU_R32UI; - case GL_R32I: - return GPU_R32I; - case GL_R32F: - return GPU_R32F; - case GL_R16UI: - return GPU_R16UI; - case GL_R16I: - return GPU_R16I; - case GL_R16F: - return GPU_R16F; - case GL_R16: - return GPU_R16; - /* Special formats texture & renderbuffer */ - case GL_R11F_G11F_B10F: - return GPU_R11F_G11F_B10F; - case GL_DEPTH32F_STENCIL8: - return GPU_DEPTH32F_STENCIL8; - case GL_DEPTH24_STENCIL8: - return GPU_DEPTH24_STENCIL8; - case GL_SRGB8_ALPHA8: - return GPU_SRGB8_A8; - /* Texture only format */ - case GL_RGB16F: - return GPU_RGB16F; - /* Special formats texture only */ - /* ** Add Format here */ - /* Depth Formats */ - case GL_DEPTH_COMPONENT32F: - return GPU_DEPTH_COMPONENT32F; - case GL_DEPTH_COMPONENT24: - return GPU_DEPTH_COMPONENT24; - case GL_DEPTH_COMPONENT16: - return GPU_DEPTH_COMPONENT16; - default: - BLI_assert(!"Internal format incorrect or unsupported\n"); - } - return -1; -} - static GLenum gpu_get_gl_datatype(eGPUDataFormat format) { switch (format) { @@ -639,8 +581,8 @@ static float *GPU_texture_rescale_3d( GPUTexture *tex, int w, int h, int d, int channels, const float *fpixels) { const uint xf = w / tex->w, yf = h / tex->h, zf = d / tex->d; - float *nfpixels = MEM_mallocN(channels * sizeof(float) * tex->w * tex->h * tex->d, - "GPUTexture Rescaled 3Dtex"); + float *nfpixels = (float *)MEM_mallocN(channels * sizeof(float) * tex->w * tex->h * tex->d, + "GPUTexture Rescaled 3Dtex"); if (nfpixels) { GPU_print_error_debug("You need to scale a 3D texture, feel the pain!"); @@ -677,12 +619,14 @@ static bool gpu_texture_check_capacity( GPUTexture *tex, GLenum proxy, GLenum internalformat, GLenum data_format, GLenum data_type) { if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_WIN, GPU_DRIVER_ANY) || + GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_MAC, GPU_DRIVER_OFFICIAL) || GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OFFICIAL)) { /* Some AMD drivers have a faulty `GL_PROXY_TEXTURE_..` check. * (see T55888, T56185, T59351). * Checking with `GL_PROXY_TEXTURE_..` doesn't prevent `Out Of Memory` issue, * it just states that the OGL implementation can support the texture. - * So manually check the maximum size and maximum number of layers. */ + * So manually check the maximum size and maximum number of layers. + * Same thing happens on Nvidia/macOS 10.15 (T78175). */ switch (proxy) { case GL_PROXY_TEXTURE_2D_ARRAY: if ((tex->d < 0) || (tex->d > GPU_max_texture_layers())) { @@ -825,7 +769,7 @@ GPUTexture *GPU_texture_create_nD(int w, tex_format = GPU_DEPTH32F_STENCIL8; } - GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); tex->w = w; tex->h = h; tex->d = d; @@ -834,7 +778,7 @@ GPUTexture *GPU_texture_create_nD(int w, tex->format = tex_format; tex->components = gpu_get_component_count(tex_format); tex->mipmaps = 0; - tex->format_flag = 0; + tex->format_flag = static_cast<eGPUTextureFormatFlag>(0); tex->number = -1; if (n == 2) { @@ -918,7 +862,7 @@ GPUTexture *GPU_texture_create_nD(int w, data_type, tex->components, can_rescale, - pixels, + (float *)pixels, &rescaled_pixels); if (G.debug & G_DEBUG_GPU || !valid) { @@ -949,7 +893,7 @@ GPUTexture *GPU_texture_create_nD(int w, gpu_texture_memory_footprint_add(tex); /* Upload Texture */ - const float *pix = (rescaled_pixels) ? rescaled_pixels : pixels; + const void *pix = (rescaled_pixels) ? rescaled_pixels : pixels; if (tex->target == GL_TEXTURE_2D || tex->target == GL_TEXTURE_2D_MULTISAMPLE || tex->target == GL_TEXTURE_1D_ARRAY) { @@ -999,7 +943,7 @@ GPUTexture *GPU_texture_cube_create(int w, eGPUDataFormat gpu_data_format, char err_out[256]) { - GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); tex->w = w; tex->h = w; tex->d = d; @@ -1123,11 +1067,11 @@ GPUTexture *GPU_texture_cube_create(int w, /* Special buffer textures. tex_format must be compatible with the buffer content. */ GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat tex_format, const GLuint buffer) { - GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); tex->refcount = 1; tex->format = tex_format; tex->components = gpu_get_component_count(tex_format); - tex->format_flag = 0; + tex->format_flag = static_cast<eGPUTextureFormatFlag>(0); tex->target_base = tex->target = GL_TEXTURE_BUFFER; tex->mipmaps = 0; tex->number = -1; @@ -1173,44 +1117,87 @@ GPUTexture *GPU_texture_create_buffer(eGPUTextureFormat tex_format, const GLuint return tex; } -GPUTexture *GPU_texture_from_bindcode(int textarget, int bindcode) +static GLenum convert_target_to_gl(int dimension, bool is_array) +{ + switch (dimension) { + case 1: + return is_array ? GL_TEXTURE_1D : GL_TEXTURE_1D_ARRAY; + case 2: + return is_array ? GL_TEXTURE_2D : GL_TEXTURE_2D_ARRAY; + case 3: + return GL_TEXTURE_3D; + default: + BLI_assert(0); + return GL_TEXTURE_2D; + } +} + +/* Create an error texture that will bind an invalid texture (pink) at draw time. */ +GPUTexture *GPU_texture_create_error(int dimension, bool is_array) { - GPUTexture *tex = MEM_callocN(sizeof(GPUTexture), "GPUTexture"); - tex->bindcode = bindcode; + GLenum textarget = convert_target_to_gl(dimension, is_array); + + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); + tex->bindcode = 0; tex->refcount = 1; tex->target = textarget; tex->target_base = textarget; tex->samples = 0; - tex->sampler_state = GPU_SAMPLER_REPEAT | GPU_SAMPLER_ANISO; - if (GPU_get_mipmap()) { - tex->sampler_state |= (GPU_SAMPLER_MIPMAP | GPU_SAMPLER_FILTER); - } + tex->sampler_state = GPU_SAMPLER_DEFAULT; tex->number = -1; - if (!glIsTexture(tex->bindcode)) { - GPU_print_error_debug("Blender Texture Not Loaded"); + GPU_print_error_debug("Blender Texture Not Loaded"); + return tex; +} + +/* DDS texture loading. Return NULL if support is not available. */ +GPUTexture *GPU_texture_create_compressed( + int w, int h, int miplen, eGPUTextureFormat tex_format, const void *data) +{ + if (!GLEW_EXT_texture_compression_s3tc) { + return NULL; } - else { - GLint w, h, gl_format; - GLenum gettarget; - gettarget = (textarget == GL_TEXTURE_CUBE_MAP) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : textarget; - - glBindTexture(textarget, tex->bindcode); - glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_WIDTH, &w); - glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_HEIGHT, &h); - glGetTexLevelParameteriv(gettarget, 0, GL_TEXTURE_INTERNAL_FORMAT, &gl_format); - tex->w = w; - tex->h = h; - tex->format = gl_internalformat_to_gpu_format(gl_format); - tex->components = gpu_get_component_count(tex->format); - glBindTexture(textarget, 0); - - /* Depending on how this bindcode was obtained, the memory used here could - * already have been computed. - * But that is not the case currently. */ - gpu_texture_memory_footprint_add(tex); + + GPUTexture *tex = (GPUTexture *)MEM_callocN(sizeof(GPUTexture), __func__); + tex->w = w; + tex->h = h; + tex->refcount = 1; + tex->target = tex->target_base = GL_TEXTURE_2D; + tex->format_flag = static_cast<eGPUTextureFormatFlag>(0); + tex->components = gpu_get_component_count(tex_format); + tex->mipmaps = miplen - 1; + tex->sampler_state = GPU_SAMPLER_DEFAULT; + tex->number = -1; + + GLenum internalformat = gpu_format_to_gl_internalformat(tex_format); + + glGenTextures(1, &tex->bindcode); + glBindTexture(tex->target, tex->bindcode); + + /* Reset to opengl Defaults. (Untested, might not be needed) */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + int blocksize = (ELEM(tex_format, GPU_RGBA8_DXT1, GPU_SRGB8_A8_DXT1)) ? 8 : 16; + + size_t ofs = 0; + for (int mip = 0; mip < miplen && (w || h); mip++, w >>= 1, h >>= 1) { + w = max_ii(1, w); + h = max_ii(1, h); + size_t size = ((w + 3) / 4) * ((h + 3) / 4) * blocksize; + + glCompressedTexImage2D(tex->target, mip, internalformat, w, h, 0, size, (uchar *)data + ofs); + + ofs += size; } + /* Restore Blender default. */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexParameteri(tex->target, GL_TEXTURE_MAX_LEVEL, tex->mipmaps); + glTexParameteri(tex->target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glBindTexture(tex->target, 0); + return tex; } @@ -1462,18 +1449,11 @@ void GPU_texture_update_sub(GPUTexture *tex, BLI_assert((int)tex->format > -1); BLI_assert(tex->components > -1); - const uint bytesize = gpu_get_bytesize(tex->format); GLenum data_format = gpu_get_gl_dataformat(tex->format, &tex->format_flag); GLenum data_type = gpu_get_gl_datatype(gpu_data_format); - GLint alignment; - /* The default pack size for textures is 4, which won't work for byte based textures */ - if (bytesize == 1) { - glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - } + WARN_NOT_BOUND(tex); - glBindTexture(tex->target, tex->bindcode); switch (tex->target) { case GL_TEXTURE_1D: glTexSubImage1D(tex->target, 0, offset_x, width, data_format, data_type, pixels); @@ -1501,12 +1481,6 @@ void GPU_texture_update_sub(GPUTexture *tex, default: BLI_assert(!"tex->target mode not supported"); } - - if (bytesize == 1) { - glPixelStorei(GL_UNPACK_ALIGNMENT, alignment); - } - - glBindTexture(tex->target, 0); } void *GPU_texture_read(GPUTexture *tex, eGPUDataFormat gpu_data_format, int miplvl) @@ -1772,8 +1746,13 @@ void GPU_texture_unbind(GPUTexture *tex) void GPU_texture_unbind_all(void) { if (GLEW_ARB_multi_bind) { - glBindTextures(0, GPU_max_textures(), NULL); - glBindSamplers(0, GPU_max_textures(), NULL); + /* Some drivers crash because of the NULL array even if that's explicitly + * allowed by the spec... *sigh* (see T77549). */ + GLuint texs[32] = {0}; + int count = min_ii(32, GPU_max_textures()); + + glBindTextures(0, count, texs); + glBindSamplers(0, count, texs); return; } @@ -1795,16 +1774,6 @@ void GPU_texture_unbind_all(void) glActiveTexture(GL_TEXTURE0); } -#define WARN_NOT_BOUND(_tex) \ - { \ - if (_tex->number == -1) { \ - fprintf(stderr, "Warning : Trying to set parameter on a texture not bound.\n"); \ - BLI_assert(0); \ - return; \ - } \ - } \ - ((void)0) - void GPU_texture_generate_mipmap(GPUTexture *tex) { WARN_NOT_BOUND(tex); @@ -1948,21 +1917,54 @@ void GPU_texture_mipmap_mode(GPUTexture *tex, bool use_mipmap, bool use_filter) SET_FLAG_FROM_TEST(tex->sampler_state, use_filter, GPU_SAMPLER_FILTER); } +void GPU_texture_anisotropic_filter(GPUTexture *tex, bool use_aniso) +{ + /* Stencil and integer format does not support filtering. */ + BLI_assert(!(use_aniso) || !(GPU_texture_stencil(tex) || GPU_texture_integer(tex))); + + SET_FLAG_FROM_TEST(tex->sampler_state, use_aniso, GPU_SAMPLER_ANISO); +} + void GPU_texture_wrap_mode(GPUTexture *tex, bool use_repeat, bool use_clamp) { SET_FLAG_FROM_TEST(tex->sampler_state, use_repeat, GPU_SAMPLER_REPEAT); SET_FLAG_FROM_TEST(tex->sampler_state, !use_clamp, GPU_SAMPLER_CLAMP_BORDER); } -void GPU_texture_swizzle_channel_auto(GPUTexture *tex, int channels) +static int gpu_texture_swizzle_to_enum(const char swizzle) +{ + switch (swizzle) { + case 'w': + case 'a': + return GL_ALPHA; + case 'z': + case 'b': + return GL_BLUE; + case 'y': + case 'g': + return GL_GREEN; + case '0': + return GL_ZERO; + case '1': + return GL_ONE; + case 'x': + case 'r': + default: + return GL_RED; + } +} + +void GPU_texture_swizzle_set(GPUTexture *tex, const char swizzle[4]) { WARN_NOT_BOUND(tex); + GLint gl_swizzle[4] = {gpu_texture_swizzle_to_enum(swizzle[0]), + gpu_texture_swizzle_to_enum(swizzle[1]), + gpu_texture_swizzle_to_enum(swizzle[2]), + gpu_texture_swizzle_to_enum(swizzle[3])}; + glActiveTexture(GL_TEXTURE0 + tex->number); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_R, GL_RED); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_G, (channels >= 2) ? GL_GREEN : GL_RED); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_B, (channels >= 3) ? GL_BLUE : GL_RED); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_SWIZZLE_A, (channels >= 4) ? GL_ALPHA : GL_ONE); + glTexParameteriv(tex->target_base, GL_TEXTURE_SWIZZLE_RGBA, gl_swizzle); } void GPU_texture_free(GPUTexture *tex) @@ -2137,7 +2139,7 @@ void GPU_samplers_init(void) { glGenSamplers(GPU_SAMPLER_MAX, GG.samplers); for (int i = 0; i < GPU_SAMPLER_MAX; i++) { - eGPUSamplerState state = i; + eGPUSamplerState state = static_cast<eGPUSamplerState>(i); GLenum clamp_type = (state & GPU_SAMPLER_CLAMP_BORDER) ? GL_CLAMP_TO_BORDER : GL_CLAMP_TO_EDGE; GLenum wrap_s = (state & GPU_SAMPLER_REPEAT_S) ? GL_REPEAT : clamp_type; GLenum wrap_t = (state & GPU_SAMPLER_REPEAT_T) ? GL_REPEAT : clamp_type; @@ -2147,8 +2149,9 @@ void GPU_samplers_init(void) ((state & GPU_SAMPLER_MIPMAP) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR) : ((state & GPU_SAMPLER_MIPMAP) ? GL_NEAREST_MIPMAP_LINEAR : GL_NEAREST); GLenum compare_mode = (state & GPU_SAMPLER_COMPARE) ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE; + /* TODO(fclem) Anisotropic level should be a render engine parameter. */ float aniso_filter = ((state & GPU_SAMPLER_MIPMAP) && (state & GPU_SAMPLER_ANISO)) ? - GPU_get_anisotropic() : + U.anisotropic_filter : 1.0f; glSamplerParameteri(GG.samplers[i], GL_TEXTURE_WRAP_S, wrap_s); @@ -2169,11 +2172,23 @@ void GPU_samplers_init(void) * - GL_TEXTURE_LOD_BIAS is 0.0f. **/ } + + /* Custom sampler for icons. */ + glGenSamplers(1, &GG.icon_sampler); + glSamplerParameteri(GG.icon_sampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + glSamplerParameteri(GG.icon_sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(GG.icon_sampler, GL_TEXTURE_LOD_BIAS, -0.5f); +} + +void GPU_sampler_icon_bind(int unit) +{ + glBindSampler(unit, GG.icon_sampler); } void GPU_samplers_free(void) { glDeleteSamplers(GPU_SAMPLER_MAX, GG.samplers); + glDeleteSamplers(1, &GG.icon_sampler); } /** \} */ diff --git a/source/blender/gpu/intern/gpu_uniformbuffer.c b/source/blender/gpu/intern/gpu_uniformbuffer.cc index 130e8fe7da1..b2e31894f9d 100644 --- a/source/blender/gpu/intern/gpu_uniformbuffer.c +++ b/source/blender/gpu/intern/gpu_uniformbuffer.cc @@ -25,6 +25,7 @@ #include <string.h> #include "BLI_blenlib.h" +#include "BLI_math_base.h" #include "gpu_context_private.h" #include "gpu_node_graph.h" @@ -34,217 +35,63 @@ #include "GPU_material.h" #include "GPU_uniformbuffer.h" -typedef enum eGPUUniformBufferFlag { - GPU_UBO_FLAG_INITIALIZED = (1 << 0), - GPU_UBO_FLAG_DIRTY = (1 << 1), -} eGPUUniformBufferFlag; - -typedef enum eGPUUniformBufferType { - GPU_UBO_STATIC = 0, - GPU_UBO_DYNAMIC = 1, -} eGPUUniformBufferType; - -struct GPUUniformBuffer { - int size; /* in bytes */ - GLuint bindcode; /* opengl identifier for UBO */ - int bindpoint; /* current binding point */ - eGPUUniformBufferType type; -}; - -#define GPUUniformBufferStatic GPUUniformBuffer - -typedef struct GPUUniformBufferDynamic { - GPUUniformBuffer buffer; - void *data; /* Continuous memory block to copy to GPU. */ - char flag; -} GPUUniformBufferDynamic; - -/* Prototypes */ -static eGPUType get_padded_gpu_type(struct LinkData *link); -static void gpu_uniformbuffer_inputs_sort(struct ListBase *inputs); - -/* Only support up to this type, if you want to extend it, make sure the - * padding logic is correct for the new types. */ -#define MAX_UBO_GPU_TYPE GPU_MAT4 - -static void gpu_uniformbuffer_initialize(GPUUniformBuffer *ubo, const void *data) -{ - glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); - glBufferData(GL_UNIFORM_BUFFER, ubo->size, data, GL_DYNAMIC_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, 0); -} +typedef struct GPUUniformBuffer { + /** Data size in bytes. */ + int size; + /** GL handle for UBO. */ + GLuint bindcode; + /** Current binding point. */ + int bindpoint; + /** Continuous memory block to copy to GPU. Is own by the GPUUniformBuffer. */ + void *data; +} GPUUniformBuffer; GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_out[256]) { /* Make sure that UBO is padded to size of vec4 */ BLI_assert((size % 16) == 0); - GPUUniformBuffer *ubo = MEM_callocN(sizeof(GPUUniformBufferStatic), "GPUUniformBufferStatic"); - ubo->size = size; - ubo->bindpoint = -1; - - /* Generate Buffer object */ - ubo->bindcode = GPU_buf_alloc(); - - if (!ubo->bindcode) { - if (err_out) { - BLI_strncpy(err_out, "GPUUniformBuffer: UBO create failed", 256); - } - GPU_uniformbuffer_free(ubo); - return NULL; - } - - if (ubo->size > GPU_max_ubo_size()) { + if (size > GPU_max_ubo_size()) { if (err_out) { BLI_strncpy(err_out, "GPUUniformBuffer: UBO too big", 256); } - GPU_uniformbuffer_free(ubo); - return NULL; - } - - gpu_uniformbuffer_initialize(ubo, data); - return ubo; -} - -/** - * Create dynamic UBO from parameters - * Return NULL if failed to create or if \param inputs: is empty. - * - * \param inputs: ListBase of #BLI_genericNodeN(#GPUInput). - */ -GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_out[256]) -{ - /* There is no point on creating an UBO if there is no arguments. */ - if (BLI_listbase_is_empty(inputs)) { return NULL; } - GPUUniformBufferDynamic *ubo = MEM_callocN(sizeof(GPUUniformBufferDynamic), - "GPUUniformBufferDynamic"); - ubo->buffer.type = GPU_UBO_DYNAMIC; - ubo->buffer.bindpoint = -1; - ubo->flag = GPU_UBO_FLAG_DIRTY; - - /* Generate Buffer object. */ - ubo->buffer.bindcode = GPU_buf_alloc(); - - if (!ubo->buffer.bindcode) { - if (err_out) { - BLI_strncpy(err_out, "GPUUniformBuffer: UBO create failed", 256); - } - GPU_uniformbuffer_free(&ubo->buffer); - return NULL; - } - - if (ubo->buffer.size > GPU_max_ubo_size()) { - if (err_out) { - BLI_strncpy(err_out, "GPUUniformBuffer: UBO too big", 256); - } - GPU_uniformbuffer_free(&ubo->buffer); - return NULL; - } - - /* Make sure we comply to the ubo alignment requirements. */ - gpu_uniformbuffer_inputs_sort(inputs); - - LISTBASE_FOREACH (LinkData *, link, inputs) { - const eGPUType gputype = get_padded_gpu_type(link); - ubo->buffer.size += gputype * sizeof(float); - } - - /* Round up to size of vec4 */ - ubo->buffer.size = ((ubo->buffer.size + 15) / 16) * 16; - - /* Allocate the data. */ - ubo->data = MEM_mallocN(ubo->buffer.size, __func__); + GPUUniformBuffer *ubo = (GPUUniformBuffer *)MEM_mallocN(sizeof(GPUUniformBuffer), __func__); + ubo->size = size; + ubo->data = NULL; + ubo->bindcode = 0; + ubo->bindpoint = -1; - /* Now that we know the total ubo size we can start populating it. */ - float *offset = ubo->data; - LISTBASE_FOREACH (LinkData *, link, inputs) { - GPUInput *input = link->data; - memcpy(offset, input->vec, input->type * sizeof(float)); - offset += get_padded_gpu_type(link); + /* Direct init. */ + if (data != NULL) { + GPU_uniformbuffer_update(ubo, data); } - /* Note since we may create the UBOs in the CPU in a different thread than the main drawing one, - * we don't create the UBO in the GPU here. This will happen when we first bind the UBO. - */ - - return &ubo->buffer; -} - -/** - * Free the data - */ -static void gpu_uniformbuffer_dynamic_free(GPUUniformBuffer *ubo_) -{ - BLI_assert(ubo_->type == GPU_UBO_DYNAMIC); - GPUUniformBufferDynamic *ubo = (GPUUniformBufferDynamic *)ubo_; - - ubo->buffer.size = 0; - if (ubo->data) { - MEM_freeN(ubo->data); - } + return ubo; } void GPU_uniformbuffer_free(GPUUniformBuffer *ubo) { - if (ubo->type == GPU_UBO_DYNAMIC) { - gpu_uniformbuffer_dynamic_free(ubo); - } - + MEM_SAFE_FREE(ubo->data); GPU_buf_free(ubo->bindcode); MEM_freeN(ubo); } -static void gpu_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) -{ - glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); - glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo->size, data); - glBindBuffer(GL_UNIFORM_BUFFER, 0); -} - -void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) -{ - BLI_assert(ubo->type == GPU_UBO_STATIC); - gpu_uniformbuffer_update(ubo, data); -} - -/** - * We need to recalculate the internal data, and re-generate it - * from its populated items. - */ -void GPU_uniformbuffer_dynamic_update(GPUUniformBuffer *ubo_) -{ - BLI_assert(ubo_->type == GPU_UBO_DYNAMIC); - GPUUniformBufferDynamic *ubo = (GPUUniformBufferDynamic *)ubo_; - - if (ubo->flag & GPU_UBO_FLAG_INITIALIZED) { - gpu_uniformbuffer_update(ubo_, ubo->data); - } - else { - ubo->flag |= GPU_UBO_FLAG_INITIALIZED; - gpu_uniformbuffer_initialize(ubo_, ubo->data); - } - - ubo->flag &= ~GPU_UBO_FLAG_DIRTY; -} - /** * We need to pad some data types (vec3) on the C side * To match the GPU expected memory block alignment. */ static eGPUType get_padded_gpu_type(LinkData *link) { - GPUInput *input = link->data; + GPUInput *input = (GPUInput *)link->data; eGPUType gputype = input->type; - /* Unless the vec3 is followed by a float we need to treat it as a vec4. */ if (gputype == GPU_VEC3 && (link->next != NULL) && (((GPUInput *)link->next->data)->type != GPU_FLOAT)) { gputype = GPU_VEC4; } - return gputype; } @@ -254,8 +101,9 @@ static eGPUType get_padded_gpu_type(LinkData *link) */ static int inputs_cmp(const void *a, const void *b) { - const LinkData *link_a = a, *link_b = b; - const GPUInput *input_a = link_a->data, *input_b = link_b->data; + const LinkData *link_a = (const LinkData *)a, *link_b = (const LinkData *)b; + const GPUInput *input_a = (const GPUInput *)link_a->data; + const GPUInput *input_b = (const GPUInput *)link_b->data; return input_a->type < input_b->type ? 1 : 0; } @@ -265,15 +113,19 @@ static int inputs_cmp(const void *a, const void *b) */ static void gpu_uniformbuffer_inputs_sort(ListBase *inputs) { +/* Only support up to this type, if you want to extend it, make sure the + * padding logic is correct for the new types. */ +#define MAX_UBO_GPU_TYPE GPU_MAT4 + /* Order them as mat4, vec4, vec3, vec2, float. */ BLI_listbase_sort(inputs, inputs_cmp); /* Creates a lookup table for the different types; */ LinkData *inputs_lookup[MAX_UBO_GPU_TYPE + 1] = {NULL}; - eGPUType cur_type = MAX_UBO_GPU_TYPE + 1; + eGPUType cur_type = static_cast<eGPUType>(MAX_UBO_GPU_TYPE + 1); LISTBASE_FOREACH (LinkData *, link, inputs) { - GPUInput *input = link->data; + GPUInput *input = (GPUInput *)link->data; if (input->type == GPU_MAT3) { /* Alignment for mat3 is not handled currently, so not supported */ @@ -319,6 +171,74 @@ static void gpu_uniformbuffer_inputs_sort(ListBase *inputs) link = link_next; } +#undef MAX_UBO_GPU_TYPE +} + +/** + * Create dynamic UBO from parameters + * Return NULL if failed to create or if \param inputs: is empty. + * + * \param inputs: ListBase of #BLI_genericNodeN(#GPUInput). + */ +GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_out[256]) +{ + /* There is no point on creating an UBO if there is no arguments. */ + if (BLI_listbase_is_empty(inputs)) { + return NULL; + } + /* Make sure we comply to the ubo alignment requirements. */ + gpu_uniformbuffer_inputs_sort(inputs); + + size_t buffer_size = 0; + + LISTBASE_FOREACH (LinkData *, link, inputs) { + const eGPUType gputype = get_padded_gpu_type(link); + buffer_size += gputype * sizeof(float); + } + /* Round up to size of vec4. (Opengl Requirement) */ + size_t alignment = sizeof(float[4]); + buffer_size = divide_ceil_u(buffer_size, alignment) * alignment; + void *data = MEM_mallocN(buffer_size, __func__); + + /* Now that we know the total ubo size we can start populating it. */ + float *offset = (float *)data; + LISTBASE_FOREACH (LinkData *, link, inputs) { + GPUInput *input = (GPUInput *)link->data; + memcpy(offset, input->vec, input->type * sizeof(float)); + offset += get_padded_gpu_type(link); + } + + /* Pass data as NULL for late init. */ + GPUUniformBuffer *ubo = GPU_uniformbuffer_create(buffer_size, NULL, err_out); + /* Data will be update just before binding. */ + ubo->data = data; + return ubo; +} + +static void gpu_uniformbuffer_init(GPUUniformBuffer *ubo) +{ + BLI_assert(ubo->bindcode == 0); + ubo->bindcode = GPU_buf_alloc(); + + if (ubo->bindcode == 0) { + fprintf(stderr, "GPUUniformBuffer: UBO create failed"); + BLI_assert(0); + return; + } + + glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); + glBufferData(GL_UNIFORM_BUFFER, ubo->size, NULL, GL_DYNAMIC_DRAW); +} + +void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) +{ + if (ubo->bindcode == 0) { + gpu_uniformbuffer_init(ubo); + } + + glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); + glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo->size, data); + glBindBuffer(GL_UNIFORM_BUFFER, 0); } void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number) @@ -328,28 +248,30 @@ void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number) return; } - if (ubo->type == GPU_UBO_DYNAMIC) { - GPUUniformBufferDynamic *ubo_dynamic = (GPUUniformBufferDynamic *)ubo; - if (ubo_dynamic->flag & GPU_UBO_FLAG_DIRTY) { - GPU_uniformbuffer_dynamic_update(ubo); - } + if (ubo->bindcode == 0) { + gpu_uniformbuffer_init(ubo); } - if (ubo->bindcode != 0) { - glBindBufferBase(GL_UNIFORM_BUFFER, number, ubo->bindcode); + if (ubo->data != NULL) { + GPU_uniformbuffer_update(ubo, ubo->data); + MEM_SAFE_FREE(ubo->data); } + glBindBufferBase(GL_UNIFORM_BUFFER, number, ubo->bindcode); ubo->bindpoint = number; } void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo) { - ubo->bindpoint = -1; +#ifndef NDEBUG + glBindBufferBase(GL_UNIFORM_BUFFER, ubo->bindpoint, 0); +#endif + ubo->bindpoint = 0; } -int GPU_uniformbuffer_bindpoint(GPUUniformBuffer *ubo) +void GPU_uniformbuffer_unbind_all(void) { - return ubo->bindpoint; -} - -#undef MAX_UBO_GPU_TYPE + for (int i = 0; i < GPU_max_ubo_binds(); i++) { + glBindBufferBase(GL_UNIFORM_BUFFER, i, 0); + } +}
\ No newline at end of file diff --git a/source/blender/gpu/intern/gpu_vertex_buffer.c b/source/blender/gpu/intern/gpu_vertex_buffer.cc index 25daabe601d..eda6d1c7300 100644 --- a/source/blender/gpu/intern/gpu_vertex_buffer.c +++ b/source/blender/gpu/intern/gpu_vertex_buffer.cc @@ -39,17 +39,22 @@ static uint vbo_memory_usage; static GLenum convert_usage_type_to_gl(GPUUsageType type) { - static const GLenum table[] = { - [GPU_USAGE_STREAM] = GL_STREAM_DRAW, - [GPU_USAGE_STATIC] = GL_STATIC_DRAW, - [GPU_USAGE_DYNAMIC] = GL_DYNAMIC_DRAW, - }; - return table[type]; + switch (type) { + case GPU_USAGE_STREAM: + return GL_STREAM_DRAW; + case GPU_USAGE_DYNAMIC: + return GL_DYNAMIC_DRAW; + case GPU_USAGE_STATIC: + return GL_STATIC_DRAW; + default: + BLI_assert(0); + return GL_STATIC_DRAW; + } } GPUVertBuf *GPU_vertbuf_create(GPUUsageType usage) { - GPUVertBuf *verts = MEM_mallocN(sizeof(GPUVertBuf), "GPUVertBuf"); + GPUVertBuf *verts = (GPUVertBuf *)MEM_mallocN(sizeof(GPUVertBuf), "GPUVertBuf"); GPU_vertbuf_init(verts, usage); return verts; } @@ -85,6 +90,35 @@ void GPU_vertbuf_init_with_format_ex(GPUVertBuf *verts, } } +GPUVertBuf *GPU_vertbuf_duplicate(GPUVertBuf *verts) +{ + GPUVertBuf *verts_dst = GPU_vertbuf_create(GPU_USAGE_STATIC); + /* Full copy. */ + *verts_dst = *verts; + GPU_vertformat_copy(&verts_dst->format, &verts->format); + + if (verts->vbo_id) { + uint buffer_sz = GPU_vertbuf_size_get(verts); + + verts_dst->vbo_id = GPU_buf_alloc(); + + glBindBuffer(GL_COPY_READ_BUFFER, verts->vbo_id); + glBindBuffer(GL_COPY_WRITE_BUFFER, verts_dst->vbo_id); + + glBufferData(GL_COPY_WRITE_BUFFER, buffer_sz, NULL, convert_usage_type_to_gl(verts->usage)); + + glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, buffer_sz); +#if VRAM_USAGE + vbo_memory_usage += GPU_vertbuf_size_get(verts); +#endif + } + + if (verts->data) { + verts_dst->data = (uchar *)MEM_dupallocN(verts->data); + } + return verts_dst; +} + /** Same as discard but does not free. */ void GPU_vertbuf_clear(GPUVertBuf *verts) { @@ -132,7 +166,7 @@ void GPU_vertbuf_data_alloc(GPUVertBuf *verts, uint v_len) #endif verts->dirty = true; verts->vertex_len = verts->vertex_alloc = v_len; - verts->data = MEM_mallocN(sizeof(GLubyte) * GPU_vertbuf_size_get(verts), "GPUVertBuf data"); + verts->data = (uchar *)MEM_mallocN(sizeof(GLubyte) * GPU_vertbuf_size_get(verts), __func__); } /* resize buffer keeping existing data */ @@ -149,7 +183,7 @@ void GPU_vertbuf_data_resize(GPUVertBuf *verts, uint v_len) #endif verts->dirty = true; verts->vertex_len = verts->vertex_alloc = v_len; - verts->data = MEM_reallocN(verts->data, sizeof(GLubyte) * GPU_vertbuf_size_get(verts)); + verts->data = (uchar *)MEM_reallocN(verts->data, sizeof(GLubyte) * GPU_vertbuf_size_get(verts)); } /* Set vertex count but does not change allocation. diff --git a/source/blender/gpu/intern/gpu_vertex_format.c b/source/blender/gpu/intern/gpu_vertex_format.cc index b84a7e0f554..a59a6a468ce 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.c +++ b/source/blender/gpu/intern/gpu_vertex_format.cc @@ -63,21 +63,29 @@ void GPU_vertformat_copy(GPUVertFormat *dest, const GPUVertFormat *src) memcpy(dest, src, sizeof(GPUVertFormat)); } -static GLenum convert_comp_type_to_gl(GPUVertCompType type) +GLenum convert_comp_type_to_gl(GPUVertCompType type) { - static const GLenum table[] = { - [GPU_COMP_I8] = GL_BYTE, - [GPU_COMP_U8] = GL_UNSIGNED_BYTE, - [GPU_COMP_I16] = GL_SHORT, - [GPU_COMP_U16] = GL_UNSIGNED_SHORT, - [GPU_COMP_I32] = GL_INT, - [GPU_COMP_U32] = GL_UNSIGNED_INT, - - [GPU_COMP_F32] = GL_FLOAT, - - [GPU_COMP_I10] = GL_INT_2_10_10_10_REV, - }; - return table[type]; + switch (type) { + case GPU_COMP_I8: + return GL_BYTE; + case GPU_COMP_U8: + return GL_UNSIGNED_BYTE; + case GPU_COMP_I16: + return GL_SHORT; + case GPU_COMP_U16: + return GL_UNSIGNED_SHORT; + case GPU_COMP_I32: + return GL_INT; + case GPU_COMP_U32: + return GL_UNSIGNED_INT; + case GPU_COMP_F32: + return GL_FLOAT; + case GPU_COMP_I10: + return GL_INT_2_10_10_10_REV; + default: + BLI_assert(0); + return GL_FLOAT; + } } static uint comp_sz(GPUVertCompType type) @@ -94,7 +102,7 @@ static uint attr_sz(const GPUVertAttr *a) if (a->comp_type == GPU_COMP_I10) { return 4; /* always packed as 10_10_10_2 */ } - return a->comp_len * comp_sz(a->comp_type); + return a->comp_len * comp_sz(static_cast<GPUVertCompType>(a->comp_type)); } static uint attr_align(const GPUVertAttr *a) @@ -102,7 +110,7 @@ static uint attr_align(const GPUVertAttr *a) if (a->comp_type == GPU_COMP_I10) { return 4; /* always packed as 10_10_10_2 */ } - uint c = comp_sz(a->comp_type); + uint c = comp_sz(static_cast<GPUVertCompType>(a->comp_type)); if (a->comp_len == 3 && c <= 2) { return 4 * c; /* AMD HW can't fetch these well, so pad it out (other vendors too?) */ } @@ -185,7 +193,6 @@ uint GPU_vertformat_attr_add(GPUVertFormat *format, attr->names[attr->name_len++] = copy_attr_name(format, name); attr->comp_type = comp_type; - attr->gl_comp_type = convert_comp_type_to_gl(comp_type); attr->comp_len = (comp_type == GPU_COMP_I10) ? 4 : comp_len; /* system needs 10_10_10_2 to be 4 or BGRA */ @@ -262,10 +269,24 @@ int GPU_vertformat_attr_id_get(const GPUVertFormat *format, const char *name) return -1; } +void GPU_vertformat_attr_rename(GPUVertFormat *format, int attr_id, const char *new_name) +{ + BLI_assert(attr_id > -1 && attr_id < format->attr_len); + GPUVertAttr *attr = &format->attrs[attr_id]; + char *attr_name = (char *)GPU_vertformat_attr_name_get(format, attr, 0); + BLI_assert(strlen(attr_name) == strlen(new_name)); + int i = 0; + while (attr_name[i] != '\0') { + attr_name[i] = new_name[i]; + i++; + } + attr->name_len = 1; +} + /* Encode 8 original bytes into 11 safe bytes. */ static void safe_bytes(char out[11], const char data[8]) { - char safe_chars[63] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; + char safe_chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; uint64_t in = *(uint64_t *)data; for (int i = 0; i < 11; i++) { @@ -354,14 +375,6 @@ static void show_pack(uint a_idx, uint sz, uint pad) void VertexFormat_pack(GPUVertFormat *format) { - /* For now, attributes are packed in the order they were added, - * making sure each attribute is naturally aligned (add padding where necessary) - * Later we can implement more efficient packing w/ reordering - * (keep attribute ID order, adjust their offsets to reorder in buffer). */ - - /* TODO: realloc just enough to hold the final combo string. And just enough to - * hold used attributes, not all 16. */ - GPUVertAttr *a0 = &format->attrs[0]; a0->offset = 0; uint offset = a0->sz; @@ -498,7 +511,6 @@ void GPU_vertformat_from_shader(GPUVertFormat *format, const GPUShader *shader) attr->sz = attr->comp_len * 4; attr->fetch_mode = fetch_mode; attr->comp_type = comp_type; - attr->gl_comp_type = convert_comp_type_to_gl(comp_type); attr += 1; } } diff --git a/source/blender/gpu/intern/gpu_vertex_format_private.h b/source/blender/gpu/intern/gpu_vertex_format_private.h index a850d17a1dd..7b560a35bd0 100644 --- a/source/blender/gpu/intern/gpu_vertex_format_private.h +++ b/source/blender/gpu/intern/gpu_vertex_format_private.h @@ -26,8 +26,17 @@ #ifndef __GPU_VERTEX_FORMAT_PRIVATE_H__ #define __GPU_VERTEX_FORMAT_PRIVATE_H__ +#ifdef __cplusplus +extern "C" { +#endif + void VertexFormat_pack(GPUVertFormat *format); uint padding(uint offset, uint alignment); uint vertex_buffer_size(const GPUVertFormat *format, uint vertex_len); +GLenum convert_comp_type_to_gl(GPUVertCompType type); + +#ifdef __cplusplus +} +#endif #endif /* __GPU_VERTEX_FORMAT_PRIVATE_H__ */ diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index 753da8544ea..ed5297f0a4a 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -38,7 +38,6 @@ #include "DNA_vec_types.h" #include "GPU_framebuffer.h" -#include "GPU_glew.h" #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_texture.h" @@ -630,13 +629,13 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo if (settings == S3D_DISPLAY_ANAGLYPH) { switch (stereo_format->anaglyph_type) { case S3D_ANAGLYPH_REDCYAN: - glColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_TRUE); + GPU_color_mask(false, true, true, true); break; case S3D_ANAGLYPH_GREENMAGENTA: - glColorMask(GL_TRUE, GL_FALSE, GL_TRUE, GL_TRUE); + GPU_color_mask(true, false, true, true); break; case S3D_ANAGLYPH_YELLOWBLUE: - glColorMask(GL_FALSE, GL_FALSE, GL_TRUE, GL_TRUE); + GPU_color_mask(false, false, true, true); break; } } @@ -666,7 +665,7 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo GPU_matrix_pop(); if (settings == S3D_DISPLAY_ANAGLYPH) { - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + GPU_color_mask(true, true, true, true); } GPU_framebuffer_restore(); |