Compositor: Add experimental option for new CPU compositor

This patch introduces a new experimental option for the new CPU
compositor under development. This is to make development easier such
that it happens directly in main, but the compositor is not expected to
work and will probably crash.

Pull Request: https://projects.blender.org/blender/blender/pulls/125960
This commit is contained in:
Omar Emara
2024-08-08 15:40:06 +02:00
committed by Omar Emara
parent d74d8ceb23
commit f40cf759c7
14 changed files with 126 additions and 50 deletions

View File

@@ -848,6 +848,27 @@ class NODE_PT_quality(bpy.types.Panel):
col = layout.column()
col.prop(tree, "use_viewer_border")
class NODE_PT_compositor_debug(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Options"
bl_label = "Debug"
bl_parent_id = "NODE_PT_quality"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
render_data = context.scene.render
if render_data.compositor_device != "CPU":
return False
preferences = context.preferences
return preferences.view.show_developer_ui and preferences.experimental.enable_new_cpu_compositor
def draw(self, context):
render_data = context.scene.render
self.layout.prop(render_data, "use_new_cpu_compositor", text="Experimental CPU Implementation")
class NODE_PT_overlay(Panel):
bl_space_type = 'NODE_EDITOR'
@@ -1072,6 +1093,7 @@ classes = (
NODE_PT_active_tool,
NODE_PT_backdrop,
NODE_PT_quality,
NODE_PT_compositor_debug,
NODE_PT_annotation,
NODE_PT_overlay,
NODE_PT_active_node_properties,

View File

@@ -2860,6 +2860,7 @@ class USERPREF_PT_experimental_prototypes(ExperimentalPanel, Panel):
({"property": "use_sculpt_texture_paint"}, ("blender/blender/issues/96225", "#96225")),
({"property": "enable_overlay_next"}, ("blender/blender/issues/102179", "#102179")),
({"property": "use_animation_baklava"}, ("/blender/blender/issues/120406", "#120406")),
({"property": "enable_new_cpu_compositor"}, ("/blender/blender/issues/125968", "#125968")),
),
)

View File

@@ -6,6 +6,8 @@
#include "BLT_translation.hh"
#include "DNA_userdef_types.h"
#include "BKE_node.hh"
#include "BKE_node_runtime.hh"
#include "BKE_scene.hh"
@@ -77,8 +79,10 @@ void COM_execute(Render *render,
compositor_init_node_previews(render_data, node_tree);
compositor_reset_node_tree_status(node_tree);
if (scene->r.compositor_device == SCE_COMPOSITOR_DEVICE_GPU) {
/* GPU compositor. */
if (scene->r.compositor_device == SCE_COMPOSITOR_DEVICE_GPU ||
(USER_EXPERIMENTAL_TEST(&U, enable_new_cpu_compositor) && !scene->r.use_old_cpu_compositor))
{
/* Realtime compositor. */
RE_compositor_execute(
*render, *scene, *render_data, *node_tree, view_name, render_context, profiler);
}

View File

@@ -51,6 +51,9 @@ class Context {
/* Get the node tree used for compositing. */
virtual const bNodeTree &get_node_tree() const = 0;
/* True if the compositor should use GPU acceleration. */
virtual bool use_gpu() const = 0;
/* True if the compositor should write file outputs, false otherwise. */
virtual bool use_file_output() const = 0;

View File

@@ -182,7 +182,12 @@ void Result::allocate_texture(Domain domain)
}
is_single_value_ = false;
texture_ = context_->texture_pool().acquire(domain.size, get_texture_format());
if (context_->use_gpu()) {
texture_ = context_->texture_pool().acquire(domain.size, get_texture_format());
}
else {
/* TODO: Host side allocation. */
}
domain_ = domain;
}
@@ -191,7 +196,12 @@ void Result::allocate_single_value()
is_single_value_ = true;
/* Single values are stored in 1x1 textures as well as the single value members. */
const int2 texture_size{1, 1};
texture_ = context_->texture_pool().acquire(texture_size, get_texture_format());
if (context_->use_gpu()) {
texture_ = context_->texture_pool().acquire(texture_size, get_texture_format());
}
else {
/* TODO: Host side allocation. */
}
domain_ = Domain::identity();
}

