Animation: blend offset slider #106518

Closed
AresDeveaux wants to merge 12 commits from AresDeveaux/blender:blend_offset_slider into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
43 changed files with 1432 additions and 337 deletions
Showing only changes of commit 6332c5abae - Show all commits

View File

@ -1300,16 +1300,29 @@ macro(windows_install_shared_manifest)
endif()
if(WINDOWS_INSTALL_DEBUG)
set(WINDOWS_CONFIGURATIONS "${WINDOWS_CONFIGURATIONS};Debug")
list(APPEND WINDOWS_SHARED_MANIFEST_DEBUG ${WINDOWS_INSTALL_FILES})
endif()
if(WINDOWS_INSTALL_RELEASE)
list(APPEND WINDOWS_SHARED_MANIFEST_RELEASE ${WINDOWS_INSTALL_FILES})
set(WINDOWS_CONFIGURATIONS "${WINDOWS_CONFIGURATIONS};Release;RelWithDebInfo;MinSizeRel")
endif()
install(FILES ${WINDOWS_INSTALL_FILES}
CONFIGURATIONS ${WINDOWS_CONFIGURATIONS}
DESTINATION "./blender.shared"
)
if(NOT WITH_PYTHON_MODULE)
# Blender executable with manifest.
if(WINDOWS_INSTALL_DEBUG)
list(APPEND WINDOWS_SHARED_MANIFEST_DEBUG ${WINDOWS_INSTALL_FILES})
endif()
if(WINDOWS_INSTALL_RELEASE)
list(APPEND WINDOWS_SHARED_MANIFEST_RELEASE ${WINDOWS_INSTALL_FILES})
endif()
install(FILES ${WINDOWS_INSTALL_FILES}
CONFIGURATIONS ${WINDOWS_CONFIGURATIONS}
DESTINATION "./blender.shared"
)
else()
# Python module without manifest.
install(FILES ${WINDOWS_INSTALL_FILES}
CONFIGURATIONS ${WINDOWS_CONFIGURATIONS}
DESTINATION "./bpy"
)
endif()
endmacro()
macro(windows_generate_manifest)
@ -1326,24 +1339,28 @@ macro(windows_generate_manifest)
endmacro()
macro(windows_generate_shared_manifest)
windows_generate_manifest(
FILES "${WINDOWS_SHARED_MANIFEST_DEBUG}"
OUTPUT "${CMAKE_BINARY_DIR}/Debug/blender.shared.manifest"
NAME "blender.shared"
)
windows_generate_manifest(
FILES "${WINDOWS_SHARED_MANIFEST_RELEASE}"
OUTPUT "${CMAKE_BINARY_DIR}/Release/blender.shared.manifest"
NAME "blender.shared"
)
install(
FILES ${CMAKE_BINARY_DIR}/Release/blender.shared.manifest
DESTINATION "./blender.shared"
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
install(
FILES ${CMAKE_BINARY_DIR}/Debug/blender.shared.manifest
DESTINATION "./blender.shared"
CONFIGURATIONS Debug
)
if(WINDOWS_SHARED_MANIFEST_DEBUG)
windows_generate_manifest(
FILES "${WINDOWS_SHARED_MANIFEST_DEBUG}"
OUTPUT "${CMAKE_BINARY_DIR}/Debug/blender.shared.manifest"
NAME "blender.shared"
)
install(
FILES ${CMAKE_BINARY_DIR}/Debug/blender.shared.manifest
DESTINATION "./blender.shared"
CONFIGURATIONS Debug
)
endif()
if(WINDOWS_SHARED_MANIFEST_RELEASE)
windows_generate_manifest(
FILES "${WINDOWS_SHARED_MANIFEST_RELEASE}"
OUTPUT "${CMAKE_BINARY_DIR}/Release/blender.shared.manifest"
NAME "blender.shared"
)
install(
FILES ${CMAKE_BINARY_DIR}/Release/blender.shared.manifest
DESTINATION "./blender.shared"
CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel
)
endif()
endmacro()

View File

@ -114,12 +114,13 @@ add_definitions(-D_WIN32_WINNT=0x603)
# First generate the manifest for tests since it will not need the dependency on the CRT.
configure_file(${CMAKE_SOURCE_DIR}/release/windows/manifest/blender.exe.manifest.in ${CMAKE_CURRENT_BINARY_DIR}/tests.exe.manifest @ONLY)
if(WITH_WINDOWS_BUNDLE_CRT)
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE)
set(CMAKE_INSTALL_OPENMP_LIBRARIES ${WITH_OPENMP})
include(InstallRequiredSystemLibraries)
# Always detect CRT paths, but only manually install with WITH_WINDOWS_BUNDLE_CRT.
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE)
set(CMAKE_INSTALL_OPENMP_LIBRARIES ${WITH_OPENMP})
include(InstallRequiredSystemLibraries)
if(WITH_WINDOWS_BUNDLE_CRT)
# ucrtbase(d).dll cannot be in the manifest, due to the way windows 10 handles
# redirects for this dll, for details see #88813.
foreach(lib ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS})
@ -141,7 +142,9 @@ if(WITH_WINDOWS_BUNDLE_CRT)
install(FILES ${CMAKE_BINARY_DIR}/blender.crt.manifest DESTINATION ./blender.crt)
set(BUNDLECRT "<dependency><dependentAssembly><assemblyIdentity type=\"win32\" name=\"blender.crt\" version=\"1.0.0.0\" /></dependentAssembly></dependency>")
endif()
set(BUNDLECRT "${BUNDLECRT}<dependency><dependentAssembly><assemblyIdentity type=\"win32\" name=\"blender.shared\" version=\"1.0.0.0\" /></dependentAssembly></dependency>")
if(NOT WITH_PYTHON_MODULE)
set(BUNDLECRT "${BUNDLECRT}<dependency><dependentAssembly><assemblyIdentity type=\"win32\" name=\"blender.shared\" version=\"1.0.0.0\" /></dependentAssembly></dependency>")
endif()
configure_file(${CMAKE_SOURCE_DIR}/release/windows/manifest/blender.exe.manifest.in ${CMAKE_CURRENT_BINARY_DIR}/blender.exe.manifest @ONLY)

