Node: Gabor Noise Texture #110802

Open
Charlie Jolly wants to merge 68 commits from CharlieJolly/blender:gabor into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
34 changed files with 400 additions and 324 deletions
Showing only changes of commit 38c607df96 - Show all commits

View File

@ -473,7 +473,7 @@ extern "C" int GHOST_HACK_getFirstFile(char buf[FIRSTFILEBUFLG])
if (![closing_window isKeyWindow]) {
/* If the window wasn't key then its either none of the windows are key or another window
* is a key. The former situation is a bit strange, but probably forcin a key window is not
* is a key. The former situation is a bit strange, but probably forcing a key window is not
* something desirable. The latter situation is when we definitely do not want to change the
* key window.
*

View File

@ -4701,14 +4701,9 @@ static void output_handle_done(void *data, wl_output * /*wl_output*/)
CLOG_INFO(LOG, 2, "done");
GWL_Output *output = static_cast<GWL_Output *>(data);
int32_t size_native[2];
if (output->transform & WL_OUTPUT_TRANSFORM_90) {
size_native[0] = output->size_native[1];
size_native[1] = output->size_native[0];
}
else {
size_native[0] = output->size_native[0];
size_native[1] = output->size_native[1];
int32_t size_native[2] = {UNPACK2(output->size_native)};
if (ELEM(output->transform, WL_OUTPUT_TRANSFORM_90, WL_OUTPUT_TRANSFORM_270)) {
std::swap(size_native[0], size_native[1]);
}
/* If `xdg-output` is present, calculate the true scale of the desktop */
@ -5684,6 +5679,12 @@ GHOST_SystemWayland::GHOST_SystemWayland(bool background)
}
}
/* Without this, the output fractional size from `display->xdg.output_manager` isn't known,
* while this isn't essential, the first window creation uses this for setting the size.
* Supporting both XDG initialized/uninitialized outputs is possible it complicates logic.
* see: #113328 for an example of size on startup issues. */
wl_display_roundtrip(display_->wl.display);
#ifdef USE_EVENT_BACKGROUND_THREAD
gwl_display_event_thread_create(display_);
@ -6300,8 +6301,13 @@ void GHOST_SystemWayland::getMainDisplayDimensions(uint32_t &width, uint32_t &he
if (!display_->outputs.empty()) {
/* We assume first output as main. */
width = uint32_t(display_->outputs[0]->size_native[0]);
height = uint32_t(display_->outputs[0]->size_native[1]);
const GWL_Output *output = display_->outputs[0];
int32_t size_native[2] = {UNPACK2(output->size_native)};
if (ELEM(output->transform, WL_OUTPUT_TRANSFORM_90, WL_OUTPUT_TRANSFORM_270)) {
std::swap(size_native[0], size_native[1]);
}
width = uint32_t(size_native[0]);
height = uint32_t(size_native[1]);
}
}
@ -6316,14 +6322,18 @@ void GHOST_SystemWayland::getAllDisplayDimensions(uint32_t &width, uint32_t &hei
for (const GWL_Output *output : display_->outputs) {
int32_t xy[2] = {0, 0};
int32_t size_native[2] = {UNPACK2(output->size_native)};
if (output->has_position_logical) {
xy[0] = output->position_logical[0];
xy[1] = output->position_logical[1];
}
if (ELEM(output->transform, WL_OUTPUT_TRANSFORM_90, WL_OUTPUT_TRANSFORM_270)) {
std::swap(size_native[0], size_native[1]);
}
xy_min[0] = std::min(xy_min[0], xy[0]);
xy_min[1] = std::min(xy_min[1], xy[1]);
xy_max[0] = std::max(xy_max[0], xy[0] + output->size_native[0]);
xy_max[1] = std::max(xy_max[1], xy[1] + output->size_native[1]);
xy_max[0] = std::max(xy_max[0], xy[0] + size_native[0]);
xy_max[1] = std::max(xy_max[1], xy[1] + size_native[1]);
}
width = xy_max[0] - xy_min[0];

View File

@ -1122,38 +1122,22 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind
/* Warp within bounds. */
{
GHOST_Rect bounds;
int32_t bounds_margin = 0;
GHOST_TAxisFlag bounds_axis = GHOST_kAxisNone;
if (window->getCursorGrabMode() == GHOST_kGrabHide) {
if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
/* Use custom grab bounds if available, window bounds if not. */
if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
window->getClientBounds(bounds);
window->getClientBounds(bounds);
}
/* WARNING(@ideasman42): The current warping logic fails to warp on every event,
* so the box needs to small enough not to let the cursor escape the window but large
* enough that the cursor isn't being warped every time.
* If this was not the case it would be less trouble to simply warp the cursor to the
* center of the screen on every motion, see: D16558 (alternative fix for #102346). */
const int32_t subregion_div = 4; /* One quarter of the region. */
const int32_t size[2] = {bounds.getWidth(), bounds.getHeight()};
const int32_t center[2] = {(bounds.m_l + bounds.m_r) / 2, (bounds.m_t + bounds.m_b) / 2};
/* Shrink the box to prevent the cursor escaping. */
bounds.m_l = center[0] - (size[0] / (subregion_div * 2));
bounds.m_r = center[0] + (size[0] / (subregion_div * 2));
bounds.m_t = center[1] - (size[1] / (subregion_div * 2));
bounds.m_b = center[1] + (size[1] / (subregion_div * 2));
}
bounds_axis = GHOST_TAxisFlag(GHOST_kAxisX | GHOST_kAxisY);
}
else {
/* Fallback to window bounds. */
if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) {
window->getClientBounds(bounds);
}
bounds_margin = 2;
bounds_axis = window->getCursorGrabAxis();
}
/* WARNING(@ideasman42): The current warping logic fails to warp on every event,
* so the box needs to small enough not to let the cursor escape the window but large
* enough that the cursor isn't being warped every time. If this was not the case it
* would be less trouble to simply warp the cursor to the center of the screen on
* every motion, see: D16558 (alternative fix for #102346). */
/* Rather than adjust the bounds, use a margin based on the bounds width. */
int32_t bounds_margin = (window->getCursorGrabMode() == GHOST_kGrabHide) ?
bounds.getWidth() / 10 :
2;
GHOST_TAxisFlag bounds_axis = window->getCursorGrabAxis();
/* Could also clamp to screen bounds wrap with a window outside the view will
* fail at the moment. Use inset in case the window is at screen bounds. */