View File

@@ -69,6 +69,11 @@ class Context : public realtime_compositor::Context {
return *DRW_context_state_get()->scene->nodetree;
}
bool use_gpu() const override
{
return true;
}
bool use_file_output() const override
{
return false;

View File

@@ -256,8 +256,8 @@ static void compo_initjob(void *cjv)
compo_tag_output_nodes(cj->localtree, cj->recalc_flags);
}
cj->re = RE_NewInteractiveCompositorRender(scene);
if (scene->r.compositor_device == SCE_COMPOSITOR_DEVICE_GPU) {
cj->re = RE_NewInteractiveCompositorRender(scene);
RE_system_gpu_context_ensure(cj->re);
}
}

View File

@@ -831,6 +831,11 @@ typedef struct RenderData {
/** Precision used by the GPU execution of the compositor tree. */
int compositor_precision; /* eCompositorPrecision */
/* If false and the experimental enable_new_cpu_compositor is true, use the new experimental
* CPU compositor implementation, otherwise, use the old CPU compositor. */
char use_old_cpu_compositor;
char _pad10[7];
} RenderData;
/** #RenderData::quality_flag */

View File

@@ -757,7 +757,8 @@ typedef struct UserDef_Experimental {
char use_shader_node_previews;
char use_animation_baklava;
char use_docking;
char _pad[2];
char enable_new_cpu_compositor;
char _pad[1];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;

View File

@@ -7537,6 +7537,12 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
prop, "Compositor Precision", "The precision of compositor intermediate result");
RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_Scene_compositor_update");
prop = RNA_def_property(srna, "use_new_cpu_compositor", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, nullptr, "use_old_cpu_compositor", 1);
RNA_def_property_ui_text(
prop, "Use New CPU Compositor", "Use the new CPU compositor implementation");
RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_Scene_compositor_update");
/* Nestled Data. */
/* *** Non-Animated *** */
RNA_define_animate_sdna(false);

View File

