Compositor: Remove Texture node

This patch removes the Texture node from the compositor, which was based
on the legacy Internal Textures system in Blender. The main motivation
for removing this node is as follows:

- Procedural texturing nodes that previously existed in shading and
  geometry nodes are now supported in the compositor, which cover 95% of
  what is previously possible using and even adds new possibilities like
  Gabor, Bricks, and various improvements to existing texture types.
- The old texture system did not support GPU evaluation, so it was
  always computed and cached on the CPU, which causes bad performance
  especially for interactive use in the viewport compositor. While the
  new nodes are fully GPU accelerated and do not require any caching.
- The Texture node didn't support Texture nodes, so it was not fully
  supported and we so far had a warning about that.
- The general direction in Blender is to remove the old texture system,
  and the compositor was one of the last main users of it. 5.0 is thus
  the ideal time to remove such use.
- The Texture node was always and still is a source of bugs, since it
  relies on proper tagging for cache invalidation and updates, which is
  so far not perfect. It also suffers from UI/UX issues, since it needs
  to be adjusted from the properties panel, which can break if there are
  other texture nodes in the context.

This is a breaking change and no versioning was attempted since:

1. It is impossible to get the same results as before due to the use of
different random number generators, so any versioning would just give us
the general look.
2. The Texture node supports a lot of possible configurations. For
instance, each general texture can have many options for the basis type,
and each basis type might have multiple options. So versioning all of
that will take a lot of time, code, and effort.

Pull Request: https://projects.blender.org/blender/blender/pulls/140545
This commit is contained in:
Omar Emara
2025-06-24 11:54:39 +02:00
committed by Omar Emara
parent e63a20fee1
commit 383c8860a2
21 changed files with 9 additions and 428 deletions

View File

@@ -27,7 +27,6 @@ class NODE_MT_category_compositor_input(Menu):
node_add_menu.add_node_type(layout, "CompositorNodeImageCoordinates")
node_add_menu.add_node_type(layout, "CompositorNodeMask")
node_add_menu.add_node_type(layout, "CompositorNodeMovieClip")
node_add_menu.add_node_type(layout, "CompositorNodeTexture")
if is_group:
layout.separator()

View File

@@ -169,7 +169,7 @@
#define CMP_NODE_R_LAYERS 221
#define CMP_NODE_COMPOSITE 222
#define CMP_NODE_OUTPUT_FILE 223
#define CMP_NODE_TEXTURE 224
#define CMP_NODE_TEXTURE_DEPRECATED 224
#define CMP_NODE_TRANSLATE 225
#define CMP_NODE_ZCOMBINE 226
#define CMP_NODE_COMBRGBA_LEGACY 227

View File

@@ -673,7 +673,7 @@ static const char *node_get_static_idname(int type, int treetype)
return "CompositorNodeComposite";
case CMP_NODE_OUTPUT_FILE:
return "CompositorNodeOutputFile";
case CMP_NODE_TEXTURE:
case CMP_NODE_TEXTURE_DEPRECATED:
return "CompositorNodeTexture";
case CMP_NODE_TRANSLATE:
return "CompositorNodeTranslate";

View File

