Realtime Compositor: Allow limited compositing region

This patch allows the realtime compositor to be limited to a specific
compositing region that is a subset of the full render region. In the
context of the viewport compositor, when the viewport is in camera view
and has a completely opaque passepartout, the compositing region will be
limited to the visible camera region.

On the user-level, this gives the user the ability to make the result of
the compositor invariant of the aspect ratio, shift, and zoom of the
viewport, making the result in the viewport identical to the final
render compositor assuming size relative operations.

It should be noted that compositing region is the *visible* camera
region, that is, the result of the intersection of the camera region and
the render region. So the user should be careful not to shift or zoom
the view such that the camera border extends outside of the viewport to
have the aforementioned benefits. While we could implement logic to fill
the areas outside of the render region with zeros in some cases, there
are many other ambiguous cases where such a solution wouldn't work,
including the problematic case where the user zooms in very close,
making the camera region much bigger than that of the render region.

Differential Revision: https://developer.blender.org/D16899

Reviewed By: Clement Foucault
This commit is contained in:
Omar Emara 2023-01-06 14:42:00 +02:00
parent 5eab813fc0
commit cc78fd4e93
Notes: blender-bot 2023-08-21 19:32:35 +02:00
Referenced by issue #111344, Compositor: Viewport issues when zoomed in
20 changed files with 345 additions and 97 deletions

View File

@ -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 "")

View File

@ -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;

View File

@ -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;

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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")

View File

@ -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);

View File

@ -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 *>(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();
}

View File

@ -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();
}

View File

@ -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());
}
};

View File

@ -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();
}

View File

@ -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)

View File

@ -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));
}

View File

@ -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()

View File

@ -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());
}
};