@@ -7428,6 +7428,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Overlay Next", "Enable the new Overlay codebase, requires restart");
prop = RNA_def_property(srna, "enable_new_cpu_compositor", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "enable_new_cpu_compositor", 1);
RNA_def_property_ui_text(prop, "CPU Compositor", "Enable the new CPU compositor");
prop = RNA_def_property(srna, "use_all_linked_data_direct", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_ui_text(
prop,

View File

@@ -193,6 +193,11 @@ class Context : public realtime_compositor::Context {
return *input_data_.node_tree;
}
bool use_gpu() const override
{
return this->get_render_data().compositor_device == SCE_COMPOSITOR_DEVICE_GPU;
}
bool use_file_output() const override
{
return this->render_context() != nullptr;
@@ -587,45 +592,52 @@ class RealtimeCompositor {
~RealtimeCompositor()
{
/* Free resources with GPU context enabled. Cleanup may happen from the
* main thread, and we must use the main context there. */
if (BLI_thread_is_main()) {
DRW_gpu_context_enable();
}
else {
DRW_render_context_enable(&render_);
const bool use_gpu = context_->use_gpu();
if (use_gpu) {
/* Free resources with GPU context enabled. Cleanup may happen from the
* main thread, and we must use the main context there. */
if (BLI_thread_is_main()) {
DRW_gpu_context_enable();
}
else {
DRW_render_context_enable(&render_);
}
}
context_.reset();
texture_pool_.reset();
if (BLI_thread_is_main()) {
DRW_gpu_context_disable();
}
else {
DRW_render_context_disable(&render_);
if (use_gpu) {
if (BLI_thread_is_main()) {
DRW_gpu_context_disable();
}
else {
DRW_render_context_disable(&render_);
}
}
}
/* Evaluate the compositor and output to the scene render result. */
void execute(const ContextInputData &input_data)
{
/* For main thread rendering in background mode, blocking rendering, or when we do not have a
* render system GPU context, use the DRW context directly, while for threaded rendering when
* we have a render system GPU context, use the render's system GPU context to avoid blocking
* with the global DST. */
void *re_system_gpu_context = RE_system_gpu_context_get(&render_);
if (BLI_thread_is_main() || re_system_gpu_context == nullptr) {
DRW_gpu_context_enable();
}
else {
if (context_->use_gpu()) {
/* For main thread rendering in background mode, blocking rendering, or when we do not have a
* render system GPU context, use the DRW context directly, while for threaded rendering when
* we have a render system GPU context, use the render's system GPU context to avoid blocking
* with the global DST. */
void *re_system_gpu_context = RE_system_gpu_context_get(&render_);
WM_system_gpu_context_activate(re_system_gpu_context);
if (BLI_thread_is_main() || re_system_gpu_context == nullptr) {
DRW_gpu_context_enable();
}
else {
void *re_system_gpu_context = RE_system_gpu_context_get(&render_);
WM_system_gpu_context_activate(re_system_gpu_context);
void *re_blender_gpu_context = RE_blender_gpu_context_ensure(&render_);
void *re_blender_gpu_context = RE_blender_gpu_context_ensure(&render_);
GPU_render_begin();
GPU_context_active_set(static_cast<GPUContext *>(re_blender_gpu_context));
GPU_render_begin();
GPU_context_active_set(static_cast<GPUContext *>(re_blender_gpu_context));
}
}
context_->update_input_data(input_data);
@@ -641,14 +653,17 @@ class RealtimeCompositor {
context_->viewer_output_to_viewer_image();
texture_pool_->free_unused_and_reset();
if (BLI_thread_is_main() || re_system_gpu_context == nullptr) {
DRW_gpu_context_disable();
}
else {
GPU_render_end();
GPU_context_active_set(nullptr);
if (context_->use_gpu()) {
void *re_system_gpu_context = RE_system_gpu_context_get(&render_);
WM_system_gpu_context_release(re_system_gpu_context);
if (BLI_thread_is_main() || re_system_gpu_context == nullptr) {
DRW_gpu_context_disable();
}
else {
GPU_render_end();
GPU_context_active_set(nullptr);
void *re_system_gpu_context = RE_system_gpu_context_get(&render_);
WM_system_gpu_context_release(re_system_gpu_context);
}
}
}
};
@@ -662,25 +677,25 @@ void Render::compositor_execute(const Scene &scene,
blender::realtime_compositor::RenderContext *render_context,
blender::realtime_compositor::Profiler *profiler)
{
std::unique_lock lock(gpu_compositor_mutex);
std::unique_lock lock(this->compositor_mutex);
blender::render::ContextInputData input_data(
scene, render_data, node_tree, view_name, render_context, profiler);
if (gpu_compositor == nullptr) {
gpu_compositor = new blender::render::RealtimeCompositor(*this, input_data);
if (this->compositor == nullptr) {
this->compositor = new blender::render::RealtimeCompositor(*this, input_data);
}
gpu_compositor->execute(input_data);
this->compositor->execute(input_data);
}
void Render::compositor_free()
{
std::unique_lock lock(gpu_compositor_mutex);
std::unique_lock lock(this->compositor_mutex);
if (gpu_compositor != nullptr) {
delete gpu_compositor;
gpu_compositor = nullptr;
if (this->compositor != nullptr) {
delete this->compositor;
this->compositor = nullptr;
}
}

View File

@@ -650,7 +650,7 @@ void RE_FreeAllPersistentData()
static void re_gpu_texture_caches_free(Render *re)
{
/* Free persistent compositor that may be using these textures. */
if (re->gpu_compositor) {
if (re->compositor) {
RE_compositor_free(*re);
}

View File

@@ -211,11 +211,11 @@ struct Render : public BaseRender {
struct Depsgraph *pipeline_depsgraph = nullptr;
Scene *pipeline_scene_eval = nullptr;
/* Realtime GPU Compositor.
/* Realtime Compositor.
* NOTE: Use bare pointer instead of smart pointer because the RealtimeCompositor is a fully
* opaque type. */
blender::render::RealtimeCompositor *gpu_compositor = nullptr;
std::mutex gpu_compositor_mutex;
blender::render::RealtimeCompositor *compositor = nullptr;
std::mutex compositor_mutex;
/* Callbacks for the corresponding base class method implementation. */
void (*display_init_cb)(void *handle, RenderResult *rr) = nullptr;