diff options
author | Omar Emara <mail@OmarEmara.dev> | 2022-08-10 10:40:07 +0300 |
---|---|---|
committer | Omar Emara <mail@OmarEmara.dev> | 2022-08-10 10:40:07 +0300 |
commit | 365fbb447e0fda9bf4ef1ad04eee1908cefb8e92 (patch) | |
tree | d72e6fdeeaecfe5af4c53c6cece87edd6ce9e0a0 /source | |
parent | 624b0ac656e4876c8518adb479be94e581c46bb8 (diff) |
Realtime Compositor: Add basic output nodes
This patch implements the following nodes for the realtime compositor:
- Composite node.
- Viewer node.
- Split viewer node.
Differential Revision: https://developer.blender.org/D15226
Reviewed By: Clement Foucault
Diffstat (limited to 'source')
11 files changed, 406 insertions, 0 deletions
diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index dcb6788c4d8..c20fff7082e 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -325,6 +325,8 @@ set(GLSL_SRC shaders/compositor/compositor_convert.glsl shaders/compositor/compositor_realize_on_domain.glsl + shaders/compositor/compositor_set_alpha.glsl + shaders/compositor/compositor_split_viewer.glsl shaders/compositor/library/gpu_shader_compositor_main.glsl shaders/compositor/library/gpu_shader_compositor_store_output.glsl @@ -538,6 +540,8 @@ set(SRC_SHADER_CREATE_INFOS shaders/compositor/infos/compositor_convert_info.hh shaders/compositor/infos/compositor_realize_on_domain_info.hh + shaders/compositor/infos/compositor_set_alpha_info.hh + shaders/compositor/infos/compositor_split_viewer_info.hh ) set(SHADER_CREATE_INFOS_CONTENT "") diff --git a/source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl b/source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl new file mode 100644 index 00000000000..7dd40581790 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl @@ -0,0 +1,8 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + vec4 color = vec4(texture_load(image_tx, texel).rgb, texture_load(alpha_tx, texel).x); + imageStore(output_img, texel, color); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl b/source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl new file mode 100644 index 00000000000..866b9045da2 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl @@ -0,0 +1,14 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); +#if defined(SPLIT_HORIZONTAL) + bool condition = (view_size.x * split_ratio) < texel.x; +#elif defined(SPLIT_VERTICAL) + bool condition = (view_size.y * split_ratio) < texel.y; +#endif + vec4 color = condition ? texture_load(first_image_tx, texel) : + texture_load(second_image_tx, texel); + imageStore(output_img, texel, color); +} diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh index 235525b582b..35e60056736 100644 --- a/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh +++ b/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh @@ -61,3 +61,9 @@ GPU_SHADER_CREATE_INFO(compositor_convert_float_to_half_float) .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") .define("CONVERT_EXPRESSION(value)", "vec4(value.r, vec3(0.0))") .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_color_to_opaque) + .additional_info("compositor_convert_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4(value.rgb, 1.0)") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh new file mode 100644 index 00000000000..ca28194e921 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_set_alpha) + .local_group_size(16, 16) + .sampler(0, ImageType::FLOAT_2D, "image_tx") + .sampler(1, ImageType::FLOAT_2D, "alpha_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_set_alpha.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh new file mode 100644 index 00000000000..d5793b0ce59 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_split_viewer_shared) + .local_group_size(16, 16) + .push_constant(Type::FLOAT, "split_ratio") + .push_constant(Type::IVEC2, "view_size") + .sampler(0, ImageType::FLOAT_2D, "first_image_tx") + .sampler(1, ImageType::FLOAT_2D, "second_image_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_split_viewer.glsl"); + +GPU_SHADER_CREATE_INFO(compositor_split_viewer_horizontal) + .additional_info("compositor_split_viewer_shared") + .define("SPLIT_HORIZONTAL") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_split_viewer_vertical) + .additional_info("compositor_split_viewer_shared") + .define("SPLIT_VERTICAL") + .do_static_compilation(true); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 76d8207eead..d3c3b70808d 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1838,6 +1838,12 @@ enum { /* viewer and composite output. */ #define CMP_NODE_OUTPUT_IGNORE_ALPHA 1 +/* Split Viewer Node. Stored in custom2. */ +typedef enum CMPNodeSplitViewerAxis { + CMP_NODE_SPLIT_VIEWER_HORIZONTAL = 0, + CMP_NODE_SPLIT_VIEWER_VERTICAL = 1, +} CMPNodeSplitViewerAxis; + /* Plane track deform node. */ enum { diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt index 6b1522b7634..5034ddc4a30 100644 --- a/source/blender/nodes/composite/CMakeLists.txt +++ b/source/blender/nodes/composite/CMakeLists.txt @@ -10,11 +10,13 @@ set(INC ../../blenlib ../../blentranslation ../../depsgraph + ../../gpu ../../imbuf ../../makesdna ../../makesrna ../../render ../../windowmanager + ../../compositor/realtime_compositor ../../../../intern/guardedalloc # dna_type_offsets.h @@ -120,6 +122,10 @@ set(SRC node_composite_util.hh ) +set(LIB + bf_realtime_compositor +) + if(WITH_IMAGE_OPENEXR) add_definitions(-DWITH_OPENEXR) endif() diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.cc b/source/blender/nodes/composite/nodes/node_composite_composite.cc index d35ce7dc11a..68061bb434d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_composite.cc +++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc @@ -5,9 +5,18 @@ * \ingroup cmpnodes */ +#include "BLI_math_vec_types.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_state.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** COMPOSITE ******************** */ @@ -26,6 +35,125 @@ static void node_composit_buts_composite(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "use_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class CompositeOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + const Result &image = get_input("Image"); + const Result &alpha = get_input("Alpha"); + + if (image.is_single_value() && alpha.is_single_value()) { + execute_clear(); + } + else if (ignore_alpha()) { + execute_ignore_alpha(); + } + else if (!node().input_by_identifier("Alpha")->is_logically_linked()) { + execute_copy(); + } + else { + execute_set_alpha(); + } + } + + /* Executes when all inputs are single values, in which case, the output texture can just be + * cleared to the appropriate color. */ + void execute_clear() + { + const Result &image = get_input("Image"); + const Result &alpha = get_input("Alpha"); + + float4 color = image.get_color_value(); + if (ignore_alpha()) { + color.w = 1.0f; + } + else if (node().input_by_identifier("Alpha")->is_logically_linked()) { + color.w = alpha.get_float_value(); + } + + GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color); + } + + /* Executes when the alpha channel of the image is ignored. */ + void execute_ignore_alpha() + { + GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque"); + GPU_shader_bind(shader); + + const Result &image = get_input("Image"); + image.bind_as_texture(shader, "input_tx"); + + GPUTexture *output_texture = context().get_output_texture(); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(output_texture, image_unit); + + compute_dispatch_threads_at_least(shader, compute_domain().size); + + image.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); + } + + /* Executes when the image texture is written with no adjustments and can thus be copied directly + * to the output texture. */ + void execute_copy() + { + const Result &image = get_input("Image"); + + /* Make sure any prior writes to the texture are reflected before copying it. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + + GPU_texture_copy(context().get_output_texture(), image.texture()); + } + + /* Executes when the alpha channel of the image is set as the value of the input alpha. */ + void execute_set_alpha() + { + GPUShader *shader = shader_manager().get("compositor_set_alpha"); + GPU_shader_bind(shader); + + const Result &image = get_input("Image"); + image.bind_as_texture(shader, "image_tx"); + + const Result &alpha = get_input("Alpha"); + alpha.bind_as_texture(shader, "alpha_tx"); + + GPUTexture *output_texture = context().get_output_texture(); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(output_texture, image_unit); + + compute_dispatch_threads_at_least(shader, compute_domain().size); + + image.unbind_as_texture(); + alpha.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); + } + + /* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the + * alpha channel of the image is retained, but only if the alpha input is not linked. If the + * alpha input is linked, it the value of that input will be used as the alpha of the image. */ + bool ignore_alpha() + { + return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA; + } + + /* The operation domain have the same dimensions of the output without any transformations. */ + Domain compute_domain() override + { + return Domain(context().get_output_size()); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new CompositeOperation(context, node); +} + } // namespace blender::nodes::node_composite_composite_cc void register_node_type_cmp_composite() @@ -37,6 +165,7 @@ void register_node_type_cmp_composite() cmp_node_type_base(&ntype, CMP_NODE_COMPOSITE, "Composite", NODE_CLASS_OUTPUT); ntype.declare = file_ns::cmp_node_composite_declare; ntype.draw_buttons = file_ns::node_composit_buts_composite; + ntype.get_compositor_operation = file_ns::get_compositor_operation; ntype.flag |= NODE_PREVIEW; ntype.no_muting = true; diff --git a/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc index ab325c4559f..085de69e63e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc @@ -11,6 +11,12 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** SPLIT VIEWER ******************** */ @@ -43,6 +49,70 @@ static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C) uiItemR(col, ptr, "factor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class ViewerOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + GPUShader *shader = get_split_viewer_shader(); + GPU_shader_bind(shader); + + const int2 size = compute_domain().size; + + GPU_shader_uniform_1f(shader, "split_ratio", get_split_ratio()); + GPU_shader_uniform_2iv(shader, "view_size", size); + + const Result &first_image = get_input("Image"); + first_image.bind_as_texture(shader, "first_image_tx"); + const Result &second_image = get_input("Image_001"); + second_image.bind_as_texture(shader, "second_image_tx"); + + GPUTexture *output_texture = context().get_output_texture(); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(output_texture, image_unit); + + compute_dispatch_threads_at_least(shader, size); + + first_image.unbind_as_texture(); + second_image.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); + } + + /* The operation domain have the same dimensions of the output without any transformations. */ + Domain compute_domain() override + { + return Domain(context().get_output_size()); + } + + GPUShader *get_split_viewer_shader() + { + if (get_split_axis() == CMP_NODE_SPLIT_VIEWER_HORIZONTAL) { + return shader_manager().get("compositor_split_viewer_horizontal"); + } + + return shader_manager().get("compositor_split_viewer_vertical"); + } + + CMPNodeSplitViewerAxis get_split_axis() + { + return (CMPNodeSplitViewerAxis)bnode().custom2; + } + + float get_split_ratio() + { + return bnode().custom1 / 100.0f; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ViewerOperation(context, node); +} + } // namespace blender::nodes::node_composite_split_viewer_cc void register_node_type_cmp_splitviewer() @@ -57,6 +127,7 @@ void register_node_type_cmp_splitviewer() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_splitviewer); node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; ntype.no_muting = true; diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_viewer.cc index 05f395183b5..4e82b31ca47 100644 --- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "BLI_math_vec_types.hh" + #include "BKE_global.h" #include "BKE_image.h" @@ -13,6 +15,13 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_state.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** VIEWER ******************** */ @@ -55,6 +64,125 @@ static void node_composit_buts_viewer_ex(uiLayout *layout, bContext *UNUSED(C), } } +using namespace blender::realtime_compositor; + +class ViewerOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + const Result &image = get_input("Image"); + const Result &alpha = get_input("Alpha"); + + if (image.is_single_value() && alpha.is_single_value()) { + execute_clear(); + } + else if (ignore_alpha()) { + execute_ignore_alpha(); + } + else if (!node().input_by_identifier("Alpha")->is_logically_linked()) { + execute_copy(); + } + else { + execute_set_alpha(); + } + } + + /* Executes when all inputs are single values, in which case, the output texture can just be + * cleared to the appropriate color. */ + void execute_clear() + { + const Result &image = get_input("Image"); + const Result &alpha = get_input("Alpha"); + + float4 color = image.get_color_value(); + if (ignore_alpha()) { + color.w = 1.0f; + } + else if (node().input_by_identifier("Alpha")->is_logically_linked()) { + color.w = alpha.get_float_value(); + } + + GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color); + } + + /* Executes when the alpha channel of the image is ignored. */ + void execute_ignore_alpha() + { + GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque"); + GPU_shader_bind(shader); + + const Result &image = get_input("Image"); + image.bind_as_texture(shader, "input_tx"); + + GPUTexture *output_texture = context().get_output_texture(); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(output_texture, image_unit); + + compute_dispatch_threads_at_least(shader, compute_domain().size); + + image.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); + } + + /* Executes when the image texture is written with no adjustments and can thus be copied directly + * to the output texture. */ + void execute_copy() + { + const Result &image = get_input("Image"); + + /* Make sure any prior writes to the texture are reflected before copying it. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + + GPU_texture_copy(context().get_output_texture(), image.texture()); + } + + /* Executes when the alpha channel of the image is set as the value of the input alpha. */ + void execute_set_alpha() + { + GPUShader *shader = shader_manager().get("compositor_set_alpha"); + GPU_shader_bind(shader); + + const Result &image = get_input("Image"); + image.bind_as_texture(shader, "image_tx"); + + const Result &alpha = get_input("Alpha"); + alpha.bind_as_texture(shader, "alpha_tx"); + + GPUTexture *output_texture = context().get_output_texture(); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(output_texture, image_unit); + + compute_dispatch_threads_at_least(shader, compute_domain().size); + + image.unbind_as_texture(); + alpha.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); + } + + /* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the + * alpha channel of the image is retained, but only if the alpha input is not linked. If the + * alpha input is linked, it the value of that input will be used as the alpha of the image. */ + bool ignore_alpha() + { + return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA; + } + + /* The operation domain have the same dimensions of the output without any transformations. */ + Domain compute_domain() override + { + return Domain(context().get_output_size()); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ViewerOperation(context, node); +} + } // namespace blender::nodes::node_composite_viewer_cc void register_node_type_cmp_viewer() @@ -70,6 +198,7 @@ void register_node_type_cmp_viewer() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_viewer); node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; ntype.no_muting = true; |