Merge branch 'master' into sculpt-dev

This commit is contained in:
Pablo Dobarro 2021-03-11 17:53:47 +01:00
commit e1f338b9f4
51 changed files with 801 additions and 298 deletions

View File

@ -32,9 +32,7 @@ if(MSVC_CLANG AND WITH_OPENMP AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.1
endif()
# Exporting functions from the blender binary gives linker warnings on Apple arm64 systems.
# For now and until Apple arm64 is officially supported, these will just be silenced here.
# TODO (sebbas): Check if official arm64 devices give linker warnings without this block.
# Silence them here.
if(APPLE AND ("${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64"))
if(CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
string(APPEND CMAKE_C_FLAGS " -fvisibility=hidden")

View File

@ -27,8 +27,8 @@ BVHOptiX::BVHOptiX(const BVHParams &params_,
Device *device)
: BVH(params_, geometry_, objects_),
traversable_handle(0),
as_data(device, params_.top_level ? "optix tlas" : "optix blas"),
motion_transform_data(device, "optix motion transform")
as_data(device, params_.top_level ? "optix tlas" : "optix blas", false),
motion_transform_data(device, "optix motion transform", false)
{
}

View File

@ -854,7 +854,7 @@ CUDADevice::CUDAMem *CUDADevice::generic_alloc(device_memory &mem, size_t pitch_
void *shared_pointer = 0;
if (mem_alloc_result != CUDA_SUCCESS && can_map_host) {
if (mem_alloc_result != CUDA_SUCCESS && can_map_host && mem.type != MEM_DEVICE_ONLY) {
if (mem.shared_pointer) {
/* Another device already allocated host memory. */
mem_alloc_result = CUDA_SUCCESS;
@ -877,8 +877,14 @@ CUDADevice::CUDAMem *CUDADevice::generic_alloc(device_memory &mem, size_t pitch_
}
if (mem_alloc_result != CUDA_SUCCESS) {
status = " failed, out of device and host memory";
set_error("System is out of GPU and shared host memory");
if (mem.type == MEM_DEVICE_ONLY) {
status = " failed, out of device memory";
set_error("System is out of GPU memory");
}
else {
status = " failed, out of device and host memory";
set_error("System is out of GPU and shared host memory");
}
}
if (mem.name) {

View File

@ -396,8 +396,7 @@ class CPUDevice : public Device {
<< string_human_readable_size(mem.memory_size()) << ")";
}
if (mem.type == MEM_DEVICE_ONLY) {
assert(!mem.host_pointer);
if (mem.type == MEM_DEVICE_ONLY || !mem.host_pointer) {
size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES;
void *data = util_aligned_malloc(mem.memory_size(), alignment);
mem.device_pointer = (device_ptr)data;
@ -459,7 +458,7 @@ class CPUDevice : public Device {
tex_free((device_texture &)mem);
}
else if (mem.device_pointer) {
if (mem.type == MEM_DEVICE_ONLY) {
if (mem.type == MEM_DEVICE_ONLY || !mem.host_pointer) {
util_aligned_free((void *)mem.device_pointer);
}
mem.device_pointer = 0;

View File

@ -171,7 +171,8 @@ class DenoisingTask {
bool gpu_temporary_mem;
DenoiseBuffers(Device *device)
: mem(device, "denoising pixel buffer"), temporary_mem(device, "denoising temporary mem")
: mem(device, "denoising pixel buffer"),
temporary_mem(device, "denoising temporary mem", true)
{
}
} buffer;

View File

@ -270,8 +270,8 @@ class device_memory {
template<typename T> class device_only_memory : public device_memory {
public:
device_only_memory(Device *device, const char *name)
: device_memory(device, name, MEM_DEVICE_ONLY)
device_only_memory(Device *device, const char *name, bool allow_host_memory_fallback = false)
: device_memory(device, name, allow_host_memory_fallback ? MEM_READ_WRITE : MEM_DEVICE_ONLY)
{
data_type = device_type_traits<T>::data_type;
data_elements = max(device_type_traits<T>::num_elements, 1);

View File

@ -197,8 +197,8 @@ class OptiXDevice : public CUDADevice {
OptiXDevice(DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool background_)
: CUDADevice(info_, stats_, profiler_, background_),
sbt_data(this, "__sbt", MEM_READ_ONLY),
launch_params(this, "__params"),
denoiser_state(this, "__denoiser_state")
launch_params(this, "__params", false),
denoiser_state(this, "__denoiser_state", true)
{
// Store number of CUDA streams in device info
info.cpu_threads = DebugFlags().optix.cuda_streams;
@ -878,8 +878,8 @@ class OptiXDevice : public CUDADevice {
device_ptr input_ptr = rtile.buffer + pixel_offset;
// Copy tile data into a common buffer if necessary
device_only_memory<float> input(this, "denoiser input");
device_vector<TileInfo> tile_info_mem(this, "denoiser tile info", MEM_READ_WRITE);
device_only_memory<float> input(this, "denoiser input", true);
device_vector<TileInfo> tile_info_mem(this, "denoiser tile info", MEM_READ_ONLY);
bool contiguous_memory = true;
for (int i = 0; i < RenderTileNeighbors::SIZE; i++) {
@ -924,7 +924,7 @@ class OptiXDevice : public CUDADevice {
}
# if OPTIX_DENOISER_NO_PIXEL_STRIDE
device_only_memory<float> input_rgb(this, "denoiser input rgb");
device_only_memory<float> input_rgb(this, "denoiser input rgb", true);
input_rgb.alloc_to_device(rect_size.x * rect_size.y * 3 * task.denoising.input_passes);
void *input_args[] = {&input_rgb.device_pointer,
@ -1146,6 +1146,13 @@ class OptiXDevice : public CUDADevice {
const OptixBuildInput &build_input,
uint16_t num_motion_steps)
{
/* Allocate and build acceleration structures only one at a time, to prevent parallel builds
* from running out of memory (since both original and compacted acceleration structure memory
* may be allocated at the same time for the duration of this function). The builds would
* otherwise happen on the same CUDA stream anyway. */
static thread_mutex mutex;
thread_scoped_lock lock(mutex);
const CUDAContextScope scope(cuContext);
// Compute memory usage
@ -1170,11 +1177,12 @@ class OptiXDevice : public CUDADevice {
optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes));
// Allocate required output buffers
device_only_memory<char> temp_mem(this, "optix temp as build mem");
device_only_memory<char> temp_mem(this, "optix temp as build mem", true);
temp_mem.alloc_to_device(align_up(sizes.tempSizeInBytes, 8) + 8);
if (!temp_mem.device_pointer)
return false; // Make sure temporary memory allocation succeeded
// Acceleration structure memory has to be allocated on the device (not allowed to be on host)
device_only_memory<char> &out_data = bvh->as_data;
if (operation == OPTIX_BUILD_OPERATION_BUILD) {
assert(out_data.device == this);
@ -1222,7 +1230,7 @@ class OptiXDevice : public CUDADevice {
// There is no point compacting if the size does not change
if (compacted_size < sizes.outputSizeInBytes) {
device_only_memory<char> compacted_data(this, "optix compacted as");
device_only_memory<char> compacted_data(this, "optix compacted as", false);
compacted_data.alloc_to_device(compacted_size);
if (!compacted_data.device_pointer)
// Do not compact if memory allocation for compacted acceleration structure fails
@ -1242,6 +1250,7 @@ class OptiXDevice : public CUDADevice {
std::swap(out_data.device_size, compacted_data.device_size);
std::swap(out_data.device_pointer, compacted_data.device_pointer);
// Original acceleration structure memory is freed when 'compacted_data' goes out of scope
}
}

View File

@ -32,32 +32,36 @@ extern "C" {
/**
* Counts how many bytes is required for future utf-8 string using utf-16
* \param string16: pointer to working utf-16 string
* \return How many bytes must be allocated includeng NULL.
* \return How many bytes must be allocated including NULL.
*/
size_t count_utf_8_from_16(const wchar_t *string16);
/**
* Counts how many wchar_t (two byte) is required for future utf-16 string using utf-8
* \param string8: pointer to working utf-8 string
* \return How many bytes must be allocated includeng NULL.
* \return How many bytes must be allocated including NULL.
*/
size_t count_utf_16_from_8(const char *string8);
/**
/*
* conv_utf_*** errors
*/
#define UTF_ERROR_NULL_IN (1 << 0) /* Error occures when requered parameter is missing*/
#define UTF_ERROR_ILLCHAR (1 << 1) /* Error if character is in illigal UTF rage*/
#define UTF_ERROR_SMALL \
(1 << 2) /* Passed size is to small. It gives legal string with character missing at the end */
#define UTF_ERROR_ILLSEQ (1 << 3) /* Error if sequence is broken and doesn't finish*/
/** Error occurs when required parameter is missing. */
#define UTF_ERROR_NULL_IN (1 << 0)
/** Error if character is in illegal UTF range. */
#define UTF_ERROR_ILLCHAR (1 << 1)
/** Passed size is to small. It gives legal string with character missing at the end. */
#define UTF_ERROR_SMALL (1 << 2)
/** Error if sequence is broken and doesn't finish. */
#define UTF_ERROR_ILLSEQ (1 << 3)
/**
* Converts utf-16 string to allocated utf-8 string
* \param in16: utf-16 string to convert
* \param out8: utf-8 string to string the conversion
* \param size8: the allocated size in bytes of out8
* \return Returns any errors occured during conversion. See the block above,
* \return Returns any errors occurred during conversion. See the block above,
*/
int conv_utf_16_to_8(const wchar_t *in16, char *out8, size_t size8);
@ -66,7 +70,7 @@ int conv_utf_16_to_8(const wchar_t *in16, char *out8, size_t size8);
* \param in8: utf-8 string to convert
* \param out16: utf-16 string to string the conversion
* \param size16: the allocated size in wchar_t (two byte) of out16
* \return Returns any errors occured during conversion. See the block above,
* \return Returns any errors occurred during conversion. See the block above,
*/
int conv_utf_8_to_16(const char *in8, wchar_t *out16, size_t size16);

View File

@ -384,7 +384,7 @@ class AddPresetFluid(AddPresetBase, Operator):
"""Add or remove a Fluid Preset"""
bl_idname = "fluid.preset_add"
bl_label = "Add Fluid Preset"
preset_menu = "FLUID_MT_presets"
preset_menu = "FLUID_PT_presets"
preset_defines = [
"fluid = bpy.context.fluid"

View File

@ -24,7 +24,7 @@ class SPREADSHEET_OT_toggle_pin(bpy.types.Operator):
'''Turn on or off pinning'''
bl_idname = "spreadsheet.toggle_pin"
bl_label = "Toggle Pin"
bl_options = {'REGISTER', 'UNDO'}
bl_options = {'REGISTER'}
@classmethod
def poll(cls, context):

View File

@ -31,6 +31,9 @@ class SPREADSHEET_HT_header(bpy.types.Header):
pinned_id = space.pinned_id
used_id = pinned_id if pinned_id else context.active_object
layout.prop(space, "geometry_component_type", text="")
layout.prop(space, "attribute_domain", text="")
if used_id:
layout.label(text=used_id.name, icon="OBJECT_DATA")

View File

@ -23,6 +23,7 @@
extern "C" {
#endif
struct BlendFileData;
struct BlendFileReadParams;
struct ID;
struct Main;
@ -31,36 +32,32 @@ struct ReportList;
struct UserDef;
struct bContext;
bool BKE_blendfile_read_ex(struct bContext *C,
const char *filepath,
const struct BlendFileReadParams *params,
struct ReportList *reports,
/* Extra args. */
const bool startup_update_defaults,
const char *startup_app_template);
bool BKE_blendfile_read(struct bContext *C,
const char *filepath,
const struct BlendFileReadParams *params,
struct ReportList *reports);
void BKE_blendfile_read_setup_ex(struct bContext *C,
struct BlendFileData *bfd,
const struct BlendFileReadParams *params,
struct ReportList *reports,
/* Extra args. */
const bool startup_update_defaults,
const char *startup_app_template);
bool BKE_blendfile_read_from_memory_ex(struct bContext *C,
const void *filebuf,
int filelength,
const struct BlendFileReadParams *params,
struct ReportList *reports,
/* Extra args. */
const bool startup_update_defaults,
const char *startup_app_template);
bool BKE_blendfile_read_from_memory(struct bContext *C,
const void *filebuf,
int filelength,
const struct BlendFileReadParams *params,
struct ReportList *reports);
void BKE_blendfile_read_setup(struct bContext *C,
struct BlendFileData *bfd,
const struct BlendFileReadParams *params,
struct ReportList *reports);
bool BKE_blendfile_read_from_memfile(struct bContext *C,
struct MemFile *memfile,
const struct BlendFileReadParams *params,
struct ReportList *reports);
struct BlendFileData *BKE_blendfile_read(const char *filepath,
const struct BlendFileReadParams *params,
struct ReportList *reports);
struct BlendFileData *BKE_blendfile_read_from_memory(const void *filebuf,
int filelength,
const struct BlendFileReadParams *params,
struct ReportList *reports);
struct BlendFileData *BKE_blendfile_read_from_memfile(struct Main *bmain,
struct MemFile *memfile,
const struct BlendFileReadParams *params,
struct ReportList *reports);
void BKE_blendfile_read_make_empty(struct bContext *C);
struct UserDef *BKE_blendfile_userdef_read(const char *filepath, struct ReportList *reports);

View File

@ -61,6 +61,8 @@ void BKE_lib_override_library_copy(struct ID *dst_id,
void BKE_lib_override_library_clear(struct IDOverrideLibrary *override, const bool do_id_user);
void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bool do_id_user);
bool BKE_lib_override_library_is_user_edited(struct ID *id);
struct ID *BKE_lib_override_library_create_from_id(struct Main *bmain,
struct ID *reference_id,
const bool do_tagged_remap);
@ -78,6 +80,10 @@ bool BKE_lib_override_library_resync(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
struct ID *id_root);
void BKE_lib_override_library_main_resync(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer);
void BKE_lib_override_library_delete(struct Main *bmain, struct ID *id_root);
struct IDOverrideLibraryProperty *BKE_lib_override_library_property_find(

View File

@ -76,6 +76,8 @@ enum {
ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE = 1 << 4,
/** Do not remap library override pointers. */
ID_REMAP_SKIP_OVERRIDE_LIBRARY = 1 << 5,
/** Don't touch the user count (use for low level actions such as swapping pointers). */
ID_REMAP_SKIP_USER_CLEAR = 1 << 6,
};
/* Note: Requiring new_id to be non-null, this *may* not be the case ultimately,

View File

@ -77,7 +77,12 @@ bool BKE_memfile_undo_decode(MemFileUndoData *mfu,
G.fileflags |= G_FILE_NO_UI;
if (UNDO_DISK) {
success = BKE_blendfile_read(C, mfu->filename, &(const struct BlendFileReadParams){0}, NULL);
const struct BlendFileReadParams params = {0};
struct BlendFileData *bfd = BKE_blendfile_read(mfu->filename, &params, NULL);
if (bfd != NULL) {
BKE_blendfile_read_setup(C, bfd, &params, NULL);
success = true;
}
}
else {
struct BlendFileReadParams params = {0};
@ -85,7 +90,12 @@ bool BKE_memfile_undo_decode(MemFileUndoData *mfu,
if (!use_old_bmain_data) {
params.skip_flags |= BLO_READ_SKIP_UNDO_OLD_MAIN;
}
success = BKE_blendfile_read_from_memfile(C, &mfu->memfile, &params, NULL);
struct BlendFileData *bfd = BKE_blendfile_read_from_memfile(
bmain, &mfu->memfile, &params, NULL);
if (bfd != NULL) {
BKE_blendfile_read_setup(C, bfd, &params, NULL);
success = true;
}
}
/* Restore, bmain has been re-allocated. */

View File

@ -51,6 +51,7 @@
#include "BKE_keyconfig.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
#include "BKE_main.h"
#include "BKE_preferences.h"
#include "BKE_report.h"
@ -131,11 +132,9 @@ static void setup_app_userdef(BlendFileData *bfd)
* should be avoided or check (mode != LOAD_UNDO).
*
* \param bfd: Blend file data, freed by this function on exit.
* \param filepath: File path or identifier.
*/
static void setup_app_data(bContext *C,
BlendFileData *bfd,
const char *filepath,
const struct BlendFileReadParams *params,
ReportList *reports)
{
@ -349,16 +348,10 @@ static void setup_app_data(bContext *C,
if (is_startup) {
bmain->name[0] = '\0';
}
else if (recover && G.relbase_valid) {
/* in case of autosave or quit.blend, use original filename instead
* use relbase_valid to make sure the file is saved, else we get <memory2> in the filename */
filepath = bfd->filename;
else if (recover) {
/* In case of autosave or quit.blend, use original filename instead. */
bmain->recovered = 1;
/* these are the same at times, should never copy to the same location */
if (bmain->name != filepath) {
BLI_strncpy(bmain->name, filepath, FILE_MAX);
}
BLI_strncpy(bmain->name, bfd->filename, FILE_MAX);
}
/* baseflags, groups, make depsgraph, etc */
@ -401,11 +394,17 @@ static void setup_app_data(bContext *C,
* to recompute refcount for all local IDs too. */
BKE_main_id_refcount_recompute(bmain, false);
}
if (mode != LOAD_UNDO) {
BKE_lib_override_library_main_resync(
bmain,
curscene,
bfd->cur_view_layer ? bfd->cur_view_layer : BKE_view_layer_default_view(curscene));
}
}
static void setup_app_blend_file_data(bContext *C,
BlendFileData *bfd,
const char *filepath,
const struct BlendFileReadParams *params,
ReportList *reports)
{
@ -413,7 +412,7 @@ static void setup_app_blend_file_data(bContext *C,
setup_app_userdef(bfd);
}
if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
setup_app_data(C, bfd, filepath, params, reports);
setup_app_data(C, bfd, params, reports);
}
}
@ -430,15 +429,46 @@ static void handle_subversion_warning(Main *main, ReportList *reports)
}
}
bool BKE_blendfile_read_ex(bContext *C,
const char *filepath,
const struct BlendFileReadParams *params,
ReportList *reports,
/* Extra args. */
const bool startup_update_defaults,
const char *startup_app_template)
/**
* Shared setup function that makes the data from `bfd` into the current blend file,
* replacing the contents of #G.main.
* This uses the bfd #BKE_blendfile_read and similarly named functions.
*
* This is done in a separate step so the caller may perform actions after it is known the file
* loaded correctly but before the file replaces the existing blend file contents.
*/
void BKE_blendfile_read_setup_ex(bContext *C,
BlendFileData *bfd,
const struct BlendFileReadParams *params,
ReportList *reports,
/* Extra args. */
const bool startup_update_defaults,
const char *startup_app_template)
{
if (startup_update_defaults) {
if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
BLO_update_defaults_startup_blend(bfd->main, startup_app_template);
}
}
setup_app_blend_file_data(C, bfd, params, reports);
BLO_blendfiledata_free(bfd);
}
void BKE_blendfile_read_setup(bContext *C,
BlendFileData *bfd,
const struct BlendFileReadParams *params,
ReportList *reports)
{
BKE_blendfile_read_setup_ex(C, bfd, params, reports, false, NULL);
}
/**
* \return Blend file data, this must be passed to #BKE_blendfile_read_setup when non-NULL.
*/
struct BlendFileData *BKE_blendfile_read(const char *filepath,
const struct BlendFileReadParams *params,
ReportList *reports)
{
/* Don't print startup file loading. */
if (params->is_startup == false) {
printf("Read blend: %s\n", filepath);
@ -447,69 +477,40 @@ bool BKE_blendfile_read_ex(bContext *C,
BlendFileData *bfd = BLO_read_from_file(filepath, params->skip_flags, reports);
if (bfd) {
handle_subversion_warning(bfd->main, reports);
if (startup_update_defaults) {
if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
BLO_update_defaults_startup_blend(bfd->main, startup_app_template);
}
}
setup_app_blend_file_data(C, bfd, filepath, params, reports);
BLO_blendfiledata_free(bfd);
}
else {
BKE_reports_prependf(reports, "Loading '%s' failed: ", filepath);
}
return (bfd != NULL);
return bfd;
}
bool BKE_blendfile_read(bContext *C,
const char *filepath,
const struct BlendFileReadParams *params,
ReportList *reports)
{
return BKE_blendfile_read_ex(C, filepath, params, reports, false, NULL);
}
bool BKE_blendfile_read_from_memory_ex(bContext *C,
const void *filebuf,
int filelength,
const struct BlendFileReadParams *params,
ReportList *reports,
/* Extra args. */
const bool startup_update_defaults,
const char *startup_app_template)
/**
* \return Blend file data, this must be passed to #BKE_blendfile_read_setup when non-NULL.
*/
struct BlendFileData *BKE_blendfile_read_from_memory(const void *filebuf,
int filelength,
const struct BlendFileReadParams *params,
ReportList *reports)
{
BlendFileData *bfd = BLO_read_from_memory(filebuf, filelength, params->skip_flags, reports);
if (bfd) {
if (startup_update_defaults) {
if ((params->skip_flags & BLO_READ_SKIP_DATA) == 0) {
BLO_update_defaults_startup_blend(bfd->main, startup_app_template);
}
}
setup_app_blend_file_data(C, bfd, "<memory2>", params, reports);
BLO_blendfiledata_free(bfd);
/* Pass. */
}
else {
BKE_reports_prepend(reports, "Loading failed: ");
}
return (bfd != NULL);
return bfd;
}
bool BKE_blendfile_read_from_memory(bContext *C,
const void *filebuf,
int filelength,
const struct BlendFileReadParams *params,
ReportList *reports)
/**
* \return Blend file data, this must be passed to #BKE_blendfile_read_setup when non-NULL.
* \note `memfile` is the undo buffer.
*/
struct BlendFileData *BKE_blendfile_read_from_memfile(Main *bmain,
struct MemFile *memfile,
const struct BlendFileReadParams *params,
ReportList *reports)
{
return BKE_blendfile_read_from_memory_ex(C, filebuf, filelength, params, reports, false, NULL);
}
/* memfile is the undo buffer */
bool BKE_blendfile_read_from_memfile(bContext *C,
struct MemFile *memfile,
const struct BlendFileReadParams *params,
ReportList *reports)
{
Main *bmain = CTX_data_main(C);
BlendFileData *bfd = BLO_read_from_memfile(
bmain, BKE_main_blendfile_path(bmain), memfile, params, reports);
if (bfd) {
@ -520,14 +521,11 @@ bool BKE_blendfile_read_from_memfile(bContext *C,
BLI_assert(BLI_listbase_is_empty(&bfd->main->wm));
BLI_assert(BLI_listbase_is_empty(&bfd->main->workspaces));
BLI_assert(BLI_listbase_is_empty(&bfd->main->screens));
setup_app_blend_file_data(C, bfd, "<memory1>", params, reports);
BLO_blendfiledata_free(bfd);
}
else {
BKE_reports_prepend(reports, "Loading failed: ");
}
return (bfd != NULL);
return bfd;
}
/**

View File

@ -217,6 +217,32 @@ static ID *lib_override_library_create_from(Main *bmain, ID *reference_id)
return local_id;
}
/** Check if given ID has some override rules that actually indicate the user edited it.
*
* TODO: This could be simplified by storing a flag in IDOverrideLibrary during the diffing
* process? */
bool BKE_lib_override_library_is_user_edited(struct ID *id)
{
if (!ID_IS_OVERRIDE_LIBRARY(id)) {
return false;
}
LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) {
LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) {
if ((opop->flag & IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) {
continue;
}
if (opop->operation == IDOVERRIDE_LIBRARY_OP_NOOP) {
continue;
}
/* If an operation does not match the filters above, it is considered as a user-editing one,
* therefore this override is user-edited. */
return true;
}
}
return false;
}
/** Create an overridden local copy of linked reference. */
ID *BKE_lib_override_library_create_from_id(Main *bmain,
ID *reference_id,
@ -383,7 +409,7 @@ typedef struct LibOverrideGroupTagData {
*
* Requires existing `Main.relations`.
*
* Note: this is typically called to complete `lib_override_linked_group_tag()`.
* NOTE: This is typically called to complete `lib_override_linked_group_tag()`.
*/
static bool lib_override_hierarchy_dependencies_recursive_tag(LibOverrideGroupTagData *data)
{
@ -883,6 +909,8 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_
ID *id_override_new = id->newid;
ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id);
BLI_assert((id_override_new->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0);
if (id_override_old != NULL) {
/* Swap the names between old override ID and new one. */
char id_name_buf[MAX_ID_NAME];
@ -975,11 +1003,21 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_
}
id->tag &= ~LIB_TAG_DOIT;
}
/* Also cleanup old overrides that went missing in new linked data. */
/* Also deal with old overrides that went missing in new linked data. */
else if (id->tag & LIB_TAG_MISSING && !ID_IS_LINKED(id)) {
BLI_assert(ID_IS_OVERRIDE_LIBRARY(id));
id->tag |= LIB_TAG_DOIT;
id->tag &= ~LIB_TAG_MISSING;
if (!BKE_lib_override_library_is_user_edited(id)) {
/* If user never edited them, we can delete them. */
id->tag |= LIB_TAG_DOIT;
id->tag &= ~LIB_TAG_MISSING;
CLOG_INFO(&LOG, 3, "Old override %s is being deleted", id->name);
}
else {
/* Otherwise, keep them, user needs to decide whether what to do with them. */
BLI_assert((id->tag & LIB_TAG_DOIT) == 0);
id_fake_user_set(id);
CLOG_INFO(&LOG, 3, "Old override %s is being kept around as it was user-edited", id->name);
}
}
}
FOREACH_MAIN_ID_END;
@ -1006,6 +1044,119 @@ bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_
return success;
}
/**
* Detect and handle required resync of overrides data, when relations between reference linked IDs
* have changed.
*
* This is a fairly complex and costly operation, typically it should be called after
* #BKE_lib_override_library_main_update, which would already detect and tag a lot of cases.
*
* This function will first detect the remaining cases requiring a resync (namely, either when an
* existing linked ID that did not require to be overridden before now would be, or when new IDs
* are added to the hierarchy).
*
* Then it will handle the resync of necessary IDs (through calls to
* #BKE_lib_override_library_resync).
*/
void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *view_layer)
{
BKE_main_relations_create(bmain, 0);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
/* NOTE: in code below, the order in which `FOREACH_MAIN_ID_BEGIN` processes ID types ensures
* that we always process 'higher-level' overrides first (i.e. scenes, then collections, then
* objects, then other types). */
/* Detect all linked data that would need to be overridden if we had to create an override from
* those used by current existing overrides. */
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
continue;
}
if (id->tag & (LIB_TAG_DOIT | LIB_TAG_MISSING)) {
/* We already processed that ID as part of another ID's hierarchy. */
continue;
}
LibOverrideGroupTagData data = {.bmain = bmain,
.id_root = id->override_library->reference,
.tag = LIB_TAG_DOIT,
.missing_tag = LIB_TAG_MISSING};
lib_override_linked_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
lib_override_hierarchy_dependencies_recursive_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
}
FOREACH_MAIN_ID_END;
/* Now check existing overrides, those needing resync will be the one either already tagged as
* such, or the one using linked data that is now tagged as needing override. */
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
continue;
}
if (id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) {
CLOG_INFO(&LOG, 4, "ID %s was already tagged as needing resync", id->name);
continue;
}
MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->relations_from_pointers, id);
BLI_assert(entry != NULL);
for (MainIDRelationsEntryItem *entry_item = entry->to_ids; entry_item != NULL;
entry_item = entry_item->next) {
if (entry_item->usage_flag &
(IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) {
continue;
}
ID *id_to = *entry_item->id_pointer.to;
/* Case where this ID pointer was to a linked ID, that now needs to be overridden. */
if (ID_IS_LINKED(id_to) && (id_to->tag & LIB_TAG_DOIT) != 0) {
id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
CLOG_INFO(&LOG,
3,
"ID %s now tagged as needing resync because they use linked %s that now needs "
"to be overridden",
id->name,
id_to->name);
break;
}
}
}
FOREACH_MAIN_ID_END;
BKE_main_relations_free(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
/* And do the actual resync for all IDs detected as needing it.
* NOTE: Since this changes `bmain` (adding **and** removing IDs), we cannot use
* `FOREACH_MAIN_ID_BEGIN/END` here, and need special multi-loop processing. */
bool do_continue = true;
while (do_continue) {
ListBase *lb;
do_continue = false;
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) {
if ((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0) {
continue;
}
do_continue = true;
const bool success = BKE_lib_override_library_resync(bmain, scene, view_layer, id);
CLOG_INFO(&LOG, 2, "Resynced %s, success: %d", id->name, success);
break;
}
FOREACH_MAIN_LISTBASE_ID_END;
if (do_continue) {
break;
}
}
FOREACH_MAIN_LISTBASE_END;
}
}
/**
* Advanced 'smart' function to delete library overrides (including their existing override
* hierarchy) and remap their usages to their linked reference IDs.
@ -1882,6 +2033,14 @@ void BKE_lib_override_library_main_unused_cleanup(struct Main *bmain)
FOREACH_MAIN_ID_END;
}
static void lib_override_id_swap(Main *bmain, ID *id_local, ID *id_temp)
{
BKE_lib_id_swap(bmain, id_local, id_temp);
/* We need to keep these tags from temp ID into orig one.
* ID swap does not swap most of ID data itself. */
id_local->tag |= (id_temp->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC);
}
/** Update given override from its reference (re-applying overridden properties). */
void BKE_lib_override_library_update(Main *bmain, ID *local)
{
@ -1950,11 +2109,11 @@ void BKE_lib_override_library_update(Main *bmain, ID *local)
/* This also transfers all pointers (memory) owned by local to tmp_id, and vice-versa.
* So when we'll free tmp_id, we'll actually free old, outdated data from local. */
BKE_lib_id_swap(bmain, local, tmp_id);
lib_override_id_swap(bmain, local, tmp_id);
if (local_key != NULL && tmp_key != NULL) {
/* This is some kind of hard-coded 'always enforced override'. */
BKE_lib_id_swap(bmain, &local_key->id, &tmp_key->id);
lib_override_id_swap(bmain, &local_key->id, &tmp_key->id);
tmp_key->id.flag |= (local_key->id.flag & LIB_EMBEDDED_DATA_LIB_OVERRIDE);
/* The swap of local and tmp_id inverted those pointers, we need to redefine proper
* relationships. */

View File

@ -422,15 +422,17 @@ static void libblock_remap_data(
FOREACH_MAIN_ID_END;
}
/* XXX We may not want to always 'transfer' fake-user from old to new id...
* Think for now it's desired behavior though,
* we can always add an option (flag) to control this later if needed. */
if (old_id && (old_id->flag & LIB_FAKEUSER)) {
id_fake_user_clear(old_id);
id_fake_user_set(new_id);
}
if ((remap_flags & ID_REMAP_SKIP_USER_CLEAR) == 0) {
/* XXX We may not want to always 'transfer' fake-user from old to new id...
* Think for now it's desired behavior though,
* we can always add an option (flag) to control this later if needed. */
if (old_id && (old_id->flag & LIB_FAKEUSER)) {
id_fake_user_clear(old_id);
id_fake_user_set(new_id);
}
id_us_clear_real(old_id);
id_us_clear_real(old_id);
}
if (new_id && (new_id->tag & LIB_TAG_INDIRECT) &&
(r_id_remap_data->status & ID_REMAP_IS_LINKED_DIRECT)) {
@ -479,12 +481,14 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
skipped_direct = id_remap_data.skipped_direct;
skipped_refcounted = id_remap_data.skipped_refcounted;
/* If old_id was used by some ugly 'user_one' stuff (like Image or Clip editors...), and user
* count has actually been incremented for that, we have to decrease once more its user count...
* unless we had to skip some 'user_one' cases. */
if ((old_id->tag & LIB_TAG_EXTRAUSER_SET) &&
!(id_remap_data.status & ID_REMAP_IS_USER_ONE_SKIPPED)) {
id_us_clear_real(old_id);
if ((remap_flags & ID_REMAP_SKIP_USER_CLEAR) == 0) {
/* If old_id was used by some ugly 'user_one' stuff (like Image or Clip editors...), and user
* count has actually been incremented for that, we have to decrease once more its user
* count... unless we had to skip some 'user_one' cases. */
if ((old_id->tag & LIB_TAG_EXTRAUSER_SET) &&
!(id_remap_data.status & ID_REMAP_IS_USER_ONE_SKIPPED)) {
id_us_clear_real(old_id);
}
}
if (old_id->us - skipped_refcounted < 0) {

View File

@ -2985,6 +2985,12 @@ static void lib_link_workspace_layout_restore(struct IDNameLib_Map *id_map,
sclip->scopes.ok = 0;
}
else if (sl->spacetype == SPACE_SPREADSHEET) {
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
sspreadsheet->pinned_id = restore_pointer_by_name(
id_map, sspreadsheet->pinned_id, USER_IGNORE);
}
}
}
}

View File

@ -139,13 +139,13 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v
*
* Following Frostbite stochastic SSR.
*
* - First pass Trace rays across the depth buffer. The hit position and pdf are
* - First pass Trace rays across the depth buffer. The hit position and PDF are
* recorded in a RGBA16F render target for each ray (sample).
*
* - We down-sample the previous frame color buffer.
*
* - For each final pixel, we gather neighbors rays and choose a color buffer
* mipmap for each ray using its pdf. (filtered importance sampling)
* mipmap for each ray using its PDF. (filtered importance sampling)
* We then evaluate the lighting from the probes and mix the results together.
*/
DRW_PASS_CREATE(psl->ssr_raytrace, DRW_STATE_WRITE_COLOR);

View File

@ -3095,7 +3095,6 @@ static int gpencil_snap_cursor_to_sel(bContext *C, wmOperator *op)
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
Scene *scene = CTX_data_scene(C);
View3D *v3d = CTX_wm_view3d(C);
float *cursor = scene->cursor.location;
float centroid[3] = {0.0f};
@ -3125,7 +3124,7 @@ static int gpencil_snap_cursor_to_sel(bContext *C, wmOperator *op)
}
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
}
return OPERATOR_FINISHED;

View File

@ -1710,6 +1710,17 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op)
tgpf->mat = ma;
/* Untag strokes to be sure nothing is pending due any canceled process. */
LISTBASE_FOREACH (bGPDlayer *, gpl, &tgpf->gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if (gps->flag & GP_STROKE_TAG) {
gps->flag &= ~GP_STROKE_TAG;
}
}
}
}
/* check whether the material was newly added */
if (totcol != tgpf->ob->totcol) {
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, NULL);

View File

@ -936,6 +936,12 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Data Path"),
ICON_NONE,
"UI_OT_copy_data_path_button");
uiItemBooleanO(layout,
CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy Full Data Path"),
ICON_NONE,
"UI_OT_copy_data_path_button",
"full_path",
true);
if (ptr->owner_id && !is_whole_array &&
ELEM(type, PROP_BOOLEAN, PROP_INT, PROP_FLOAT, PROP_ENUM)) {

View File

@ -1819,7 +1819,7 @@ static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, cons
but->strwidth = BLF_width(fstyle->uifont_id, but->drawstr, sizeof(but->drawstr));
but->ofs = 0;
/* First shorten num-buttons eg,
/* First shorten number-buttons eg,
* Translucency: 0.000
* becomes
* Trans: 0.000
@ -3801,7 +3801,7 @@ static void widget_numslider(
wtb.draw_inner = false;
widgetbase_draw(&wtb, wcol);
/* Add space at either side of the button so text aligns with numbuttons
/* Add space at either side of the button so text aligns with number-buttons
* (which have arrow icons). */
if (!(state & UI_STATE_TEXT_INPUT)) {
rect->xmax -= toffs;

View File

@ -1756,7 +1756,7 @@ static int make_links_data_exec(bContext *C, wmOperator *op)
}
DEG_relations_tag_update(bmain);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, CTX_wm_view3d(C));
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, CTX_wm_view3d(C));
WM_event_add_notifier(C, NC_OBJECT, NULL);
@ -2712,7 +2712,7 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent
DEG_id_tag_update(&base->object->id, ID_RECALC_TRANSFORM);
WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, base->object);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, CTX_wm_view3d(C));
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma);
return OPERATOR_FINISHED;

View File

@ -193,6 +193,8 @@ static void file_init(wmWindowManager *UNUSED(wm), ScrArea *area)
if (sfile->runtime == NULL) {
sfile->runtime = MEM_callocN(sizeof(*sfile->runtime), __func__);
}
/* Validate the params right after file read. */
fileselect_refresh_params(sfile);
}
static void file_exit(wmWindowManager *wm, ScrArea *area)
@ -418,6 +420,15 @@ static void file_on_reload_callback_call(SpaceFile *sfile)
sfile->runtime->on_reload_custom_data = NULL;
}
static void file_reset_filelist_showing_main_data(ScrArea *area, SpaceFile *sfile)
{
if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) {
/* Full refresh of the file list if local asset data was changed. Refreshing this view
* is cheap and users expect this to be updated immediately. */
file_tag_reset_list(area, sfile);
}
}
static void file_listener(const wmSpaceTypeListenerParams *params)
{
ScrArea *area = params->area;
@ -444,6 +455,11 @@ static void file_listener(const wmSpaceTypeListenerParams *params)
ED_area_tag_refresh(area);
}
break;
case ND_SPACE_CHANGED:
/* If the space was just turned into a file/asset browser, the file-list may need to be
* updated to reflect latest changes in main data. */
file_reset_filelist_showing_main_data(area, sfile);
break;
}
switch (wmn->action) {
case NA_JOB_FINISHED:
@ -460,11 +476,7 @@ static void file_listener(const wmSpaceTypeListenerParams *params)
case NA_ADDED:
case NA_REMOVED:
case NA_EDITED:
if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) {
/* Full refresh of the file list if local asset data was changed. Refreshing this view
* is cheap and users expect this to be updated immediately. */
file_tag_reset_list(area, sfile);
}
file_reset_filelist_showing_main_data(area, sfile);
break;
}
break;
@ -888,13 +900,11 @@ static void file_id_remap(ScrArea *area, SpaceLink *sl, ID *UNUSED(old_id), ID *
{
SpaceFile *sfile = (SpaceFile *)sl;
/* If the file shows main data (IDs), tag it for reset. */
if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) {
/* Full refresh of the file list if main data was changed, don't even attempt remap pointers.
* We could give file list types a id-remap callback, but it's probably not worth it.
* Refreshing local file lists is relatively cheap. */
file_tag_reset_list(area, sfile);
}
/* If the file shows main data (IDs), tag it for reset.
* Full reset of the file list if main data was changed, don't even attempt remap pointers.
* We could give file list types a id-remap callback, but it's probably not worth it.
* Refreshing local file lists is relatively cheap. */
file_reset_filelist_showing_main_data(area, sfile);
}
/* only called once, from space/spacetypes.c */

View File

@ -647,7 +647,7 @@ static int material_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEve
BKE_object_material_assign(bmain, ob, ma, ob->totcol + 1, BKE_MAT_ASSIGN_USERPREF);
WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, ob);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, CTX_wm_view3d(C));
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma);
return OPERATOR_FINISHED;

View File

@ -889,6 +889,10 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
/* New C++ based type handle (`TreeElementType` in C, `AbstractTreeElement` in C++). Only some
* support this, eventually this should replace `TreeElement` entirely. */
te->type = outliner_tree_element_type_create(type, te, idv);
if (te->type) {
/* Element types ported to the new design are expected to have their name set at this point! */
BLI_assert(te->name != NULL);
}
if (ELEM(type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) {
/* pass */

View File

@ -18,6 +18,7 @@
* \ingroup spoutliner
*/
#include "DNA_anim_types.h"
#include "DNA_listBase.h"
#include "tree_element_anim_data.hh"
@ -36,15 +37,25 @@ namespace blender::ed::outliner {
static AbstractTreeElement *tree_element_create(int type, TreeElement &legacy_te, void *idv)
{
/* Would be nice to get rid of void * here, can we somehow expect the right type right away?
* Perfect forwarding maybe, once the API is C++ only? */
ID &id = *static_cast<ID *>(idv);
/*
* The following calls make an implicit assumption about what data was passed to the `idv`
* argument of #outliner_add_element(). The old code does this already, here we just centralize
* it as much as possible for now. Would be nice to entirely get rid of that, no more `void *`.
*
* Once #outliner_add_element() is sufficiently simplified, it should be replaced by a C++ call.
* It could take the derived type as template parameter (e.g. #TreeElementAnimData) and use C++
* perfect forwarding to pass any data to the type's constructor.
* If general Outliner code wants to access the data, they can query that through the derived
* element type then. There's no need for `void *` anymore then.
*/
switch (type) {
case TSE_SOME_ID:
return TreeElementID::createFromID(legacy_te, id);
case TSE_ANIM_DATA:
return new TreeElementAnimData(legacy_te, id);
return new TreeElementAnimData(legacy_te, *reinterpret_cast<IdAdtTemplate &>(id).adt);
case TSE_DRIVER_BASE:
return new TreeElementDriverBase(legacy_te, *static_cast<AnimData *>(idv));
case TSE_NLA:
@ -52,7 +63,7 @@ static AbstractTreeElement *tree_element_create(int type, TreeElement &legacy_te
case TSE_NLA_TRACK:
return new TreeElementNLATrack(legacy_te, *static_cast<NlaTrack *>(idv));
case TSE_NLA_ACTION:
return new TreeElementNLAAction(legacy_te);
return new TreeElementNLAAction(legacy_te, *static_cast<bAction *>(idv));
case TSE_GP_LAYER:
return new TreeElementGPencilLayer(legacy_te, *static_cast<bGPDlayer *>(idv));
case TSE_R_LAYER_BASE:

View File

@ -32,8 +32,8 @@
namespace blender::ed::outliner {
TreeElementAnimData::TreeElementAnimData(TreeElement &legacy_te, ID &id)
: AbstractTreeElement(legacy_te), anim_data_(*reinterpret_cast<IdAdtTemplate &>(id).adt)
TreeElementAnimData::TreeElementAnimData(TreeElement &legacy_te, AnimData &anim_data)
: AbstractTreeElement(legacy_te), anim_data_(anim_data)
{
BLI_assert(legacy_te.store_elem->type == TSE_ANIM_DATA);
/* this element's info */

View File

@ -30,7 +30,7 @@ class TreeElementAnimData final : public AbstractTreeElement {
AnimData &anim_data_;
public:
TreeElementAnimData(TreeElement &legacy_te, ID &id);
TreeElementAnimData(TreeElement &legacy_te, AnimData &anim_data);
void expand(SpaceOutliner &space_outliner) const override;

View File

@ -70,9 +70,11 @@ void TreeElementNLATrack::expand(SpaceOutliner &space_outliner) const
/* -------------------------------------------------------------------- */
TreeElementNLAAction::TreeElementNLAAction(TreeElement &legacy_te) : AbstractTreeElement(legacy_te)
TreeElementNLAAction::TreeElementNLAAction(TreeElement &legacy_te, const bAction &action)
: AbstractTreeElement(legacy_te)
{
BLI_assert(legacy_te.store_elem->type == TSE_NLA_ACTION);
legacy_te.name = action.id.name + 2;
}
} // namespace blender::ed::outliner

View File

@ -46,7 +46,7 @@ class TreeElementNLATrack final : public AbstractTreeElement {
class TreeElementNLAAction final : public AbstractTreeElement {
public:
TreeElementNLAAction(TreeElement &legacy_te);
TreeElementNLAAction(TreeElement &legacy_te, const bAction &action);
};
} // namespace blender::ed::outliner

View File

@ -126,7 +126,7 @@ static std::unique_ptr<SpreadsheetDrawer> generate_spreadsheet_drawer(const bCon
return {};
}
Object *object_orig = (Object *)used_id;
if (object_orig->type != OB_MESH) {
if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD)) {
return {};
}
Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig);

View File

@ -332,10 +332,11 @@ static void add_columns_for_attribute(const ReadAttribute *attribute,
}
}
static GeometrySet get_display_geometry_set(Object *object_eval)
static GeometrySet get_display_geometry_set(Object *object_eval,
const GeometryComponentType used_component_type)
{
GeometrySet geometry_set;
if (object_eval->mode == OB_MODE_EDIT) {
if (used_component_type == GEO_COMPONENT_TYPE_MESH && object_eval->mode == OB_MODE_EDIT) {
Mesh *mesh = BKE_modifier_get_evaluated_mesh_from_evaluated_object(object_eval, false);
if (mesh == nullptr) {
return geometry_set;
@ -354,10 +355,87 @@ static GeometrySet get_display_geometry_set(Object *object_eval)
return geometry_set;
}
static Span<int64_t> filter_visible_mesh_vertex_rows(const bContext *C,
Object *object_eval,
const MeshComponent *component,
ResourceCollector &resources)
using IsVertexSelectedFn = FunctionRef<bool(int vertex_index)>;
static void get_selected_vertex_indices(const Mesh &mesh,
const IsVertexSelectedFn is_vertex_selected_fn,
Vector<int64_t> &r_vertex_indices)
{
for (const int i : IndexRange(mesh.totvert)) {
if (is_vertex_selected_fn(i)) {
r_vertex_indices.append(i);
}
}
}
static void get_selected_corner_indices(const Mesh &mesh,
const IsVertexSelectedFn is_vertex_selected_fn,
Vector<int64_t> &r_corner_indices)
{
for (const int i : IndexRange(mesh.totloop)) {
const MLoop &loop = mesh.mloop[i];
if (is_vertex_selected_fn(loop.v)) {
r_corner_indices.append(i);
}
}
}
static void get_selected_polygon_indices(const Mesh &mesh,
const IsVertexSelectedFn is_vertex_selected_fn,
Vector<int64_t> &r_polygon_indices)
{
for (const int poly_index : IndexRange(mesh.totpoly)) {
const MPoly &poly = mesh.mpoly[poly_index];
bool is_selected = true;
for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) {
const MLoop &loop = mesh.mloop[loop_index];
if (!is_vertex_selected_fn(loop.v)) {
is_selected = false;
break;
}
}
if (is_selected) {
r_polygon_indices.append(poly_index);
}
}
}
static void get_selected_edge_indices(const Mesh &mesh,
const IsVertexSelectedFn is_vertex_selected_fn,
Vector<int64_t> &r_edge_indices)
{
for (const int i : IndexRange(mesh.totedge)) {
const MEdge &edge = mesh.medge[i];
if (is_vertex_selected_fn(edge.v1) && is_vertex_selected_fn(edge.v2)) {
r_edge_indices.append(i);
}
}
}
static void get_selected_indices_on_domain(const Mesh &mesh,
const AttributeDomain domain,
const IsVertexSelectedFn is_vertex_selected_fn,
Vector<int64_t> &r_indices)
{
switch (domain) {
case ATTR_DOMAIN_POINT:
return get_selected_vertex_indices(mesh, is_vertex_selected_fn, r_indices);
case ATTR_DOMAIN_POLYGON:
return get_selected_polygon_indices(mesh, is_vertex_selected_fn, r_indices);
case ATTR_DOMAIN_CORNER:
return get_selected_corner_indices(mesh, is_vertex_selected_fn, r_indices);
case ATTR_DOMAIN_EDGE:
return get_selected_edge_indices(mesh, is_vertex_selected_fn, r_indices);
default:
return;
}
}
static Span<int64_t> filter_mesh_elements_by_selection(const bContext *C,
Object *object_eval,
const MeshComponent *component,
const AttributeDomain domain,
ResourceCollector &resources)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
const bool show_only_selected = sspreadsheet->filter_flag & SPREADSHEET_FILTER_SELECTED_ONLY;
@ -372,47 +450,55 @@ static Span<int64_t> filter_visible_mesh_vertex_rows(const bContext *C,
int *orig_indices = (int *)CustomData_get_layer(&mesh_eval->vdata, CD_ORIGINDEX);
if (orig_indices != nullptr) {
/* Use CD_ORIGINDEX layer if it exists. */
for (const int i_eval : IndexRange(mesh_eval->totvert)) {
const int i_orig = orig_indices[i_eval];
if (i_orig >= 0 && i_orig < bm->totvert) {
BMVert *vert = bm->vtable[i_orig];
if (BM_elem_flag_test(vert, BM_ELEM_SELECT)) {
visible_rows.append(i_eval);
}
auto is_vertex_selected = [&](int vertex_index) -> bool {
const int i_orig = orig_indices[vertex_index];
if (i_orig < 0) {
return false;
}
}
if (i_orig >= bm->totvert) {
return false;
}
BMVert *vert = bm->vtable[i_orig];
return BM_elem_flag_test(vert, BM_ELEM_SELECT);
};
get_selected_indices_on_domain(*mesh_eval, domain, is_vertex_selected, visible_rows);
}
else if (mesh_eval->totvert == bm->totvert) {
/* Use a simple heuristic to match original vertices to evaluated ones. */
for (const int i : IndexRange(mesh_eval->totvert)) {
BMVert *vert = bm->vtable[i];
if (BM_elem_flag_test(vert, BM_ELEM_SELECT)) {
visible_rows.append(i);
}
}
auto is_vertex_selected = [&](int vertex_index) -> bool {
BMVert *vert = bm->vtable[vertex_index];
return BM_elem_flag_test(vert, BM_ELEM_SELECT);
};
get_selected_indices_on_domain(*mesh_eval, domain, is_vertex_selected, visible_rows);
}
/* This is safe, because the vector lives in the resource collector. */
return visible_rows.as_span();
}
/* No filter is used. */
const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_POINT);
const int domain_size = component->attribute_domain_size(domain);
return IndexRange(domain_size).as_span();
}
std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_geometry_attributes(const bContext *C,
Object *object_eval)
{
SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C);
const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain;
const GeometryComponentType component_type = (GeometryComponentType)
sspreadsheet->geometry_component_type;
/* Create a resource collector that owns stuff that needs to live until drawing is done. */
std::unique_ptr<ResourceCollector> resources = std::make_unique<ResourceCollector>();
GeometrySet &geometry_set = resources->add_value(get_display_geometry_set(object_eval),
"geometry set");
GeometrySet &geometry_set = resources->add_value(
get_display_geometry_set(object_eval, component_type), "geometry set");
const AttributeDomain domain = ATTR_DOMAIN_POINT;
const GeometryComponentType component_type = GEO_COMPONENT_TYPE_MESH;
const GeometryComponent *component = geometry_set.get_component_for_read(component_type);
if (component == nullptr) {
return {};
}
if (!component->attribute_domain_supported(domain)) {
return {};
}
Vector<std::string> attribute_names = get_sorted_attribute_names_to_display(*component, domain);
@ -425,9 +511,14 @@ std::unique_ptr<SpreadsheetDrawer> spreadsheet_drawer_from_geometry_attributes(c
}
/* The filter below only works for mesh vertices currently. */
BLI_assert(domain == ATTR_DOMAIN_POINT && component_type == GEO_COMPONENT_TYPE_MESH);
Span<int64_t> visible_rows = filter_visible_mesh_vertex_rows(
C, object_eval, static_cast<const MeshComponent *>(component), *resources);
Span<int64_t> visible_rows;
if (component_type == GEO_COMPONENT_TYPE_MESH) {
visible_rows = filter_mesh_elements_by_selection(
C, object_eval, static_cast<const MeshComponent *>(component), domain, *resources);
}
else {
visible_rows = IndexRange(component->attribute_domain_size(domain)).as_span();
}
const int domain_size = component->attribute_domain_size(domain);
return std::make_unique<GeometryAttributeSpreadsheetDrawer>(

View File

@ -1575,7 +1575,7 @@ static void do_view3d_region_buttons(bContext *C, void *UNUSED(index), int event
}
/* default for now */
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
}
static bool view3d_panel_transform_poll(const bContext *C, PanelType *UNUSED(pt))

View File

@ -667,7 +667,7 @@ static int snap_curs_to_grid_exec(bContext *C, wmOperator *UNUSED(op))
curs[1] = gridf * floorf(0.5f + curs[1] / gridf);
curs[2] = gridf * floorf(0.5f + curs[2] / gridf);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); /* hrm */
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL); /* hrm */
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
return OPERATOR_FINISHED;
@ -913,7 +913,7 @@ static int snap_curs_to_active_exec(bContext *C, wmOperator *UNUSED(op))
View3D *v3d = CTX_wm_view3d(C);
if (snap_calc_active_center(C, false, scene->cursor.location)) {
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
return OPERATOR_FINISHED;

View File

@ -163,14 +163,13 @@ const EnumPropertyItem rna_enum_transform_mode_types[] = {
static int select_orientation_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
View3D *v3d = CTX_wm_view3d(C);
int orientation = RNA_enum_get(op->ptr, "orientation");
BKE_scene_orientation_slot_set_index(&scene->orientation_slots[SCE_ORIENT_DEFAULT], orientation);
WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
struct wmMsgBus *mbus = CTX_wm_message_bus(C);
WM_msg_publish_rna_prop(mbus, &scene->id, scene, TransformOrientationSlot, type);
@ -286,7 +285,7 @@ static int create_orientation_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_SCENE | NA_EDITED, scene);
}
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
return OPERATOR_FINISHED;
}

View File

@ -572,6 +572,10 @@ enum {
* When set #ID.session_uuid isn't initialized, since the data isn't part of the session. */
LIB_TAG_TEMP_MAIN = 1 << 20,
/**
* The data-block is a library override that needs re-sync to its linked reference.
*/
LIB_TAG_LIB_OVERRIDE_NEED_RESYNC = 1 << 21,
};
/* Tag given ID for an update in all the dependency graphs. */

View File

@ -1858,7 +1858,12 @@ typedef struct SpaceSpreadsheet {
/* eSpaceSpreadsheet_FilterFlag. */
uint8_t filter_flag;
char _pad1[7];
/* #GeometryComponentType. */
uint8_t geometry_component_type;
/* #AttributeDomain. */
uint8_t attribute_domain;
char _pad1[5];
} SpaceSpreadsheet;
/** \} */

View File

@ -61,7 +61,7 @@ static CLG_LogRef LOG = {"rna.access_compare_override"};
* Find the actual ID owner of the given \a ptr #PointerRNA, in override sense, and generate the
* full rna path from it to given \a prop #PropertyRNA if \a rna_path is given.
*
* \note this is slightly different than 'generic' RNA 'id owner' as returned by
* \note This is slightly different than 'generic' RNA 'id owner' as returned by
* #RNA_find_real_ID_and_path, since in overrides we also consider shape keys as embedded data, not
* only root node trees and master collections.
*/
@ -104,10 +104,6 @@ static ID *rna_property_override_property_real_id_owner(Main *bmain,
}
}
if (!ID_IS_OVERRIDE_LIBRARY_REAL(owner_id)) {
return NULL;
}
if (r_rna_path == NULL) {
return owner_id;
}
@ -1158,6 +1154,39 @@ void RNA_struct_override_apply(Main *bmain,
ptr_storage, op->rna_path, &data_storage, &prop_storage, &data_item_storage);
}
/* Check if an overridden ID pointer supposed to be in sync with linked data gets out of
* sync. */
if ((ptr_dst->owner_id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0 &&
op->rna_prop_type == PROP_POINTER &&
(((IDOverrideLibraryPropertyOperation *)op->operations.first)->flag &
IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE) != 0) {
BLI_assert(ptr_src->owner_id ==
rna_property_override_property_real_id_owner(bmain, &data_src, NULL, NULL));
BLI_assert(ptr_dst->owner_id ==
rna_property_override_property_real_id_owner(bmain, &data_dst, NULL, NULL));
PointerRNA prop_ptr_src = RNA_property_pointer_get(&data_src, prop_src);
PointerRNA prop_ptr_dst = RNA_property_pointer_get(&data_dst, prop_dst);
ID *id_src = rna_property_override_property_real_id_owner(
bmain, &prop_ptr_src, NULL, NULL);
ID *id_dst = rna_property_override_property_real_id_owner(
bmain, &prop_ptr_dst, NULL, NULL);
BLI_assert(id_src == NULL || ID_IS_OVERRIDE_LIBRARY_REAL(id_src));
if (/* We might be in a case where id_dst has already been processed and its usages
* remapped to its new local override. In that case overrides and linked data are
* always properly matching. */
id_src != id_dst &&
/* If one of the pointers is NULL and not the other, or if linked reference ID of
* `id_src` is not `id_dst`, we are in a non-matching case. */
(ELEM(NULL, id_src, id_dst) || id_src->override_library->reference != id_dst)) {
ptr_dst->owner_id->tag |= LIB_TAG_LIB_OVERRIDE_NEED_RESYNC;
CLOG_INFO(
&LOG, 3, "Local override %s detected as needing resync", ptr_dst->owner_id->name);
}
}
rna_property_override_apply_ex(bmain,
&data_dst,
&data_src,

View File

@ -1238,6 +1238,8 @@ static bool rna_property_override_diff_propptr_validate_diffing(PointerRNA *prop
/* Used for both Pointer and Collection properties. */
static int rna_property_override_diff_propptr(Main *bmain,
ID *owner_id_a,
ID *owner_id_b,
PointerRNA *propptr_a,
PointerRNA *propptr_b,
eRNACompareMode mode,
@ -1359,6 +1361,17 @@ static int rna_property_override_diff_propptr(Main *bmain,
* override is not matching its reference anymore. */
opop->flag &= ~IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE;
}
else if ((owner_id_a->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) != 0 ||
(owner_id_b->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) != 0) {
/* In case one of the owner of the checked property is tagged as needing resync, do
* not change the 'match reference' status of its ID pointer properties overrides,
* since many non-matching ones are likely due to missing resync. */
printf(
"%s: Not checking matching ID pointer properties, since owner %s is tagged as "
"needing resync.\n",
__func__,
id_a->name);
}
else if (id_a->override_library != NULL && id_a->override_library->reference == id_b) {
opop->flag |= IDOVERRIDE_LIBRARY_FLAG_IDPOINTER_MATCH_REFERENCE;
}
@ -1778,6 +1791,8 @@ int rna_property_override_diff_default(Main *bmain,
PointerRNA propptr_a = RNA_property_pointer_get(ptr_a, rawprop_a);
PointerRNA propptr_b = RNA_property_pointer_get(ptr_b, rawprop_b);
return rna_property_override_diff_propptr(bmain,
ptr_a->owner_id,
ptr_b->owner_id,
&propptr_a,
&propptr_b,
mode,
@ -1934,6 +1949,8 @@ int rna_property_override_diff_default(Main *bmain,
else if (is_id || is_valid_for_diffing) {
if (equals || do_create) {
const int eq = rna_property_override_diff_propptr(bmain,
ptr_a->owner_id,
ptr_b->owner_id,
&iter_a.ptr,
&iter_b.ptr,
mode,

View File

@ -25,6 +25,8 @@
#include "BLT_translation.h"
#include "BKE_attribute.h"
#include "BKE_geometry_set.h"
#include "BKE_image.h"
#include "BKE_key.h"
#include "BKE_movieclip.h"
@ -2991,6 +2993,48 @@ static void rna_SpaceSpreadsheet_pinned_id_set(PointerRNA *ptr,
sspreadsheet->pinned_id = value.data;
}
static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bmain),
Scene *UNUSED(scene),
PointerRNA *ptr)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data;
if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) {
sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT;
}
}
const EnumPropertyItem *rna_SpaceSpreadsheet_attribute_domain_itemf(bContext *UNUSED(C),
PointerRNA *ptr,
PropertyRNA *UNUSED(prop),
bool *r_free)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data;
EnumPropertyItem *item_array = NULL;
int items_len = 0;
for (const EnumPropertyItem *item = rna_enum_attribute_domain_items; item->identifier != NULL;
item++) {
if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_MESH) {
if (!ELEM(item->value,
ATTR_DOMAIN_CORNER,
ATTR_DOMAIN_EDGE,
ATTR_DOMAIN_POINT,
ATTR_DOMAIN_POLYGON)) {
continue;
}
}
if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) {
if (item->value != ATTR_DOMAIN_POINT) {
continue;
}
}
RNA_enum_item_add(&item_array, &items_len, item);
}
RNA_enum_item_end(&item_array, &items_len);
*r_free = true;
return item_array;
}
#else
static const EnumPropertyItem dt_uv_items[] = {
@ -7196,6 +7240,20 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
PropertyRNA *prop;
StructRNA *srna;
static const EnumPropertyItem geometry_component_type_items[] = {
{GEO_COMPONENT_TYPE_MESH,
"MESH",
ICON_MESH_DATA,
"Mesh",
"Mesh component containing point, corner, edge and polygon data"},
{GEO_COMPONENT_TYPE_POINT_CLOUD,
"POINTCLOUD",
ICON_POINTCLOUD_DATA,
"Point Cloud",
"Point cloud component containing only point data"},
{0, NULL, 0, NULL, NULL},
};
srna = RNA_def_struct(brna, "SpaceSpreadsheet", "Space");
RNA_def_struct_ui_text(srna, "Space Spreadsheet", "Spreadsheet space data");
@ -7210,6 +7268,20 @@ static void rna_def_space_spreadsheet(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Show Only Selected", "Only include rows that correspond to selected elements");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
prop = RNA_def_property(srna, "geometry_component_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, geometry_component_type_items);
RNA_def_property_ui_text(
prop, "Geometry Component", "Part of the geometry to display data from");
RNA_def_property_update(prop,
NC_SPACE | ND_SPACE_SPREADSHEET,
"rna_SpaceSpreadsheet_geometry_component_type_update");
prop = RNA_def_property(srna, "attribute_domain", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items);
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_SpaceSpreadsheet_attribute_domain_itemf");
RNA_def_property_ui_text(prop, "Attribute Domain", "Attribute domain to display");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SPREADSHEET, NULL);
}
void RNA_def_space(BlenderRNA *brna)

View File

@ -331,7 +331,7 @@ class GeometryNodesEvaluator {
values.append(this->get_input_from_incoming_link(socket_to_compute, from_socket));
}
else {
/* If the same from-socket occures more than once, we make a copy of the first value. This
/* If the same from-socket occurs more than once, we make a copy of the first value. This
* can happen when a node linked to a multi-input-socket is muted. */
GMutablePointer value = values[first_occurence];
const CPPType *type = value.type();
@ -647,7 +647,7 @@ static IDProperty *socket_add_property(IDProperty *settings_prop_group,
prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY;
/* Make the group in the ui container group to hold the property's UI settings. */
/* Make the group in the UI container group to hold the property's UI settings. */
IDProperty *prop_ui_group;
{
IDPropertyTemplate idprop = {0};

View File

@ -91,7 +91,8 @@ static void geo_node_subdivide_exec(GeoNodeExecParams params)
Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in);
BKE_mesh_calc_normals(mesh_out);
geometry_set.replace_mesh(mesh_out);
MeshComponent &mesh_component = geometry_set.get_component_for_write<MeshComponent>();
mesh_component.replace_mesh_but_keep_vertex_group_names(mesh_out);
BKE_subdiv_free(subdiv);

View File

@ -181,7 +181,7 @@ void NodeTreeRef::find_origins_skipping_reroutes(InputSocketRef &socket,
this->find_origins_skipping_reroutes(*direct_origin->node_->inputs_[0], r_origins);
}
else {
r_origins.append_non_duplicates(direct_origin);
r_origins.append(direct_origin);
}
}
}

View File

@ -50,7 +50,7 @@ void register_node_type_sh_value(void)
{
static bNodeType ntype;
sh_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0);
sh_fn_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0);
node_type_socket_templates(&ntype, nullptr, sh_node_value_out);
node_type_gpu(&ntype, gpu_shader_value);
ntype.expand_in_mf_network = sh_node_value_expand_in_mf_network;

View File

@ -287,6 +287,9 @@ typedef struct wmNotifier {
#define NC_TEXT (12 << 24)
#define NC_WORLD (13 << 24)
#define NC_ANIMATION (14 << 24)
/* When passing a space as reference data with this (e.g. `WM_event_add_notifier(..., space)`),
* the notifier will only be sent to this space. That avoids unnecessary updates for unrelated
* spaces. */
#define NC_SPACE (15 << 24)
#define NC_GEOM (16 << 24)
#define NC_NODE (17 << 24)

View File

@ -20,7 +20,7 @@
/** \file
* \ingroup wm
*
* Internal functions for managing UI registrable types (operator, UI and menu types).
* Internal functions for managing UI registerable types (operator, UI and menu types).
*
* Also Blender's main event loop (WM_main).
*/
@ -642,10 +642,10 @@ void WM_main(bContext *C)
/* Per window, all events to the window, screen, area and region handlers. */
wm_event_do_handlers(C);
/* Wvents have left notes about changes, we handle and cache it. */
/* Events have left notes about changes, we handle and cache it. */
wm_event_do_notifiers(C);
/* Wxecute cached changes draw. */
/* Execute cached changes draw. */
wm_draw_update(C);
}
}

View File

@ -577,6 +577,10 @@ void wm_event_do_notifiers(bContext *C)
}
ED_screen_areas_iter (win, screen, area) {
if ((note->category == NC_SPACE) && note->reference &&
(note->reference != area->spacedata.first)) {
continue;
}
wmSpaceTypeListenerParams area_params = {
.window = win,
.area = area,
@ -1319,7 +1323,7 @@ static int wm_operator_invoke(bContext *C,
op->flag |= OP_IS_INVOKE;
}
/* /initialize setting from previous run. */
/* Initialize setting from previous run. */
if (!is_nested_call && use_last_properties) { /* Not called by py script. */
WM_operator_last_properties_init(op);
}
@ -3190,7 +3194,7 @@ static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEv
else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
event->type = EVT_DROP;
/* Vreate customdata, first free existing. */
/* Create customdata, first free existing. */
if (event->customdata) {
if (event->customdatafree) {
MEM_freeN(event->customdata);
@ -3201,7 +3205,7 @@ static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEv
event->customdata = &wm->drags;
event->customdatafree = 1;
/* Vlear drop icon. */
/* Clear drop icon. */
screen->do_draw_drag = true;
/* restore cursor (disabled, see wm_dragdrop.c) */

View File

@ -81,6 +81,7 @@
#include "BKE_idprop.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
#include "BKE_packedFile.h"
#include "BKE_report.h"
@ -296,6 +297,33 @@ static void wm_window_match_replace_by_file_wm(bContext *C,
{
wmWindowManager *oldwm = current_wm_list->first;
wmWindowManager *wm = readfile_wm_list->first; /* will become our new WM */
/* Support window-manager ID references being held between file load operations by keeping
* #Main.wm.first memory address in-place, while swapping all of it's contents.
*
* This is needed so items such as key-maps can be held by an add-on,
* without it pointing to invalid memory, see: T86431 */
{
/* Referencing the window-manager pointer from elsewhere in the file is highly unlikely
* however it's possible with ID-properties & animation-drivers.
* At some point we could check on disallowing this since it doesn't seem practical. */
Main *bmain = G_MAIN;
BLI_assert(bmain->relations == NULL);
BKE_libblock_remap(bmain, wm, oldwm, ID_REMAP_SKIP_INDIRECT_USAGE | ID_REMAP_SKIP_USER_CLEAR);
/* Maintain the undo-depth between file loads. Useful so Python can perform
* nested operator calls that exit with the proper undo-depth. */
wm->op_undo_depth = oldwm->op_undo_depth;
/* Simple pointer swapping step. */
BLI_remlink(current_wm_list, oldwm);
BLI_remlink(readfile_wm_list, wm);
SWAP(wmWindowManager, *oldwm, *wm);
SWAP(wmWindowManager *, oldwm, wm);
BLI_addhead(current_wm_list, oldwm);
BLI_addhead(readfile_wm_list, wm);
}
bool has_match = false;
/* this code could move to setup_appdata */
@ -713,8 +741,6 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
WM_cursor_wait(true);
wm_file_read_pre(C, use_data, use_userdef);
/* first try to append data from exotic file formats... */
/* it throws error box when file doesn't exist and returns -1 */
/* note; it should set some error message somewhere... (ton) */
@ -722,58 +748,60 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
/* we didn't succeed, now try to read Blender file */
if (retval == BKE_READ_EXOTIC_OK_BLEND) {
const int G_f_orig = G.f;
ListBase wmbase;
const struct BlendFileReadParams params = {
.is_startup = false,
/* Loading preferences when the user intended to load a regular file is a security
* risk, because the excluded path list is also loaded. Further it's just confusing
* if a user loads a file and various preferences change. */
.skip_flags = BLO_READ_SKIP_USERDEF,
};
/* put aside screens to match with persistent windows later */
/* also exit screens and editors */
wm_window_match_init(C, &wmbase);
struct BlendFileData *bfd = BKE_blendfile_read(filepath, &params, reports);
if (bfd != NULL) {
wm_file_read_pre(C, use_data, use_userdef);
/* confusing this global... */
G.relbase_valid = 1;
success = BKE_blendfile_read(
C,
filepath,
/* Loading preferences when the user intended to load a regular file is a security risk,
* because the excluded path list is also loaded.
* Further it's just confusing if a user loads a file and various preferences change. */
&(const struct BlendFileReadParams){
.is_startup = false,
.skip_flags = BLO_READ_SKIP_USERDEF,
},
reports);
/* Put aside screens to match with persistent windows later,
* also exit screens and editors. */
ListBase wmbase;
wm_window_match_init(C, &wmbase);
/* BKE_file_read sets new Main into context. */
Main *bmain = CTX_data_main(C);
/* This flag is initialized by the operator but overwritten on read.
* need to re-enable it here else drivers + registered scripts wont work. */
const int G_f_orig = G.f;
/* when loading startup.blend's, we can be left with a blank path */
if (BKE_main_blendfile_path(bmain)[0] != '\0') {
G.save_over = 1;
}
else {
G.save_over = 0;
G.relbase_valid = 0;
}
BKE_blendfile_read_setup(C, bfd, &params, reports);
/* this flag is initialized by the operator but overwritten on read.
* need to re-enable it here else drivers + registered scripts wont work. */
if (G.f != G_f_orig) {
const int flags_keep = G_FLAG_ALL_RUNTIME;
G.f &= G_FLAG_ALL_READFILE;
G.f = (G.f & ~flags_keep) | (G_f_orig & flags_keep);
}
if (G.f != G_f_orig) {
const int flags_keep = G_FLAG_ALL_RUNTIME;
G.f &= G_FLAG_ALL_READFILE;
G.f = (G.f & ~flags_keep) | (G_f_orig & flags_keep);
}
/* match the read WM with current WM */
wm_window_match_do(C, &wmbase, &bmain->wm, &bmain->wm);
WM_check(C); /* opens window(s), checks keymaps */
/* #BKE_blendfile_read_result_setup sets new Main into context. */
Main *bmain = CTX_data_main(C);
/* When recovering a session from an unsaved file, this can have a blank path. */
if (BKE_main_blendfile_path(bmain)[0] != '\0') {
G.save_over = 1;
G.relbase_valid = 1;
}
else {
G.save_over = 0;
G.relbase_valid = 0;
}
/* match the read WM with current WM */
wm_window_match_do(C, &wmbase, &bmain->wm, &bmain->wm);
WM_check(C); /* opens window(s), checks keymaps */
if (success) {
if (do_history_file_update) {
wm_history_file_update();
}
}
wm_file_read_post(C, false, false, use_data, use_userdef, false);
wm_file_read_post(C, false, false, use_data, use_userdef, false);
success = true;
}
}
#if 0
else if (retval == BKE_READ_EXOTIC_OK_OTHER) {
@ -950,6 +978,9 @@ void wm_homefile_read(bContext *C,
#endif /* WITH_PYTHON */
}
/* For regular file loading this only runs after the file is successfully read.
* In the case of the startup file, the in-memory startup file is used as a fallback
* so we know this will work if all else fails. */
wm_file_read_pre(C, use_data, use_userdef);
if (use_data) {
@ -1041,15 +1072,17 @@ void wm_homefile_read(bContext *C,
if (!use_factory_settings || (filepath_startup[0] != '\0')) {
if (BLI_access(filepath_startup, R_OK) == 0) {
success = BKE_blendfile_read_ex(C,
filepath_startup,
&(const struct BlendFileReadParams){
.is_startup = true,
.skip_flags = skip_flags | BLO_READ_SKIP_USERDEF,
},
NULL,
update_defaults && use_data,
app_template);
const struct BlendFileReadParams params = {
.is_startup = true,
.skip_flags = skip_flags | BLO_READ_SKIP_USERDEF,
};
struct BlendFileData *bfd = BKE_blendfile_read(filepath_startup, &params, NULL);
if (bfd != NULL) {
BKE_blendfile_read_setup_ex(
C, bfd, &params, NULL, update_defaults && use_data, app_template);
success = true;
}
}
if (success) {
is_factory_startup = filepath_startup_is_factory;
@ -1070,16 +1103,16 @@ void wm_homefile_read(bContext *C,
}
if (success == false) {
success = BKE_blendfile_read_from_memory_ex(C,
datatoc_startup_blend,
datatoc_startup_blend_size,
&(const struct BlendFileReadParams){
.is_startup = true,
.skip_flags = skip_flags,
},
NULL,
true,
NULL);
const struct BlendFileReadParams params = {
.is_startup = true,
.skip_flags = skip_flags,
};
struct BlendFileData *bfd = BKE_blendfile_read_from_memory(
datatoc_startup_blend, datatoc_startup_blend_size, &params, NULL);
if (bfd != NULL) {
BKE_blendfile_read_setup_ex(C, bfd, &params, NULL, true, NULL);
success = true;
}
if (use_data && BLI_listbase_is_empty(&wmbase)) {
wm_clear_default_size(C);