diff --git a/source/blender/compositor/realtime_compositor/CMakeLists.txt b/source/blender/compositor/realtime_compositor/CMakeLists.txt index 5826c70793c..f9739972690 100644 --- a/source/blender/compositor/realtime_compositor/CMakeLists.txt +++ b/source/blender/compositor/realtime_compositor/CMakeLists.txt @@ -116,15 +116,16 @@ set(GLSL_SRC shaders/compositor_normalize.glsl shaders/compositor_parallel_reduction.glsl shaders/compositor_projector_lens_distortion.glsl + shaders/compositor_read_pass.glsl shaders/compositor_realize_on_domain.glsl shaders/compositor_screen_lens_distortion.glsl - shaders/compositor_set_alpha.glsl shaders/compositor_split_viewer.glsl shaders/compositor_symmetric_blur.glsl shaders/compositor_symmetric_blur_variable_size.glsl shaders/compositor_symmetric_separable_blur.glsl shaders/compositor_tone_map_photoreceptor.glsl shaders/compositor_tone_map_simple.glsl + shaders/compositor_write_output.glsl shaders/library/gpu_shader_compositor_alpha_over.glsl shaders/library/gpu_shader_compositor_blur_common.glsl @@ -204,15 +205,16 @@ set(SRC_SHADER_CREATE_INFOS shaders/infos/compositor_normalize_info.hh shaders/infos/compositor_parallel_reduction_info.hh shaders/infos/compositor_projector_lens_distortion_info.hh + shaders/infos/compositor_read_pass_info.hh shaders/infos/compositor_realize_on_domain_info.hh shaders/infos/compositor_screen_lens_distortion_info.hh - shaders/infos/compositor_set_alpha_info.hh shaders/infos/compositor_split_viewer_info.hh shaders/infos/compositor_symmetric_blur_info.hh shaders/infos/compositor_symmetric_blur_variable_size_info.hh shaders/infos/compositor_symmetric_separable_blur_info.hh shaders/infos/compositor_tone_map_photoreceptor_info.hh shaders/infos/compositor_tone_map_simple_info.hh + shaders/infos/compositor_write_output_info.hh ) set(SHADER_CREATE_INFOS_CONTENT "") diff --git a/source/blender/compositor/realtime_compositor/COM_context.hh b/source/blender/compositor/realtime_compositor/COM_context.hh index 80fb4f70ca4..7be89bfeb0e 100644 --- a/source/blender/compositor/realtime_compositor/COM_context.hh +++ b/source/blender/compositor/realtime_compositor/COM_context.hh @@ -6,6 +6,7 @@ #include "BLI_string_ref.hh" #include "DNA_scene_types.h" +#include "DNA_vec_types.h" #include "GPU_texture.h" @@ -41,8 +42,17 @@ class Context { /* Get the active compositing scene. */ virtual const Scene *get_scene() const = 0; - /* Get the dimensions of the output. */ - virtual int2 get_output_size() = 0; + /* Get the width and height of the render passes and of the output texture returned by the + * get_input_texture and get_output_texture methods respectively. */ + virtual int2 get_render_size() const = 0; + + /* Get the rectangular region representing the area of the input that the compositor will operate + * on. Conversely, the compositor will only update the region of the output that corresponds to + * the compositing region. In the base case, the compositing region covers the entirety of the + * render region with a lower bound of zero and an upper bound of the render size returned by the + * get_render_size method. In other cases, the compositing region might be a subset of the render + * region. */ + virtual rcti get_compositing_region() const = 0; /* Get the texture representing the output where the result of the compositor should be * written. This should be called by output nodes to get their target texture. */ @@ -60,6 +70,9 @@ class Context { * appropriate place, which can be directly in the UI or just logged to the output stream. */ virtual void set_info_message(StringRef message) const = 0; + /* Get the size of the compositing region. See get_compositing_region(). */ + int2 get_compositing_region_size() const; + /* Get the current frame number of the active scene. */ int get_frame_number() const; diff --git a/source/blender/compositor/realtime_compositor/intern/context.cc b/source/blender/compositor/realtime_compositor/intern/context.cc index 0b123a2c271..a53c839c365 100644 --- a/source/blender/compositor/realtime_compositor/intern/context.cc +++ b/source/blender/compositor/realtime_compositor/intern/context.cc @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_rect.h" + +#include "DNA_vec_types.h" + #include "COM_context.hh" #include "COM_static_cache_manager.hh" #include "COM_static_shader_manager.hh" @@ -11,6 +15,12 @@ Context::Context(TexturePool &texture_pool) : texture_pool_(texture_pool) { } +int2 Context::get_compositing_region_size() const +{ + const rcti compositing_region = get_compositing_region(); + return int2(BLI_rcti_size_x(&compositing_region), BLI_rcti_size_y(&compositing_region)); +} + int Context::get_frame_number() const { return get_scene()->r.cfra; diff --git a/source/blender/compositor/realtime_compositor/shaders/compositor_read_pass.glsl b/source/blender/compositor/realtime_compositor/shaders/compositor_read_pass.glsl new file mode 100644 index 00000000000..ff8b33af655 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/shaders/compositor_read_pass.glsl @@ -0,0 +1,8 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + vec4 pass_color = texture_load(input_tx, texel + compositing_region_lower_bound); + imageStore(output_img, texel, READ_EXPRESSION(pass_color)); +} diff --git a/source/blender/compositor/realtime_compositor/shaders/compositor_set_alpha.glsl b/source/blender/compositor/realtime_compositor/shaders/compositor_set_alpha.glsl deleted file mode 100644 index 7dd40581790..00000000000 --- a/source/blender/compositor/realtime_compositor/shaders/compositor_set_alpha.glsl +++ /dev/null @@ -1,8 +0,0 @@ -#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/compositor/realtime_compositor/shaders/compositor_split_viewer.glsl b/source/blender/compositor/realtime_compositor/shaders/compositor_split_viewer.glsl index 866b9045da2..4ed378e9ec4 100644 --- a/source/blender/compositor/realtime_compositor/shaders/compositor_split_viewer.glsl +++ b/source/blender/compositor/realtime_compositor/shaders/compositor_split_viewer.glsl @@ -10,5 +10,5 @@ void main() #endif vec4 color = condition ? texture_load(first_image_tx, texel) : texture_load(second_image_tx, texel); - imageStore(output_img, texel, color); + imageStore(output_img, texel + compositing_region_lower_bound, color); } diff --git a/source/blender/compositor/realtime_compositor/shaders/compositor_write_output.glsl b/source/blender/compositor/realtime_compositor/shaders/compositor_write_output.glsl new file mode 100644 index 00000000000..d22e2a9ee67 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/shaders/compositor_write_output.glsl @@ -0,0 +1,18 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + vec4 input_color = texture_load(input_tx, texel); + +#if defined(DIRECT_OUTPUT) + vec4 output_color = input_color; +#elif defined(OPAQUE_OUTPUT) + vec4 output_color = vec4(input_color.rgb, 1.0); +#elif defined(ALPHA_OUTPUT) + float alpha = texture_load(alpha_tx, texel).x; + vec4 output_color = vec4(input_color.rgb, alpha); +#endif + + imageStore(output_img, texel + compositing_region_lower_bound, output_color); +} diff --git a/source/blender/compositor/realtime_compositor/shaders/infos/compositor_convert_info.hh b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_convert_info.hh index 35e60056736..235525b582b 100644 --- a/source/blender/compositor/realtime_compositor/shaders/infos/compositor_convert_info.hh +++ b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_convert_info.hh @@ -61,9 +61,3 @@ 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/compositor/realtime_compositor/shaders/infos/compositor_read_pass_info.hh b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_read_pass_info.hh new file mode 100644 index 00000000000..e3e2d43f6f8 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_read_pass_info.hh @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_read_pass_shared) + .local_group_size(16, 16) + .push_constant(Type::IVEC2, "compositing_region_lower_bound") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .compute_source("compositor_read_pass.glsl"); + +GPU_SHADER_CREATE_INFO(compositor_read_pass) + .additional_info("compositor_read_pass_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("READ_EXPRESSION(pass_color)", "pass_color") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_read_pass_alpha) + .additional_info("compositor_read_pass_shared") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("READ_EXPRESSION(pass_color)", "vec4(pass_color.a, vec3(0.0))") + .do_static_compilation(true); diff --git a/source/blender/compositor/realtime_compositor/shaders/infos/compositor_set_alpha_info.hh b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_set_alpha_info.hh deleted file mode 100644 index ca28194e921..00000000000 --- a/source/blender/compositor/realtime_compositor/shaders/infos/compositor_set_alpha_info.hh +++ /dev/null @@ -1,11 +0,0 @@ -/* 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/compositor/realtime_compositor/shaders/infos/compositor_split_viewer_info.hh b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_split_viewer_info.hh index d5793b0ce59..b7a8b7657aa 100644 --- a/source/blender/compositor/realtime_compositor/shaders/infos/compositor_split_viewer_info.hh +++ b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_split_viewer_info.hh @@ -6,6 +6,7 @@ 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") + .push_constant(Type::IVEC2, "compositing_region_lower_bound") .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") diff --git a/source/blender/compositor/realtime_compositor/shaders/infos/compositor_write_output_info.hh b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_write_output_info.hh new file mode 100644 index 00000000000..c530ecc7741 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/shaders/infos/compositor_write_output_info.hh @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_write_output_shared) + .local_group_size(16, 16) + .push_constant(Type::IVEC2, "compositing_region_lower_bound") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_write_output.glsl"); + +GPU_SHADER_CREATE_INFO(compositor_write_output) + .additional_info("compositor_write_output_shared") + .define("DIRECT_OUTPUT") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_write_output_opaque) + .additional_info("compositor_write_output_shared") + .define("OPAQUE_OUTPUT") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_write_output_alpha) + .additional_info("compositor_write_output_shared") + .sampler(1, ImageType::FLOAT_2D, "alpha_tx") + .define("ALPHA_OUTPUT") + .do_static_compilation(true); diff --git a/source/blender/draw/engines/compositor/compositor_engine.cc b/source/blender/draw/engines/compositor/compositor_engine.cc index 03ef0be81b4..06e599e77b1 100644 --- a/source/blender/draw/engines/compositor/compositor_engine.cc +++ b/source/blender/draw/engines/compositor/compositor_engine.cc @@ -2,16 +2,23 @@ #include "BLI_listbase.h" #include "BLI_math_vec_types.hh" +#include "BLI_rect.h" #include "BLI_string_ref.hh" #include "BLI_utildefines.h" #include "BLT_translation.h" #include "DNA_ID_enums.h" +#include "DNA_camera_types.h" +#include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_vec_types.h" +#include "DNA_view3d_types.h" #include "DEG_depsgraph_query.h" +#include "ED_view3d.h" + #include "DRW_render.h" #include "IMB_colormanagement.h" @@ -50,11 +57,68 @@ class Context : public realtime_compositor::Context { return DRW_context_state_get()->scene; } - int2 get_output_size() override + int2 get_render_size() const override { return int2(float2(DRW_viewport_size_get())); } + /* Returns true if the viewport is in camera view and has an opaque passepartout, that is, the + * area outside of the camera border is not visible. */ + bool is_opaque_camera_view() const + { + /* Check if the viewport is in camera view. */ + if (DRW_context_state_get()->rv3d->persp != RV3D_CAMOB) { + return false; + } + + /* Check if the camera object that is currently in view is an actual camera. It is possible for + * a non camera object to be used as a camera, in which case, there will be no passepartout or + * any other camera setting, so those pseudo cameras can be ignored. */ + Object *camera_object = DRW_context_state_get()->v3d->camera; + if (camera_object->type != OB_CAMERA) { + return false; + } + + /* Check if the camera has passepartout active and is totally opaque. */ + Camera *cam = static_cast(camera_object->data); + if (!(cam->flag & CAM_SHOWPASSEPARTOUT) || cam->passepartalpha != 1.0f) { + return false; + } + + return true; + } + + rcti get_compositing_region() const override + { + const int2 viewport_size = int2(float2(DRW_viewport_size_get())); + const rcti render_region = rcti{0, viewport_size.x, 0, viewport_size.y}; + + /* If the camera view is not opaque, that means the content outside of the camera region is + * visible to some extent, so it would make sense to include them in the compositing region. + * Otherwise, we limit the compositing region to the visible camera region because anything + * outside of the camera region will not be visible anyways. */ + if (!is_opaque_camera_view()) { + return render_region; + } + + rctf camera_border; + ED_view3d_calc_camera_border(DRW_context_state_get()->scene, + DRW_context_state_get()->depsgraph, + DRW_context_state_get()->region, + DRW_context_state_get()->v3d, + DRW_context_state_get()->rv3d, + &camera_border, + false); + + rcti camera_region; + BLI_rcti_rctf_copy_floor(&camera_region, &camera_border); + + rcti visible_camera_region; + BLI_rcti_isect(&render_region, &camera_region, &visible_camera_region); + + return visible_camera_region; + } + GPUTexture *get_output_texture() override { return DRW_viewport_texture_list_get()->color; @@ -83,36 +147,36 @@ class Engine { TexturePool texture_pool_; Context context_; realtime_compositor::Evaluator evaluator_; - /* Stores the viewport size at the time the last compositor evaluation happened. See the - * update_viewport_size method for more information. */ - int2 last_viewport_size_; + /* Stores the compositing region size at the time the last compositor evaluation happened. See + * the update_compositing_region_size method for more information. */ + int2 last_compositing_region_size_; public: Engine(char *info_message) : context_(texture_pool_, info_message), evaluator_(context_), - last_viewport_size_(context_.get_output_size()) + last_compositing_region_size_(context_.get_compositing_region_size()) { } - /* Update the viewport size and evaluate the compositor. */ + /* Update the compositing region size and evaluate the compositor. */ void draw() { - update_viewport_size(); + update_compositing_region_size(); evaluator_.evaluate(); } - /* If the size of the viewport changed from the last time the compositor was evaluated, update - * the viewport size and reset the evaluator. That's because the evaluator compiles the node tree - * in a manner that is specifically optimized for the size of the viewport. This should be called - * before evaluating the compositor. */ - void update_viewport_size() + /* If the size of the compositing region changed from the last time the compositor was evaluated, + * update the last compositor region size and reset the evaluator. That's because the evaluator + * compiles the node tree in a manner that is specifically optimized for the size of the + * compositing region. This should be called before evaluating the compositor. */ + void update_compositing_region_size() { - if (last_viewport_size_ == context_.get_output_size()) { + if (last_compositing_region_size_ == context_.get_compositing_region_size()) { return; } - last_viewport_size_ = context_.get_output_size(); + last_compositing_region_size_ = context_.get_compositing_region_size(); evaluator_.reset(); } diff --git a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc index 17b5d64de91..1ff2b82f7bb 100644 --- a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc @@ -100,7 +100,7 @@ class BoxMaskOperation : public NodeOperation { Domain compute_domain() override { if (get_input("Mask").is_single_value()) { - return Domain(context().get_output_size()); + return Domain(context().get_compositing_region_size()); } return get_input("Mask").domain(); } diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.cc b/source/blender/nodes/composite/nodes/node_composite_composite.cc index 24fe4e2d986..a7babeb28bd 100644 --- a/source/blender/nodes/composite/nodes/node_composite_composite.cc +++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc @@ -81,9 +81,15 @@ class CompositeOperation : public NodeOperation { /* Executes when the alpha channel of the image is ignored. */ void execute_ignore_alpha() { - GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque"); + GPUShader *shader = shader_manager().get("compositor_write_output_opaque"); GPU_shader_bind(shader); + /* The compositing space might be limited to a subset of the output texture, so only write into + * that compositing region. */ + const rcti compositing_region = context().get_compositing_region(); + const int2 lower_bound = int2(compositing_region.xmin, compositing_region.ymin); + GPU_shader_uniform_2iv(shader, "compositing_region_lower_bound", lower_bound); + const Result &image = get_input("Image"); image.bind_as_texture(shader, "input_tx"); @@ -91,7 +97,8 @@ class CompositeOperation : public NodeOperation { 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); + const int2 compositing_region_size = context().get_compositing_region_size(); + compute_dispatch_threads_at_least(shader, compositing_region_size); image.unbind_as_texture(); GPU_texture_image_unbind(output_texture); @@ -102,22 +109,44 @@ class CompositeOperation : public NodeOperation { * to the output texture. */ void execute_copy() { + GPUShader *shader = shader_manager().get("compositor_write_output"); + GPU_shader_bind(shader); + + /* The compositing space might be limited to a subset of the output texture, so only write into + * that compositing region. */ + const rcti compositing_region = context().get_compositing_region(); + const int2 lower_bound = int2(compositing_region.xmin, compositing_region.ymin); + GPU_shader_uniform_2iv(shader, "compositing_region_lower_bound", lower_bound); + const Result &image = get_input("Image"); + image.bind_as_texture(shader, "input_tx"); - /* Make sure any prior writes to the texture are reflected before copying it. */ - GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + 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); - GPU_texture_copy(context().get_output_texture(), image.texture()); + const int2 compositing_region_size = context().get_compositing_region_size(); + compute_dispatch_threads_at_least(shader, compositing_region_size); + + image.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); } /* 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"); + GPUShader *shader = shader_manager().get("compositor_write_output_alpha"); GPU_shader_bind(shader); + /* The compositing space might be limited to a subset of the output texture, so only write into + * that compositing region. */ + const rcti compositing_region = context().get_compositing_region(); + const int2 lower_bound = int2(compositing_region.xmin, compositing_region.ymin); + GPU_shader_uniform_2iv(shader, "compositing_region_lower_bound", lower_bound); + const Result &image = get_input("Image"); - image.bind_as_texture(shader, "image_tx"); + image.bind_as_texture(shader, "input_tx"); const Result &alpha = get_input("Alpha"); alpha.bind_as_texture(shader, "alpha_tx"); @@ -126,7 +155,8 @@ class CompositeOperation : public NodeOperation { 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); + const int2 compositing_region_size = context().get_compositing_region_size(); + compute_dispatch_threads_at_least(shader, compositing_region_size); image.unbind_as_texture(); alpha.unbind_as_texture(); @@ -142,10 +172,11 @@ class CompositeOperation : public NodeOperation { return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA; } - /* The operation domain have the same dimensions of the output without any transformations. */ + /* The operation domain has the same size as the compositing region without any transformations + * applied. */ Domain compute_domain() override { - return Domain(context().get_output_size()); + return Domain(context().get_compositing_region_size()); } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc index f06b64f10b5..2b222eb17b1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc @@ -98,7 +98,7 @@ class EllipseMaskOperation : public NodeOperation { Domain compute_domain() override { if (get_input("Mask").is_single_value()) { - return Domain(context().get_output_size()); + return Domain(context().get_compositing_region_size()); } return get_input("Mask").domain(); } diff --git a/source/blender/nodes/composite/nodes/node_composite_image.cc b/source/blender/nodes/composite/nodes/node_composite_image.cc index e743c003701..98cd5834551 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.cc +++ b/source/blender/nodes/composite/nodes/node_composite_image.cc @@ -9,6 +9,7 @@ #include "BLI_linklist.h" #include "BLI_math_vec_types.hh" +#include "BLI_rect.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -23,6 +24,7 @@ #include "DEG_depsgraph_query.h" #include "DNA_scene_types.h" +#include "DNA_vec_types.h" #include "RE_engine.h" #include "RE_pipeline.h" @@ -825,30 +827,9 @@ class RenderLayerOperation : public NodeOperation { { const int view_layer = bnode().custom1; GPUTexture *pass_texture = context().get_input_texture(view_layer, SCE_PASS_COMBINED); - const int2 size = int2(GPU_texture_width(pass_texture), GPU_texture_height(pass_texture)); - /* Compute image output. */ - Result &image_result = get_result("Image"); - image_result.allocate_texture(Domain(size)); - GPU_texture_copy(image_result.texture(), pass_texture); - - /* Compute alpha output. */ - Result &alpha_result = get_result("Alpha"); - alpha_result.allocate_texture(Domain(size)); - - GPUShader *shader = shader_manager().get("compositor_extract_alpha_from_color"); - GPU_shader_bind(shader); - - const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx"); - GPU_texture_bind(pass_texture, input_unit); - - alpha_result.bind_as_image(shader, "output_img"); - - compute_dispatch_threads_at_least(shader, size); - - GPU_shader_unbind(); - GPU_texture_unbind(pass_texture); - alpha_result.unbind_as_image(); + execute_image(pass_texture); + execute_alpha(pass_texture); /* Other output passes are not supported for now, so allocate them as invalid. */ for (const bNodeSocket *output : this->node()->output_sockets()) { @@ -861,6 +842,66 @@ class RenderLayerOperation : public NodeOperation { } } } + + void execute_image(GPUTexture *pass_texture) + { + Result &image_result = get_result("Image"); + if (!image_result.should_compute()) { + return; + } + + GPUShader *shader = shader_manager().get("compositor_read_pass"); + GPU_shader_bind(shader); + + /* The compositing space might be limited to a subset of the pass texture, so only read that + * compositing region into an appropriately sized texture. */ + const rcti compositing_region = context().get_compositing_region(); + const int2 lower_bound = int2(compositing_region.xmin, compositing_region.ymin); + GPU_shader_uniform_2iv(shader, "compositing_region_lower_bound", lower_bound); + + const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(pass_texture, input_unit); + + const int2 compositing_region_size = context().get_compositing_region_size(); + image_result.allocate_texture(Domain(compositing_region_size)); + image_result.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, compositing_region_size); + + GPU_shader_unbind(); + GPU_texture_unbind(pass_texture); + image_result.unbind_as_image(); + } + + void execute_alpha(GPUTexture *pass_texture) + { + Result &alpha_result = get_result("Alpha"); + if (!alpha_result.should_compute()) { + return; + } + + GPUShader *shader = shader_manager().get("compositor_read_pass_alpha"); + GPU_shader_bind(shader); + + /* The compositing space might be limited to a subset of the pass texture, so only read that + * compositing region into an appropriately sized texture. */ + const rcti compositing_region = context().get_compositing_region(); + const int2 lower_bound = int2(compositing_region.xmin, compositing_region.ymin); + GPU_shader_uniform_2iv(shader, "compositing_region_lower_bound", lower_bound); + + const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(pass_texture, input_unit); + + const int2 compositing_region_size = context().get_compositing_region_size(); + alpha_result.allocate_texture(Domain(compositing_region_size)); + alpha_result.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, compositing_region_size); + + GPU_shader_unbind(); + GPU_texture_unbind(pass_texture); + alpha_result.unbind_as_image(); + } }; static NodeOperation *get_compositor_operation(Context &context, DNode node) diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.cc b/source/blender/nodes/composite/nodes/node_composite_scale.cc index a4e28326d9d..a9c35e985a5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_scale.cc +++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc @@ -149,7 +149,7 @@ class ScaleOperation : public NodeOperation { float2 get_scale_render_size_stretch() { const float2 input_size = float2(get_input("Image").domain().size); - const float2 render_size = float2(context().get_output_size()); + const float2 render_size = float2(context().get_compositing_region_size()); return render_size / input_size; } @@ -160,7 +160,7 @@ class ScaleOperation : public NodeOperation { float2 get_scale_render_size_fit() { const float2 input_size = float2(get_input("Image").domain().size); - const float2 render_size = float2(context().get_output_size()); + const float2 render_size = float2(context().get_compositing_region_size()); const float2 scale = render_size / input_size; return float2(math::min(scale.x, scale.y)); } @@ -172,7 +172,7 @@ class ScaleOperation : public NodeOperation { float2 get_scale_render_size_crop() { const float2 input_size = float2(get_input("Image").domain().size); - const float2 render_size = float2(context().get_output_size()); + const float2 render_size = float2(context().get_compositing_region_size()); const float2 scale = render_size / input_size; return float2(math::max(scale.x, scale.y)); } 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 b4e698f5096..73dcf42904c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc @@ -60,10 +60,16 @@ class ViewerOperation : public NodeOperation { GPUShader *shader = get_split_viewer_shader(); GPU_shader_bind(shader); - const int2 size = compute_domain().size; + /* The compositing space might be limited to a subset of the output texture, so only write into + * that compositing region. */ + const rcti compositing_region = context().get_compositing_region(); + const int2 lower_bound = int2(compositing_region.xmin, compositing_region.ymin); + GPU_shader_uniform_2iv(shader, "compositing_region_lower_bound", lower_bound); GPU_shader_uniform_1f(shader, "split_ratio", get_split_ratio()); - GPU_shader_uniform_2iv(shader, "view_size", size); + + const int2 compositing_region_size = context().get_compositing_region_size(); + GPU_shader_uniform_2iv(shader, "view_size", compositing_region_size); const Result &first_image = get_input("Image"); first_image.bind_as_texture(shader, "first_image_tx"); @@ -74,7 +80,7 @@ class ViewerOperation : public NodeOperation { 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); + compute_dispatch_threads_at_least(shader, compositing_region_size); first_image.unbind_as_texture(); second_image.unbind_as_texture(); @@ -82,10 +88,11 @@ class ViewerOperation : public NodeOperation { GPU_shader_unbind(); } - /* The operation domain have the same dimensions of the output without any transformations. */ + /* The operation domain has the same size as the compositing region without any transformations + * applied. */ Domain compute_domain() override { - return Domain(context().get_output_size()); + return Domain(context().get_compositing_region_size()); } GPUShader *get_split_viewer_shader() diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_viewer.cc index 682b0626ccd..dca2f7a1cda 100644 --- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc @@ -110,9 +110,15 @@ class ViewerOperation : public NodeOperation { /* Executes when the alpha channel of the image is ignored. */ void execute_ignore_alpha() { - GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque"); + GPUShader *shader = shader_manager().get("compositor_write_output_opaque"); GPU_shader_bind(shader); + /* The compositing space might be limited to a smaller region of the output texture, so only + * write into that compositing region. */ + const rcti compositing_region = context().get_compositing_region(); + const int2 lower_bound = int2(compositing_region.xmin, compositing_region.ymin); + GPU_shader_uniform_2iv(shader, "compositing_region_lower_bound", lower_bound); + const Result &image = get_input("Image"); image.bind_as_texture(shader, "input_tx"); @@ -120,7 +126,8 @@ class ViewerOperation : public NodeOperation { 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); + const int2 compositing_region_size = context().get_compositing_region_size(); + compute_dispatch_threads_at_least(shader, compositing_region_size); image.unbind_as_texture(); GPU_texture_image_unbind(output_texture); @@ -131,22 +138,44 @@ class ViewerOperation : public NodeOperation { * to the output texture. */ void execute_copy() { + GPUShader *shader = shader_manager().get("compositor_write_output"); + GPU_shader_bind(shader); + + /* The compositing space might be limited to a smaller region of the output texture, so only + * write into that compositing region. */ + const rcti compositing_region = context().get_compositing_region(); + const int2 lower_bound = int2(compositing_region.xmin, compositing_region.ymin); + GPU_shader_uniform_2iv(shader, "compositing_region_lower_bound", lower_bound); + const Result &image = get_input("Image"); + image.bind_as_texture(shader, "input_tx"); - /* Make sure any prior writes to the texture are reflected before copying it. */ - GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + 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); - GPU_texture_copy(context().get_output_texture(), image.texture()); + const int2 compositing_region_size = context().get_compositing_region_size(); + compute_dispatch_threads_at_least(shader, compositing_region_size); + + image.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); } /* 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"); + GPUShader *shader = shader_manager().get("compositor_write_output_alpha"); GPU_shader_bind(shader); + /* The compositing space might be limited to a smaller region of the output texture, so only + * write into that compositing region. */ + const rcti compositing_region = context().get_compositing_region(); + const int2 lower_bound = int2(compositing_region.xmin, compositing_region.ymin); + GPU_shader_uniform_2iv(shader, "compositing_region_lower_bound", lower_bound); + const Result &image = get_input("Image"); - image.bind_as_texture(shader, "image_tx"); + image.bind_as_texture(shader, "input_tx"); const Result &alpha = get_input("Alpha"); alpha.bind_as_texture(shader, "alpha_tx"); @@ -155,7 +184,8 @@ class ViewerOperation : public NodeOperation { 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); + const int2 compositing_region_size = context().get_compositing_region_size(); + compute_dispatch_threads_at_least(shader, compositing_region_size); image.unbind_as_texture(); alpha.unbind_as_texture(); @@ -171,10 +201,11 @@ class ViewerOperation : public NodeOperation { return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA; } - /* The operation domain have the same dimensions of the output without any transformations. */ + /* The operation domain has the same size as the compositing region without any transformations + * applied. */ Domain compute_domain() override { - return Domain(context().get_output_size()); + return Domain(context().get_compositing_region_size()); } };