Realtime Compositor: Add support for node previews #108904
|
@ -112,6 +112,7 @@ set(GLSL_SRC
|
|||
shaders/compositor_blur_variable_size.glsl
|
||||
shaders/compositor_bokeh_image.glsl
|
||||
shaders/compositor_box_mask.glsl
|
||||
shaders/compositor_compute_preview.glsl
|
||||
shaders/compositor_convert.glsl
|
||||
shaders/compositor_despeckle.glsl
|
||||
shaders/compositor_directional_blur.glsl
|
||||
|
@ -226,6 +227,7 @@ set(SRC_SHADER_CREATE_INFOS
|
|||
shaders/infos/compositor_blur_variable_size_info.hh
|
||||
shaders/infos/compositor_bokeh_image_info.hh
|
||||
shaders/infos/compositor_box_mask_info.hh
|
||||
shaders/infos/compositor_compute_preview_info.hh
|
||||
shaders/infos/compositor_convert_info.hh
|
||||
shaders/infos/compositor_despeckle_info.hh
|
||||
shaders/infos/compositor_directional_blur_info.hh
|
||||
|
|
|
@ -42,6 +42,9 @@ class Context {
|
|||
public:
|
||||
Context(TexturePool &texture_pool);
|
||||
|
||||
/* Get the compositing scene. */
|
||||
virtual const Scene &get_scene() const = 0;
|
||||
|
||||
/* Get the node tree used for compositing. */
|
||||
virtual const bNodeTree &get_node_tree() const = 0;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "COM_context.hh"
|
||||
#include "COM_operation.hh"
|
||||
#include "COM_result.hh"
|
||||
#include "COM_scheduler.hh"
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
@ -44,6 +45,12 @@ class NodeOperation : public Operation {
|
|||
void compute_results_reference_counts(const Schedule &schedule);
|
||||
|
||||
protected:
|
||||
/* Compute a preview for the operation and set to the bNodePreview of the node. This is only done
|
||||
* for nodes which enables previews, are not hidden, and are part of the active node context. The
|
||||
* preview is computed as a lower resolution version of the output of the get_preview_result
|
||||
* method. */
|
||||
void compute_preview() override;
|
||||
|
||||
/* Returns a reference to the derived node that this operation represents. */
|
||||
const DNode &node() const;
|
||||
|
||||
|
@ -53,6 +60,17 @@ class NodeOperation : public Operation {
|
|||
/* Returns true if the output identified by the given identifier is needed and should be
|
||||
* computed, otherwise returns false. */
|
||||
bool should_compute_output(StringRef identifier);
|
||||
|
||||
private:
|
||||
/* Get the result which will be previewed in the node, this is chosen as the first linked output
|
||||
* of the node, if no outputs exist, then the first allocated input will be chosen. Nullptr is
|
||||
* guaranteed not to be returned, since the node will always either have a linked output or an
|
||||
* allocated input. */
|
||||
Result *get_preview_result();
|
||||
|
||||
/* Resize the give input result to the given preview size and set it to the preview buffer after
|
||||
* applying the necessary color management processor.*/
|
||||
void write_preview_from_result(bNodePreview &preview, Result &input_result);
|
||||
};
|
||||
|
||||
} // namespace blender::realtime_compositor
|
||||
|
|
|
@ -130,6 +130,10 @@ class Operation {
|
|||
* output results. */
|
||||
virtual void execute() = 0;
|
||||
|
||||
/* Compute and set a preview of the operation if needed. This method defaults to an empty
|
||||
* implementation and should be implemented by operations which can have previews. */
|
||||
virtual void compute_preview();
|
||||
|
||||
/* Get a reference to the result connected to the input identified by the given identifier. */
|
||||
Result &get_input(StringRef identifier) const;
|
||||
|
||||
|
|
|
@ -4,15 +4,29 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#include "BLI_assert.h"
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_math_base.hh"
|
||||
#include "BLI_math_color.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_task.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "GPU_shader.h"
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "IMB_colormanagement.h"
|
||||
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include "NOD_derived_node_tree.hh"
|
||||
#include "NOD_node_declaration.hh"
|
||||
|
||||
#include "BKE_node.h"
|
||||
|
||||
#include "COM_context.hh"
|
||||
#include "COM_input_descriptor.hh"
|
||||
#include "COM_node_operation.hh"
|
||||
|
@ -39,6 +53,126 @@ NodeOperation::NodeOperation(Context &context, DNode node) : Operation(context),
|
|||
}
|
||||
}
|
||||
|
||||
/* Given the size of a result, compute a lower resolution size for a preview. The greater dimension
|
||||
* will be assigned an arbitrarily chosen size of 128, while the other dimension will get the size
|
||||
* that maintains the same aspect ratio. */
|
||||
static int2 compute_preview_size(int2 size)
|
||||
{
|
||||
const int greater_dimension_size = 128;
|
||||
if (size.x > size.y) {
|
||||
return int2(greater_dimension_size, int(greater_dimension_size * (float(size.y) / size.x)));
|
||||
}
|
||||
else {
|
||||
return int2(int(greater_dimension_size * (float(size.x) / size.y)), greater_dimension_size);
|
||||
}
|
||||
}
|
||||
|
||||
void NodeOperation::compute_preview()
|
||||
{
|
||||
if (!(node()->flag & NODE_PREVIEW)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node()->flag & NODE_HIDDEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Only compute previews for nodes in the active context. */
|
||||
if (node().context()->instance_key().value !=
|
||||
node().context()->derived_tree().active_context().instance_key().value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Initialize node tree previews if not already initialized. */
|
||||
bNodeTree *root_tree = const_cast<bNodeTree *>(
|
||||
&node().context()->derived_tree().root_context().btree());
|
||||
if (!root_tree->previews) {
|
||||
root_tree->previews = BKE_node_instance_hash_new("node previews");
|
||||
}
|
||||
|
||||
Result *preview_result = get_preview_result();
|
||||
const int2 preview_size = compute_preview_size(preview_result->domain().size);
|
||||
node()->runtime->preview_xsize = preview_size.x;
|
||||
node()->runtime->preview_ysize = preview_size.y;
|
||||
|
||||
bNodePreview *preview = bke::node_preview_verify(
|
||||
root_tree->previews, node().instance_key(), preview_size.x, preview_size.y, true);
|
||||
|
||||
write_preview_from_result(*preview, *preview_result);
|
||||
}
|
||||
|
||||
Result *NodeOperation::get_preview_result()
|
||||
{
|
||||
/* Find the first linked output. */
|
||||
for (const bNodeSocket *output : node()->output_sockets()) {
|
||||
Result &output_result = get_result(output->identifier);
|
||||
if (output_result.should_compute()) {
|
||||
return &output_result;
|
||||
}
|
||||
}
|
||||
|
||||
/* No linked outputs, find the first allocated input. */
|
||||
for (const bNodeSocket *input : node()->input_sockets()) {
|
||||
Result &input_result = get_input(input->identifier);
|
||||
if (input_result.is_allocated()) {
|
||||
return &input_result;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NodeOperation::write_preview_from_result(bNodePreview &preview, Result &input_result)
|
||||
{
|
||||
GPUShader *shader = shader_manager().get("compositor_compute_preview");
|
||||
GPU_shader_bind(shader);
|
||||
|
||||
if (input_result.type() == ResultType::Float) {
|
||||
GPU_texture_swizzle_set(input_result.texture(), "rrr1");
|
||||
}
|
||||
|
||||
input_result.bind_as_texture(shader, "input_tx");
|
||||
|
||||
const int2 preview_size = int2(preview.xsize, preview.ysize);
|
||||
Result preview_result = Result::Temporary(ResultType::Color, texture_pool());
|
||||
preview_result.allocate_texture(Domain(preview_size));
|
||||
preview_result.bind_as_image(shader, "preview_img");
|
||||
|
||||
compute_dispatch_threads_at_least(shader, preview_size);
|
||||
|
||||
input_result.unbind_as_texture();
|
||||
preview_result.unbind_as_image();
|
||||
GPU_shader_unbind();
|
||||
|
||||
GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH);
|
||||
float *preview_pixels = static_cast<float *>(
|
||||
GPU_texture_read(preview_result.texture(), GPU_DATA_FLOAT, 0));
|
||||
preview_result.release();
|
||||
|
||||
ColormanageProcessor *color_processor = IMB_colormanagement_display_processor_new(
|
||||
&context().get_scene().view_settings, &context().get_scene().display_settings);
|
||||
|
||||
threading::parallel_for(IndexRange(preview_size.y), 1, [&](const IndexRange sub_y_range) {
|
||||
for (const int64_t y : sub_y_range) {
|
||||
for (const int64_t x : IndexRange(preview_size.x)) {
|
||||
const int index = (y * preview_size.x + x) * 4;
|
||||
IMB_colormanagement_processor_apply_v4(color_processor, preview_pixels + index);
|
||||
rgba_float_to_uchar(preview.rect + index, preview_pixels + index);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Restore original swizzle mask set above. */
|
||||
if (input_result.type() == ResultType::Float) {
|
||||
GPU_texture_swizzle_set(input_result.texture(), "rgba");
|
||||
}
|
||||
|
||||
IMB_colormanagement_processor_free(color_processor);
|
||||
MEM_freeN(preview_pixels);
|
||||
}
|
||||
|
||||
void NodeOperation::compute_results_reference_counts(const Schedule &schedule)
|
||||
{
|
||||
for (const bNodeSocket *output : this->node()->output_sockets()) {
|
||||
|
|
|
@ -35,6 +35,8 @@ void Operation::evaluate()
|
|||
|
||||
execute();
|
||||
|
||||
compute_preview();
|
||||
|
||||
release_inputs();
|
||||
|
||||
release_unneeded_results();
|
||||
|
@ -136,6 +138,8 @@ void Operation::add_and_evaluate_input_processor(StringRef identifier, SimpleOpe
|
|||
processor->evaluate();
|
||||
}
|
||||
|
||||
void Operation::compute_preview(){};
|
||||
|
||||
Result &Operation::get_input(StringRef identifier) const
|
||||
{
|
||||
return *results_mapped_to_inputs_.lookup(identifier);
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
void main()
|
||||
{
|
||||
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
|
||||
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(imageSize(preview_img));
|
||||
imageStore(preview_img, texel, texture(input_tx, coordinates));
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/* SPDX-FileCopyrightText: 2023 Blender Foundation
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
GPU_SHADER_CREATE_INFO(compositor_compute_preview)
|
||||
.local_group_size(16, 16)
|
||||
.sampler(0, ImageType::FLOAT_2D, "input_tx")
|
||||
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "preview_img")
|
||||
.compute_source("compositor_compute_preview.glsl")
|
||||
.do_static_compilation(true);
|
|
@ -58,6 +58,11 @@ class Context : public realtime_compositor::Context {
|
|||
{
|
||||
}
|
||||
|
||||
const Scene &get_scene() const override
|
||||
{
|
||||
return *DRW_context_state_get()->scene;
|
||||
}
|
||||
|
||||
const bNodeTree &get_node_tree() const override
|
||||
{
|
||||
return *DRW_context_state_get()->scene->nodetree;
|
||||
|
|
|
@ -99,6 +99,11 @@ class Context : public realtime_compositor::Context {
|
|||
GPU_TEXTURE_FREE_SAFE(viewer_output_texture_);
|
||||
}
|
||||
|
||||
const Scene &get_scene() const override
|
||||
{
|
||||
return scene_;
|
||||
}
|
||||
|
||||
const bNodeTree &get_node_tree() const override
|
||||
{
|
||||
return node_tree_;
|
||||
|
|
Loading…
Reference in New Issue