View File

@ -231,3 +231,22 @@ index 355ee008246..a770bbee60c 100644
}
allocator.deallocate(values, capacity);
capacity = 0;
diff --git a/extern/quadriflow/src/hierarchy.cpp b/extern/quadriflow/src/hierarchy.cpp
index 8cc41da23d0..70a9628320f 100644
--- a/extern/quadriflow/src/hierarchy.cpp
+++ b/extern/quadriflow/src/hierarchy.cpp
@@ -269,7 +269,13 @@ void Hierarchy::DownsampleGraph(const AdjacentMatrix adj, const MatrixXd& V, con
for (auto it = ad.begin(); it != ad.end(); ++it, ++entry_it) {
int k = it->id;
double dp = N.col(i).dot(N.col(k));
- double ratio = A[i] > A[k] ? (A[i] / A[k]) : (A[k] / A[i]);
+ double ratio;
+ if (A[i] > A[k]) {
+ ratio = (A[k] == 0.0f) ? 1.0f : A[i] / A[k];
+ }
+ else {
+ ratio = (A[i] == 0.0f) ? 1.0f : A[k] / A[i];
+ }
*entry_it = Entry(i, k, dp * ratio);
}
}

View File

@ -269,7 +269,13 @@ void Hierarchy::DownsampleGraph(const AdjacentMatrix adj, const MatrixXd& V, con
for (auto it = ad.begin(); it != ad.end(); ++it, ++entry_it) {
int k = it->id;
double dp = N.col(i).dot(N.col(k));
double ratio = A[i] > A[k] ? (A[i] / A[k]) : (A[k] / A[i]);
double ratio;
if (A[i] > A[k]) {
ratio = (A[k] == 0.0f) ? 1.0f : A[i] / A[k];
}
else {
ratio = (A[i] == 0.0f) ? 1.0f : A[k] / A[i];
}
*entry_it = Entry(i, k, dp * ratio);
}
}

View File

