Initial Grease Pencil 3.0 stage #106848
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1744,7 +1744,7 @@ GHOST_Context *GHOST_WindowWayland::newDrawingContext(GHOST_TDrawingContextType
|
|||
window_->wl_surface,
|
||||
system_->wl_display(),
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
true);
|
||||
break;
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1235,7 +1235,7 @@ GHOST_Context *GHOST_WindowX11::newDrawingContext(GHOST_TDrawingContextType type
|
|||
NULL,
|
||||
NULL,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
m_is_debug_context);
|
||||
|
||||
if (!context->initializeDrawingContext()) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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).
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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}")
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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, ¶ms, 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");
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -63,4 +63,5 @@ class VKBuffer {
|
|||
bool map(VKContext &context);
|
||||
void unmap(VKContext &context);
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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, \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue