Realtime Compositor: Implement Texture node
This patch implements the Texture node for the realtime compositor. The evaluation of the texture is not GPU accelerated, but is cached as a form of temporary implementation since the Texture node is deprecated and will be removed in the future. Furthermore, texture node evaluation is not supported for now. This patch also introduces the concept of an ID static cache, which uses the DrawDataList mechanism to invalidate the cache as needed, consequently, a DrawDataList was added to the Tex ID structure. An improvement that should be implemented outside of this patch is to implement support for proxy textures in results to avoid redundant copies in the execute method of the texture node. This should be straightforward bit will be implemented in a separate patch. Pull Request: https://projects.blender.org/blender/blender/pulls/107291
This commit is contained in:
@@ -56,6 +56,8 @@
|
||||
|
||||
#include "RE_texture.h"
|
||||
|
||||
#include "DRW_engine.h"
|
||||
|
||||
#include "BLO_read_write.h"
|
||||
|
||||
static void texture_init_data(ID *id)
|
||||
@@ -100,6 +102,8 @@ static void texture_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const i
|
||||
texture_dst->nodetree->owner_id = &texture_dst->id;
|
||||
}
|
||||
|
||||
BLI_listbase_clear((ListBase *)&texture_dst->drawdata);
|
||||
|
||||
if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) {
|
||||
BKE_previewimg_id_copy(&texture_dst->id, &texture_src->id);
|
||||
}
|
||||
@@ -112,6 +116,8 @@ static void texture_free_data(ID *id)
|
||||
{
|
||||
Tex *texture = (Tex *)id;
|
||||
|
||||
DRW_drawdata_free(id);
|
||||
|
||||
/* is no lib link block, but texture extension */
|
||||
if (texture->nodetree) {
|
||||
ntreeFreeEmbeddedTree(texture->nodetree);
|
||||
|
||||
@@ -70,12 +70,14 @@ set(SRC
|
||||
algorithms/COM_algorithm_smaa.hh
|
||||
algorithms/COM_algorithm_symmetric_separable_blur.hh
|
||||
|
||||
cached_resources/intern/cached_texture.cc
|
||||
cached_resources/intern/morphological_distance_feather_weights.cc
|
||||
cached_resources/intern/smaa_precomputed_textures.cc
|
||||
cached_resources/intern/symmetric_blur_weights.cc
|
||||
cached_resources/intern/symmetric_separable_blur_weights.cc
|
||||
|
||||
cached_resources/COM_cached_resource.hh
|
||||
cached_resources/COM_cached_texture.hh
|
||||
cached_resources/COM_morphological_distance_feather_weights.hh
|
||||
cached_resources/COM_smaa_precomputed_textures.hh
|
||||
cached_resources/COM_symmetric_blur_weights.hh
|
||||
@@ -248,4 +250,16 @@ endforeach()
|
||||
set(shader_create_info_list_file "${CMAKE_CURRENT_BINARY_DIR}/compositor_shader_create_info_list.hh")
|
||||
file(GENERATE OUTPUT ${shader_create_info_list_file} CONTENT "${SHADER_CREATE_INFOS_CONTENT}")
|
||||
|
||||
if(WITH_TBB)
|
||||
list(APPEND INC_SYS
|
||||
${TBB_INCLUDE_DIRS}
|
||||
)
|
||||
add_definitions(-DWITH_TBB)
|
||||
if(WIN32)
|
||||
# TBB includes Windows.h which will define min/max macros
|
||||
# that will collide with the stl versions.
|
||||
add_definitions(-DNOMINMAX)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_realtime_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_vec_types.h"
|
||||
|
||||
@@ -70,6 +71,14 @@ 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;
|
||||
|
||||
/* Returns the ID recalculate flag of the given ID and reset it to zero. The given ID is assumed
|
||||
* to be one that has a DrawDataList and conforms to the IdDdtTemplate.
|
||||
*
|
||||
* The ID recalculate flag is a mechanism through which one can identify if an ID has changed
|
||||
* since the last time the flag was reset, hence why the method reset the flag after querying it,
|
||||
* that is, to ready it to track the next change. */
|
||||
virtual IDRecalcFlag query_id_recalc_flag(ID *id) const = 0;
|
||||
|
||||
/* Get the size of the compositing region. See get_compositing_region(). */
|
||||
int2 get_compositing_region_size() const;
|
||||
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_texture_types.h"
|
||||
|
||||
#include "COM_cached_texture.hh"
|
||||
#include "COM_context.hh"
|
||||
#include "COM_morphological_distance_feather_weights.hh"
|
||||
#include "COM_smaa_precomputed_textures.hh"
|
||||
#include "COM_symmetric_blur_weights.hh"
|
||||
@@ -14,6 +19,8 @@
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
class Context;
|
||||
|
||||
/* -------------------------------------------------------------------------------------------------
|
||||
* Static Cache Manager
|
||||
*
|
||||
@@ -48,6 +55,11 @@ class StaticCacheManager {
|
||||
Map<MorphologicalDistanceFeatherWeightsKey, std::unique_ptr<MorphologicalDistanceFeatherWeights>>
|
||||
morphological_distance_feather_weights_;
|
||||
|
||||
/* A nested map that stores all CachedTexture cached resources. The outer map identifies the
|
||||
* textures using their ID name, while the inner map identifies the textures using their
|
||||
* parameters. */
|
||||
Map<std::string, Map<CachedTextureKey, std::unique_ptr<CachedTexture>>> cached_textures_;
|
||||
|
||||
/* A unique pointers that stores the cached SMAAPrecomputedTextures, if one is cached. */
|
||||
std::unique_ptr<SMAAPrecomputedTextures> smaa_precomputed_textures_;
|
||||
|
||||
@@ -77,6 +89,15 @@ class StaticCacheManager {
|
||||
MorphologicalDistanceFeatherWeights &get_morphological_distance_feather_weights(int type,
|
||||
int radius);
|
||||
|
||||
/* 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 manager, if one exists, return
|
||||
* it, otherwise, return a newly created one and add it to the manager. In both cases, tag the
|
||||
* cached resource as needed to keep it cached for the next evaluation. */
|
||||
CachedTexture &get_cached_texture(
|
||||
Context &context, Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale);
|
||||
|
||||
/* Check if a cached SMAA precomputed texture exists, if it does, return it, otherwise, return
|
||||
* a newly created one and store it in the manager. In both cases, tag the cached resource as
|
||||
* needed to keep it cached for the next evaluation. */
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_texture_types.h"
|
||||
|
||||
#include "COM_cached_resource.hh"
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
/* ------------------------------------------------------------------------------------------------
|
||||
* Cached Texture Key.
|
||||
*/
|
||||
class CachedTextureKey {
|
||||
public:
|
||||
int2 size;
|
||||
float2 offset;
|
||||
float2 scale;
|
||||
|
||||
CachedTextureKey(int2 size, float2 offset, float2 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 the result of evaluating
|
||||
* the given texture ID on a space that spans the given size, modified by the given offset and
|
||||
* scale. */
|
||||
class CachedTexture : public CachedResource {
|
||||
private:
|
||||
GPUTexture *color_texture_ = nullptr;
|
||||
GPUTexture *value_texture_ = nullptr;
|
||||
|
||||
public:
|
||||
CachedTexture(Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale);
|
||||
|
||||
~CachedTexture();
|
||||
|
||||
GPUTexture *color_texture();
|
||||
|
||||
GPUTexture *value_texture();
|
||||
};
|
||||
|
||||
} // namespace blender::realtime_compositor
|
||||
@@ -0,0 +1,102 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_index_range.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "GPU_texture.h"
|
||||
|
||||
#include "BKE_texture.h"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_texture_types.h"
|
||||
|
||||
#include "RE_texture.h"
|
||||
|
||||
#include "COM_cached_texture.hh"
|
||||
|
||||
namespace blender::realtime_compositor {
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Cached Texture Key.
|
||||
*/
|
||||
|
||||
CachedTextureKey::CachedTextureKey(int2 size, float2 offset, float2 scale)
|
||||
: size(size), offset(offset), scale(scale)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t CachedTextureKey::hash() const
|
||||
{
|
||||
return get_default_hash_3(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(
|
||||
Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale)
|
||||
{
|
||||
Array<float4> color_pixels(size.x * size.y);
|
||||
Array<float> value_pixels(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 [0, 1] range and add 0.5 to evaluate the texture at the
|
||||
* center of pixels in case it was interpolated. */
|
||||
float2 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. */
|
||||
coordinates = (coordinates + offset) * scale;
|
||||
TexResult texture_result;
|
||||
BKE_texture_get_value(scene, texture, coordinates, &texture_result, true);
|
||||
color_pixels[y * size.x + x] = float4(texture_result.trgba);
|
||||
value_pixels[y * size.x + x] = texture_result.talpha ? texture_result.trgba[3] :
|
||||
texture_result.tin;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
color_texture_ = GPU_texture_create_2d("Cached Color Texture",
|
||||
size.x,
|
||||
size.y,
|
||||
1,
|
||||
GPU_RGBA16F,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
*color_pixels.data());
|
||||
|
||||
value_texture_ = GPU_texture_create_2d("Cached Value Texture",
|
||||
size.x,
|
||||
size.y,
|
||||
1,
|
||||
GPU_R16F,
|
||||
GPU_TEXTURE_USAGE_SHADER_READ,
|
||||
value_pixels.data());
|
||||
}
|
||||
|
||||
CachedTexture::~CachedTexture()
|
||||
{
|
||||
GPU_texture_free(color_texture_);
|
||||
GPU_texture_free(value_texture_);
|
||||
}
|
||||
|
||||
GPUTexture *CachedTexture::color_texture()
|
||||
{
|
||||
return color_texture_;
|
||||
}
|
||||
|
||||
GPUTexture *CachedTexture::value_texture()
|
||||
{
|
||||
return value_texture_;
|
||||
}
|
||||
|
||||
} // namespace blender::realtime_compositor
|
||||
@@ -4,6 +4,11 @@
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_texture_types.h"
|
||||
|
||||
#include "COM_context.hh"
|
||||
#include "COM_morphological_distance_feather_weights.hh"
|
||||
#include "COM_smaa_precomputed_textures.hh"
|
||||
#include "COM_symmetric_blur_weights.hh"
|
||||
@@ -23,6 +28,12 @@ void StaticCacheManager::reset()
|
||||
symmetric_blur_weights_.remove_if([](auto item) { return !item.value->needed; });
|
||||
symmetric_separable_blur_weights_.remove_if([](auto item) { return !item.value->needed; });
|
||||
morphological_distance_feather_weights_.remove_if([](auto item) { return !item.value->needed; });
|
||||
|
||||
for (auto &cached_textures_for_id : cached_textures_.values()) {
|
||||
cached_textures_for_id.remove_if([](auto item) { return !item.value->needed; });
|
||||
}
|
||||
cached_textures_.remove_if([](auto item) { return item.value.is_empty(); });
|
||||
|
||||
if (smaa_precomputed_textures_ && !smaa_precomputed_textures_->needed) {
|
||||
smaa_precomputed_textures_.reset();
|
||||
}
|
||||
@@ -38,6 +49,11 @@ void StaticCacheManager::reset()
|
||||
for (auto &value : morphological_distance_feather_weights_.values()) {
|
||||
value->needed = false;
|
||||
}
|
||||
for (auto &cached_textures_for_id : cached_textures_.values()) {
|
||||
for (auto &value : cached_textures_for_id.values()) {
|
||||
value->needed = false;
|
||||
}
|
||||
}
|
||||
if (smaa_precomputed_textures_) {
|
||||
smaa_precomputed_textures_->needed = false;
|
||||
}
|
||||
@@ -78,6 +94,24 @@ MorphologicalDistanceFeatherWeights &StaticCacheManager::
|
||||
return weights;
|
||||
}
|
||||
|
||||
CachedTexture &StaticCacheManager::get_cached_texture(
|
||||
Context &context, Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale)
|
||||
{
|
||||
const CachedTextureKey key(size, offset, scale);
|
||||
|
||||
auto &cached_textures_for_id = cached_textures_.lookup_or_add_default(texture->id.name);
|
||||
|
||||
if (context.query_id_recalc_flag(reinterpret_cast<ID *>(texture)) & ID_RECALC_ALL) {
|
||||
cached_textures_for_id.clear();
|
||||
}
|
||||
|
||||
auto &cached_texture = *cached_textures_for_id.lookup_or_add_cb(
|
||||
key, [&]() { return std::make_unique<CachedTexture>(texture, scene, size, offset, scale); });
|
||||
|
||||
cached_texture.needed = true;
|
||||
return cached_texture;
|
||||
}
|
||||
|
||||
SMAAPrecomputedTextures &StaticCacheManager::get_smaa_precomputed_textures()
|
||||
{
|
||||
if (!smaa_precomputed_textures_) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_ID_enums.h"
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
@@ -143,6 +144,15 @@ class Context : public realtime_compositor::Context {
|
||||
{
|
||||
message.copy(info_message_, GPU_INFO_SIZE);
|
||||
}
|
||||
|
||||
IDRecalcFlag query_id_recalc_flag(ID *id) const override
|
||||
{
|
||||
DrawEngineType *owner = &draw_engine_compositor_type;
|
||||
DrawData *draw_data = DRW_drawdata_ensure(id, owner, sizeof(DrawData), nullptr, nullptr);
|
||||
IDRecalcFlag recalc_flag = IDRecalcFlag(draw_data->recalc);
|
||||
draw_data->recalc = IDRecalcFlag(0);
|
||||
return recalc_flag;
|
||||
}
|
||||
};
|
||||
|
||||
class Engine {
|
||||
|
||||
@@ -832,6 +832,7 @@ static bool id_type_can_have_drawdata(const short id_type)
|
||||
case ID_OB:
|
||||
case ID_WO:
|
||||
case ID_SCE:
|
||||
case ID_TE:
|
||||
return true;
|
||||
|
||||
/* no DrawData */
|
||||
|
||||
@@ -153,6 +153,8 @@ typedef struct Tex {
|
||||
ID id;
|
||||
/** Animation data (must be immediately after id for utilities to use it). */
|
||||
struct AnimData *adt;
|
||||
/* runtime (must be immediately after id for utilities to use it). */
|
||||
DrawDataList drawdata;
|
||||
|
||||
float noisesize, turbul;
|
||||
float bright, contrast, saturation, rfac, gfac, bfac;
|
||||
@@ -264,14 +266,14 @@ typedef struct ColorMapping {
|
||||
#define TEX_STUCCI 6
|
||||
#define TEX_NOISE 7
|
||||
#define TEX_IMAGE 8
|
||||
//#define TEX_PLUGIN 9 /* Deprecated */
|
||||
//#define TEX_ENVMAP 10 /* Deprecated */
|
||||
// #define TEX_PLUGIN 9 /* Deprecated */
|
||||
// #define TEX_ENVMAP 10 /* Deprecated */
|
||||
#define TEX_MUSGRAVE 11
|
||||
#define TEX_VORONOI 12
|
||||
#define TEX_DISTNOISE 13
|
||||
//#define TEX_POINTDENSITY 14 /* Deprecated */
|
||||
//#define TEX_VOXELDATA 15 /* Deprecated */
|
||||
//#define TEX_OCEAN 16 /* Deprecated */
|
||||
// #define TEX_POINTDENSITY 14 /* Deprecated */
|
||||
// #define TEX_VOXELDATA 15 /* Deprecated */
|
||||
// #define TEX_OCEAN 16 /* Deprecated */
|
||||
|
||||
/* musgrave stype */
|
||||
#define TEX_MFRACTAL 0
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "COM_cached_texture.hh"
|
||||
#include "COM_node_operation.hh"
|
||||
#include "COM_utilities.hh"
|
||||
|
||||
#include "node_composite_util.hh"
|
||||
|
||||
@@ -17,12 +19,17 @@ namespace blender::nodes::node_composite_texture_cc {
|
||||
|
||||
static void cmp_node_texture_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Vector>(N_("Offset")).min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION);
|
||||
b.add_input<decl::Vector>(N_("Offset"))
|
||||
.min(-2.0f)
|
||||
.max(2.0f)
|
||||
.subtype(PROP_TRANSLATION)
|
||||
.compositor_expects_single_value();
|
||||
b.add_input<decl::Vector>(N_("Scale"))
|
||||
.default_value({1.0f, 1.0f, 1.0f})
|
||||
.min(-10.0f)
|
||||
.max(10.0f)
|
||||
.subtype(PROP_XYZ);
|
||||
.subtype(PROP_XYZ)
|
||||
.compositor_expects_single_value();
|
||||
b.add_output<decl::Float>(N_("Value"));
|
||||
b.add_output<decl::Color>(N_("Color"));
|
||||
}
|
||||
@@ -35,9 +42,46 @@ class TextureOperation : public NodeOperation {
|
||||
|
||||
void execute() override
|
||||
{
|
||||
get_result("Value").allocate_invalid();
|
||||
get_result("Color").allocate_invalid();
|
||||
context().set_info_message("Viewport compositor setup not fully supported");
|
||||
Result &color_result = get_result("Color");
|
||||
Result &value_result = get_result("Value");
|
||||
if (!get_texture()) {
|
||||
if (color_result.should_compute()) {
|
||||
color_result.allocate_invalid();
|
||||
}
|
||||
if (value_result.should_compute()) {
|
||||
value_result.allocate_invalid();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const Domain domain = compute_domain();
|
||||
CachedTexture &cached_texture = context().cache_manager().get_cached_texture(
|
||||
context(),
|
||||
get_texture(),
|
||||
context().get_scene(),
|
||||
domain.size,
|
||||
get_input("Offset").get_vector_value_default(float4(0.0f)).xy(),
|
||||
get_input("Scale").get_vector_value_default(float4(0.0f)).xy());
|
||||
|
||||
if (color_result.should_compute()) {
|
||||
color_result.allocate_texture(domain);
|
||||
GPU_texture_copy(color_result.texture(), cached_texture.color_texture());
|
||||
}
|
||||
|
||||
if (value_result.should_compute()) {
|
||||
value_result.allocate_texture(domain);
|
||||
GPU_texture_copy(value_result.texture(), cached_texture.value_texture());
|
||||
}
|
||||
}
|
||||
|
||||
Domain compute_domain() override
|
||||
{
|
||||
return Domain(context().get_compositing_region_size());
|
||||
}
|
||||
|
||||
Tex *get_texture()
|
||||
{
|
||||
return (Tex *)bnode().id;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -58,8 +102,6 @@ void register_node_type_cmp_texture()
|
||||
ntype.declare = file_ns::cmp_node_texture_declare;
|
||||
ntype.flag |= NODE_PREVIEW;
|
||||
ntype.get_compositor_operation = file_ns::get_compositor_operation;
|
||||
ntype.realtime_compositor_unsupported_message = N_(
|
||||
"Node not supported in the Viewport compositor");
|
||||
|
||||
nodeRegisterType(&ntype);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user