Initial Grease Pencil 3.0 stage #106848

Merged
Falk David merged 224 commits from filedescriptor/blender:grease-pencil-v3 into main 2023-05-30 11:14:22 +02:00
93 changed files with 1579 additions and 691 deletions
Showing only changes of commit 7492a2254b - Show all commits

View File

@ -692,10 +692,12 @@ if(WITH_GHOST_WAYLAND)
if(WITH_GHOST_WAYLAND_LIBDECOR)
if(_use_system_wayland)
pkg_check_modules(libdecor REQUIRED libdecor-0>=0.1)
pkg_check_modules(libdecor libdecor-0>=0.1)
else()
set(libdecor_INCLUDE_DIRS "${LIBDIR}/wayland_libdecor/include/libdecor-0")
set(libdecor_FOUND ON)
endif()
set_and_warn_library_found("libdecor" libdecor_FOUND WITH_GHOST_WAYLAND_LIBDECOR)
endif()
if(WITH_GHOST_WAYLAND_DBUS)

View File

@ -224,10 +224,13 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
}
int blas_index = metal_ancillaries->blas_userID_to_index_lookUp[local_object];
// transform the ray into object's local space
Transform itfm = kernel_data_fetch(objects, local_object).itfm;
r.origin = transform_point(&itfm, r.origin);
r.direction = transform_direction(&itfm, r.direction);
if (!(kernel_data_fetch(object_flag, local_object) & SD_OBJECT_TRANSFORM_APPLIED)) {
// transform the ray into object's local space
Transform itfm = kernel_data_fetch(objects, local_object).itfm;
r.origin = transform_point(&itfm, r.origin);
r.direction = transform_direction(&itfm, r.direction);
}
intersection = metalrt_intersect.intersect(
r,

View File

@ -761,7 +761,7 @@ GHOST_IContext *GHOST_SystemCocoa::createOffscreenContext(GHOST_GLSettings glSet
#ifdef WITH_VULKAN_BACKEND
if (glSettings.context_type == GHOST_kDrawingContextTypeVulkan) {
const bool debug_context = (glSettings.flags & GHOST_glDebugContext) != 0;
GHOST_Context *context = new GHOST_ContextVK(false, NULL, 1, 0, debug_context);
GHOST_Context *context = new GHOST_ContextVK(false, NULL, 1, 2, debug_context);
if (!context->initializeDrawingContext()) {
delete context;
return NULL;

View File

@ -6298,7 +6298,7 @@ GHOST_IContext *GHOST_SystemWayland::createOffscreenContext(GHOST_GLSettings glS
wl_surface,
display_->wl_display,
1,
0,
2,
debug_context);
if (!context->initializeDrawingContext()) {

View File

@ -267,7 +267,7 @@ GHOST_IContext *GHOST_SystemWin32::createOffscreenContext(GHOST_GLSettings glSet
#ifdef WITH_VULKAN_BACKEND
/* Vulkan does not need a window. */
if (glSettings.context_type == GHOST_kDrawingContextTypeVulkan) {
context = new GHOST_ContextVK(false, (HWND)0, 1, 0, debug_context);
context = new GHOST_ContextVK(false, (HWND)0, 1, 2, debug_context);
if (!context->initializeDrawingContext()) {
delete context;

View File

@ -417,7 +417,7 @@ GHOST_IContext *GHOST_SystemX11::createOffscreenContext(GHOST_GLSettings glSetti
#ifdef WITH_VULKAN_BACKEND
if (glSettings.context_type == GHOST_kDrawingContextTypeVulkan) {
context = new GHOST_ContextVK(
false, GHOST_kVulkanPlatformX11, 0, m_display, NULL, NULL, 1, 0, debug_context);
false, GHOST_kVulkanPlatformX11, 0, m_display, NULL, NULL, 1, 2, debug_context);
if (!context->initializeDrawingContext()) {
delete context;

View File

@ -815,7 +815,7 @@ GHOST_Context *GHOST_WindowCocoa::newDrawingContext(GHOST_TDrawingContextType ty
{
#ifdef WITH_VULKAN_BACKEND
if (type == GHOST_kDrawingContextTypeVulkan) {
GHOST_Context *context = new GHOST_ContextVK(m_wantStereoVisual, m_metalLayer, 1, 0, true);
GHOST_Context *context = new GHOST_ContextVK(m_wantStereoVisual, m_metalLayer, 1, 2, true);
if (!context->initializeDrawingContext()) {
delete context;

View File

@ -1744,7 +1744,7 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType
window_->wl_surface,
system_->wl_display(),
1,
0,
2,
true);
break;
#endif

View File

@ -637,7 +637,7 @@ GHOST_Context *GHOST_WindowWin32::newDrawingContext(GHOST_TDrawingContextType ty
#ifdef WITH_VULKAN_BACKEND
else if (type == GHOST_kDrawingContextTypeVulkan) {
GHOST_Context *context = new GHOST_ContextVK(false, m_hWnd, 1, 0, m_debug_context);
GHOST_Context *context = new GHOST_ContextVK(false, m_hWnd, 1, 2, m_debug_context);
if (context->initializeDrawingContext()) {
return context;

View File

@ -1235,7 +1235,7 @@ GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type
NULL,
NULL,
1,
0,
2,
m_is_debug_context);
if (!context->initializeDrawingContext()) {

View File

@ -1,3 +1,5 @@
#!/bin/sh
# Disable ALSA and OSS as they are not available, and trying to initialize them
# breaks sound in other apps. Use PulseAudio instead.
export ALSOFT_DRIVERS=-oss,-alsa,

View File

@ -43,6 +43,8 @@ def get_context_modifier(context):
if context.area.type == 'PROPERTIES':
modifier = context.modifier
else:
if context.object is None:
return False
modifier = context.object.modifiers.active
if modifier is None or modifier.type != 'NODES':
return None

View File

@ -261,6 +261,7 @@ class NODE_MT_view(Menu):
layout.separator()
layout.operator_context = 'INVOKE_REGION_WIN'
layout.operator("node.view_selected")
layout.operator("node.view_all")

View File

@ -32,6 +32,8 @@
#include "BLI_compiler_attrs.h"
#include "BLI_utildefines.h"
#include "DNA_userdef_enums.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -448,7 +450,7 @@ struct ID *BKE_id_copy(struct Main *bmain, const struct ID *id);
*/
struct ID *BKE_id_copy_for_duplicate(struct Main *bmain,
struct ID *id,
uint duplicate_flags,
eDupli_ID_Flags duplicate_flags,
int copy_flags);
/**

View File

@ -148,6 +148,28 @@ enum {
IDWALK_INCLUDE_UI = (1 << 2),
/** Do not process ID pointers inside embedded IDs. Needed by depsgraph processing e.g. */
IDWALK_IGNORE_EMBEDDED_ID = (1 << 3),
/**
* Do not access original processed pointer's data, only process its address value.
*
* This is required in cases where to current address may not be valid anymore (e.g. during
* readfile process). A few ID pointers (like e.g. the `LayerCollection.collection` one) are by
* default accessed to check things (e.g. whether they are pointing to an embedded ID or a
* regular one).
*
* \note Access to owning embedded ID pointers (e.g. `Scene.master_collection`) is not affected
* here, these are presumed always valid.
*
* \note This flag is mutually exclusive with `IDWALK_READONLY` and `IDWALK_RECURSE`, since by
* definition the only thing doable in readonly case is accessing current ID pointer, and this is
* also required for recursion.
*
* \note After remapping, code may access the newly set ID pointer, which is always presumed
* valid.
*
* \warning Use only with great caution, this flag will modify the handling of some ID pointers
* (especially when it comes to detecting `IDWALK_CB_EMBEDDED_NOT_OWNING` usages).
*/
IDWALK_NO_ORIG_POINTERS_ACCESS = (1 << 5),
/**
* Also process internal ID pointers like `ID.newid` or `ID.orig_id`.

View File

@ -88,6 +88,13 @@ enum {
/** Do NOT tag IDs which had some of their ID pointers updated for update in the depsgraph, or ID
* type specific updates, like e.g. with node trees. */
ID_REMAP_SKIP_UPDATE_TAGGING = 1 << 19,
/**
* Do not attempt to access original ID pointers (triggers usages of
* `IDWALK_NO_ORIG_POINTERS_ACCESS` too).
*
* Use when original ID pointers values are (probably) not valid, e.g. dureing readfile process.
*/
ID_REMAP_NO_ORIG_POINTERS_ACCESS = 1 << 20,
};
typedef enum eIDRemapType {

View File

@ -11,6 +11,7 @@
#include "BLI_sys_types.h"
#include "DNA_object_enums.h"
#include "DNA_userdef_enums.h"
#ifdef __cplusplus
extern "C" {
@ -234,7 +235,7 @@ bool BKE_object_obdata_is_libdata(const struct Object *ob);
*/
struct Object *BKE_object_duplicate(struct Main *bmain,
struct Object *ob,
uint dupflag,
eDupli_ID_Flags dupflag,
uint duplicate_options);
/**

View File

@ -168,6 +168,7 @@ static void collection_free_data(ID *id)
static void collection_foreach_id(ID *id, LibraryForeachIDData *data)
{
Collection *collection = (Collection *)id;
const int data_flags = BKE_lib_query_foreachid_process_flags_get(data);
BKE_LIB_FOREACHID_PROCESS_ID(
data, collection->runtime.owner_id, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF);
@ -197,6 +198,7 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data)
/* XXX This is very weak. The whole idea of keeping pointers to private IDs is very bad
* anyway... */
const int cb_flag = ((parent->collection != NULL &&
(data_flags & IDWALK_NO_ORIG_POINTERS_ACCESS) == 0 &&
(parent->collection->id.flag & LIB_EMBEDDED_DATA) != 0) ?
IDWALK_CB_EMBEDDED_NOT_OWNING :
IDWALK_CB_NOP);
@ -885,21 +887,33 @@ ListBase BKE_collection_object_cache_instanced_get(Collection *collection)
static void collection_object_cache_free(Collection *collection)
{
/* Clear own cache an for all parents, since those are affected by changes as well. */
collection->flag &= ~(COLLECTION_HAS_OBJECT_CACHE | COLLECTION_HAS_OBJECT_CACHE_INSTANCED);
BLI_freelistN(&collection->runtime.object_cache);
BLI_freelistN(&collection->runtime.object_cache_instanced);
}
void BKE_collection_object_cache_free(Collection *collection)
static void collection_object_cache_free_parent_recursive(Collection *collection)
{
collection_object_cache_free(collection);
/* Clear cache in all parents recursively, since those are affected by changes as well. */
LISTBASE_FOREACH (CollectionParent *, parent, &collection->runtime.parents) {
collection_object_cache_free(parent->collection);
/* In theory there should be no NULL pointer here. However, this code can be called from
* non-valid temporary states (e.g. indirectly from #BKE_collections_object_remove_invalids
* as part of ID remapping process). */
if (parent->collection == NULL) {
continue;
}
collection_object_cache_free_parent_recursive(parent->collection);
}
}
void BKE_collection_object_cache_free(Collection *collection)
{
BLI_assert(collection != NULL);
collection_object_cache_free_parent_recursive(collection);
}
void BKE_main_collections_object_cache_free(const Main *bmain)
{
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
@ -1467,11 +1481,16 @@ bool BKE_collection_object_replace(Main *bmain,
return false;
}
id_us_min(&cob->ob->id);
cob->ob = ob_new;
id_us_plus(&cob->ob->id);
if (!BLI_ghash_haskey(collection->runtime.gobject_hash, ob_new)) {
id_us_min(&cob->ob->id);
cob->ob = ob_new;
id_us_plus(&cob->ob->id);
BLI_ghash_insert(collection->runtime.gobject_hash, cob->ob, cob);
BLI_ghash_insert(collection->runtime.gobject_hash, cob->ob, cob);
}
else {
collection_object_remove_no_gobject_hash(bmain, collection, cob, false);
}
if (BKE_collection_is_in_scene(collection)) {
BKE_main_collection_sync(bmain);

View File

@ -329,7 +329,7 @@ void BKE_cryptomatte_store_metadata(const struct CryptomatteSession *session,
RenderResult *render_result,
const ViewLayer *view_layer)
{
for (const blender::Map<std::string, blender::bke::cryptomatte::CryptomatteLayer>::Item item :
for (const blender::MapItem<std::string, blender::bke::cryptomatte::CryptomatteLayer> item :
session->layers.items()) {
const blender::StringRefNull layer_name(item.key);
const blender::bke::cryptomatte::CryptomatteLayer &layer = item.value;
@ -457,7 +457,7 @@ static std::string to_manifest(const CryptomatteLayer *layer)
bool is_first = true;
const blender::Map<std::string, CryptomatteHash> &const_map = layer->hashes;
manifest << "{";
for (blender::Map<std::string, CryptomatteHash>::Item item : const_map.items()) {
for (blender::MapItem<std::string, CryptomatteHash> item : const_map.items()) {
if (is_first) {
is_first = false;
}
@ -544,7 +544,7 @@ void CryptomatteLayer::add_hash(blender::StringRef name, CryptomatteHash cryptom
std::optional<std::string> CryptomatteLayer::operator[](float encoded_hash) const
{
const blender::Map<std::string, CryptomatteHash> &const_map = hashes;
for (blender::Map<std::string, CryptomatteHash>::Item item : const_map.items()) {
for (blender::MapItem<std::string, CryptomatteHash> item : const_map.items()) {
if (BKE_cryptomatte_hash_to_float(item.value.hash) == encoded_hash) {
return std::make_optional(item.key);
}

View File

@ -874,7 +874,12 @@ static void id_embedded_swap(ID **embedded_id_a,
struct IDRemapper *remapper_id_b)
{
if (embedded_id_a != NULL && *embedded_id_a != NULL) {
BLI_assert(embedded_id_b != NULL && *embedded_id_b != NULL);
BLI_assert(embedded_id_b != NULL);
if (*embedded_id_b == NULL) {
/* Cannot swap anything if one of the embedded IDs is NULL. */
return;
}
/* Do not remap internal references to itself here, since embedded IDs pointers also need to be
* potentially remapped in owner ID's data, which will also handle embedded IDs data. */

View File

@ -2076,6 +2076,7 @@ static bool lib_override_library_resync(Main *bmain,
}
BKE_id_remapper_free(id_remapper);
BLI_linklist_free(id_override_old_list, nullptr);
id_override_old_list = nullptr;
/* Delete old override IDs.
* Note that we have to use tagged group deletion here, since ID deletion also uses
@ -2083,40 +2084,55 @@ static bool lib_override_library_resync(Main *bmain,
int user_edited_overrides_deletion_count = 0;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (id->tag & LIB_TAG_DOIT) {
/* Note that this works because linked IDs are always after local ones (including
* overrides), so we will only ever tag an old override ID after we have already checked it
* in this loop, hence we cannot untag it later. */
/* Since this code can also be called on linked liboverride now (during recursive resync),
* order of processing cannot guarantee anymore that the old liboverride won't be tagged for
* deletion before being processed by this loop (which would then untag it again).
*
* So instead store old liboverrides in Main into a temp list again, and do the tagging
* separately once this loop over all IDs in main is done. */
if (id->newid != nullptr && id->lib == id_root_reference->lib) {
ID *id_override_old = static_cast<ID *>(BLI_ghash_lookup(linkedref_to_old_override, id));
if (id_override_old != nullptr) {
id->newid->tag &= ~LIB_TAG_DOIT;
id_override_old->tag |= LIB_TAG_DOIT;
if (id_override_old->tag & LIB_TAG_NO_MAIN) {
id_override_old->tag |= LIB_TAG_DOIT;
BLI_assert(BLI_findindex(no_main_ids_list, id_override_old) != -1);
}
else {
/* Defer tagging. */
BLI_linklist_prepend(&id_override_old_list, id_override_old);
}
}
}
id->tag &= ~LIB_TAG_DOIT;
}
/* Also deal with old overrides that went missing in new linked data - only for real local
* overrides for now, not those who are linked. */
else if (id->tag & LIB_TAG_MISSING && !ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY(id)) {
if (ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
id->override_library->reference->lib->id.tag & LIB_TAG_MISSING) {
else if (id->tag & LIB_TAG_MISSING && !ID_IS_LINKED(id) && ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
bool do_delete;
ID *hierarchy_root = id->override_library->hierarchy_root;
if (id->override_library->reference->lib->id.tag & LIB_TAG_MISSING) {
/* Do not delete overrides which reference is missing because the library itself is missing
* (ref. #100586). */
do_delete = false;
}
else if (hierarchy_root != nullptr &&
hierarchy_root->override_library->reference->tag & LIB_TAG_MISSING) {
/* Do not delete overrides which root hierarchy reference is missing. This would typically
* cause more harm than good. */
do_delete = false;
}
else 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;
do_delete = true;
CLOG_INFO(&LOG, 2, "Old override %s is being deleted", id->name);
}
#if 0
else {
/* Otherwise, keep them, user needs to decide whether what to do with them. */
BLI_assert((id->tag & LIB_TAG_DOIT) == 0);
do_delete = false;
id_fake_user_set(id);
id->flag |= LIB_LIB_OVERRIDE_RESYNC_LEFTOVER;
CLOG_INFO(&LOG, 2, "Old override %s is being kept around as it was user-edited", id->name);
@ -2125,17 +2141,27 @@ static bool lib_override_library_resync(Main *bmain,
else {
/* Delete them nevertheless, with fat warning, user needs to decide whether they want to
* save that version of the file (and accept the loss), or not. */
id->tag |= LIB_TAG_DOIT;
id->tag &= ~LIB_TAG_MISSING;
do_delete = true;
CLOG_WARN(
&LOG, "Old override %s is being deleted even though it was user-edited", id->name);
user_edited_overrides_deletion_count++;
}
#endif
if (do_delete) {
id->tag |= LIB_TAG_DOIT;
id->tag &= ~LIB_TAG_MISSING;
}
}
}
FOREACH_MAIN_ID_END;
/* Finalize tagging old liboverrides for deletion. */
for (LinkNode *ln_iter = id_override_old_list; ln_iter != nullptr; ln_iter = ln_iter->next) {
ID *id_override_old = static_cast<ID *>(ln_iter->link);
id_override_old->tag |= LIB_TAG_DOIT;
}
BLI_linklist_free(id_override_old_list, nullptr);
/* Cleanup, many pointers in this GHash are already invalid now. */
BLI_ghash_free(linkedref_to_old_override, nullptr, nullptr);
@ -2375,6 +2401,41 @@ static bool lib_override_resync_tagging_finalize_recurse(
return is_ancestor_tagged_for_resync;
}
/* Return true if the ID should be skipped for resync given current context. */
static bool lib_override_library_main_resync_id_skip_check(ID *id,
const int library_indirect_level)
{
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
return true;
}
if (!lib_override_resync_id_lib_level_is_valid(id, library_indirect_level, true)) {
return true;
}
/* Do not attempt to resync from missing data. */
if (((id->tag | id->override_library->reference->tag) & LIB_TAG_MISSING) != 0) {
return true;
}
if (id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY) {
/* This ID is not part of an override hierarchy. */
BLI_assert((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0);
return true;
}
/* Do not attempt to resync when hierarchy root is missing, this would usually do more harm
* than good. */
ID *hierarchy_root = id->override_library->hierarchy_root;
if (hierarchy_root == nullptr ||
((hierarchy_root->tag | hierarchy_root->override_library->reference->tag) &
LIB_TAG_MISSING) != 0) {
return true;
}
return false;
}
/* Ensure resync of all overrides at one level of indirect usage.
*
* We need to handle each level independently, since an override at level n may be affected by
@ -2411,11 +2472,7 @@ static void lib_override_library_main_resync_on_library_indirect_level(
lib_override_group_tag_data_object_to_collection_init(&data);
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
continue;
}
if (!lib_override_resync_id_lib_level_is_valid(id, library_indirect_level, true)) {
if (lib_override_library_main_resync_id_skip_check(id, library_indirect_level)) {
continue;
}
@ -2424,16 +2481,6 @@ static void lib_override_library_main_resync_on_library_indirect_level(
continue;
}
/* Do not attempt to resync from missing data. */
if (((id->tag | id->override_library->reference->tag) & LIB_TAG_MISSING) != 0) {
continue;
}
if (id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY) {
/* This ID is not part of an override hierarchy. */
continue;
}
data.id_root = id->override_library->reference;
lib_override_linked_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
@ -2449,22 +2496,7 @@ static void lib_override_library_main_resync_on_library_indirect_level(
* such, or the one using linked data that is now tagged as needing override. */
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
FOREACH_MAIN_ID_BEGIN (bmain, id) {
if (!ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
continue;
}
if (!lib_override_resync_id_lib_level_is_valid(id, library_indirect_level, true)) {
continue;
}
/* Do not attempt to resync from missing data. */
if (((id->tag | id->override_library->reference->tag) & LIB_TAG_MISSING) != 0) {
continue;
}
if (id->override_library->flag & IDOVERRIDE_LIBRARY_FLAG_NO_HIERARCHY) {
/* This ID is not part of an override hierarchy. */
BLI_assert((id->tag & LIB_TAG_LIB_OVERRIDE_NEED_RESYNC) == 0);
if (lib_override_library_main_resync_id_skip_check(id, library_indirect_level)) {
continue;
}
@ -2687,6 +2719,9 @@ static int lib_override_libraries_index_define(Main *bmain)
do_continue = false;
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
/* NOTE: In theory all non-liboverride IDs could be skipped here. This does not gives any
* performances boost though, so for now keep it as is (i.e. also consider non-liboverride
* relationships to establish libraries hierarchy). */
BKE_library_foreach_ID_link(
bmain, id, lib_override_sort_libraries_func, &do_continue, IDWALK_READONLY);
}

View File

@ -201,6 +201,12 @@ static bool library_foreach_ID_link(Main *bmain,
LibraryForeachIDData data = {.bmain = bmain};
BLI_assert(inherit_data == NULL || data.bmain == inherit_data->bmain);
/* `IDWALK_NO_ORIG_POINTERS_ACCESS` is mutually exclusive with both `IDWALK_READONLY` and
* `IDWALK_RECURSE`. */
BLI_assert((flag & (IDWALK_NO_ORIG_POINTERS_ACCESS | IDWALK_READONLY)) !=
(IDWALK_NO_ORIG_POINTERS_ACCESS | IDWALK_READONLY));
BLI_assert((flag & (IDWALK_NO_ORIG_POINTERS_ACCESS | IDWALK_RECURSE)) !=
(IDWALK_NO_ORIG_POINTERS_ACCESS | IDWALK_RECURSE));
if (flag & IDWALK_RECURSE) {
/* For now, recursion implies read-only, and no internal pointers. */

View File

@ -483,7 +483,9 @@ static void libblock_remap_data(Main *bmain,
(((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ?
IDWALK_DO_INTERNAL_RUNTIME_POINTERS :
IDWALK_NOP) |
((remap_flags & ID_REMAP_FORCE_UI_POINTERS) != 0 ? IDWALK_INCLUDE_UI : IDWALK_NOP));
((remap_flags & ID_REMAP_FORCE_UI_POINTERS) != 0 ? IDWALK_INCLUDE_UI : IDWALK_NOP) |
((remap_flags & ID_REMAP_NO_ORIG_POINTERS_ACCESS) != 0 ? IDWALK_NO_ORIG_POINTERS_ACCESS :
IDWALK_NOP));
id_remap_data.id_remapper = id_remapper;
id_remap_data.type = remap_type;

View File

@ -13,12 +13,12 @@
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "BLI_bitmap.h"
#include "BLI_math_vector.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
#include "BKE_customdata.h"
#include "BKE_mesh.hh"
#include "BKE_multires.h"
#include "BKE_subdiv.h"
#include "BKE_subdiv_eval.h"
@ -135,8 +135,8 @@ struct MultiresReshapeSmoothContext {
* The data is actually stored as a delta, which is then to be added to the higher levels. */
LinearGrids linear_delta_grids;
/* Index i of this map indicates that base edge i is adjacent to at least one face. */
BLI_bitmap *non_loose_base_edge_map;
/* From #Mesh::loose_edges(). May be empty. */
blender::BitSpan loose_base_edges;
/* Subdivision surface created for geometry at a reshape level. */
Subdiv *reshape_subdiv;
@ -488,9 +488,9 @@ static float get_effective_crease(const MultiresReshapeSmoothContext *reshape_sm
const int base_edge_index)
{
if (!is_crease_supported(reshape_smooth_context)) {
return 255;
return 1.0f;
}
const float *creases = reshape_smooth_context->reshape_context->cd_vertex_crease;
const float *creases = reshape_smooth_context->reshape_context->cd_edge_crease;
return creases ? creases[base_edge_index] : 0.0f;
}
@ -524,7 +524,7 @@ static void context_init(MultiresReshapeSmoothContext *reshape_smooth_context,
linear_grids_init(&reshape_smooth_context->linear_delta_grids);
reshape_smooth_context->non_loose_base_edge_map = nullptr;
reshape_smooth_context->loose_base_edges = {};
reshape_smooth_context->reshape_subdiv = nullptr;
reshape_smooth_context->base_surface_grids = nullptr;
@ -556,8 +556,6 @@ static void context_free_subdiv(MultiresReshapeSmoothContext *reshape_smooth_con
static void context_free(MultiresReshapeSmoothContext *reshape_smooth_context)
{
MEM_freeN(reshape_smooth_context->non_loose_base_edge_map);
context_free_geometry(reshape_smooth_context);
context_free_subdiv(reshape_smooth_context);
base_surface_grids_free(reshape_smooth_context);
@ -619,13 +617,11 @@ static void foreach_single_vertex(const SubdivForeachContext *foreach_context,
const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
const float *cd_vertex_crease = reshape_context->cd_vertex_crease;
if (cd_vertex_crease == nullptr) {
return;
}
float crease = cd_vertex_crease[coarse_vertex_index];
if (crease == 0.0f) {
return;
}
@ -833,7 +829,8 @@ static void foreach_edge(const SubdivForeachContext *foreach_context,
return;
}
/* Ignore all loose edges as well, as they are not communicated to the OpenSubdiv. */
if (!BLI_BITMAP_TEST_BOOL(reshape_smooth_context->non_loose_base_edge_map, coarse_edge_index)) {
if (!reshape_smooth_context->loose_base_edges.is_empty() &&
reshape_smooth_context->loose_base_edges[coarse_edge_index]) {
return;
}
/* Edges without crease are to be ignored as well. */
@ -848,24 +845,20 @@ static void geometry_init_loose_information(MultiresReshapeSmoothContext *reshap
{
const MultiresReshapeContext *reshape_context = reshape_smooth_context->reshape_context;
const Mesh *base_mesh = reshape_context->base_mesh;
const blender::OffsetIndices base_polys = reshape_context->base_polys;
const blender::Span<int> base_corner_edges = reshape_context->base_corner_edges;
reshape_smooth_context->non_loose_base_edge_map = BLI_BITMAP_NEW(base_mesh->totedge,
"non_loose_base_edge_map");
const blender::bke::LooseEdgeCache &loose_edges = base_mesh->loose_edges();
reshape_smooth_context->loose_base_edges = loose_edges.is_loose_bits;
int num_used_edges = 0;
for (const int poly_index : base_polys.index_range()) {
for (const int edge : base_corner_edges.slice(base_polys[poly_index])) {
if (!BLI_BITMAP_TEST_BOOL(reshape_smooth_context->non_loose_base_edge_map, edge)) {
BLI_BITMAP_ENABLE(reshape_smooth_context->non_loose_base_edge_map, edge);
const float crease = get_effective_crease(reshape_smooth_context, edge);
if (crease > 0.0f) {
++num_used_edges;
}
}
for (const int edge : blender::IndexRange(base_mesh->totedge)) {
if (loose_edges.count > 0 && loose_edges.is_loose_bits[edge]) {
continue;
}
const float crease = get_effective_crease(reshape_smooth_context, edge);
if (crease == 0.0f) {
continue;
}
num_used_edges++;
}
const int resolution = get_reshape_level_resolution(reshape_context);

File diff suppressed because it is too large Load Diff

View File

@ -2734,7 +2734,10 @@ void BKE_object_transform_copy(Object *ob_tar, const Object *ob_src)
copy_v3_v3(ob_tar->scale, ob_src->scale);
}
Object *BKE_object_duplicate(Main *bmain, Object *ob, uint dupflag, uint duplicate_options)
Object *BKE_object_duplicate(Main *bmain,
Object *ob,
eDupli_ID_Flags dupflag,
uint duplicate_options)
{
const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0;
const bool is_root_id = (duplicate_options & LIB_ID_DUPLICATE_IS_ROOT_ID) != 0;

View File

@ -758,10 +758,10 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data,
static void scene_foreach_layer_collection(LibraryForeachIDData *data, ListBase *lb)
{
const int data_flags = BKE_lib_query_foreachid_process_flags_get(data);
LISTBASE_FOREACH (LayerCollection *, lc, lb) {
/* XXX This is very weak. The whole idea of keeping pointers to private IDs is very bad
* anyway... */
const int cb_flag = (lc->collection != nullptr &&
(data_flags & IDWALK_NO_ORIG_POINTERS_ACCESS) == 0 &&
(lc->collection->id.flag & LIB_EMBEDDED_DATA) != 0) ?
IDWALK_CB_EMBEDDED_NOT_OWNING :
IDWALK_CB_NOP;

View File

@ -85,8 +85,9 @@ static void screen_foreach_id_dopesheet(LibraryForeachIDData *data, bDopeSheet *
void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area)
{
const bool is_readonly = (BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_READONLY) !=
0;
const int data_flags = BKE_lib_query_foreachid_process_flags_get(data);
const bool is_readonly = (data_flags & IDWALK_READONLY) != 0;
const bool allow_pointer_access = (data_flags & IDWALK_NO_ORIG_POINTERS_ACCESS) == 0;
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, area->full, IDWALK_CB_NOP);
@ -190,7 +191,7 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
while ((tselem = BLI_mempool_iterstep(&iter))) {
/* Do not try to restore non-ID pointers (drivers/sequence/etc.). */
if (TSE_IS_REAL_ID(tselem)) {
const int cb_flag = (tselem->id != NULL &&
const int cb_flag = (tselem->id != NULL && allow_pointer_access &&
(tselem->id->flag & LIB_EMBEDDED_DATA) != 0) ?
IDWALK_CB_EMBEDDED_NOT_OWNING :
IDWALK_CB_NOP;
@ -209,7 +210,7 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
}
case SPACE_NODE: {
SpaceNode *snode = (SpaceNode *)sl;
const bool is_embedded_nodetree = snode->id != NULL &&
const bool is_embedded_nodetree = snode->id != NULL && allow_pointer_access &&
ntreeFromID(snode->id) == snode->nodetree;
BKE_LIB_FOREACHID_PROCESS_ID(data, snode->id, IDWALK_CB_NOP);
@ -227,8 +228,11 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
/* Embedded ID pointers are not remapped (besides exceptions), ensure it still matches
* actual data. Note that `snode->id` was already processed (and therefore potentially
* remapped) above.*/
if (!is_readonly && path != NULL) {
path->nodetree = snode->nodetree = (snode->id == NULL) ? NULL : ntreeFromID(snode->id);
if (!is_readonly) {
snode->nodetree = (snode->id == NULL) ? NULL : ntreeFromID(snode->id);
if (path != NULL) {
path->nodetree = snode->nodetree;
}
}
}
else {
@ -238,10 +242,18 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
}
}
/* Both `snode->id` and `snode->nodetree` have been remapped now, sotheir data can be
* accessed. */
BLI_assert(snode->id == NULL || snode->nodetree == NULL ||
(snode->nodetree->id.flag & LIB_EMBEDDED_DATA) == 0 ||
snode->nodetree == ntreeFromID(snode->id));
if (path != NULL) {
for (path = path->next; path != NULL; path = path->next) {
BLI_assert(path->nodetree != NULL);
BLI_assert((path->nodetree->id.flag & LIB_EMBEDDED_DATA) == 0);
if ((data_flags & IDWALK_NO_ORIG_POINTERS_ACCESS) == 0) {
BLI_assert((path->nodetree->id.flag & LIB_EMBEDDED_DATA) == 0);
}
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, path->nodetree, IDWALK_CB_USER_ONE);
@ -270,13 +282,16 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area
snode->edittree = NULL;
}
}
/* NOTE: It is disputable whether this should be called here, especially when editing it is
* allowed? But for now, for sake of consistency, do it in any case. */
if (is_embedded_nodetree && snode->edittree == snode->nodetree) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, snode->edittree, IDWALK_CB_EMBEDDED_NOT_OWNING);
}
else {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, snode->edittree, IDWALK_CB_NOP);
/* Only process this pointer in readonly case, otherwise could lead to a bad
* double-remapping e.g. */
if (is_embedded_nodetree && snode->edittree == snode->nodetree) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
data, snode->edittree, IDWALK_CB_EMBEDDED_NOT_OWNING);
}
else {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, snode->edittree, IDWALK_CB_NOP);
}
}
break;
}

View File

@ -10,9 +10,9 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "BLI_bitmap.h"
#include "BLI_math_vector.h"
#include "BLI_task.h"
#include "BLI_timeit.hh"
#include "BLI_utildefines.h"
#include "BKE_customdata.h"
@ -78,46 +78,30 @@ bool BKE_subdiv_eval_begin(Subdiv *subdiv,
}
static void set_coarse_positions(Subdiv *subdiv,
const Mesh *mesh,
const float (*coarse_vertex_cos)[3])
const blender::Span<blender::float3> positions,
const blender::bke::LooseVertCache &verts_no_face)
{
const float(*positions)[3] = BKE_mesh_vert_positions(mesh);
const blender::OffsetIndices polys = mesh->polys();
const blender::Span<int> corner_verts = mesh->corner_verts();
/* Mark vertices which needs new coordinates. */
/* TODO(sergey): This is annoying to calculate this on every update,
* maybe it's better to cache this mapping. Or make it possible to have
* OpenSubdiv's vertices match mesh ones? */
BLI_bitmap *vertex_used_map = BLI_BITMAP_NEW(mesh->totvert, "vert used map");
for (const int i : polys.index_range()) {
for (const int vert : corner_verts.slice(polys[i])) {
BLI_BITMAP_ENABLE(vertex_used_map, vert);
}
using namespace blender;
OpenSubdiv_Evaluator *evaluator = subdiv->evaluator;
if (verts_no_face.count == 0) {
evaluator->setCoarsePositions(
evaluator, reinterpret_cast<const float *>(positions.data()), 0, positions.size());
return;
}
/* Use a temporary buffer so we do not upload vertices one at a time to the GPU. */
float(*buffer)[3] = static_cast<float(*)[3]>(
MEM_mallocN(sizeof(float[3]) * mesh->totvert, __func__));
int manifold_vertex_count = 0;
for (int vertex_index = 0, manifold_vertex_index = 0; vertex_index < mesh->totvert;
vertex_index++) {
if (!BLI_BITMAP_TEST_BOOL(vertex_used_map, vertex_index)) {
Array<float3> used_vert_positions(positions.size() - verts_no_face.count);
const BitSpan bits = verts_no_face.is_loose_bits;
int used_vert_count = 0;
for (const int vert : positions.index_range()) {
if (bits[vert]) {
continue;
}
const float *vertex_co;
if (coarse_vertex_cos != nullptr) {
vertex_co = coarse_vertex_cos[vertex_index];
}
else {
vertex_co = positions[vertex_index];
}
copy_v3_v3(&buffer[manifold_vertex_index][0], vertex_co);
manifold_vertex_index++;
manifold_vertex_count++;
used_vert_positions[used_vert_count] = positions[vert];
used_vert_count++;
}
subdiv->evaluator->setCoarsePositions(
subdiv->evaluator, &buffer[0][0], 0, manifold_vertex_count);
MEM_freeN(vertex_used_map);
MEM_freeN(buffer);
evaluator->setCoarsePositions(evaluator,
reinterpret_cast<const float *>(used_vert_positions.data()),
0,
used_vert_positions.size());
}
/* Context which is used to fill face varying data in parallel. */
@ -242,13 +226,20 @@ bool BKE_subdiv_eval_refine_from_mesh(Subdiv *subdiv,
const Mesh *mesh,
const float (*coarse_vertex_cos)[3])
{
using namespace blender;
if (subdiv->evaluator == nullptr) {
/* NOTE: This situation is supposed to be handled by begin(). */
BLI_assert_msg(0, "Is not supposed to happen");
return false;
}
/* Set coordinates of base mesh vertices. */
set_coarse_positions(subdiv, mesh, coarse_vertex_cos);
set_coarse_positions(
subdiv,
coarse_vertex_cos ?
Span(reinterpret_cast<const float3 *>(coarse_vertex_cos), mesh->totvert) :
mesh->vert_positions(),
mesh->verts_no_face());
/* Set face-varying data to UV maps. */
const int num_uv_layers = CustomData_number_of_layers(&mesh->ldata, CD_PROP_FLOAT2);
for (int layer_index = 0; layer_index < num_uv_layers; layer_index++) {

View File

@ -56,6 +56,8 @@
#include "RE_texture.h"
#include "DRW_engine.h"
#include "BLO_read_write.h"
static void texture_init_data(ID *id)
@ -100,6 +102,8 @@ static void texture_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const i
texture_dst->nodetree->owner_id = &texture_dst->id;
}
BLI_listbase_clear((ListBase *)&texture_dst->drawdata);
if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) {
BKE_previewimg_id_copy(&texture_dst->id, &texture_src->id);
}
@ -112,6 +116,8 @@ static void texture_free_data(ID *id)
{
Tex *texture = (Tex *)id;
DRW_drawdata_free(id);
/* is no lib link block, but texture extension */
if (texture->nodetree) {
ntreeFreeEmbeddedTree(texture->nodetree);

View File

@ -63,6 +63,28 @@
namespace blender {
/**
* A key-value-pair stored in a #Map. This is used when looping over Map.items().
*/
template<typename Key, typename Value> struct MapItem {
const Key &key;
const Value &value;
};
/**
* Same as #MapItem, but the value is mutable. The key is still const because changing it might
* change its hash value which would lead to undefined behavior in the #Map.
*/
template<typename Key, typename Value> struct MutableMapItem {
const Key &key;
Value &value;
operator MapItem<Key, Value>() const
{
return {this->key, this->value};
}
};
template<
/**
* Type of the keys stored in the map. Keys have to be movable. Furthermore, the hash and
@ -108,6 +130,8 @@ template<
class Map {
public:
using size_type = int64_t;
using Item = MapItem<Key, Value>;
using MutableItem = MutableMapItem<Key, Value>;
private:
/**
@ -771,21 +795,6 @@ class Map {
}
};
struct Item {
const Key &key;
const Value &value;
};
struct MutableItem {
const Key &key;
Value &value;
operator Item() const
{
return Item{key, value};
}
};
class ItemIterator final : public BaseIteratorRange<ItemIterator> {
public:
using value_type = Item;
@ -850,9 +859,8 @@ class Map {
}
/**
* Returns an iterator over all key-value-pairs in the map. The key-value-pairs are stored in
* a temporary struct with a .key and a .value field.The iterator is invalidated, when the map is
* changed.
* Returns an iterator over all key-value-pairs in the map. The key-value-pairs are stored in a
* #MapItem. The iterator is invalidated, when the map is changed.
*/
ItemIterator items() const
{
@ -860,9 +868,8 @@ class Map {
}
/**
* Returns an iterator over all key-value-pairs in the map. The key-value-pairs are stored in
* a temporary struct with a .key and a .value field. The iterator is invalidated, when the map
* is changed.
* Returns an iterator over all key-value-pairs in the map. The key-value-pairs are stored in a
* #MutableMapItem. The iterator is invalidated, when the map is changed.
*
* This iterator also allows you to modify the value (but not the key).
*/

View File

@ -240,7 +240,7 @@ TEST(map, MutableItemToItemConversion)
map.add(2, 1);
Vector<int> keys, values;
for (Map<int, int>::Item item : map.items()) {
for (MapItem<int, int> item : map.items()) {
keys.append(item.key);
values.append(item.value);
}
@ -628,7 +628,7 @@ TEST(map, RemoveDuringIteration)
Iter begin = map.items().begin();
Iter end = map.items().end();
for (Iter iter = begin; iter != end; ++iter) {
Map<int, int>::MutableItem item = *iter;
MutableMapItem<int, int> item = *iter;
if (item.value == 2) {
map.remove(iter);
}

View File

@ -3119,8 +3119,8 @@ static void read_libblock_undo_restore_at_old_address(FileData *fd, Main *main,
id,
id_old,
true,
ID_REMAP_SKIP_NEVER_NULL_USAGE | ID_REMAP_SKIP_UPDATE_TAGGING |
ID_REMAP_SKIP_USER_REFCOUNT);
(ID_REMAP_NO_ORIG_POINTERS_ACCESS | ID_REMAP_SKIP_NEVER_NULL_USAGE |
ID_REMAP_SKIP_UPDATE_TAGGING | ID_REMAP_SKIP_USER_REFCOUNT));
/* Special temporary usage of this pointer, necessary for the `undo_preserve` call after
* lib-linking to restore some data that should never be affected by undo, e.g. the 3D cursor of

View File

@ -45,7 +45,7 @@ void MetaData::replace_hash_neutral_cryptomatte_keys(const blender::StringRef la
void MetaData::add_to_render_result(RenderResult *render_result) const
{
for (Map<std::string, std::string>::Item entry : entries_.items()) {
for (MapItem<std::string, std::string> entry : entries_.items()) {
BKE_render_result_stamp_data(render_result, entry.key.c_str(), entry.value.c_str());
}
}

View File

@ -51,7 +51,7 @@ void NodeOperationBuilder::convert_to_operations(ExecutionSystem *system)
* so multiple operations can use the same node input.
*/
blender::MultiValueMap<NodeInput *, NodeOperationInput *> inverse_input_map;
for (Map<NodeOperationInput *, NodeInput *>::MutableItem item : input_map_.items()) {
for (MutableMapItem<NodeOperationInput *, NodeInput *> item : input_map_.items()) {
inverse_input_map.add(item.value, item.key);
}

View File

@ -70,12 +70,14 @@ set(SRC
algorithms/COM_algorithm_smaa.hh
algorithms/COM_algorithm_symmetric_separable_blur.hh
cached_resources/intern/cached_texture.cc
cached_resources/intern/morphological_distance_feather_weights.cc
cached_resources/intern/smaa_precomputed_textures.cc
cached_resources/intern/symmetric_blur_weights.cc
cached_resources/intern/symmetric_separable_blur_weights.cc
cached_resources/COM_cached_resource.hh
cached_resources/COM_cached_texture.hh
cached_resources/COM_morphological_distance_feather_weights.hh
cached_resources/COM_smaa_precomputed_textures.hh
cached_resources/COM_symmetric_blur_weights.hh
@ -248,4 +250,16 @@ endforeach()
set(shader_create_info_list_file "${CMAKE_CURRENT_BINARY_DIR}/compositor_shader_create_info_list.hh")
file(GENERATE OUTPUT ${shader_create_info_list_file} CONTENT "${SHADER_CREATE_INFOS_CONTENT}")
if(WITH_TBB)
list(APPEND INC_SYS
${TBB_INCLUDE_DIRS}
)
add_definitions(-DWITH_TBB)
if(WIN32)
# TBB includes Windows.h which will define min/max macros
# that will collide with the stl versions.
add_definitions(-DNOMINMAX)
endif()
endif()
blender_add_lib(bf_realtime_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")

View File

@ -5,6 +5,7 @@
#include "BLI_math_vector_types.hh"
#include "BLI_string_ref.hh"
#include "DNA_ID.h"
#include "DNA_scene_types.h"
#include "DNA_vec_types.h"
@ -70,6 +71,14 @@ class Context {
* appropriate place, which can be directly in the UI or just logged to the output stream. */
virtual void set_info_message(StringRef message) const = 0;
/* Returns the ID recalculate flag of the given ID and reset it to zero. The given ID is assumed
* to be one that has a DrawDataList and conforms to the IdDdtTemplate.
*
* The ID recalculate flag is a mechanism through which one can identify if an ID has changed
* since the last time the flag was reset, hence why the method reset the flag after querying it,
* that is, to ready it to track the next change. */
virtual IDRecalcFlag query_id_recalc_flag(ID *id) const = 0;
/* Get the size of the compositing region. See get_compositing_region(). */
int2 get_compositing_region_size() const;

View File

@ -2,11 +2,7 @@
#pragma once
#include <memory>
#include "BLI_map.hh"
#include "BLI_math_vector_types.hh"
#include "COM_cached_texture.hh"
#include "COM_morphological_distance_feather_weights.hh"
#include "COM_smaa_precomputed_textures.hh"
#include "COM_symmetric_blur_weights.hh"
@ -18,9 +14,10 @@ namespace blender::realtime_compositor {
* Static Cache Manager
*
* A static cache manager is a collection of cached resources that can be retrieved when needed and
* created if not already available. In particular, each cached resource type has its own Map in
* the class, where all instances of that cached resource type are stored and tracked. See the
* CachedResource class for more information.
* created if not already available. In particular, each cached resource type has its own instance
* of a container derived from the CachedResourceContainer type in the class. All instances of that
* cached resource type are stored and tracked in the container. See the CachedResource and
* CachedResourceContainer classes for more information.
*
* The manager deletes the cached resources that are no longer needed. A cached resource is said to
* be not needed when it was not used in the previous evaluation. This is done through the
@ -36,51 +33,18 @@ namespace blender::realtime_compositor {
* evaluation will be deleted before the next evaluation. This mechanism is implemented in the
* reset() method of the class, which should be called before every evaluation. */
class StaticCacheManager {
private:
/* A map that stores all SymmetricBlurWeights cached resources. */
Map<SymmetricBlurWeightsKey, std::unique_ptr<SymmetricBlurWeights>> symmetric_blur_weights_;
/* A map that stores all SymmetricSeparableBlurWeights cached resources. */
Map<SymmetricSeparableBlurWeightsKey, std::unique_ptr<SymmetricSeparableBlurWeights>>
symmetric_separable_blur_weights_;
/* A map that stores all MorphologicalDistanceFeatherWeights cached resources. */
Map<MorphologicalDistanceFeatherWeightsKey, std::unique_ptr<MorphologicalDistanceFeatherWeights>>
morphological_distance_feather_weights_;
/* A unique pointers that stores the cached SMAAPrecomputedTextures, if one is cached. */
std::unique_ptr<SMAAPrecomputedTextures> smaa_precomputed_textures_;
public:
SymmetricBlurWeightsContainer symmetric_blur_weights;
SymmetricSeparableBlurWeightsContainer symmetric_separable_blur_weights;
MorphologicalDistanceFeatherWeightsContainer morphological_distance_feather_weights;
SMAAPrecomputedTexturesContainer smaa_precomputed_textures;
CachedTextureContainer cached_textures;
/* Reset the cache manager by deleting the cached resources that are no longer needed because
* they weren't used in the last evaluation and prepare the remaining cached resources to track
* their needed status in the next evaluation. See the class description for more information.
* This should be called before every evaluation. */
void reset();
/* Check if there is an available SymmetricBlurWeights cached resource with the given parameters
* in the manager, if one exists, return it, otherwise, return a newly created one and add it to
* the manager. In both cases, tag the cached resource as needed to keep it cached for the next
* evaluation. */
SymmetricBlurWeights &get_symmetric_blur_weights(int type, float2 radius);
/* Check if there is an available SymmetricSeparableBlurWeights cached resource with the given
* parameters in the manager, if one exists, return it, otherwise, return a newly created one and
* add it to the manager. In both cases, tag the cached resource as needed to keep it cached for
* the next evaluation. */
SymmetricSeparableBlurWeights &get_symmetric_separable_blur_weights(int type, float radius);
/* Check if there is an available MorphologicalDistanceFeatherWeights cached resource with the
* given parameters in the manager, if one exists, return it, otherwise, return a newly created
* one and add it to the manager. In both cases, tag the cached resource as needed to keep it
* cached for the next evaluation. */
MorphologicalDistanceFeatherWeights &get_morphological_distance_feather_weights(int type,
int radius);
/* Check if a cached SMAA precomputed texture exists, if it does, return it, otherwise, return
* a newly created one and store it in the manager. In both cases, tag the cached resource as
* needed to keep it cached for the next evaluation. */
SMAAPrecomputedTextures &get_smaa_precomputed_textures();
};
} // namespace blender::realtime_compositor

View File

@ -73,7 +73,7 @@ static Result calculate_blending_weights(Context &context, Result &edges, int co
edges.bind_as_texture(shader, "edges_tx");
const SMAAPrecomputedTextures &smaa_precomputed_textures =
context.cache_manager().get_smaa_precomputed_textures();
context.cache_manager().smaa_precomputed_textures.get();
smaa_precomputed_textures.bind_area_texture(shader, "area_tx");
smaa_precomputed_textures.bind_search_texture(shader, "search_tx");

View File

@ -33,7 +33,7 @@ static Result horizontal_pass(Context &context,
input.bind_as_texture(shader, "input_tx");
const SymmetricSeparableBlurWeights &weights =
context.cache_manager().get_symmetric_separable_blur_weights(filter_type, radius);
context.cache_manager().symmetric_separable_blur_weights.get(filter_type, radius);
weights.bind_as_texture(shader, "weights_tx");
Domain domain = input.domain();
@ -84,7 +84,7 @@ static void vertical_pass(Context &context,
horizontal_pass_result.bind_as_texture(shader, "input_tx");
const SymmetricSeparableBlurWeights &weights =
context.cache_manager().get_symmetric_separable_blur_weights(filter_type, radius.y);
context.cache_manager().symmetric_separable_blur_weights.get(filter_type, radius.y);
weights.bind_as_texture(shader, "weights_tx");
Domain domain = original_input.domain();

View File

@ -8,17 +8,17 @@ namespace blender::realtime_compositor {
* Cached Resource.
*
* A cached resource is any resource that can be cached across compositor evaluations and across
* multiple operations. Cached resources are managed by an instance of a StaticCacheManager and are
* freed when they are no longer needed, a state which is represented by the `needed` member in the
* class. For more information on the caching mechanism, see the StaticCacheManager class.
* multiple operations. Cached resources are managed by an instance of a StaticCacheManager, stored
* in an instance of a CachedResourceContainer, and are freed when they are no longer needed, a
* state which is represented by the `needed` member in the class. For more information on the
* caching mechanism, see the StaticCacheManager class.
*
* To add a new cached resource:
*
* - Create a key class that can be used to identify the resource in a Map if needed.
* - Create a derived class from CachedResource to represent the resource.
* - Create a key class that can be used in a Map to identify the resource.
* - Add a new Map to StaticCacheManager mapping the key to the resource.
* - Reset the contents of the added map in StaticCacheManager::reset.
* - Add an appropriate getter method in StaticCacheManager.
* - Create a derived class from CachedResourceContainer to store the resources.
* - Add an instance of the container to StaticCacheManager and call its reset method.
*
* See the existing cached resources for reference. */
class CachedResource {
@ -28,4 +28,23 @@ class CachedResource {
bool needed = true;
};
/* -------------------------------------------------------------------------------------------------
* Cached Resource Container.
*
* A cached resource container stores all the cached resources for a specific cached resource type.
* The cached resources are typically stored in a map identified by a key type. The reset method
* should be implemented as described in StaticCacheManager::reset. An appropriate getter method
* should be provided that properly sets the CachedResource::needed flag as described in the
* description of the StaticCacheManager class.
*
* See the existing cached resources for reference. */
class CachedResourceContainer {
public:
/* Reset the container by deleting the cached resources that are no longer needed because they
* weren't used in the last evaluation and prepare the remaining cached resources to track their
* needed status in the next evaluation. See the description of the StaticCacheManager class for
* more information. This should be called in StaticCacheManager::reset. */
virtual void reset() = 0;
};
} // namespace blender::realtime_compositor

View File

@ -0,0 +1,80 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <cstdint>
#include <memory>
#include <string>
#include "BLI_map.hh"
#include "BLI_math_vector_types.hh"
#include "GPU_texture.h"
#include "DNA_scene_types.h"
#include "DNA_texture_types.h"
#include "COM_cached_resource.hh"
namespace blender::realtime_compositor {
class Context;
/* ------------------------------------------------------------------------------------------------
* Cached Texture Key.
*/
class CachedTextureKey {
public:
int2 size;
float2 offset;
float2 scale;
CachedTextureKey(int2 size, float2 offset, float2 scale);
uint64_t hash() const;
};
bool operator==(const CachedTextureKey &a, const CachedTextureKey &b);
/* -------------------------------------------------------------------------------------------------
* Cached Texture.
*
* A cached resource that computes and caches a GPU texture containing the the result of evaluating
* the given texture ID on a space that spans the given size, modified by the given offset and
* scale. */
class CachedTexture : public CachedResource {
private:
GPUTexture *color_texture_ = nullptr;
GPUTexture *value_texture_ = nullptr;
public:
CachedTexture(Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale);
~CachedTexture();
GPUTexture *color_texture();
GPUTexture *value_texture();
};
/* ------------------------------------------------------------------------------------------------
* Cached Texture Container.
*/
class CachedTextureContainer : CachedResourceContainer {
private:
Map<std::string, Map<CachedTextureKey, std::unique_ptr<CachedTexture>>> map_;
public:
void reset() override;
/* Check if the given texture ID has changed since the last time it was retrieved through its
* recalculate flag, and if so, invalidate its corresponding cached textures and reset the
* recalculate flag to ready it to track the next change. Then, check if there is an available
* CachedTexture cached resource with the given parameters in the container, if one exists,
* return it, otherwise, return a newly created one and add it to the container. In both cases,
* tag the cached resource as needed to keep it cached for the next evaluation. */
CachedTexture &get(
Context &context, Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale);
};
} // namespace blender::realtime_compositor

View File

@ -3,6 +3,9 @@
#pragma once
#include <cstdint>
#include <memory>
#include "BLI_map.hh"
#include "GPU_shader.h"
#include "GPU_texture.h"
@ -58,4 +61,22 @@ class MorphologicalDistanceFeatherWeights : public CachedResource {
void unbind_distance_falloffs_as_texture() const;
};
/* ------------------------------------------------------------------------------------------------
* Morphological Distance Feather Key.
*/
class MorphologicalDistanceFeatherWeightsContainer : CachedResourceContainer {
private:
Map<MorphologicalDistanceFeatherWeightsKey, std::unique_ptr<MorphologicalDistanceFeatherWeights>>
map_;
public:
void reset() override;
/* Check if there is an available MorphologicalDistanceFeatherWeights cached resource with the
* given parameters in the container, if one exists, return it, otherwise, return a newly created
* one and add it to the container. In both cases, tag the cached resource as needed to keep it
* cached for the next evaluation. */
MorphologicalDistanceFeatherWeights &get(int type, int radius);
};
} // namespace blender::realtime_compositor

View File

@ -2,6 +2,8 @@
#pragma once
#include <memory>
#include "GPU_shader.h"
#include "GPU_texture.h"
@ -33,4 +35,20 @@ class SMAAPrecomputedTextures : public CachedResource {
void unbind_area_texture() const;
};
/* ------------------------------------------------------------------------------------------------
* SMAA Precomputed Textures Container.
*/
class SMAAPrecomputedTexturesContainer : public CachedResourceContainer {
private:
std::unique_ptr<SMAAPrecomputedTextures> textures_;
public:
void reset() override;
/* Check if a cached SMAA precomputed texture exists, if it does, return it, otherwise, return
* a newly created one and store it in the container. In both cases, tag the cached resource as
* needed to keep it cached for the next evaluation. */
SMAAPrecomputedTextures &get();
};
} // namespace blender::realtime_compositor

View File

@ -3,7 +3,9 @@
#pragma once
#include <cstdint>
#include <memory>
#include "BLI_map.hh"
#include "BLI_math_vector_types.hh"
#include "GPU_shader.h"
@ -49,4 +51,21 @@ class SymmetricBlurWeights : public CachedResource {
void unbind_as_texture() const;
};
/* ------------------------------------------------------------------------------------------------
* Symmetric Blur Weights Container.
*/
class SymmetricBlurWeightsContainer : public CachedResourceContainer {
private:
Map<SymmetricBlurWeightsKey, std::unique_ptr<SymmetricBlurWeights>> map_;
public:
void reset() override;
/* Check if there is an available SymmetricBlurWeights cached resource with the given parameters
* in the container, if one exists, return it, otherwise, return a newly created one and add it
* to the container. In both cases, tag the cached resource as needed to keep it cached for the
* next evaluation. */
SymmetricBlurWeights &get(int type, float2 radius);
};
} // namespace blender::realtime_compositor

View File

@ -3,7 +3,9 @@
#pragma once
#include <cstdint>
#include <memory>
#include "BLI_map.hh"
#include "BLI_math_vector_types.hh"
#include "GPU_shader.h"
@ -50,4 +52,22 @@ class SymmetricSeparableBlurWeights : public CachedResource {
void unbind_as_texture() const;
};
/* ------------------------------------------------------------------------------------------------
* Symmetric Separable Blur Weights Container.
*/
class SymmetricSeparableBlurWeightsContainer : public CachedResourceContainer {
private:
Map<SymmetricSeparableBlurWeightsKey, std::unique_ptr<SymmetricSeparableBlurWeights>> map_;
public:
void reset() override;
/* Check if there is an available SymmetricSeparableBlurWeights cached resource with the given
* parameters in the container, if one exists, return it, otherwise, return a newly created one
* and add it to the container. In both cases, tag the cached resource as needed to keep it
* cached for the next evaluation. */
SymmetricSeparableBlurWeights &get(int type, float radius);
};
} // namespace blender::realtime_compositor

View File

@ -0,0 +1,145 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <cstdint>
#include <memory>
#include "BLI_array.hh"
#include "BLI_hash.hh"
#include "BLI_index_range.hh"
#include "BLI_math_vector_types.hh"
#include "BLI_task.hh"
#include "GPU_texture.h"
#include "BKE_texture.h"
#include "DNA_ID.h"
#include "DNA_scene_types.h"
#include "DNA_texture_types.h"
#include "RE_texture.h"
#include "COM_cached_texture.hh"
#include "COM_context.hh"
namespace blender::realtime_compositor {
/* --------------------------------------------------------------------
* Cached Texture Key.
*/
CachedTextureKey::CachedTextureKey(int2 size, float2 offset, float2 scale)
: size(size), offset(offset), scale(scale)
{
}
uint64_t CachedTextureKey::hash() const
{
return get_default_hash_3(size, offset, scale);
}
bool operator==(const CachedTextureKey &a, const CachedTextureKey &b)
{
return a.size == b.size && a.offset == b.offset && a.scale == b.scale;
}
/* --------------------------------------------------------------------
* Cached Texture.
*/
CachedTexture::CachedTexture(
Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale)
{
Array<float4> color_pixels(size.x * size.y);
Array<float> value_pixels(size.x * size.y);
threading::parallel_for(IndexRange(size.y), 1, [&](const IndexRange sub_y_range) {
for (const int64_t y : sub_y_range) {
for (const int64_t x : IndexRange(size.x)) {
/* Compute the coordinates in the [0, 1] range and add 0.5 to evaluate the texture at the
* center of pixels in case it was interpolated. */
float2 coordinates = ((float2(x, y) + 0.5f) / float2(size)) * 2.0f - 1.0f;
/* Note that it is expected that the offset is scaled by the scale. */
coordinates = (coordinates + offset) * scale;
TexResult texture_result;
BKE_texture_get_value(scene, texture, coordinates, &texture_result, true);
color_pixels[y * size.x + x] = float4(texture_result.trgba);
value_pixels[y * size.x + x] = texture_result.talpha ? texture_result.trgba[3] :
texture_result.tin;
}
}
});
color_texture_ = GPU_texture_create_2d("Cached Color Texture",
size.x,
size.y,
1,
GPU_RGBA16F,
GPU_TEXTURE_USAGE_SHADER_READ,
*color_pixels.data());
value_texture_ = GPU_texture_create_2d("Cached Value Texture",
size.x,
size.y,
1,
GPU_R16F,
GPU_TEXTURE_USAGE_SHADER_READ,
value_pixels.data());
}
CachedTexture::~CachedTexture()
{
GPU_texture_free(color_texture_);
GPU_texture_free(value_texture_);
}
GPUTexture *CachedTexture::color_texture()
{
return color_texture_;
}
GPUTexture *CachedTexture::value_texture()
{
return value_texture_;
}
/* --------------------------------------------------------------------
* Cached Texture Container.
*/
void CachedTextureContainer::reset()
{
/* First, delete all cached textures that are no longer needed. */
for (auto &cached_textures_for_id : map_.values()) {
cached_textures_for_id.remove_if([](auto item) { return !item.value->needed; });
}
map_.remove_if([](auto item) { return item.value.is_empty(); });
/* Second, reset the needed status of the remaining cached textures to false to ready them to
* track their needed status for the next evaluation. */
for (auto &cached_textures_for_id : map_.values()) {
for (auto &value : cached_textures_for_id.values()) {
value->needed = false;
}
}
}
CachedTexture &CachedTextureContainer::get(
Context &context, Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale)
{
const CachedTextureKey key(size, offset, scale);
auto &cached_textures_for_id = map_.lookup_or_add_default(texture->id.name);
/* Invalidate the cache for that texture ID if it was changed and reset the recalculate flag. */
if (context.query_id_recalc_flag(reinterpret_cast<ID *>(texture)) & ID_RECALC_ALL) {
cached_textures_for_id.clear();
}
auto &cached_texture = *cached_textures_for_id.lookup_or_add_cb(
key, [&]() { return std::make_unique<CachedTexture>(texture, scene, size, offset, scale); });
cached_texture.needed = true;
return cached_texture;
}
} // namespace blender::realtime_compositor

View File

@ -2,6 +2,7 @@
#include <cmath>
#include <cstdint>
#include <memory>
#include "BLI_array.hh"
#include "BLI_hash.hh"
@ -157,4 +158,32 @@ void MorphologicalDistanceFeatherWeights::unbind_distance_falloffs_as_texture()
GPU_texture_unbind(distance_falloffs_texture_);
}
/* --------------------------------------------------------------------
* Morphological Distance Feather Weights Container.
*/
void MorphologicalDistanceFeatherWeightsContainer::reset()
{
/* First, delete all resources that are no longer needed. */
map_.remove_if([](auto item) { return !item.value->needed; });
/* Second, reset the needed status of the remaining resources to false to ready them to track
* their needed status for the next evaluation. */
for (auto &value : map_.values()) {
value->needed = false;
}
}
MorphologicalDistanceFeatherWeights &MorphologicalDistanceFeatherWeightsContainer::get(int type,
int radius)
{
const MorphologicalDistanceFeatherWeightsKey key(type, radius);
auto &weights = *map_.lookup_or_add_cb(
key, [&]() { return std::make_unique<MorphologicalDistanceFeatherWeights>(type, radius); });
weights.needed = true;
return weights;
}
} // namespace blender::realtime_compositor

View File

@ -1,5 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <memory>
#include "BLI_smaa_textures.h"
#include "GPU_shader.h"
@ -9,6 +11,10 @@
namespace blender::realtime_compositor {
/* ------------------------------------------------------------------------------------------------
* SMAA Precomputed Textures.
*/
SMAAPrecomputedTextures::SMAAPrecomputedTextures()
{
search_texture_ = GPU_texture_create_2d("SMAA Search",
@ -61,4 +67,32 @@ void SMAAPrecomputedTextures::unbind_area_texture() const
GPU_texture_unbind(area_texture_);
}
/* ------------------------------------------------------------------------------------------------
* SMAA Precomputed Textures Container.
*/
void SMAAPrecomputedTexturesContainer::reset()
{
/* First, delete the textures if they are no longer needed. */
if (textures_ && !textures_->needed) {
textures_.reset();
}
/* Second, if they were not deleted, reset their needed status to false to ready them to track
* their needed status for the next evaluation. */
if (textures_) {
textures_->needed = false;
}
}
SMAAPrecomputedTextures &SMAAPrecomputedTexturesContainer::get()
{
if (!textures_) {
textures_ = std::make_unique<SMAAPrecomputedTextures>();
}
textures_->needed = true;
return *textures_;
}
} // namespace blender::realtime_compositor

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <cstdint>
#include <memory>
#include "BLI_array.hh"
#include "BLI_hash.hh"
@ -113,4 +114,31 @@ void SymmetricBlurWeights::unbind_as_texture() const
GPU_texture_unbind(texture_);
}
/* --------------------------------------------------------------------
* Symmetric Blur Weights Container.
*/
void SymmetricBlurWeightsContainer::reset()
{
/* First, delete all resources that are no longer needed. */
map_.remove_if([](auto item) { return !item.value->needed; });
/* Second, reset the needed status of the remaining resources to false to ready them to track
* their needed status for the next evaluation. */
for (auto &value : map_.values()) {
value->needed = false;
}
}
SymmetricBlurWeights &SymmetricBlurWeightsContainer::get(int type, float2 radius)
{
const SymmetricBlurWeightsKey key(type, radius);
auto &weights = *map_.lookup_or_add_cb(
key, [&]() { return std::make_unique<SymmetricBlurWeights>(type, radius); });
weights.needed = true;
return weights;
}
} // namespace blender::realtime_compositor

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <cstdint>
#include <memory>
#include "BLI_array.hh"
#include "BLI_hash.hh"
@ -91,4 +92,31 @@ void SymmetricSeparableBlurWeights::unbind_as_texture() const
GPU_texture_unbind(texture_);
}
/* --------------------------------------------------------------------
* Symmetric Separable Blur Weights Container.
*/
void SymmetricSeparableBlurWeightsContainer::reset()
{
/* First, delete all resources that are no longer needed. */
map_.remove_if([](auto item) { return !item.value->needed; });
/* Second, reset the needed status of the remaining resources to false to ready them to track
* their needed status for the next evaluation. */
for (auto &value : map_.values()) {
value->needed = false;
}
}
SymmetricSeparableBlurWeights &SymmetricSeparableBlurWeightsContainer::get(int type, float radius)
{
const SymmetricSeparableBlurWeightsKey key(type, radius);
auto &weights = *map_.lookup_or_add_cb(
key, [&]() { return std::make_unique<SymmetricSeparableBlurWeights>(type, radius); });
weights.needed = true;
return weights;
}
} // namespace blender::realtime_compositor

View File

@ -1,91 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include <memory>
#include "BLI_math_vector_types.hh"
#include "COM_morphological_distance_feather_weights.hh"
#include "COM_smaa_precomputed_textures.hh"
#include "COM_symmetric_blur_weights.hh"
#include "COM_symmetric_separable_blur_weights.hh"
#include "COM_static_cache_manager.hh"
namespace blender::realtime_compositor {
/* --------------------------------------------------------------------
* Static Cache Manager.
*/
void StaticCacheManager::reset()
{
/* First, delete all resources that are no longer needed. */
symmetric_blur_weights_.remove_if([](auto item) { return !item.value->needed; });
symmetric_separable_blur_weights_.remove_if([](auto item) { return !item.value->needed; });
morphological_distance_feather_weights_.remove_if([](auto item) { return !item.value->needed; });
if (smaa_precomputed_textures_ && !smaa_precomputed_textures_->needed) {
smaa_precomputed_textures_.reset();
}
/* Second, reset the needed status of the remaining resources to false to ready them to track
* their needed status for the next evaluation. */
for (auto &value : symmetric_blur_weights_.values()) {
value->needed = false;
}
for (auto &value : symmetric_separable_blur_weights_.values()) {
value->needed = false;
}
for (auto &value : morphological_distance_feather_weights_.values()) {
value->needed = false;
}
if (smaa_precomputed_textures_) {
smaa_precomputed_textures_->needed = false;
}
}
SymmetricBlurWeights &StaticCacheManager::get_symmetric_blur_weights(int type, float2 radius)
{
const SymmetricBlurWeightsKey key(type, radius);
auto &weights = *symmetric_blur_weights_.lookup_or_add_cb(
key, [&]() { return std::make_unique<SymmetricBlurWeights>(type, radius); });
weights.needed = true;
return weights;
}
SymmetricSeparableBlurWeights &StaticCacheManager::get_symmetric_separable_blur_weights(
int type, float radius)
{
const SymmetricSeparableBlurWeightsKey key(type, radius);
auto &weights = *symmetric_separable_blur_weights_.lookup_or_add_cb(
key, [&]() { return std::make_unique<SymmetricSeparableBlurWeights>(type, radius); });
weights.needed = true;
return weights;
}
MorphologicalDistanceFeatherWeights &StaticCacheManager::
get_morphological_distance_feather_weights(int type, int radius)
{
const MorphologicalDistanceFeatherWeightsKey key(type, radius);
auto &weights = *morphological_distance_feather_weights_.lookup_or_add_cb(
key, [&]() { return std::make_unique<MorphologicalDistanceFeatherWeights>(type, radius); });
weights.needed = true;
return weights;
}
SMAAPrecomputedTextures &StaticCacheManager::get_smaa_precomputed_textures()
{
if (!smaa_precomputed_textures_) {
smaa_precomputed_textures_ = std::make_unique<SMAAPrecomputedTextures>();
}
smaa_precomputed_textures_->needed = true;
return *smaa_precomputed_textures_;
symmetric_blur_weights.reset();
symmetric_separable_blur_weights.reset();
morphological_distance_feather_weights.reset();
cached_textures.reset();
smaa_precomputed_textures.reset();
}
} // namespace blender::realtime_compositor

View File

@ -8,6 +8,7 @@
#include "BLT_translation.h"
#include "DNA_ID.h"
#include "DNA_ID_enums.h"
#include "DNA_camera_types.h"
#include "DNA_object_types.h"
@ -143,6 +144,15 @@ class Context : public realtime_compositor::Context {
{
message.copy(info_message_, GPU_INFO_SIZE);
}
IDRecalcFlag query_id_recalc_flag(ID *id) const override
{
DrawEngineType *owner = &draw_engine_compositor_type;
DrawData *draw_data = DRW_drawdata_ensure(id, owner, sizeof(DrawData), nullptr, nullptr);
IDRecalcFlag recalc_flag = IDRecalcFlag(draw_data->recalc);
draw_data->recalc = IDRecalcFlag(0);
return recalc_flag;
}
};
class Engine {

View File

@ -263,7 +263,7 @@ void VelocityModule::end_sync()
uint32_t max_resource_id_ = 1u;
for (Map<ObjectKey, VelocityObjectData>::Item item : velocity_map.items()) {
for (MapItem<ObjectKey, VelocityObjectData> item : velocity_map.items()) {
if (item.value.obj.resource_id == uint32_t(-1)) {
deleted_obj.append(item.key);
}

View File

@ -833,6 +833,7 @@ static bool id_type_can_have_drawdata(const short id_type)
case ID_OB:
case ID_WO:
case ID_SCE:
case ID_TE:
return true;
/* no DrawData */

View File

@ -3158,12 +3158,14 @@ static bool ui_textedit_insert_buf(uiBut *but,
return changed;
}
#ifdef WITH_INPUT_IME
static bool ui_textedit_insert_ascii(uiBut *but, uiHandleButtonData *data, const char ascii)
{
BLI_assert(isascii(ascii));
const char buf[2] = {ascii, '\0'};
return ui_textedit_insert_buf(but, data, buf, sizeof(buf) - 1);
}
#endif
static void ui_textedit_move(uiBut *but,
uiHandleButtonData *data,
@ -3940,9 +3942,6 @@ static void ui_do_but_textedit(
else if (event->type == WM_IME_COMPOSITE_END) {
changed = true;
}
#else
/* Prevent the function from being unused. */
(void)ui_textedit_insert_ascii;
#endif
if (changed) {

View File

@ -395,8 +395,7 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
const bool create_collection = RNA_boolean_get(op->ptr, "create_collection");
char prim_path_mask[1024];
RNA_string_get(op->ptr, "prim_path_mask", prim_path_mask);
char *prim_path_mask = RNA_string_get_alloc(op->ptr, "prim_path_mask", NULL, 0, NULL);
const bool import_guide = RNA_boolean_get(op->ptr, "import_guide");
const bool import_proxy = RNA_boolean_get(op->ptr, "import_proxy");
@ -448,6 +447,7 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
.import_meshes = import_meshes,
.import_volumes = import_volumes,
.import_shapes = import_shapes,
.prim_path_mask = prim_path_mask,
.import_subdiv = import_subdiv,
.import_instance_proxies = import_instance_proxies,
.create_collection = create_collection,
@ -464,7 +464,6 @@ static int wm_usd_import_exec(bContext *C, wmOperator *op)
.tex_name_collision_mode = tex_name_collision_mode,
.import_all_materials = import_all_materials};
STRNCPY(params.prim_path_mask, prim_path_mask);
STRNCPY(params.import_textures_dir, import_textures_dir);
const bool ok = USD_import(C, filename, &params, as_background_job);
@ -620,12 +619,14 @@ void WM_OT_usd_import(struct wmOperatorType *ot)
RNA_def_boolean(
ot->srna, "read_mesh_colors", true, "Color Attributes", "Read mesh color attributes");
RNA_def_string(ot->srna,
"prim_path_mask",
NULL,
1024,
"Path Mask",
"Import only the subset of the USD scene rooted at the given primitive");
RNA_def_string(
ot->srna,
"prim_path_mask",
NULL,
0,
"Path Mask",
"Import only the primitive at the given path and its descendents. "
"Multiple paths may be specified in a list delimited by commas or semicolons");
RNA_def_boolean(ot->srna, "import_guide", false, "Guide", "Import guide geometry");

View File

@ -260,6 +260,7 @@ void SCULPT_filter_cache_free(SculptSession *ss)
MEM_SAFE_FREE(ss->filter_cache->limit_surface_co);
MEM_SAFE_FREE(ss->filter_cache->pre_smoothed_color);
MEM_delete<FilterCache>(ss->filter_cache);
ss->filter_cache = nullptr;
}
typedef enum eSculptMeshFilterType {

View File

@ -16,21 +16,15 @@
namespace blender::ed::outliner {
static void outliner_context_selected_ids_recursive(const SpaceOutliner &space_outliner,
bContextDataResult *result)
static void outliner_context_selected_ids(const SpaceOutliner *space_outliner,
bContextDataResult *result)
{
tree_iterator::all(space_outliner, [&](const TreeElement *te) {
tree_iterator::all(*space_outliner, [&](const TreeElement *te) {
const TreeStoreElem *tse = TREESTORE(te);
if ((tse->flag & TSE_SELECTED) && ELEM(tse->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) {
CTX_data_id_list_add(result, tse->id);
}
});
}
static void outliner_context_selected_ids(const SpaceOutliner *space_outliner,
bContextDataResult *result)
{
outliner_context_selected_ids_recursive(*space_outliner, result);
CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
}

View File

@ -1246,13 +1246,7 @@ static void snap_source_active_fn(TransInfo *t)
{
/* Only need to calculate once */
if ((t->tsnap.status & SNAP_SOURCE_FOUND) == 0) {
if (t->around == V3D_AROUND_ACTIVE) {
/* Just copy the already calculated active center. */
copy_v3_v3(t->tsnap.snap_source, t->center_global);
TargetSnapOffset(t, nullptr);
t->tsnap.status |= SNAP_SOURCE_FOUND;
}
else if (calculateCenterActive(t, true, t->tsnap.snap_source)) {
if (calculateCenterActive(t, true, t->tsnap.snap_source)) {
TargetSnapOffset(t, nullptr);
t->tsnap.status |= SNAP_SOURCE_FOUND;
}

View File

@ -336,7 +336,7 @@ struct MTLContextTextureUtils {
template<typename T> void free_cached_pso_map(blender::Map<T, id<MTLComputePipelineState>> &map)
{
for (typename blender::Map<T, id<MTLComputePipelineState>>::MutableItem item : map.items()) {
for (typename blender::MutableMapItem<T, id<MTLComputePipelineState>> item : map.items()) {
[item.value release];
}
map.clear();

View File

@ -8,7 +8,7 @@ vec3 compute_masks(vec2 uv)
/* Correct aspect ratio for 2D views not using uniform scaling.
* uv is already in pixel space so a uniform scale should give us a ratio of 1. */
float ratio = (butCo != -2.0) ? (dFdy(uv.y) / dFdx(uv.x)) : 1.0;
float ratio = (butCo != -2.0) ? abs(dFdy(uv.y) / dFdx(uv.x)) : 1.0;
vec2 uv_sdf = uv;
uv_sdf.x *= ratio;

View File

@ -151,13 +151,36 @@ shaderc::Compiler &VKBackend::get_shaderc_compiler()
return shaderc_compiler_;
}
void VKBackend::capabilities_init(VKContext & /*context*/)
void VKBackend::capabilities_init(VKContext &context)
{
const VkPhysicalDeviceLimits limits = context.physical_device_limits_get();
/* Reset all capabilities from previous context. */
GCaps = {};
GCaps.compute_shader_support = true;
GCaps.shader_storage_buffer_objects_support = true;
GCaps.shader_image_load_store_support = true;
GCaps.max_texture_size = max_ii(limits.maxImageDimension1D, limits.maxImageDimension2D);
GCaps.max_texture_3d_size = limits.maxImageDimension3D;
GCaps.max_texture_layers = limits.maxImageArrayLayers;
GCaps.max_textures = limits.maxDescriptorSetSampledImages;
GCaps.max_textures_vert = limits.maxPerStageDescriptorSampledImages;
GCaps.max_textures_geom = limits.maxPerStageDescriptorSampledImages;
GCaps.max_textures_frag = limits.maxPerStageDescriptorSampledImages;
GCaps.max_samplers = limits.maxSamplerAllocationCount;
for (int i = 0; i < 3; i++) {
GCaps.max_work_group_count[i] = limits.maxComputeWorkGroupCount[i];
GCaps.max_work_group_size[i] = limits.maxComputeWorkGroupSize[i];
}
GCaps.max_uniforms_vert = limits.maxPerStageDescriptorUniformBuffers;
GCaps.max_uniforms_frag = limits.maxPerStageDescriptorUniformBuffers;
GCaps.max_batch_indices = limits.maxDrawIndirectCount;
GCaps.max_batch_vertices = limits.maxDrawIndexedIndexValue;
GCaps.max_vertex_attribs = limits.maxVertexInputAttributes;
GCaps.max_varying_floats = limits.maxVertexOutputComponents;
GCaps.max_shader_storage_buffer_bindings = limits.maxPerStageDescriptorStorageBuffers;
GCaps.max_compute_shader_storage_blocks = limits.maxPerStageDescriptorStorageBuffers;
}
} // namespace blender::gpu

View File

@ -25,9 +25,8 @@ static VmaAllocationCreateFlagBits vma_allocation_flags(GPUUsageType usage)
{
switch (usage) {
case GPU_USAGE_STATIC:
return static_cast<VmaAllocationCreateFlagBits>(
VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT);
case GPU_USAGE_DYNAMIC:
case GPU_USAGE_STREAM:
return static_cast<VmaAllocationCreateFlagBits>(
VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT);
case GPU_USAGE_DEVICE_ONLY:
@ -35,7 +34,6 @@ static VmaAllocationCreateFlagBits vma_allocation_flags(GPUUsageType usage)
VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT |
VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT);
case GPU_USAGE_FLAG_BUFFER_TEXTURE_ONLY:
case GPU_USAGE_STREAM:
break;
}
BLI_assert_msg(false, "Unimplemented GPUUsageType");
@ -83,6 +81,10 @@ void VKBuffer::update(const void *data) const
{
BLI_assert_msg(is_mapped(), "Cannot update a non-mapped buffer.");
memcpy(mapped_memory_, data, size_in_bytes_);
VKContext &context = *VKContext::get();
VmaAllocator mem_allocator = context.mem_allocator_get();
vmaFlushAllocation(mem_allocator, allocation_, 0, VK_WHOLE_SIZE);
}
void VKBuffer::clear(VKContext &context, uint32_t clear_value)

View File

@ -63,4 +63,5 @@ class VKBuffer {
bool map(VKContext &context);
void unmap(VKContext &context);
};
} // namespace blender::gpu

View File

@ -12,6 +12,7 @@
#include "vk_memory.hh"
#include "vk_pipeline.hh"
#include "vk_texture.hh"
#include "vk_vertex_buffer.hh"
#include "BLI_assert.h"
@ -34,30 +35,42 @@ void VKCommandBuffer::init(const VkDevice vk_device,
vk_queue_ = vk_queue;
vk_command_buffer_ = vk_command_buffer;
submission_id_.reset();
state.stage = Stage::Initial;
if (vk_fence_ == VK_NULL_HANDLE) {
VK_ALLOCATION_CALLBACKS;
VkFenceCreateInfo fenceInfo{};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
vkCreateFence(vk_device_, &fenceInfo, vk_allocation_callbacks, &vk_fence_);
}
else {
vkResetFences(vk_device_, 1, &vk_fence_);
}
}
void VKCommandBuffer::begin_recording()
{
vkWaitForFences(vk_device_, 1, &vk_fence_, VK_TRUE, UINT64_MAX);
vkResetFences(vk_device_, 1, &vk_fence_);
vkResetCommandBuffer(vk_command_buffer_, 0);
if (is_in_stage(Stage::Submitted)) {
vkWaitForFences(vk_device_, 1, &vk_fence_, VK_TRUE, FenceTimeout);
vkResetFences(vk_device_, 1, &vk_fence_);
stage_transfer(Stage::Submitted, Stage::Executed);
}
if (is_in_stage(Stage::Executed)) {
vkResetCommandBuffer(vk_command_buffer_, 0);
stage_transfer(Stage::Executed, Stage::Initial);
}
VkCommandBufferBeginInfo begin_info = {};
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
vkBeginCommandBuffer(vk_command_buffer_, &begin_info);
stage_transfer(Stage::Initial, Stage::Recording);
}
void VKCommandBuffer::end_recording()
{
ensure_no_active_framebuffer();
vkEndCommandBuffer(vk_command_buffer_);
stage_transfer(Stage::Recording, Stage::BetweenRecordingAndSubmitting);
}
void VKCommandBuffer::bind(const VKPipeline &pipeline, VkPipelineBindPoint bind_point)
@ -74,19 +87,35 @@ void VKCommandBuffer::bind(const VKDescriptorSet &descriptor_set,
vk_command_buffer_, bind_point, vk_pipeline_layout, 0, 1, &vk_descriptor_set, 0, 0);
}
void VKCommandBuffer::begin_render_pass(const VKFrameBuffer &framebuffer)
void VKCommandBuffer::bind(const uint32_t binding,
const VKVertexBuffer &vertex_buffer,
const VkDeviceSize offset)
{
VkRenderPassBeginInfo render_pass_begin_info = {};
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_begin_info.renderPass = framebuffer.vk_render_pass_get();
render_pass_begin_info.framebuffer = framebuffer.vk_framebuffer_get();
render_pass_begin_info.renderArea = framebuffer.vk_render_area_get();
vkCmdBeginRenderPass(vk_command_buffer_, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
bind(binding, vertex_buffer.vk_handle(), offset);
}
void VKCommandBuffer::end_render_pass(const VKFrameBuffer & /*framebuffer*/)
void VKCommandBuffer::bind(const uint32_t binding,
const VkBuffer &vk_vertex_buffer,
const VkDeviceSize offset)
{
vkCmdEndRenderPass(vk_command_buffer_);
validate_framebuffer_exists();
ensure_active_framebuffer();
vkCmdBindVertexBuffers(vk_command_buffer_, binding, 1, &vk_vertex_buffer, &offset);
}
void VKCommandBuffer::begin_render_pass(const VKFrameBuffer &framebuffer)
{
validate_framebuffer_not_exists();
state.framebuffer_ = &framebuffer;
}
void VKCommandBuffer::end_render_pass(const VKFrameBuffer &framebuffer)
{
UNUSED_VARS_NDEBUG(framebuffer)
validate_framebuffer_exists();
BLI_assert(state.framebuffer_ == &framebuffer);
ensure_no_active_framebuffer();
state.framebuffer_ = nullptr;
}
void VKCommandBuffer::push_constants(const VKPushConstants &push_constants,
@ -105,6 +134,7 @@ void VKCommandBuffer::push_constants(const VKPushConstants &push_constants,
void VKCommandBuffer::fill(VKBuffer &buffer, uint32_t clear_data)
{
ensure_no_active_framebuffer();
vkCmdFillBuffer(vk_command_buffer_, buffer.vk_handle(), 0, buffer.size_in_bytes(), clear_data);
}
@ -112,6 +142,7 @@ void VKCommandBuffer::copy(VKBuffer &dst_buffer,
VKTexture &src_texture,
Span<VkBufferImageCopy> regions)
{
ensure_no_active_framebuffer();
vkCmdCopyImageToBuffer(vk_command_buffer_,
src_texture.vk_image_handle(),
src_texture.current_layout_get(),
@ -123,6 +154,7 @@ void VKCommandBuffer::copy(VKTexture &dst_texture,
VKBuffer &src_buffer,
Span<VkBufferImageCopy> regions)
{
ensure_no_active_framebuffer();
vkCmdCopyBufferToImage(vk_command_buffer_,
src_buffer.vk_handle(),
dst_texture.vk_image_handle(),
@ -130,12 +162,27 @@ void VKCommandBuffer::copy(VKTexture &dst_texture,
regions.size(),
regions.data());
}
void VKCommandBuffer::blit(VKTexture &dst_texture,
VKTexture &src_buffer,
Span<VkImageBlit> regions)
{
ensure_no_active_framebuffer();
vkCmdBlitImage(vk_command_buffer_,
src_buffer.vk_image_handle(),
src_buffer.current_layout_get(),
dst_texture.vk_image_handle(),
dst_texture.current_layout_get(),
regions.size(),
regions.data(),
VK_FILTER_NEAREST);
}
void VKCommandBuffer::clear(VkImage vk_image,
VkImageLayout vk_image_layout,
const VkClearColorValue &vk_clear_color,
Span<VkImageSubresourceRange> ranges)
{
ensure_no_active_framebuffer();
vkCmdClearColorImage(vk_command_buffer_,
vk_image,
vk_image_layout,
@ -146,13 +193,36 @@ void VKCommandBuffer::clear(VkImage vk_image,
void VKCommandBuffer::clear(Span<VkClearAttachment> attachments, Span<VkClearRect> areas)
{
validate_framebuffer_exists();
ensure_active_framebuffer();
vkCmdClearAttachments(
vk_command_buffer_, attachments.size(), attachments.data(), areas.size(), areas.data());
}
void VKCommandBuffer::draw(int v_first, int v_count, int i_first, int i_count)
{
validate_framebuffer_exists();
ensure_active_framebuffer();
vkCmdDraw(vk_command_buffer_, v_count, i_count, v_first, i_first);
state.draw_counts++;
}
void VKCommandBuffer::draw(
int index_count, int instance_count, int first_index, int vertex_offset, int first_instance)
{
validate_framebuffer_exists();
ensure_active_framebuffer();
vkCmdDrawIndexed(
vk_command_buffer_, index_count, instance_count, first_index, vertex_offset, first_instance);
state.draw_counts++;
}
void VKCommandBuffer::pipeline_barrier(VkPipelineStageFlags source_stages,
VkPipelineStageFlags destination_stages)
{
if (state.framebuffer_) {
ensure_active_framebuffer();
}
vkCmdPipelineBarrier(vk_command_buffer_,
source_stages,
destination_stages,
@ -167,6 +237,7 @@ void VKCommandBuffer::pipeline_barrier(VkPipelineStageFlags source_stages,
void VKCommandBuffer::pipeline_barrier(Span<VkImageMemoryBarrier> image_memory_barriers)
{
ensure_no_active_framebuffer();
vkCmdPipelineBarrier(vk_command_buffer_,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
@ -181,11 +252,13 @@ void VKCommandBuffer::pipeline_barrier(Span<VkImageMemoryBarrier> image_memory_b
void VKCommandBuffer::dispatch(int groups_x_len, int groups_y_len, int groups_z_len)
{
ensure_no_active_framebuffer();
vkCmdDispatch(vk_command_buffer_, groups_x_len, groups_y_len, groups_z_len);
}
void VKCommandBuffer::submit()
{
ensure_no_active_framebuffer();
end_recording();
encode_recorded_commands();
submit_encoded_commands();
@ -208,6 +281,55 @@ void VKCommandBuffer::submit_encoded_commands()
vkQueueSubmit(vk_queue_, 1, &submit_info, vk_fence_);
submission_id_.next();
stage_transfer(Stage::BetweenRecordingAndSubmitting, Stage::Submitted);
}
/* -------------------------------------------------------------------- */
/** \name Framebuffer/RenderPass state tracking
* \{ */
void VKCommandBuffer::validate_framebuffer_not_exists()
{
BLI_assert_msg(state.framebuffer_ == nullptr && state.framebuffer_active_ == false,
"State error: expected no framebuffer being tracked.");
}
void VKCommandBuffer::validate_framebuffer_exists()
{
BLI_assert_msg(state.framebuffer_, "State error: expected framebuffer being tracked.");
}
void VKCommandBuffer::ensure_no_active_framebuffer()
{
state.checks_++;
if (state.framebuffer_ && state.framebuffer_active_) {
vkCmdEndRenderPass(vk_command_buffer_);
state.framebuffer_active_ = false;
state.switches_++;
}
}
void VKCommandBuffer::ensure_active_framebuffer()
{
BLI_assert(state.framebuffer_);
state.checks_++;
if (!state.framebuffer_active_) {
VkRenderPassBeginInfo render_pass_begin_info = {};
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
render_pass_begin_info.renderPass = state.framebuffer_->vk_render_pass_get();
render_pass_begin_info.framebuffer = state.framebuffer_->vk_framebuffer_get();
render_pass_begin_info.renderArea = state.framebuffer_->vk_render_area_get();
/* We don't use clear ops, but vulkan wants to have at least one. */
VkClearValue clear_value = {};
render_pass_begin_info.clearValueCount = 1;
render_pass_begin_info.pClearValues = &clear_value;
vkCmdBeginRenderPass(vk_command_buffer_, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
state.framebuffer_active_ = true;
state.switches_++;
}
}
/** \} */
} // namespace blender::gpu

View File

@ -16,21 +16,116 @@ namespace blender::gpu {
class VKBuffer;
class VKDescriptorSet;
class VKFrameBuffer;
class VKIndexBuffer;
class VKPipeline;
class VKPushConstants;
class VKTexture;
class VKVertexBuffer;
/** Command buffer to keep track of the life-time of a command buffer. */
class VKCommandBuffer : NonCopyable, NonMovable {
/** None owning handle to the command buffer and device. Handle is owned by `GHOST_ContextVK`. */
/** Not owning handle to the command buffer and device. Handle is owned by `GHOST_ContextVK`. */
VkDevice vk_device_ = VK_NULL_HANDLE;
VkCommandBuffer vk_command_buffer_ = VK_NULL_HANDLE;
VkQueue vk_queue_ = VK_NULL_HANDLE;
/**
* Timeout to use when waiting for fences in nanoseconds.
*
* Currently added as the fence will halt when there are no commands in the command buffer for
* the second time. This should be solved and this timeout should be removed.
*/
static constexpr uint64_t FenceTimeout = UINT64_MAX;
/** Owning handles */
VkFence vk_fence_ = VK_NULL_HANDLE;
VKSubmissionID submission_id_;
private:
enum class Stage {
Initial,
Recording,
BetweenRecordingAndSubmitting,
Submitted,
Executed,
};
/*
* Some vulkan command require an active frame buffer. Others require no active frame-buffer. As
* our current API does not provide a solution for this we need to keep track of the actual state
* and do the changes when recording the next command.
*
* This is a temporary solution to get things rolling.
* TODO: In a future solution we should decide the scope of a command buffer.
*
* - command buffer per draw command.
* - minimize command buffers and track render passes.
* - add custom encoder to also track resource usages.
*
* Currently I expect the custom encoder has to be done eventually. But want to keep post-poning
* the custom encoder for now to collect more use cases it should solve. (first pixel drawn on
* screen).
*
* Some command can also be encoded in another way when encoded as a first command. For example
* clearing a framebuffer textures isn't allowed inside a render pass, but clearing the
* framebuffer textures via ops is allowed. When clearing a framebuffer texture directly after
* beginning a render pass could be re-encoded to do this in the same command.
*
* So for now we track the state and temporary switch to another state if the command requires
* it.
*/
struct {
/* Reference to the last_framebuffer where begin_render_pass was called for. */
const VKFrameBuffer *framebuffer_ = nullptr;
/* Is last_framebuffer_ currently bound. Each call should ensure the correct state. */
bool framebuffer_active_ = false;
/* Amount of times a check has been requested. */
uint64_t checks_ = 0;
/* Amount of times a check required to change the render pass. */
uint64_t switches_ = 0;
/* Number of times a vkDraw command has been recorded. */
uint64_t draw_counts = 0;
/**
* Current stage of the command buffer to keep track of inconsistencies & incorrect usage.
*/
Stage stage = Stage::Initial;
} state;
bool is_in_stage(Stage stage)
{
return state.stage == stage;
}
void stage_set(Stage stage)
{
state.stage = stage;
}
std::string to_string(Stage stage)
{
switch (stage) {
case Stage::Initial:
return "INITIAL";
case Stage::Recording:
return "RECORDING";
case Stage::BetweenRecordingAndSubmitting:
return "BEFORE_SUBMIT";
case Stage::Submitted:
return "SUBMITTED";
case Stage::Executed:
return "EXECUTED";
}
return "UNKNOWN";
}
void stage_transfer(Stage stage_from, Stage stage_to)
{
BLI_assert(is_in_stage(stage_from));
#if 0
printf(" *** Transfer stage from %s to %s\n",
to_string(stage_from).c_str(),
to_string(stage_to).c_str());
#endif
stage_set(stage_to);
}
public:
virtual ~VKCommandBuffer();
void init(const VkDevice vk_device, const VkQueue vk_queue, VkCommandBuffer vk_command_buffer);
@ -40,6 +135,12 @@ class VKCommandBuffer : NonCopyable, NonMovable {
void bind(const VKDescriptorSet &descriptor_set,
const VkPipelineLayout vk_pipeline_layout,
VkPipelineBindPoint bind_point);
void bind(const uint32_t binding,
const VKVertexBuffer &vertex_buffer,
const VkDeviceSize offset);
/* Bind the given buffer as a vertex buffer. */
void bind(const uint32_t binding, const VkBuffer &vk_vertex_buffer, const VkDeviceSize offset);
void begin_render_pass(const VKFrameBuffer &framebuffer);
void end_render_pass(const VKFrameBuffer &framebuffer);
@ -55,6 +156,7 @@ class VKCommandBuffer : NonCopyable, NonMovable {
/** Copy the contents of a texture MIP level to the dst buffer. */
void copy(VKBuffer &dst_buffer, VKTexture &src_texture, Span<VkBufferImageCopy> regions);
void copy(VKTexture &dst_texture, VKBuffer &src_buffer, Span<VkBufferImageCopy> regions);
void blit(VKTexture &dst_texture, VKTexture &src_texture, Span<VkImageBlit> regions);
void pipeline_barrier(VkPipelineStageFlags source_stages,
VkPipelineStageFlags destination_stages);
void pipeline_barrier(Span<VkImageMemoryBarrier> image_memory_barriers);
@ -72,6 +174,10 @@ class VKCommandBuffer : NonCopyable, NonMovable {
void clear(Span<VkClearAttachment> attachments, Span<VkClearRect> areas);
void fill(VKBuffer &buffer, uint32_t data);
void draw(int v_first, int v_count, int i_first, int i_count);
void draw(
int index_count, int instance_count, int first_index, int vertex_offset, int first_instance);
/**
* Stop recording commands, encode + send the recordings to Vulkan, wait for the until the
* commands have been executed and start the command buffer to accept recordings again.
@ -86,6 +192,30 @@ class VKCommandBuffer : NonCopyable, NonMovable {
private:
void encode_recorded_commands();
void submit_encoded_commands();
/**
* Validate that there isn't a framebuffer being tracked (bound or not bound).
*
* Raises an assert in debug when a framebuffer is being tracked.
*/
void validate_framebuffer_not_exists();
/**
* Validate that there is a framebuffer being tracked (bound or not bound).
*
* Raises an assert in debug when no framebuffer is being tracked.
*/
void validate_framebuffer_exists();
/**
* Ensure that there is no framebuffer being tracked or the tracked framebuffer isn't bound.
*/
void ensure_no_active_framebuffer();
/**
* Ensure that the tracked framebuffer is bound.
*/
void ensure_active_framebuffer();
};
} // namespace blender::gpu

View File

@ -39,10 +39,7 @@ VKContext::VKContext(void *ghost_window, void *ghost_context)
/* Initialize the memory allocator. */
VmaAllocatorCreateInfo info = {};
/* Should use same vulkan version as GHOST (1.2), but set to 1.0 as 1.2 requires
* correct extensions and functions to be found by VMA, which isn't working as expected and
* requires more research. To continue development we lower the API to version 1.0. */
info.vulkanApiVersion = VK_API_VERSION_1_0;
info.vulkanApiVersion = VK_API_VERSION_1_2;
info.physicalDevice = vk_physical_device_;
info.device = vk_device_;
info.instance = vk_instance_;

View File

@ -203,17 +203,22 @@ class VKPushConstants : VKResourceTracker<VKUniformBuffer> {
const T *input_data)
{
const Layout::PushConstant *push_constant_layout = layout_->find(location);
BLI_assert(push_constant_layout);
if (push_constant_layout == nullptr) {
/* Legacy code can still try to update push constants when they don't exist. For example
* `immDrawPixelsTexSetup` will bind an image slot manually. This works in OpenGL, but in
* vulkan images aren't stored as push constants. */
return;
}
uint8_t *bytes = static_cast<uint8_t *>(data_);
T *dst = static_cast<T *>(static_cast<void *>(&bytes[push_constant_layout->offset]));
const bool is_tightly_std140_packed = (comp_len % 4) == 0;
if (layout_->storage_type_get() == StorageType::PUSH_CONSTANTS || array_size == 0 ||
is_tightly_std140_packed) {
BLI_assert_msg(push_constant_layout->offset + comp_len * array_size * sizeof(T) <=
layout_->size_in_bytes(),
push_constant_layout->array_size == 0 || is_tightly_std140_packed) {
const size_t copy_size_in_bytes = comp_len * max_ii(array_size, 1) * sizeof(T);
BLI_assert_msg(push_constant_layout->offset + copy_size_in_bytes <= layout_->size_in_bytes(),
"Tried to write outside the push constant allocated memory.");
memcpy(dst, input_data, comp_len * array_size * sizeof(T));
memcpy(dst, input_data, copy_size_in_bytes);
is_dirty_ = true;
return;
}

View File

@ -984,12 +984,14 @@ void VKShader::unbind()
void VKShader::uniform_float(int location, int comp_len, int array_size, const float *data)
{
pipeline_get().push_constants_get().push_constant_set(location, comp_len, array_size, data);
VKPushConstants &push_constants = pipeline_get().push_constants_get();
push_constants.push_constant_set(location, comp_len, array_size, data);
}
void VKShader::uniform_int(int location, int comp_len, int array_size, const int *data)
{
pipeline_get().push_constants_get().push_constant_set(location, comp_len, array_size, data);
VKPushConstants &push_constants = pipeline_get().push_constants_get();
push_constants.push_constant_set(location, comp_len, array_size, data);
}
std::string VKShader::resources_declare(const shader::ShaderCreateInfo &info) const
@ -1183,6 +1185,7 @@ std::string VKShader::geometry_layout_declare(const shader::ShaderCreateInfo &in
}
ss << "\n";
location = 0;
for (const StageInterfaceInfo *iface : info.geometry_out_interfaces_) {
bool has_matching_input_iface = find_interface_by_name(info.vertex_out_interfaces_,
iface->instance_name) != nullptr;

View File

@ -17,7 +17,7 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
using namespace blender::gpu::shader;
attr_len_ = 0;
attr_len_ = info.vertex_inputs_.size();
uniform_len_ = info.push_constants_.size();
ssbo_len_ = 0;
ubo_len_ = 0;
@ -58,7 +58,7 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
/* Make sure that the image slots don't overlap with the sampler slots. */
image_offset_++;
int32_t input_tot_len = ubo_len_ + uniform_len_ + ssbo_len_;
int32_t input_tot_len = attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_;
inputs_ = static_cast<ShaderInput *>(
MEM_calloc_arrayN(input_tot_len, sizeof(ShaderInput), __func__));
ShaderInput *input = inputs_;
@ -66,6 +66,20 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
name_buffer_ = (char *)MEM_mallocN(names_size, "name_buffer");
uint32_t name_buffer_offset = 0;
/* Attributes */
for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) {
copy_input_name(input, attr.name, name_buffer_, name_buffer_offset);
input->location = input->binding = attr.index;
if (input->location != -1) {
enabled_attr_mask_ |= (1 << input->location);
/* Used in `GPU_shader_get_attribute_info`. */
attr_types_[input->location] = uint8_t(attr.type);
}
input++;
}
/* Uniform blocks */
for (const ShaderCreateInfo::Resource &res : all_resources) {
if (res.bind_type == ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) {
@ -131,7 +145,9 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
}
/* Determine the descriptor set locations after the inputs have been sorted. */
descriptor_set_locations_ = Array<VKDescriptorSet::Location>(input_tot_len);
/* Note: input_tot_len is sometimes more than we need. */
const uint32_t resources_len = input_tot_len;
descriptor_set_locations_ = Array<VKDescriptorSet::Location>(resources_len);
uint32_t descriptor_set_location = 0;
for (ShaderCreateInfo::Resource &res : all_resources) {
const ShaderInput *input = shader_input_get(res);

View File

@ -7,6 +7,8 @@
#include "vk_uniform_buffer.hh"
#include "vk_context.hh"
#include "vk_shader.hh"
#include "vk_shader_interface.hh"
namespace blender::gpu {
@ -22,13 +24,42 @@ void VKUniformBuffer::update(const void *data)
void VKUniformBuffer::allocate(VKContext &context)
{
buffer_.create(context, size_in_bytes_, GPU_USAGE_STATIC, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
debug::object_label(&context, buffer_.vk_handle(), name_);
}
void VKUniformBuffer::clear_to_zero() {}
void VKUniformBuffer::clear_to_zero()
{
VKContext &context = *VKContext::get();
if (!buffer_.is_allocated()) {
allocate(context);
}
buffer_.clear(context, 0);
}
void VKUniformBuffer::bind(int /*slot*/) {}
void VKUniformBuffer::bind(int slot, shader::ShaderCreateInfo::Resource::BindType bind_type)
{
VKContext &context = *VKContext::get();
if (!buffer_.is_allocated()) {
allocate(context);
}
void VKUniformBuffer::bind_as_ssbo(int /*slot*/) {}
VKShader *shader = static_cast<VKShader *>(context.shader);
const VKShaderInterface &shader_interface = shader->interface_get();
const VKDescriptorSet::Location location = shader_interface.descriptor_set_location(bind_type,
slot);
VKDescriptorSetTracker &descriptor_set = shader->pipeline_get().descriptor_set_get();
descriptor_set.bind(*this, location);
}
void VKUniformBuffer::bind(int slot)
{
bind(slot, shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER);
}
void VKUniformBuffer::bind_as_ssbo(int slot)
{
bind(slot, shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER);
}
void VKUniformBuffer::unbind() {}

View File

@ -39,6 +39,7 @@ class VKUniformBuffer : public UniformBuf, NonCopyable {
private:
void allocate(VKContext &context);
void bind(int slot, shader::ShaderCreateInfo::Resource::BindType bind_type);
};
} // namespace blender::gpu

View File

@ -202,7 +202,21 @@ static void import_startjob(void *customdata, bool *stop, bool *do_update, float
*data->do_update = true;
*data->progress = 0.1f;
pxr::UsdStageRefPtr stage = pxr::UsdStage::Open(data->filepath);
std::string prim_path_mask(data->params.prim_path_mask);
pxr::UsdStagePopulationMask pop_mask;
if (!prim_path_mask.empty()) {
const std::vector<std::string> mask_tokens = pxr::TfStringTokenize(prim_path_mask, ",;");
for (const std::string &tok : mask_tokens) {
pxr::SdfPath prim_path(tok);
if (!prim_path.IsEmpty()) {
pop_mask.Add(prim_path);
}
}
}
pxr::UsdStageRefPtr stage = pop_mask.IsEmpty() ?
pxr::UsdStage::Open(data->filepath) :
pxr::UsdStage::OpenMasked(data->filepath, pop_mask);
if (!stage) {
WM_reportf(RPT_ERROR, "USD Import: unable to open stage to read %s", data->filepath);
@ -376,6 +390,8 @@ static void import_endjob(void *customdata)
break;
}
MEM_SAFE_FREE(data->params.prim_path_mask);
WM_main_add_notifier(NC_SCENE | ND_FRAME, data->scene);
report_job_duration(data);
}

View File

@ -312,19 +312,6 @@ void USDStageReader::collect_readers(Main *bmain)
/* Iterate through the stage. */
pxr::UsdPrim root = stage_->GetPseudoRoot();
std::string prim_path_mask(params_.prim_path_mask);
if (!prim_path_mask.empty()) {
pxr::UsdPrim prim = stage_->GetPrimAtPath(pxr::SdfPath(prim_path_mask));
if (prim.IsValid()) {
root = prim;
}
else {
std::cerr << "WARNING: Prim Path Mask " << prim_path_mask
<< " does not specify a valid prim.\n";
}
}
stage_->SetInterpolationType(pxr::UsdInterpolationType::UsdInterpolationTypeHeld);
collect_readers(bmain, root);
}

View File

@ -67,7 +67,7 @@ struct USDImportParams {
bool import_meshes;
bool import_volumes;
bool import_shapes;
char prim_path_mask[1024];
char *prim_path_mask;
bool import_subdiv;
bool import_instance_proxies;
bool create_collection;

View File

@ -533,7 +533,8 @@ typedef enum ObjectType {
#define OB_TYPE_SUPPORT_MATERIAL(_type) \
(((_type) >= OB_MESH && (_type) <= OB_MBALL) || \
((_type) >= OB_GPENCIL_LEGACY && (_type) <= OB_GREASE_PENCIL))
/** Does the object have some render-able geometry (unlike empties, cameras, etc.). */
/** Does the object have some render-able geometry (unlike empties, cameras, etc.). True for
* #OB_CURVES_LEGACY, since these often evaluate to objects with geometry. */
#define OB_TYPE_IS_GEOMETRY(_type) \
(ELEM(_type, \
OB_MESH, \
@ -541,6 +542,7 @@ typedef enum ObjectType {
OB_FONT, \
OB_MBALL, \
OB_GPENCIL_LEGACY, \
OB_CURVES_LEGACY, \
OB_CURVES, \
OB_POINTCLOUD, \
OB_VOLUME, \

View File

@ -153,6 +153,8 @@ typedef struct Tex {
ID id;
/** Animation data (must be immediately after id for utilities to use it). */
struct AnimData *adt;
/* runtime (must be immediately after id for utilities to use it). */
DrawDataList drawdata;
float noisesize, turbul;
float bright, contrast, saturation, rfac, gfac, bfac;
@ -264,14 +266,14 @@ typedef struct ColorMapping {
#define TEX_STUCCI 6
#define TEX_NOISE 7
#define TEX_IMAGE 8
//#define TEX_PLUGIN 9 /* Deprecated */
//#define TEX_ENVMAP 10 /* Deprecated */
// #define TEX_PLUGIN 9 /* Deprecated */
// #define TEX_ENVMAP 10 /* Deprecated */
#define TEX_MUSGRAVE 11
#define TEX_VORONOI 12
#define TEX_DISTNOISE 13
//#define TEX_POINTDENSITY 14 /* Deprecated */
//#define TEX_VOXELDATA 15 /* Deprecated */
//#define TEX_OCEAN 16 /* Deprecated */
// #define TEX_POINTDENSITY 14 /* Deprecated */
// #define TEX_VOXELDATA 15 /* Deprecated */
// #define TEX_OCEAN 16 /* Deprecated */
/* musgrave stype */
#define TEX_MFRACTAL 0

View File

@ -6,6 +6,8 @@
#pragma once
#include "BLI_utildefines.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -50,6 +52,7 @@ typedef enum eDupli_ID_Flags {
/* Duplicate (and hence make local) linked data. */
USER_DUP_LINKED_ID = (1 << 30),
} eDupli_ID_Flags;
ENUM_OPERATORS(eDupli_ID_Flags, USER_DUP_LINKED_ID)
#ifdef __cplusplus
}

View File

@ -2074,6 +2074,14 @@ static void rna_def_ID(BlenderRNA *brna)
"This data-block is not an independent one, but is actually a sub-data of another ID "
"(typical example: root node trees or master collections)");
prop = RNA_def_property(srna, "is_missing", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "tag", LIB_TAG_MISSING);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop,
"Missing Data",
"This data-block is a place-holder for missing linked data (i.e. it is "
"[an override of] a linked data that could not be found anymore)");
prop = RNA_def_property(srna, "is_runtime_data", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "tag", LIB_TAG_RUNTIME);
RNA_def_property_editable_func(prop, "rna_ID_is_runtime_editable");

View File

@ -136,7 +136,7 @@ class BlurOperation : public NodeOperation {
const float2 blur_radius = compute_blur_radius();
const SymmetricBlurWeights &weights = context().cache_manager().get_symmetric_blur_weights(
const SymmetricBlurWeights &weights = context().cache_manager().symmetric_blur_weights.get(
node_storage(bnode()).filtertype, blur_radius);
weights.bind_as_texture(shader, "weights_tx");
@ -171,7 +171,7 @@ class BlurOperation : public NodeOperation {
const float2 blur_radius = compute_blur_radius();
const SymmetricBlurWeights &weights = context().cache_manager().get_symmetric_blur_weights(
const SymmetricBlurWeights &weights = context().cache_manager().symmetric_blur_weights.get(
node_storage(bnode()).filtertype, blur_radius);
weights.bind_as_texture(shader, "weights_tx");

View File

@ -27,8 +27,16 @@ NODE_STORAGE_FUNCS(NodeBoxMask)
static void cmp_node_boxmask_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f);
b.add_input<decl::Float>(N_("Mask"))
.default_value(0.0f)
.min(0.0f)
.max(1.0f)
.compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Value"))
.default_value(1.0f)
.min(0.0f)
.max(1.0f)
.compositor_domain_priority(1);
b.add_output<decl::Float>(N_("Mask"));
}

View File

@ -256,7 +256,7 @@ class DilateErodeOperation : public NodeOperation {
input_image.bind_as_texture(shader, "input_tx");
const MorphologicalDistanceFeatherWeights &weights =
context().cache_manager().get_morphological_distance_feather_weights(
context().cache_manager().morphological_distance_feather_weights.get(
node_storage(bnode()).falloff, math::abs(get_distance()));
weights.bind_weights_as_texture(shader, "weights_tx");
weights.bind_distance_falloffs_as_texture(shader, "falloffs_tx");
@ -297,7 +297,7 @@ class DilateErodeOperation : public NodeOperation {
GPU_texture_bind(horizontal_pass_result, texture_image_unit);
const MorphologicalDistanceFeatherWeights &weights =
context().cache_manager().get_morphological_distance_feather_weights(
context().cache_manager().morphological_distance_feather_weights.get(
node_storage(bnode()).falloff, math::abs(get_distance()));
weights.bind_weights_as_texture(shader, "weights_tx");
weights.bind_distance_falloffs_as_texture(shader, "falloffs_tx");

View File

@ -27,8 +27,16 @@ NODE_STORAGE_FUNCS(NodeEllipseMask)
static void cmp_node_ellipsemask_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Float>(N_("Mask")).default_value(0.0f).min(0.0f).max(1.0f);
b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f);
b.add_input<decl::Float>(N_("Mask"))
.default_value(0.0f)
.min(0.0f)
.max(1.0f)
.compositor_domain_priority(0);
b.add_input<decl::Float>(N_("Value"))
.default_value(1.0f)
.min(0.0f)
.max(1.0f)
.compositor_domain_priority(1);
b.add_output<decl::Float>(N_("Mask"));
}

View File

@ -7,7 +7,9 @@
#include "BLT_translation.h"
#include "COM_cached_texture.hh"
#include "COM_node_operation.hh"
#include "COM_utilities.hh"
#include "node_composite_util.hh"
@ -17,12 +19,17 @@ namespace blender::nodes::node_composite_texture_cc {
static void cmp_node_texture_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Vector>(N_("Offset")).min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION);
b.add_input<decl::Vector>(N_("Offset"))
.min(-2.0f)
.max(2.0f)
.subtype(PROP_TRANSLATION)
.compositor_expects_single_value();
b.add_input<decl::Vector>(N_("Scale"))
.default_value({1.0f, 1.0f, 1.0f})
.min(-10.0f)
.max(10.0f)
.subtype(PROP_XYZ);
.subtype(PROP_XYZ)
.compositor_expects_single_value();
b.add_output<decl::Float>(N_("Value"));
b.add_output<decl::Color>(N_("Color"));
}
@ -35,9 +42,46 @@ class TextureOperation : public NodeOperation {
void execute() override
{
get_result("Value").allocate_invalid();
get_result("Color").allocate_invalid();
context().set_info_message("Viewport compositor setup not fully supported");
Result &color_result = get_result("Color");
Result &value_result = get_result("Value");
if (!get_texture()) {
if (color_result.should_compute()) {
color_result.allocate_invalid();
}
if (value_result.should_compute()) {
value_result.allocate_invalid();
}
return;
}
const Domain domain = compute_domain();
CachedTexture &cached_texture = context().cache_manager().cached_textures.get(
context(),
get_texture(),
context().get_scene(),
domain.size,
get_input("Offset").get_vector_value_default(float4(0.0f)).xy(),
get_input("Scale").get_vector_value_default(float4(0.0f)).xy());
if (color_result.should_compute()) {
color_result.allocate_texture(domain);
GPU_texture_copy(color_result.texture(), cached_texture.color_texture());
}
if (value_result.should_compute()) {
value_result.allocate_texture(domain);
GPU_texture_copy(value_result.texture(), cached_texture.value_texture());
}
}
Domain compute_domain() override
{
return Domain(context().get_compositing_region_size());
}
Tex *get_texture()
{
return (Tex *)bnode().id;
}
};
@ -58,8 +102,6 @@ void register_node_type_cmp_texture()
ntype.declare = file_ns::cmp_node_texture_declare;
ntype.flag |= NODE_PREVIEW;
ntype.get_compositor_operation = file_ns::get_compositor_operation;
ntype.realtime_compositor_unsupported_message = N_(
"Node not supported in the Viewport compositor");
nodeRegisterType(&ntype);
}

View File

@ -44,7 +44,7 @@ static void copy_attributes(const Map<AttributeIDRef, AttributeKind> &attributes
bke::MutableAttributeAccessor dst_attributes,
const Span<eAttrDomain> domains)
{
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
for (MapItem<AttributeIDRef, AttributeKind> entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
GAttributeReader attribute = src_attributes.lookup(attribute_id);
if (!attribute) {
@ -75,7 +75,7 @@ static void copy_attributes_based_on_mask(const Map<AttributeIDRef, AttributeKin
const eAttrDomain domain,
const IndexMask mask)
{
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
for (MapItem<AttributeIDRef, AttributeKind> entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
GAttributeReader attribute = src_attributes.lookup(attribute_id);
if (!attribute) {
@ -104,7 +104,7 @@ static void copy_attributes_based_on_map(const Map<AttributeIDRef, AttributeKind
const eAttrDomain domain,
const Span<int> index_map)
{
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
for (MapItem<AttributeIDRef, AttributeKind> entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
GAttributeReader attribute = src_attributes.lookup(attribute_id);
if (!attribute) {

View File

@ -294,7 +294,7 @@ BLI_NOINLINE static void propagate_existing_attributes(
const AttributeAccessor mesh_attributes = mesh.attributes();
MutableAttributeAccessor point_attributes = points.attributes_for_write();
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
for (MapItem<AttributeIDRef, AttributeKind> entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const eCustomDataType output_data_type = entry.value.data_type;

View File

@ -84,7 +84,7 @@ static void join_attributes(Span<const GeometryComponent *> src_components,
const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(src_components,
ignored_attributes);
for (const Map<AttributeIDRef, AttributeMetaData>::Item item : info.items()) {
for (const MapItem<AttributeIDRef, AttributeMetaData> item : info.items()) {
const AttributeIDRef attribute_id = item.key;
const AttributeMetaData &meta_data = item.value;

View File

@ -112,7 +112,7 @@ static void geometry_set_mesh_to_points(GeometrySet &geometry_set,
attributes.remove("radius");
attributes.remove("position");
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
for (MapItem<AttributeIDRef, AttributeKind> entry : attributes.items()) {
const AttributeIDRef attribute_id = entry.key;
const eCustomDataType data_type = entry.value.data_type;
const bke::GAttributeReader src = src_attributes.lookup(attribute_id, domain, data_type);

View File

@ -63,7 +63,7 @@ static void geometry_set_points_to_vertices(
const AttributeAccessor src_attributes = points->attributes();
MutableAttributeAccessor dst_attributes = mesh->attributes_for_write();
for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
for (MapItem<AttributeIDRef, AttributeKind> entry : attributes.items()) {
const AttributeIDRef id = entry.key;
const eCustomDataType data_type = entry.value.data_type;
const GAttributeReader src = src_attributes.lookup(id);

View File

@ -28,18 +28,9 @@ static void node_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::String>(N_("String"));
b.add_input<decl::Float>(N_("Size")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
b.add_input<decl::Float>(N_("Character Spacing"))
.default_value(1.0f)
.min(0.0f)
.subtype(PROP_DISTANCE);
b.add_input<decl::Float>(N_("Word Spacing"))
.default_value(1.0f)
.min(0.0f)
.subtype(PROP_DISTANCE);
b.add_input<decl::Float>(N_("Line Spacing"))
.default_value(1.0f)
.min(0.0f)
.subtype(PROP_DISTANCE);
b.add_input<decl::Float>(N_("Character Spacing")).default_value(1.0f).min(0.0f);
b.add_input<decl::Float>(N_("Word Spacing")).default_value(1.0f).min(0.0f);
b.add_input<decl::Float>(N_("Line Spacing")).default_value(1.0f).min(0.0f);
b.add_input<decl::Float>(N_("Text Box Width"))
.default_value(0.0f)
.min(0.0f)

View File

@ -568,6 +568,7 @@ void register_node_type_group_input()
node_type_base(ntype, NODE_GROUP_INPUT, "Group Input", NODE_CLASS_INTERFACE);
node_type_size(ntype, 140, 80, 400);
ntype->gather_add_node_search_ops = blender::nodes::search_node_add_ops_for_basic_node;
ntype->declare_dynamic = blender::nodes::group_input_declare_dynamic;
ntype->insert_link = blender::nodes::group_input_insert_link;
@ -593,6 +594,7 @@ void register_node_type_group_output()
node_type_base(ntype, NODE_GROUP_OUTPUT, "Group Output", NODE_CLASS_INTERFACE);
node_type_size(ntype, 140, 80, 400);
ntype->gather_add_node_search_ops = blender::nodes::search_node_add_ops_for_basic_node;
ntype->declare_dynamic = blender::nodes::group_output_declare_dynamic;
ntype->insert_link = blender::nodes::group_output_insert_link;

View File

@ -2619,6 +2619,8 @@ void *WM_opengl_context_create(void)
BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get());
GHOST_GLSettings glSettings = {0};
const eGPUBackendType gpu_backend = GPU_backend_type_selection_get();
glSettings.context_type = wm_ghost_drawing_context_type(gpu_backend);
if (G.debug & G_DEBUG_GPU) {
glSettings.flags |= GHOST_glDebugContext;
}