@@ -104,7 +104,6 @@ set(SRC
cached_resources/intern/cached_image.cc
cached_resources/intern/cached_mask.cc
cached_resources/intern/cached_shader.cc
cached_resources/intern/cached_texture.cc
cached_resources/intern/deriche_gaussian_coefficients.cc
cached_resources/intern/distortion_grid.cc
cached_resources/intern/fog_glow_kernel.cc
@@ -122,7 +121,6 @@ set(SRC
cached_resources/COM_cached_mask.hh
cached_resources/COM_cached_resource.hh
cached_resources/COM_cached_shader.hh
cached_resources/COM_cached_texture.hh
cached_resources/COM_deriche_gaussian_coefficients.hh
cached_resources/COM_distortion_grid.hh
cached_resources/COM_fog_glow_kernel.hh

View File

@@ -8,7 +8,6 @@
#include "COM_cached_image.hh"
#include "COM_cached_mask.hh"
#include "COM_cached_shader.hh"
#include "COM_cached_texture.hh"
#include "COM_deriche_gaussian_coefficients.hh"
#include "COM_distortion_grid.hh"
#include "COM_fog_glow_kernel.hh"
@@ -52,7 +51,6 @@ class StaticCacheManager {
SymmetricBlurWeightsContainer symmetric_blur_weights;
SymmetricSeparableBlurWeightsContainer symmetric_separable_blur_weights;
MorphologicalDistanceFeatherWeightsContainer morphological_distance_feather_weights;
CachedTextureContainer cached_textures;
CachedMaskContainer cached_masks;
SMAAPrecomputedTexturesContainer smaa_precomputed_textures;
OCIOColorSpaceConversionShaderContainer ocio_color_space_conversion_shaders;

View File

@@ -1,90 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <cstdint>
#include <memory>
#include <string>
#include "BLI_map.hh"
#include "BLI_math_vector_types.hh"
#include "DNA_texture_types.h"
#include "COM_cached_resource.hh"
#include "COM_result.hh"
namespace blender::compositor {
class Context;
/* ------------------------------------------------------------------------------------------------
* Cached Texture Key.
*/
class CachedTextureKey {
public:
int2 size;
float3 offset;
float3 scale;
CachedTextureKey(int2 size, float3 offset, float3 scale);
uint64_t hash() const;
};
bool operator==(const CachedTextureKey &a, const CachedTextureKey &b);
/* -------------------------------------------------------------------------------------------------
* Cached Texture.
*
* A cached resource that computes and caches a GPU texture containing the result of evaluating the
* given texture ID on a space that spans the given size, parameterized by the given parameters. */
class CachedTexture : public CachedResource {
private:
Array<float4> color_pixels_;
Array<float> value_pixels_;
public:
Result color_result;
Result value_result;
CachedTexture(Context &context,
Tex *texture,
bool use_color_management,
int2 size,
float3 offset,
float3 scale);
~CachedTexture();
};
/* ------------------------------------------------------------------------------------------------
* Cached Texture Container.
*/
class CachedTextureContainer : CachedResourceContainer {
private:
Map<std::string, Map<CachedTextureKey, std::unique_ptr<CachedTexture>>> map_;
/* A map that stores the update counts of the textures at the moment they were cached. */
Map<std::string, uint64_t> update_counts_;
public:
void reset() override;
/* Check if the given texture ID has changed since the last time it was retrieved through its
* recalculate flag, and if so, invalidate its corresponding cached textures and reset the
* recalculate flag to ready it to track the next change. Then, check if there is an available
* CachedTexture cached resource with the given parameters in the container, if one exists,
* return it, otherwise, return a newly created one and add it to the container. In both cases,
* tag the cached resource as needed to keep it cached for the next evaluation. */
CachedTexture &get(Context &context,
Tex *texture,
bool use_color_management,
int2 size,
float3 offset,
float3 scale);
};
} // namespace blender::compositor

View File

@@ -1,171 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include <cstdint>
#include <memory>
#include "BLI_array.hh"
#include "BLI_hash.hh"
#include "BLI_index_range.hh"
#include "BLI_math_vector.h"
#include "BLI_math_vector_types.hh"
#include "BLI_task.hh"
#include "GPU_texture.hh"
#include "BKE_image.hh"
#include "BKE_texture.h"
#include "DNA_ID.h"
#include "DNA_texture_types.h"
#include "RE_texture.h"
#include "COM_cached_texture.hh"
#include "COM_context.hh"
#include "COM_result.hh"
namespace blender::compositor {
/* --------------------------------------------------------------------
* Cached Texture Key.
*/
CachedTextureKey::CachedTextureKey(int2 size, float3 offset, float3 scale)
: size(size), offset(offset), scale(scale)
{
}
uint64_t CachedTextureKey::hash() const
{
return get_default_hash(size, offset, scale);
}
bool operator==(const CachedTextureKey &a, const CachedTextureKey &b)
{
return a.size == b.size && a.offset == b.offset && a.scale == b.scale;
}
/* --------------------------------------------------------------------
* Cached Texture.
*/
CachedTexture::CachedTexture(Context &context,
Tex *texture,
bool use_color_management,
int2 size,
float3 offset,
float3 scale)
: color_result(context.create_result(ResultType::Color)),
value_result(context.create_result(ResultType::Float))
{
ImagePool *image_pool = BKE_image_pool_new();
BKE_texture_fetch_images_for_pool(texture, image_pool);
color_pixels_ = Array<float4>(size.x * size.y);
value_pixels_ = Array<float>(size.x * size.y);
threading::parallel_for(IndexRange(size.y), 1, [&](const IndexRange sub_y_range) {
for (const int64_t y : sub_y_range) {
for (const int64_t x : IndexRange(size.x)) {
/* Compute the coordinates in the [-1, 1] range and add 0.5 to evaluate the texture at the
* center of pixels in case it was interpolated. */
const float2 pixel_coordinates = ((float2(x, y) + 0.5f) / float2(size)) * 2.0f - 1.0f;
/* Note that it is expected that the offset is scaled by the scale. */
const float3 coordinates = (float3(pixel_coordinates, 0.0f) + offset) * scale;
TexResult texture_result;
const int result_type = multitex_ext_safe(
texture, coordinates, &texture_result, image_pool, use_color_management, false);
float4 color = float4(texture_result.trgba);
color.w = texture_result.talpha ? color.w : texture_result.tin;
if (!(result_type & TEX_RGB)) {
copy_v3_fl(color, color.w);
}
color_pixels_[y * size.x + x] = color;
value_pixels_[y * size.x + x] = color.w;
}
}
});
BKE_image_pool_free(image_pool);
if (context.use_gpu()) {
this->color_result.allocate_texture(Domain(size), false);
this->value_result.allocate_texture(Domain(size), false);
GPU_texture_update(this->color_result, GPU_DATA_FLOAT, color_pixels_.data());
GPU_texture_update(this->value_result, GPU_DATA_FLOAT, value_pixels_.data());
/* CPU-side data no longer needed, so free it. */
color_pixels_ = Array<float4>();
value_pixels_ = Array<float>();
}
else {
this->color_result.wrap_external(&color_pixels_.data()[0].x, size);
this->value_result.wrap_external(value_pixels_.data(), size);
}
}
CachedTexture::~CachedTexture()
{
this->color_result.release();
this->value_result.release();
}
/* --------------------------------------------------------------------
* Cached Texture Container.
*/
void CachedTextureContainer::reset()
{
/* First, delete all cached textures that are no longer needed. */
for (auto &cached_textures_for_id : map_.values()) {
cached_textures_for_id.remove_if([](auto item) { return !item.value->needed; });
}
map_.remove_if([](auto item) { return item.value.is_empty(); });
update_counts_.remove_if([&](auto item) { return !map_.contains(item.key); });
/* Second, reset the needed status of the remaining cached textures to false to ready them to
* track their needed status for the next evaluation. */
for (auto &cached_textures_for_id : map_.values()) {
for (auto &value : cached_textures_for_id.values()) {
value->needed = false;
}
}
}
CachedTexture &CachedTextureContainer::get(Context &context,
Tex *texture,
bool use_color_management,
int2 size,
float3 offset,
float3 scale)
{
const CachedTextureKey key(size, offset, scale);
const std::string library_key = texture->id.lib ? texture->id.lib->id.name : "";
const std::string id_key = std::string(texture->id.name) + library_key;
auto &cached_textures_for_id = map_.lookup_or_add_default(id_key);
/* Invalidate the cache for that texture if it was changed since it was cached. */
if (!cached_textures_for_id.is_empty() &&
texture->runtime.last_update != update_counts_.lookup(id_key))
{
cached_textures_for_id.clear();
}
auto &cached_texture = *cached_textures_for_id.lookup_or_add_cb(key, [&]() {
return std::make_unique<CachedTexture>(
context, texture, use_color_management, size, offset, scale);
});
/* Store the current update count to later compare to and check if the texture changed. */
update_counts_.add_overwrite(id_key, texture->runtime.last_update);
cached_texture.needed = true;
return cached_texture;
}
} // namespace blender::compositor

View File

@@ -16,7 +16,6 @@ void StaticCacheManager::reset()
symmetric_blur_weights.reset();
symmetric_separable_blur_weights.reset();
morphological_distance_feather_weights.reset();
cached_textures.reset();
cached_masks.reset();
smaa_precomputed_textures.reset();
ocio_color_space_conversion_shaders.reset();

View File

@@ -153,13 +153,7 @@ static void buttons_texture_users_find_nodetree(ListBase *users,
{
if (ntree) {
for (bNode *node : ntree->all_nodes()) {
if (node->type_legacy == CMP_NODE_TEXTURE) {
PointerRNA ptr = RNA_pointer_create_discrete(&ntree->id, &RNA_Node, node);
PropertyRNA *prop = RNA_struct_find_property(&ptr, "texture");
buttons_texture_user_node_add(
users, id, ntree, node, ptr, prop, category, RNA_struct_ui_icon(ptr.type), node->name);
}
else if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
if (node->typeinfo->nclass == NODE_CLASS_TEXTURE) {
PointerRNA ptr = RNA_pointer_create_discrete(&ntree->id, &RNA_Node, node);
buttons_texture_user_node_add(users,
id,

View File

@@ -175,7 +175,6 @@ static void node_buts_texture(uiLayout *layout, bContext *C, PointerRNA *ptr)
bNode *node = (bNode *)ptr->data;
short multi = (node->id && ((Tex *)node->id)->use_nodes &&
(node->type_legacy != CMP_NODE_TEXTURE) &&
(node->type_legacy != TEX_NODE_TEXTURE));
uiTemplateID(layout, C, ptr, "texture", "texture.new", nullptr, nullptr);
@@ -632,9 +631,6 @@ static void node_composit_set_butfunc(blender::bke::bNodeType *ntype)
case CMP_NODE_TIME:
ntype->draw_buttons = node_buts_time;
break;
case CMP_NODE_TEXTURE:
ntype->draw_buttons = node_buts_texture;
break;
case CMP_NODE_HUECORRECT:
ntype->draw_buttons = node_composit_buts_huecorrect;
break;

View File

@@ -10643,7 +10643,6 @@ static void rna_def_nodes(BlenderRNA *brna)
define("CompositorNode", "CompositorNodeSunBeams");
define("CompositorNode", "CompositorNodeSwitch");
define("CompositorNode", "CompositorNodeSwitchView");
define("CompositorNode", "CompositorNodeTexture", def_texture);
define("CompositorNode", "CompositorNodeTime", def_time);
define("CompositorNode", "CompositorNodeTonemap", def_cmp_tonemap);
define("CompositorNode", "CompositorNodeTrackPos", def_cmp_trackpos);

View File

@@ -96,7 +96,6 @@ set(SRC
nodes/node_composite_sunbeams.cc
nodes/node_composite_switch.cc
nodes/node_composite_switchview.cc
nodes/node_composite_texture.cc
nodes/node_composite_tonemap.cc
nodes/node_composite_trackpos.cc
nodes/node_composite_transform.cc

View File

@@ -210,9 +210,6 @@ void ntreeCompositTagRender(Scene *scene)
if (node->id == (ID *)scene || node->type_legacy == CMP_NODE_COMPOSITE) {
BKE_ntree_update_tag_node_property(sce_iter->compositing_node_group, node);
}
else if (node->type_legacy == CMP_NODE_TEXTURE) /* uses scene size_x/size_y */ {
BKE_ntree_update_tag_node_property(sce_iter->compositing_node_group, node);
}
}
}
}

View File

@@ -1,125 +0,0 @@
/* SPDX-FileCopyrightText: 2006 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup cmpnodes
*/
#include "COM_cached_texture.hh"
#include "COM_node_operation.hh"
#include "node_composite_util.hh"
/* **************** TEXTURE ******************** */
namespace blender::nodes::node_composite_texture_cc {
static void cmp_node_texture_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Vector>("Offset")
.min(-2.0f)
.max(2.0f)
.subtype(PROP_TRANSLATION)
.compositor_expects_single_value();
b.add_input<decl::Vector>("Scale")
.default_value({1.0f, 1.0f, 1.0f})
.min(-10.0f)
.max(10.0f)
.subtype(PROP_XYZ)
.compositor_expects_single_value();
b.add_output<decl::Float>("Value");
b.add_output<decl::Color>("Color");
}
using namespace blender::compositor;
class TextureOperation : public NodeOperation {
public:
using NodeOperation::NodeOperation;
void execute() override
{
Tex *texture = get_texture();
if (!texture || !context().is_valid_compositing_region()) {
execute_invalid();
return;
}
if (texture->use_nodes) {
execute_invalid();
context().set_info_message("Viewport compositor setup not fully supported");
return;
}
const Domain domain = compute_domain();
CachedTexture &cached_texture = context().cache_manager().cached_textures.get(
context(),
texture,
true,
domain.size,
get_input("Offset").get_single_value_default(float3(0.0f)),
get_input("Scale").get_single_value_default(float3(1.0f)));
Result &color_result = get_result("Color");
if (color_result.should_compute()) {
color_result.wrap_external(cached_texture.color_result);
}
Result &value_result = get_result("Value");
if (value_result.should_compute()) {
value_result.wrap_external(cached_texture.value_result);
}
}
void execute_invalid()
{
Result &color_result = get_result("Color");
if (color_result.should_compute()) {
color_result.allocate_invalid();
}
Result &value_result = get_result("Value");
if (value_result.should_compute()) {
value_result.allocate_invalid();
}
}
Domain compute_domain() override
{
return Domain(context().get_compositing_region_size());
}
Tex *get_texture()
{
return reinterpret_cast<Tex *>(bnode().id);
}
};
static NodeOperation *get_compositor_operation(Context &context, DNode node)
{
return new TextureOperation(context, node);
}
} // namespace blender::nodes::node_composite_texture_cc
static void register_node_type_cmp_texture()
{
namespace file_ns = blender::nodes::node_composite_texture_cc;
static blender::bke::bNodeType ntype;
cmp_node_type_base(&ntype, "CompositorNodeTexture", CMP_NODE_TEXTURE);
ntype.ui_name = "Texture";
ntype.ui_description = "Generate texture pattern from texture datablock";
ntype.enum_name_legacy = "TEXTURE";
ntype.nclass = NODE_CLASS_INPUT;
ntype.declare = file_ns::cmp_node_texture_declare;
ntype.compositor_unsupported_message = N_(
"Texture nodes not supported in the Viewport compositor");
ntype.flag |= NODE_PREVIEW;
ntype.get_compositor_operation = file_ns::get_compositor_operation;
blender::bke::node_register_type(ntype);
}
NOD_REGISTER_NODE(register_node_type_cmp_texture)

Binary file not shown.

Binary file not shown.

Binary file not shown.