Animation: blend offset slider #106518
@ -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()
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
19
extern/quadriflow/patches/blender.patch
vendored
19
extern/quadriflow/patches/blender.patch
vendored
@ -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);
|
||||
}
|
||||
}
|
8
extern/quadriflow/src/hierarchy.cpp
vendored
8
extern/quadriflow/src/hierarchy.cpp
vendored
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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.
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
109
source/blender/blenlib/BLI_implicit_sharing.hh
Normal file
109
source/blender/blenlib/BLI_implicit_sharing.hh
Normal 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
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
85
source/blender/blenlib/tests/BLI_implicit_sharing_test.cc
Normal file
85
source/blender/blenlib/tests/BLI_implicit_sharing_test.cc
Normal 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
|
@ -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.
|
||||
*/
|
||||
|
@ -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.).
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|