Alternative Upload geometry data in parallel to multiple GPUs using the "Multi-Device" #107552

Open
William Leeson wants to merge 137 commits from leesonw/blender-cluster:upload_changed into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
199 changed files with 3059 additions and 1573 deletions
Showing only changes of commit ee8d450adc - Show all commits

View File

@ -58,9 +58,6 @@ Static Source Code Checking
* check_cppcheck: Run blender source through cppcheck (C & C++).
* check_clang_array: Run blender source through clang array checking script (C & C++).
* check_deprecated: Check if there is any deprecated code to remove.
* check_splint: Run blenders source through splint (C only).
* check_sparse: Run blenders source through sparse (C only).
* check_smatch: Run blenders source through smatch (C only).
* check_descriptions: Check for duplicate/invalid descriptions.
* check_licenses: Check license headers follow the SPDX license specification,
using one of the accepted licenses in 'doc/license/SPDX-license-identifiers.txt'
@ -474,21 +471,6 @@ check_clang_array: .FORCE
@cd "$(BUILD_DIR)" ; \
$(PYTHON) "$(BLENDER_DIR)/build_files/cmake/cmake_static_check_clang_array.py"
check_splint: .FORCE
@$(CMAKE_CONFIG)
@cd "$(BUILD_DIR)" ; \
$(PYTHON) "$(BLENDER_DIR)/build_files/cmake/cmake_static_check_splint.py"
check_sparse: .FORCE
@$(CMAKE_CONFIG)
@cd "$(BUILD_DIR)" ; \
$(PYTHON) "$(BLENDER_DIR)/build_files/cmake/cmake_static_check_sparse.py"
check_smatch: .FORCE
@$(CMAKE_CONFIG)
@cd "$(BUILD_DIR)" ; \
$(PYTHON) "$(BLENDER_DIR)/build_files/cmake/cmake_static_check_smatch.py"
check_mypy: .FORCE
@$(PYTHON) "$(BLENDER_DIR)/tools/check_source/check_mypy.py"

View File

@ -1,58 +0,0 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later
CHECKER_IGNORE_PREFIX = [
"extern",
"intern/moto",
]
CHECKER_BIN = "smatch"
CHECKER_ARGS = [
"--full-path",
"--two-passes",
]
import project_source_info
import subprocess
import sys
import os
USE_QUIET = (os.environ.get("QUIET", None) is not None)
def main():
source_info = project_source_info.build_info(use_cxx=False, ignore_prefix_list=CHECKER_IGNORE_PREFIX)
source_defines = project_source_info.build_defines_as_args()
check_commands = []
for c, inc_dirs, defs in source_info:
cmd = ([CHECKER_BIN] +
CHECKER_ARGS +
[c] +
[("-I%s" % i) for i in inc_dirs] +
[("-D%s" % d) for d in defs] +
source_defines
)
check_commands.append((c, cmd))
def my_process(i, c, cmd):
if not USE_QUIET:
percent = 100.0 * (i / len(check_commands))
percent_str = "[" + ("%.2f]" % percent).rjust(7) + " %:"
sys.stdout.flush()
sys.stdout.write("%s %s\n" % (percent_str, c))
return subprocess.Popen(cmd)
process_functions = []
for i, (c, cmd) in enumerate(check_commands):
process_functions.append((my_process, (i, c, cmd)))
project_source_info.queue_processes(process_functions)
if __name__ == "__main__":
main()

View File

@ -1,56 +0,0 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later
CHECKER_IGNORE_PREFIX = [
"extern",
"intern/moto",
]
CHECKER_BIN = "sparse"
CHECKER_ARGS = [
]
import project_source_info
import subprocess
import sys
import os
USE_QUIET = (os.environ.get("QUIET", None) is not None)
def main():
source_info = project_source_info.build_info(use_cxx=False, ignore_prefix_list=CHECKER_IGNORE_PREFIX)
source_defines = project_source_info.build_defines_as_args()
check_commands = []
for c, inc_dirs, defs in source_info:
cmd = ([CHECKER_BIN] +
CHECKER_ARGS +
[c] +
[("-I%s" % i) for i in inc_dirs] +
[("-D%s" % d) for d in defs] +
source_defines
)
check_commands.append((c, cmd))
def my_process(i, c, cmd):
if not USE_QUIET:
percent = 100.0 * (i / len(check_commands))
percent_str = "[" + ("%.2f]" % percent).rjust(7) + " %:"
sys.stdout.flush()
sys.stdout.write("%s %s\n" % (percent_str, c))
return subprocess.Popen(cmd)
process_functions = []
for i, (c, cmd) in enumerate(check_commands):
process_functions.append((my_process, (i, c, cmd)))
project_source_info.queue_processes(process_functions)
if __name__ == "__main__":
main()

View File

@ -1,86 +0,0 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0-or-later
CHECKER_IGNORE_PREFIX = [
"extern",
"intern/moto",
]
CHECKER_BIN = "splint"
CHECKER_ARGS = [
"-weak",
"-posix-lib",
"-linelen", "10000",
"+ignorequals",
"+relaxtypes",
"-retvalother",
"+matchanyintegral",
"+longintegral",
"+ignoresigns",
"-nestcomment",
"-predboolothers",
"-ifempty",
"-unrecogcomments",
# we may want to remove these later
"-type",
"-fixedformalarray",
"-fullinitblock",
"-fcnuse",
"-initallelements",
"-castfcnptr",
# -forcehints,
"-bufferoverflowhigh", # warns a lot about sprintf()
# re-definitions, rna causes most of these
"-redef",
"-syntax",
# dummy, witjout this splint complains with:
# /usr/include/bits/confname.h:31:27: *** Internal Bug at cscannerHelp.c:2428: Unexpanded macro not function or constant: int _PC_MAX_CANON
"-D_PC_MAX_CANON=0",
]
import project_source_info
import subprocess
import sys
import os
USE_QUIET = (os.environ.get("QUIET", None) is not None)
def main():
source_info = project_source_info.build_info(use_cxx=False, ignore_prefix_list=CHECKER_IGNORE_PREFIX)
check_commands = []
for c, inc_dirs, defs in source_info:
cmd = ([CHECKER_BIN] +
CHECKER_ARGS +
[c] +
[("-I%s" % i) for i in inc_dirs] +
[("-D%s" % d) for d in defs]
)
check_commands.append((c, cmd))
def my_process(i, c, cmd):
if not USE_QUIET:
percent = 100.0 * (i / len(check_commands))
percent_str = "[" + ("%.2f]" % percent).rjust(7) + " %:"
sys.stdout.write("%s %s\n" % (percent_str, c))
sys.stdout.flush()
return subprocess.Popen(cmd)
process_functions = []
for i, (c, cmd) in enumerate(check_commands):
process_functions.append((my_process, (i, c, cmd)))
project_source_info.queue_processes(process_functions)
if __name__ == "__main__":
main()

View File

@ -865,29 +865,40 @@ Unfortunate Corner Cases
Besides all expected cases listed above, there are a few others that should not be
an issue but, due to internal implementation details, currently are:
- ``Object.hide_viewport``, ``Object.hide_select`` and ``Object.hide_render``:
Setting any of those Booleans will trigger a rebuild of Collection caches,
thus breaking any current iteration over ``Collection.all_objects``.
Collection Objects
^^^^^^^^^^^^^^^^^^
Changing: ``Object.hide_viewport``, ``Object.hide_select`` or ``Object.hide_render``
will trigger a rebuild of Collection caches, thus breaking any current iteration over ``Collection.all_objects``.
.. rubric:: Do not:
.. code-block:: python
# `all_objects` is an iterator. Using it directly while performing operations on its members that will update
# the memory accessed by the `all_objects` iterator will lead to invalid memory accesses and crashes.
for object in bpy.data.collections["Collection"].all_objects:
object.hide_viewport = True
.. rubric:: Do not:
.. rubric:: Do:
.. code-block:: python
.. code-block:: python
# `all_objects` is an iterator. Using it directly while performing operations on its members that will update
# the memory accessed by the `all_objects` iterator will lead to invalid memory accesses and crashes.
for object in bpy.data.collections["Collection"].all_objects:
object.hide_viewport = True
# `all_objects[:]` is an independent list generated from the iterator. As long as no objects are deleted,
# its content will remain valid even if the data accessed by the `all_objects` iterator is modified.
for object in bpy.data.collections["Collection"].all_objects[:]:
object.hide_viewport = True
.. rubric:: Do:
Data-Blocks Renaming During Iteration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
# `all_objects[:]` is an independent list generated from the iterator. As long as no objects are deleted,
# its content will remain valid even if the data accessed by the `all_objects` iterator is modified.
for object in bpy.data.collections["Collection"].all_objects[:]:
object.hide_viewport = True
Data-blocks accessed from ``bpy.data`` are sorted when their name is set.
Any loop that iterates of a data such as ``bpy.data.objects`` for example,
and sets the objects ``name`` must get all items from the iterator first (typically by converting to a list or tuple)
to avoid missing some objects and iterating over others multiple times.
sys.exit

View File

@ -113,9 +113,13 @@ BVHEmbree::~BVHEmbree()
}
}
void BVHEmbree::build(Progress &progress, Stats *stats, RTCDevice rtc_device_)
void BVHEmbree::build(Progress &progress,
Stats *stats,
RTCDevice rtc_device_,
const bool rtc_device_is_sycl_)
{
rtc_device = rtc_device_;
rtc_device_is_sycl = rtc_device_is_sycl_;
assert(rtc_device);
rtcSetDeviceErrorFunction(rtc_device, rtc_error_func, NULL);
@ -268,15 +272,29 @@ void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i)
rtcSetGeometryTimeStepCount(geom_id, num_motion_steps);
const int *triangles = mesh->get_triangles().data();
rtcSetSharedGeometryBuffer(geom_id,
RTC_BUFFER_TYPE_INDEX,
0,
RTC_FORMAT_UINT3,
triangles,
0,
sizeof(int) * 3,
num_triangles);
if (!rtc_device_is_sycl) {
rtcSetSharedGeometryBuffer(geom_id,
RTC_BUFFER_TYPE_INDEX,
0,
RTC_FORMAT_UINT3,
triangles,
0,
sizeof(int) * 3,
num_triangles);
}
else {
/* NOTE(sirgienko): If the Embree device is a SYCL device, then Embree execution will
* happen on GPU, and we cannot use standard host pointers at this point. So instead
* of making a shared geometry buffer - a new Embree buffer will be created and data
* will be copied. */
int *triangles_buffer = (int *)rtcSetNewGeometryBuffer(
geom_id, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, sizeof(int) * 3, num_triangles);
assert(triangles_buffer);
if (triangles_buffer) {
static_assert(sizeof(int) == sizeof(uint));
std::memcpy(triangles_buffer, triangles, sizeof(int) * 3 * (num_triangles));
}
}
set_tri_vertex_buffer(geom_id, mesh, false);
rtcSetGeometryUserData(geom_id, (void *)prim_offset);
@ -325,14 +343,38 @@ void BVHEmbree::set_tri_vertex_buffer(RTCGeometry geom_id, const Mesh *mesh, con
rtcUpdateGeometryBuffer(geom_id, RTC_BUFFER_TYPE_VERTEX, t);
}
else {
rtcSetSharedGeometryBuffer(geom_id,
RTC_BUFFER_TYPE_VERTEX,
t,
RTC_FORMAT_FLOAT3,
verts,
0,
sizeof(float3),
num_verts + 1);
if (!rtc_device_is_sycl) {
rtcSetSharedGeometryBuffer(geom_id,
RTC_BUFFER_TYPE_VERTEX,
t,
RTC_FORMAT_FLOAT3,
verts,
0,
sizeof(float3),
num_verts + 1);
}
else {
/* NOTE(sirgienko): If the Embree device is a SYCL device, then Embree execution will
* happen on GPU, and we cannot use standard host pointers at this point. So instead
* of making a shared geometry buffer - a new Embree buffer will be created and data
* will be copied. */
/* As float3 is packed on GPU side, we map it to packed_float3. */
packed_float3 *verts_buffer = (packed_float3 *)rtcSetNewGeometryBuffer(
geom_id,
RTC_BUFFER_TYPE_VERTEX,
t,
RTC_FORMAT_FLOAT3,
sizeof(packed_float3),
num_verts + 1);
assert(verts_buffer);
if (verts_buffer) {
for (size_t i = (size_t)0; i < num_verts + 1; ++i) {
verts_buffer[i].x = verts[i].x;
verts_buffer[i].y = verts[i].y;
verts_buffer[i].z = verts[i].z;
}
}
}
}
}
}

View File

@ -30,7 +30,10 @@ class Device;
class BVHEmbree : public BVH {
public:
void build(Progress &progress, Stats *stats, RTCDevice rtc_device);
void build(Progress &progress,
Stats *stats,
RTCDevice rtc_device,
const bool isSyclEmbreeDevice = false);
void refit(Progress &progress);
RTCScene scene;
@ -57,6 +60,7 @@ class BVHEmbree : public BVH {
const bool update);
RTCDevice rtc_device;
bool rtc_device_is_sycl;
enum RTCBuildQuality build_quality;
const Device *device;
};

View File