@ -619,7 +619,12 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
const Spectrum emission = volume_emission_integrate(
&coeff, closure_flag, transmittance, dt);
accum_emission += result.indirect_throughput * emission;
guiding_record_volume_emission(kg, state, emission);
# if OPENPGL_VERSION_MINOR < 5 // WORKAROUND #104329
if (kernel_data.integrator.max_volume_bounce > 1)
# endif
{
guiding_record_volume_emission(kg, state, emission);
}
}
}
@ -961,9 +966,13 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
const Spectrum phase_weight = bsdf_eval_sum(&phase_eval) / phase_pdf;
/* Add phase function sampling data to the path segment. */
guiding_record_volume_bounce(
kg, state, sd, phase_weight, phase_pdf, normalize(phase_wo), sampled_roughness);
# if OPENPGL_VERSION_MINOR < 5 // WORKAROUND #104329
if (kernel_data.integrator.max_volume_bounce > 1)
# endif
{
guiding_record_volume_bounce(
kg, state, sd, phase_weight, phase_pdf, normalize(phase_wo), sampled_roughness);
}
/* Update throughput. */
const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput);
const Spectrum throughput_phase = throughput * phase_weight;
@ -1058,7 +1067,11 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
const float3 direct_P = ray->P + result.direct_t * ray->D;
# ifdef __PATH_GUIDING__
# if OPENPGL_VERSION_MINOR < 5 // WORKAROUND #104329
if (kernel_data.integrator.use_guiding && kernel_data.integrator.max_volume_bounce > 1) {
# else
if (kernel_data.integrator.use_guiding) {
# endif
# if PATH_GUIDING_LEVEL >= 1
if (result.direct_sample_method == VOLUME_SAMPLE_DISTANCE) {
/* If the direct scatter event is generated using VOLUME_SAMPLE_DISTANCE the direct event
@ -1131,7 +1144,12 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
# if defined(__PATH_GUIDING__)
# if PATH_GUIDING_LEVEL >= 1
if (!guiding_generated_new_segment) {
guiding_record_volume_segment(kg, state, sd.P, sd.wi);
# if OPENPGL_VERSION_MINOR < 5 // WORKAROUND #104329
if (kernel_data.integrator.max_volume_bounce > 1)
# endif
{
guiding_record_volume_segment(kg, state, sd.P, sd.wi);
}
}
# endif
# if PATH_GUIDING_LEVEL >= 4

View File

@ -23,6 +23,9 @@
#include <cstdio>
#include <cstring>
#include <iostream>
#include <sstream>
#include <sys/stat.h>
/* Set to 0 to allow devices that do not have the required features.
* This allows development on OSX until we really needs these features. */
@ -80,6 +83,21 @@ static const char *vulkan_error_as_string(VkResult result)
}
}
enum class VkLayer : uint8_t { KHRONOS_validation };
static bool vklayer_config_exist(const char *vk_extension_config)
{
const char *ev_val = getenv("VK_LAYER_PATH");
if (ev_val == nullptr) {
return false;
}
std::stringstream filename;
filename << ev_val;
filename << "/" << vk_extension_config;
struct stat buffer;
return (stat(filename.str().c_str(), &buffer) == 0);
}
#define __STR(A) "" #A
#define VK_CHECK(__expression) \
do { \
@ -401,16 +419,38 @@ static bool checkLayerSupport(vector<VkLayerProperties> &layers_available, const
static void enableLayer(vector<VkLayerProperties> &layers_available,
vector<const char *> &layers_enabled,
const char *layer_name,
const bool debug)
const VkLayer layer,
const bool display_warning)
{
if (checkLayerSupport(layers_available, layer_name)) {
layers_enabled.push_back(layer_name);
#define PUSH_VKLAYER(name, name2) \
if (vklayer_config_exist("VkLayer_" #name ".json") && \
checkLayerSupport(layers_available, "VK_LAYER_" #name2)) { \
layers_enabled.push_back("VK_LAYER_" #name2); \
enabled = true; \
} \
else { \
warnings << "VK_LAYER_" #name2; \
}
else if (debug) {
fprintf(
stderr, "Warning: Layer requested, but not supported by the platform. [%s]\n", layer_name);
bool enabled = false;
std::stringstream warnings;
switch (layer) {
case VkLayer::KHRONOS_validation:
PUSH_VKLAYER(khronos_validation, KHRONOS_validation);
};
if (enabled) {
return;
}
if (display_warning) {
fprintf(stderr,
"Warning: Layer requested, but not supported by the platform. [%s] \n",
warnings.str().c_str());
}
#undef PUSH_VKLAYER
}
static bool device_extensions_support(VkPhysicalDevice device, vector<const char *> required_exts)
@ -864,7 +904,7 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext()
vector<const char *> layers_enabled;
if (m_debug) {
enableLayer(layers_available, layers_enabled, "VK_LAYER_KHRONOS_validation", m_debug);
enableLayer(layers_available, layers_enabled, VkLayer::KHRONOS_validation, m_debug);
}
vector<const char *> extensions_device;

View File

@ -398,7 +398,7 @@ Copyright Contributors to the OpenColorIO Project.
** OpenEXR; version 3.1.5 --
https://github.com/AcademySoftwareFoundation/openexr
Copyright Contributors to the OpenEXR Project. All rights reserved.
** OpenImageIO; version 2.4.6.0 -- http://www.openimageio.org
** OpenImageIO; version 2.4.9.0 -- http://www.openimageio.org
Copyright (c) 2008-present by Contributors to the OpenImageIO project. All
Rights Reserved.
** Pystring; version 1.1.3 -- https://github.com/imageworks/pystring

View File

@ -318,9 +318,9 @@ class NODE_MT_node(Menu):
layout.separator()
layout.operator("node.clipboard_copy", text="Copy")
row = layout.row()
row.operator_context = 'EXEC_DEFAULT'
row.operator("node.clipboard_paste", text="Paste")
layout.operator_context = 'EXEC_DEFAULT'
layout.operator("node.clipboard_paste", text="Paste")
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("node.duplicate_move")
layout.operator("node.duplicate_move_linked")
layout.operator("node.delete")

View File

@ -4,9 +4,9 @@
#include <atomic>
#include "BLI_implicit_sharing_ptr.hh"
#include "BLI_set.hh"
#include "BLI_string_ref.hh"
#include "BLI_user_counter.hh"
namespace blender::bke {
@ -32,10 +32,7 @@ namespace blender::bke {
* because that is not available in C code. If possible, the #AutoAnonymousAttributeID wrapper
* should be used to avoid manual reference counting in C++ code.
*/
class AnonymousAttributeID {
private:
mutable std::atomic<int> users_ = 1;
class AnonymousAttributeID : public ImplicitSharingMixin {
protected:
std::string name_;
@ -49,22 +46,15 @@ class AnonymousAttributeID {
virtual std::string user_name() const;
void user_add() const
private:
void delete_self() override
{
users_.fetch_add(1);
}
void user_remove() const
{
const int new_users = users_.fetch_sub(1) - 1;
if (new_users == 0) {
MEM_delete(this);
}
MEM_delete(this);
}
};
/** Wrapper for #AnonymousAttributeID that avoids manual reference counting. */
using AutoAnonymousAttributeID = UserCounter<const AnonymousAttributeID>;
using AutoAnonymousAttributeID = ImplicitSharingPtr<const AnonymousAttributeID>;
/**
* A set of anonymous attribute names that is passed around in geometry nodes.

View File

@ -12,8 +12,6 @@
#include "BLI_function_ref.hh"
#include "BLI_map.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_user_counter.hh"
#include "BLI_vector_set.hh"
#include "BKE_attribute.hh"
@ -40,18 +38,13 @@ class CurvesEditHints;
class Instances;
} // namespace blender::bke
class GeometryComponent;
/**
* This is the base class for specialized geometry component types. A geometry component handles
* a user count to allow avoiding duplication when it is wrapped with #UserCounter. It also handles
* the attribute API, which generalizes storing and modifying generic information on a geometry.
* This is the base class for specialized geometry component types. A geometry component uses
* implicit sharing to avoid read-only copies. It also integrates with attribute API, which
* generalizes storing and modifying generic information on a geometry.
*/
class GeometryComponent {
class GeometryComponent : public blender::ImplicitSharingMixin {
private:
/* The reference count has two purposes. When it becomes zero, the component is freed. When it is
* larger than one, the component becomes immutable. */
mutable std::atomic<int> users_ = 1;
GeometryComponentType type_;
public:
@ -77,13 +70,12 @@ class GeometryComponent {
virtual bool owns_direct_data() const = 0;
virtual void ensure_owns_direct_data() = 0;
void user_add() const;
void user_remove() const;
bool is_mutable() const;
GeometryComponentType type() const;
virtual bool is_empty() const;
private:
void delete_self() override;
};
template<typename T>
@ -109,7 +101,7 @@ inline constexpr bool is_geometry_component_v = std::is_base_of_v<GeometryCompon
*/
struct GeometrySet {
private:
using GeometryComponentPtr = blender::UserCounter<class GeometryComponent>;
using GeometryComponentPtr = blender::ImplicitSharingPtr<class GeometryComponent>;
/* Indexed by #GeometryComponentType. */
std::array<GeometryComponentPtr, GEO_COMPONENT_TYPE_ENUM_SIZE> components_;

View File

@ -2273,7 +2273,7 @@ bool CustomData_merge(const CustomData *source,
layer->anonymous_id = nullptr;
}
else {
layer->anonymous_id->user_add();
layer->anonymous_id->add_user();
}
}
if (alloctype == CD_ASSIGN) {
@ -2365,7 +2365,7 @@ static void customData_free_layer__internal(CustomDataLayer *layer, const int to
const LayerTypeInfo *typeInfo;
if (layer->anonymous_id != nullptr) {
layer->anonymous_id->user_remove();
layer->anonymous_id->remove_user_and_delete_if_last();
layer->anonymous_id = nullptr;
}
if (!(layer->flag & CD_FLAG_NOFREE) && layer->data) {
@ -2956,7 +2956,7 @@ void *CustomData_add_layer_anonymous(CustomData *data,
return nullptr;
}
anonymous_id->user_add();
anonymous_id->add_user();
layer->anonymous_id = anonymous_id;
return layer->data;
}

View File

@ -84,26 +84,6 @@ std::optional<blender::bke::MutableAttributeAccessor> GeometryComponent::attribu
return std::nullopt;
}
void GeometryComponent::user_add() const
{
users_.fetch_add(1);
}
void GeometryComponent::user_remove() const
{
const int new_users = users_.fetch_sub(1) - 1;
if (new_users == 0) {
delete this;
}
}
bool GeometryComponent::is_mutable() const
{
/* If the item is shared, it is read-only. */
/* The user count can be 0, when this is called from the destructor. */
return users_ <= 1;
}
GeometryComponentType GeometryComponent::type() const
{
return type_;
@ -114,6 +94,11 @@ bool GeometryComponent::is_empty() const
return false;
}
void GeometryComponent::delete_self()
{
delete this;
}
/** \} */
/* -------------------------------------------------------------------- */
@ -198,7 +183,7 @@ void GeometrySet::remove_geometry_during_modify()
void GeometrySet::add(const GeometryComponent &component)
{
BLI_assert(!components_[component.type()]);
component.user_add();
component.add_user();
components_[component.type()] = const_cast<GeometryComponent *>(&component);
}

View File

@ -0,0 +1,109 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bli
*/
#include <atomic>
#include "BLI_compiler_attrs.h"
#include "BLI_utildefines.h"
#include "BLI_utility_mixins.hh"
namespace blender {
/**
* #ImplicitSharingInfo is the core data structure for implicit sharing in Blender. Implicit
* sharing is a technique that avoids copying data when it is not necessary. This results in better
* memory usage and performance. Only read-only data can be shared, because otherwise multiple
* owners might want to change the data in conflicting ways.
*
* To determine whether data is shared, #ImplicitSharingInfo keeps a user count. If the count is 1,
* the data only has a single owner and is therefore mutable. If some code wants to modify data
* that is currently shared, it has to make a copy first.
* This behavior is also called "copy on write".
*
* In addition to containing the reference count, #ImplicitSharingInfo also knows how to destruct
* the referenced data. This is important because the code freeing the data in the end might not
* know how it was allocated (for example, it doesn't know whether an array was allocated using the
* system or guarded allocator).
*
* #ImplicitSharingInfo can be used in two ways:
* - It can be allocated separately from the referenced data. This is used when the shared data is
* e.g. a plain data array.
* - It can be embedded into another struct. For that it's best to use #ImplicitSharingMixin.
*/
class ImplicitSharingInfo : NonCopyable, NonMovable {
private:
mutable std::atomic<int> users_;
public:
ImplicitSharingInfo(const int initial_users) : users_(initial_users)
{
}
virtual ~ImplicitSharingInfo()
{
BLI_assert(this->is_mutable());
}
/** True if there are other const references to the resource, meaning it cannot be modified. */
bool is_shared() const
{
return users_.load(std::memory_order_relaxed) >= 2;
}
/** Whether the resource can be modified without a copy because there is only one owner. */
bool is_mutable() const
{
return !this->is_shared();
}
/** Call when a the data has a new additional owner. */
void add_user() const
{
users_.fetch_add(1, std::memory_order_relaxed);
}
/**
* Call when the data is no longer needed. This might just decrement the user count, or it might
* also delete the data if this was the last user.
*/
void remove_user_and_delete_if_last() const
{
const int old_user_count = users_.fetch_sub(1, std::memory_order_acq_rel);
BLI_assert(old_user_count >= 1);
const bool was_last_user = old_user_count == 1;
if (was_last_user) {
const_cast<ImplicitSharingInfo *>(this)->delete_self_with_data();
}
}
private:
/** Has to free the #ImplicitSharingInfo and the referenced data. */
virtual void delete_self_with_data() = 0;
};
/**
* Makes it easy to embed implicit-sharing behavior into a struct. Structs that derive from this
* class can be used with #ImplicitSharingPtr.
*/
class ImplicitSharingMixin : public ImplicitSharingInfo {
public:
ImplicitSharingMixin() : ImplicitSharingInfo(1)
{
}
private:
void delete_self_with_data() override
{
/* Can't use `delete this` here, because we don't know what allocator was used. */
this->delete_self();
}
virtual void delete_self() = 0;
};
} // namespace blender

View File

@ -6,59 +6,60 @@
* \ingroup bli
*/
#include <atomic>
#include "BLI_implicit_sharing.hh"
namespace blender {
/**
* A simple automatic reference counter. It is similar to std::shared_ptr, but expects that the
* reference count is inside the object.
* #ImplicitSharingPtr is a smart pointer that manages implicit sharing. It's designed to work with
* types that derive from #ImplicitSharingMixin. It is fairly similar to #std::shared_ptr but
* requires the reference count to be embedded in the data.
*/
template<typename T> class UserCounter {
template<typename T> class ImplicitSharingPtr {
private:
T *data_ = nullptr;
public:
UserCounter() = default;
ImplicitSharingPtr() = default;
UserCounter(T *data) : data_(data)
ImplicitSharingPtr(T *data) : data_(data)
{
}
UserCounter(const UserCounter &other) : data_(other.data_)
ImplicitSharingPtr(const ImplicitSharingPtr &other) : data_(other.data_)
{
this->user_add(data_);
this->add_user(data_);
}
UserCounter(UserCounter &&other) : data_(other.data_)
ImplicitSharingPtr(ImplicitSharingPtr &&other) : data_(other.data_)
{
other.data_ = nullptr;
}
~UserCounter()
~ImplicitSharingPtr()
{
this->user_remove(data_);
this->remove_user_and_delete_if_last(data_);
}
UserCounter &operator=(const UserCounter &other)
ImplicitSharingPtr &operator=(const ImplicitSharingPtr &other)
{
if (this == &other) {
return *this;
}
this->user_remove(data_);
this->remove_user_and_delete_if_last(data_);
data_ = other.data_;
this->user_add(data_);
this->add_user(data_);
return *this;
}
UserCounter &operator=(UserCounter &&other)
ImplicitSharingPtr &operator=(ImplicitSharingPtr &&other)
{
if (this == &other) {
return *this;
}
this->user_remove(data_);
this->remove_user_and_delete_if_last(data_);
data_ = other.data_;
other.data_ = nullptr;
return *this;
@ -112,7 +113,7 @@ template<typename T> class UserCounter {
void reset()
{
this->user_remove(data_);
this->remove_user_and_delete_if_last(data_);
data_ = nullptr;
}
@ -126,29 +127,23 @@ template<typename T> class UserCounter {
return get_default_hash(data_);
}
friend bool operator==(const UserCounter &a, const UserCounter &b)
friend bool operator==(const ImplicitSharingPtr &a, const ImplicitSharingPtr &b)
{
return a.data_ == b.data_;
}
friend std::ostream &operator<<(std::ostream &stream, const UserCounter &value)
{
stream << value.data_;
return stream;
}
private:
static void user_add(T *data)
static void add_user(T *data)
{
if (data != nullptr) {
data->user_add();
data->add_user();
}
}
static void user_remove(T *data)
static void remove_user_and_delete_if_last(T *data)
{
if (data != nullptr) {
data->user_remove();
data->remove_user_and_delete_if_last();
}
}
};

View File

@ -646,6 +646,24 @@ class Set {
return !Intersects(a, b);
}
friend bool operator==(const Set &a, const Set &b)
{
if (a.size() != b.size()) {
return false;
}
for (const Key &key : a) {
if (!b.contains(key)) {
return false;
}
}
return true;
}
friend bool operator!=(const Set &a, const Set &b)
{
return !(a == b);
}
private:
BLI_NOINLINE void realloc_and_reinsert(const int64_t min_usable_slots)
{

View File

@ -243,6 +243,8 @@ set(SRC
BLI_hash_tables.hh
BLI_heap.h
BLI_heap_simple.h
BLI_implicit_sharing.hh
BLI_implicit_sharing_ptr.hh
BLI_index_mask.hh
BLI_index_mask_ops.hh
BLI_index_range.hh
@ -355,7 +357,6 @@ set(SRC
BLI_timecode.h
BLI_timeit.hh
BLI_timer.h
BLI_user_counter.hh
BLI_utildefines.h
BLI_utildefines_iter.h
BLI_utildefines_stack.h
@ -493,6 +494,7 @@ if(WITH_GTESTS)
tests/BLI_hash_mm2a_test.cc
tests/BLI_heap_simple_test.cc
tests/BLI_heap_test.cc
tests/BLI_implicit_sharing_test.cc
tests/BLI_index_mask_test.cc
tests/BLI_index_range_test.cc
tests/BLI_inplace_priority_queue_test.cc

View File

@ -0,0 +1,85 @@
/* SPDX-License-Identifier: Apache-2.0 */
#include "MEM_guardedalloc.h"
#include "BLI_implicit_sharing_ptr.hh"
#include "testing/testing.h"
namespace blender::tests {
class ImplicitlySharedData : public ImplicitSharingMixin {
public:
ImplicitSharingPtr<ImplicitlySharedData> copy() const
{
return MEM_new<ImplicitlySharedData>(__func__);
}
void delete_self() override
{
MEM_delete(this);
}
};
class SharedDataContainer {
private:
ImplicitSharingPtr<ImplicitlySharedData> data_;
public:
SharedDataContainer() : data_(MEM_new<ImplicitlySharedData>(__func__))
{
}
const ImplicitlySharedData *get_for_read() const
{
return data_.get();
}
ImplicitlySharedData *get_for_write()
{
if (!data_) {
return nullptr;
}
if (data_->is_mutable()) {
return data_.get();
}
data_ = data_->copy();
return data_.get();
}
};
TEST(implicit_sharing, CopyOnWriteAccess)
{
/* Create the initial data. */
SharedDataContainer a;
EXPECT_NE(a.get_for_read(), nullptr);
/* a and b share the same underlying data now. */
SharedDataContainer b = a;
EXPECT_EQ(a.get_for_read(), b.get_for_read());
/* c now shares the data with a and b. */
SharedDataContainer c = a;
EXPECT_EQ(b.get_for_read(), c.get_for_read());
/* Retrieving write access on b should make a copy because the data is shared. */
ImplicitlySharedData *data_b1 = b.get_for_write();
EXPECT_NE(data_b1, nullptr);
EXPECT_EQ(data_b1, b.get_for_read());
EXPECT_NE(data_b1, a.get_for_read());
EXPECT_NE(data_b1, c.get_for_read());
/* Retrieving the same write access again should *not* make another copy. */
ImplicitlySharedData *data_b2 = b.get_for_write();
EXPECT_EQ(data_b1, data_b2);
/* Moving b should also move the data. b then does not have ownership anymore. Since the data in
* b only had one owner, the data is still mutable now that d is the owner. */
SharedDataContainer d = std::move(b);
EXPECT_EQ(b.get_for_read(), nullptr);
EXPECT_EQ(b.get_for_write(), nullptr);
EXPECT_EQ(d.get_for_read(), data_b1);
EXPECT_EQ(d.get_for_write(), data_b1);
}
} // namespace blender::tests

View File

@ -600,6 +600,28 @@ TEST(set, RemoveUniquePtrWithRaw)
EXPECT_TRUE(set.is_empty());
}
TEST(set, Equality)
{
const Set<int> a = {1, 2, 3, 4, 5};
const Set<int> b = {5, 2, 3, 1, 4};
const Set<int> c = {1, 2, 3};
const Set<int> d = {1, 2, 3, 4, 5, 6};
const Set<int> e = {};
const Set<int> f = {10, 11, 12, 13, 14};
EXPECT_EQ(a, a);
EXPECT_EQ(a, b);
EXPECT_EQ(b, a);
EXPECT_NE(a, c);
EXPECT_NE(a, d);
EXPECT_NE(a, e);
EXPECT_NE(a, f);
EXPECT_NE(c, a);
EXPECT_NE(d, a);
EXPECT_NE(e, a);
EXPECT_NE(f, a);
}
/**
* Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot.
*/

View File

@ -1141,7 +1141,7 @@ static bool write_file_handle(Main *mainvar,
* asap afterward. */
id_lib_extern(id_iter);
}
else if (ID_FAKE_USERS(id_iter) > 0) {
else if (ID_FAKE_USERS(id_iter) > 0 && id_iter->asset_data == nullptr) {
/* Even though fake user is not directly editable by the user on linked data, it is a
* common 'work-around' to set it in library files on data-blocks that need to be linked
* but typically do not have an actual real user (e.g. texts, etc.).

View File

@ -3,6 +3,7 @@
#include "COM_MaskNode.h"
#include "COM_MaskOperation.h"
#include "COM_ScaleOperation.h"
namespace blender::compositor {
@ -50,7 +51,21 @@ void MaskNode::convert_to_operations(NodeConverter &converter,
}
converter.add_operation(operation);
converter.map_output_socket(output_mask, operation->get_output_socket());
ScaleFixedSizeOperation *scale_operation = new ScaleFixedSizeOperation();
scale_operation->set_variable_size(true);
/* Consider aspect ratio from scene. */
const int new_height = rd->xasp / rd->yasp * operation->get_mask_height();
scale_operation->set_new_height(new_height);
scale_operation->set_new_width(operation->get_mask_width());
scale_operation->set_is_aspect(false);
scale_operation->set_is_crop(false);
scale_operation->set_scale_canvas_max_size({float(data->size_x), float(data->size_y)});
converter.add_operation(scale_operation);
converter.add_link(operation->get_output_socket(0), scale_operation->get_input_socket(0));
converter.map_output_socket(output_mask, scale_operation->get_output_socket(0));
}
} // namespace blender::compositor

View File

@ -63,6 +63,14 @@ class MaskOperation : public MultiThreadedOperation {
mask_height_inv_ = 1.0f / (float)height;
mask_px_ofs_[1] = mask_height_inv_ * 0.5f;
}
int get_mask_width()
{
return mask_width_;
}
int get_mask_height()
{
return mask_height_;
}
void set_framenumber(int frame_number)
{
frame_number_ = frame_number;

View File

@ -4949,28 +4949,31 @@ static void draw_setting_widget(bAnimContext *ac,
bAnimListElem *ale,
const bAnimChannelType *acf,
uiBlock *block,
int xpos,
int ypos,
int setting)
const int xpos,
const int ypos,
const eAnimChannel_Settings setting)
{
short ptrsize, butType;
bool negflag;
bool usetoggle = true;
int flag, icon;
void *ptr;
int icon;
const char *tooltip;
uiBut *but = NULL;
bool enabled;
/* get the flag and the pointer to that flag */
flag = acf->setting_flag(ac, setting, &negflag);
ptr = acf->setting_ptr(ale, setting, &ptrsize);
enabled = ANIM_channel_setting_get(ac, ale, setting);
bool negflag;
const int flag = acf->setting_flag(ac, setting, &negflag);
short ptrsize;
void *ptr = acf->setting_ptr(ale, setting, &ptrsize);
if (!ptr || !flag) {
return;
}
const bool enabled = ANIM_channel_setting_get(ac, ale, setting);
/* get the base icon for the setting */
switch (setting) {
case ACHANNEL_SETTING_VISIBLE: /* visibility eyes */
// icon = ((enabled) ? ICON_HIDE_OFF : ICON_HIDE_ON);
// icon = (enabled ? ICON_HIDE_OFF : ICON_HIDE_ON);
icon = ICON_HIDE_ON;
if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) {
@ -4995,13 +4998,13 @@ static void draw_setting_widget(bAnimContext *ac,
break;
case ACHANNEL_SETTING_EXPAND: /* expanded triangle */
// icon = ((enabled) ? ICON_TRIA_DOWN : ICON_TRIA_RIGHT);
// icon = (enabled ? ICON_TRIA_DOWN : ICON_TRIA_RIGHT);
icon = ICON_TRIA_RIGHT;
tooltip = TIP_("Make channels grouped under this channel visible");
break;
case ACHANNEL_SETTING_SOLO: /* NLA Tracks only */
// icon = ((enabled) ? ICON_SOLO_OFF : ICON_SOLO_ON);
// icon = (enabled ? ICON_SOLO_OFF : ICON_SOLO_ON);
icon = ICON_SOLO_OFF;
tooltip = TIP_(
"NLA Track is the only one evaluated in this animation data-block, with all others "
@ -5012,7 +5015,7 @@ static void draw_setting_widget(bAnimContext *ac,
case ACHANNEL_SETTING_PROTECT: /* protected lock */
/* TODO: what about when there's no protect needed? */
// icon = ((enabled) ? ICON_LOCKED : ICON_UNLOCKED);
// icon = (enabled ? ICON_LOCKED : ICON_UNLOCKED);
icon = ICON_UNLOCKED;
if (ale->datatype != ALE_NLASTRIP) {
@ -5024,7 +5027,7 @@ static void draw_setting_widget(bAnimContext *ac,
break;
case ACHANNEL_SETTING_MUTE: /* muted speaker */
icon = ((enabled) ? ICON_CHECKBOX_DEHLT : ICON_CHECKBOX_HLT);
icon = (enabled ? ICON_CHECKBOX_DEHLT : ICON_CHECKBOX_HLT);
usetoggle = false;
if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) {
@ -5045,7 +5048,7 @@ static void draw_setting_widget(bAnimContext *ac,
break;
case ACHANNEL_SETTING_PINNED: /* pin icon */
// icon = ((enabled) ? ICON_PINNED : ICON_UNPINNED);
// icon = (enabled ? ICON_PINNED : ICON_UNPINNED);
icon = ICON_UNPINNED;
if (ale->type == ANIMTYPE_NLAACTION) {
@ -5064,6 +5067,7 @@ static void draw_setting_widget(bAnimContext *ac,
}
/* type of button */
short butType;
if (usetoggle) {
if (negflag) {
butType = UI_BTYPE_ICON_TOGGLE_N;
@ -5080,100 +5084,99 @@ static void draw_setting_widget(bAnimContext *ac,
butType = UI_BTYPE_TOGGLE;
}
}
/* draw button for setting */
if (ptr && flag) {
switch (ptrsize) {
case sizeof(int): /* integer pointer for setting */
but = uiDefIconButBitI(block,
butType,
flag,
0,
icon,
xpos,
ypos,
ICON_WIDTH,
ICON_WIDTH,
ptr,
0,
0,
0,
0,
tooltip);
break;
uiBut *but = NULL;
switch (ptrsize) {
case sizeof(int): /* integer pointer for setting */
but = uiDefIconButBitI(block,
butType,
flag,
0,
icon,
xpos,
ypos,
ICON_WIDTH,
ICON_WIDTH,
ptr,
0,
0,
0,
0,
tooltip);
break;
case sizeof(short): /* short pointer for setting */
but = uiDefIconButBitS(block,
butType,
flag,
0,
icon,
xpos,
ypos,
ICON_WIDTH,
ICON_WIDTH,
ptr,
0,
0,
0,
0,
tooltip);
break;
case sizeof(short): /* short pointer for setting */
but = uiDefIconButBitS(block,
butType,
flag,
0,
icon,
xpos,
ypos,
ICON_WIDTH,
ICON_WIDTH,
ptr,
0,
0,