Blender V5.0
COM_result.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#pragma once
6
7#include <cstdint>
8#include <optional>
9#include <string>
10#include <variant>
11
12#include "BLI_assert.h"
13#include "BLI_compiler_compat.h"
14#include "BLI_cpp_type.hh"
16#include "BLI_generic_span.hh"
17#include "BLI_math_interp.hh"
19#include "BLI_math_vector.h"
20#include "BLI_math_vector.hh"
22
23#include "GPU_shader.hh"
24#include "GPU_texture.hh"
25
26#include "NOD_menu_value.hh"
27
28#include "COM_domain.hh"
29#include "COM_meta_data.hh"
30
31namespace blender::compositor {
32
33class Context;
35
36/* Make sure to update the format related static methods in the Result class. */
37enum class ResultType : uint8_t {
47
48 /* Single value only types. See Result::is_single_value_only_type. */
50};
51
52/* The precision of the data. CPU data is always stored using full precision at the moment. */
53enum class ResultPrecision : uint8_t {
56};
57
58/* The type of storage used to hold the result data. */
59enum class ResultStorageType : uint8_t {
60 /* Stored as a blender::gpu::Texture on the GPU. */
62 /* Stored as a buffer on the CPU and wrapped in a GMutableSpan. */
64};
65
66/* ------------------------------------------------------------------------------------------------
67 * Result
68 *
69 * A result represents the computed value of an output of an operation. A result can either
70 * represent an image or a single value. A result is typed, and can be of types like color, vector,
71 * or float. Single value results are stored in 1x1 textures to make them easily accessible in
72 * shaders. But the same value is also stored in the value union member of the result for any
73 * host-side processing. The GPU texture of the result can either be allocated from the texture
74 * pool of the context referenced by the result or it can be allocated directly from the GPU
75 * module, see the allocation method for more information.
76 *
77 * Results are reference counted and their data are released once their reference count reaches
78 * zero. After constructing a result, the set_reference_count method is called to declare the
79 * number of operations that needs this result. Once each operation that needs the result no longer
80 * needs it, the release method is called and the reference count is decremented, until it reaches
81 * zero, where the result's data is then released.
82 *
83 * A result not only represents an image, but also the area it occupies in the virtual compositing
84 * space. This area is called the Domain of the result, see the discussion in COM_domain.hh for
85 * more information.
86 *
87 * Allocated data of results can be shared by multiple results, this is achieved by tracking an
88 * extra reference count for data data_reference_count_, which is heap allocated along with the
89 * data, and shared by all results that share the same data. This reference count is incremented
90 * every time the data is shared by a call to the share_data method, and decremented during
91 * freeing, where the data is only freed if the reference count is 1, that is, no longer shared.
92 *
93 * A result can wrap external data that is not allocated nor managed by the result. This is set up
94 * by a call to the wrap_external method. In that case, when the reference count eventually reach
95 * zero, the data will not be freed.
96 *
97 * A result may store resources that are computed and cached in case they are needed by multiple
98 * operations. Those are called Derived Resources and can be accessed using the derived_resources
99 * method. */
100class Result {
101 private:
102 /* The context that the result was created within, this should be initialized during
103 * construction. */
104 Context *context_ = nullptr;
105 /* The base type of the result's image or single value. */
107 /* The precision of the result's data. Only relevant for GPU textures. CPU buffers and single
108 * values are always stored using full precision. */
110 /* If true, the result is a single value, otherwise, the result is an image. */
111 bool is_single_value_ = false;
112 /* The type of storage used to hold the data. Used to correctly interpret the data union. */
114 /* Stores the result's pixel data, either stored in a GPU texture or a buffer that is wrapped in
115 * a GMutableSpan on CPU. This will represent a 1x1 image if the result is a single value, the
116 * value of which will be identical to that of the value member. See class description for more
117 * information. */
118 union {
121 };
122 /* The number of users that currently needs this result. Operations initializes this by calling
123 * the set_reference_count method before evaluation. Once each operation that needs the result no
124 * longer needs it, the release method is called and the reference count is decremented, until it
125 * reaches zero, where the result's data is then released. */
126 int reference_count_ = 1;
127 /* Allocated result data can be shared by multiple results by calling the share_data method. This
128 * member stores the number of results that share the data. This is heap allocated and have the
129 * same lifetime as allocated data, that's because this reference count is shared by all results
130 * that share the same data. Unlike the result's reference count, the data is freed if the count
131 * becomes 1, that is, data is no longer shared with some other result. This is nullptr if the
132 * data is external. */
133 int *data_reference_count_ = nullptr;
134 /* If the result is a single value, this member stores the value of the result, the value of
135 * which will be identical to that stored in the data_ member. The active variant member depends
136 * on the type of the result. This member is uninitialized and should not be used if the result
137 * is not a single value. */
138 std::variant<float, float2, float3, float4, int32_t, int2, bool, std::string, nodes::MenuValue>
139 single_value_ = 0.0f;
140 /* The domain of the result. This only matters if the result was not a single value. See the
141 * discussion in COM_domain.hh for more information. */
142 Domain domain_ = Domain::identity();
143 /* If true, then the result wraps external data that is not allocated nor managed by the result.
144 * This is set up by a call to the wrap_external method. In that case, when the reference count
145 * eventually reach zero, the data will not be freed. */
146 bool is_external_ = false;
147 /* If true, the GPU texture that holds the data was allocated from the texture pool of the
148 * context and should be released back into the pool instead of being freed. For CPU storage,
149 * this is irrelevant. */
150 bool is_from_pool_ = false;
151 /* Stores resources that are derived from this result. Lazily allocated if needed. See the class
152 * description for more information. */
153 DerivedResources *derived_resources_ = nullptr;
154
155 public:
156 /* Stores extra information about the result such as image meta data that can eventually be
157 * written to file. */
159
160 /* Construct a result within the given context. */
161 Result(Context &context);
162
163 /* Construct a result of the given type and precision within the given context. */
165
166 /* Construct a result of an appropriate type and precision based on the given GPU texture format
167 * within the given context. */
169
170 /* Returns true if the given type can only be used with single value results. Consequently, it is
171 * always allocated on the CPU and GPU code paths needn't support the type. */
173
174 /* Returns the appropriate GPU texture format based on the given result type and precision. A
175 * special case is given to ResultType::Float3, because 3-component textures can't be used as
176 * write targets in shaders, so we need to allocate 4-component textures for them, and ignore the
177 * fourth channel during processing. */
180
181 /* Returns the GPU data format that corresponds to the give result type. */
183
184 /* Returns the GPU texture format that corresponds to the give one, but whose precision is the
185 * given precision. */
188
189 /* Returns the precision of the given GPU texture format. */
191
192 /* Returns the type of the given GPU texture format. */
194
195 /* Returns the float type of the result given the channels count. */
196 static ResultType float_type(const int channels_count);
197
198 /* Returns the CPP type corresponding to the given result type. */
199 static const CPPType &cpp_type(const ResultType type);
200
201 /* Returns a string representation of the given result type. */
202 static const char *type_name(const ResultType type);
203
204 /* Implicit conversion to the internal GPU texture. */
205 operator blender::gpu::Texture *() const;
206
207 /* Returns the CPP type of the result. */
208 const CPPType &get_cpp_type() const;
209
210 /* Returns the appropriate texture format based on the result's type and precision. This is
211 * identical to the gpu_texture_format static method. This will match the format of the allocated
212 * texture, with one exception. Results of type ResultType::Float3 that wrap external textures
213 * might hold a 3-component texture as opposed to a 4-component one, which would have been
214 * created by uploading data from CPU. */
216
217 /* Identical to gpu_data_format but assumes the result's type. */
219
220 /* Declare the result to be a texture result, allocate a texture of an appropriate type with
221 * the size of the given domain, and set the domain of the result to the given domain.
222 *
223 * See the allocate_data method for more information on the from_pool and storage_type
224 * parameters. */
225 void allocate_texture(const Domain domain,
226 const bool from_pool = true,
227 const std::optional<ResultStorageType> storage_type = std::nullopt);
228
229 /* Declare the result to be a single value result, allocate a texture of an appropriate type with
230 * size 1x1 from the texture pool, and set the domain to be an identity domain. The value is zero
231 * initialized. See class description for more information. */
233
234 /* Allocate a single value result whose value is zero. This is called for results whose value
235 * can't be computed and are considered invalid. */
236 void allocate_invalid();
237
238 /* Creates and allocates a new result that matches the type and precision of this result and
239 * uploads the CPU data that exist in this result. The result is assumed to be allocated on the
240 * CPU. See the allocate_data method for more information on the from_pool parameters. */
241 Result upload_to_gpu(const bool from_pool) const;
242
243 /* Creates and allocates a new result that matches the type and precision of this result and
244 * downloads the GPU data that exist in this result. The result is assumed to be allocated on the
245 * GPU. */
246 Result download_to_cpu() const;
247
248 /* Bind the GPU texture of the result to the texture image unit with the given name in the
249 * currently bound given shader. This also inserts a memory barrier for texture fetches to ensure
250 * any prior writes to the texture are reflected before reading from it. */
251 void bind_as_texture(gpu::Shader *shader, const char *texture_name) const;
252
253 /* Bind the GPU texture of the result to the image unit with the given name in the currently
254 * bound given shader. If read is true, a memory barrier will be inserted for image reads to
255 * ensure any prior writes to the images are reflected before reading from it. */
256 void bind_as_image(gpu::Shader *shader, const char *image_name, bool read = false) const;
257
258 /* Unbind the GPU texture which was previously bound using bind_as_texture. */
259 void unbind_as_texture() const;
260
261 /* Unbind the GPU texture which was previously bound using bind_as_image. */
262 void unbind_as_image() const;
263
264 /* Share the data of the given source result. For a source that wraps external results, this just
265 * shallow copies the data since it can be transparency shared. Otherwise, the data is also
266 * shallow copied and the data_reference_count_ is incremented to denote sharing. The source data
267 * is expect to be allocated and have the same type and precision as this result. */
268 void share_data(const Result &source);
269
270 /* Steal the allocated data from the given source result and assign it to this result, then
271 * remove any references to the data from the source result. It is assumed that:
272 *
273 * - Both results are of the same type.
274 * - This result is not allocated but the source result is allocated.
275 *
276 * This is most useful in multi-step compositor operations where some steps can be optional, in
277 * that case, intermediate results can be temporary results that can eventually be stolen by the
278 * actual output of the operation. See the uses of the method for a practical example of use. */
279 void steal_data(Result &source);
280
281 /* Similar to the Result variant of steal_data, but steals from a raw data buffer. The buffer is
282 * assumed to be allocated using Blender's guarded allocator. */
283 void steal_data(void *data, int2 size);
284
285 /* Set up the result to wrap an external GPU texture that is not allocated nor managed by the
286 * result. The is_external_ member will be set to true, the domain will be set to have the same
287 * size as the texture, and the texture will be set to the given texture. See the is_external_
288 * member for more information. The given texture should have the same format as the result and
289 * is assumed to have a lifetime that covers the evaluation of the compositor. */
291
292 /* Identical to GPU variant of wrap_external but wraps a CPU buffer instead. */
293 void wrap_external(void *data, int2 size);
294
295 /* Identical to GPU variant of wrap_external but wraps whatever the given result has instead. */
296 void wrap_external(const Result &result);
297
298 /* Sets the transformation of the domain of the result to the given transformation. */
299 void set_transformation(const float3x3 &transformation);
300
301 /* Transform the result by the given transformation. This effectively pre-multiply the given
302 * transformation by the current transformation of the domain of the result. */
303 void transform(const float3x3 &transformation);
304
305 /* Get a reference to the realization options of this result. See the RealizationOptions struct
306 * for more information. */
309
310 /* Set the value of reference_count_, see that member for more details. This should be called
311 * after constructing the result to declare the number of operations that needs it. */
312 void set_reference_count(int count);
313
314 /* Increment the reference count of the result by the given count. */
315 void increment_reference_count(int count = 1);
316
317 /* Decrement the reference count of the result by the given count. */
318 void decrement_reference_count(int count = 1);
319
320 /* Decrement the reference count of the result and free its data if it reaches zero. */
321 void release();
322
323 /* Frees the result data. If the result is not allocated, wraps external data, or shares data
324 * with some other result, then this does nothing. */
325 void free();
326
327 /* Returns true if this result should be computed and false otherwise. The result should be
328 * computed if its reference count is not zero, that is, its result is used by at least one
329 * operation. */
330 bool should_compute();
331
332 /* Returns a reference to the derived resources of the result, which is allocated if it was not
333 * allocated already. */
335
336 /* Returns the type of the result. */
337 ResultType type() const;
338
339 /* Returns the precision of the result. */
341
342 /* Sets the type of the result. */
344
345 /* Sets the precision of the result. */
347
348 /* Returns true if the result is a single value and false of it is an image. */
349 bool is_single_value() const;
350
351 /* Returns true if the result is allocated. */
352 bool is_allocated() const;
353
354 /* Returns the reference count of the result. */
355 int reference_count() const;
356
357 /* Returns a reference to the domain of the result. See the Domain class. */
358 const Domain &domain() const;
359
360 /* Computes the number of channels of the result based on its type. */
361 int64_t channels_count() const;
362
363 /* Computes the size of the result's data in bytes. */
364 int64_t size_in_bytes() const;
365
367
368 GSpan cpu_data() const;
370
371 /* It is important to call update_single_value_data after adjusting the single value. See that
372 * method for more information. */
373 GPointer single_value() const;
375
376 /* Gets the single value stored in the result. Assumes the result stores a value of the given
377 * template type. */
378 template<typename T> const T &get_single_value() const;
379
380 /* Gets the single value stored in the result, if the result is not a single value, the given
381 * default value is returned. Assumes the result stores a value of the same type as the template
382 * type. */
383 template<typename T> T get_single_value_default(const T &default_value) const;
384
385 /* Sets the single value of the result to the given value, which also involves setting the single
386 * pixel in the image to that value. See the class description for more information. Assumes
387 * the result stores a value of the given template type. */
388 template<typename T> void set_single_value(const T &value);
389
390 /* Updates the single pixel in the image to the current single value in the result. This is
391 * called implicitly in the set_single_value method, but calling this explicitly is useful when
392 * the single value was adjusted through its data pointer returned by the single_value method.
393 * See the class description for more information. */
395
396 /* Loads the pixel at the given texel coordinates. Assumes the result stores a value of the given
397 * template type. If the CouldBeSingleValue template argument is true and the result is a single
398 * value result, then that single value is returned for all texel coordinates. */
399 template<typename T, bool CouldBeSingleValue = false> T load_pixel(const int2 &texel) const;
400
401 /* Identical to load_pixel but with extended boundary condition. */
402 template<typename T, bool CouldBeSingleValue = false>
403 T load_pixel_extended(const int2 &texel) const;
404
405 /* Identical to load_pixel but with a fallback value for out of bound access. */
406 template<typename T, bool CouldBeSingleValue = false>
407 T load_pixel_fallback(const int2 &texel, const T &fallback) const;
408
409 /* Identical to load_pixel but with zero boundary condition. */
410 template<typename T, bool CouldBeSingleValue = false> T load_pixel_zero(const int2 &texel) const;
411
412 /* Similar to load_pixel, but can load a result whose type is not known at compile time. If the
413 * number of channels in the result are less than 4, then the rest of the returned float4 will
414 * have its vales initialized as follows: float4(0, 0, 0, 1). This is similar to how the
415 * texelFetch function in GLSL works. */
416 float4 load_pixel_generic_type(const int2 &texel) const;
417
418 /* Stores the given pixel value in the pixel at the given texel coordinates. Assumes the result
419 * stores a value of the given template type. */
420 template<typename T> void store_pixel(const int2 &texel, const T &pixel_value);
421
422 /* Similar to store_pixel, but can write to a result whose types is not known at compile time.
423 * While a float4 is given, only the number of channels of the result will be written, while the
424 * rest of the float4 will be ignored. This is similar to how the imageStore function in GLSL
425 * works. */
426 void store_pixel_generic_type(const int2 &texel, const float4 &pixel_value);
427
428 float4 sample(const float2 &coordinates,
429 const Interpolation &interpolation,
430 const ExtensionMode &extend_mode_x,
431 const ExtensionMode &extend_mode_y) const;
432
433 /* Equivalent to the GLSL texture() function with nearest interpolation and zero boundary
434 * condition. The coordinates are thus expected to have half-pixels offsets. A float4 is always
435 * returned regardless of the number of channels of the buffer, the remaining channels will be
436 * initialized with the template float4(0, 0, 0, 1). */
437 float4 sample_nearest_zero(const float2 &coordinates) const;
438
439 /* Identical to sample_nearest_zero but with bilinear interpolation. */
440 float4 sample_bilinear_zero(const float2 &coordinates) const;
441
442 /* Identical to sample_nearest_zero but with extended boundary condition. */
443 float4 sample_nearest_extended(const float2 &coordinates) const;
444
445 /* Identical to sample_nearest_extended but with bilinear interpolation. */
446 float4 sample_bilinear_extended(const float2 &coordinates) const;
447
448 /* Identical to sample_nearest_extended but with cubic interpolation. */
449 float4 sample_cubic_extended(const float2 &coordinates) const;
450
451 float4 sample_nearest_wrap(const float2 &coordinates, bool wrap_x, bool wrap_y) const;
452 float4 sample_bilinear_wrap(const float2 &coordinates, bool wrap_x, bool wrap_y) const;
453 float4 sample_cubic_wrap(const float2 &coordinates, bool wrap_x, bool wrap_y) const;
454
455 /* Equivalent to the GLSL textureGrad() function with EWA filtering and extended boundary
456 * condition. Note that extended boundaries only cover areas touched by the ellipses whose
457 * center is inside the image, other areas will be zero. The coordinates are thus expected to
458 * have half-pixels offsets. Only supports ResultType::Color. */
459 float4 sample_ewa_extended(const float2 &coordinates,
460 const float2 &x_gradient,
461 const float2 &y_gradient) const;
462
463 /* Identical to sample_ewa_extended but with zero boundary condition. */
464 float4 sample_ewa_zero(const float2 &coordinates,
465 const float2 &x_gradient,
466 const float2 &y_gradient) const;
467
468 private:
469 /* Allocates the image data for the given size.
470 *
471 * The data is allocated on the CPU or GPU depending on the given storage_type. A nullopt may be
472 * passed to storage_type, in which case, the data will be allocated on the device of the
473 * result's context as specified by context.use_gpu().
474 *
475 * If from_pool is true, GPU textures will be allocated from the texture pool of the context,
476 * otherwise, a new texture will be allocated. Pooling should not be used for persistent results
477 * that might span more than one evaluation, like cached resources. While pooling should be used
478 * for most other cases where the result will be allocated then later released in the same
479 * evaluation. */
480 void allocate_data(const int2 size,
481 const bool from_pool = true,
482 const std::optional<ResultStorageType> storage_type = std::nullopt);
483
484 /* Same as get_pixel_index but can be used when the type of the result is not known at compile
485 * time. */
486 int64_t get_pixel_index(const int2 &texel) const;
487};
488
489/* -------------------------------------------------------------------- */
490/* Inline Methods.
491 */
492
494{
495 return domain_;
496}
497
499{
500 switch (type_) {
502 case ResultType::Int:
503 case ResultType::Bool:
504 case ResultType::Menu:
505 return 1;
507 case ResultType::Int2:
508 return 2;
510 return 3;
513 return 4;
515 /* Single only types do not have channels. */
518 break;
519 }
520
522 return 4;
523}
524
530
532{
533 BLI_assert(storage_type_ == ResultStorageType::CPU);
534 return cpu_data_;
535}
536
542
543template<typename T> BLI_INLINE_METHOD const T &Result::get_single_value() const
544{
546
547 return std::get<T>(single_value_);
548}
549
550template<typename T>
552{
553 if (this->is_single_value()) {
554 return this->get_single_value<T>();
555 }
556 return default_value;
557}
558
559template<typename T> BLI_INLINE_METHOD void Result::set_single_value(const T &value)
560{
561 BLI_assert(this->is_allocated());
563
564 single_value_ = value;
566}
567
568template<typename T, bool CouldBeSingleValue>
570{
571 if constexpr (CouldBeSingleValue) {
572 if (is_single_value_) {
573 return this->get_single_value<T>();
574 }
575 }
576 else {
577 BLI_assert(!this->is_single_value());
578 }
579
580 return this->cpu_data().typed<T>()[this->get_pixel_index(texel)];
581}
582
583template<typename T, bool CouldBeSingleValue>
585{
586 if constexpr (CouldBeSingleValue) {
587 if (is_single_value_) {
588 return this->get_single_value<T>();
589 }
590 }
591 else {
592 BLI_assert(!this->is_single_value());
593 }
594
595 const int2 clamped_texel = math::clamp(texel, int2(0), domain_.size - int2(1));
596 return this->cpu_data().typed<T>()[this->get_pixel_index(clamped_texel)];
597}
598
599template<typename T, bool CouldBeSingleValue>
600BLI_INLINE_METHOD T Result::load_pixel_fallback(const int2 &texel, const T &fallback) const
601{
602 if constexpr (CouldBeSingleValue) {
603 if (is_single_value_) {
604 return this->get_single_value<T>();
605 }
606 }
607 else {
608 BLI_assert(!this->is_single_value());
609 }
610
611 if (texel.x < 0 || texel.y < 0 || texel.x >= domain_.size.x || texel.y >= domain_.size.y) {
612 return fallback;
613 }
614
615 return this->cpu_data().typed<T>()[this->get_pixel_index(texel)];
616}
617
618template<typename T, bool CouldBeSingleValue>
620{
621 return this->load_pixel_fallback<T, CouldBeSingleValue>(texel, T(0));
622}
623
625{
626 float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
627 if (is_single_value_) {
628 this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
629 }
630 else {
631 this->get_cpp_type().copy_assign(this->cpu_data()[this->get_pixel_index(texel)], pixel_value);
632 }
633 return pixel_value;
634}
635
636template<typename T>
637BLI_INLINE_METHOD void Result::store_pixel(const int2 &texel, const T &pixel_value)
638{
639 this->cpu_data().typed<T>()[this->get_pixel_index(texel)] = pixel_value;
640}
641
643 const float4 &pixel_value)
644{
645 this->get_cpp_type().copy_assign(pixel_value, this->cpu_data()[this->get_pixel_index(texel)]);
646}
647
649 const Interpolation &interpolation,
650 const ExtensionMode &mode_x,
651 const ExtensionMode &mode_y) const
652{
653 float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
654 if (is_single_value_) {
655 this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
656 return pixel_value;
657 }
658
659 const int2 size = domain_.size;
660 const float2 texel_coordinates = (interpolation == Interpolation::Nearest) ?
661 coordinates * float2(size) :
662 (coordinates * float2(size)) - 0.5f;
663
664 const float *buffer = static_cast<const float *>(this->cpu_data().data());
665 const math::InterpWrapMode extension_mode_x = map_extension_mode_to_wrap_mode(mode_x);
666 const math::InterpWrapMode extension_mode_y = map_extension_mode_to_wrap_mode(mode_y);
667
668 switch (interpolation) {
671 pixel_value,
672 size.x,
673 size.y,
674 this->channels_count(),
675 texel_coordinates.x,
676 texel_coordinates.y,
677 extension_mode_x,
678 extension_mode_y);
679 break;
682 pixel_value,
683 size.x,
684 size.y,
685 this->channels_count(),
686 texel_coordinates.x,
687 texel_coordinates.y,
688 extension_mode_x,
689 extension_mode_y);
690 break;
694 pixel_value,
695 size.x,
696 size.y,
697 this->channels_count(),
698 texel_coordinates.x,
699 texel_coordinates.y,
700 extension_mode_x,
701 extension_mode_y);
702 break;
703 }
704
705 return pixel_value;
706}
707
709{
710 float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
711 if (is_single_value_) {
712 this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
713 return pixel_value;
714 }
715
716 const int2 size = domain_.size;
717 const float2 texel_coordinates = coordinates * float2(size);
718
719 const float *buffer = static_cast<const float *>(this->cpu_data().data());
721 pixel_value,
722 size.x,
723 size.y,
724 this->channels_count(),
725 texel_coordinates.x,
726 texel_coordinates.y);
727 return pixel_value;
728}
729
731 bool wrap_x,
732 bool wrap_y) const
733{
734 float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
735 if (is_single_value_) {
736 this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
737 return pixel_value;
738 }
739
740 const int2 size = domain_.size;
741 const float2 texel_coordinates = coordinates * float2(size);
742
743 const float *buffer = static_cast<const float *>(this->cpu_data().data());
745 buffer,
746 pixel_value,
747 size.x,
748 size.y,
749 this->channels_count(),
750 texel_coordinates.x,
751 texel_coordinates.y,
754 return pixel_value;
755}
756
758 bool wrap_x,
759 bool wrap_y) const
760{
761 float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
762 if (is_single_value_) {
763 this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
764 return pixel_value;
765 }
766
767 const int2 size = domain_.size;
768 const float2 texel_coordinates = coordinates * float2(size) - 0.5f;
769
770 const float *buffer = static_cast<const float *>(this->cpu_data().data());
772 buffer,
773 pixel_value,
774 size.x,
775 size.y,
776 this->channels_count(),
777 texel_coordinates.x,
778 texel_coordinates.y,
781 return pixel_value;
782}
783
785 bool wrap_x,
786 bool wrap_y) const
787{
788 float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
789 if (is_single_value_) {
790 this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
791 return pixel_value;
792 }
793
794 const int2 size = domain_.size;
795 const float2 texel_coordinates = coordinates * float2(size) - 0.5f;
796
797 const float *buffer = static_cast<const float *>(this->cpu_data().data());
799 buffer,
800 pixel_value,
801 size.x,
802 size.y,
803 this->channels_count(),
804 texel_coordinates.x,
805 texel_coordinates.y,
808 return pixel_value;
809}
810
812{
813 float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
814 if (is_single_value_) {
815 this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
816 return pixel_value;
817 }
818
819 const int2 size = domain_.size;
820 const float2 texel_coordinates = (coordinates * float2(size)) - 0.5f;
821
822 const float *buffer = static_cast<const float *>(this->cpu_data().data());
824 pixel_value,
825 size.x,
826 size.y,
827 this->channels_count(),
828 texel_coordinates.x,
829 texel_coordinates.y);
830 return pixel_value;
831}
832
834{
835 float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
836 if (is_single_value_) {
837 this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
838 return pixel_value;
839 }
840
841 const int2 size = domain_.size;
842 const float2 texel_coordinates = coordinates * float2(size);
843
844 const float *buffer = static_cast<const float *>(this->cpu_data().data());
846 pixel_value,
847 size.x,
848 size.y,
849 this->channels_count(),
850 texel_coordinates.x,
851 texel_coordinates.y);
852 return pixel_value;
853}
854
856{
857 float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
858 if (is_single_value_) {
859 this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
860 return pixel_value;
861 }
862
863 const int2 size = domain_.size;
864 const float2 texel_coordinates = (coordinates * float2(size)) - 0.5f;
865
866 const float *buffer = static_cast<const float *>(this->cpu_data().data());
868 pixel_value,
869 size.x,
870 size.y,
871 this->channels_count(),
872 texel_coordinates.x,
873 texel_coordinates.y);
874 return pixel_value;
875}
876
878{
879 float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
880 if (is_single_value_) {
881 this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
882 return pixel_value;
883 }
884
885 const int2 size = domain_.size;
886 const float2 texel_coordinates = (coordinates * float2(size)) - 0.5f;
887
888 const float *buffer = static_cast<const float *>(this->cpu_data().data());
890 pixel_value,
891 size.x,
892 size.y,
893 this->channels_count(),
894 texel_coordinates.x,
895 texel_coordinates.y);
896 return pixel_value;
897}
898
903static void sample_ewa_extended_read_callback(void *userdata, int x, int y, float result[4])
904{
905 const Result *input = static_cast<const Result *>(userdata);
906 const float4 sampled_result = input->load_pixel_extended<float4>(int2(x, y));
907 copy_v4_v4(result, sampled_result);
908}
909
911 const float2 &x_gradient,
912 const float2 &y_gradient) const
913{
915
916 float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
917 if (is_single_value_) {
918 this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
919 return pixel_value;
920 }
921
922 const int2 size = domain_.size;
924 size.y,
925 false,
926 true,
927 coordinates,
928 x_gradient,
929 y_gradient,
931 const_cast<Result *>(this),
932 pixel_value);
933 return pixel_value;
934}
935
940static void sample_ewa_zero_read_callback(void *userdata, int x, int y, float result[4])
941{
942 const Result *input = static_cast<const Result *>(userdata);
943 const float4 sampled_result = input->load_pixel_zero<float4>(int2(x, y));
944 copy_v4_v4(result, sampled_result);
945}
946
948 const float2 &x_gradient,
949 const float2 &y_gradient) const
950{
952
953 float4 pixel_value = float4(0.0f, 0.0f, 0.0f, 1.0f);
954 if (is_single_value_) {
955 this->get_cpp_type().copy_assign(this->cpu_data().data(), pixel_value);
956 return pixel_value;
957 }
958
959 const int2 size = domain_.size;
961 size.y,
962 false,
963 true,
964 coordinates,
965 x_gradient,
966 y_gradient,
968 const_cast<Result *>(this),
969 pixel_value);
970 return pixel_value;
971}
972
973BLI_INLINE_METHOD int64_t Result::get_pixel_index(const int2 &texel) const
974{
975 BLI_assert(!is_single_value_);
976 BLI_assert(this->is_allocated());
977 BLI_assert(texel.x >= 0 && texel.y >= 0 && texel.x < domain_.size.x && texel.y < domain_.size.y);
978 return int64_t(texel.y) * domain_.size.x + texel.x;
979}
980
981} // namespace blender::compositor
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE_METHOD
void BLI_ewa_filter(int width, int height, bool intpol, bool use_alpha, const float uv[2], const float du[2], const float dv[2], ewa_filter_read_pixel_cb read_pixel_cb, void *userdata, float result[4])
MINLINE void copy_v4_v4(float r[4], const float a[4])
eGPUDataFormat
BMesh const char void * data
long long int int64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
void copy_assign(const void *src, void *dst) const
Span< T > typed() const
const void * data() const
static Domain identity()
Definition domain.cc:36
float4 sample_nearest_extended(const float2 &coordinates) const
void decrement_reference_count(int count=1)
Definition result.cc:650
float4 sample_cubic_wrap(const float2 &coordinates, bool wrap_x, bool wrap_y) const
void share_data(const Result &source)
Definition result.cc:523
RealizationOptions & get_realization_options()
Definition result.cc:630
Result upload_to_gpu(const bool from_pool) const
Definition result.cc:462
float4 sample_nearest_wrap(const float2 &coordinates, bool wrap_x, bool wrap_y) const
const CPPType & get_cpp_type() const
Definition result.cc:374
T get_single_value_default(const T &default_value) const
void store_pixel(const int2 &texel, const T &pixel_value)
int reference_count() const
Definition result.cc:775
void allocate_texture(const Domain domain, const bool from_pool=true, const std::optional< ResultStorageType > storage_type=std::nullopt)
Definition result.cc:389
static eGPUDataFormat gpu_data_format(const ResultType type)
Definition result.cc:136
int64_t size_in_bytes() const
Definition result.cc:780
float4 sample_nearest_zero(const float2 &coordinates) const
void wrap_external(blender::gpu::Texture *texture)
Definition result.cc:584
void unbind_as_texture() const
Definition result.cc:511
Result download_to_cpu() const
Definition result.cc:474
static blender::gpu::TextureFormat gpu_texture_format(ResultType type, ResultPrecision precision)
Definition result.cc:65
static const char * type_name(const ResultType type)
Definition result.cc:340
float4 sample_ewa_extended(const float2 &coordinates, const float2 &x_gradient, const float2 &y_gradient) const
bool is_allocated() const
Definition result.cc:763
float4 sample_cubic_extended(const float2 &coordinates) const
void set_transformation(const float3x3 &transformation)
Definition result.cc:620
T load_pixel_zero(const int2 &texel) const
static bool is_single_value_only_type(ResultType type)
Definition result.cc:44
float4 sample_bilinear_zero(const float2 &coordinates) const
float4 sample(const float2 &coordinates, const Interpolation &interpolation, const ExtensionMode &extend_mode_x, const ExtensionMode &extend_mode_y) const
GPointer single_value() const
Definition result.cc:790
blender::gpu::Texture * gpu_texture_
blender::gpu::TextureFormat get_gpu_texture_format() const
Definition result.cc:379
eGPUDataFormat get_gpu_data_format() const
Definition result.cc:384
void set_single_value(const T &value)
static ResultType type(blender::gpu::TextureFormat format)
Definition result.cc:261
void bind_as_texture(gpu::Shader *shader, const char *texture_name) const
Definition result.cc:487
ResultPrecision precision() const
Definition result.cc:739
float4 sample_bilinear_wrap(const float2 &coordinates, bool wrap_x, bool wrap_y) const
ResultType type() const
Definition result.cc:734
blender::gpu::Texture * gpu_texture() const
void store_pixel_generic_type(const int2 &texel, const float4 &pixel_value)
T load_pixel_extended(const int2 &texel) const
static ResultType float_type(const int channels_count)
Definition result.cc:292
void set_precision(ResultPrecision precision)
Definition result.cc:751
void transform(const float3x3 &transformation)
Definition result.cc:625
void increment_reference_count(int count=1)
Definition result.cc:645
const Domain & domain() const
static const CPPType & cpp_type(const ResultType type)
Definition result.cc:311
T load_pixel(const int2 &texel) const
int64_t channels_count() const
void unbind_as_image() const
Definition result.cc:517
T load_pixel_fallback(const int2 &texel, const T &fallback) const
static ResultPrecision precision(blender::gpu::TextureFormat format)
Definition result.cc:233
Result(Context &context)
Definition result.cc:32
float4 sample_ewa_zero(const float2 &coordinates, const float2 &x_gradient, const float2 &y_gradient) const
DerivedResources & derived_resources()
Definition result.cc:726
float4 sample_bilinear_extended(const float2 &coordinates) const
float4 load_pixel_generic_type(const int2 &texel) const
void bind_as_image(gpu::Shader *shader, const char *image_name, bool read=false) const
Definition result.cc:498
void set_reference_count(int count)
Definition result.cc:640
bool is_single_value() const
Definition result.cc:758
void steal_data(Result &source)
Definition result.cc:540
const T & get_single_value() const
void set_type(ResultType type)
Definition result.cc:744
#define input
TEX_TEMPLATE DataVec texture(T, FltCoord, float=0.0f) RET
int count
format
#define T
static void sample_ewa_extended_read_callback(void *userdata, int x, int y, float result[4])
static void sample_ewa_zero_read_callback(void *userdata, int x, int y, float result[4])
math::InterpWrapMode map_extension_mode_to_wrap_mode(const ExtensionMode &mode)
Definition domain.cc:56
T clamp(const T &a, const T &min, const T &max)
void interpolate_nearest_fl(const float *buffer, float *output, int width, int height, int components, float u, float v)
void interpolate_nearest_border_fl(const float *buffer, float *output, int width, int height, int components, float u, float v)
float4 interpolate_bilinear_border_fl(const float *buffer, int width, int height, float u, float v)
float4 interpolate_cubic_bspline_fl(const float *buffer, int width, int height, float u, float v)
void interpolate_nearest_wrapmode_fl(const float *buffer, float *output, int width, int height, int components, float u, float v, InterpWrapMode wrap_u, InterpWrapMode wrap_v)
float4 interpolate_bilinear_fl(const float *buffer, int width, int height, float u, float v)
void interpolate_bilinear_wrapmode_fl(const float *buffer, float *output, int width, int height, int components, float u, float v, InterpWrapMode wrap_u, InterpWrapMode wrap_v)
void interpolate_cubic_bspline_wrapmode_fl(const float *buffer, float *output, int width, int height, int components, float u, float v, InterpWrapMode wrap_u, InterpWrapMode wrap_v)
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
MatBase< float, 3, 3 > float3x3