@ -143,7 +143,7 @@ void OneapiDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
bvh_embree->refit(progress);
}
else {
bvh_embree->build(progress, &stats, embree_device);
bvh_embree->build(progress, &stats, embree_device, true);
}
if (bvh->params.top_level) {
embree_scene = bvh_embree->scene;

View File

@ -778,8 +778,6 @@ if(WITH_CYCLES_DEVICE_ONEAPI)
# Host execution won't use GPU binaries, no need to compile them.
if(WITH_CYCLES_ONEAPI_BINARIES AND NOT WITH_CYCLES_ONEAPI_HOST_TASK_EXECUTION)
# AoT binaries aren't currently reused when calling sycl::build.
list(APPEND sycl_compiler_flags -DWITH_CYCLES_ONEAPI_BINARIES)
# Iterate over all targest and their options
list(JOIN CYCLES_ONEAPI_SYCL_TARGETS "," targets_string)
list(APPEND sycl_compiler_flags -fsycl-targets=${targets_string})

View File

@ -289,6 +289,15 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
return false;
}
# ifdef __EMBREE__
IF_USING_EMBREE
{
if (kernel_data.device_bvh) {
return kernel_embree_intersect_volume(kg, ray, isect, visibility);
}
}
# endif
IF_NOT_USING_EMBREE
{
# ifdef __OBJECT_MOTION__

View File

@ -109,7 +109,9 @@ struct CCLVolumeContext
#if EMBREE_MAJOR_VERSION >= 4
KernelGlobals kg;
const Ray *ray;
# ifdef __VOLUME_RECORD_ALL__
numhit_t max_hits;
# endif
numhit_t num_hits;
#endif
Intersection *vol_isect;
@ -505,8 +507,10 @@ ccl_device_forceinline void kernel_embree_filter_occluded_volume_all_func_impl(
#endif
const Ray *cray = ctx->ray;
#ifdef __VOLUME_RECORD_ALL__
/* Append the intersection to the end of the array. */
if (ctx->num_hits < ctx->max_hits) {
#endif
Intersection current_isect;
kernel_embree_convert_hit(
kg, ray, hit, &current_isect, reinterpret_cast<intptr_t>(args->geometryUserPtr));
@ -523,10 +527,17 @@ ccl_device_forceinline void kernel_embree_filter_occluded_volume_all_func_impl(
int object_flag = kernel_data_fetch(object_flag, tri_object);
if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) {
--ctx->num_hits;
#ifndef __VOLUME_RECORD_ALL__
/* Without __VOLUME_RECORD_ALL__ we need only a first counted hit, so we will
* continue tracing only if a current hit is not counted. */
*args->valid = 0;
#endif
}
#ifdef __VOLUME_RECORD_ALL__
/* This tells Embree to continue tracing. */
*args->valid = 0;
}
#endif
}
#if EMBREE_MAJOR_VERSION < 4
@ -844,7 +855,9 @@ ccl_device_intersect bool kernel_embree_intersect_shadow_all(KernelGlobals kg,
ccl_device_intersect uint kernel_embree_intersect_volume(KernelGlobals kg,
ccl_private const Ray *ray,
ccl_private Intersection *isect,
# ifdef __VOLUME_RECORD_ALL__
const uint max_hits,
# endif
const uint visibility)
{
# if EMBREE_MAJOR_VERSION >= 4
@ -864,7 +877,9 @@ ccl_device_intersect uint kernel_embree_intersect_volume(KernelGlobals kg,
rtcInitIntersectContext(&ctx);
# endif
ctx.vol_isect = isect;
# ifdef __VOLUME_RECORD_ALL__
ctx.max_hits = numhit_t(max_hits);
# endif
ctx.num_hits = numhit_t(0);
ctx.ray = ray;
RTCRay rtc_ray;

View File

@ -174,6 +174,14 @@ bool oneapi_kernel_is_required_for_features(const std::string &kernel_name,
return true;
}
bool oneapi_kernel_is_raytrace_or_mnee(const std::string &kernel_name)
{
return (kernel_name.find(device_kernel_as_string(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE)) !=
std::string::npos) ||
(kernel_name.find(device_kernel_as_string(
DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE)) != std::string::npos);
}
bool oneapi_kernel_is_using_embree(const std::string &kernel_name)
{
# ifdef WITH_EMBREE_GPU
@ -182,8 +190,7 @@ bool oneapi_kernel_is_using_embree(const std::string &kernel_name)
DeviceKernel kernel = (DeviceKernel)i;
if (device_kernel_has_intersection(kernel)) {
if (kernel_name.find(device_kernel_as_string(kernel)) != std::string::npos) {
return !(kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE ||
kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_MNEE);
return !oneapi_kernel_is_raytrace_or_mnee(kernel_name);
}
}
}
@ -241,10 +248,6 @@ bool oneapi_load_kernels(SyclQueue *queue_,
}
# endif
# ifdef WITH_CYCLES_ONEAPI_BINARIES
(void)queue_;
(void)kernel_features;
# else
try {
sycl::kernel_bundle<sycl::bundle_state::input> all_kernels_bundle =
sycl::get_kernel_bundle<sycl::bundle_state::input>(queue->get_context(),
@ -260,18 +263,22 @@ bool oneapi_load_kernels(SyclQueue *queue_,
continue;
}
sycl::kernel_bundle<sycl::bundle_state::input> one_kernel_bundle_input =
sycl::get_kernel_bundle<sycl::bundle_state::input>(queue->get_context(), {kernel_id});
# ifdef WITH_EMBREE_GPU
/* This is expected to be the default, we set it again to be sure. */
if (one_kernel_bundle_input
.has_specialization_constant<ONEAPIKernelContext::oneapi_embree_features>()) {
# ifdef WITH_EMBREE_GPU
if (oneapi_kernel_is_using_embree(kernel_name) ||
oneapi_kernel_is_raytrace_or_mnee(kernel_name)) {
sycl::kernel_bundle<sycl::bundle_state::input> one_kernel_bundle_input =
sycl::get_kernel_bundle<sycl::bundle_state::input>(queue->get_context(), {kernel_id});
one_kernel_bundle_input
.set_specialization_constant<ONEAPIKernelContext::oneapi_embree_features>(
RTC_FEATURE_FLAG_NONE);
sycl::build(one_kernel_bundle_input);
continue;
}
# endif
sycl::build(one_kernel_bundle_input);
# endif
/* This call will ensure that AoT or cached JIT binaries are available
* for execution. It will trigger compilation if it is not already the case. */
(void)sycl::get_kernel_bundle<sycl::bundle_state::executable>(queue->get_context(),
{kernel_id});
}
}
catch (sycl::exception const &e) {
@ -280,7 +287,6 @@ bool oneapi_load_kernels(SyclQueue *queue_,
}
return false;
}
# endif
return true;
}

View File

@ -557,7 +557,7 @@ static void log_kernel_features(const uint features)
<< "\n";
VLOG_INFO << "Use Shader Raytrace " << string_from_bool(features & KERNEL_FEATURE_NODE_RAYTRACE)
<< "\n";
VLOG_INFO << "Use MNEE" << string_from_bool(features & KERNEL_FEATURE_MNEE) << "\n";
VLOG_INFO << "Use MNEE " << string_from_bool(features & KERNEL_FEATURE_MNEE) << "\n";
VLOG_INFO << "Use Transparent " << string_from_bool(features & KERNEL_FEATURE_TRANSPARENT)
<< "\n";
VLOG_INFO << "Use Denoising " << string_from_bool(features & KERNEL_FEATURE_DENOISING) << "\n";

View File

@ -72,7 +72,7 @@ class GHOST_ISystemPaths {
/**
* Add the file to the operating system most recently used files
*/
virtual void addToSystemRecentFiles(const char *filename) const = 0;
virtual void addToSystemRecentFiles(const char *filepath) const = 0;
private:
/** The one and only system paths. */

View File

@ -10,6 +10,9 @@
#include "GHOST_Context.hh"
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <epoxy/wgl.h>
# include <tchar.h>
#
@ -18,6 +21,8 @@
# endif
#endif
#include <epoxy/gl.h>
#include <cstdio>
#include <cstring>

View File

@ -11,8 +11,6 @@
#include "GHOST_IContext.hh"
#include "GHOST_Types.h"
#include <epoxy/gl.h>
#include <cstdlib> // for NULL
class GHOST_Context : public GHOST_IContext {

View File

@ -19,6 +19,8 @@
#include <Metal/Metal.h>
#include <QuartzCore/QuartzCore.h>
#include <epoxy/gl.h>
#include <cassert>
#include <vector>

View File

@ -903,13 +903,14 @@ GHOST_TSuccess GHOST_ContextVK::initializeDrawingContext()
auto extensions_available = getExtensionsAvailable();
vector<const char *> layers_enabled;
if (m_debug) {
enableLayer(layers_available, layers_enabled, VkLayer::KHRONOS_validation, m_debug);
}
vector<const char *> extensions_device;
vector<const char *> extensions_enabled;
if (m_debug) {
enableLayer(layers_available, layers_enabled, VkLayer::KHRONOS_validation, m_debug);
requireExtension(extensions_available, extensions_enabled, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
if (use_window_surface) {
const char *native_surface_extension_name = getPlatformSpecificSurfaceExtension();

View File

@ -47,5 +47,5 @@ class GHOST_SystemPaths : public GHOST_ISystemPaths {
/**
* Add the file to the operating system most recently used files
*/
virtual void addToSystemRecentFiles(const char *filename) const = 0;
virtual void addToSystemRecentFiles(const char *filepath) const = 0;
};

View File

@ -54,5 +54,5 @@ class GHOST_SystemPathsCocoa : public GHOST_SystemPaths {
/**
* Add the file to the operating system most recently used files
*/
void addToSystemRecentFiles(const char *filename) const;
void addToSystemRecentFiles(const char *filepath) const;
};

View File

@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2010 Blender Foundation */
#import <AppKit/NSDocumentController.h>
#import <Foundation/Foundation.h>
#include "GHOST_Debug.hh"
@ -112,7 +113,10 @@ const char *GHOST_SystemPathsCocoa::getBinaryDir() const
return tempPath;
}
void GHOST_SystemPathsCocoa::addToSystemRecentFiles(const char *filename) const
void GHOST_SystemPathsCocoa::addToSystemRecentFiles(const char *filepath) const
{
/* TODO: implement for macOS */
@autoreleasepool {
NSURL *const file_url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:filepath]];
[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:file_url];
}
}

View File

@ -52,5 +52,5 @@ class GHOST_SystemPathsUnix : public GHOST_SystemPaths {
/**
* Add the file to the operating system most recently used files
*/
void addToSystemRecentFiles(const char *filename) const;
void addToSystemRecentFiles(const char *filepath) const;
};

View File

@ -117,10 +117,10 @@ const char *GHOST_SystemPathsWin32::getBinaryDir() const
return NULL;
}
void GHOST_SystemPathsWin32::addToSystemRecentFiles(const char *filename) const
void GHOST_SystemPathsWin32::addToSystemRecentFiles(const char *filepath) const
{
/* SHARD_PATH resolves to SHARD_PATHA for non-UNICODE build */
UTF16_ENCODE(filename);
SHAddToRecentDocs(SHARD_PATHW, filename_16);
UTF16_UN_ENCODE(filename);
UTF16_ENCODE(filepath);
SHAddToRecentDocs(SHARD_PATHW, filepath_16);
UTF16_UN_ENCODE(filepath);
}

View File

@ -61,5 +61,5 @@ class GHOST_SystemPathsWin32 : public GHOST_SystemPaths {
/**
* Add the file to the operating system most recently used files
*/
void addToSystemRecentFiles(const char *filename) const;
void addToSystemRecentFiles(const char *filepath) const;
};

View File

@ -99,7 +99,12 @@ static bool use_xwayland_hack = false;
using namespace std;
GHOST_SystemX11::GHOST_SystemX11() : GHOST_System(), m_xkb_descr(nullptr), m_start_time(0)
GHOST_SystemX11::GHOST_SystemX11()
: GHOST_System(),
m_xkb_descr(nullptr),
m_start_time(0),
m_keyboard_vector{0},
m_keycode_last_repeat_key(uint(-1))
{
XInitThreads();
m_display = XOpenDisplay(nullptr);
@ -897,7 +902,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
#endif /* WITH_X11_XINPUT */
switch (xe->type) {
case Expose: {
XExposeEvent &xee = xe->xexpose;
const XExposeEvent &xee = xe->xexpose;
if (xee.count == 0) {
/* Only generate a single expose event
@ -909,7 +914,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
}
case MotionNotify: {
XMotionEvent &xme = xe->xmotion;
const XMotionEvent &xme = xe->xmotion;
bool is_tablet = window->GetTabletData().Active != GHOST_kTabletModeNone;
@ -1235,7 +1240,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
case ButtonPress:
case ButtonRelease: {
XButtonEvent &xbe = xe->xbutton;
const XButtonEvent &xbe = xe->xbutton;
GHOST_TButton gbmask = GHOST_kButtonMaskLeft;
GHOST_TEventType type = (xbe.type == ButtonPress) ? GHOST_kEventButtonDown :
GHOST_kEventButtonUp;
@ -1290,14 +1295,14 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
/* change of size, border, layer etc. */
case ConfigureNotify: {
// XConfigureEvent & xce = xe->xconfigure;
// const XConfigureEvent & xce = xe->xconfigure;
g_event = new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window);
break;
}
case FocusIn:
case FocusOut: {
XFocusChangeEvent &xfe = xe->xfocus;
const XFocusChangeEvent &xfe = xe->xfocus;
/* TODO: make sure this is the correct place for activate/deactivate */
// printf("X: focus %s for window %d\n",
@ -1385,7 +1390,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe)
* (really crossing between windows) since some window-managers
* also send grab/un-grab crossings for mouse-wheel events.
*/
XCrossingEvent &xce = xe->xcrossing;
const XCrossingEvent &xce = xe->xcrossing;
if (xce.mode == NotifyNormal) {
g_event = new GHOST_EventCursor(getMilliSeconds(),
GHOST_kEventCursorMove,
@ -2376,7 +2381,7 @@ class DialogData {
}
/* Is the mouse inside the given button */
bool isInsideButton(XEvent &e, uint button_num)
bool isInsideButton(const XEvent &e, uint button_num)
{
return (
(e.xmotion.y > int(height - padding_y - button_height)) &&

View File

@ -21,22 +21,21 @@ GHOST_Window::GHOST_Window(uint32_t width,
const bool wantStereoVisual,
const bool /*exclusive*/)
: m_drawingContextType(GHOST_kDrawingContextTypeNone),
m_userData(nullptr),
m_cursorVisible(true),
m_cursorGrab(GHOST_kGrabDisable),
m_cursorGrabAxis(GHOST_kAxisNone),
m_cursorGrabInitPos{0, 0},
m_cursorGrabAccumPos{0, 0},
m_cursorShape(GHOST_kStandardCursorDefault),
m_progressBarVisible(false),
m_canAcceptDragOperation(false),
m_isUnsavedChanges(false),
m_wantStereoVisual(wantStereoVisual),
m_nativePixelSize(1.0f),
m_context(new GHOST_ContextNone(false))
{
m_isUnsavedChanges = false;
m_canAcceptDragOperation = false;
m_progressBarVisible = false;
m_cursorGrabAccumPos[0] = 0;
m_cursorGrabAccumPos[1] = 0;
m_nativePixelSize = 1.0f;
m_fullScreen = state == GHOST_kWindowStateFullScreen;
if (m_fullScreen) {

View File

@ -6848,6 +6848,17 @@ def km_3d_view_tool_cursor(params):
)
def km_3d_view_tool_text_select(params):
return (
"3D View Tool: Edit Text, Select Text",
{"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
{"items": [
("font.selection_set", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
("font.select_word", {"type": 'LEFTMOUSE', "value": 'DOUBLE_CLICK'}, None),
]},
)
def km_3d_view_tool_select(params, *, fallback):
if params.use_tweak_select_passthrough:
operator_props = (("vert_without_handles", True),)
@ -8264,6 +8275,7 @@ def generate_keymaps(params=None):
*(km_node_editor_tool_select_circle(params, fallback=fallback) for fallback in (False, True)),
km_node_editor_tool_links_cut(params),
km_3d_view_tool_cursor(params),
km_3d_view_tool_text_select(params),
*(km_3d_view_tool_select(params, fallback=fallback) for fallback in (False, True)),
*(km_3d_view_tool_select_box(params, fallback=fallback) for fallback in (False, True)),
*(km_3d_view_tool_select_circle(params, fallback=fallback) for fallback in (False, True)),

View File

@ -220,6 +220,7 @@ class NODE_MT_geometry_node_GEO_GEOMETRY_SAMPLE(Menu):
def draw(self, _context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeProximity")
node_add_menu.add_node_type(layout, "GeometryNodeIndexOfNearest")
node_add_menu.add_node_type(layout, "GeometryNodeRaycast")
node_add_menu.add_node_type(layout, "GeometryNodeSampleIndex")
node_add_menu.add_node_type(layout, "GeometryNodeSampleNearest")

View File

@ -1284,6 +1284,20 @@ class _defs_edit_curve:
)
class _defs_edit_text:
@ToolDef.from_fn
def select_text():
return dict(
idname="builtin.select_text",
label="Select Text",
cursor='TEXT',
icon="ops.generic.select_box",
widget=None,
keymap=(),
)
class _defs_pose:
@ToolDef.from_fn
@ -2980,6 +2994,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
_defs_transform.shear,
],
'EDIT_TEXT': [
_defs_edit_text.select_text,
_defs_view3d_generic.cursor,
None,
*_tools_annotate,

View File

@ -23,7 +23,7 @@ std::string normalize_directory_path(StringRef directory)
directory.data(),
/* + 1 for null terminator. */
std::min(directory.size() + 1, int64_t(sizeof(dir_normalized))));
BLI_path_normalize_dir(nullptr, dir_normalized, sizeof(dir_normalized));
BLI_path_normalize_dir(dir_normalized, sizeof(dir_normalized));
return std::string(dir_normalized);
}
@ -34,7 +34,7 @@ std::string normalize_path(StringRefNull path, int64_t max_len)
char *buf = BLI_strdupn(path.c_str(), len);
BLI_path_slash_native(buf);
BLI_path_normalize(nullptr, buf);
BLI_path_normalize(buf);
std::string normalized_path = buf;
MEM_freeN(buf);

View File

@ -29,7 +29,7 @@ namespace blender::bke {
*
* Once created, #AnonymousAttributeID is immutable. Also it is intrinsically reference counted so
* that it can have shared ownership. `std::shared_ptr` can't be used for that purpose here,
* because that is not available in C code. If possible, the #AutoAnonymousAttributeID wrapper
* because that is not available in C code. If possible, the #AnonymousAttributeIDPtr wrapper
* should be used to avoid manual reference counting in C++ code.
*/
class AnonymousAttributeID : public ImplicitSharingMixin {
@ -54,7 +54,7 @@ class AnonymousAttributeID : public ImplicitSharingMixin {
};
/** Wrapper for #AnonymousAttributeID that avoids manual reference counting. */
using AutoAnonymousAttributeID = ImplicitSharingPtr<const AnonymousAttributeID>;
using AnonymousAttributeIDPtr = ImplicitSharingPtr<const AnonymousAttributeID>;
/**
* A set of anonymous attribute names that is passed around in geometry nodes.

View File

@ -55,7 +55,7 @@ struct BasisCache {
class CurvesGeometryRuntime {
public:
/** Implicit sharing user count for #CurvesGeometry::curve_offsets. */
ImplicitSharingInfo *curve_offsets_sharing_info = nullptr;
const ImplicitSharingInfo *curve_offsets_sharing_info = nullptr;
/**
* The cached number of curves with each type. Unlike other caches here, this is not computed

View File

@ -259,11 +259,11 @@ class NormalFieldInput : public GeometryFieldInput {
class AnonymousAttributeFieldInput : public GeometryFieldInput {
private:
AutoAnonymousAttributeID anonymous_id_;
AnonymousAttributeIDPtr anonymous_id_;
std::string producer_name_;
public:
AnonymousAttributeFieldInput(AutoAnonymousAttributeID anonymous_id,
AnonymousAttributeFieldInput(AnonymousAttributeIDPtr anonymous_id,
const CPPType &type,
std::string producer_name)
: GeometryFieldInput(type, anonymous_id->user_name()),
@ -274,7 +274,7 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput {
}
template<typename T>
static fn::Field<T> Create(AutoAnonymousAttributeID anonymous_id, std::string producer_name)
static fn::Field<T> Create(AnonymousAttributeIDPtr anonymous_id, std::string producer_name)
{
const CPPType &type = CPPType::get<T>();
auto field_input = std::make_shared<AnonymousAttributeFieldInput>(
@ -282,7 +282,7 @@ class AnonymousAttributeFieldInput : public GeometryFieldInput {
return fn::Field<T>{field_input};
}
const AutoAnonymousAttributeID &anonymous_id() const
const AnonymousAttributeIDPtr &anonymous_id() const
{
return anonymous_id_;
}

View File

@ -11,8 +11,6 @@
# include <mutex>
# include "MEM_guardedalloc.h"
# include "BLI_array.hh"
# include "BLI_bit_vector.hh"
# include "BLI_bounds_types.hh"
@ -22,7 +20,6 @@
# include "BLI_span.hh"
# include "BLI_vector.hh"
# include "DNA_customdata_types.h"
# include "DNA_meshdata_types.h"
struct BVHCache;
@ -69,20 +66,25 @@ namespace blender::bke {
/**
* Cache of a mesh's loose edges, accessed with #Mesh::loose_edges(). *
*/
struct LooseEdgeCache {
struct LooseGeomCache {
/**
* A bitmap set to true for each loose edge, false if the edge is used by any face.
* Allocated only if there is at least one loose edge.
* A bitmap set to true for each loose element, false if the element is used by any face.
* Allocated only if there is at least one loose element.
*/
blender::BitVector<> is_loose_bits;
/**
* The number of loose edges. If zero, the #is_loose_bits shouldn't be accessed.
* The number of loose elements. If zero, the #is_loose_bits shouldn't be accessed.
* If less than zero, the cache has been accessed in an invalid way
* (i.e.directly instead of through #Mesh::loose_edges()).
*/
int count = -1;
};
struct LooseEdgeCache : public LooseGeomCache {
};
struct LooseVertCache : public LooseGeomCache {
};
struct MeshRuntime {
/* Evaluated mesh for objects which do not have effective modifiers.
* This mesh is used as a result of modifier stack evaluation.
@ -98,7 +100,7 @@ struct MeshRuntime {
std::mutex render_mutex;
/** Implicit sharing user count for #Mesh::poly_offset_indices. */
ImplicitSharingInfoHandle *poly_offsets_sharing_info;
const ImplicitSharingInfoHandle *poly_offsets_sharing_info;
/**
* A cache of bounds shared between data-blocks with unchanged positions. When changing positions
@ -166,11 +168,12 @@ struct MeshRuntime {
mutable Vector<float3> vert_normals;
mutable Vector<float3> poly_normals;
/**
* A cache of data about the loose edges. Can be shared with other data-blocks with unchanged
* topology. Accessed with #Mesh::loose_edges().
*/
/** Cache of data about edges not used by faces. See #Mesh::loose_edges(). */
SharedCache<LooseEdgeCache> loose_edges_cache;
/** Cache of data about vertices not used by edges. See #Mesh::loose_verts(). */
SharedCache<LooseVertCache> loose_verts_cache;
/** Cache of data about vertices not used by faces. See #Mesh::verts_no_face(). */
SharedCache<LooseVertCache> verts_no_face_cache;
/**
* A bit vector the size of the number of vertices, set to true for the center vertices of

View File

@ -1581,6 +1581,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define GEO_NODE_SDF_VOLUME_SPHERE 1196
#define GEO_NODE_MEAN_FILTER_SDF_VOLUME 1197
#define GEO_NODE_OFFSET_SDF_VOLUME 1198
#define GEO_NODE_INDEX_OF_NEAREST 1199
/** \} */

View File

@ -154,6 +154,11 @@ void BKE_sound_set_scene_sound_volume(void *handle, float volume, char animated)
void BKE_sound_set_scene_sound_pitch(void *handle, float pitch, char animated);
void BKE_sound_set_scene_sound_pitch_constant_range(void *handle,
int frame_start,
int frame_end,
float pitch);
void BKE_sound_set_scene_sound_pan(void *handle, float pan, char animated);
void BKE_sound_update_sequencer(struct Main *main, struct bSound *sound);

View File

@ -89,6 +89,9 @@ bool BKE_vfont_to_curve_ex(struct Object *ob,
bool *r_text_free,
struct CharTrans **r_chartransdata);
bool BKE_vfont_to_curve_nubase(struct Object *ob, int mode, struct ListBase *r_nubase);
int BKE_vfont_cursor_to_text_index(struct Object *ob, float cursor_location[2]);
/**
* \warning Expects to have access to evaluated data (i.e. passed object should be evaluated one).
*/

View File

@ -569,6 +569,27 @@ static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md,
return mesh_output;
}
static void set_rest_position(Mesh &mesh)
{
using namespace blender;
using namespace blender::bke;
MutableAttributeAccessor attributes = mesh.attributes_for_write();
const AttributeReader positions = attributes.lookup<float3>("position");
attributes.remove("rest_position");
if (positions) {
if (positions.sharing_info && positions.varray.is_span()) {
attributes.add<float3>("rest_position",
ATTR_DOMAIN_POINT,
AttributeInitShared(positions.varray.get_internal_span().data(),
*positions.sharing_info));
}
else {
attributes.add<float3>(
"rest_position", ATTR_DOMAIN_POINT, AttributeInitVArray(positions.varray));
}
}
}
static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
const Scene *scene,
Object *ob,
@ -657,20 +678,7 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph,
mesh_final = BKE_mesh_copy_for_eval(mesh_input);
ASSERT_IS_VALID_MESH(mesh_final);
}
MutableAttributeAccessor attributes = mesh_final->attributes_for_write();
const AttributeReader positions = attributes.lookup<float3>("position");
if (positions) {
if (positions.sharing_info && positions.varray.is_span()) {
attributes.add<float3>("rest_position",
ATTR_DOMAIN_POINT,
AttributeInitShared(positions.varray.get_internal_span().data(),
*positions.sharing_info));
}
else {
attributes.add<float3>(
"rest_position", ATTR_DOMAIN_POINT, AttributeInitVArray(positions.varray));
}
}
set_rest_position(*mesh_final);
}
/* Apply all leading deform modifiers. */
@ -1194,6 +1202,14 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph,
/* Clear errors before evaluation. */
BKE_modifiers_clear_errors(ob);
if (ob->modifier_flag & OB_MODIFIER_FLAG_ADD_REST_POSITION) {
if (mesh_final == nullptr) {
mesh_final = BKE_mesh_from_bmesh_for_eval_nomain(em_input->bm, nullptr, mesh_input);
ASSERT_IS_VALID_MESH(mesh_final);
}
set_rest_position(*mesh_final);
}
for (int i = 0; md; i++, md = md->next, md_datamask = md_datamask->next) {
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);

View File

@ -386,7 +386,7 @@ static bool get_path_local_ex(char *targetpath,
char osx_resourses[FILE_MAX + 4 + 9];
BLI_path_join(osx_resourses, sizeof(osx_resourses), g_app.program_dirname, "..", "Resources");
/* Remove the '/../' added above. */
BLI_path_normalize(NULL, osx_resourses);
BLI_path_normalize(osx_resourses);
path_base = osx_resourses;
#endif
return test_path(targetpath,
@ -871,7 +871,7 @@ static void where_am_i(char *fullname, const size_t maxlen, const char *name)
BLI_path_program_search(fullname, maxlen, name);
}
/* Remove "/./" and "/../" so string comparisons can be used on the path. */
BLI_path_normalize(NULL, fullname);
BLI_path_normalize(fullname);
# if defined(DEBUG)
if (!STREQ(name, fullname)) {
@ -890,7 +890,7 @@ void BKE_appdir_program_path_init(const char *argv0)
* which must point to the Python module for data-files to be detected. */
STRNCPY(g_app.program_filepath, argv0);
BLI_path_abs_from_cwd(g_app.program_filepath, sizeof(g_app.program_filepath));
BLI_path_normalize(NULL, g_app.program_filepath);
BLI_path_normalize(g_app.program_filepath);
if (g_app.program_dirname[0] == '\0') {
/* First time initializing, the file binary path isn't valid from a Python module.

View File

@ -441,7 +441,7 @@ static bool relative_rebase_foreach_path_cb(BPathForeachPathData *bpath_data,
return false;
}
BLI_path_normalize(NULL, filepath);
BLI_path_normalize(filepath);
/* This may fail, if so it's fine to leave absolute since the path is still valid. */
BLI_path_rel(filepath, data->basedir_dst);

View File

@ -833,8 +833,7 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree(float epsilon,
}
static BVHTree *bvhtree_from_mesh_edges_create_tree(const float (*positions)[3],
const blender::int2 *edge,
const int edge_num,
blender::Span<blender::int2> edges,
const BitSpan edges_mask,
int edges_num_active,
float epsilon,
@ -842,10 +841,10 @@ static BVHTree *bvhtree_from_mesh_edges_create_tree(const float (*positions)[3],
int axis)
{
if (!edges_mask.is_empty()) {
BLI_assert(IN_RANGE_INCL(edges_num_active, 0, edge_num));
BLI_assert(IN_RANGE_INCL(edges_num_active, 0, edges.size()));
}
else {
edges_num_active = edge_num;
edges_num_active = edges.size();
}
if (edges_num_active == 0) {
return nullptr;
@ -857,13 +856,13 @@ static BVHTree *bvhtree_from_mesh_edges_create_tree(const float (*positions)[3],
return nullptr;
}
for (int i = 0; i < edge_num; i++) {
for (const int i : edges.index_range()) {
if (!edges_mask.is_empty() && !edges_mask[i]) {
continue;
}
float co[2][3];
copy_v3_v3(co[0], positions[edge[i][0]]);
copy_v3_v3(co[1], positions[edge[i][1]]);
copy_v3_v3(co[0], positions[edges[i][0]]);
copy_v3_v3(co[1], positions[edges[i][1]]);
BLI_bvhtree_insert(tree, i, co[0], 2);
}
@ -908,7 +907,7 @@ BVHTree *bvhtree_from_mesh_edges_ex(BVHTreeFromMesh *data,
int axis)
{
BVHTree *tree = bvhtree_from_mesh_edges_create_tree(
vert_positions, edge, edges_num, edges_mask, edges_num_active, epsilon, tree_type, axis);
vert_positions, {edge, edges_num}, edges_mask, edges_num_active, epsilon, tree_type, axis);
bvhtree_balance(tree, false);
@ -1142,38 +1141,6 @@ BVHTree *bvhtree_from_mesh_looptri_ex(BVHTreeFromMesh *data,
return tree;
}
static BitVector<> loose_verts_map_get(const Span<blender::int2> edges,
int verts_num,
int *r_loose_vert_num)
{
BitVector<> loose_verts_mask(verts_num, true);
int num_linked_verts = 0;
for (const int64_t i : edges.index_range()) {
const blender::int2 &edge = edges[i];
if (loose_verts_mask[edge[0]]) {
loose_verts_mask[edge[0]].reset();
num_linked_verts++;
}
if (loose_verts_mask[edge[1]]) {
loose_verts_mask[edge[1]].reset();
num_linked_verts++;
}
}
*r_loose_vert_num = verts_num - num_linked_verts;
return loose_verts_mask;
}
static BitVector<> loose_edges_map_get(const Mesh &mesh, int *r_loose_edge_len)
{
using namespace blender::bke;
const LooseEdgeCache &loose_edges = mesh.loose_edges();
*r_loose_edge_len = loose_edges.count;
return loose_edges.is_loose_bits;
}
static BitVector<> looptri_no_hidden_map_get(const blender::OffsetIndices<int> polys,
const VArray<bool> &hide_poly,
const int looptri_len,
@ -1243,27 +1210,36 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data,
}
/* Create BVHTree. */
BitVector<> mask;
int mask_bits_act_len = -1;
switch (bvh_cache_type) {
case BVHTREE_FROM_LOOSEVERTS:
mask = loose_verts_map_get(edges, mesh->totvert, &mask_bits_act_len);
ATTR_FALLTHROUGH;
case BVHTREE_FROM_VERTS:
case BVHTREE_FROM_LOOSEVERTS: {
const blender::bke::LooseVertCache &loose_verts = mesh->loose_verts();
data->tree = bvhtree_from_mesh_verts_create_tree(0.0f,
tree_type,
6,
positions,
mesh->totvert,
loose_verts.is_loose_bits,
loose_verts.count);
break;
}
case BVHTREE_FROM_VERTS: {
data->tree = bvhtree_from_mesh_verts_create_tree(
0.0f, tree_type, 6, positions, mesh->totvert, mask, mask_bits_act_len);
0.0f, tree_type, 6, positions, mesh->totvert, {}, -1);
break;
case BVHTREE_FROM_LOOSEEDGES:
mask = loose_edges_map_get(*mesh, &mask_bits_act_len);
ATTR_FALLTHROUGH;
case BVHTREE_FROM_EDGES:
}
case BVHTREE_FROM_LOOSEEDGES: {
const blender::bke::LooseEdgeCache &loose_edges = mesh->loose_edges();
data->tree = bvhtree_from_mesh_edges_create_tree(
positions, edges.data(), mesh->totedge, mask, mask_bits_act_len, 0.0f, tree_type, 6);
positions, edges, loose_edges.is_loose_bits, loose_edges.count, 0.0f, tree_type, 6);
break;
case BVHTREE_FROM_FACES:
}
case BVHTREE_FROM_EDGES: {
data->tree = bvhtree_from_mesh_edges_create_tree(
positions, edges, {}, -1, 0.0f, tree_type, 6);
break;
}
case BVHTREE_FROM_FACES: {
BLI_assert(!(mesh->totface == 0 && mesh->totpoly != 0));
data->tree = bvhtree_from_mesh_faces_create_tree(
0.0f,
@ -1275,25 +1251,29 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data,
{},
-1);
break;
}
case BVHTREE_FROM_LOOPTRI_NO_HIDDEN: {
blender::bke::AttributeAccessor attributes = mesh->attributes();
mask = looptri_no_hidden_map_get(
int mask_bits_act_len = -1;
const BitVector<> mask = looptri_no_hidden_map_get(
mesh->polys(),
*attributes.lookup_or_default(".hide_poly", ATTR_DOMAIN_FACE, false),
looptris.size(),
&mask_bits_act_len);
ATTR_FALLTHROUGH;
}
case BVHTREE_FROM_LOOPTRI:
data->tree = bvhtree_from_mesh_looptri_create_tree(
0.0f, tree_type, 6, positions, corner_verts.data(), looptris, mask, mask_bits_act_len);
break;
}
case BVHTREE_FROM_LOOPTRI: {
data->tree = bvhtree_from_mesh_looptri_create_tree(
0.0f, tree_type, 6, positions, corner_verts.data(), looptris, {}, -1);
break;
}
case BVHTREE_FROM_EM_VERTS:
case BVHTREE_FROM_EM_EDGES:
case BVHTREE_FROM_EM_LOOPTRI:
case BVHTREE_MAX_ITEM:
BLI_assert(false);
BLI_assert_unreachable();
break;
}

View File

@ -198,7 +198,7 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data)
* anyway... */
const int cb_flag = ((parent->collection != NULL &&
(parent->collection->id.flag & LIB_EMBEDDED_DATA) != 0) ?
IDWALK_CB_EMBEDDED :
IDWALK_CB_EMBEDDED_NOT_OWNING :
IDWALK_CB_NOP);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
data, parent->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_LOOPBACK | cb_flag);

View File

@ -237,7 +237,8 @@ struct ResultOffsets {
Array<int> profile_indices;
/** Whether any curve in the profile or curve input has only a single evaluated point. */
bool any_single_point_curve;
bool any_single_point_main;
bool any_single_point_profile;
};
static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool fill_caps)
{
@ -315,10 +316,8 @@ static ResultOffsets calculate_result_offsets(const CurvesInfo &info, const bool
}
}
},
[&]() {
result.any_single_point_curve = offsets_contain_single_point(main_offsets) ||
offsets_contain_single_point(profile_offsets);
});
[&]() { result.any_single_point_main = offsets_contain_single_point(main_offsets); },
[&]() { result.any_single_point_profile = offsets_contain_single_point(profile_offsets); });
return result;
}
@ -765,9 +764,13 @@ Mesh *curve_to_mesh_sweep(const CurvesGeometry &main,
positions.slice(info.vert_range));
});
if (!offsets.any_single_point_curve) {
/* If there are no single point curves, every curve combination will always have faces. */
mesh->loose_edges_tag_none();
if (!offsets.any_single_point_main) {
/* If there are no single point curves, every combination will have at least loose edges. */
mesh->tag_loose_verts_none();
if (!offsets.any_single_point_profile) {
/* If there are no single point profiles, every combination will have faces. */
mesh->loose_edges_tag_none();
}
}
SpanAttributeWriter<bool> sharp_edges;

View File

@ -2376,9 +2376,9 @@ class CustomDataLayerImplicitSharing : public ImplicitSharingInfo {
};
/** Create a #ImplicitSharingInfo that takes ownership of the data. */
static ImplicitSharingInfo *make_implicit_sharing_info_for_layer(const eCustomDataType type,
const void *data,
const int totelem)
static const ImplicitSharingInfo *make_implicit_sharing_info_for_layer(const eCustomDataType type,
const void *data,
const int totelem)
{
return MEM_new<CustomDataLayerImplicitSharing>(__func__, data, totelem, type);
}

View File

@ -431,7 +431,9 @@ CDataFileLayer *cdf_layer_add(CDataFile *cdf, int type, const char *name, size_t
/* expand array */
newlayer = MEM_calloc_arrayN((cdf->totlayer + 1), sizeof(CDataFileLayer), "CDataFileLayer");
memcpy(newlayer, cdf->layer, sizeof(CDataFileLayer) * cdf->totlayer);
if (cdf->totlayer > 0) {
memcpy(newlayer, cdf->layer, sizeof(CDataFileLayer) * cdf->totlayer);
}
cdf->layer = newlayer;
cdf->totlayer++;

View File

@ -190,29 +190,27 @@ void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh,
BLI_assert(r_values.size() == mesh.totvert);
const Span<int> corner_verts = mesh.corner_verts();
Array<bool> loose_verts(mesh.totvert, true);
r_values.fill(true);
for (const int corner : IndexRange(mesh.totloop)) {
const int point_index = corner_verts[corner];
loose_verts[point_index] = false;
if (!old_values[corner]) {
r_values[point_index] = false;
}
}
/* Deselect loose vertices without corners that are still selected from the 'true' default. */
/* The record fact says that the value is true.
* Writing to the array from different threads is okay because each thread sets the same value.
*/
threading::parallel_for(loose_verts.index_range(), 2048, [&](const IndexRange range) {
for (const int vert_index : range) {
if (loose_verts[vert_index]) {
r_values[vert_index] = false;
const bke::LooseVertCache &loose_verts = mesh.verts_no_face();
if (loose_verts.count > 0) {
const BitSpan bits = loose_verts.is_loose_bits;
threading::parallel_for(bits.index_range(), 2048, [&](const IndexRange range) {
for (const int vert_index : range) {
if (bits[vert_index]) {
r_values[vert_index] = false;
}
}
}
});
});
}
}
static GVArray adapt_mesh_domain_corner_to_point(const Mesh &mesh, const GVArray &varray)
@ -754,20 +752,26 @@ static bool can_simple_adapt_for_single(const Mesh &mesh,
/* All other domains are always connected to points. */
return true;
case ATTR_DOMAIN_EDGE:
/* There may be loose vertices not connected to edges. */
return ELEM(to_domain, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER);
if (to_domain == ATTR_DOMAIN_POINT) {
return mesh.loose_verts().count == 0;
}
return true;
case ATTR_DOMAIN_FACE:
/* There may be loose vertices or edges not connected to faces. */
if (to_domain == ATTR_DOMAIN_POINT) {
return mesh.verts_no_face().count == 0;
}
if (to_domain == ATTR_DOMAIN_EDGE) {
return mesh.loose_edges().count == 0;
}
return to_domain == ATTR_DOMAIN_CORNER;
return true;
case ATTR_DOMAIN_CORNER:
/* Only faces are always connected to corners. */
if (to_domain == ATTR_DOMAIN_POINT) {
return mesh.verts_no_face().count == 0;
}
if (to_domain == ATTR_DOMAIN_EDGE) {
return mesh.loose_edges().count == 0;
}
return to_domain == ATTR_DOMAIN_FACE;
return true;
default:
BLI_assert_unreachable();
return false;

View File

@ -137,7 +137,7 @@ static bool lib_id_library_local_paths_callback(BPathForeachPathData *bpath_data
/* Path was relative and is now absolute. Remap.
* Important BLI_path_normalize runs before the path is made relative
* because it won't work for paths that start with "//../" */
BLI_path_normalize(base_new, filepath);
BLI_path_normalize(filepath);
BLI_path_rel(filepath, base_new);
BLI_strncpy(r_path_dst, filepath, FILE_MAX);
return true;

View File

@ -115,14 +115,14 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
/* This is a direct copy of a main mesh, so for now it has the same topology. */
mesh_dst->runtime->deformed_only = true;
}
/* This option is set for run-time meshes that have been copied from the current objects mode.
/* This option is set for run-time meshes that have been copied from the current object's mode.
* Currently this is used for edit-mesh although it could be used for sculpt or other
* kinds of data specific to an objects mode.
* kinds of data specific to an object's mode.
*
* The flag signals that the mesh hasn't been modified from the data that generated it,
* allowing us to use the object-mode data for drawing.
*
* While this could be the callers responsibility, keep here since it's
* While this could be the caller's responsibility, keep here since it's
* highly unlikely we want to create a duplicate and not use it for drawing. */
mesh_dst->runtime->is_original_bmesh = false;
@ -130,6 +130,8 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int
* when the source is persistent and edits to the destination mesh don't affect the caches.
* Caches will be "un-shared" as necessary later on. */
mesh_dst->runtime->bounds_cache = mesh_src->runtime->bounds_cache;
mesh_dst->runtime->loose_verts_cache = mesh_src->runtime->loose_verts_cache;
mesh_dst->runtime->verts_no_face_cache = mesh_src->runtime->verts_no_face_cache;
mesh_dst->runtime->loose_edges_cache = mesh_src->runtime->loose_edges_cache;
mesh_dst->runtime->looptris_cache = mesh_src->runtime->looptris_cache;

View File

@ -106,32 +106,90 @@ MeshRuntime::~MeshRuntime()
}
}
static int reset_bits_and_count(MutableBitSpan bits, const Span<int> indices_to_reset)
{
int count = bits.size();
for (const int vert : indices_to_reset) {
if (bits[vert]) {
bits[vert].reset();
count--;
}
}
return count;
}
static void bit_vector_with_reset_bits_or_empty(const Span<int> indices_to_reset,
const int indexed_elems_num,
BitVector<> &r_bits,
int &r_count)
{
r_bits.resize(0);
r_bits.resize(indexed_elems_num, true);
r_count = reset_bits_and_count(r_bits, indices_to_reset);
if (r_count == 0) {
r_bits.clear_and_shrink();
}
}
/**
* If there are no loose edges and no loose vertices, all vertices are used by faces.
*/
static void try_tag_verts_no_face_none(const Mesh &mesh)
{
if (!mesh.runtime->loose_edges_cache.is_cached() || mesh.loose_edges().count > 0) {
return;
}
if (!mesh.runtime->loose_verts_cache.is_cached() || mesh.loose_verts().count > 0) {
return;
}
mesh.runtime->verts_no_face_cache.ensure([&](LooseVertCache &r_data) {
r_data.is_loose_bits.clear_and_shrink();
r_data.count = 0;
});
}
} // namespace blender::bke
const blender::bke::LooseVertCache &Mesh::loose_verts() const
{
using namespace blender::bke;
this->runtime->loose_verts_cache.ensure([&](LooseVertCache &r_data) {
const Span<int> verts = this->edges().cast<int>();
bit_vector_with_reset_bits_or_empty(verts, this->totvert, r_data.is_loose_bits, r_data.count);
});
return this->runtime->loose_verts_cache.data();
}
const blender::bke::LooseVertCache &Mesh::verts_no_face() const
{
using namespace blender::bke;
this->runtime->verts_no_face_cache.ensure([&](LooseVertCache &r_data) {
const Span<int> verts = this->corner_verts();
bit_vector_with_reset_bits_or_empty(verts, this->totvert, r_data.is_loose_bits, r_data.count);
});
return this->runtime->verts_no_face_cache.data();
}
const blender::bke::LooseEdgeCache &Mesh::loose_edges() const
{
using namespace blender::bke;
this->runtime->loose_edges_cache.ensure([&](LooseEdgeCache &r_data) {
blender::BitVector<> &loose_edges = r_data.is_loose_bits;
loose_edges.resize(0);
loose_edges.resize(this->totedge, true);
int count = this->totedge;
for (const int edge : this->corner_edges()) {
if (loose_edges[edge]) {
loose_edges[edge].reset();
count--;
}
}
if (count == 0) {
loose_edges.clear_and_shrink();
}
r_data.count = count;
const Span<int> edges = this->corner_edges();
bit_vector_with_reset_bits_or_empty(edges, this->totedge, r_data.is_loose_bits, r_data.count);
});
return this->runtime->loose_edges_cache.data();
}
void Mesh::tag_loose_verts_none() const
{
using namespace blender::bke;
this->runtime->loose_verts_cache.ensure([&](LooseVertCache &r_data) {
r_data.is_loose_bits.clear_and_shrink();
r_data.count = 0;
});
try_tag_verts_no_face_none(*this);
}
void Mesh::loose_edges_tag_none() const
{
using namespace blender::bke;
@ -139,6 +197,7 @@ void Mesh::loose_edges_tag_none() const
r_data.is_loose_bits.clear_and_shrink();
r_data.count = 0;
});
try_tag_verts_no_face_none(*this);
}
blender::Span<MLoopTri> Mesh::looptris() const
@ -219,6 +278,8 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh)
free_subdiv_ccg(*mesh->runtime);
mesh->runtime->bounds_cache.tag_dirty();
mesh->runtime->loose_edges_cache.tag_dirty();
mesh->runtime->loose_verts_cache.tag_dirty();
mesh->runtime->verts_no_face_cache.tag_dirty();
mesh->runtime->looptris_cache.tag_dirty();
mesh->runtime->subsurf_face_dot_tags.clear_and_shrink();
mesh->runtime->subsurf_optimal_display_edges.clear_and_shrink();
@ -237,6 +298,8 @@ void BKE_mesh_tag_edges_split(struct Mesh *mesh)
reset_normals(*mesh->runtime);
free_subdiv_ccg(*mesh->runtime);
mesh->runtime->loose_edges_cache.tag_dirty();
mesh->runtime->loose_verts_cache.tag_dirty();
mesh->runtime->verts_no_face_cache.tag_dirty();
mesh->runtime->subsurf_face_dot_tags.clear_and_shrink();
mesh->runtime->subsurf_optimal_display_edges.clear_and_shrink();
if (mesh->runtime->shrinkwrap_data) {

View File

@ -63,20 +63,10 @@ BLI_INLINE void mesh_calc_tessellation_for_face_impl(const Span<int> corner_vert
MLoopTri *mlt_a = mlt++;
create_tri(0, 2, 3);
MLoopTri *mlt_b = mlt;
if (UNLIKELY(face_normal ? is_quad_flip_v3_first_third_fast_with_normal(
/* Simpler calculation (using the normal). */
positions[corner_verts[mlt_a->tri[0]]],
positions[corner_verts[mlt_a->tri[1]]],
positions[corner_verts[mlt_a->tri[2]]],
positions[corner_verts[mlt_b->tri[2]]],
normal_precalc) :
is_quad_flip_v3_first_third_fast(
/* Expensive calculation (no normal). */
positions[corner_verts[mlt_a->tri[0]]],
positions[corner_verts[mlt_a->tri[1]]],
positions[corner_verts[mlt_a->tri[2]]],
positions[corner_verts[mlt_b->tri[2]]]))) {
if (UNLIKELY(is_quad_flip_v3_first_third_fast(positions[corner_verts[mlt_a->tri[0]]],
positions[corner_verts[mlt_a->tri[1]]],
positions[corner_verts[mlt_a->tri[2]]],
positions[corner_verts[mlt_b->tri[2]]]))) {
/* Flip out of degenerate 0-2 state. */
mlt_a->tri[2] = mlt_b->tri[2];
mlt_b->tri[0] = mlt_a->tri[1];

View File

@ -795,7 +795,7 @@ static void foreach_vertex_of_loose_edge(const SubdivForeachContext *foreach_con
static void store_edge(MultiresReshapeSmoothContext *reshape_smooth_context,
const int subdiv_v1,
const int subdiv_v2,
const char crease)
const float crease)
{
/* This is a bit overhead to use atomics in such a simple function called from many threads,
* but this allows to save quite measurable amount of memory. */
@ -805,7 +805,7 @@ static void store_edge(MultiresReshapeSmoothContext *reshape_smooth_context,
Edge *edge = &reshape_smooth_context->geometry.edges[edge_index];
edge->v1 = subdiv_v1;
edge->v2 = subdiv_v2;
edge->sharpness = BKE_subdiv_crease_to_sharpness_char(crease);
edge->sharpness = BKE_subdiv_crease_to_sharpness_f(crease);
}
static void foreach_edge(const SubdivForeachContext *foreach_context,
@ -821,7 +821,7 @@ static void foreach_edge(const SubdivForeachContext *foreach_context,
if (reshape_smooth_context->smoothing_type == MULTIRES_SUBDIVIDE_LINEAR) {
if (!is_loose) {
store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, char(255));
store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, 1.0f);
}
return;
}
@ -837,8 +837,8 @@ static void foreach_edge(const SubdivForeachContext *foreach_context,
return;
}
/* Edges without crease are to be ignored as well. */
const char crease = get_effective_crease(reshape_smooth_context, coarse_edge_index);
if (crease == 0) {
const float crease = get_effective_crease(reshape_smooth_context, coarse_edge_index);
if (crease == 0.0f) {
return;
}
store_edge(reshape_smooth_context, subdiv_v1, subdiv_v2, crease);

View File

@ -1020,8 +1020,6 @@ static void multires_unsubdivide_extract_grids(MultiresUnsubdivideContext *conte
* so they can be used from #BMesh. */
multires_unsubdivide_add_original_index_datalayers(base_mesh);
const int base_l_layer_index = CustomData_get_named_layer_index(
&base_mesh->ldata, CD_PROP_INT32, lname);
BMesh *bm_base_mesh = get_bmesh_from_mesh(base_mesh);
BMIter iter, iter_a, iter_b;
BMVert *v;
@ -1031,8 +1029,8 @@ static void multires_unsubdivide_extract_grids(MultiresUnsubdivideContext *conte
BM_mesh_elem_table_ensure(bm_base_mesh, BM_FACE);
/* Get the data-layer that contains the loops indices. */
const int base_l_offset = CustomData_get_n_offset(
&bm_base_mesh->ldata, CD_PROP_INT32, base_l_layer_index);
const int base_l_offset = CustomData_get_offset_named(
&bm_base_mesh->ldata, CD_PROP_INT32, lname);
const blender::OffsetIndices polys = base_mesh->polys();
const blender::Span<int> corner_verts = base_mesh->corner_verts();

View File

@ -2430,29 +2430,7 @@ bNode *node_copy_with_mapping(bNodeTree *dst_tree,
return node_dst;
}
static void for_each_node_group_instance(Main &bmain,
const bNodeTree &node_group,
const Span<int> tree_types_to_lookup,
const FunctionRef<void(bNode &)> func)
{
LISTBASE_FOREACH (bNodeTree *, other_group, &bmain.nodetrees) {
if (!tree_types_to_lookup.contains(other_group->type)) {
continue;
}
if (other_group == &node_group) {
continue;
}
other_group->ensure_topology_cache();
for (bNode *node : other_group->group_nodes()) {
if (node->id == &node_group.id) {
func(*node);
}
}
}
}
void node_socket_move_default_value(Main &bmain,
void node_socket_move_default_value(Main & /*bmain*/,
bNodeTree &tree,
bNodeSocket &src,
bNodeSocket &dst)
@ -2490,14 +2468,6 @@ void node_socket_move_default_value(Main &bmain,
dst_values.append(&dst_node.id);
break;
}
case NODE_GROUP_INPUT: {
for_each_node_group_instance(bmain, tree, {NTREE_GEOMETRY}, [&](bNode &node_group) {
bNodeSocket &socket = node_group.input_by_identifier(dst.identifier);
Image **tmp_dst_value = &socket.default_value_typed<bNodeSocketValueImage>()->value;
dst_values.append(reinterpret_cast<ID **>(tmp_dst_value));
});
break;
}
default: {
break;
}

View File

@ -819,6 +819,15 @@ void BKE_sound_set_scene_sound_pitch(void *handle, float pitch, char animated)
AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PITCH, sound_cfra, &pitch, animated);
}
void BKE_sound_set_scene_sound_pitch_constant_range(void *handle,
int frame_start,
int frame_end,
float pitch)
{
AUD_SequenceEntry_setConstantRangeAnimationData(
handle, AUD_AP_PITCH, frame_start, frame_end, &pitch);
}
void BKE_sound_set_scene_sound_pan(void *handle, float pan, char animated)
{
AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PANNING, sound_cfra, &pan, animated);
@ -1369,6 +1378,12 @@ void BKE_sound_set_scene_sound_pitch(void *UNUSED(handle),
char UNUSED(animated))
{
}
void BKE_sound_set_scene_sound_pitch_constant_range(void *UNUSED(handle),
int UNUSED(frame_start),
int UNUSED(frame_end),
float UNUSED(pitch))
{
}
float BKE_sound_get_length(struct Main *UNUSED(bmain), bSound *UNUSED(sound))
{
return 0;

View File

@ -161,7 +161,7 @@ static float get_edge_sharpness(const OpenSubdiv_Converter *converter, int manif
return 10.0f;
}
#endif
if (!storage->settings.use_creases || storage->cd_edge_crease == nullptr) {
if (storage->cd_edge_crease == nullptr) {
return 0.0f;
}
const int edge_index = storage->manifold_edge_index_reverse[manifold_edge_index];
@ -177,6 +177,9 @@ static bool is_infinite_sharp_vertex(const OpenSubdiv_Converter *converter,
return true;
}
#endif
if (storage->infinite_sharp_vertices_map == nullptr) {
return false;
}
const int vertex_index = storage->manifold_vertex_index_reverse[manifold_vertex_index];
return BLI_BITMAP_TEST_BOOL(storage->infinite_sharp_vertices_map, vertex_index);
}
@ -184,7 +187,7 @@ static bool is_infinite_sharp_vertex(const OpenSubdiv_Converter *converter,
static float get_vertex_sharpness(const OpenSubdiv_Converter *converter, int manifold_vertex_index)
{
ConverterStorage *storage = static_cast<ConverterStorage *>(converter->user_data);
if (!storage->settings.use_creases || storage->cd_vertex_crease == nullptr) {
if (storage->cd_vertex_crease == nullptr) {
return 0.0f;
}
const int vertex_index = storage->manifold_vertex_index_reverse[manifold_vertex_index];
@ -211,16 +214,15 @@ static void precalc_uv_layer(const OpenSubdiv_Converter *converter, const int la
storage->loop_uv_indices = static_cast<int *>(
MEM_malloc_arrayN(mesh->totloop, sizeof(int), "loop uv vertex index"));
}
UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create(
storage->polys,
(const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"),
(const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".select_poly"),
storage->corner_verts.data(),
mloopuv,
num_vert,
limit,
false,
true);
UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create(storage->polys,
nullptr,
nullptr,
storage->corner_verts.data(),
mloopuv,
num_vert,
limit,
false,
true);
/* NOTE: First UV vertex is supposed to be always marked as separate. */
storage->num_uv_coordinates = -1;
for (int vertex_index = 0; vertex_index < num_vert; vertex_index++) {
@ -264,7 +266,7 @@ static void free_user_data(const OpenSubdiv_Converter *converter)
ConverterStorage *user_data = static_cast<ConverterStorage *>(converter->user_data);
MEM_SAFE_FREE(user_data->loop_uv_indices);
MEM_freeN(user_data->manifold_vertex_index);
MEM_freeN(user_data->infinite_sharp_vertices_map);
MEM_SAFE_FREE(user_data->infinite_sharp_vertices_map);
MEM_freeN(user_data->manifold_vertex_index_reverse);
MEM_freeN(user_data->manifold_edge_index_reverse);
MEM_freeN(user_data);
@ -306,7 +308,7 @@ static void init_functions(OpenSubdiv_Converter *converter)
converter->freeUserData = free_user_data;
}
static void initialize_manifold_index_array(const BLI_bitmap *used_map,
static void initialize_manifold_index_array(const blender::BitSpan not_used_map,
const int num_elements,
int **r_indices,
int **r_indices_reverse,
@ -323,7 +325,7 @@ static void initialize_manifold_index_array(const BLI_bitmap *used_map,
}
int offset = 0;
for (int i = 0; i < num_elements; i++) {
if (BLI_BITMAP_TEST_BOOL(used_map, i)) {
if (not_used_map.is_empty() || !not_used_map[i]) {
if (indices != nullptr) {
indices[i] = i - offset;
}
@ -349,42 +351,35 @@ static void initialize_manifold_index_array(const BLI_bitmap *used_map,
static void initialize_manifold_indices(ConverterStorage *storage)
{
using namespace blender;
const Mesh *mesh = storage->mesh;
const blender::Span<blender::int2> edges = storage->edges;
const blender::OffsetIndices<int> polys = storage->polys;
const blender::Span<int> corner_verts = storage->corner_verts;
const blender::Span<int> corner_edges = storage->corner_edges;
/* Set bits of elements which are not loose. */
BLI_bitmap *vert_used_map = BLI_BITMAP_NEW(mesh->totvert, "vert used map");
BLI_bitmap *edge_used_map = BLI_BITMAP_NEW(mesh->totedge, "edge used map");
for (int poly_index = 0; poly_index < mesh->totpoly; poly_index++) {
for (const int corner : polys[poly_index]) {
BLI_BITMAP_ENABLE(vert_used_map, corner_verts[corner]);
BLI_BITMAP_ENABLE(edge_used_map, corner_edges[corner]);
}
}
initialize_manifold_index_array(vert_used_map,
const bke::LooseVertCache &loose_verts = mesh->verts_no_face();
const bke::LooseEdgeCache &loose_edges = mesh->loose_edges();
initialize_manifold_index_array(loose_verts.is_loose_bits,
mesh->totvert,
&storage->manifold_vertex_index,
&storage->manifold_vertex_index_reverse,
&storage->num_manifold_vertices);
initialize_manifold_index_array(edge_used_map,
initialize_manifold_index_array(loose_edges.is_loose_bits,
mesh->totedge,
nullptr,
&storage->manifold_edge_index_reverse,
&storage->num_manifold_edges);
/* Initialize infinite sharp mapping. */
storage->infinite_sharp_vertices_map = BLI_BITMAP_NEW(mesh->totvert, "vert used map");
for (int edge_index = 0; edge_index < mesh->totedge; edge_index++) {
if (!BLI_BITMAP_TEST_BOOL(edge_used_map, edge_index)) {
const blender::int2 &edge = edges[edge_index];
BLI_BITMAP_ENABLE(storage->infinite_sharp_vertices_map, edge[0]);
BLI_BITMAP_ENABLE(storage->infinite_sharp_vertices_map, edge[1]);
if (loose_edges.count > 0) {
const Span<int2> edges = storage->edges;
storage->infinite_sharp_vertices_map = BLI_BITMAP_NEW(mesh->totvert, "vert used map");
for (int edge_index = 0; edge_index < mesh->totedge; edge_index++) {
if (loose_edges.is_loose_bits[edge_index]) {
const int2 edge = edges[edge_index];
BLI_BITMAP_ENABLE(storage->infinite_sharp_vertices_map, edge[0]);
BLI_BITMAP_ENABLE(storage->infinite_sharp_vertices_map, edge[1]);
}
}
}
/* Free working variables. */
MEM_freeN(vert_used_map);
MEM_freeN(edge_used_map);
else {
storage->infinite_sharp_vertices_map = nullptr;
}
}
static void init_user_data(OpenSubdiv_Converter *converter,
@ -399,10 +394,12 @@ static void init_user_data(OpenSubdiv_Converter *converter,
user_data->polys = mesh->polys();
user_data->corner_verts = mesh->corner_verts();
user_data->corner_edges = mesh->corner_edges();
user_data->cd_vertex_crease = static_cast<const float *>(
CustomData_get_layer(&mesh->vdata, CD_CREASE));
user_data->cd_edge_crease = static_cast<const float *>(
CustomData_get_layer(&mesh->edata, CD_CREASE));
if (settings->use_creases) {
user_data->cd_vertex_crease = static_cast<const float *>(
CustomData_get_layer(&mesh->vdata, CD_CREASE));
user_data->cd_edge_crease = static_cast<const float *>(
CustomData_get_layer(&mesh->edata, CD_CREASE));
}
user_data->loop_uv_indices = nullptr;
initialize_manifold_indices(user_data);
converter->user_data = user_data;

View File

@ -91,9 +91,3 @@ BLI_INLINE float BKE_subdiv_crease_to_sharpness_f(float edge_crease)
{
return edge_crease * edge_crease * 10.0f;
}
BLI_INLINE float BKE_subdiv_crease_to_sharpness_char(char edge_crease)
{
const float edge_crease_f = edge_crease / 255.0f;
return BKE_subdiv_crease_to_sharpness_f(edge_crease_f);
}

View File

@ -1192,11 +1192,19 @@ Mesh *BKE_subdiv_to_mesh(Subdiv *subdiv,
BitVector<> &bit_vector = result->runtime->subsurf_optimal_display_edges;
bit_vector.clear();
bit_vector.resize(subdiv_context.subdiv_display_edges.size());
threading::parallel_for_aligned(span.index_range(), 4096, 64, [&](const IndexRange range) {
for (const int i : range) {
bit_vector[i].set(span[i]);
}
});
threading::parallel_for_aligned(
span.index_range(), 4096, bits::BitsPerInt, [&](const IndexRange range) {
for (const int i : range) {
bit_vector[i].set(span[i]);
}
});
}
if (coarse_mesh->verts_no_face().count == 0) {
result->tag_loose_verts_none();
}
if (coarse_mesh->loose_edges().count == 0) {
result->loose_edges_tag_none();
}
if (subdiv->settings.is_simple) {

View File

@ -170,12 +170,9 @@ Subdiv *BKE_subsurf_modifier_subdiv_descriptor_ensure(SubsurfRuntimeData *runtim
return runtime_data->subdiv_gpu = BKE_subdiv_update_from_mesh(
runtime_data->subdiv_gpu, &runtime_data->settings, mesh);
}
else {
runtime_data->used_cpu = 2;
return runtime_data->subdiv_cpu = BKE_subdiv_update_from_mesh(
runtime_data->subdiv_cpu, &runtime_data->settings, mesh);
}
runtime_data->used_cpu = 2;
return runtime_data->subdiv_cpu = BKE_subdiv_update_from_mesh(
runtime_data->subdiv_cpu, &runtime_data->settings, mesh);
}
int BKE_subsurf_modifier_eval_required_mode(bool is_final_render, bool is_edit_mode)

View File

@ -625,7 +625,7 @@ fn::GField DataTypeConversions::try_convert(fn::GField field, const CPPType &to_
}
const mf::MultiFunction &fn = *this->get_conversion_multi_function(
mf::DataType::ForSingle(from_type), mf::DataType::ForSingle(to_type));
return {std::make_shared<fn::FieldOperation>(fn, Vector<fn::GField>{std::move(field)})};
return {fn::FieldOperation::Create(fn, {std::move(field)})};
}
} // namespace blender::bke

View File

@ -734,6 +734,25 @@ typedef struct VFontToCurveIter {
int status;
} VFontToCurveIter;
/** \} */
/* -------------------------------------------------------------------- */
/** \name VFont Mouse Cursor to Text Offset
*
* This is an optional argument to `vfont_to_curve` for getting the text
* offset into the string at a mouse cursor location. Used for getting
* text cursor (caret) position or selection range.
* \{ */
/* Used when translating a mouse cursor location to a position within the string. */
typedef struct VFontCursor_Params {
/* Mouse cursor location in Object coordinate space as input. */
float cursor_location[2];
/* Character position within EditFont::textbuf as output. */
int r_string_offset;
} VFontCursor_Params;
/** \} */
enum {
VFONT_TO_CURVE_INIT = 0,
VFONT_TO_CURVE_BISECT,
@ -774,6 +793,7 @@ static bool vfont_to_curve(Object *ob,
Curve *cu,
int mode,
VFontToCurveIter *iter_data,
struct VFontCursor_Params *cursor_params,
ListBase *r_nubase,
const char32_t **r_text,
int *r_text_len,
@ -1441,6 +1461,35 @@ static bool vfont_to_curve(Object *ob,
}
}
if (cursor_params) {
cursor_params->r_string_offset = -1;
for (i = 0; i <= slen; i++, ct++) {
info = &custrinfo[i];
ascii = mem[i];
if (info->flag & CU_CHINFO_SMALLCAPS_CHECK) {
ascii = towupper(ascii);
}
ct = &chartransdata[i];
che = find_vfont_char(vfd, ascii);
float charwidth = char_width(cu, che, info);
float charhalf = (charwidth / 2.0f);
if (cursor_params->cursor_location[1] >= ct->yof - (0.25f * linedist) &&
cursor_params->cursor_location[1] <= (ct->yof + (0.75f * linedist))) {
/* On this row. */
if (cursor_params->cursor_location[0] >= (ct->xof) &&
cursor_params->cursor_location[0] <= (ct->xof + charhalf)) {
/* Left half of character. */
cursor_params->r_string_offset = i;
}
else if (cursor_params->cursor_location[0] >= (ct->xof + charhalf) &&
cursor_params->cursor_location[0] <= (ct->xof + charwidth)) {
/* Right half of character. */
cursor_params->r_string_offset = i + 1;
}
}
}
}
if (ELEM(mode, FO_CURSUP, FO_CURSDOWN, FO_PAGEUP, FO_PAGEDOWN) &&
iter_data->status == VFONT_TO_CURVE_INIT) {
ct = &chartransdata[ef->pos];
@ -1738,13 +1787,49 @@ bool BKE_vfont_to_curve_ex(Object *ob,
};
do {
data.ok &= vfont_to_curve(
ob, cu, mode, &data, r_nubase, r_text, r_text_len, r_text_free, r_chartransdata);
data.ok &= vfont_to_curve(ob,
cu,
mode,
&data,
NULL,
r_nubase,
r_text,
r_text_len,
r_text_free,
r_chartransdata);
} while (data.ok && ELEM(data.status, VFONT_TO_CURVE_SCALE_ONCE, VFONT_TO_CURVE_BISECT));
return data.ok;
}
int BKE_vfont_cursor_to_text_index(Object *ob, float cursor_location[2])
{
Curve *cu = (Curve *)ob->data;
ListBase *r_nubase = &cu->nurb;
/* TODO: iterating to calculate the scale can be avoided. */
VFontToCurveIter data = {
.iteraction = cu->totbox * FONT_TO_CURVE_SCALE_ITERATIONS,
.scale_to_fit = 1.0f,
.word_wrap = true,
.ok = true,
.status = VFONT_TO_CURVE_INIT,
};
VFontCursor_Params cursor_params = {
.cursor_location = {cursor_location[0], cursor_location[1]},
.r_string_offset = -1,
};
do {
data.ok &= vfont_to_curve(
ob, cu, FO_CURS, &data, &cursor_params, r_nubase, NULL, NULL, NULL, NULL);
} while (data.ok && ELEM(data.status, VFONT_TO_CURVE_SCALE_ONCE, VFONT_TO_CURVE_BISECT));
return cursor_params.r_string_offset;
}
#undef FONT_TO_CURVE_SCALE_ITERATIONS
#undef FONT_TO_CURVE_SCALE_THRESHOLD

View File

@ -15,6 +15,14 @@ namespace blender::array_utils {
* grain-size.
*/
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size = 4096);
template<typename T>
inline void copy(const VArray<T> &src, MutableSpan<T> dst, const int64_t grain_size = 4096)
{
BLI_assert(src.size() == dst.size());
threading::parallel_for(src.index_range(), grain_size, [&](const IndexRange range) {
src.materialize_to_uninitialized(range, dst);
});
}
/**
* Fill the destination span by copying all values from the `src` array. Threaded based on
@ -28,6 +36,7 @@ inline void copy(const Span<T> src, MutableSpan<T> dst, const int64_t grain_size
dst.slice(range).copy_from(src.slice(range));
});
}
/**
* Fill the destination span by copying masked values from the `src` array. Threaded based on
* grain-size.

View File

@ -217,7 +217,7 @@ template<typename T> struct DefaultHash<T *> {
template<typename T> uint64_t get_default_hash(const T &v)
{
return DefaultHash<T>{}(v);
return DefaultHash<std::decay_t<T>>{}(v);
}
template<typename T1, typename T2> uint64_t get_default_hash_2(const T1 &v1, const T2 &v2)

View File

@ -110,11 +110,11 @@ void *resize_trivial_array_impl(void *old_data,
int64_t old_size,
int64_t new_size,
int64_t alignment,
ImplicitSharingInfo **sharing_info);
const ImplicitSharingInfo **sharing_info);
void *make_trivial_data_mutable_impl(void *old_data,
int64_t size,
int64_t alignment,
ImplicitSharingInfo **sharing_info);
const ImplicitSharingInfo **sharing_info);
} // namespace detail
@ -124,9 +124,9 @@ void *make_trivial_data_mutable_impl(void *old_data,
*/
template<typename T>
void copy_shared_pointer(T *src_ptr,
ImplicitSharingInfo *src_sharing_info,
const ImplicitSharingInfo *src_sharing_info,
T **r_dst_ptr,
ImplicitSharingInfo **r_dst_sharing_info)
const ImplicitSharingInfo **r_dst_sharing_info)
{
*r_dst_ptr = src_ptr;
*r_dst_sharing_info = src_sharing_info;
@ -139,7 +139,7 @@ void copy_shared_pointer(T *src_ptr,
/**
* Remove this reference to the shared data and remove dangling pointers.
*/
template<typename T> void free_shared_data(T **data, ImplicitSharingInfo **sharing_info)
template<typename T> void free_shared_data(T **data, const ImplicitSharingInfo **sharing_info)
{
if (*sharing_info) {
BLI_assert(*data != nullptr);
@ -153,13 +153,15 @@ template<typename T> void free_shared_data(T **data, ImplicitSharingInfo **shari
* Create an implicit sharing object that takes ownership of the data, allowing it to be shared.
* When it is no longer used, the data is freed with #MEM_freeN, so it must be a trivial type.
*/
ImplicitSharingInfo *info_for_mem_free(void *data);
const ImplicitSharingInfo *info_for_mem_free(void *data);
/**
* Make data mutable (single-user) if it is shared. For trivially-copyable data only.
*/
template<typename T>
void make_trivial_data_mutable(T **data, ImplicitSharingInfo **sharing_info, const int64_t size)
void make_trivial_data_mutable(T **data,
const ImplicitSharingInfo **sharing_info,
const int64_t size)
{
*data = static_cast<T *>(
detail::make_trivial_data_mutable_impl(*data, sizeof(T) * size, alignof(T), sharing_info));
@ -171,7 +173,7 @@ void make_trivial_data_mutable(T **data, ImplicitSharingInfo **sharing_info, con
*/
template<typename T>
void resize_trivial_array(T **data,
ImplicitSharingInfo **sharing_info,
const ImplicitSharingInfo **sharing_info,
int64_t old_size,
int64_t new_size)
{

View File

@ -105,6 +105,23 @@ inline void BLI_kdtree_nd_(range_search_cb_cpp)(const KDTree *tree,
},
const_cast<Fn *>(&fn));
}
template<typename Fn>
inline int BLI_kdtree_nd_(find_nearest_cb_cpp)(const KDTree *tree,
const float co[KD_DIMS],
KDTreeNearest *r_nearest,
Fn &&fn)
{
return BLI_kdtree_nd_(find_nearest_cb)(
tree,
co,
[](void *user_data, const int index, const float *co, const float dist_sq) {
Fn &fn = *static_cast<Fn *>(user_data);
return fn(index, co, dist_sq);
},
&fn,
r_nearest);
}
#endif
#undef _BLI_CONCAT_AUX

View File

@ -145,11 +145,6 @@ bool is_quad_flip_v3_first_third_fast(const float v1[3],
const float v2[3],
const float v3[3],
const float v4[3]);
bool is_quad_flip_v3_first_third_fast_with_normal(const float v1[3],
const float v2[3],
const float v3[3],
const float v4[3],
const float normal[3]);
/** \} */

View File

@ -323,19 +323,18 @@ void BLI_path_sequence_encode(
/**
* Remove redundant characters from \a path and optionally make absolute.
*
* \param relabase: The path this is relative to, or ignored when NULL.
* \param path: Can be any input, and this function converts it to a regular full path.
* Also removes garbage from directory paths, like `/../` or double slashes etc.
*
* \note \a path isn't protected for max string names.
*/
void BLI_path_normalize(const char *relabase, char *path) ATTR_NONNULL(2);
void BLI_path_normalize(char *path) ATTR_NONNULL(1);
/**
* Cleanup file-path ensuring a trailing slash.
*
* \note Same as #BLI_path_normalize but adds a trailing slash.
*/
void BLI_path_normalize_dir(const char *relabase, char *dir, size_t dir_maxlen) ATTR_NONNULL(2);
void BLI_path_normalize_dir(char *dir, size_t dir_maxlen) ATTR_NONNULL(1);
/**
* Make given name safe to be used in paths.

View File

@ -26,7 +26,7 @@ class MEMFreeImplicitSharing : public ImplicitSharingInfo {
}
};
ImplicitSharingInfo *info_for_mem_free(void *data)
const ImplicitSharingInfo *info_for_mem_free(void *data)
{
return MEM_new<MEMFreeImplicitSharing>(__func__, data);
}
@ -36,7 +36,7 @@ namespace detail {
void *make_trivial_data_mutable_impl(void *old_data,
const int64_t size,
const int64_t alignment,
ImplicitSharingInfo **sharing_info)
const ImplicitSharingInfo **sharing_info)
{
if (!old_data) {
BLI_assert(size == 0);
@ -59,7 +59,7 @@ void *resize_trivial_array_impl(void *old_data,
const int64_t old_size,
const int64_t new_size,
const int64_t alignment,
ImplicitSharingInfo **sharing_info)
const ImplicitSharingInfo **sharing_info)
{
if (new_size == 0) {
if (*sharing_info) {
@ -79,7 +79,8 @@ void *resize_trivial_array_impl(void *old_data,
BLI_assert(old_size != 0);
if ((*sharing_info)->is_mutable()) {
if (auto *info = dynamic_cast<MEMFreeImplicitSharing *>(*sharing_info)) {
if (auto *info = const_cast<MEMFreeImplicitSharing *>(
dynamic_cast<const MEMFreeImplicitSharing *>(*sharing_info))) {
/* If the array was allocated with the MEM allocator, we can use realloc directly, which
* could theoretically give better performance if the data can be reused in place. */
void *new_data = static_cast<int *>(MEM_reallocN(old_data, new_size));

View File

@ -5889,6 +5889,9 @@ bool is_quad_flip_v3_first_third_fast(const float v1[3],
const float v3[3],
const float v4[3])
{
/* NOTE: if the faces normal has been calculated it's possible to simplify the following checks,
* however this means the solution may be different depending on the existence of normals
* causing tessellation to be "unstable" depending on the existence of normals, see #106469. */
float d_12[3], d_13[3], d_14[3];
float cross_a[3], cross_b[3];
sub_v3_v3v3(d_12, v2, v1);
@ -5899,19 +5902,6 @@ bool is_quad_flip_v3_first_third_fast(const float v1[3],
return dot_v3v3(cross_a, cross_b) > 0.0f;
}
bool is_quad_flip_v3_first_third_fast_with_normal(const float v1[3],
const float v2[3],
const float v3[3],
const float v4[3],
const float normal[3])
{
float dir_v3v1[3], tangent[3];
sub_v3_v3v3(dir_v3v1, v3, v1);
cross_v3_v3v3(tangent, dir_v3v1, normal);
const float dot = dot_v3v3(v1, tangent);
return (dot_v3v3(v4, tangent) >= dot) || (dot_v3v3(v2, tangent) <= dot);
}
float cubic_tangent_factor_circle_v3(const float tan_l[3], const float tan_r[3])
{
BLI_ASSERT_UNIT_V3(tan_l);

View File

@ -2728,7 +2728,7 @@ void svd_m4(float U[4][4], float s[4], float V[4][4], float A_[4][4])
e[k + 1] += 1.0f;
}
e[k] = -e[k];
if ((k + 1 < m) & (e[k] != 0.0f)) {
if ((k + 1 < m) && (e[k] != 0.0f)) {
float invek1;
/* Apply the transformation. */
@ -2812,7 +2812,7 @@ void svd_m4(float U[4][4], float s[4], float V[4][4], float A_[4][4])
/* If required, generate V. */
for (k = n - 1; k >= 0; k--) {
if ((k < nrt) & (e[k] != 0.0f)) {
if ((k < nrt) && (e[k] != 0.0f)) {
for (j = k + 1; j < nu; j++) {
float t = 0;
for (i = k + 1; i < n; i++) {

View File

@ -1962,17 +1962,19 @@ static Face *cdt_tri_as_imesh_face(
return facep;
}
/* Like BLI_math's is_quad_flip_v3_first_third_fast_with_normal, with const double3's. */
/* Like BLI_math's is_quad_flip_v3_first_third_fast, with const double3's. */
static bool is_quad_flip_first_third(const double3 &v1,
const double3 &v2,
const double3 &v3,
const double3 &v4,
const double3 &normal)
const double3 &v4)
{
double3 dir_v3v1 = v3 - v1;
double3 tangent = math::cross(dir_v3v1, normal);
double dot = math::dot(v1, tangent);
return (math::dot(v4, tangent) >= dot) || (math::dot(v2, tangent) <= dot);
const double3 d_12 = v2 - v1;
const double3 d_13 = v3 - v1;
const double3 d_14 = v4 - v1;
const double3 cross_a = math::cross(d_12, d_13);
const double3 cross_b = math::cross(d_14, d_13);
return math::dot(cross_a, cross_b) > 0.0f;
}
/**
@ -2008,7 +2010,7 @@ static Array<Face *> polyfill_triangulate_poly(Face *f, IMeshArena *arena)
int eo_23 = f->edge_orig[2];
int eo_30 = f->edge_orig[3];
Face *f0, *f1;
if (UNLIKELY(is_quad_flip_first_third(v0->co, v1->co, v2->co, v3->co, f->plane->norm))) {
if (UNLIKELY(is_quad_flip_first_third(v0->co, v1->co, v2->co, v3->co))) {
f0 = arena->add_face({v0, v1, v3}, f->orig, {eo_01, -1, eo_30}, {false, false, false});
f1 = arena->add_face({v1, v2, v3}, f->orig, {eo_12, eo_23, -1}, {false, false, false});
}

View File

@ -37,22 +37,16 @@
#include "MEM_guardedalloc.h"
/* Declarations */
/* Declarations. */
static int BLI_path_unc_prefix_len(const char *path);
#ifdef WIN32
/**
* Return true if the path is absolute ie starts with a drive specifier
* (eg A:\) or is a UNC path.
*/
static bool BLI_path_is_abs(const char *name);
static bool BLI_path_is_abs_win32(const char *name);
#endif /* WIN32 */
// #define DEBUG_STRSIZE
/* implementation */
int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort *r_digits_len)
{
uint nums = 0, nume = 0;
@ -102,9 +96,8 @@ int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort
strcpy(tail, string + name_end);
}
if (head) {
/* name_end points to last character of head,
* make it +1 so null-terminator is nicely placed
*/
/* Name_end points to last character of head,
* make it +1 so null-terminator is nicely placed. */
BLI_strncpy(head, string, name_end + 1);
}
if (r_digits_len) {
@ -119,84 +112,139 @@ void BLI_path_sequence_encode(
BLI_sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail);
}
static int BLI_path_unc_prefix_len(const char *path); /* defined below in same file */
void BLI_path_normalize(const char *relabase, char *path)
void BLI_path_normalize(char *path)
{
const char *path_orig = path;
int path_len;
ptrdiff_t a;
char *start, *eind;
if (relabase) {
BLI_path_abs(path, relabase);
}
else {
if (path[0] == '/' && path[1] == '/') {
if (path[2] == '\0') {
return; /* path is "//" - can't clean it */
}
path = path + 2; /* leave the initial "//" untouched */
}
}
/* Note
* memmove(start, eind, strlen(eind) + 1);
* is the same as
* strcpy(start, eind);
* except strcpy should not be used because there is overlap,
* so use memmove's slightly more obscure syntax - Campbell
*/
path_len = strlen(path);
if (path[0] == '/' && path[1] == '/') {
path = path + 2; /* Leave the initial `//` untouched. */
path_len -= 2;
/* Strip leading slashes, as they will interfere with the absolute/relative check
* (besides being redundant). */
int i = 0;
while (path[i] == SEP) {
i++;
}
if (i != 0) {
memmove(path, path + i, (path_len - i) + 1);
path_len -= i;
}
BLI_assert(path_len == strlen(path));
}
#ifdef WIN32
while ((start = strstr(path, "\\.\\"))) {
eind = start + strlen("\\.\\") - 1;
memmove(start, eind, strlen(eind) + 1);
/* Skip to the first slash of the drive or UNC path,
* so additional slashes are treated as doubles. */
if (path_orig == path) {
int path_unc_len = BLI_path_unc_prefix_len(path);
if (path_unc_len) {
path_unc_len -= 1;
BLI_assert(path_unc_len > 0 && path[path_unc_len] == SEP);
path += path_unc_len;
path_len -= path_unc_len;
}
else if (isalpha(path[0]) && (path[1] == ':')) {
path += 2;
path_len -= 2;
}
}
#endif /* WIN32 */
/* remove two consecutive backslashes, but skip the UNC prefix,
* which needs to be preserved */
while ((start = strstr(path + BLI_path_unc_prefix_len(path), "\\\\"))) {
eind = start + strlen("\\\\") - 1;
memmove(start, eind, strlen(eind) + 1);
}
/* Works on WIN32 as well, because the drive component is skipped. */
const bool is_relative = path[0] && (path[0] != SEP);
while ((start = strstr(path, "\\..\\"))) {
eind = start + strlen("\\..\\") - 1;
a = start - path - 1;
while (a > 0) {
if (path[a] == '\\') {
break;
/* NOTE(@ideasman42):
* `memmove(start, eind, strlen(eind) + 1);`
* is the same as
* `strcpy(start, eind);`
* except `strcpy` should not be used because there is overlap,
* so use `memmove` 's slightly more obscure syntax. */
/* Inline replacement:
* - `/./` -> `/`.
* - `//` -> `/`.
* Performed until no more replacements can be made. */
if (path_len > 1) {
for (int i = path_len - 1; i > 0; i--) {
/* Calculate the redundant slash span (if any). */
if (path[i] == SEP) {
const int i_end = i;
do {
/* Stepping over elements assumes 'i' references a separator. */
BLI_assert(path[i] == SEP);
if (path[i - 1] == SEP) {
i -= 1; /* Found `//`, replace with `/`. */
}
else if (i >= 2 && path[i - 1] == '.' && path[i - 2] == SEP) {
i -= 2; /* Found `/./`, replace with `/`. */
}
else {
break;
}
} while (i > 0);
if (i < i_end) {
memmove(path + i, path + i_end, (path_len - i_end) + 1);
path_len -= i_end - i;
BLI_assert(strlen(path) == path_len);
}
}
a--;
}
if (a < 0) {
break;
}
else {
memmove(path + a, eind, strlen(eind) + 1);
}
}
#else
while ((start = strstr(path, "/./"))) {
eind = start + (3 - 1) /* strlen("/./") - 1 */;
memmove(start, eind, strlen(eind) + 1);
/* Remove redundant `./` prefix, while it could be kept, it confuses the loop below. */
if (is_relative) {
if ((path_len > 2) && (path[0] == '.') && (path[1] == SEP)) {
memmove(path, path + 2, (path_len - 2) + 1);
path_len -= 2;
}
}
while ((start = strstr(path, "//"))) {
eind = start + (2 - 1) /* strlen("//") - 1 */;
memmove(start, eind, strlen(eind) + 1);
}
const ptrdiff_t a_start = is_relative ? 0 : 1;
start = path;
while ((start = strstr(start, SEP_STR ".."))) {
if (!ELEM(start[3], SEP, '\0')) {
start += 3;
continue;
}
while ((start = strstr(path, "/../"))) {
a = start - path - 1;
if (a > 0) {
/* <prefix>/<parent>/../<postfix> => <prefix>/<postfix> */
eind = start + (4 - 1) /* strlen("/../") - 1 */; /* strip "/.." and keep last "/" */
while (a > 0 && path[a] != '/') { /* find start of <parent> */
a = (start - path) - 1;
if (a >= a_start) {
/* `<prefix>/<parent>/../<postfix> => <prefix>/<postfix>`. */
eind = start + (4 - 1) /* `strlen("/../") - 1` */; /* Strip "/.." and keep the char after. */
while (a > 0 && path[a] != SEP) { /* Find start of `<parent>`. */
a--;
}
memmove(path + a, eind, strlen(eind) + 1);
if (is_relative && (a == 0) && *eind) {
/* When the path does not start with a slash, don't copy the first `/` to the destination
* as it will make a relative path into an absolute path. */
eind += 1;
}
const size_t eind_len = path_len - (eind - path);
BLI_assert(eind_len == strlen(eind));
/* Only remove the parent if it's not also a `..`. */
if (is_relative && STRPREFIX(path + ((path[a] == SEP) ? a + 1 : a), ".." SEP_STR)) {
start += 3 /* `strlen("/..")` */;
}
else {
start = path + a;
BLI_assert(start < eind);
memmove(start, eind, eind_len + 1);
path_len -= (eind - start);
BLI_assert(strlen(path) == path_len);
BLI_assert(!is_relative || (path[0] != SEP));
}
}
else {
/* Support for odd paths: eg `/../home/me` --> `/home/me`
@ -208,21 +256,32 @@ void BLI_path_normalize(const char *relabase, char *path)
* which meant that the `/../home/me` example actually became `home/me`.
* Using offset of 3 gives behavior consistent with the aforementioned
* Python routine. */
memmove(path, path + 3, strlen(path + 3) + 1);
eind = start + 3;
const size_t eind_len = path_len - (eind - path);
memmove(start, eind, eind_len + 1);
path_len -= 3;
BLI_assert(strlen(path) == path_len);
BLI_assert(!is_relative || (path[0] != SEP));
}
}
#endif
if (is_relative && path_len == 0 && (path == path_orig)) {
path[0] = '.';
path[1] = '\0';
path_len += 1;
}
BLI_assert(strlen(path) == path_len);
}
void BLI_path_normalize_dir(const char *relabase, char *dir, size_t dir_maxlen)
void BLI_path_normalize_dir(char *dir, size_t dir_maxlen)
{
/* Would just create an unexpected "/" path, just early exit entirely. */
if (dir[0] == '\0') {
return;
}
BLI_path_normalize(relabase, dir);
BLI_path_normalize(dir);
BLI_path_slash_ensure(dir, dir_maxlen);
}
@ -253,7 +312,7 @@ bool BLI_filename_make_safe_ex(char *fname, bool allow_tokens)
/* Forbid only dots. */
for (fn = fname; *fn == '.'; fn++) {
/* pass */
/* Pass. */
}
if (*fn == '\0') {
*fname = '_';
@ -279,15 +338,14 @@ bool BLI_filename_make_safe_ex(char *fname, bool allow_tokens)
/* Check for forbidden names - not we have to check all combination
* of upper and lower cases, hence the usage of lower_fname
* (more efficient than using BLI_strcasestr repeatedly). */
* (more efficient than using #BLI_strcasestr repeatedly). */
BLI_str_tolower_ascii(lower_fname, len);
for (iname = invalid_names; *iname; iname++) {
if (strstr(lower_fname, *iname) == lower_fname) {
const size_t iname_len = strlen(*iname);
/* Only invalid if the whole name is made of the invalid chunk, or it has an
* (assumed extension) dot just after. This means it will also catch 'valid'
* names like 'aux.foo.bar', but should be
* good enough for us! */
* (assumed extension) dot just after. This means it will also catch *valid*
* names like `aux.foo.bar`, but should be good enough for us! */
if ((iname_len == len) || (lower_fname[iname_len] == '.')) {
*fname = '_';
changed = true;
@ -311,14 +369,14 @@ bool BLI_filename_make_safe(char *fname)
bool BLI_path_make_safe(char *path)
{
/* Simply apply #BLI_filename_make_safe() over each component of the path.
* Luckily enough, same 'safe' rules applies to file & directory names. */
* Luckily enough, same *safe* rules applies to file & directory names. */
char *curr_slash, *curr_path = path;
bool changed = false;
bool skip_first = false;
#ifdef WIN32
if (BLI_path_is_abs(path)) {
/* Do not make safe 'C:' in 'C:\foo\bar'... */
if (BLI_path_is_abs_win32(path)) {
/* Do not make safe `C:` in `C:\foo\bar`. */
skip_first = true;
}
#endif
@ -361,7 +419,7 @@ static int BLI_path_unc_prefix_len(const char *path)
{
if (BLI_path_is_unc(path)) {
if ((path[2] == '?') && (path[3] == '\\')) {
/* we assume long UNC path like \\?\server\share\folder etc... */
/* We assume long UNC path like `\\?\server\share\folder` etc. */
return 4;
}
@ -374,10 +432,14 @@ static int BLI_path_unc_prefix_len(const char *path)
#if defined(WIN32)
/**
* Return true if the path is absolute ie starts with a drive specifier
* (eg A:\) or is a UNC path.
* Return true if the path is an absolute path on a WIN32 file-system, it either:
* - Starts with a drive specifier* (eg `A:\`).
* - Is a UNC path.
*
* \note Not to be confused with the opposite of #BLI_path_is_rel which checks for the
* Blender specific convention of using `//` prefix for blend-file relative paths.
*/
static bool BLI_path_is_abs(const char *name)
static bool BLI_path_is_abs_win32(const char *name)
{
return (name[1] == ':' && ELEM(name[2], '\\', '/')) || BLI_path_is_unc(name);
}
@ -409,9 +471,10 @@ static void BLI_path_unc_to_short(wchar_t *unc)
wchar_t tmp[PATH_MAX];
int len = wcslen(unc);
/* convert:
* \\?\UNC\server\share\folder\... to \\server\share\folder\...
* \\?\C:\ to C:\ and \\?\C:\folder\... to C:\folder\...
/* Convert:
* - `\\?\UNC\server\share\folder\...` to `\\server\share\folder\...`
* - `\\?\C:\` to `C:\`
* - `\\?\C:\folder\...` to `C:\folder\...`
*/
if ((len > 3) && (unc[0] == L'\\') && (unc[1] == L'\\') && (unc[2] == L'?') &&
ELEM(unc[3], L'\\', L'/')) {
@ -450,18 +513,18 @@ void BLI_path_rel(char *file, const char *relfile)
char temp[FILE_MAX];
char res[FILE_MAX];
/* if file is already relative, bail out */
/* If file is already relative, bail out. */
if (BLI_path_is_rel(file)) {
return;
}
/* also bail out if relative path is not set */
/* Also bail out if relative path is not set. */
if (relfile[0] == '\0') {
return;
}
#ifdef WIN32
if (BLI_strnlen(relfile, 3) > 2 && !BLI_path_is_abs(relfile)) {
if (BLI_strnlen(relfile, 3) > 2 && !BLI_path_is_abs_win32(relfile)) {
char *ptemp;
/* Fix missing volume name in relative base,
* can happen with old `recent-files.txt` files. */
@ -479,12 +542,12 @@ void BLI_path_rel(char *file, const char *relfile)
if (BLI_strnlen(file, 3) > 2) {
bool is_unc = BLI_path_is_unc(file);
/* Ensure paths are both UNC paths or are both drives */
/* Ensure paths are both UNC paths or are both drives. */
if (BLI_path_is_unc(temp) != is_unc) {
return;
}
/* Ensure both UNC paths are on the same share */
/* Ensure both UNC paths are on the same share. */
if (is_unc) {
int off;
int slash = 0;
@ -509,16 +572,16 @@ void BLI_path_rel(char *file, const char *relfile)
BLI_str_replace_char(temp + BLI_path_unc_prefix_len(temp), '\\', '/');
BLI_str_replace_char(file + BLI_path_unc_prefix_len(file), '\\', '/');
/* remove /./ which confuse the following slash counting... */
BLI_path_normalize(NULL, file);
BLI_path_normalize(NULL, temp);
/* Remove `/./` which confuse the following slash counting. */
BLI_path_normalize(file);
BLI_path_normalize(temp);
/* the last slash in the file indicates where the path part ends */
/* The last slash in the file indicates where the path part ends. */
lslash = BLI_path_slash_rfind(temp);
if (lslash) {
/* find the prefix of the filename that is equal for both filenames.
* This is replaced by the two slashes at the beginning */
/* Find the prefix of the filename that is equal for both filenames.
* This is replaced by the two slashes at the beginning. */
const char *p = temp;
const char *q = file;
char *r = res;
@ -532,16 +595,14 @@ void BLI_path_rel(char *file, const char *relfile)
p++;
q++;
/* don't search beyond the end of the string
* in the rare case they match */
/* Don't search beyond the end of the string in the rare case they match. */
if ((*p == '\0') || (*q == '\0')) {
break;
}
}
/* we might have passed the slash when the beginning of a dir matches
* so we rewind. Only check on the actual filename
*/
/* We might have passed the slash when the beginning of a dir matches
* so we rewind. Only check on the actual filename. */
if (*q != '/') {
while ((q >= file) && (*q != '/')) {
q--;
@ -557,11 +618,10 @@ void BLI_path_rel(char *file, const char *relfile)
r += BLI_strcpy_rlen(r, "//");
/* p now points to the slash that is at the beginning of the part
/* `p` now points to the slash that is at the beginning of the part
* where the path is different from the relative path.
* We count the number of directories we need to go up in the
* hierarchy to arrive at the common 'prefix' of the path
*/
* hierarchy to arrive at the common prefix of the path. */
if (p < temp) {
p = temp;
}
@ -572,7 +632,7 @@ void BLI_path_rel(char *file, const char *relfile)
p++;
}
/* don't copy the slash at the beginning */
/* Don't copy the slash at the beginning. */
r += BLI_strncpy_rlen(r, q + 1, FILE_MAX - (r - res));
#ifdef WIN32
@ -627,7 +687,7 @@ bool BLI_path_parent_dir(char *path)
return false;
}
if (tail_len == 1) {
/* Last path is ".", as normalize should remove this, it's safe to assume failure.
/* Last path is `.`, as normalize should remove this, it's safe to assume failure.
* This happens when the input a single period (possibly with slashes before or after). */
if (path[tail_ofs] == '.') {
return false;
@ -646,7 +706,7 @@ bool BLI_path_parent_dir_until_exists(char *dir)
/* Loop as long as cur path is not a dir, and we can get a parent path. */
while ((BLI_access(dir, R_OK) != 0) && (valid_path = BLI_path_parent_dir(dir))) {
/* pass */
/* Pass. */
}
return (valid_path && dir[0]);
}
@ -659,11 +719,11 @@ bool BLI_path_parent_dir_until_exists(char *dir)
static bool stringframe_chars(const char *path, int *char_start, int *char_end)
{
uint ch_sta, ch_end, i;
/* Insert current frame: file### -> file001 */
/* Insert current frame: `file###` -> `file001`. */
ch_sta = ch_end = 0;
for (i = 0; path[i] != '\0'; i++) {
if (ELEM(path[i], '\\', '/')) {
ch_end = 0; /* this is a directory name, don't use any hashes we found */
ch_end = 0; /* This is a directory name, don't use any hashes we found. */
}
else if (path[i] == '#') {
ch_sta = i;
@ -671,9 +731,9 @@ static bool stringframe_chars(const char *path, int *char_start, int *char_end)
while (path[ch_end] == '#') {
ch_end++;
}
i = ch_end - 1; /* keep searching */
i = ch_end - 1; /* Keep searching. */
/* don't break, there may be a slash after this that invalidates the previous #'s */
/* Don't break, there may be a slash after this that invalidates the previous #'s. */
}
}
@ -718,7 +778,7 @@ bool BLI_path_frame(char *path, int frame, int digits)
ensure_digits(path, digits);
}
if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
if (stringframe_chars(path, &ch_sta, &ch_end)) { /* Warning: `ch_end` is the last # +1. */
char tmp[FILE_MAX];
BLI_snprintf(
tmp, sizeof(tmp), "%.*s%.*d%s", ch_sta, path, ch_end - ch_sta, frame, path + ch_end);
@ -736,7 +796,7 @@ bool BLI_path_frame_range(char *path, int sta, int end, int digits)
ensure_digits(path, digits);
}
if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
if (stringframe_chars(path, &ch_sta, &ch_end)) { /* Warning: `ch_end` is the last # +1. */
char tmp[FILE_MAX];
BLI_snprintf(tmp,
sizeof(tmp),
@ -812,8 +872,8 @@ void BLI_path_frame_strip(char *path, char *r_ext, const size_t ext_maxlen)
bool BLI_path_frame_check_chars(const char *path)
{
int ch_sta, ch_end; /* dummy args */
return stringframe_chars(path, &ch_sta, &ch_end);
int ch_sta_dummy, ch_end_dummy;
return stringframe_chars(path, &ch_sta_dummy, &ch_end_dummy);
}
void BLI_path_to_display_name(char *display_name, int maxlen, const char *name)
@ -860,16 +920,14 @@ bool BLI_path_abs(char *path, const char *basepath)
char base[FILE_MAX];
#ifdef WIN32
/* without this: "" --> "C:\" */
/* Without this, an empty string converts to: `C:\` */
if (*path == '\0') {
return wasrelative;
}
/* we are checking here if we have an absolute path that is not in the current
* blend file as a lib main - we are basically checking for the case that a
* UNIX root '/' is passed.
*/
if (!wasrelative && !BLI_path_is_abs(path)) {
/* We are checking here if we have an absolute path that is not in the current `.blend` file
* as a lib main - we are basically checking for the case that a UNIX root `/` is passed. */
if (!wasrelative && !BLI_path_is_abs_win32(path)) {
char *p = path;
BLI_windows_get_default_root_dir(tmp);
/* Get rid of the slashes at the beginning of the path. */
@ -892,7 +950,7 @@ bool BLI_path_abs(char *path, const char *basepath)
* `C:\foo.JPG` -> `/c/foo.JPG` */
if (isalpha(tmp[0]) && (tmp[1] == ':') && ELEM(tmp[2], '\\', '/')) {
tmp[1] = tolower(tmp[0]); /* Replace ':' with drive-letter. */
tmp[1] = tolower(tmp[0]); /* Replace `:` with drive-letter. */
tmp[0] = '/';
/* `\` the slash will be converted later. */
}
@ -916,28 +974,28 @@ bool BLI_path_abs(char *path, const char *basepath)
const char *lslash;
BLI_strncpy(base, basepath, sizeof(base));
/* file component is ignored, so don't bother with the trailing slash */
BLI_path_normalize(NULL, base);
/* File component is ignored, so don't bother with the trailing slash. */
BLI_path_normalize(base);
lslash = BLI_path_slash_rfind(base);
BLI_str_replace_char(base + BLI_path_unc_prefix_len(base), '\\', '/');
if (lslash) {
/* length up to and including last "/" */
/* Length up to and including last `/`. */
const int baselen = (int)(lslash - base) + 1;
/* use path for temp storage here, we copy back over it right away */
BLI_strncpy(path, tmp + 2, FILE_MAX); /* strip "//" */
/* Use path for temp storage here, we copy back over it right away. */
BLI_strncpy(path, tmp + 2, FILE_MAX); /* Strip `//` prefix. */
memcpy(tmp, base, baselen); /* prefix with base up to last "/" */
BLI_strncpy(tmp + baselen, path, sizeof(tmp) - baselen); /* append path after "//" */
BLI_strncpy(path, tmp, FILE_MAX); /* return as result */
memcpy(tmp, base, baselen); /* Prefix with base up to last `/`. */
BLI_strncpy(tmp + baselen, path, sizeof(tmp) - baselen); /* Append path after `//`. */
BLI_strncpy(path, tmp, FILE_MAX); /* Return as result. */
}
else {
/* base doesn't seem to be a directory--ignore it and just strip "//" prefix on path */
/* Base doesn't seem to be a directory, ignore it and just strip `//` prefix on path. */
BLI_strncpy(path, tmp + 2, FILE_MAX);
}
}
else {
/* base ignored */
/* Base ignored. */
BLI_strncpy(path, tmp, FILE_MAX);
}
@ -948,8 +1006,8 @@ bool BLI_path_abs(char *path, const char *basepath)
BLI_str_replace_char(path + 2, '/', '\\');
#endif
/* ensure this is after correcting for path switch */
BLI_path_normalize(NULL, path);
/* Ensure this is after correcting for path switch. */
BLI_path_normalize(path);
return wasrelative;
}
@ -960,7 +1018,7 @@ bool BLI_path_is_abs_from_cwd(const char *path)
const int path_len_clamp = BLI_strnlen(path, 3);
#ifdef WIN32
if ((path_len_clamp >= 3 && BLI_path_is_abs(path)) || BLI_path_is_unc(path)) {
if ((path_len_clamp >= 3 && BLI_path_is_abs_win32(path)) || BLI_path_is_unc(path)) {
is_abs = true;
}
#else
@ -979,7 +1037,7 @@ bool BLI_path_abs_from_cwd(char *path, const size_t maxlen)
if (!BLI_path_is_abs_from_cwd(path)) {
char cwd[FILE_MAX];
/* in case the full path to the blend isn't used */
/* In case the full path to the blend isn't used. */
if (BLI_current_working_dir(cwd, sizeof(cwd))) {
char origpath[FILE_MAX];
BLI_strncpy(origpath, path, FILE_MAX);
@ -996,7 +1054,7 @@ bool BLI_path_abs_from_cwd(char *path, const size_t maxlen)
#ifdef _WIN32
/**
* Tries appending each of the semicolon-separated extensions in the PATHEXT
* Tries appending each of the semicolon-separated extensions in the `PATHEXT`
* environment variable (Windows-only) onto `name` in turn until such a file is found.
* Returns success/failure.
*/
@ -1007,7 +1065,7 @@ bool BLI_path_program_extensions_add_win32(char *name, const size_t maxlen)
type = BLI_exists(name);
if ((type == 0) || S_ISDIR(type)) {
/* typically 3-5, ".EXE", ".BAT"... etc */
/* Typically 3-5, ".EXE", ".BAT"... etc. */
const int ext_max = 12;
const char *ext = BLI_getenv("PATHEXT");
if (ext) {
@ -1016,7 +1074,7 @@ bool BLI_path_program_extensions_add_win32(char *name, const size_t maxlen)
char *filename_ext;
const char *ext_next;
/* null terminated in the loop */
/* Null terminated in the loop. */
memcpy(filename, name, name_len);
filename_ext = filename + name_len;
@ -1101,11 +1159,9 @@ bool BLI_path_program_search(char *fullname, const size_t maxlen, const char *na
void BLI_setenv(const char *env, const char *val)
{
/* free windows */
#if (defined(_WIN32) || defined(_WIN64))
/* MS-Windows. */
uputenv(env, val);
#else
/* Linux/macOS/BSD */
if (val) {
@ -1158,7 +1214,7 @@ bool BLI_make_existing_file(const char *name)
char di[FILE_MAX];
BLI_split_dir_part(name, di, sizeof(di));
/* make if the dir doesn't exist */
/* Make if the dir doesn't exist. */
return BLI_dir_create_recursive(di);
}
@ -1260,7 +1316,7 @@ bool BLI_path_extension_glob_validate(char *ext_fnmatch)
/* Non-wildcard char, we can break here and consider the pattern valid. */
return false;
}
/* So far, only wildcards in last group of the pattern... */
/* So far, only wildcards in last group of the pattern. */
only_wildcards = true;
}
/* Only one group in the pattern, so even if its only made of wildcard(s),
@ -1352,7 +1408,7 @@ void BLI_split_dirfile(
if (dir) {
if (lslash) {
/* +1 to include the slash and the last char */
/* +1 to include the slash and the last char. */
BLI_strncpy(dir, string, MIN2(dirlen, lslash + 1));
}
else {
@ -1429,7 +1485,7 @@ size_t BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__
}
if (dirlen >= maxlen) {
return dirlen; /* fills the path */
return dirlen; /* Fills the path. */
}
return dirlen + BLI_strncpy_rlen(dst + dirlen, file, maxlen - dirlen);
@ -1470,7 +1526,7 @@ size_t BLI_path_join_array(char *__restrict dst,
}
#ifdef WIN32
/* Special case "//" for relative paths, don't use separator #SEP
/* Special case `//` for relative paths, don't use separator #SEP
* as this has a special meaning on both WIN32 & UNIX.
* Without this check joining `"//", "path"`. results in `"//\path"`. */
if (ofs != 0) {
@ -1519,7 +1575,7 @@ size_t BLI_path_join_array(char *__restrict dst,
}
if (len != 0) {
/* the very first path may have a slash at the end */
/* The very first path may have a slash at the end. */
if (ofs && !BLI_path_slash_is_native_compat(dst[ofs - 1])) {
dst[ofs++] = SEP;
if (ofs == dst_last) {
@ -1574,7 +1630,7 @@ static bool path_name_at_index_forward(const char *__restrict path,
if ((c == '\0') || BLI_path_slash_is_native_compat(c)) {
if (prev + 1 != i) {
prev += 1;
/* Skip '/./' (behave as if they don't exist). */
/* Skip `/./` (behave as if they don't exist). */
if (!((i - prev == 1) && (prev != 0) && (path[prev] == '.'))) {
if (index_step == index) {
*r_offset = prev;
@ -1609,7 +1665,7 @@ static bool path_name_at_index_backward(const char *__restrict path,
if ((c == '\0') || BLI_path_slash_is_native_compat(c)) {
if (prev - 1 != i) {
i += 1;
/* Skip '/./' (behave as if they don't exist). */
/* Skip `/./` (behave as if they don't exist). */
if (!((prev - i == 1) && (i != 0) && (path[i] == '.'))) {
if (index_step == index) {
*r_offset = i;
@ -1644,15 +1700,15 @@ bool BLI_path_contains(const char *container_path, const char *containee_path)
char containee_native[PATH_MAX];
/* Keep space for a trailing slash. If the path is truncated by this, the containee path is
* longer than PATH_MAX and the result is ill-defined. */
* longer than #PATH_MAX and the result is ill-defined. */
BLI_strncpy(container_native, container_path, PATH_MAX - 1);
BLI_strncpy(containee_native, containee_path, PATH_MAX);
BLI_path_slash_native(container_native);
BLI_path_slash_native(containee_native);
BLI_path_normalize(NULL, container_native);
BLI_path_normalize(NULL, containee_native);
BLI_path_normalize(container_native);
BLI_path_normalize(containee_native);
#ifdef WIN32
BLI_str_tolower_ascii(container_native, PATH_MAX);
@ -1755,8 +1811,8 @@ int BLI_path_cmp_normalized(const char *p1, const char *p2)
BLI_path_slash_native(norm_p1);
BLI_path_slash_native(norm_p2);
BLI_path_normalize(NULL, norm_p1);
BLI_path_normalize(NULL, norm_p2);
BLI_path_normalize(norm_p1);
BLI_path_normalize(norm_p2);
return BLI_path_cmp(norm_p1, norm_p2);
}

View File

@ -413,7 +413,9 @@ char32_t BLI_str_utf32_char_to_upper(const char32_t wc)
{
if (wc < U'\xFF') { /* Latin. */
if ((wc <= U'z' && wc >= U'a') || (wc <= U'\xF6' && wc >= U'\xE0') ||
(wc <= U'\xFE' && wc >= U'\xF8')) {
/* Correct but the first case is know, only check the second */
// (wc <= U'\xFE' && wc >= U'\xF8')
(wc >= U'\xF8')) {
return wc - 32;
}
return wc;

View File

@ -42,29 +42,41 @@ static char *str_replace_char_strdup(const char *str, char src, char dst)
/** \name Tests for: #BLI_path_normalize
* \{ */
#define NORMALIZE_WITH_BASEDIR(input, input_base, output_expect) \
#define NORMALIZE(input, output_expect) \
{ \
char path[FILE_MAX] = input; \
const char *input_base_test = input_base; \
if (SEP == '\\') { \
str_replace_char_with_relative_exception(path, '/', '\\'); \
input_base_test = str_replace_char_strdup(input_base_test, '/', '\\'); \
} \
BLI_path_normalize(input_base_test, path); \
BLI_path_normalize(path); \
if (SEP == '\\') { \
BLI_str_replace_char(path, '\\', '/'); \
if (input_base_test) { \
free((void *)input_base_test); \
} \
} \
EXPECT_STREQ(path, output_expect); \
} \
((void)0)
#define NORMALIZE(input, output_expect) NORMALIZE_WITH_BASEDIR(input, nullptr, output_expect)
/* #BLI_path_normalize: do nothing. */
TEST(path_util, Normalize_Nop)
{
NORMALIZE(".", ".");
NORMALIZE("./", "./");
NORMALIZE("/", "/");
NORMALIZE("//", "//");
NORMALIZE("//a", "//a");
}
TEST(path_util, Normalize_NopRelative)
{
NORMALIZE("..", "..");
NORMALIZE("../", "../");
NORMALIZE("../", "../");
NORMALIZE("../..", "../..");
NORMALIZE("../../", "../../");
}
/* #BLI_path_normalize: "/./" -> "/" */
TEST(path_util, Clean_Dot)
TEST(path_util, Normalize_Dot)
{
NORMALIZE("/./", "/");
NORMALIZE("/a/./b/./c/./", "/a/b/c/");
@ -72,28 +84,68 @@ TEST(path_util, Clean_Dot)
NORMALIZE("/a/./././b/", "/a/b/");
}
/* #BLI_path_normalize: complex "/./" -> "/", "//" -> "/", "./path/../" -> "./". */
TEST(path_util, Clean_Complex)
TEST(path_util, Normalize_Complex)
{
NORMALIZE("/a/./b/./c/./.././.././", "/a/");
NORMALIZE("/a//.//b//.//c//.//..//.//..//.//", "/a/");
}
/* #BLI_path_normalize: "//" -> "/" */
TEST(path_util, Clean_DoubleSlash)
TEST(path_util, Normalize_DoubleSlash)
{
NORMALIZE("//", "//"); /* Exception, double forward slash. */
NORMALIZE(".//", "./");
NORMALIZE("a////", "a/");
NORMALIZE("./a////", "./a/");
NORMALIZE("./a////", "a/");
}
/* #BLI_path_normalize: "foo/bar/../" -> "foo/" */
TEST(path_util, Clean_Parent)
TEST(path_util, Normalize_Parent)
{
NORMALIZE("/a/b/c/../../../", "/");
NORMALIZE("/a/../a/b/../b/c/../c/", "/a/b/c/");
NORMALIZE_WITH_BASEDIR("//../", "/a/b/c/", "/a/b/");
}
/* #BLI_path_normalize: with too many "/../", match Python's behavior. */
TEST(path_util, Normalize_UnbalancedAbsolute)
{
NORMALIZE("/../", "/");
NORMALIZE("/../a", "/a");
NORMALIZE("/a/b/c/../../../../../d", "/d");
NORMALIZE("/a/b/c/../../../../d", "/d");
NORMALIZE("/a/b/c/../../../d", "/d");
}
/* #BLI_path_normalize: with relative paths that result in leading "../". */
TEST(path_util, Normalize_UnbalancedRelative)
{
NORMALIZE("./a/b/c/../../../", ".");
NORMALIZE("a/b/c/../../../", ".");
NORMALIZE("//a/b/c/../../../", "//");
NORMALIZE("./a/../../../", "../../");
NORMALIZE("a/../../../", "../../");
NORMALIZE("///a/../../../", "//../../");
NORMALIZE("//./a/../../../", "//../../");
NORMALIZE("../a/../../../", "../../../");
NORMALIZE("a/b/../c/../../d/../../../e/../../../../f", "../../../../../f");
NORMALIZE(".../.../a/.../b/../c/../../d/../../../e/../../../.../../f", "../f");
}
TEST(path_util, Normalize_UnbalancedRelativeTrailing)
{
NORMALIZE("./a/b/c/../../..", ".");
NORMALIZE("a/b/c/../../..", ".");
NORMALIZE("//a/b/c/../../..", "//");
NORMALIZE("./a/../../..", "../..");
NORMALIZE("a/../../..", "../..");
NORMALIZE("///a/../../..", "//../..");
NORMALIZE("//./a/../../..", "//../..");
NORMALIZE("../a/../../..", "../../..");
}
#undef NORMALIZE_WITH_BASEDIR
#undef NORMALIZE
/** \} */

View File

@ -516,7 +516,8 @@ static Main *blo_find_main(FileData *fd, const char *filepath, const char *relab
char name1[FILE_MAX];
BLI_strncpy(name1, filepath, sizeof(name1));
BLI_path_normalize(relabase, name1);
BLI_path_abs(name1, relabase);
BLI_path_normalize(name1);
// printf("blo_find_main: relabase %s\n", relabase);
// printf("blo_find_main: original in %s\n", filepath);
@ -2695,7 +2696,8 @@ static void direct_link_library(FileData *fd, Library *lib, Main *main)
/* Make sure we have full path in lib->filepath_abs */
BLI_strncpy(lib->filepath_abs, lib->filepath, sizeof(lib->filepath));
BLI_path_normalize(fd->relabase, lib->filepath_abs);
BLI_path_abs(lib->filepath_abs, fd->relabase);
BLI_path_normalize(lib->filepath_abs);
// printf("direct_link_library: filepath %s\n", lib->filepath);
// printf("direct_link_library: filepath_abs %s\n", lib->filepath_abs);

View File

@ -1474,13 +1474,13 @@ bool BLO_write_file(Main *mainvar,
/* Normalize the paths in case there is some subtle difference (so they can be compared). */
if (relbase_valid) {
BLI_split_dir_part(mainvar->filepath, dir_src, sizeof(dir_src));
BLI_path_normalize(nullptr, dir_src);
BLI_path_normalize(dir_src);
}
else {
dir_src[0] = '\0';
}
BLI_split_dir_part(filepath, dir_dst, sizeof(dir_dst));
BLI_path_normalize(nullptr, dir_dst);
BLI_path_normalize(dir_dst);
/* Only for relative, not relative-all, as this means making existing paths relative. */
if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) {

View File

@ -81,11 +81,8 @@ BLI_INLINE void bmesh_calc_tessellation_for_face_impl(BMLoop *(*looptris)[3],
efa->no, l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co);
}
if (UNLIKELY(is_quad_flip_v3_first_third_fast_with_normal(l_ptr_a[0]->v->co,
l_ptr_a[1]->v->co,
l_ptr_a[2]->v->co,
l_ptr_b[2]->v->co,
efa->no))) {
if (UNLIKELY(is_quad_flip_v3_first_third_fast(
l_ptr_a[0]->v->co, l_ptr_a[1]->v->co, l_ptr_a[2]->v->co, l_ptr_b[2]->v->co))) {
/* Flip out of degenerate 0-2 state. */
l_ptr_a[2] = l_ptr_b[2];
l_ptr_b[0] = l_ptr_a[1];

View File

@ -19,6 +19,8 @@
#include "DNA_world_types.h"
#include "GPU_context.h"
#include "IMB_imbuf.h"
#include "eevee_private.h"
@ -348,6 +350,16 @@ static void eevee_draw_scene(void *vedata)
EEVEE_temporal_sampling_reset(vedata);
stl->effects->ssr_was_valid_double_buffer = stl->g_data->valid_double_buffer;
}
/* Perform render step between samples to allow flushing of freed temporary GPUBackend
* resources. This prevents the GPU backend accumulating a high amount of in-flight memory when
* performing renders using eevee_draw_scene. e.g. During file thumbnail generation. */
if (loop_len > 2) {
if (GPU_backend_get_type() == GPU_BACKEND_METAL) {
GPU_flush();
GPU_render_step();
}
}
}
if ((stl->g_data->render_passes & EEVEE_RENDER_PASS_COMBINED) != 0) {

View File

@ -166,7 +166,8 @@ void DepthOfField::sync()
/* Now that we know the maximum render resolution of every view, using depth of field, allocate
* the reduced buffers. Color needs to be signed format here. See note in shader for
* explanation. Do not use texture pool because of needs mipmaps. */
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT |
GPU_TEXTURE_USAGE_MIP_SWIZZLE_VIEW;
reduced_color_tx_.ensure_2d(GPU_RGBA16F, reduce_size, usage, nullptr, DOF_MIP_COUNT);
reduced_coc_tx_.ensure_2d(GPU_R16F, reduce_size, usage, nullptr, DOF_MIP_COUNT);
reduced_color_tx_.ensure_mip_views();

View File

@ -24,7 +24,8 @@ void HiZBuffer::sync()
int2 hiz_extent = math::ceil_to_multiple(render_extent, int2(1u << (HIZ_MIP_COUNT - 1)));
int2 dispatch_size = math::divide_ceil(hiz_extent, int2(HIZ_GROUP_SIZE));
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE;
eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE |
GPU_TEXTURE_USAGE_MIP_SWIZZLE_VIEW;
hiz_tx_.ensure_2d(GPU_R32F, hiz_extent, usage, nullptr, HIZ_MIP_COUNT);
hiz_tx_.ensure_mip_views();
GPU_texture_mipmap_mode(hiz_tx_, true, false);

View File

@ -628,7 +628,7 @@ struct LightData {
float radius_squared;
/** NOTE: It is ok to use float3 here. A float is declared right after it.
* float3 is also aligned to 16 bytes. */
float3 color;
packed_float3 color;
/** Light Type. */
eLightType type;
/** Spot size. Aligned to size of float2. */

View File

@ -257,7 +257,8 @@ class ShadowModule {
/** Tile to physical page mapping. This is an array texture with one layer per view. */
Texture render_map_tx_ = {"ShadowRenderMap",
GPU_R32UI,
GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE,
GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE |
GPU_TEXTURE_USAGE_MIP_SWIZZLE_VIEW,
int2(SHADOW_TILEMAP_RES),
64,
nullptr,

View File

@ -12,7 +12,7 @@ void main()
/* Display backfacing surfels with a transparent checkerboard grid. */
if (!gl_FrontFacing) {
ivec2 grid_uv = ivec2(gl_FragCoord) / 5;
ivec2 grid_uv = ivec2(gl_FragCoord.xy) / 5;
if ((grid_uv.x + grid_uv.y) % 2 == 0) {
discard;
return;

View File

@ -45,7 +45,8 @@ void main()
case LIGHT_RECT:
case LIGHT_ELLIPSE:
case LIGHT_POINT:
sphere = Sphere(light._position, light.influence_radius_max);
sphere.center = light._position;
sphere.radius = light.influence_radius_max;
break;
}

View File

@ -110,7 +110,7 @@ bool debug_tilemaps(vec3 P, LightData light)
out_color_add = vec4(debug_tile_state_color(tile), 0.0);
out_color_mul = vec4(0.0);
if (ivec2(gl_FragCoord.xy) == ivec2(0)) {
if (all(equal(ivec2(gl_FragCoord.xy), ivec2(0)))) {
drw_print(light.object_mat);
}
return true;

View File

@ -25,7 +25,9 @@
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
/* TODO(@fclem): Implement. */
#define assert(check)
#ifndef GPU_METAL
# define assert(check)
#endif
/* Remove page ownership from the tile and append it to the cache. */
void shadow_page_free(inout ShadowTileData tile)

View File

@ -45,7 +45,7 @@ float pixel_size_at(float linear_depth)
if (is_persp) {
pixel_size *= max(0.01, linear_depth);
}
return pixel_size * exp2(fb_lod);
return pixel_size * exp2(float(fb_lod));
}
void step_bounding_sphere(vec3 vs_near_plane,
@ -117,6 +117,7 @@ void main()
step_bounding_sphere(vs_near_plane, vs_view_direction, t, t + step_size, P, step_radius);
vec3 vP = point_world_to_view(P);
shadow_tag_usage(vP, P, ws_view_direction, step_radius, t, gl_FragCoord.xy * exp2(fb_lod));
shadow_tag_usage(
vP, P, ws_view_direction, step_radius, t, gl_FragCoord.xy * exp2(float(fb_lod)));
}
}

View File

@ -17,7 +17,7 @@ void inflate_bounds(vec3 ls_center, inout vec3 P, inout vec3 lP)
{
vec3 vP = point_world_to_view(P);
float inflate_scale = pixel_world_radius * exp2(fb_lod);
float inflate_scale = pixel_world_radius * exp2(float(fb_lod));
bool is_persp = (ProjectionMatrix[3][3] == 0.0);
if (is_persp) {
inflate_scale *= -vP.z;

View File

@ -45,6 +45,8 @@ void write_depth(ivec2 texel_co, const int lod, ivec2 tile_co, float depth)
/* Quantization bias. Equivalent to nextafter in C without all the safety. 1 is not enough. */
u_depth += 2;
/* TOOD(Metal): For Metal, textures will need to be viewed as buffers to workaround missing image
* atomics support. */
imageAtomicMin(shadow_atlas_img, out_texel, u_depth);
}

View File

@ -8,6 +8,11 @@
/** \name Shadow pipeline
* \{ */
/* NOTE(Metal): As this is implemented using a fundamental data type, this needs to be specified
* explicitly as uint for code generation, as the MSLShaderGenerator needs to be able to
* distinguish between classes and fundamental types during code generation. */
#define SHADOW_TILE_DATA_PACKED "uint"
GPU_SHADER_CREATE_INFO(eevee_shadow_clipmap_clear)
.do_static_compilation(true)
.local_group_size(SHADOW_CLIPMAP_GROUP_SIZE)
@ -34,7 +39,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tilemap_init)
.do_static_compilation(true)
.local_group_size(SHADOW_TILEMAP_RES, SHADOW_TILEMAP_RES)
.storage_buf(0, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
.storage_buf(1, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
.storage_buf(1, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
.storage_buf(2, Qualifier::READ_WRITE, "ShadowTileMapClip", "tilemaps_clip_buf[]")
.storage_buf(4, Qualifier::READ_WRITE, "uvec2", "pages_cached_buf[]")
.additional_info("eevee_shared")
@ -44,7 +49,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_update)
.do_static_compilation(true)
.local_group_size(1, 1, 1)
.storage_buf(0, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
.storage_buf(1, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
.storage_buf(1, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
.storage_buf(5, Qualifier::READ, "ObjectBounds", "bounds_buf[]")
.storage_buf(6, Qualifier::READ, "uint", "resource_ids_buf[]")
.additional_info("eevee_shared", "draw_view", "draw_view_culling")
@ -55,7 +60,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_opaque)
.local_group_size(SHADOW_DEPTH_SCAN_GROUP_SIZE, SHADOW_DEPTH_SCAN_GROUP_SIZE)
.sampler(0, ImageType::DEPTH_2D, "depth_tx")
.storage_buf(5, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
.storage_buf(6, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
.storage_buf(6, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
.push_constant(Type::FLOAT, "tilemap_projection_ratio")
.additional_info("eevee_shared", "draw_view", "draw_view_culling", "eevee_light_data")
.compute_source("eevee_shadow_tag_usage_comp.glsl");
@ -71,7 +76,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tag_usage_transparent)
.vertex_in(0, Type::VEC3, "pos")
.storage_buf(4, Qualifier::READ, "ObjectBounds", "bounds_buf[]")
.storage_buf(5, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
.storage_buf(6, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
.storage_buf(6, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
.push_constant(Type::FLOAT, "tilemap_projection_ratio")
.push_constant(Type::FLOAT, "pixel_world_radius")
.push_constant(Type::IVEC2, "fb_resolution")
@ -91,7 +96,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_page_mask)
.do_static_compilation(true)
.local_group_size(SHADOW_TILEMAP_RES, SHADOW_TILEMAP_RES)
.storage_buf(0, Qualifier::READ, "ShadowTileMapData", "tilemaps_buf[]")
.storage_buf(1, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
.storage_buf(1, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
.additional_info("eevee_shared")
.compute_source("eevee_shadow_page_mask_comp.glsl");
@ -99,7 +104,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_page_free)
.do_static_compilation(true)
.local_group_size(SHADOW_TILEMAP_LOD0_LEN)
.storage_buf(0, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
.storage_buf(1, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
.storage_buf(1, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
.storage_buf(2, Qualifier::READ_WRITE, "ShadowPagesInfoData", "pages_infos_buf")
.storage_buf(3, Qualifier::READ_WRITE, "uint", "pages_free_buf[]")
.storage_buf(4, Qualifier::READ_WRITE, "uvec2", "pages_cached_buf[]")
@ -110,7 +115,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_page_defrag)
.do_static_compilation(true)
.local_group_size(1)
.typedef_source("draw_shader_shared.h")
.storage_buf(1, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
.storage_buf(1, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
.storage_buf(2, Qualifier::READ_WRITE, "ShadowPagesInfoData", "pages_infos_buf")
.storage_buf(3, Qualifier::READ_WRITE, "uint", "pages_free_buf[]")
.storage_buf(4, Qualifier::READ_WRITE, "uvec2", "pages_cached_buf[]")
@ -124,7 +129,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_page_allocate)
.local_group_size(SHADOW_TILEMAP_LOD0_LEN)
.typedef_source("draw_shader_shared.h")
.storage_buf(0, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
.storage_buf(1, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
.storage_buf(1, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
.storage_buf(2, Qualifier::READ_WRITE, "ShadowPagesInfoData", "pages_infos_buf")
.storage_buf(3, Qualifier::READ_WRITE, "uint", "pages_free_buf[]")
.storage_buf(4, Qualifier::READ_WRITE, "uvec2", "pages_cached_buf[]")
@ -137,7 +142,7 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_tilemap_finalize)
.typedef_source("draw_shader_shared.h")
.local_group_size(SHADOW_TILEMAP_RES, SHADOW_TILEMAP_RES)
.storage_buf(0, Qualifier::READ_WRITE, "ShadowTileMapData", "tilemaps_buf[]")
.storage_buf(1, Qualifier::READ_WRITE, "ShadowTileDataPacked", "tiles_buf[]")
.storage_buf(1, Qualifier::READ_WRITE, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
.storage_buf(2, Qualifier::READ_WRITE, "ShadowPagesInfoData", "pages_infos_buf")
.storage_buf(3, Qualifier::WRITE, "ViewMatrices", "view_infos_buf[64]")
.storage_buf(4, Qualifier::READ_WRITE, "ShadowStatistics", "statistics_buf")
@ -183,11 +188,12 @@ GPU_SHADER_CREATE_INFO(eevee_shadow_debug)
.do_static_compilation(true)
.additional_info("eevee_shared")
.storage_buf(5, Qualifier::READ, "ShadowTileMapData", "tilemaps_buf[]")
.storage_buf(6, Qualifier::READ, "ShadowTileDataPacked", "tiles_buf[]")
.storage_buf(6, Qualifier::READ, SHADOW_TILE_DATA_PACKED, "tiles_buf[]")
.fragment_out(0, Type::VEC4, "out_color_add", DualBlend::SRC_0)
.fragment_out(0, Type::VEC4, "out_color_mul", DualBlend::SRC_1)
.push_constant(Type::INT, "debug_mode")
.push_constant(Type::INT, "debug_tilemap_index")
.depth_write(DepthWrite::ANY)
.fragment_source("eevee_shadow_debug_frag.glsl")
.additional_info(
"draw_fullscreen", "draw_view", "eevee_hiz_data", "eevee_light_data", "eevee_shadow_data");

View File

@ -30,46 +30,32 @@
/** \name Update Loose Geometry
* \{ */
static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr, MeshBufferCache *cache)
static void extract_set_bits(const blender::BitSpan bits, blender::MutableSpan<int> indices)
{
using namespace blender;
BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__);
const bke::LooseEdgeCache &loose_edges = mr->me->loose_edges();
if (loose_edges.count > 0) {
cache->loose_geom.edges.reinitialize(loose_edges.count);
int count = 0;
for (const int64_t i : loose_edges.is_loose_bits.index_range()) {
if (loose_edges.is_loose_bits[i]) {
cache->loose_geom.edges[count] = int(i);
count++;
}
}
}
/* Tag verts as not loose. */
for (const int2 &edge : mr->edges) {
BLI_BITMAP_ENABLE(lvert_map, edge[0]);
BLI_BITMAP_ENABLE(lvert_map, edge[1]);
}
int count = 0;
Array<int> loose_verts(mr->vert_len);
for (int v = 0; v < mr->vert_len; v++) {
if (!BLI_BITMAP_TEST(lvert_map, v)) {
loose_verts[count] = v;
for (const int64_t i : bits.index_range()) {
if (bits[i]) {
indices[count] = int(i);
count++;
}
}
if (count < mr->vert_len) {
cache->loose_geom.verts = loose_verts.as_span().take_front(count);
}
else {
cache->loose_geom.verts = std::move(loose_verts);
BLI_assert(count == indices.size());
}
static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr, MeshBufferCache *cache)
{
using namespace blender;
const bke::LooseEdgeCache &loose_edges = mr->me->loose_edges();
if (loose_edges.count > 0) {
cache->loose_geom.edges.reinitialize(loose_edges.count);
extract_set_bits(loose_edges.is_loose_bits, cache->loose_geom.edges);
}
MEM_freeN(lvert_map);
const bke::LooseVertCache &loose_verts = mr->me->loose_verts();
if (loose_verts.count > 0) {
cache->loose_geom.verts.reinitialize(loose_verts.count);
extract_set_bits(loose_verts.is_loose_bits, cache->loose_geom.verts);
}
}
static void mesh_render_data_loose_verts_bm(const MeshRenderData *mr,

View File

@ -33,7 +33,12 @@
BLI_INLINE eParticleRefineShaderType drw_curves_shader_type_get()
{
if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support()) {
/* NOTE: Curve refine is faster using transform feedback via vertex processing pipeline with
* Metal and Apple Silicon GPUs. This is also because vertex work can more easily be executed in
* parallel with fragment work, whereas compute inserts an explicit dependency,
* due to switching of command encoder types. */
if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support() &&
(GPU_backend_get_type() != GPU_BACKEND_METAL)) {
return PART_REFINE_SHADER_COMPUTE;
}
if (GPU_transform_feedback_support()) {

View File

@ -36,7 +36,12 @@
BLI_INLINE eParticleRefineShaderType drw_hair_shader_type_get()
{
if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support()) {
/* NOTE: Hair refine is faster using transform feedback via vertex processing pipeline with Metal
* and Apple Silicon GPUs. This is also because vertex work can more easily be executed in
* parallel with fragment work, whereas compute inserts an explicit dependency,
* due to switching of command encoder types. */
if (GPU_compute_shader_support() && GPU_shader_storage_buffer_objects_support() &&
(GPU_backend_get_type() != GPU_BACKEND_METAL)) {
return PART_REFINE_SHADER_COMPUTE;
}
if (GPU_transform_feedback_support()) {

View File

@ -350,6 +350,16 @@ struct DRWDebugVert {
};
BLI_STATIC_ASSERT_ALIGN(DRWDebugVert, 16)
inline DRWDebugVert debug_vert_make(uint in_pos0, uint in_pos1, uint in_pos2, uint in_vert_color)
{
DRWDebugVert debug_vert;
debug_vert.pos0 = in_pos0;
debug_vert.pos1 = in_pos1;
debug_vert.pos2 = in_pos2;
debug_vert.vert_color = in_vert_color;
return debug_vert;
}
/* Take the header (DrawCommand) into account. */
#define DRW_DEBUG_DRAW_VERT_MAX (64 * 8192) - 1

View File

@ -34,9 +34,9 @@ uint drw_debug_color_pack(vec4 v_color)
void drw_debug_line(inout uint vertid, vec3 v1, vec3 v2, uint v_color)
{
drw_debug_verts_buf[vertid++] = DRWDebugVert(
drw_debug_verts_buf[vertid++] = debug_vert_make(
floatBitsToUint(v1.x), floatBitsToUint(v1.y), floatBitsToUint(v1.z), v_color);
drw_debug_verts_buf[vertid++] = DRWDebugVert(
drw_debug_verts_buf[vertid++] = debug_vert_make(
floatBitsToUint(v2.x), floatBitsToUint(v2.y), floatBitsToUint(v2.z), v_color);
}

View File

@ -86,6 +86,9 @@ void FONT_OT_text_cut(struct wmOperatorType *ot);
void FONT_OT_text_paste(struct wmOperatorType *ot);
void FONT_OT_text_paste_from_file(struct wmOperatorType *ot);
void FONT_OT_selection_set(struct wmOperatorType *ot);
void FONT_OT_select_word(struct wmOperatorType *ot);
void FONT_OT_move(struct wmOperatorType *ot);
void FONT_OT_move_select(struct wmOperatorType *ot);
void FONT_OT_delete(struct wmOperatorType *ot);

View File

@ -40,6 +40,9 @@ void ED_operatortypes_curve(void)
WM_operatortype_append(FONT_OT_text_paste);
WM_operatortype_append(FONT_OT_text_paste_from_file);
WM_operatortype_append(FONT_OT_selection_set);
WM_operatortype_append(FONT_OT_select_word);
WM_operatortype_append(FONT_OT_move);
WM_operatortype_append(FONT_OT_move_select);
WM_operatortype_append(FONT_OT_delete);

View File

@ -1805,6 +1805,142 @@ void FONT_OT_text_insert(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Font Selection Operator
* \{ */
static int font_cursor_text_index_from_event(bContext *C, Object *obedit, const wmEvent *event)
{
Curve *cu = obedit->data;
/* Calculate a plane from the text object's orientation. */
float plane[4];
plane_from_point_normal_v3(plane, obedit->object_to_world[3], obedit->object_to_world[2]);
/* Convert Mouse location in region to 3D location in world space. */
float mal_fl[2] = {(float)event->mval[0], (float)event->mval[1]};
float mouse_loc[3];
ED_view3d_win_to_3d_on_plane(CTX_wm_region(C), plane, mal_fl, true, mouse_loc);
/* Convert to object space and scale by font size. */
mul_m4_v3(obedit->world_to_object, mouse_loc);
float curs_loc[2] = {mouse_loc[0] / cu->fsize, mouse_loc[1] / cu->fsize};
return BKE_vfont_cursor_to_text_index(obedit, curs_loc);
}
static void font_cursor_set_apply(bContext *C, const wmEvent *event)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *ob = DEG_get_evaluated_object(depsgraph, CTX_data_active_object(C));
Curve *cu = ob->data;
EditFont *ef = cu->editfont;
BLI_assert(ef->len >= 0);
const int string_offset = font_cursor_text_index_from_event(C, ob, event);
if (string_offset > ef->len || string_offset < 0) {
return;
}
cu->curinfo = ef->textbufinfo[ef->pos ? ef->pos - 1 : 0];
if (ob->totcol > 0) {
ob->actcol = cu->curinfo.mat_nr;
if (ob->actcol < 1) {
ob->actcol = 1;
}
}
if (!ef->selboxes && (ef->selstart == 0)) {
if (ef->pos == 0) {
ef->selstart = ef->selend = 1;
}
else {
ef->selstart = ef->selend = string_offset + 1;
}
}
ef->selend = string_offset;
ef->pos = string_offset;
DEG_id_tag_update(ob->data, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
}
static int font_selection_set_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Object *obedit = CTX_data_active_object(C);
Curve *cu = obedit->data;
EditFont *ef = cu->editfont;
font_cursor_set_apply(C, event);
ef->selstart = 0;
ef->selend = 0;
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
static int font_selection_set_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
{
switch (event->type) {
case LEFTMOUSE:
if (event->val == KM_RELEASE) {
font_cursor_set_apply(C, event);
return OPERATOR_FINISHED;
}
break;
case MIDDLEMOUSE:
case RIGHTMOUSE:
return OPERATOR_FINISHED;
case MOUSEMOVE:
font_cursor_set_apply(C, event);
break;
}
return OPERATOR_RUNNING_MODAL;
}
void FONT_OT_selection_set(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "Set Selection";
ot->idname = "FONT_OT_selection_set";
ot->description = "Set cursor selection";
/* api callbacks */
ot->invoke = font_selection_set_invoke;
ot->modal = font_selection_set_modal;
ot->poll = ED_operator_editfont;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Select Word Operator
* \{ */
static int font_select_word_exec(bContext *C, wmOperator *UNUSED(op))
{
move_cursor(C, NEXT_CHAR, false);
move_cursor(C, PREV_WORD, false);
move_cursor(C, NEXT_WORD, true);
return OPERATOR_FINISHED;
}
void FONT_OT_select_word(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select Word";
ot->idname = "FONT_OT_select_word";
ot->description = "Select word under cursor";
/* api callbacks */
ot->exec = font_select_word_exec;
ot->poll = ED_operator_editfont;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Text-Box Add Operator
* \{ */

View File

@ -151,6 +151,10 @@ short ED_transform_calc_orientation_from_type_ex(const struct Scene *scene,
int pivot_point,
float r_mat[3][3]);
bool ED_transform_calc_pivot_pos(const struct bContext *C,
const short pivot_type,
float r_pivot_pos[3]);
/* transform gizmos */
void VIEW3D_GGT_xform_gizmo(struct wmGizmoGroupType *gzgt);
@ -201,7 +205,8 @@ struct TransformCalcParams {
*/
int ED_transform_calc_gizmo_stats(const struct bContext *C,
const struct TransformCalcParams *params,
struct TransformBounds *tbounds);
struct TransformBounds *tbounds,
struct RegionView3D *rv3d);
/**
* Iterates over all the strips and finds the closest snapping candidate of either \a frame_1 or \a

View File

@ -154,7 +154,7 @@ struct SculptUndoNodeGeometry {
CustomData ldata;
CustomData pdata;
int *poly_offset_indices;
blender::ImplicitSharingInfo *poly_offsets_sharing_info;
const blender::ImplicitSharingInfo *poly_offsets_sharing_info;
int totvert;
int totedge;
int totloop;

View File

@ -118,7 +118,6 @@ static void tracking_segment_knot_cb(void *userdata,
float val)
{
TrackMotionCurveUserData *data = (TrackMotionCurveUserData *)userdata;
int sel = 0, sel_flag;
if (track != data->act_track) {
return;
@ -127,8 +126,9 @@ static void tracking_segment_knot_cb(void *userdata,
return;
}
sel_flag = value_source == CLIP_VALUE_SOURCE_SPEED_X ? MARKER_GRAPH_SEL_X : MARKER_GRAPH_SEL_Y;
sel = (marker->flag & sel_flag) ? 1 : 0;
const int sel_flag = value_source == CLIP_VALUE_SOURCE_SPEED_X ? MARKER_GRAPH_SEL_X :
MARKER_GRAPH_SEL_Y;
const bool sel = (marker->flag & sel_flag) != 0;
if (sel == data->sel) {
immUniformThemeColor(sel ? TH_HANDLE_VERTEX_SELECT : TH_HANDLE_VERTEX);

Some files were not shown because too many files have changed in this diff Show More