View File

@ -101,10 +101,10 @@ void BLF_color3fv_alpha(int fontid, const float rgb[3], float alpha);
/**
* Set a 4x4 matrix to be multiplied before draw the text.
* Remember that you need call BLF_enable(BLF_MATRIX)
* Remember that you need call `BLF_enable(BLF_MATRIX)`
* to enable this.
*
* The order of the matrix is like GL:
* The order of the matrix is column major (following the GPU module):
* \code{.unparsed}
* | m[0] m[4] m[8] m[12] |
* | m[1] m[5] m[9] m[13] |

View File

@ -7,7 +7,7 @@
*
* Main BlenFont (BLF) API, public functions for font handling.
*
* Wraps OpenGL and FreeType.
* Wraps GPU display and FreeType.
*/
#include <cmath>
@ -521,7 +521,7 @@ void BLF_batch_draw_end()
g_batch.enabled = false;
}
static void blf_draw_gl__start(const FontBLF *font)
static void blf_draw_gpu__start(const FontBLF *font)
{
/*
* The pixmap alignment hack is handle
@ -549,7 +549,7 @@ static void blf_draw_gl__start(const FontBLF *font)
}
}
static void blf_draw_gl__end(const FontBLF *font)
static void blf_draw_gpu__end(const FontBLF *font)
{
if ((font->flags & (BLF_ROTATION | BLF_MATRIX | BLF_ASPECT)) != 0) {
GPU_matrix_pop();
@ -563,14 +563,14 @@ void BLF_draw_ex(int fontid, const char *str, const size_t str_len, ResultBLF *r
BLF_RESULT_CHECK_INIT(r_info);
if (font) {
blf_draw_gl__start(font);
blf_draw_gpu__start(font);
if (font->flags & BLF_WORD_WRAP) {
blf_font_draw__wrap(font, str, str_len, r_info);
}
else {
blf_font_draw(font, str, str_len, r_info);
}
blf_draw_gl__end(font);
blf_draw_gpu__end(font);
}
}
void BLF_draw(int fontid, const char *str, const size_t str_len)
@ -595,9 +595,9 @@ int BLF_draw_mono(int fontid, const char *str, const size_t str_len, int cwidth,
int columns = 0;
if (font) {
blf_draw_gl__start(font);
blf_draw_gpu__start(font);
columns = blf_font_draw_mono(font, str, str_len, cwidth, tab_columns);
blf_draw_gl__end(font);
blf_draw_gpu__end(font);
}
return columns;

View File

@ -258,7 +258,7 @@ typedef struct FontBLF {
/**
* Multiplied this matrix with the current one before draw the text!
* see #blf_draw_gl__start.
* see #blf_draw_gpu__start.
*/
float m[16];

View File

@ -1575,12 +1575,12 @@ static ID *lib_override_root_find(Main *bmain, ID *id, const int curr_level, int
}
if (entry->tags & MAINIDRELATIONS_ENTRY_TAGS_INPROGRESS) {
/* Re-processing an entry already being processed higher in the callgraph (re-entry caused by a
* dependency loops). Just do nothing, there is no more usefull info to provide here. */
/* Re-processing an entry already being processed higher in the call-graph (re-entry caused by
* a dependency loops). Just do nothing, there is no more useful info to provide here. */
return nullptr;
}
/* Flag this entry to avoid re-processing it in case some dependency loop leads to it again
* downwards in the callstack. */
* downwards in the call-stack. */
entry->tags |= MAINIDRELATIONS_ENTRY_TAGS_INPROGRESS;
int best_level_candidate = curr_level;

View File

@ -67,17 +67,21 @@ set(SRC
algorithms/intern/morphological_distance.cc
algorithms/intern/morphological_distance_feather.cc
algorithms/intern/parallel_reduction.cc
algorithms/intern/realize_on_domain.cc
algorithms/intern/smaa.cc
algorithms/intern/summed_area_table.cc
algorithms/intern/symmetric_separable_blur.cc
algorithms/intern/transform.cc
algorithms/COM_algorithm_jump_flooding.hh
algorithms/COM_algorithm_morphological_distance.hh
algorithms/COM_algorithm_morphological_distance_feather.hh
algorithms/COM_algorithm_parallel_reduction.hh
algorithms/COM_algorithm_realize_on_domain.hh
algorithms/COM_algorithm_smaa.hh
algorithms/COM_algorithm_summed_area_table.hh
algorithms/COM_algorithm_symmetric_separable_blur.hh
algorithms/COM_algorithm_transform.hh
cached_resources/intern/cached_mask.cc
cached_resources/intern/cached_texture.cc

View File

@ -16,14 +16,6 @@ namespace blender::realtime_compositor {
struct InputRealizationOptions {
/* The input should be realized on the operation domain of the operation. */
bool realize_on_operation_domain : 1;
/* The input should be realized on a domain that is identical to the domain of the input but with
* an identity rotation and an increased size that completely fits the image after rotation. This
* is useful for operations that are not rotation invariant. */
bool realize_rotation : 1;
/* The input should be realized on a domain that is identical to the domain of the input but with
* an identity scale and an increased/decreased size that completely fits the image after
* scaling. This is useful for operations that are not scale invariant. */
bool realize_scale : 1;
};
/* ------------------------------------------------------------------------------------------------

View File

@ -42,40 +42,6 @@ class RealizeOnDomainOperation : public SimpleOperation {
protected:
/* The operation domain is just the target domain. */
Domain compute_domain() override;
private:
/* Get the realization shader of the appropriate type. */
GPUShader *get_realization_shader();
};
/* ------------------------------------------------------------------------------------------------
* Realize Transformation Operation
*
* A simple operation that realizes its input on a domain such that its transformations become
* identity and its size is increased/decreased to adapt to the new domain. The transformations
* that become identity are the ones marked to be realized in the given InputRealizationOptions.
* For instance, if InputRealizationOptions.realize_rotation is true and the input is rotated, the
* output of the operation will be a result of zero rotation but expanded size to account for the
* bounding box of the input after rotation. This is useful for operations that are not rotation
* invariant and thus require an input of zero rotation for correct operation.
*
* Notice that this class is not an actual operation, but constructs a RealizeOnDomainOperation
* with the appreciate domain in its construct_if_needed static constructor. */
class RealizeTransformationOperation {
public:
/* Determine if a realize transformation operation is needed for the input with the given result
* and descriptor. If it is not needed, return a null pointer. If it is needed, return an
* instance of RealizeOnDomainOperation with the appropriate domain. */
static SimpleOperation *construct_if_needed(Context &context,
const Result &input_result,
const InputDescriptor &input_descriptor);
private:
/* Given the domain of an input and its realization options, compute a domain such that the
* appropriate transformations specified in the realization options become identity and the size
* of the domain is increased/reduced to adapt to the new domain. */
static Domain compute_target_domain(const Domain &input_domain,
const InputRealizationOptions &realization_options);
};
} // namespace blender::realtime_compositor

View File

@ -0,0 +1,28 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_math_matrix_types.hh"
#include "COM_context.hh"
#include "COM_domain.hh"
#include "COM_result.hh"
namespace blender::realtime_compositor {
/* Projects the input on a target domain, copies the area of the input that intersects the target
* domain, and fill the rest with zeros or repetitions of the input depending on the realization
* options. The transformation and realization options of the input are ignored and the given
* input_transformation and realization_options are used instead to allow the caller to change them
* without mutating the input result directly. See the discussion in COM_domain.hh for more
* information on what realization on domain means. */
void realize_on_domain(Context &context,
Result &input,
Result &output,
const Domain &domain,
const float3x3 &input_transformation,
const RealizationOptions &realization_options);
} // namespace blender::realtime_compositor

View File

@ -0,0 +1,33 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "BLI_math_matrix_types.hh"
#include "COM_context.hh"
#include "COM_domain.hh"
#include "COM_result.hh"
namespace blender::realtime_compositor {
/* Transforms the given result based on the given transformation and interpolation, writing the
* transformed result to the given output.
*
* The rotation and scale components of the transformation are realized and the size of the result
* is increased/reduced to adapt to the new transformation. For instance, if the transformation is
* a rotation, the input will be rotated and expanded in size to account for the bounding box of
* the input after rotation. The size of the returned result is bound and clipped by the maximum
* possible GPU texture size to avoid allocations that surpass hardware limits, which is typically
* 16k.
*
* The translation component of the transformation is delayed and only stored in the domain of the
* result to be realized later when needed. */
void transform(Context &context,
Result &input,
Result &output,
float3x3 transformation,
Interpolation interpolation);
} // namespace blender::realtime_compositor

View File

@ -0,0 +1,113 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_angle_types.hh"
#include "BLI_math_matrix.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_utildefines.h"
#include "GPU_capabilities.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
#include "COM_context.hh"
#include "COM_domain.hh"
#include "COM_result.hh"
#include "COM_utilities.hh"
#include "COM_algorithm_realize_on_domain.hh"
namespace blender::realtime_compositor {
static const char *get_realization_shader(Result &input,
const RealizationOptions &realization_options)
{
if (realization_options.interpolation == Interpolation::Bicubic) {
switch (input.type()) {
case ResultType::Color:
return "compositor_realize_on_domain_bicubic_color";
case ResultType::Vector:
return "compositor_realize_on_domain_bicubic_vector";
case ResultType::Float:
return "compositor_realize_on_domain_bicubic_float";
case ResultType::Int2:
/* Realization does not support integer images. */
break;
}
}
else {
switch (input.type()) {
case ResultType::Color:
return "compositor_realize_on_domain_color";
case ResultType::Vector:
return "compositor_realize_on_domain_vector";
case ResultType::Float:
return "compositor_realize_on_domain_float";
case ResultType::Int2:
/* Realization does not support integer images. */
break;
}
}
BLI_assert_unreachable();
return nullptr;
}
void realize_on_domain(Context &context,
Result &input,
Result &output,
const Domain &domain,
const float3x3 &input_transformation,
const RealizationOptions &realization_options)
{
GPUShader *shader = context.shader_manager().get(
get_realization_shader(input, realization_options));
GPU_shader_bind(shader);
/* Transform the input space into the domain space. */
const float3x3 local_transformation = math::invert(domain.transformation) * input_transformation;
/* Set the origin of the transformation to be the center of the domain. */
const float3x3 transformation = math::from_origin_transform<float3x3>(
local_transformation, float2(domain.size) / 2.0f);
/* Invert the transformation because the shader transforms the domain coordinates instead of the
* input image itself and thus expect the inverse. */
const float3x3 inverse_transformation = math::invert(transformation);
GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", inverse_transformation.ptr());
/* The texture sampler should use bilinear interpolation for both the bilinear and bicubic
* cases, as the logic used by the bicubic realization shader expects textures to use bilinear
* interpolation. */
const bool use_bilinear = ELEM(
realization_options.interpolation, Interpolation::Bilinear, Interpolation::Bicubic);
GPU_texture_filter_mode(input.texture(), use_bilinear);
/* If the input repeats, set a repeating wrap mode for out-of-bound texture access. Otherwise,
* make out-of-bound texture access return zero by setting a clamp to border extend mode. */
GPU_texture_extend_mode_x(input.texture(),
realization_options.repeat_x ?
GPU_SAMPLER_EXTEND_MODE_REPEAT :
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
GPU_texture_extend_mode_y(input.texture(),
realization_options.repeat_y ?
GPU_SAMPLER_EXTEND_MODE_REPEAT :
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
input.bind_as_texture(shader, "input_tx");
output.allocate_texture(domain);
output.bind_as_image(shader, "domain_img");
compute_dispatch_threads_at_least(shader, domain.size);
input.unbind_as_texture();
output.unbind_as_image();
GPU_shader_unbind();
}
} // namespace blender::realtime_compositor

View File

@ -0,0 +1,88 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_math_angle_types.hh"
#include "BLI_math_matrix.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
#include "GPU_capabilities.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
#include "COM_algorithm_realize_on_domain.hh"
#include "COM_context.hh"
#include "COM_domain.hh"
#include "COM_result.hh"
#include "COM_algorithm_transform.hh"
namespace blender::realtime_compositor {
/* Given a potentially transformed domain, compute a domain such that its rotation and scale become
* identity and the size of the domain is increased/reduced to adapt to the new transformation. For
* instance, if the domain is rotated, the returned domain will have zero rotation but expanded
* size to account for the bounding box of the domain after rotation. The size of the returned
* domain is bound and clipped by the maximum possible GPU texture size to avoid allocations that
* surpass hardware limits, which is typically 16k. */
static Domain compute_realized_transformation_domain(const Domain &domain)
{
math::AngleRadian rotation;
float2 translation, scale;
float2 size = float2(domain.size);
math::to_loc_rot_scale(domain.transformation, translation, rotation, scale);
/* Set the rotation to zero and expand the domain size to fit the bounding box of the rotated
* result. */
const float sine = math::abs(math::sin(rotation));
const float cosine = math::abs(math::cos(rotation));
size = float2(size.x * sine + size.y * cosine, size.x * cosine + size.y * sine);
rotation = 0.0f;
/* Set the scale to 1 and scale the domain size to adapt to the new domain. */
size *= scale;
scale = float2(1.0f);
const float3x3 transformation = math::from_loc_rot_scale<float3x3>(translation, rotation, scale);
const int2 domain_size = math::clamp(
int2(math::round(size)), int2(1), int2(GPU_max_texture_size()));
return Domain(domain_size, transformation);
}
void transform(Context &context,
Result &input,
Result &output,
float3x3 transformation,
Interpolation interpolation)
{
math::AngleRadian rotation;
float2 translation, scale;
math::to_loc_rot_scale(transformation, translation, rotation, scale);
/* Rotation and scale transformations are immediately realized. */
if (rotation != 0.0f || scale != float2(1.0f)) {
RealizationOptions realization_options = input.get_realization_options();
realization_options.interpolation = interpolation;
Domain input_domain = input.domain();
input_domain.transform(transformation);
const Domain target_domain = compute_realized_transformation_domain(input_domain);
realize_on_domain(
context, input, output, target_domain, input_domain.transformation, realization_options);
}
else {
input.pass_through(output);
}
/* Translation transformations are delayed and are only stored in the result. */
const float3x3 translation_matrix = math::from_location<float3x3>(translation);
output.transform(translation_matrix);
output.get_realization_options().interpolation = interpolation;
}
} // namespace blender::realtime_compositor

View File

@ -106,12 +106,6 @@ void Operation::add_and_evaluate_input_processors()
add_and_evaluate_input_processor(identifier, conversion);
}
for (const StringRef &identifier : results_mapped_to_inputs_.keys()) {
SimpleOperation *realize_transformation = RealizeTransformationOperation::construct_if_needed(
context(), get_input(identifier), get_input_descriptor(identifier));
add_and_evaluate_input_processor(identifier, realize_transformation);
}
for (const StringRef &identifier : results_mapped_to_inputs_.keys()) {
SimpleOperation *realize_on_domain = RealizeOnDomainOperation::construct_if_needed(
context(), get_input(identifier), get_input_descriptor(identifier), compute_domain());

View File

@ -2,22 +2,13 @@
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_assert.h"
#include "BLI_math_angle_types.hh"
#include "BLI_math_matrix.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_utildefines.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
#include "COM_algorithm_realize_on_domain.hh"
#include "COM_context.hh"
#include "COM_domain.hh"
#include "COM_input_descriptor.hh"
#include "COM_realize_on_domain_operation.hh"
#include "COM_result.hh"
#include "COM_utilities.hh"
#include "COM_realize_on_domain_operation.hh"
namespace blender::realtime_compositor {
@ -38,90 +29,12 @@ RealizeOnDomainOperation::RealizeOnDomainOperation(Context &context,
void RealizeOnDomainOperation::execute()
{
Result &input = get_input();
Result &result = get_result();
result.allocate_texture(domain_);
GPUShader *shader = get_realization_shader();
GPU_shader_bind(shader);
/* Transform the input space into the domain space. */
const float3x3 local_transformation = math::invert(domain_.transformation) *
input.domain().transformation;
/* Set the origin of the transformation to be the center of the domain. */
const float3x3 transformation = math::from_origin_transform<float3x3>(
local_transformation, float2(domain_.size) / 2.0f);
/* Invert the transformation because the shader transforms the domain coordinates instead of the
* input image itself and thus expect the inverse. */
const float3x3 inverse_transformation = math::invert(transformation);
GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", inverse_transformation.ptr());
/* The texture sampler should use bilinear interpolation for both the bilinear and bicubic
* cases, as the logic used by the bicubic realization shader expects textures to use bilinear
* interpolation. */
const bool use_bilinear = ELEM(input.get_realization_options().interpolation,
Interpolation::Bilinear,
Interpolation::Bicubic);
GPU_texture_filter_mode(input.texture(), use_bilinear);
/* If the input repeats, set a repeating wrap mode for out-of-bound texture access. Otherwise,
* make out-of-bound texture access return zero by setting a clamp to border extend mode. */
GPU_texture_extend_mode_x(input.texture(),
input.get_realization_options().repeat_x ?
GPU_SAMPLER_EXTEND_MODE_REPEAT :
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
GPU_texture_extend_mode_y(input.texture(),
input.get_realization_options().repeat_y ?
GPU_SAMPLER_EXTEND_MODE_REPEAT :
GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER);
input.bind_as_texture(shader, "input_tx");
result.bind_as_image(shader, "domain_img");
compute_dispatch_threads_at_least(shader, domain_.size);
input.unbind_as_texture();
result.unbind_as_image();
GPU_shader_unbind();
}
GPUShader *RealizeOnDomainOperation::get_realization_shader()
{
if (get_input().get_realization_options().interpolation == Interpolation::Bicubic) {
switch (get_result().type()) {
case ResultType::Color:
return shader_manager().get("compositor_realize_on_domain_bicubic_color");
case ResultType::Vector:
return shader_manager().get("compositor_realize_on_domain_bicubic_vector");
case ResultType::Float:
return shader_manager().get("compositor_realize_on_domain_bicubic_float");
default:
/* Other types are internal and needn't be handled by operations. */
BLI_assert_unreachable();
return nullptr;
}
}
else {
switch (get_result().type()) {
case ResultType::Color:
return shader_manager().get("compositor_realize_on_domain_color");
case ResultType::Vector:
return shader_manager().get("compositor_realize_on_domain_vector");
case ResultType::Float:
return shader_manager().get("compositor_realize_on_domain_float");
default:
/* Other types are internal and needn't be handled by operations. */
BLI_assert_unreachable();
return nullptr;
}
}
BLI_assert_unreachable();
return nullptr;
realize_on_domain(context(),
get_input(),
get_result(),
domain_,
get_input().domain().transformation,
get_input().get_realization_options());
}
Domain RealizeOnDomainOperation::compute_domain()
@ -161,68 +74,4 @@ SimpleOperation *RealizeOnDomainOperation::construct_if_needed(
return new RealizeOnDomainOperation(context, operation_domain, input_descriptor.type);
}
/* ------------------------------------------------------------------------------------------------
* Realize Transformation Operation
*/
Domain RealizeTransformationOperation::compute_target_domain(
const Domain &input_domain, const InputRealizationOptions &realization_options)
{
if (!realization_options.realize_rotation && !realization_options.realize_scale) {
return input_domain;
}
math::AngleRadian rotation;
float2 translation, scale;
float2 size = float2(input_domain.size);
math::to_loc_rot_scale(input_domain.transformation, translation, rotation, scale);
/* Set the rotation to zero and expand the domain size to fit the bounding box of the rotated
* result. */
if (realization_options.realize_rotation) {
const float sine = math::abs(math::sin(rotation));
const float cosine = math::abs(math::cos(rotation));
size = float2(size.x * sine + size.y * cosine, size.x * cosine + size.y * sine);
rotation = 0.0f;
}
/* Set the scale to 1 and scale the domain size to adapt to the new domain. */
if (realization_options.realize_scale) {
size *= scale;
scale = float2(1.0f);
}
const float3x3 transformation = math::from_loc_rot_scale<float3x3>(translation, rotation, scale);
return Domain(int2(math::ceil(size)), transformation);
}
SimpleOperation *RealizeTransformationOperation::construct_if_needed(
Context &context, const Result &input_result, const InputDescriptor &input_descriptor)
{
/* The input expects a single value and if no single value is provided, it will be ignored and a
* default value will be used, so no need to realize it and the operation is not needed. */
if (input_descriptor.expects_single_value) {
return nullptr;
}
/* Input result is a single value and does not need realization, the operation is not needed. */
if (input_result.is_single_value()) {
return nullptr;
}
const Domain target_domain = compute_target_domain(input_result.domain(),
input_descriptor.realization_options);
/* The input have an identical domain to the target domain, either because the input doesn't need
* to realize its transformations or because it has identity transformations, so no need to
* realize it and the operation is not needed. */
if (target_domain == input_result.domain()) {
return nullptr;
}
/* Otherwise, realization on the target domain is needed. */
return new RealizeOnDomainOperation(context, target_domain, input_descriptor.type);
}
} // namespace blender::realtime_compositor

View File

@ -129,12 +129,6 @@ InputDescriptor input_descriptor_from_input_socket(const bNodeSocket *socket)
input_descriptor.realization_options.realize_on_operation_domain = bool(
socket_declaration->compositor_realization_options() &
CompositorInputRealizationOptions::RealizeOnOperationDomain);
input_descriptor.realization_options.realize_rotation = bool(
socket_declaration->compositor_realization_options() &
CompositorInputRealizationOptions::RealizeRotation);
input_descriptor.realization_options.realize_scale = bool(
socket_declaration->compositor_realization_options() &
CompositorInputRealizationOptions::RealizeScale);
return input_descriptor;
}

View File

@ -1099,7 +1099,8 @@ PassMain::Sub *CapturePipeline::surface_material_add(::Material *blender_mat, GP
PassMain::Sub &sub_pass = surface_ps_.sub(GPU_material_get_name(gpumat));
GPUPass *gpupass = GPU_material_get_pass(gpumat);
sub_pass.shader_set(GPU_pass_shader_get(gpupass));
sub_pass.push_constant("double_sided", !(blender_mat->blend_flag & MA_BL_CULL_BACKFACE_PROBE));
sub_pass.push_constant("is_double_sided",
!(blender_mat->blend_flag & MA_BL_CULL_BACKFACE_PROBE));
return &sub_pass;
}

View File

@ -60,7 +60,7 @@ float lightprobe_planar_score(ProbePlanarData planar, vec3 P, vec3 V, vec3 L)
#ifdef PLANAR_PROBES
/**
* Return the best planar probe index for a given light direction vector and postion.
* Return the best planar probe index for a given light direction vector and position.
*/
int lightprobe_planar_select(vec3 P, vec3 V, vec3 L)
{

View File

@ -53,10 +53,10 @@ void main()
surfel_buf[surfel_id].radiance_direct.front.rgb = g_emission;
surfel_buf[surfel_id].radiance_direct.front.a = 0.0;
/* TODO(fclem): 2nd surface evaluation. */
surfel_buf[surfel_id].albedo_back = double_sided ? albedo : vec3(0);
surfel_buf[surfel_id].radiance_direct.back.rgb = double_sided ? g_emission : vec3(0);
surfel_buf[surfel_id].albedo_back = is_double_sided ? albedo : vec3(0);
surfel_buf[surfel_id].radiance_direct.back.rgb = is_double_sided ? g_emission : vec3(0);
surfel_buf[surfel_id].radiance_direct.back.a = 0.0;
surfel_buf[surfel_id].double_sided = double_sided;
surfel_buf[surfel_id].double_sided = is_double_sided;
if (!capture_info_buf.capture_emission) {
surfel_buf[surfel_id].radiance_direct.front.rgb = vec3(0.0);

View File

@ -182,7 +182,7 @@ GPU_SHADER_CREATE_INFO(eevee_surf_capture)
.define("MAT_CAPTURE")
.storage_buf(SURFEL_BUF_SLOT, Qualifier::WRITE, "Surfel", "surfel_buf[]")
.storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ_WRITE, "CaptureInfoData", "capture_info_buf")
.push_constant(Type::BOOL, "double_sided")
.push_constant(Type::BOOL, "is_double_sided")
.fragment_source("eevee_surf_capture_frag.glsl")
.additional_info("eevee_global_ubo", "eevee_utility_texture");

View File

@ -118,17 +118,21 @@ struct uiViewItemHandle;
/** #uiBlock.emboss and #uiBut.emboss */
enum eUIEmbossType {
UI_EMBOSS = 0, /* use widget style for drawing */
UI_EMBOSS_NONE = 1, /* Nothing, only icon and/or text */
UI_EMBOSS_PULLDOWN = 2, /* Pull-down menu style */
UI_EMBOSS_RADIAL = 3, /* Pie Menu */
/** Use widget style for drawing. */
UI_EMBOSS = 0,
/** Nothing, only icon and/or text */
UI_EMBOSS_NONE = 1,
/** Pull-down menu style */
UI_EMBOSS_PULLDOWN = 2,
/** Pie Menu */
UI_EMBOSS_RADIAL = 3,
/**
* The same as #UI_EMBOSS_NONE, unless the button has
* a coloring status like an animation state or red alert.
*/
UI_EMBOSS_NONE_OR_STATUS = 4,
UI_EMBOSS_UNDEFINED = 255, /* For layout engine, use emboss from block. */
/** For layout engine, use emboss from block. */
UI_EMBOSS_UNDEFINED = 255,
};
/** #uiBlock::direction */
@ -2834,7 +2838,11 @@ void uiItemPointerR(uiLayout *layout,
const char *name,
int icon);
/* Create a list of enum items. Active is an optional item to highlight. */
/**
* Create a list of enum items.
* \param active: an optional item to highlight.
*/
void uiItemsFullEnumO(uiLayout *layout,
const char *opname,
const char *propname,
@ -2846,7 +2854,7 @@ void uiItemsFullEnumO(uiLayout *layout,
* Create UI items for enum items in \a item_array.
*
* A version of #uiItemsFullEnumO that takes pre-calculated item array.
* active, if not -1, will highlight that item.
* \param active: if not -1, will highlight that item.
*/
void uiItemsFullEnumO_items(uiLayout *layout,
wmOperatorType *ot,

View File

@ -159,7 +159,7 @@ static void gpu_viewport_textures_create(GPUViewport *viewport)
}
}
/* Can be shared with GPUOffscreen. */
/* Can be shared with #GPUOffscreen. */
if (viewport->depth_tx == nullptr) {
/* Depth texture can be read back by gizmos #view3d_depths_create. */
/* Swizzle flag is needed by Workbench Volumes to read the stencil view. */

View File

@ -266,7 +266,8 @@ static void detect_workarounds()
/* Although an OpenGL 4.3 feature, our implementation requires shader_draw_parameters_support.
* NOTE: we should untangle this by checking both features for clarity. */
GLContext::multi_draw_indirect_support = false;
/* Turn off extensions. */
GLContext::layered_rendering_support = false;
/* Turn off vendor specific extensions. */
GLContext::native_barycentric_support = false;
GLContext::framebuffer_fetch_support = false;
@ -281,7 +282,6 @@ static void detect_workarounds()
GLContext::debug_layer_support = false;
GLContext::fixed_restart_index_support = false;
GLContext::geometry_shader_invocations = false;
GLContext::layered_rendering_support = false;
GLContext::texture_cube_map_array_support = false;
GLContext::texture_gather_support = false;
GLContext::texture_storage_support = false;

View File

@ -113,8 +113,8 @@ class VKFrameBuffer : public FrameBuffer {
* Return the number of color attachments of this frame buffer, including unused color
* attachments.
*
* Framebuffers can have unused attachments. When higher attachment slots are being used, unused
* lower attachment slots will be counted as they are required resources in renderpasses.
* Frame-buffers can have unused attachments. When higher attachment slots are being used, unused
* lower attachment slots will be counted as they are required resources in render-passes.
*/
int color_attachments_resource_size() const;

View File

@ -52,10 +52,9 @@ enum class OutputSocketFieldType {
enum class CompositorInputRealizationOptions : uint8_t {
None = 0,
RealizeOnOperationDomain = (1 << 0),
RealizeRotation = (1 << 1),
RealizeScale = (1 << 2),
};
ENUM_OPERATORS(CompositorInputRealizationOptions, CompositorInputRealizationOptions::RealizeScale)
ENUM_OPERATORS(CompositorInputRealizationOptions,
CompositorInputRealizationOptions::RealizeOnOperationDomain)
/**
* Contains information about how a node output's field state depends on inputs of the same node.

View File

@ -30,7 +30,7 @@ static void cmp_node_bokehblur_declare(NodeDeclarationBuilder &b)
.compositor_domain_priority(0);
b.add_input<decl::Color>("Bokeh")
.default_value({1.0f, 1.0f, 1.0f, 1.0f})
.compositor_realization_options(CompositorInputRealizationOptions::RealizeRotation);
.compositor_realization_options(CompositorInputRealizationOptions::None);
b.add_input<decl::Float>("Size")
.default_value(1.0f)
.min(0.0f)

View File

@ -7,11 +7,13 @@
*/
#include "BLI_assert.h"
#include "BLI_math_angle_types.hh"
#include "BLI_math_matrix.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "COM_algorithm_transform.hh"
#include "COM_node_operation.hh"
#include "node_composite_util.hh"
@ -53,15 +55,12 @@ class RotateOperation : public NodeOperation {
void execute() override
{
Result &input = get_input("Image");
Result &result = get_result("Image");
input.pass_through(result);
Result &output = get_result("Image");
const math::AngleRadian rotation = get_input("Degr").get_float_value_default(0.0f);
const float3x3 transformation = math::from_rotation<float3x3>(rotation);
result.transform(transformation);
result.get_realization_options().interpolation = get_interpolation();
transform(context(), input, output, transformation, get_interpolation());
}
Interpolation get_interpolation()

View File

@ -7,8 +7,10 @@
*/
#include "BLI_assert.h"
#include "BLI_math_angle_types.hh"
#include "BLI_math_base.hh"
#include "BLI_math_matrix.hh"
#include "BLI_math_matrix_types.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_string.h"
@ -17,6 +19,7 @@
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "COM_algorithm_transform.hh"
#include "COM_node_operation.hh"
#include "node_composite_util.hh"
@ -82,13 +85,16 @@ class ScaleOperation : public NodeOperation {
void execute() override
{
Result &input = get_input("Image");
Result &result = get_result("Image");
input.pass_through(result);
Result &output = get_result("Image");
const float2 scale = get_scale();
const math::AngleRadian rotation = 0.0f;
const float2 translation = get_translation();
const float3x3 transformation = math::from_loc_rot_scale<float3x3>(
get_translation(), math::AngleRadian(0.0f), get_scale());
translation, rotation, scale);
result.transform(transformation);
const Interpolation interpolation = input.get_realization_options().interpolation;
transform(context(), input, output, transformation, interpolation);
}
float2 get_scale()

View File

@ -23,6 +23,7 @@
#include "BKE_movieclip.h"
#include "BKE_tracking.h"
#include "COM_algorithm_transform.hh"
#include "COM_node_operation.hh"
#include "node_composite_util.hh"
@ -82,17 +83,17 @@ class Stabilize2DOperation : public NodeOperation {
void execute() override
{
Result &input_image = get_input("Image");
Result &output_image = get_result("Image");
input_image.pass_through(output_image);
Result &input = get_input("Image");
Result &output = get_result("Image");
MovieClip *movie_clip = get_movie_clip();
if (input_image.is_single_value() || !movie_clip) {
if (input.is_single_value() || !movie_clip) {
input.pass_through(output);
return;
}
const int width = input_image.domain().size.x;
const int height = input_image.domain().size.y;
const int width = input.domain().size.x;
const int height = input.domain().size.y;
const int frame_number = BKE_movieclip_remap_scene_to_clip_frame(movie_clip,
context().get_frame_number());
@ -107,8 +108,7 @@ class Stabilize2DOperation : public NodeOperation {
transformation = math::invert(transformation);
}
output_image.transform(transformation);
output_image.get_realization_options().interpolation = get_interpolation();
transform(context(), input, output, transformation, get_interpolation());
}
Interpolation get_interpolation()

View File

@ -7,12 +7,14 @@
*/
#include "BLI_assert.h"
#include "BLI_math_angle_types.hh"
#include "BLI_math_matrix.hh"
#include "BLI_math_vector.h"
#include "UI_interface.hh"
#include "UI_resources.hh"
#include "COM_algorithm_transform.hh"
#include "COM_node_operation.hh"
#include "node_composite_util.hh"
@ -56,7 +58,6 @@ static void node_composit_buts_transform(uiLayout *layout, bContext * /*C*/, Poi
}
using namespace blender::realtime_compositor;
using namespace blender::math;
class TransformOperation : public NodeOperation {
public:
@ -65,18 +66,16 @@ class TransformOperation : public NodeOperation {
void execute() override
{
Result &input = get_input("Image");
Result &result = get_result("Image");
input.pass_through(result);
Result &output = get_result("Image");
const float2 translation = float2(get_input("X").get_float_value_default(0.0f),
get_input("Y").get_float_value_default(0.0f));
const AngleRadian rotation = AngleRadian(get_input("Angle").get_float_value_default(0.0f));
const math::AngleRadian rotation = get_input("Angle").get_float_value_default(0.0f);
const float2 scale = float2(get_input("Scale").get_float_value_default(1.0f));
const float3x3 transformation = math::from_loc_rot_scale<float3x3>(
translation, rotation, scale);
const float3x3 transformation = from_loc_rot_scale<float3x3>(translation, rotation, scale);
result.transform(transformation);
result.get_realization_options().interpolation = get_interpolation();
transform(context(), input, output, transformation, get_interpolation());
}
Interpolation get_interpolation()

View File

@ -64,7 +64,7 @@ static bool pygpu_buffer_pyobj_as_shape(PyObject *shape_obj,
Py_ssize_t shape_len = 0;
if (PyLong_Check(shape_obj)) {
shape_len = 1;
if ((r_shape[0] = PyLong_AsLong(shape_obj)) < 1) {
if ((r_shape[0] = PyLong_AsSsize_t(shape_obj)) < 1) {
PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
return false;
}
@ -92,7 +92,7 @@ static bool pygpu_buffer_pyobj_as_shape(PyObject *shape_obj,
return false;
}
r_shape[i] = PyLong_AsLong(ob);
r_shape[i] = PyLong_AsSsize_t(ob);
Py_DECREF(ob);
if (r_shape[i] < 1) {

View File

@ -232,6 +232,13 @@ static void wm_software_cursor_motion_clear()
g_software_cursor.xy[1] = -1;
}
static void wm_software_cursor_motion_clear_with_window(const wmWindow *win)
{
if (g_software_cursor.winid == win->winid) {
wm_software_cursor_motion_clear();
}
}
static void wm_software_cursor_draw_bitmap(const int event_xy[2],
const GHOST_CursorBitmapRef *bitmap)
{
@ -1144,7 +1151,8 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view)
wm_software_cursor_motion_update(win);
}
else {
wm_software_cursor_motion_clear();
/* Checking the window is needed so one window doesn't clear the cursor state of another. */
wm_software_cursor_motion_clear_with_window(win);
}
}
@ -1158,7 +1166,7 @@ static void wm_draw_window(bContext *C, wmWindow *win)
bScreen *screen = WM_window_get_active_screen(win);
bool stereo = WM_stereo3d_enabled(win, false);
/* Avoid any BGL call issued before this to alter the window drawin. */
/* Avoid any BGL call issued before this to alter the window drawing. */
GPU_bgl_end();
/* Draw area regions into their own frame-buffer. This way we can redraw
@ -1486,7 +1494,7 @@ static bool wm_draw_update_test_window(Main *bmain, bContext *C, wmWindow *win)
else {
/* Detect the edge case when the previous draw used the software cursor but this one doesn't,
* it's important to redraw otherwise the software cursor will remain displayed. */
if (g_software_cursor.winid != -1) {
if (g_software_cursor.winid == win->winid) {
return true;
}
}

View File

@ -2490,7 +2490,8 @@ static eHandlerActionFlag wm_handler_operator_call(bContext *C,
if (retval & (OPERATOR_CANCELLED | OPERATOR_FINISHED)) {
wm_operator_reports(C, op, retval, false);
if (op->type->modalkeymap) {
wmOperator *op_test = handler->op->opm ? handler->op->opm : handler->op;
if (op_test->type->modalkeymap) {
WM_window_status_area_tag_redraw(win);
}
}