realize-depth #5

Merged
Arye Ramaty merged 43 commits from David-Haver/blender:realize-depth into WIP-realize-depth 2024-03-31 17:22:49 +02:00
70 changed files with 2436 additions and 2814 deletions

View File

@ -1949,7 +1949,7 @@ if html_theme == "furo":
"sidebar/scroll-start.html",
"sidebar/navigation.html",
"sidebar/scroll-end.html",
# "sidebar/variant-selector.html",
"sidebar/variant-selector.html",
]
}
""")

View File

@ -104,10 +104,6 @@ if(WITH_MOD_FLUID)
add_subdirectory(mantaflow)
endif()
if(WITH_COMPOSITOR_CPU)
add_subdirectory(smaa_areatex)
endif()
if(WITH_VULKAN_BACKEND)
add_subdirectory(vulkan_memory_allocator)
endif()

View File

@ -1,5 +0,0 @@
# SPDX-FileCopyrightText: 2017 Blender Foundation
#
# SPDX-License-Identifier: GPL-2.0-or-later
add_executable(smaa_areatex smaa_areatex.cpp)

View File

@ -1,5 +0,0 @@
Project: smaa-cpp
URL: https://github.com/iRi-E/smaa-cpp
License: MIT
Upstream version: 0.4.0
Local modifications:

File diff suppressed because it is too large Load Diff

View File

@ -106,7 +106,7 @@ struct ShaderCache {
friend ShaderCache *get_shader_cache(id<MTLDevice> mtlDevice);
void compile_thread_func(int thread_index);
void compile_thread_func();
using PipelineCollection = std::vector<unique_ptr<MetalKernelPipeline>>;
@ -174,7 +174,7 @@ void ShaderCache::wait_for_all()
}
}
void ShaderCache::compile_thread_func(int /*thread_index*/)
void ShaderCache::compile_thread_func()
{
while (running) {
@ -309,7 +309,7 @@ void ShaderCache::load_kernel(DeviceKernel device_kernel,
metal_printf("Spawning %d Cycles kernel compilation threads\n", max_mtlcompiler_threads);
for (int i = 0; i < max_mtlcompiler_threads; i++) {
compile_threads.push_back(std::thread([&] { compile_thread_func(i); }));
compile_threads.push_back(std::thread([this] { this->compile_thread_func(); }));
}
}
}

View File

@ -64,6 +64,11 @@ typedef struct VolumeShaderCoefficients {
Spectrum emission;
} VolumeShaderCoefficients;
typedef struct EquiangularCoefficients {
float3 P;
float2 t_range;
} EquiangularCoefficients;
/* Evaluate shader to get extinction coefficient at P. */
ccl_device_inline bool shadow_volume_shader_sample(KernelGlobals kg,
IntegratorShadowState state,
@ -264,18 +269,18 @@ ccl_device void volume_shadow_heterogeneous(KernelGlobals kg,
# define VOLUME_SAMPLE_PDF_CUTOFF 1e-8f
ccl_device float volume_equiangular_sample(ccl_private const Ray *ccl_restrict ray,
const float3 light_P,
ccl_private const EquiangularCoefficients &coeffs,
const float xi,
ccl_private float *pdf)
{
const float tmin = ray->tmin;
const float tmax = ray->tmax;
const float delta = dot((light_P - ray->P), ray->D);
const float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta);
const float delta = dot((coeffs.P - ray->P), ray->D);
const float D = safe_sqrtf(len_squared(coeffs.P - ray->P) - delta * delta);
if (UNLIKELY(D == 0.0f)) {
*pdf = 0.0f;
return 0.0f;
}
const float tmin = coeffs.t_range.x;
const float tmax = coeffs.t_range.y;
const float theta_a = atan2f(tmin - delta, D);
const float theta_b = atan2f(tmax - delta, D);
const float t_ = D * tanf((xi * theta_b) + (1 - xi) * theta_a);
@ -289,17 +294,17 @@ ccl_device float volume_equiangular_sample(ccl_private const Ray *ccl_restrict r
}
ccl_device float volume_equiangular_pdf(ccl_private const Ray *ccl_restrict ray,
const float3 light_P,
ccl_private const EquiangularCoefficients &coeffs,
const float sample_t)
{
const float delta = dot((light_P - ray->P), ray->D);
const float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta);
const float delta = dot((coeffs.P - ray->P), ray->D);
const float D = safe_sqrtf(len_squared(coeffs.P - ray->P) - delta * delta);
if (UNLIKELY(D == 0.0f)) {
return 0.0f;
}
const float tmin = ray->tmin;
const float tmax = ray->tmax;
const float tmin = coeffs.t_range.x;
const float tmax = coeffs.t_range.y;
const float t_ = sample_t - delta;
const float theta_a = atan2f(tmin - delta, D);
@ -313,6 +318,29 @@ ccl_device float volume_equiangular_pdf(ccl_private const Ray *ccl_restrict ray,
return pdf;
}
ccl_device_inline bool volume_equiangular_valid_ray_segment(KernelGlobals kg,
const float3 ray_P,
const float3 ray_D,
ccl_private float2 *t_range,
const ccl_private LightSample *ls)
{
if (ls->type == LIGHT_SPOT) {
ccl_global const KernelLight *klight = &kernel_data_fetch(lights, ls->lamp);
return spot_light_valid_ray_segment(klight, ray_P, ray_D, t_range);
}
if (ls->type == LIGHT_AREA) {
ccl_global const KernelLight *klight = &kernel_data_fetch(lights, ls->lamp);
return area_light_valid_ray_segment(&klight->area, ray_P - klight->co, ray_D, t_range);
}
if (ls->type == LIGHT_TRIANGLE) {
return triangle_light_valid_ray_segment(kg, ray_P - ls->P, ray_D, t_range, ls);
}
/* Point light, the whole range of the ray is visible. */
kernel_assert(ls->type == LIGHT_POINT);
return true;
}
/* Distance sampling */
ccl_device float volume_distance_sample(float max_t,
@ -403,7 +431,7 @@ typedef struct VolumeIntegrateState {
ccl_device_forceinline void volume_integrate_step_scattering(
ccl_private const ShaderData *sd,
ccl_private const Ray *ray,
const float3 equiangular_light_P,
ccl_private const EquiangularCoefficients &equiangular_coeffs,
ccl_private const VolumeShaderCoefficients &ccl_restrict coeff,
const Spectrum transmittance,
ccl_private VolumeIntegrateState &ccl_restrict vstate,
@ -474,7 +502,7 @@ ccl_device_forceinline void volume_integrate_step_scattering(
/* Multiple importance sampling. */
if (vstate.use_mis) {
const float equiangular_pdf = volume_equiangular_pdf(ray, equiangular_light_P, new_t);
const float equiangular_pdf = volume_equiangular_pdf(ray, equiangular_coeffs, new_t);
const float mis_weight = power_heuristic(vstate.distance_pdf * distance_pdf,
equiangular_pdf);
result.direct_throughput *= 2.0f * mis_weight;
@ -509,7 +537,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
ccl_global float *ccl_restrict render_buffer,
const float object_step_size,
const VolumeSampleMethod direct_sample_method,
const float3 equiangular_light_P,
ccl_private const EquiangularCoefficients &equiangular_coeffs,
ccl_private VolumeIntegrateResult &result)
{
PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_INTEGRATE);
@ -560,7 +588,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
/* Equiangular sampling: compute distance and PDF in advance. */
if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) {
result.direct_t = volume_equiangular_sample(
ray, equiangular_light_P, vstate.rscatter, &vstate.equiangular_pdf);
ray, equiangular_coeffs, vstate.rscatter, &vstate.equiangular_pdf);
}
# ifdef __PATH_GUIDING__
result.direct_sample_method = vstate.direct_sample_method;
@ -614,7 +642,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous(
/* Scattering and absorption. */
volume_integrate_step_scattering(
sd, ray, equiangular_light_P, coeff, transmittance, vstate, result);
sd, ray, equiangular_coeffs, coeff, transmittance, vstate, result);
}
else {
/* Absorption only. */
@ -673,7 +701,8 @@ ccl_device_forceinline bool integrate_volume_equiangular_sample_light(
ccl_private const Ray *ccl_restrict ray,
ccl_private const ShaderData *ccl_restrict sd,
ccl_private const RNGState *ccl_restrict rng_state,
ccl_private float3 *ccl_restrict P)
ccl_private EquiangularCoefficients *ccl_restrict equiangular_coeffs,
ccl_private LightSample &ccl_restrict ls)
{
/* Test if there is a light or BSDF that needs direct light. */
if (!kernel_data.integrator.use_direct_light) {
@ -685,7 +714,6 @@ ccl_device_forceinline bool integrate_volume_equiangular_sample_light(
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
const float3 rand_light = path_state_rng_3D(kg, rng_state, PRNG_LIGHT);
LightSample ls ccl_optional_struct_init;
if (!light_sample_from_volume_segment(kg,
rand_light,
sd->time,
@ -708,9 +736,10 @@ ccl_device_forceinline bool integrate_volume_equiangular_sample_light(
return false;
}
*P = ls.P;
equiangular_coeffs->P = ls.P;
return true;
return volume_equiangular_valid_ray_segment(
kg, ray->P, ray->D, &equiangular_coeffs->t_range, &ls);
}
/* Path tracing: sample point on light and evaluate light shader, then
@ -725,41 +754,26 @@ ccl_device_forceinline void integrate_volume_direct_light(
# ifdef __PATH_GUIDING__
ccl_private const Spectrum unlit_throughput,
# endif
ccl_private const Spectrum throughput)
ccl_private const Spectrum throughput,
ccl_private LightSample &ccl_restrict ls)
{
PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_DIRECT_LIGHT);
if (!kernel_data.integrator.use_direct_light) {
if (!kernel_data.integrator.use_direct_light || ls.emitter_id == EMITTER_NONE) {
return;
}
/* Sample position on the same light again, now from the shading point where we scattered.
*
* Note that this means we sample the light tree twice when equiangular sampling is used.
* We could consider sampling the light tree just once and use the same light position again.
*
* This would make the PDFs for MIS weights more complicated due to having to account for
* both distance/equiangular and direct/indirect light sampling, but could be more accurate.
* Additionally we could end up behind the light or outside a spot light cone, which might
* waste a sample. Though on the other hand it would be possible to prevent that with
* equiangular sampling restricted to a smaller sub-segment where the light has influence. */
LightSample ls ccl_optional_struct_init;
/* Sample position on the same light again, now from the shading point where we scattered. */
{
const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag);
const uint bounce = INTEGRATOR_STATE(state, path, bounce);
const float3 rand_light = path_state_rng_3D(kg, rng_state, PRNG_LIGHT);
const float3 N = zero_float3();
const int object_receiver = light_link_receiver_nee(kg, sd);
const int shader_flags = SD_BSDF_HAS_TRANSMISSION;
if (!light_sample_from_position(kg,
rng_state,
rand_light,
sd->time,
P,
zero_float3(),
light_link_receiver_nee(kg, sd),
SD_BSDF_HAS_TRANSMISSION,
bounce,
path_flag,
&ls))
if (!light_sample<false>(
kg, rand_light, sd->time, P, N, object_receiver, shader_flags, bounce, path_flag, &ls))
{
return;
}
@ -877,6 +891,7 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
KernelGlobals kg,
IntegratorState state,
ccl_private ShaderData *sd,
ccl_private const Ray *ray,
ccl_private const RNGState *rng_state,
ccl_private const ShaderVolumePhases *phases)
{
@ -929,6 +944,7 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P;
INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(phase_wo);
INTEGRATOR_STATE_WRITE(state, ray, tmin) = 0.0f;
INTEGRATOR_STATE_WRITE(state, ray, previous_dt) = ray->tmax - ray->tmin;
INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX;
# ifdef __RAY_DIFFERENTIALS__
INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP);
@ -957,7 +973,8 @@ ccl_device_forceinline bool integrate_volume_phase_scatter(
/* Update path state */
INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = phase_pdf;
INTEGRATOR_STATE_WRITE(state, path, mis_origin_n) = zero_float3();
const float3 previous_P = ray->P + ray->D * ray->tmin;
INTEGRATOR_STATE_WRITE(state, path, mis_origin_n) = sd->P - previous_P;
INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf(
unguided_phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf));
@ -989,11 +1006,15 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
/* Sample light ahead of volume stepping, for equiangular sampling. */
/* TODO: distant lights are ignored now, but could instead use even distribution. */
LightSample ls ccl_optional_struct_init;
ls.emitter_id = EMITTER_NONE;
const bool need_light_sample = !(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TERMINATE);
float3 equiangular_P = zero_float3();
const bool have_equiangular_sample = need_light_sample &&
integrate_volume_equiangular_sample_light(
kg, state, ray, &sd, &rng_state, &equiangular_P);
EquiangularCoefficients equiangular_coeffs = {zero_float3(), make_float2(ray->tmin, ray->tmax)};
const bool have_equiangular_sample =
need_light_sample && integrate_volume_equiangular_sample_light(
kg, state, ray, &sd, &rng_state, &equiangular_coeffs, ls);
VolumeSampleMethod direct_sample_method = (have_equiangular_sample) ?
volume_stack_sample_method(kg, state) :
@ -1023,7 +1044,7 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
render_buffer,
step_size,
direct_sample_method,
equiangular_P,
equiangular_coeffs,
result);
/* Perform path termination. The intersect_closest will have already marked this path
@ -1091,7 +1112,8 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
# ifdef __PATH_GUIDING__
unlit_throughput,
# endif
result.direct_throughput);
result.direct_throughput,
ls);
}
/* Indirect light.
@ -1130,7 +1152,7 @@ ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg,
# endif
# endif
if (integrate_volume_phase_scatter(kg, state, &sd, &rng_state, &result.indirect_phases)) {
if (integrate_volume_phase_scatter(kg, state, &sd, ray, &rng_state, &result.indirect_phases)) {
return VOLUME_PATH_SCATTERED;
}
else {

View File

@ -75,6 +75,9 @@ KERNEL_STRUCT_MEMBER(ray, float, tmax, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER(ray, float, time, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER(ray, float, dP, KERNEL_FEATURE_PATH_TRACING)
KERNEL_STRUCT_MEMBER(ray, float, dD, KERNEL_FEATURE_PATH_TRACING)
#ifdef __LIGHT_TREE__
KERNEL_STRUCT_MEMBER(ray, float, previous_dt, KERNEL_FEATURE_PATH_TRACING)
#endif
KERNEL_STRUCT_END(ray)
/*************************** Intersection result ******************************/

View File

@ -233,6 +233,11 @@ ccl_device bool area_light_spread_clamp_light(const float3 P,
return true;
}
ccl_device_forceinline bool area_light_is_ellipse(const ccl_global KernelAreaLight *light)
{
return light->invarea < 0.0f;
}
/* Common API. */
/* Compute `eval_fac` and `pdf`. Also sample a new position on the light if `sample_coord`. */
template<bool in_volume_segment>
@ -338,7 +343,7 @@ ccl_device_inline bool area_light_sample(const ccl_global KernelLight *klight,
const float light_v = dot(inplane, klight->area.axis_v) / klight->area.len_v;
if (!in_volume_segment) {
const bool is_ellipse = (klight->area.invarea < 0.0f);
const bool is_ellipse = area_light_is_ellipse(&klight->area);
/* Sampled point lies outside of the area light. */
if (is_ellipse && (sqr(light_u) + sqr(light_v) > 0.25f)) {
@ -380,7 +385,7 @@ ccl_device_inline bool area_light_intersect(const ccl_global KernelLight *klight
{
/* Area light. */
const float invarea = fabsf(klight->area.invarea);
const bool is_ellipse = (klight->area.invarea < 0.0f);
const bool is_ellipse = area_light_is_ellipse(&klight->area);
if (invarea == 0.0f) {
return false;
}
@ -428,6 +433,55 @@ ccl_device_inline bool area_light_sample_from_intersection(
return area_light_eval<false>(klight, ray_P, &light_P, ls, zero_float2(), false);
}
/* Returns the maximal distance between the light center and the boundary. */
ccl_device_forceinline float area_light_max_extent(const ccl_global KernelAreaLight *light)
{
return 0.5f * (area_light_is_ellipse(light) ? fmaxf(light->len_u, light->len_v) :
len(make_float2(light->len_u, light->len_v)));
}
/* Find the ray segment lit by the area light. */
ccl_device_inline bool area_light_valid_ray_segment(const ccl_global KernelAreaLight *light,
float3 P,
float3 D,
ccl_private float2 *t_range)
{
bool valid;
const float tan_half_spread = light->tan_half_spread;
float3 axis = light->dir;
const bool angle_almost_zero = (tan_half_spread < 1e-5f);
if (angle_almost_zero) {
/* Map to local coordinate of the light. Do not use `itfm` in `KernelLight` as there might be
* additional scaling in the light size. */
const Transform tfm = make_transform(light->axis_u, light->axis_v, axis);
P = transform_point(&tfm, P);
D = transform_direction(&tfm, D);
axis = make_float3(0.0f, 0.0f, 1.0f);
const float half_len_u = 0.5f * light->len_u;
const float half_len_v = 0.5f * light->len_v;
if (area_light_is_ellipse(light)) {
valid = ray_infinite_cylinder_intersect(P, D, half_len_u, half_len_v, t_range);
}
else {
const float3 bbox_min = make_float3(-half_len_u, -half_len_v, 0.0f);
const float3 bbox_max = make_float3(half_len_u, half_len_v, FLT_MAX);
valid = ray_aabb_intersect(bbox_min, bbox_max, P, D, t_range);
}
}
else {
/* Conservative estimation with the smallest possible cone covering the whole spread. */
const float3 apex_to_point = P + area_light_max_extent(light) / tan_half_spread * axis;
const float cos_angle_sq = 1.0f / (1.0f + sqr(tan_half_spread));
valid = ray_cone_intersect(axis, apex_to_point, D, cos_angle_sq, t_range);
}
/* Limit the range to the positive side of the area light. */
return valid && ray_plane_intersect(axis, P, D, t_range);
}
template<bool in_volume_segment>
ccl_device_forceinline bool area_light_tree_parameters(const ccl_global KernelLight *klight,
const float3 centroid,
@ -464,9 +518,8 @@ ccl_device_forceinline bool area_light_tree_parameters(const ccl_global KernelLi
const bool shape_above_surface = dot(N, centroid - P) + fabsf(dot(N, extentu)) +
fabsf(dot(N, extentv)) >
0;
const bool in_volume = is_zero(N);
return (front_facing && shape_above_surface) || in_volume;
return front_facing && shape_above_surface;
}
CCL_NAMESPACE_END

View File

@ -12,9 +12,9 @@ CCL_NAMESPACE_BEGIN
typedef struct LightSample {
float3 P; /* position on light, or direction for distant light */
float3 Ng; /* normal on light */
float3 D; /* direction from shading point to light */
packed_float3 Ng; /* normal on light */
float t; /* distance to light (FLT_MAX for distant light) */
float3 D; /* direction from shading point to light */
float u, v; /* parametric coordinate on primitive */
float pdf; /* pdf for selecting light and point on light */
float pdf_selection; /* pdf for selecting light */
@ -25,6 +25,7 @@ typedef struct LightSample {
int lamp; /* lamp id */
int group; /* lightgroup */
LightType type; /* type of light */
int emitter_id; /* index in the emitter array */
} LightSample;
/* Utilities */

View File

@ -41,36 +41,14 @@ ccl_device int light_distribution_sample(KernelGlobals kg, const float rand)
return index;
}
template<bool in_volume_segment>
ccl_device_noinline bool light_distribution_sample(KernelGlobals kg,
const float3 rand,
const float time,
const float3 P,
const float3 N,
const int object_receiver,
const int shader_flags,
const int bounce,
const uint32_t path_flag,
const float rand,
ccl_private LightSample *ls)
{
/* Sample light index from distribution. */
/* The first two dimensions of the Sobol sequence have better stratification. */
const int index = light_distribution_sample(kg, rand.z);
const float pdf_selection = kernel_data.integrator.distribution_pdf_lights;
const float2 rand_uv = float3_to_float2(rand);
return light_sample<in_volume_segment>(kg,
rand_uv,
time,
P,
N,
object_receiver,
shader_flags,
bounce,
path_flag,
index,
0,
pdf_selection,
ls);
ls->emitter_id = light_distribution_sample(kg, rand);
ls->pdf_selection = kernel_data.integrator.distribution_pdf_lights;
return true;
}
ccl_device_inline float light_distribution_pdf_lamp(KernelGlobals kg)

View File

@ -177,7 +177,7 @@ ccl_device_inline bool light_sample(KernelGlobals kg,
template<bool in_volume_segment>
ccl_device_noinline bool light_sample(KernelGlobals kg,
const float2 rand,
const float3 rand_light,
const float time,
const float3 P,
const float3 N,
@ -185,33 +185,31 @@ ccl_device_noinline bool light_sample(KernelGlobals kg,
const int shader_flags,
const int bounce,
const uint32_t path_flag,
const int emitter_index,
const int object_id,
const float pdf_selection,
ccl_private LightSample *ls)
{
/* The first two dimensions of the Sobol sequence have better stratification, use them to sample
* position on the light. */
const float2 rand = float3_to_float2(rand_light);
int prim;
MeshLight mesh_light;
#ifdef __LIGHT_TREE__
if (kernel_data.integrator.use_light_tree) {
ccl_global const KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
emitter_index);
ls->emitter_id);
prim = kemitter->light.id;
mesh_light.shader_flag = kemitter->mesh_light.shader_flag;
mesh_light.object_id = object_id;
mesh_light.object_id = ls->object;
}
else
#endif
{
ccl_global const KernelLightDistribution *kdistribution = &kernel_data_fetch(
light_distribution, emitter_index);
light_distribution, ls->emitter_id);
prim = kdistribution->prim;
mesh_light = kdistribution->mesh_light;
}
/* A different value would be assigned in `triangle_light_sample()` if `!use_light_tree`. */
ls->pdf_selection = pdf_selection;
if (prim >= 0) {
/* Mesh light. */
const int object = mesh_light.object_id;

View File

@ -329,17 +329,25 @@ ccl_device_inline bool light_sample_from_volume_segment(KernelGlobals kg,
const uint32_t path_flag,
ccl_private LightSample *ls)
{
const int shader_flags = SD_BSDF_HAS_TRANSMISSION;
#ifdef __LIGHT_TREE__
if (kernel_data.integrator.use_light_tree) {
return light_tree_sample<true>(
kg, rand, time, P, D, t, object_receiver, SD_BSDF_HAS_TRANSMISSION, bounce, path_flag, ls);
if (!light_tree_sample<true>(kg, rand.z, P, D, t, object_receiver, shader_flags, ls)) {
return false;
}
}
else
#endif
{
return light_distribution_sample<true>(
kg, rand, time, P, D, object_receiver, SD_BSDF_HAS_TRANSMISSION, bounce, path_flag, ls);
if (!light_distribution_sample(kg, rand.z, ls)) {
return false;
}
}
/* Sample position on the selected light. */
return light_sample<true>(
kg, rand, time, P, D, object_receiver, shader_flags, bounce, path_flag, ls);
}
ccl_device bool light_sample_from_position(KernelGlobals kg,
@ -354,17 +362,24 @@ ccl_device bool light_sample_from_position(KernelGlobals kg,
const uint32_t path_flag,
ccl_private LightSample *ls)
{
/* Randomly select a light. */
#ifdef __LIGHT_TREE__
if (kernel_data.integrator.use_light_tree) {
return light_tree_sample<false>(
kg, rand, time, P, N, 0.0f, object_receiver, shader_flags, bounce, path_flag, ls);
if (!light_tree_sample<false>(kg, rand.z, P, N, 0.0f, object_receiver, shader_flags, ls)) {
return false;
}
}
else
#endif
{
return light_distribution_sample<false>(
kg, rand, time, P, N, object_receiver, shader_flags, bounce, path_flag, ls);
if (!light_distribution_sample(kg, rand.z, ls)) {
return false;
}
}
/* Sample position on the selected light. */
return light_sample<false>(
kg, rand, time, P, N, object_receiver, shader_flags, bounce, path_flag, ls);
}
/* Update light sample with new shading point position for MNEE. The position on the light is fixed
@ -415,13 +430,15 @@ ccl_device_inline float light_sample_mis_weight_forward_surface(KernelGlobals kg
#ifdef __LIGHT_TREE__
if (kernel_data.integrator.use_light_tree) {
float3 ray_P = INTEGRATOR_STATE(state, ray, P);
const float dt = INTEGRATOR_STATE(state, ray, previous_dt);
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
uint lookup_offset = kernel_data_fetch(object_lookup_offset, sd->object);
uint prim_offset = kernel_data_fetch(object_prim_offset, sd->object);
uint triangle = kernel_data_fetch(triangle_to_tree, sd->prim - prim_offset + lookup_offset);
pdf *= light_tree_pdf(
kg, ray_P, N, path_flag, sd->object, triangle, light_link_receiver_forward(kg, state));
kg, ray_P, N, dt, path_flag, sd->object, triangle, light_link_receiver_forward(kg, state));
}
else
#endif
@ -445,9 +462,11 @@ ccl_device_inline float light_sample_mis_weight_forward_lamp(KernelGlobals kg,
#ifdef __LIGHT_TREE__
if (kernel_data.integrator.use_light_tree) {
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
const float dt = INTEGRATOR_STATE(state, ray, previous_dt);
pdf *= light_tree_pdf(kg,
P,
N,
dt,
path_flag,
0,
kernel_data_fetch(light_to_tree, ls->lamp),
@ -485,9 +504,10 @@ ccl_device_inline float light_sample_mis_weight_forward_background(KernelGlobals
#ifdef __LIGHT_TREE__
if (kernel_data.integrator.use_light_tree) {
const float3 N = INTEGRATOR_STATE(state, path, mis_origin_n);
const float dt = INTEGRATOR_STATE(state, ray, previous_dt);
uint light = kernel_data_fetch(light_to_tree, kernel_data.background.light_index);
pdf *= light_tree_pdf(
kg, ray_P, N, path_flag, 0, light, light_link_receiver_forward(kg, state));
kg, ray_P, N, dt, path_flag, 0, light, light_link_receiver_forward(kg, state));
}
else
#endif

View File

@ -265,6 +265,24 @@ ccl_device_inline bool spot_light_sample_from_intersection(
return true;
}
/* Find the ray segment lit by the spot light. */
ccl_device_inline bool spot_light_valid_ray_segment(const ccl_global KernelLight *klight,
const float3 P,
const float3 D,
ccl_private float2 *t_range)
{
/* Convert to local space of the spot light. */
const Transform itfm = klight->itfm;
float3 local_P = P + klight->spot.dir * klight->spot.ray_segment_dp;
local_P = transform_point(&itfm, local_P);
const float3 local_D = transform_direction(&itfm, D);
const float3 axis = make_float3(0.0f, 0.0f, -1.0f);
/* Intersect the ray with the smallest enclosing cone of the light spread. */
return ray_cone_intersect(
axis, local_P, local_D, sqr(klight->spot.cos_half_spot_angle), t_range);
}
template<bool in_volume_segment>
ccl_device_forceinline bool spot_light_tree_parameters(const ccl_global KernelLight *klight,
const float3 centroid,

View File

@ -148,10 +148,7 @@ ccl_device void light_tree_importance(const float3 N_or_D,
float cos_min_incidence_angle = 1.0f;
float cos_max_incidence_angle = 1.0f;
/* When sampling the light tree for the second time in `shade_volume.h` and when query the pdf in
* `sample.h`. */
const bool in_volume = is_zero(N_or_D);
if (!in_volume_segment && !in_volume) {
if (!in_volume_segment) {
const float3 N = N_or_D;
const float cos_theta_i = has_transmission ? fabsf(dot(point_to_centroid, N)) :
dot(point_to_centroid, N);
@ -221,9 +218,9 @@ ccl_device void light_tree_importance(const float3 N_or_D,
max_importance = fabsf(f_a * cos_min_incidence_angle * energy * cos_min_outgoing_angle /
(in_volume_segment ? min_distance : sqr(min_distance)));
/* TODO: also min importance for volume? */
/* TODO: compute proper min importance for volume. */
if (in_volume_segment) {
min_importance = max_importance;
min_importance = 0.0f;
return;
}
@ -270,10 +267,10 @@ ccl_device bool compute_emitter_centroid_and_dir(KernelGlobals kg,
/* Arbitrary centroid and direction. */
centroid = make_float3(0.0f, 0.0f, 1.0f);
dir = make_float3(0.0f, 0.0f, -1.0f);
return !in_volume_segment;
break;
case LIGHT_DISTANT:
dir = centroid;
return !in_volume_segment;
break;
default:
return false;
}
@ -323,12 +320,13 @@ ccl_device void light_tree_node_importance(KernelGlobals kg,
float cos_theta_u;
float distance;
if (knode->type == LIGHT_TREE_DISTANT) {
if (in_volume_segment) {
return;
}
point_to_centroid = -bcone.axis;
cos_theta_u = fast_cosf(bcone.theta_o + bcone.theta_e);
distance = 1.0f;
if (t == FLT_MAX) {
/* In world volume, distant light has no contribution. */
return;
}
}
else {
const float3 centroid = 0.5f * (bbox.min + bbox.max);
@ -339,6 +337,9 @@ ccl_device void light_tree_node_importance(KernelGlobals kg,
/* Minimal distance of the ray to the cluster. */
distance = len(centroid - closest_point);
point_to_centroid = -compute_v(centroid, P, D, bcone.axis, t);
/* FIXME(weizhen): it is not clear from which point the `cos_theta_u` should be computed in
* volume segment. We could use `closest_point` as a conservative measure, but then
* `point_to_centroid` should also use `closest_point`. */
cos_theta_u = light_tree_cos_bounding_box_angle(bbox, closest_point, point_to_centroid);
}
else {
@ -697,17 +698,16 @@ ccl_device int light_tree_root_node_index(KernelGlobals kg, const int object_rec
return 0;
}
/* Pick a random light from the light tree from a given shading point P, write to the picked light
* index and the probability of picking the light. */
template<bool in_volume_segment>
ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
const float3 rand,
const float time,
const float rand,
const float3 P,
float3 N_or_D,
float t,
const int object_receiver,
const int shader_flags,
const int bounce,
const uint32_t path_flag,
ccl_private LightSample *ls)
{
if (!kernel_data.integrator.use_direct_light) {
@ -718,10 +718,8 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
float pdf_leaf = 1.0f;
float pdf_selection = 1.0f;
int selected_emitter = -1;
int object_emitter = 0;
int node_index = light_tree_root_node_index(kg, object_receiver);
/* The first two dimensions of the Sobol sequence have better stratification. */
float rand_selection = rand.z;
float rand_selection = rand;
float3 local_P = P;
@ -743,7 +741,7 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
}
/* Continue with the picked mesh light. */
object_emitter = kernel_data_fetch(light_tree_emitters, selected_emitter).mesh.object_id;
ls->object = kernel_data_fetch(light_tree_emitters, selected_emitter).mesh.object_id;
continue;
}
@ -766,27 +764,18 @@ ccl_device_noinline bool light_tree_sample(KernelGlobals kg,
pdf_leaf *= (node_index == left_index) ? left_prob : (1.0f - left_prob);
}
pdf_selection *= pdf_leaf;
ls->emitter_id = selected_emitter;
ls->pdf_selection = pdf_selection * pdf_leaf;
return light_sample<in_volume_segment>(kg,
float3_to_float2(rand),
time,
P,
N_or_D,
object_receiver,
shader_flags,
bounce,
path_flag,
selected_emitter,
object_emitter,
pdf_selection,
ls);
return true;
}
/* We need to be able to find the probability of selecting a given light for MIS. */
template<bool in_volume_segment>
ccl_device float light_tree_pdf(KernelGlobals kg,
float3 P,
float3 N,
const float dt,
const int path_flag,
const int object_emitter,
const uint index_emitter,
@ -796,7 +785,7 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
ccl_global const KernelLightTreeEmitter *kemitter = &kernel_data_fetch(light_tree_emitters,
index_emitter);
int root_index;
int subtree_root_index;
uint bit_trail, target_emitter;
if (is_triangle(kemitter)) {
@ -805,16 +794,17 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
target_emitter = kernel_data_fetch(object_to_tree, object_emitter);
ccl_global const KernelLightTreeEmitter *kmesh = &kernel_data_fetch(light_tree_emitters,
target_emitter);
root_index = kmesh->mesh.node_id;
ccl_global const KernelLightTreeNode *kroot = &kernel_data_fetch(light_tree_nodes, root_index);
subtree_root_index = kmesh->mesh.node_id;
ccl_global const KernelLightTreeNode *kroot = &kernel_data_fetch(light_tree_nodes,
subtree_root_index);
bit_trail = kroot->bit_trail;
if (kroot->type == LIGHT_TREE_INSTANCE) {
root_index = kroot->instance.reference;
subtree_root_index = kroot->instance.reference;
}
}
else {
root_index = 0;
subtree_root_index = -1;
bit_trail = kemitter->bit_trail;
target_emitter = index_emitter;
}
@ -836,8 +826,8 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
for (int i = 0; i < knode->num_emitters; i++) {
const int emitter = knode->leaf.first_emitter + i;
float max_importance, min_importance;
light_tree_emitter_importance<false>(
kg, P, N, 0, has_transmission, emitter, max_importance, min_importance);
light_tree_emitter_importance<in_volume_segment>(
kg, P, N, dt, has_transmission, emitter, max_importance, min_importance);
num_has_importance += (max_importance > 0);
if (emitter == target_emitter) {
target_max_importance = max_importance;
@ -856,13 +846,13 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
return 0.0f;
}
if (root_index) {
if (subtree_root_index != -1) {
/* Arrived at the mesh light. Continue with the subtree. */
float unused;
light_tree_to_local_space<false>(kg, object_emitter, P, N, unused);
light_tree_to_local_space<in_volume_segment>(kg, object_emitter, P, N, unused);
node_index = root_index;
root_index = 0;
node_index = subtree_root_index;
subtree_root_index = -1;
target_emitter = index_emitter;
bit_trail = kemitter->bit_trail;
continue;
@ -877,8 +867,8 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
const int right_index = knode->inner.right_child;
float left_prob;
if (!get_left_probability<false>(
kg, P, N, 0, has_transmission, left_index, right_index, left_prob))
if (!get_left_probability<in_volume_segment>(
kg, P, N, dt, has_transmission, left_index, right_index, left_prob))
{
return 0.0f;
}
@ -896,4 +886,27 @@ ccl_device float light_tree_pdf(KernelGlobals kg,
}
}
/* If the function is called in volume, retrieve the previous point in volume segment, and compute
* pdf from there. Otherwise compute from the current shading point. */
ccl_device_inline float light_tree_pdf(KernelGlobals kg,
float3 P,
float3 N,
const float dt,
const int path_flag,
const int emitter_object,
const uint emitter_id,
const int object_receiver)
{
if (path_flag & PATH_RAY_VOLUME_SCATTER) {
const float3 D_times_t = N;
const float3 D = normalize(D_times_t);
P = P - D_times_t;
return light_tree_pdf<true>(
kg, P, D, dt, path_flag, emitter_object, emitter_id, object_receiver);
}
return light_tree_pdf<false>(
kg, P, N, 0.0f, path_flag, emitter_object, emitter_id, object_receiver);
}
CCL_NAMESPACE_END

View File

@ -269,6 +269,26 @@ ccl_device_forceinline bool triangle_light_sample(KernelGlobals kg,
return (ls->pdf > 0.0f);
}
/* Find the ray segment lit by the triangle light. */
ccl_device_inline bool triangle_light_valid_ray_segment(KernelGlobals kg,
const float3 P,
const float3 D,
ccl_private float2 *t_range,
const ccl_private LightSample *ls)
{
const int shader_flag = kernel_data_fetch(shaders, ls->shader & SHADER_MASK).flags;
const int SD_MIS_BOTH = SD_MIS_BACK | SD_MIS_FRONT;
if ((shader_flag & SD_MIS_BOTH) == SD_MIS_BOTH) {
/* Both sides are sampled, the complete ray segment is visible. */
return true;
}
/* Only one side is sampled, intersect the ray and the triangle light plane to find the visible
* ray segment. Flip normal if Emission Sampling is set to back. */
const float3 N = ls->Ng;
return ray_plane_intersect((shader_flag & SD_MIS_BACK) ? -N : N, P, D, t_range);
}
template<bool in_volume_segment>
ccl_device_forceinline bool triangle_light_tree_parameters(
KernelGlobals kg,
@ -307,9 +327,8 @@ ccl_device_forceinline bool triangle_light_tree_parameters(
}
const bool front_facing = bcone.theta_o != 0.0f || dot(bcone.axis, point_to_centroid) < 0;
const bool in_volume = is_zero(N);
return (front_facing && shape_above_surface) || in_volume;
return front_facing && shape_above_surface;
}
CCL_NAMESPACE_END

View File

@ -45,6 +45,7 @@ CCL_NAMESPACE_BEGIN
#define OBJECT_NONE (~0)
#define PRIM_NONE (~0)
#define LAMP_NONE (~0)
#define EMITTER_NONE (~0)
#define ID_NONE (0.0f)
#define PASS_UNUSED (~0)
#define LIGHTGROUP_NONE (~0)
@ -1376,6 +1377,8 @@ typedef struct KernelSpotLight {
int is_sphere;
/* For non-uniform object scaling, the actual spread might be different. */
float cos_half_larger_spread;
/* Distance from the apex of the smallest enclosing cone of the light spread to light center. */
float ray_segment_dp;
} KernelSpotLight;
/* PointLight is SpotLight with only radius and invarea being used. */

View File

@ -1362,6 +1362,9 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce
/* Choose the angle which spans a larger cone. */
klights[light_index].spot.cos_half_larger_spread = inversesqrtf(
1.0f + tan_sq * fmaxf(len_u_sq, len_v_sq) / len_w_sq);
/* radius / sin(half_angle_small) */
klights[light_index].spot.ray_segment_dp =
light->size * sqrtf(1.0f + len_w_sq / (tan_sq * fminf(len_u_sq, len_v_sq)));
}
klights[light_index].shader_id = shader_id;

View File

@ -1030,6 +1030,46 @@ ccl_device_inline uint32_t reverse_integer_bits(uint32_t x)
#endif
}
/* Check if intervals (first->x, first->y) and (second.x, second.y) intersect, and replace the
* first interval with their intersection. */
ccl_device_inline bool intervals_intersect(ccl_private float2 *first, const float2 second)
{
first->x = fmaxf(first->x, second.x);
first->y = fminf(first->y, second.y);
return first->x < first->y;
}
/* Solve quadratic equation a*x^2 + b*x + c = 0, adapted from Mitsuba 3
* The solution is ordered so that x1 <= x2.
* Returns true if at least one solution is found. */
ccl_device_inline bool solve_quadratic(
const float a, const float b, const float c, ccl_private float &x1, ccl_private float &x2)
{
/* If the equation is linear, the solution is -c/b, but b has to be non-zero. */
const bool valid_linear = (a == 0.0f) && (b != 0.0f);
x1 = x2 = -c / b;
const float discriminant = sqr(b) - 4.0f * a * c;
/* Allow slightly negative discriminant in case of numerical precision issues. */
const bool valid_quadratic = (a != 0.0f) && (discriminant > -1e-5f);
if (valid_quadratic) {
/* Numerically stable version of (-b ± sqrt(discriminant)) / (2 * a), avoiding catastrophic
* cancellation when `b` is very close to `sqrt(discriminant)`, by finding the solution of
* greater magnitude which does not suffer from loss of precision, then using the identity
* x1 * x2 = c / a. */
const float temp = -0.5f * (b + copysignf(safe_sqrtf(discriminant), b));
const float r1 = temp / a;
const float r2 = c / temp;
x1 = fminf(r1, r2);
x2 = fmaxf(r1, r2);
}
return (valid_linear || valid_quadratic);
}
CCL_NAMESPACE_END
#endif /* __UTIL_MATH_H__ */

View File

@ -302,6 +302,140 @@ ccl_device bool ray_quad_intersect(float3 ray_P,
return true;
}
/* Find the ray segment that lies in the same side as the normal `N` of the plane.
* `P` is the vector pointing from any point on the plane to the ray origin. */
ccl_device bool ray_plane_intersect(const float3 N,
const float3 P,
const float3 ray_D,
ccl_private float2 *t_range)
{
const float DN = dot(ray_D, N);
/* Distance from P to the plane. */
const float t = -dot(P, N) / DN;
/* Limit the range to the positive side. */
if (DN > 0.0f) {
t_range->x = fmaxf(t_range->x, t);
}
else {
t_range->y = fminf(t_range->y, t);
}
return t_range->x < t_range->y;
}
/* Find the ray segment inside an axis-aligned bounding box. */
ccl_device bool ray_aabb_intersect(const float3 bbox_min,
const float3 bbox_max,
const float3 ray_P,
const float3 ray_D,
ccl_private float2 *t_range)
{
const float3 inv_ray_D = rcp(ray_D);
/* Absolute distances to lower and upper box coordinates; */
const float3 t_lower = (bbox_min - ray_P) * inv_ray_D;
const float3 t_upper = (bbox_max - ray_P) * inv_ray_D;
/* The four t-intervals (for x-/y-/z-slabs, and ray p(t)). */
const float4 tmins = float3_to_float4(min(t_lower, t_upper), t_range->x);
const float4 tmaxes = float3_to_float4(max(t_lower, t_upper), t_range->y);
/* Max of mins and min of maxes. */
const float tmin = reduce_max(tmins);
const float tmax = reduce_min(tmaxes);
*t_range = make_float2(tmin, tmax);
return tmin < tmax;
}
/* Find the segment of a ray defined by P + D * t that lies inside a cylinder defined by
* (x / len_u)^2 + (y / len_v)^2 = 1. */
ccl_device_inline bool ray_infinite_cylinder_intersect(const float3 P,
const float3 D,
const float len_u,
const float len_v,
ccl_private float2 *t_range)
{
/* Convert to a 2D problem. */
const float2 inv_len = 1.0f / make_float2(len_u, len_v);
float2 P_proj = float3_to_float2(P) * inv_len;
const float2 D_proj = float3_to_float2(D) * inv_len;
/* Solve quadratic equation a*t^2 + 2b*t + c = 0. */
const float a = dot(D_proj, D_proj);
float b = dot(P_proj, D_proj);
/* Move ray origin closer to the cylinder to prevent precision issue when the ray is far away. */
const float t_mid = -b / a;
P_proj += D_proj * t_mid;
/* Recompute b from the shifted origin. */
b = dot(P_proj, D_proj);
const float c = dot(P_proj, P_proj) - 1.0f;
float tmin, tmax;
const bool valid = solve_quadratic(a, 2.0f * b, c, tmin, tmax);
return valid && intervals_intersect(t_range, make_float2(tmin, tmax) + t_mid);
}
/* *
* Find the ray segment inside a single-sided cone.
*
* \param axis: a unit-length direction around which the cone has a circular symmetry
* \param P: the vector pointing from the cone apex to the ray origin
* \param D: the direction of the ray, does not need to have unit-length
* \param cos_angle_sq: `sqr(cos(half_aperture_of_the_cone))`
* \param t_range: the lower and upper bounds between which the ray lies inside the cone
* \return whether the intersection exists and is in the provided range
*
* See https://www.geometrictools.com/Documentation/IntersectionLineCone.pdf for illustration
*/
ccl_device_inline bool ray_cone_intersect(const float3 axis,
const float3 P,
float3 D,
const float cos_angle_sq,
ccl_private float2 *t_range)
{
if (cos_angle_sq < 1e-4f) {
/* The cone is nearly a plane. */
return ray_plane_intersect(axis, P, D, t_range);
}
const float inv_len = inversesqrtf(len_squared(D));
D *= inv_len;
const float AD = dot(axis, D);
const float AP = dot(axis, P);
const float a = sqr(AD) - cos_angle_sq;
const float b = 2.0f * (AD * AP - cos_angle_sq * dot(D, P));
const float c = sqr(AP) - cos_angle_sq * dot(P, P);
float tmin = 0.0f, tmax = FLT_MAX;
bool valid = solve_quadratic(a, b, c, tmin, tmax);
/* Check if the intersections are in the same hemisphere as the cone. */
const bool tmin_valid = AP + tmin * AD > 0.0f;
const bool tmax_valid = AP + tmax * AD > 0.0f;
valid &= (tmin_valid || tmax_valid);
if (!tmax_valid) {
tmax = tmin;
tmin = 0.0f;
}
else if (!tmin_valid) {
tmin = tmax;
tmax = FLT_MAX;
}
return valid && intervals_intersect(t_range, make_float2(tmin, tmax) * inv_len);
}
CCL_NAMESPACE_END
#endif /* __UTIL_MATH_INTERSECT_H__ */

View File

@ -161,6 +161,17 @@ ccl_device_inline Transform make_transform(float a,
return t;
}
ccl_device_inline Transform make_transform(const float3 x, const float3 y, const float3 z)
{
Transform t;
t.x = float3_to_float4(x, 0.0f);
t.y = float3_to_float4(y, 0.0f);
t.z = float3_to_float4(z, 0.0f);
return t;
}
ccl_device_inline Transform euler_to_transform(const float3 euler)
{
float cx = cosf(euler.x);

View File

@ -1886,6 +1886,30 @@ GHOST_WindowWayland::GHOST_WindowWayland(GHOST_SystemWayland *system,
gwl_window_state_set(window_, state);
}
/* NOTE(@ideasman42): Round trips are important before committing.
* This is needed because setting the state is likely to resize the window
* (in the case of maximized & full-screen), "normal" windows may still be resized when
* they are too large or with tiling window-managers.
*
* The additional updates allow for the actual size to be configured by the window manager
* which is read back before committing the surface. This avoids displaying the buffer
* before it's resized (avoiding flickering).
*
* Without the round-trip here:
* - The window will be created and this function will return using the requested buffer size,
* instead of the window size which ends up being used (causing a visible flicker).
* This has the down side that Blender's internal window state has the outdated size
* which then gets immediately resized, causing a noticeable glitch.
* - The window decorations will be displayed at the wrong size before refreshing
* at the new size.
* - On GNOME-Shell 46 shows the previous buffer-size under some conditions, see #119871.
* - 2x updates are needed for RIVER & HYPRLAND.
*/
for (int i = 0; i < 2; i++) {
wl_display_flush(system->wl_display_get());
wl_display_dispatch(system->wl_display_get());
}
/* Commit after setting the buffer.
* While postponing until after the buffer drawing is context is set
* isn't essential, it reduces flickering. */

View File

@ -158,7 +158,7 @@ class ARMATURE_MT_collection_tree_context_menu(Menu):
# editable or not. That means this menu has to do the disabling for it.
sub = layout.column()
sub.enabled = not active_bcoll_is_locked
sub.operator("armature.collection_add", text="Add Child Collection")
sub.operator("armature.collection_add", text="Add Bone Collection")
sub.operator("armature.collection_remove")
sub.operator("armature.collection_remove_unused", text="Remove Unused Collections")

View File

@ -2187,6 +2187,7 @@ class VIEW3D_MT_paint_grease_pencil(Menu):
layout.separator()
layout.menu("VIEW3D_MT_edit_greasepencil_showhide")
layout.menu("VIEW3D_MT_edit_greasepencil_cleanup")
layout.separator()
@ -5803,6 +5804,15 @@ class VIEW3D_MT_edit_greasepencil_showhide(Menu):
layout.operator("grease_pencil.layer_hide", text="Hide Inactive Layers").unselected = True
class VIEW3D_MT_edit_greasepencil_cleanup(Menu):
bl_label = "Cleanup"
def draw(self, _context):
layout = self.layout
layout.operator("grease_pencil.clean_loose")
class VIEW3D_MT_edit_greasepencil(Menu):
bl_label = "Grease Pencil"
@ -5828,7 +5838,7 @@ class VIEW3D_MT_edit_greasepencil(Menu):
layout.menu("VIEW3D_MT_edit_greasepencil_showhide")
layout.operator_menu_enum("grease_pencil.separate", "mode", text="Separate")
layout.operator("grease_pencil.clean_loose")
layout.menu("VIEW3D_MT_edit_greasepencil_cleanup")
layout.separator()
@ -7526,14 +7536,14 @@ class VIEW3D_PT_snapping(Panel):
layout = self.layout
col = layout.column()
col.label(text="Snap With")
col.label(text="Snap Base")
row = col.row(align=True)
row.prop(tool_settings, "snap_target", expand=True)
col.label(text="Snap To")
col.label(text="Snap Target")
col.prop(tool_settings, "snap_elements_base", expand=True)
col.label(text="Snap Individual Elements To")
col.label(text="Snap Target for Individual Elements")
col.prop(tool_settings, "snap_elements_individual", expand=True)
col.separator()
@ -8988,6 +8998,7 @@ classes = (
VIEW3D_MT_edit_gpencil_delete,
VIEW3D_MT_edit_gpencil_showhide,
VIEW3D_MT_edit_greasepencil_showhide,
VIEW3D_MT_edit_greasepencil_cleanup,
VIEW3D_MT_weight_gpencil,
VIEW3D_MT_gpencil_animation,
VIEW3D_MT_gpencil_simplify,

View File

@ -253,54 +253,6 @@ static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer
return layer.name;
}
static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data,
const eCustomDataType data_type,
const int domain_num,
const AttributeInit &initializer)
{
switch (initializer.type) {
case AttributeInit::Type::Construct: {
void *data = CustomData_add_layer(&custom_data, data_type, CD_CONSTRUCT, domain_num);
return data != nullptr;
}
case AttributeInit::Type::DefaultValue: {
void *data = CustomData_add_layer(&custom_data, data_type, CD_SET_DEFAULT, domain_num);
return data != nullptr;
}
case AttributeInit::Type::VArray: {
void *data = CustomData_add_layer(&custom_data, data_type, CD_CONSTRUCT, domain_num);
if (data == nullptr) {
return false;
}
const GVArray &varray = static_cast<const AttributeInitVArray &>(initializer).varray;
varray.materialize_to_uninitialized(varray.index_range(), data);
return true;
}
case AttributeInit::Type::MoveArray: {
void *src_data = static_cast<const AttributeInitMoveArray &>(initializer).data;
const void *stored_data = CustomData_add_layer_with_data(
&custom_data, data_type, src_data, domain_num, nullptr);
if (stored_data == nullptr) {
return false;
}
if (stored_data != src_data) {
MEM_freeN(src_data);
return true;
}
return true;
}
case AttributeInit::Type::Shared: {
const AttributeInitShared &init = static_cast<const AttributeInitShared &>(initializer);
const void *stored_data = CustomData_add_layer_with_data(
&custom_data, data_type, const_cast<void *>(init.data), domain_num, init.sharing_info);
return stored_data != nullptr;
}
}
BLI_assert_unreachable();
return false;
}
static void *add_generic_custom_data_layer(CustomData &custom_data,
const eCustomDataType data_type,
const eCDAllocType alloctype,
@ -393,10 +345,7 @@ static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer,
bool BuiltinCustomDataLayerProvider::layer_exists(const CustomData &custom_data) const
{
if (stored_as_named_attribute_) {
return CustomData_get_named_layer_index(&custom_data, stored_type_, name_) != -1;
}
return CustomData_has_layer(&custom_data, stored_type_);
return CustomData_get_named_layer_index(&custom_data, data_type_, name_) != -1;
}
GAttributeReader BuiltinCustomDataLayerProvider::try_get_for_read(const void *owner) const
@ -416,13 +365,7 @@ GAttributeReader BuiltinCustomDataLayerProvider::try_get_for_read(const void *ow
return {};
}
int index;
if (stored_as_named_attribute_) {
index = CustomData_get_named_layer_index(custom_data, stored_type_, name_);
}
else {
index = CustomData_get_layer_index(custom_data, stored_type_);
}
const int index = CustomData_get_named_layer_index(custom_data, data_type_, name_);
if (index == -1) {
return {};
}
@ -452,13 +395,7 @@ GAttributeWriter BuiltinCustomDataLayerProvider::try_get_for_write(void *owner)
return {};
}
void *data = nullptr;
if (stored_as_named_attribute_) {
data = CustomData_get_layer_named_for_write(custom_data, stored_type_, name_, element_num);
}
else {
data = CustomData_get_layer_for_write(custom_data, stored_type_, element_num);
}
void *data = CustomData_get_layer_named_for_write(custom_data, data_type_, name_, element_num);
if (data == nullptr) {
return {};
}
@ -475,57 +412,42 @@ bool BuiltinCustomDataLayerProvider::try_delete(void *owner) const
return {};
}
auto update = [&]() {
const int element_num = custom_data_access_.get_element_num(owner);
if (CustomData_free_layer_named(custom_data, name_, element_num)) {
if (update_on_change_ != nullptr) {
update_on_change_(owner);
}
};
const int element_num = custom_data_access_.get_element_num(owner);
if (stored_as_named_attribute_) {
if (CustomData_free_layer_named(custom_data, name_, element_num)) {
update();
return true;
}
return false;
}
const int layer_index = CustomData_get_layer_index(custom_data, stored_type_);
if (CustomData_free_layer(custom_data, stored_type_, element_num, layer_index)) {
update();
return true;
}
return false;
}
bool BuiltinCustomDataLayerProvider::try_create(void *owner,
const AttributeInit &initializer) const
{
if (createable_ != Creatable) {
return false;
}
CustomData *custom_data = custom_data_access_.get_custom_data(owner);
if (custom_data == nullptr) {
return false;
}
const int element_num = custom_data_access_.get_element_num(owner);
if (stored_as_named_attribute_) {
if (CustomData_has_layer_named(custom_data, data_type_, name_)) {
/* Exists already. */
return false;
}
return add_custom_data_layer_from_attribute_init(
name_, *custom_data, stored_type_, element_num, initializer);
}
if (CustomData_get_layer(custom_data, stored_type_) != nullptr) {
if (CustomData_has_layer_named(custom_data, data_type_, name_)) {
/* Exists already. */
return false;
}
return add_builtin_type_custom_data_layer_from_init(
*custom_data, stored_type_, element_num, initializer);
if (add_custom_data_layer_from_attribute_init(
name_, *custom_data, data_type_, element_num, initializer))
{
if (initializer.type != AttributeInit::Type::Construct) {
/* Avoid calling update function when values are not initialized. In that case
* values must be set elsewhere anyway, which will cause a separate update tag. */
if (update_on_change_ != nullptr) {
update_on_change_(owner);
}
}
return true;
}
return false;
}
bool BuiltinCustomDataLayerProvider::exists(const void *owner) const
@ -534,10 +456,7 @@ bool BuiltinCustomDataLayerProvider::exists(const void *owner) const
if (custom_data == nullptr) {
return false;
}
if (stored_as_named_attribute_) {
return CustomData_has_layer_named(custom_data, stored_type_, name_);
}
return CustomData_get_layer(custom_data, stored_type_) != nullptr;
return CustomData_has_layer_named(custom_data, data_type_, name_);
}
GAttributeReader CustomDataAttributeProvider::try_get_for_read(

View File

@ -31,14 +31,10 @@ struct CustomDataAccessInfo {
* A #BuiltinAttributeProvider is responsible for exactly one attribute on a geometry component.
* The attribute is identified by its name and has a fixed domain and type. Builtin attributes do
* not follow the same loose rules as other attributes, because they are mapped to internal
* "legacy" data structures. For example, some builtin attributes cannot be deleted. */
* "legacy" data structures. For example, some builtin attributes cannot be deleted.
*/
class BuiltinAttributeProvider {
public:
/* Some utility enums to avoid hard to read booleans in function calls. */
enum CreatableEnum {
Creatable,
NonCreatable,
};
enum DeletableEnum {
Deletable,
NonDeletable,
@ -48,7 +44,6 @@ class BuiltinAttributeProvider {
const std::string name_;
const AttrDomain domain_;
const eCustomDataType data_type_;
const CreatableEnum createable_;
const DeletableEnum deletable_;
const AttributeValidator validator_;
@ -56,13 +51,11 @@ class BuiltinAttributeProvider {
BuiltinAttributeProvider(std::string name,
const AttrDomain domain,
const eCustomDataType data_type,
const CreatableEnum createable,
const DeletableEnum deletable,
AttributeValidator validator = {})
: name_(std::move(name)),
domain_(domain),
data_type_(data_type),
createable_(createable),
deletable_(deletable),
validator_(validator)
{
@ -174,27 +167,21 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider {
*/
class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider {
using UpdateOnChange = void (*)(void *owner);
const eCustomDataType stored_type_;
const CustomDataAccessInfo custom_data_access_;
const UpdateOnChange update_on_change_;
bool stored_as_named_attribute_;
public:
BuiltinCustomDataLayerProvider(std::string attribute_name,
const AttrDomain domain,
const eCustomDataType attribute_type,
const eCustomDataType stored_type,
const CreatableEnum creatable,
const eCustomDataType data_type,
const DeletableEnum deletable,
const CustomDataAccessInfo custom_data_access,
const UpdateOnChange update_on_write,
const UpdateOnChange update_on_change,
const AttributeValidator validator = {})
: BuiltinAttributeProvider(
std::move(attribute_name), domain, attribute_type, creatable, deletable, validator),
stored_type_(stored_type),
std::move(attribute_name), domain, data_type, deletable, validator),
custom_data_access_(custom_data_access),
update_on_change_(update_on_write),
stored_as_named_attribute_(data_type_ == stored_type_)
update_on_change_(update_on_change)
{
}

View File

@ -972,9 +972,14 @@ static std::shared_ptr<DictionaryValue> serialize_geometry_set(const GeometrySet
auto io_references = io_instances->append_array("references");
for (const InstanceReference &reference : instances.references()) {
BLI_assert(reference.type() == InstanceReference::Type::GeometrySet);
io_references->append(
serialize_geometry_set(reference.geometry_set(), blob_writer, blob_sharing));
if (reference.type() == InstanceReference::Type::GeometrySet) {
const GeometrySet &geometry = reference.geometry_set();
io_references->append(serialize_geometry_set(geometry, blob_writer, blob_sharing));
}
else {
/* TODO: Support serializing object and collection references. */
io_references->append(serialize_geometry_set({}, blob_writer, blob_sharing));
}
}
auto io_attributes = serialize_attributes(

View File

@ -58,6 +58,8 @@ CurvesGeometry::CurvesGeometry() : CurvesGeometry(0, 0) {}
CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num)
{
this->runtime = MEM_new<CurvesGeometryRuntime>(__func__);
this->point_num = point_num;
this->curve_num = curve_num;
CustomData_reset(&this->point_data);
@ -67,8 +69,6 @@ CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num)
this->attributes_for_write().add<float3>(
"position", AttrDomain::Point, AttributeInitConstruct());
this->runtime = MEM_new<CurvesGeometryRuntime>(__func__);
if (curve_num > 0) {
this->curve_offsets = static_cast<int *>(
MEM_malloc_arrayN(this->curve_num + 1, sizeof(int), __func__));

View File

@ -91,7 +91,7 @@ void BKE_editmesh_looptris_calc_with_partial_ex(BMEditMesh *em,
const BMeshCalcTessellation_Params *params)
{
BLI_assert(em->looptris.size() == poly_to_tri_count(em->bm->totface, em->bm->totloop));
BLI_assert(!em->looptris.is_empty());
BLI_assert(!(em->bm->totface && em->looptris.is_empty()));
BM_mesh_calc_tessellation_with_partial_ex(em->bm, em->looptris, bmpinfo, params);
}

View File

@ -476,8 +476,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider position("position",
AttrDomain::Point,
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::NonDeletable,
point_access,
tag_component_positions_changed);
@ -485,8 +483,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider radius("radius",
AttrDomain::Point,
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_radii_changed);
@ -494,8 +490,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider id("id",
AttrDomain::Point,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
point_access,
nullptr);
@ -503,8 +497,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider tilt("tilt",
AttrDomain::Point,
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_normals_changed);
@ -512,8 +504,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider handle_right("handle_right",
AttrDomain::Point,
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_positions_changed);
@ -521,8 +511,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider handle_left("handle_left",
AttrDomain::Point,
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_positions_changed);
@ -536,8 +524,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider handle_type_right("handle_type_right",
AttrDomain::Point,
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_topology_changed,
@ -546,8 +532,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider handle_type_left("handle_type_left",
AttrDomain::Point,
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_topology_changed,
@ -556,21 +540,17 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider nurbs_weight("nurbs_weight",
AttrDomain::Point,
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_positions_changed);
static const auto nurbs_order_clamp = mf::build::SI1_SO<int8_t, int8_t>(
"NURBS Order Validate",
[](int8_t value) { return std::max<int8_t>(value, 0); },
[](int8_t value) { return std::max<int8_t>(value, 1); },
mf::build::exec_presets::AllSpanOrSingle());
static BuiltinCustomDataLayerProvider nurbs_order("nurbs_order",
AttrDomain::Curve,
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
curve_access,
tag_component_topology_changed,
@ -585,8 +565,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider normal_mode("normal_mode",
AttrDomain::Curve,
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
curve_access,
tag_component_normals_changed,
@ -595,8 +573,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider custom_normal("custom_normal",
AttrDomain::Point,
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_normals_changed);
@ -610,8 +586,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider nurbs_knots_mode("knots_mode",
AttrDomain::Curve,
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
curve_access,
tag_component_topology_changed,
@ -626,8 +600,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider curve_type("curve_type",
AttrDomain::Curve,
CD_PROP_INT8,
CD_PROP_INT8,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
curve_access,
tag_component_curve_types_changed,
@ -640,8 +612,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider resolution("resolution",
AttrDomain::Curve,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
curve_access,
tag_component_topology_changed,
@ -650,8 +620,6 @@ static ComponentAttributeProviders create_attribute_providers_for_curve()
static BuiltinCustomDataLayerProvider cyclic("cyclic",
AttrDomain::Curve,
CD_PROP_BOOL,
CD_PROP_BOOL,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
curve_access,
tag_component_topology_changed);

View File

@ -133,8 +133,6 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
static BuiltinCustomDataLayerProvider id("id",
AttrDomain::Instance,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
instance_custom_data_access,
nullptr);
@ -142,8 +140,6 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
static BuiltinCustomDataLayerProvider instance_transform("instance_transform",
AttrDomain::Instance,
CD_PROP_FLOAT4X4,
CD_PROP_FLOAT4X4,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::NonDeletable,
instance_custom_data_access,
nullptr);
@ -152,8 +148,6 @@ static ComponentAttributeProviders create_attribute_providers_for_instances()
static BuiltinCustomDataLayerProvider reference_index(".reference_index",
AttrDomain::Instance,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::NonDeletable,
instance_custom_data_access,
tag_component_reference_index_changed);

View File

@ -1010,8 +1010,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
static BuiltinCustomDataLayerProvider position("position",
AttrDomain::Point,
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::NonDeletable,
point_access,
tag_component_positions_changed);
@ -1019,8 +1017,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
static BuiltinCustomDataLayerProvider id("id",
AttrDomain::Point,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
point_access,
nullptr);
@ -1035,8 +1031,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
static BuiltinCustomDataLayerProvider material_index("material_index",
AttrDomain::Face,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
face_access,
nullptr,
@ -1049,8 +1043,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
static BuiltinCustomDataLayerProvider edge_verts(".edge_verts",
AttrDomain::Edge,
CD_PROP_INT32_2D,
CD_PROP_INT32_2D,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::NonDeletable,
edge_access,
nullptr,
@ -1065,8 +1057,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
static BuiltinCustomDataLayerProvider corner_vert(".corner_vert",
AttrDomain::Corner,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::NonDeletable,
corner_access,
nullptr,
@ -1074,8 +1064,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
static BuiltinCustomDataLayerProvider corner_edge(".corner_edge",
AttrDomain::Corner,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::NonDeletable,
corner_access,
nullptr,
@ -1084,8 +1072,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
static BuiltinCustomDataLayerProvider sharp_face("sharp_face",
AttrDomain::Face,
CD_PROP_BOOL,
CD_PROP_BOOL,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
face_access,
tag_component_sharpness_changed);
@ -1093,8 +1079,6 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh()
static BuiltinCustomDataLayerProvider sharp_edge("sharp_edge",
AttrDomain::Edge,
CD_PROP_BOOL,
CD_PROP_BOOL,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
edge_access,
tag_component_sharpness_changed);

View File

@ -147,24 +147,18 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud()
static BuiltinCustomDataLayerProvider position("position",
AttrDomain::Point,
CD_PROP_FLOAT3,
CD_PROP_FLOAT3,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::NonDeletable,
point_access,
tag_component_positions_changed);
static BuiltinCustomDataLayerProvider radius("radius",
AttrDomain::Point,
CD_PROP_FLOAT,
CD_PROP_FLOAT,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
point_access,
tag_component_radius_changed);
static BuiltinCustomDataLayerProvider id("id",
AttrDomain::Point,
CD_PROP_INT32,
CD_PROP_INT32,
BuiltinAttributeProvider::Creatable,
BuiltinAttributeProvider::Deletable,
point_access,
nullptr);

View File

@ -62,11 +62,11 @@ static void pointcloud_init_data(ID *id)
MEMCPY_STRUCT_AFTER(pointcloud, DNA_struct_default_get(PointCloud), id);
pointcloud->runtime = new blender::bke::PointCloudRuntime();
CustomData_reset(&pointcloud->pdata);
pointcloud->attributes_for_write().add<float3>(
"position", blender::bke::AttrDomain::Point, blender::bke::AttributeInitConstruct());
pointcloud->runtime = new blender::bke::PointCloudRuntime();
}
static void pointcloud_copy_data(Main * /*bmain*/,

View File

@ -584,20 +584,6 @@ if(WITH_COMPOSITOR_CPU)
${CMAKE_CURRENT_BINARY_DIR}/operations
)
set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations)
set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h)
add_custom_command(
OUTPUT ${GENSRC}
COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR}
COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC}
DEPENDS smaa_areatex
)
list(APPEND SRC
${GENSRC}
)
unset(GENSRC)
unset(GENSRC_DIR)
if(WITH_OPENIMAGEDENOISE)
add_definitions(-DWITH_OPENIMAGEDENOISE)
add_definitions(-DOIDN_STATIC_LIB)

View File

@ -7,37 +7,41 @@
namespace blender::compositor {
/* Blender encodes the threshold in the [0, 1] range, while the SMAA algorithm expects it in
* the [0, 0.5] range. */
static float get_threshold(const NodeAntiAliasingData *data)
{
return data->threshold / 2.0f;
}
/* Blender encodes the local contrast adaptation factor in the [0, 1] range, while the SMAA
* algorithm expects it in the [0, 10] range. */
static float get_local_contrast_adaptation_factor(const NodeAntiAliasingData *data)
{
return data->contrast_limit * 10.0f;
}
/* Blender encodes the corner rounding factor in the float [0, 1] range, while the SMAA algorithm
* expects it in the integer [0, 100] range. */
static int get_corner_rounding(const NodeAntiAliasingData *data)
{
return int(data->corner_rounding * 100.0f);
}
void AntiAliasingNode::convert_to_operations(NodeConverter &converter,
const CompositorContext & /*context*/) const
{
const bNode *node = this->get_bnode();
const NodeAntiAliasingData *data = (const NodeAntiAliasingData *)node->storage;
/* Edge Detection (First Pass) */
SMAAEdgeDetectionOperation *operation1 = nullptr;
SMAAOperation *operation = new SMAAOperation();
operation->set_threshold(get_threshold(data));
operation->set_local_contrast_adaptation_factor(get_local_contrast_adaptation_factor(data));
operation->set_corner_rounding(get_corner_rounding(data));
converter.add_operation(operation);
operation1 = new SMAAEdgeDetectionOperation();
operation1->set_threshold(data->threshold);
operation1->set_local_contrast_adaptation_factor(data->contrast_limit);
converter.add_operation(operation1);
converter.map_input_socket(get_input_socket(0), operation1->get_input_socket(0));
/* Blending Weight Calculation Pixel Shader (Second Pass) */
SMAABlendingWeightCalculationOperation *operation2 =
new SMAABlendingWeightCalculationOperation();
operation2->set_corner_rounding(data->corner_rounding);
converter.add_operation(operation2);
converter.add_link(operation1->get_output_socket(), operation2->get_input_socket(0));
/* Neighborhood Blending Pixel Shader (Third Pass) */
SMAANeighborhoodBlendingOperation *operation3 = new SMAANeighborhoodBlendingOperation();
converter.add_operation(operation3);
converter.map_input_socket(get_input_socket(0), operation3->get_input_socket(0));
converter.add_link(operation2->get_output_socket(), operation3->get_input_socket(1));
converter.map_output_socket(get_output_socket(0), operation3->get_output_socket());
converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0));
converter.map_output_socket(get_output_socket(0), operation->get_output_socket());
}
} // namespace blender::compositor

View File

@ -18,28 +18,13 @@ void CornerPinNode::convert_to_operations(NodeConverter &converter,
PlaneCornerPinMaskOperation *plane_mask_operation = new PlaneCornerPinMaskOperation();
converter.add_operation(plane_mask_operation);
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
converter.add_operation(smaa_edge_detection);
SMAAOperation *smaa_operation = new SMAAOperation();
converter.add_operation(smaa_operation);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_edge_detection->get_input_socket(0));
smaa_operation->get_input_socket(0));
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
new SMAABlendingWeightCalculationOperation();
converter.add_operation(smaa_blending_weights);
converter.add_link(smaa_edge_detection->get_output_socket(),
smaa_blending_weights->get_input_socket(0));
SMAANeighborhoodBlendingOperation *smaa_neighborhood = new SMAANeighborhoodBlendingOperation();
converter.add_operation(smaa_neighborhood);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_neighborhood->get_input_socket(0));
converter.add_link(smaa_blending_weights->get_output_socket(),
smaa_neighborhood->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(1), smaa_neighborhood->get_output_socket());
converter.map_output_socket(this->get_output_socket(1), smaa_operation->get_output_socket());
PlaneCornerPinWarpImageOperation *warp_image_operation = new PlaneCornerPinWarpImageOperation();
converter.add_operation(warp_image_operation);
@ -62,7 +47,7 @@ void CornerPinNode::convert_to_operations(NodeConverter &converter,
converter.add_operation(set_alpha_operation);
converter.add_link(warp_image_operation->get_output_socket(),
set_alpha_operation->get_input_socket(0));
converter.add_link(smaa_neighborhood->get_output_socket(),
converter.add_link(smaa_operation->get_output_socket(),
set_alpha_operation->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(0),
set_alpha_operation->get_output_socket());

View File

@ -37,26 +37,10 @@ void DilateErodeNode::convert_to_operations(NodeConverter &converter,
converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0));
if (editor_node->custom3 < 2.0f) {
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
converter.add_operation(smaa_edge_detection);
converter.add_link(operation->get_output_socket(), smaa_edge_detection->get_input_socket(0));
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
new SMAABlendingWeightCalculationOperation();
converter.add_operation(smaa_blending_weights);
converter.add_link(smaa_edge_detection->get_output_socket(),
smaa_blending_weights->get_input_socket(0));
SMAANeighborhoodBlendingOperation *smaa_neighborhood =
new SMAANeighborhoodBlendingOperation();
converter.add_operation(smaa_neighborhood);
converter.add_link(operation->get_output_socket(), smaa_neighborhood->get_input_socket(0));
converter.add_link(smaa_blending_weights->get_output_socket(),
smaa_neighborhood->get_input_socket(1));
converter.map_output_socket(get_output_socket(0), smaa_neighborhood->get_output_socket());
SMAAOperation *smaa_operation = new SMAAOperation();
converter.add_operation(smaa_operation);
converter.add_link(operation->get_output_socket(), smaa_operation->get_input_socket(0));
converter.map_output_socket(get_output_socket(0), smaa_operation->get_output_socket());
}
else {
converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0));

View File

@ -27,27 +27,10 @@ void IDMaskNode::convert_to_operations(NodeConverter &converter,
converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0));
}
else {
SMAAEdgeDetectionOperation *operation1 = nullptr;
operation1 = new SMAAEdgeDetectionOperation();
converter.add_operation(operation1);
converter.add_link(operation->get_output_socket(0), operation1->get_input_socket(0));
/* Blending Weight Calculation Pixel Shader (Second Pass). */
SMAABlendingWeightCalculationOperation *operation2 =
new SMAABlendingWeightCalculationOperation();
converter.add_operation(operation2);
converter.add_link(operation1->get_output_socket(), operation2->get_input_socket(0));
/* Neighborhood Blending Pixel Shader (Third Pass). */
SMAANeighborhoodBlendingOperation *operation3 = new SMAANeighborhoodBlendingOperation();
converter.add_operation(operation3);
converter.add_link(operation->get_output_socket(0), operation3->get_input_socket(0));
converter.add_link(operation2->get_output_socket(), operation3->get_input_socket(1));
converter.map_output_socket(get_output_socket(0), operation3->get_output_socket());
SMAAOperation *smaa_operation = new SMAAOperation();
converter.add_operation(smaa_operation);
converter.add_link(operation->get_output_socket(0), smaa_operation->get_input_socket(0));
converter.map_output_socket(get_output_socket(0), smaa_operation->get_output_socket());
}
}

View File

@ -35,28 +35,13 @@ void PlaneTrackDeformNode::convert_to_operations(NodeConverter &converter,
}
converter.add_operation(plane_mask_operation);
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
converter.add_operation(smaa_edge_detection);
SMAAOperation *smaa_operation = new SMAAOperation();
converter.add_operation(smaa_operation);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_edge_detection->get_input_socket(0));
smaa_operation->get_input_socket(0));
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
new SMAABlendingWeightCalculationOperation();
converter.add_operation(smaa_blending_weights);
converter.add_link(smaa_edge_detection->get_output_socket(),
smaa_blending_weights->get_input_socket(0));
SMAANeighborhoodBlendingOperation *smaa_neighborhood = new SMAANeighborhoodBlendingOperation();
converter.add_operation(smaa_neighborhood);
converter.add_link(plane_mask_operation->get_output_socket(),
smaa_neighborhood->get_input_socket(0));
converter.add_link(smaa_blending_weights->get_output_socket(),
smaa_neighborhood->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(1), smaa_neighborhood->get_output_socket());
converter.map_output_socket(this->get_output_socket(1), smaa_operation->get_output_socket());
PlaneTrackWarpImageOperation *warp_image_operation = new PlaneTrackWarpImageOperation();
warp_image_operation->set_movie_clip(clip);
@ -75,7 +60,7 @@ void PlaneTrackDeformNode::convert_to_operations(NodeConverter &converter,
converter.add_operation(set_alpha_operation);
converter.add_link(warp_image_operation->get_output_socket(),
set_alpha_operation->get_input_socket(0));
converter.add_link(smaa_neighborhood->get_output_socket(),
converter.add_link(smaa_operation->get_output_socket(),
set_alpha_operation->get_input_socket(1));
converter.map_output_socket(this->get_output_socket(0),
set_alpha_operation->get_output_socket());

View File

@ -54,25 +54,10 @@ void ZCombineNode::convert_to_operations(NodeConverter &converter,
converter.map_input_socket(get_input_socket(3), maskoperation->get_input_socket(1));
/* Step 2 anti alias mask bit of an expensive operation, but does the trick. */
SMAAEdgeDetectionOperation *smaa_edge_detection = new SMAAEdgeDetectionOperation();
converter.add_operation(smaa_edge_detection);
SMAAOperation *smaa_operation = new SMAAOperation();
converter.add_operation(smaa_operation);
converter.add_link(maskoperation->get_output_socket(),
smaa_edge_detection->get_input_socket(0));
SMAABlendingWeightCalculationOperation *smaa_blending_weights =
new SMAABlendingWeightCalculationOperation();
converter.add_operation(smaa_blending_weights);
converter.add_link(smaa_edge_detection->get_output_socket(),
smaa_blending_weights->get_input_socket(0));
SMAANeighborhoodBlendingOperation *smaa_neighborhood = new SMAANeighborhoodBlendingOperation();
converter.add_operation(smaa_neighborhood);
converter.add_link(maskoperation->get_output_socket(), smaa_neighborhood->get_input_socket(0));
converter.add_link(smaa_blending_weights->get_output_socket(),
smaa_neighborhood->get_input_socket(1));
converter.add_link(maskoperation->get_output_socket(), smaa_operation->get_input_socket(0));
/* use mask to blend between the input colors. */
ZCombineMaskOperation *zcombineoperation = this->get_bnode()->custom1 ?
@ -80,7 +65,7 @@ void ZCombineNode::convert_to_operations(NodeConverter &converter,
new ZCombineMaskOperation();
converter.add_operation(zcombineoperation);
converter.add_link(smaa_neighborhood->get_output_socket(),
converter.add_link(smaa_operation->get_output_socket(),
zcombineoperation->get_input_socket(0));
converter.map_input_socket(get_input_socket(0), zcombineoperation->get_input_socket(1));
converter.map_input_socket(get_input_socket(2), zcombineoperation->get_input_socket(2));

File diff suppressed because it is too large Load Diff

View File

@ -4,89 +4,38 @@
#pragma once
#include "COM_MultiThreadedOperation.h"
#include "COM_NodeOperation.h"
namespace blender::compositor {
/*-----------------------------------------------------------------------------*/
/* Edge Detection (First Pass) */
class SMAAEdgeDetectionOperation : public MultiThreadedOperation {
class SMAAOperation : public NodeOperation {
protected:
float threshold_;
float contrast_limit_;
float threshold_ = 0.1f;
float local_contrast_adaptation_factor_ = 2.0f;
int corner_rounding_ = 25;
public:
SMAAEdgeDetectionOperation();
SMAAOperation();
void set_threshold(float threshold);
void set_threshold(float threshold)
{
threshold_ = threshold;
}
void set_local_contrast_adaptation_factor(float factor);
void set_local_contrast_adaptation_factor(float factor)
{
local_contrast_adaptation_factor_ = factor;
}
void set_corner_rounding(int corner_rounding)
{
corner_rounding_ = corner_rounding;
}
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
/*-----------------------------------------------------------------------------*/
/* Blending Weight Calculation (Second Pass) */
class SMAABlendingWeightCalculationOperation : public MultiThreadedOperation {
private:
std::function<void(int x, int y, float *out)> sample_image_fn_;
int corner_rounding_;
public:
SMAABlendingWeightCalculationOperation();
void set_corner_rounding(float rounding);
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_started(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
private:
/* Diagonal Search Functions */
/**
* These functions allows to perform diagonal pattern searches.
*/
int search_diag1(int x, int y, int dir, bool *r_found);
int search_diag2(int x, int y, int dir, bool *r_found);
/**
* This searches for diagonal patterns and returns the corresponding weights.
*/
void calculate_diag_weights(int x, int y, const float edges[2], float weights[2]);
bool is_vertical_search_unneeded(int x, int y);
/* Horizontal/Vertical Search Functions */
int search_xleft(int x, int y);
int search_xright(int x, int y);
int search_yup(int x, int y);
int search_ydown(int x, int y);
/* Corner Detection Functions */
void detect_horizontal_corner_pattern(
float weights[2], int left, int right, int y, int d1, int d2);
void detect_vertical_corner_pattern(
float weights[2], int x, int top, int bottom, int d1, int d2);
};
/*-----------------------------------------------------------------------------*/
/* Neighborhood Blending (Third Pass) */
class SMAANeighborhoodBlendingOperation : public MultiThreadedOperation {
public:
SMAANeighborhoodBlendingOperation();
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override;
void update_memory_buffer_partial(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
void update_memory_buffer(MemoryBuffer *output,
const rcti &area,
Span<MemoryBuffer *> inputs) override;
};
} // namespace blender::compositor

View File

@ -7,6 +7,8 @@
#include "COM_ConstantOperation.h"
#include "COM_MultiThreadedOperation.h"
#include <mutex>
namespace blender::compositor {
class TranslateOperation : public MultiThreadedOperation {
@ -21,6 +23,8 @@ class TranslateOperation : public MultiThreadedOperation {
bool is_delta_set_;
bool is_relative_;
std::mutex mutex_;
protected:
MemoryBufferExtend x_extend_mode_;
MemoryBufferExtend y_extend_mode_;
@ -50,6 +54,11 @@ class TranslateOperation : public MultiThreadedOperation {
inline void ensure_delta()
{
if (!is_delta_set_) {
std::unique_lock lock(mutex_);
if (is_delta_set_) {
return;
}
delta_x_ = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f);
delta_y_ = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f);
if (get_is_relative()) {

View File

@ -15,6 +15,7 @@
#include "GPU_shader.hh"
#include "GPU_texture.hh"
#include "IMB_colormanagement.hh"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
@ -56,74 +57,6 @@ bool operator==(const CachedImageKey &a, const CachedImageKey &b)
* Cached Image.
*/
/* Returns a new texture of the given format and precision preprocessed using the given shader. The
* input texture is freed. */
static GPUTexture *preprocess_texture(Context &context,
GPUTexture *input_texture,
eGPUTextureFormat target_format,
ResultPrecision precision,
const char *shader_name)
{
const int2 size = int2(GPU_texture_width(input_texture), GPU_texture_height(input_texture));
GPUTexture *preprocessed_texture = GPU_texture_create_2d(
"Cached Image", size.x, size.y, 1, target_format, GPU_TEXTURE_USAGE_GENERAL, nullptr);
GPUShader *shader = context.get_shader(shader_name, precision);
GPU_shader_bind(shader);
const int input_unit = GPU_shader_get_sampler_binding(shader, "input_tx");
GPU_texture_bind(input_texture, input_unit);
const int image_unit = GPU_shader_get_sampler_binding(shader, "output_img");
GPU_texture_image_bind(preprocessed_texture, image_unit);
compute_dispatch_threads_at_least(shader, size);
GPU_shader_unbind();
GPU_texture_unbind(input_texture);
GPU_texture_image_unbind(preprocessed_texture);
GPU_texture_free(input_texture);
return preprocessed_texture;
}
/* Compositor images are expected to be always pre-multiplied, so identify if the GPU texture
* returned by the IMB module is straight and needs to be pre-multiplied. An exception is when
* the image has an alpha mode of channel packed or alpha ignore, in which case, we always ignore
* pre-multiplication. */
static bool should_premultiply_alpha(Image *image, ImBuf *image_buffer)
{
if (ELEM(image->alpha_mode, IMA_ALPHA_CHANNEL_PACKED, IMA_ALPHA_IGNORE)) {
return false;
}
return !BKE_image_has_gpu_texture_premultiplied_alpha(image, image_buffer);
}
/* Get a suitable texture format supported by the compositor given the format of the texture
* returned by the IMB module. See imb_gpu_get_format for the formats that needs to be handled. */
static eGPUTextureFormat get_compatible_texture_format(eGPUTextureFormat original_format)
{
switch (original_format) {
case GPU_R16F:
case GPU_R32F:
case GPU_RGBA16F:
case GPU_RGBA32F:
return original_format;
case GPU_R8:
return GPU_R16F;
case GPU_RGBA8:
case GPU_SRGB8_A8:
return GPU_RGBA16F;
default:
break;
}
BLI_assert_unreachable();
return original_format;
}
/* Get the selected render layer selected assuming the image is a multilayer image. */
static RenderLayer *get_render_layer(Image *image, ImageUser &image_user)
{
@ -205,6 +138,56 @@ static ImageUser compute_image_user_for_pass(Context &context,
return image_user_for_pass;
}
/* The image buffer might be stored as an sRGB 8-bit image, while the compositor expects linear
* float images, so compute a linear float buffer for the image buffer. This will also do linear
* space conversion and alpha pre-multiplication as needed. We could store those images in sRGB GPU
* textures and let the GPU do the linear space conversion, but the issues is that we don't control
* how the GPU does the conversion and so we get tiny differences across CPU and GPU compositing,
* and potentially even across GPUs/Drivers. Further, if alpha pre-multiplication is needed, we
* would need to do it ourself, which means alpha pre-multiplication will happen before linear
* space conversion, which would produce yet another difference. So we just do everything on the
* CPU, since this is already a cached resource.
*
* To avoid conflicts with other threads, create a new image buffer and assign all the necessary
* information to it, with IB_DO_NOT_TAKE_OWNERSHIP for buffers since a deep copy is not needed.
*
* The caller should free the returned image buffer. */
static ImBuf *compute_linear_buffer(ImBuf *image_buffer)
{
/* Do not pass the flags to the allocation function to avoid buffer allocation, but assign them
* after to retain important information like precision and alpha mode. */
ImBuf *linear_image_buffer = IMB_allocImBuf(
image_buffer->x, image_buffer->y, image_buffer->planes, 0);
linear_image_buffer->flags = image_buffer->flags;
/* Assign the float buffer if it exists, as well as its number of channels. */
IMB_assign_float_buffer(
linear_image_buffer, image_buffer->float_buffer, IB_DO_NOT_TAKE_OWNERSHIP);
linear_image_buffer->channels = image_buffer->channels;
/* If no float buffer exists, assign it then compute a float buffer from it. This is the main
* call of this function. */
if (!linear_image_buffer->float_buffer.data) {
IMB_assign_byte_buffer(
linear_image_buffer, image_buffer->byte_buffer, IB_DO_NOT_TAKE_OWNERSHIP);
IMB_float_from_rect(linear_image_buffer);
}
/* If the image buffer contained compressed data, assign them as well, but only if the color
* space of the buffer is linear or data, since we need linear data and can't preprocess the
* compressed buffer. If not, we fallback to the float buffer already assigned, which is
* guaranteed to exist as a fallback for compressed textures. */
const bool is_suitable_compressed_color_space =
IMB_colormanagement_space_is_data(image_buffer->byte_buffer.colorspace) ||
IMB_colormanagement_space_is_scene_linear(image_buffer->byte_buffer.colorspace);
if (image_buffer->ftype == IMB_FTYPE_DDS && is_suitable_compressed_color_space) {
linear_image_buffer->ftype = IMB_FTYPE_DDS;
IMB_assign_dds_data(linear_image_buffer, image_buffer->dds_data, IB_DO_NOT_TAKE_OWNERSHIP);
}
return linear_image_buffer;
}
CachedImage::CachedImage(Context &context,
Image *image,
ImageUser *image_user,
@ -227,34 +210,12 @@ CachedImage::CachedImage(Context &context,
context, image, image_user, pass_name);
ImBuf *image_buffer = BKE_image_acquire_ibuf(image, &image_user_for_pass, nullptr);
const bool is_premultiplied = BKE_image_has_gpu_texture_premultiplied_alpha(image, image_buffer);
texture_ = IMB_create_gpu_texture("Image Texture", image_buffer, true, is_premultiplied);
ImBuf *linear_image_buffer = compute_linear_buffer(image_buffer);
texture_ = IMB_create_gpu_texture("Image Texture", linear_image_buffer, true, true);
GPU_texture_update_mipmap_chain(texture_);
const eGPUTextureFormat original_format = GPU_texture_format(texture_);
const eGPUTextureFormat target_format = get_compatible_texture_format(original_format);
const ResultType result_type = Result::type(target_format);
const ResultPrecision precision = Result::precision(target_format);
/* The GPU image returned by the IMB module can be in a format not supported by the compositor,
* or it might need pre-multiplication, so preprocess them first. */
if (result_type == ResultType::Color && should_premultiply_alpha(image, image_buffer)) {
texture_ = preprocess_texture(
context, texture_, target_format, precision, "compositor_premultiply_alpha");
}
else if (original_format != target_format) {
const char *conversion_shader_name = result_type == ResultType::Float ?
"compositor_convert_float_to_float" :
"compositor_convert_color_to_color";
texture_ = preprocess_texture(
context, texture_, target_format, precision, conversion_shader_name);
}
/* Set the alpha to 1 using swizzling if alpha is ignored. */
if (result_type == ResultType::Color && image->alpha_mode == IMA_ALPHA_IGNORE) {
GPU_texture_swizzle_set(texture_, "rgb1");
}
IMB_freeImBuf(linear_image_buffer);
BKE_image_release_ibuf(image, image_buffer, nullptr);
}

View File

@ -3674,138 +3674,136 @@ size_t ANIM_animdata_filter(bAnimContext *ac,
void *data,
eAnimCont_Types datatype)
{
if (!data || !anim_data) {
return 0;
}
size_t items = 0;
switch (datatype) {
/* Action-Editing Modes */
case ANIMCONT_ACTION: /* 'Action Editor' */
{
Object *obact = ac->obact;
SpaceAction *saction = (SpaceAction *)ac->sl;
bDopeSheet *ads = (saction) ? &saction->ads : nullptr;
/* only filter data if there's somewhere to put it */
if (data && anim_data) {
/* firstly filter the data */
switch (datatype) {
/* Action-Editing Modes */
case ANIMCONT_ACTION: /* 'Action Editor' */
{
Object *obact = ac->obact;
SpaceAction *saction = (SpaceAction *)ac->sl;
bDopeSheet *ads = (saction) ? &saction->ads : nullptr;
/* specially check for AnimData filter, see #36687. */
if (UNLIKELY(filter_mode & ANIMFILTER_ANIMDATA)) {
/* all channels here are within the same AnimData block, hence this special case */
if (LIKELY(obact->adt)) {
ANIMCHANNEL_NEW_CHANNEL(obact->adt, ANIMTYPE_ANIMDATA, (ID *)obact, nullptr);
}
/* specially check for AnimData filter, see #36687. */
if (UNLIKELY(filter_mode & ANIMFILTER_ANIMDATA)) {
/* all channels here are within the same AnimData block, hence this special case */
if (LIKELY(obact->adt)) {
ANIMCHANNEL_NEW_CHANNEL(obact->adt, ANIMTYPE_ANIMDATA, (ID *)obact, nullptr);
}
else {
/* The check for the DopeSheet summary is included here
* since the summary works here too. */
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
items += animfilter_action(
ac, anim_data, ads, static_cast<bAction *>(data), filter_mode, (ID *)obact);
}
}
break;
}
case ANIMCONT_SHAPEKEY: /* 'ShapeKey Editor' */
{
Key *key = (Key *)data;
/* specially check for AnimData filter, see #36687. */
if (UNLIKELY(filter_mode & ANIMFILTER_ANIMDATA)) {
/* all channels here are within the same AnimData block, hence this special case */
if (LIKELY(key->adt)) {
ANIMCHANNEL_NEW_CHANNEL(key->adt, ANIMTYPE_ANIMDATA, (ID *)key, nullptr);
}
}
else {
/* The check for the DopeSheet summary is included here
* since the summary works here too. */
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
items = animdata_filter_shapekey(ac, anim_data, key, filter_mode);
}
}
break;
}
/* Modes for Specialty Data Types (i.e. not keyframes) */
case ANIMCONT_GPENCIL: {
else {
/* The check for the DopeSheet summary is included here
* since the summary works here too. */
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
if (U.experimental.use_grease_pencil_version3) {
items = animdata_filter_grease_pencil(ac, anim_data, filter_mode);
}
else {
items = animdata_filter_gpencil_legacy(ac, anim_data, data, filter_mode);
}
items += animfilter_action(
ac, anim_data, ads, static_cast<bAction *>(data), filter_mode, (ID *)obact);
}
break;
}
case ANIMCONT_MASK: {
break;
}
case ANIMCONT_SHAPEKEY: /* 'ShapeKey Editor' */
{
Key *key = (Key *)data;
/* specially check for AnimData filter, see #36687. */
if (UNLIKELY(filter_mode & ANIMFILTER_ANIMDATA)) {
/* all channels here are within the same AnimData block, hence this special case */
if (LIKELY(key->adt)) {
ANIMCHANNEL_NEW_CHANNEL(key->adt, ANIMTYPE_ANIMDATA, (ID *)key, nullptr);
}
}
else {
/* The check for the DopeSheet summary is included here
* since the summary works here too. */
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
items = animdata_filter_mask(ac->bmain, anim_data, data, filter_mode);
items = animdata_filter_shapekey(ac, anim_data, key, filter_mode);
}
break;
}
/* DopeSheet Based Modes */
case ANIMCONT_DOPESHEET: /* 'DopeSheet Editor' */
{
/* the DopeSheet editor is the primary place where the DopeSheet summaries are useful */
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
items += animdata_filter_dopesheet(
ac, anim_data, static_cast<bDopeSheet *>(data), filter_mode);
}
break;
}
case ANIMCONT_FCURVES: /* Graph Editor -> F-Curves/Animation Editing */
case ANIMCONT_DRIVERS: /* Graph Editor -> Drivers Editing */
case ANIMCONT_NLA: /* NLA Editor */
{
/* all of these editors use the basic DopeSheet data for filtering options,
* but don't have all the same features */
items = animdata_filter_dopesheet(
ac, anim_data, static_cast<bDopeSheet *>(data), filter_mode);
break;
}
/* Timeline Mode - Basically the same as dopesheet,
* except we only have the summary for now */
case ANIMCONT_TIMELINE: {
/* the DopeSheet editor is the primary place where the DopeSheet summaries are useful */
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
items += animdata_filter_dopesheet(
ac, anim_data, static_cast<bDopeSheet *>(data), filter_mode);
}
break;
}
/* Special/Internal Use */
case ANIMCONT_CHANNEL: /* animation channel */
{
bDopeSheet *ads = ac->ads;
/* based on the channel type, filter relevant data for this */
items = animdata_filter_animchan(
ac, anim_data, ads, static_cast<bAnimListElem *>(data), filter_mode);
break;
}
/* unhandled */
default: {
printf("ANIM_animdata_filter() - Invalid datatype argument %i\n", datatype);
break;
}
break;
}
/* remove any 'weedy' entries */
items = animdata_filter_remove_invalid(anim_data);
/* Modes for Specialty Data Types (i.e. not keyframes) */
case ANIMCONT_GPENCIL: {
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
if (U.experimental.use_grease_pencil_version3) {
items = animdata_filter_grease_pencil(ac, anim_data, filter_mode);
}
else {
items = animdata_filter_gpencil_legacy(ac, anim_data, data, filter_mode);
}
}
break;
}
case ANIMCONT_MASK: {
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
items = animdata_filter_mask(ac->bmain, anim_data, data, filter_mode);
}
break;
}
/* remove duplicates (if required) */
if (filter_mode & ANIMFILTER_NODUPLIS) {
items = animdata_filter_remove_duplis(anim_data);
/* DopeSheet Based Modes */
case ANIMCONT_DOPESHEET: /* 'DopeSheet Editor' */
{
/* the DopeSheet editor is the primary place where the DopeSheet summaries are useful */
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
items += animdata_filter_dopesheet(
ac, anim_data, static_cast<bDopeSheet *>(data), filter_mode);
}
break;
}
case ANIMCONT_FCURVES: /* Graph Editor -> F-Curves/Animation Editing */
case ANIMCONT_DRIVERS: /* Graph Editor -> Drivers Editing */
case ANIMCONT_NLA: /* NLA Editor */
{
/* all of these editors use the basic DopeSheet data for filtering options,
* but don't have all the same features */
items = animdata_filter_dopesheet(
ac, anim_data, static_cast<bDopeSheet *>(data), filter_mode);
break;
}
/* Timeline Mode - Basically the same as dopesheet,
* except we only have the summary for now */
case ANIMCONT_TIMELINE: {
/* the DopeSheet editor is the primary place where the DopeSheet summaries are useful */
if (animdata_filter_dopesheet_summary(ac, anim_data, filter_mode, &items)) {
items += animdata_filter_dopesheet(
ac, anim_data, static_cast<bDopeSheet *>(data), filter_mode);
}
break;
}
/* Special/Internal Use */
case ANIMCONT_CHANNEL: /* animation channel */
{
bDopeSheet *ads = ac->ads;
/* based on the channel type, filter relevant data for this */
items = animdata_filter_animchan(
ac, anim_data, ads, static_cast<bAnimListElem *>(data), filter_mode);
break;
}
/* unhandled */
default: {
printf("ANIM_animdata_filter() - Invalid datatype argument %i\n", datatype);
break;
}
}
/* return the number of items in the list */
/* remove any 'weedy' entries */
items = animdata_filter_remove_invalid(anim_data);
/* remove duplicates (if required) */
if (filter_mode & ANIMFILTER_NODUPLIS) {
items = animdata_filter_remove_duplis(anim_data);
}
return items;
}

View File

@ -582,7 +582,7 @@ static void ASSET_OT_catalog_delete(wmOperatorType *ot)
static asset_system::AssetCatalogService *get_catalog_service(bContext *C)
{
const SpaceFile *sfile = CTX_wm_space_file(C);
if (!sfile) {
if (!sfile || ED_fileselect_is_file_browser(sfile)) {
return nullptr;
}

View File

@ -1406,7 +1406,7 @@ static void GREASE_PENCIL_OT_clean_loose(wmOperatorType *ot)
ot->invoke = WM_operator_props_popup_confirm;
ot->exec = grease_pencil_clean_loose_exec;
ot->poll = editable_grease_pencil_poll;
ot->poll = active_grease_pencil_layer_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;

View File

@ -53,8 +53,8 @@ static int cachefile_open_invoke(bContext *C, wmOperator *op, const wmEvent * /*
char filepath[FILE_MAX];
Main *bmain = CTX_data_main(C);
STRNCPY(filepath, BKE_main_blendfile_path(bmain));
BLI_path_extension_replace(filepath, sizeof(filepath), ".abc");
/* Default to the same directory as the blend file. */
BLI_path_split_dir_part(BKE_main_blendfile_path(bmain), filepath, sizeof(filepath));
RNA_string_set(op->ptr, "filepath", filepath);
}
@ -119,7 +119,7 @@ void CACHEFILE_OT_open(wmOperatorType *ot)
ot->cancel = open_cancel;
WM_operator_properties_filesel(ot,
FILE_TYPE_ALEMBIC | FILE_TYPE_FOLDER,
FILE_TYPE_ALEMBIC | FILE_TYPE_USD | FILE_TYPE_FOLDER,
FILE_BLENDER,
FILE_OPENFILE,
WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH,
@ -163,8 +163,8 @@ static int cachefile_layer_open_invoke(bContext *C, wmOperator *op, const wmEven
char filepath[FILE_MAX];
Main *bmain = CTX_data_main(C);
STRNCPY(filepath, BKE_main_blendfile_path(bmain));
BLI_path_extension_replace(filepath, sizeof(filepath), ".abc");
/* Default to the same directory as the blend file. */
BLI_path_split_dir_part(BKE_main_blendfile_path(bmain), filepath, sizeof(filepath));
RNA_string_set(op->ptr, "filepath", filepath);
}
@ -215,7 +215,7 @@ void CACHEFILE_OT_layer_add(wmOperatorType *ot)
ot->exec = cachefile_layer_add_exec;
WM_operator_properties_filesel(ot,
FILE_TYPE_ALEMBIC | FILE_TYPE_FOLDER,
FILE_TYPE_ALEMBIC | FILE_TYPE_USD | FILE_TYPE_FOLDER,
FILE_BLENDER,
FILE_OPENFILE,
WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH,

View File

@ -3584,7 +3584,10 @@ static int edbm_remove_doubles_exec(bContext *C, wmOperator *op)
}
}
BKE_reportf(op->reports, RPT_INFO, "Removed %d vertice(s)", count_multi);
BKE_reportf(op->reports,
RPT_INFO,
count_multi == 1 ? "Removed %d vertex" : "Removed %d vertices",
count_multi);
return OPERATOR_FINISHED;
}

View File

@ -1613,7 +1613,6 @@ static int shade_smooth_exec(bContext *C, wmOperator *op)
const float angle = RNA_float_get(op->ptr, "angle");
bke::mesh_sharp_edges_set_from_angle(mesh, angle, keep_sharp_edges);
}
mesh.tag_sharpness_changed();
BKE_mesh_batch_cache_dirty_tag(static_cast<Mesh *>(ob->data), BKE_MESH_BATCH_DIRTY_ALL);
changed = true;
}

View File

@ -506,7 +506,7 @@ void ED_view3d_gizmo_mesh_preselect_get_active(const bContext *C,
/* weak, allocate an array just to access the index. */
Base *base = nullptr;
Object *obedit = nullptr;
{
if (object_index != -1) {
Vector<Base *> bases = BKE_view_layer_array_from_bases_in_edit_mode(
scene, view_layer, CTX_wm_view3d(C));
if (object_index < bases.size()) {

View File

@ -705,11 +705,15 @@ void Transform_Properties(wmOperatorType *ot, int flags)
RNA_def_boolean(ot->srna, "use_snap_project", false, "Project Individual Elements", "");
/* TODO(@gfxcoder): Rename `snap_target` to `snap_source` to avoid previous ambiguity of
* "target" (now, "source" is geometry to be moved and "target" is geometry to which moved
* geometry is snapped). Use "Source snap point" and "Point on source that will snap to
* target" for name and description, respectively. */
prop = RNA_def_enum(ot->srna, "snap_target", rna_enum_snap_source_items, 0, "Snap With", "");
/* TODO(@gfxcoder): Rename `snap_target` to `snap_base` to avoid previous ambiguity of
* "target" (now, "base" or "source" is geometry to be moved and "target" is geometry to
* which moved geometry is snapped). */
prop = RNA_def_enum(ot->srna,
"snap_target",
rna_enum_snap_source_items,
0,
"Snap Base",
"Point on source that will snap to target");
RNA_def_property_flag(prop, PROP_HIDDEN);
/* Target selection. */

View File

@ -13,6 +13,6 @@ bke::GeometrySet join_geometries(Span<bke::GeometrySet> geometries,
const bke::AnonymousAttributePropagationInfo &propagation_info);
void join_attributes(Span<const bke::GeometryComponent *> src_components,
bke::GeometryComponent &r_result,
Span<StringRef> ignored_attributes = {});
Span<StringRef> ignored_attributes,
bke::GeometryComponent &r_result);
} // namespace blender::geometry

View File

@ -62,16 +62,8 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
const RealizeInstancesOptions &options);
/**
* Join all instances into a single geometry component for each geometry type. For example, all
* mesh instances (including the already realized mesh) are joined into a single mesh. The output
* geometry set does not contain any instances. If the input did not contain any instances, it is
* returned directly.
*
* The `id` attribute has special handling. If there is an id attribute on any component, the
* output will contain an `id` attribute as well. The output id is generated by mixing/hashing ids
* of instances and of the instanced geometry data.
*
* Will realize only the instances chosen by varied_depth_option to there chosen depth.
* Same #realize_instances but will realize only the instances chosen by
* varied_depth_option to there chosen depth.
*/
bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
const RealizeInstancesOptions &options,

View File

@ -74,8 +74,8 @@ static void fill_new_attribute(const Span<const GeometryComponent *> src_compone
}
void join_attributes(const Span<const GeometryComponent *> src_components,
GeometryComponent &r_result,
const Span<StringRef> ignored_attributes)
const Span<StringRef> ignored_attributes,
GeometryComponent &r_result)
{
const Map<AttributeIDRef, AttributeMetaData> info = get_final_attribute_info(src_components,
ignored_attributes);
@ -129,7 +129,7 @@ static void join_instances(const Span<const GeometryComponent *> src_components,
r_result.replace_instances(dst_instances.release());
auto &dst_component = r_result.get_component_for_write<bke::InstancesComponent>();
join_attributes(src_components, dst_component, {".reference_index"});
join_attributes(src_components, {".reference_index"}, dst_component);
}
static void join_volumes(const Span<const GeometryComponent *> /*src_components*/,

View File

@ -298,7 +298,7 @@ struct InstanceContext {
curves(gather_info.curves.attributes.size()),
instances(gather_info.instances_attriubutes.size())
{
//empty
// empty
}
};
@ -823,12 +823,11 @@ static bool attribute_foreach(const bke::GeometrySet &geometry_set,
return is_relevant;
}
/**
* Based on bke::GeometrySet::gather_attributes_for_propagation.
* Specialized for Specialized attribute_foreach to get:
* current_depth, depth_target, instance_depth and selection.
*/
*/
void static gather_attributes_for_propagation(
bke::GeometrySet re_geometry_set,
const Span<bke::GeometryComponent::Type> component_types,
@ -869,7 +868,8 @@ void static gather_attributes_for_propagation(
AttrDomain domain = meta_data.domain;
if (dst_component_type != bke::GeometryComponent::Type::Instance &&
domain == AttrDomain::Instance) {
domain == AttrDomain::Instance)
{
domain = AttrDomain::Point;
}
@ -1014,7 +1014,7 @@ static void execute_instances_tasks(
}
join_attributes(
for_join_attributes, dst_component, {"position", ".reference_index", "instance_transform"});
for_join_attributes, {"position", ".reference_index", "instance_transform"}, dst_component);
}
/** \} */
@ -1990,9 +1990,7 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
VariedDepthOption all_instances;
all_instances.depths = VArray<int>::ForSingle(VariedDepthOption::MAX_DEPTH,
geometry_set.get_instances()->instances_num());
IndexMaskMemory memory;
all_instances.selection = IndexMask::from_bools(
VArray<bool>::ForSingle(true, geometry_set.get_instances()->instances_num()), memory);
all_instances.selection = IndexMask(geometry_set.get_instances()->instances_num());
return realize_instances(geometry_set, options, all_instances);
}
@ -2049,12 +2047,8 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
const float4x4 transform = float4x4::identity();
InstanceContext attribute_fallbacks(gather_info);
gather_realize_tasks_recursive(gather_info,
0,
VariedDepthOption::MAX_DEPTH,
geometry_set,
transform,
attribute_fallbacks);
gather_realize_tasks_recursive(
gather_info, 0, VariedDepthOption::MAX_DEPTH, geometry_set, transform, attribute_fallbacks);
bke::GeometrySet new_geometry_set;
execute_instances_tasks(gather_info.instances.instances_components_to_merge,
@ -2068,22 +2062,22 @@ bke::GeometrySet realize_instances(bke::GeometrySet geometry_set,
* multi-threading (overhead). */
const int64_t approximate_used_bytes_num = total_points_num * 32;
threading::memory_bandwidth_bound_task(approximate_used_bytes_num, [&]() {
execute_realize_pointcloud_tasks(options.keep_original_ids,
all_pointclouds_info,
gather_info.r_tasks.pointcloud_tasks,
all_pointclouds_info.attributes,
new_geometry_set);
execute_realize_mesh_tasks(options.keep_original_ids,
all_meshes_info,
gather_info.r_tasks.mesh_tasks,
all_meshes_info.attributes,
all_meshes_info.materials,
new_geometry_set);
execute_realize_curve_tasks(options.keep_original_ids,
all_curves_info,
gather_info.r_tasks.curve_tasks,
all_curves_info.attributes,
new_geometry_set);
execute_realize_pointcloud_tasks(options.keep_original_ids,
all_pointclouds_info,
gather_info.r_tasks.pointcloud_tasks,
all_pointclouds_info.attributes,
new_geometry_set);
execute_realize_mesh_tasks(options.keep_original_ids,
all_meshes_info,
gather_info.r_tasks.mesh_tasks,
all_meshes_info.attributes,
all_meshes_info.materials,
new_geometry_set);
execute_realize_curve_tasks(options.keep_original_ids,
all_curves_info,
gather_info.r_tasks.curve_tasks,
all_curves_info.attributes,
new_geometry_set);
});
if (gather_info.r_tasks.first_volume) {
new_geometry_set.add(*gather_info.r_tasks.first_volume);

View File

@ -527,6 +527,8 @@ void gpu::MTLTexture::update_sub(
}
}
const bool is_compressed = (format_flag_ & GPU_FORMAT_COMPRESSED);
@autoreleasepool {
/* Determine totalsize of INPUT Data. */
int num_channels = to_component_len(format_);
@ -593,10 +595,12 @@ void gpu::MTLTexture::update_sub(
false /* Not a clear. */
};
/* Determine whether we can do direct BLIT or not. */
/* Determine whether we can do direct BLIT or not. For compressed textures,
* always assume a direct blit (input data pretends to be float, but it is
* not). */
bool can_use_direct_blit = true;
if (expected_dst_bytes_per_pixel != input_bytes_per_pixel ||
num_channels != destination_num_channels)
if (!is_compressed && (expected_dst_bytes_per_pixel != input_bytes_per_pixel ||
num_channels != destination_num_channels))
{
can_use_direct_blit = false;
}
@ -620,7 +624,7 @@ void gpu::MTLTexture::update_sub(
/* Safety Checks. */
if (type == GPU_DATA_UINT_24_8 || type == GPU_DATA_10_11_11_REV ||
type == GPU_DATA_2_10_10_10_REV)
type == GPU_DATA_2_10_10_10_REV || is_compressed)
{
BLI_assert(can_use_direct_blit &&
"Special input data type must be a 1-1 mapping with destination texture as it "
@ -755,6 +759,12 @@ void gpu::MTLTexture::update_sub(
extent[0] :
ctx->pipeline_state.unpack_row_length);
size_t bytes_per_image = bytes_per_row;
if (is_compressed) {
size_t block_size = to_block_size(format_);
size_t blocks_x = divide_ceil_u(extent[0], 4);
bytes_per_row = blocks_x * block_size;
bytes_per_image = bytes_per_row;
}
int max_array_index = ((type_ == GPU_TEXTURE_1D_ARRAY) ? extent[1] : 1);
for (int array_index = 0; array_index < max_array_index; array_index++) {
@ -827,6 +837,13 @@ void gpu::MTLTexture::update_sub(
extent[0] :
ctx->pipeline_state.unpack_row_length);
size_t bytes_per_image = bytes_per_row * extent[1];
if (is_compressed) {
size_t block_size = to_block_size(format_);
size_t blocks_x = divide_ceil_u(extent[0], 4);
size_t blocks_y = divide_ceil_u(extent[1], 4);
bytes_per_row = blocks_x * block_size;
bytes_per_image = bytes_per_row * blocks_y;
}
size_t texture_array_relative_offset = 0;
int base_slice = (type_ == GPU_TEXTURE_2D_ARRAY) ? offset[2] : 0;
@ -1218,6 +1235,12 @@ void gpu::MTLTexture::ensure_mipmaps(int miplvl)
void gpu::MTLTexture::generate_mipmap()
{
/* Compressed textures allow users to provide their own custom mipmaps. And
* we can't generate them at runtime anyway. */
if (format_flag_ & GPU_FORMAT_COMPRESSED) {
return;
}
/* Fetch Active Context. */
MTLContext *ctx = static_cast<MTLContext *>(unwrap(GPU_context_active_get()));
BLI_assert(ctx);

View File

@ -160,13 +160,17 @@ MTLPixelFormat gpu_texture_format_to_metal(eGPUTextureFormat tex_format)
return MTLPixelFormatR8Snorm;
/* Special formats, texture only. */
case GPU_SRGB8_A8_DXT1:
return MTLPixelFormatBC1_RGBA_sRGB;
case GPU_SRGB8_A8_DXT3:
return MTLPixelFormatBC2_RGBA_sRGB;
case GPU_SRGB8_A8_DXT5:
return MTLPixelFormatBC3_RGBA_sRGB;
case GPU_RGBA8_DXT1:
return MTLPixelFormatBC1_RGBA;
case GPU_RGBA8_DXT3:
return MTLPixelFormatBC2_RGBA;
case GPU_RGBA8_DXT5:
BLI_assert_msg(false, "Compressed texture not implemented yet!\n");
return MTLPixelFormatRGBA8Unorm;
return MTLPixelFormatBC3_RGBA;
case GPU_SRGB8:
/* 24-Bit pixel format are not supported. Emulate using a padded type with alpha. */
return MTLPixelFormatRGBA8Unorm_sRGB;
@ -247,6 +251,14 @@ size_t get_mtl_format_bytesize(MTLPixelFormat tex_format)
return 4;
case MTLPixelFormatDepth16Unorm:
return 2;
case MTLPixelFormatBC1_RGBA:
case MTLPixelFormatBC1_RGBA_sRGB:
return 1; /* Note: not quite correct (BC1 is 0.5 bpp). */
case MTLPixelFormatBC2_RGBA:
case MTLPixelFormatBC2_RGBA_sRGB:
case MTLPixelFormatBC3_RGBA:
case MTLPixelFormatBC3_RGBA_sRGB:
return 1;
default:
BLI_assert_msg(false, "Unrecognised GPU pixel format!\n");
@ -272,6 +284,12 @@ int get_mtl_format_num_components(MTLPixelFormat tex_format)
case MTLPixelFormatRGBA8Unorm_sRGB:
case MTLPixelFormatRGB10A2Uint:
case MTLPixelFormatRGB10A2Unorm:
case MTLPixelFormatBC1_RGBA_sRGB:
case MTLPixelFormatBC2_RGBA_sRGB:
case MTLPixelFormatBC3_RGBA_sRGB:
case MTLPixelFormatBC1_RGBA:
case MTLPixelFormatBC2_RGBA:
case MTLPixelFormatBC3_RGBA:
return 4;
case MTLPixelFormatRG11B10Float:

View File

@ -118,6 +118,21 @@ ImBuf *IMB_allocFromBuffer(const uint8_t *byte_buffer,
void IMB_assign_byte_buffer(ImBuf *ibuf, uint8_t *buffer_data, ImBufOwnership ownership);
void IMB_assign_float_buffer(ImBuf *ibuf, float *buffer_data, ImBufOwnership ownership);
/**
* Assign the content and the color space of the corresponding buffer the data from the given
* buffer.
*
* \note Does not modify the topology (width, height, number of channels)
* or the mipmaps in any way.
*
* \note The ownership of the data in the source buffer is ignored.
*/
void IMB_assign_byte_buffer(ImBuf *ibuf, const ImBufByteBuffer &buffer, ImBufOwnership ownership);
void IMB_assign_float_buffer(ImBuf *ibuf,
const ImBufFloatBuffer &buffer,
ImBufOwnership ownership);
void IMB_assign_dds_data(ImBuf *ibuf, const DDSData &data, ImBufOwnership ownership);
/**
* Make corresponding buffers available for modification.
* Is achieved by ensuring that the given ImBuf is the only owner of the underlying buffer data.

View File

@ -34,17 +34,6 @@ struct IDProperty;
#define IMB_MIPMAP_LEVELS 20
#define IMB_FILEPATH_SIZE 1024
struct DDSData {
/** DDS fourcc info */
unsigned int fourcc;
/** The number of mipmaps in the dds file */
unsigned int nummipmaps;
/** The compressed image data */
unsigned char *data;
/** The size of the compressed data */
unsigned int size;
};
/**
* \ingroup imbuf
* This is the abstraction of an image. ImBuf is the basic type used for all imbuf operations.
@ -143,6 +132,19 @@ enum ImBufOwnership {
IB_TAKE_OWNERSHIP = 1,
};
struct DDSData {
/** DDS fourcc info */
unsigned int fourcc;
/** The number of mipmaps in the dds file */
unsigned int nummipmaps;
/** The compressed image data */
unsigned char *data;
/** The size of the compressed data */
unsigned int size;
/** Who owns the data buffer. */
ImBufOwnership ownership;
};
/* Different storage specialization.
*
* NOTE: Avoid direct assignments and allocations, use the buffer utilities from the IMB_imbuf.hh

View File

@ -84,6 +84,27 @@ template<class BufferType> static void imb_free_buffer(BufferType &buffer)
buffer.ownership = IB_DO_NOT_TAKE_OWNERSHIP;
}
/* Free the specified DDS buffer storage, freeing memory when needed and restoring the state of the
* buffer to its defaults. */
static void imb_free_dds_buffer(DDSData &dds_data)
{
if (dds_data.data) {
switch (dds_data.ownership) {
case IB_DO_NOT_TAKE_OWNERSHIP:
break;
case IB_TAKE_OWNERSHIP:
/* dds_data.data is allocated by DirectDrawSurface::readData(), so don't use MEM_freeN! */
free(dds_data.data);
break;
}
}
/* Reset buffer to defaults. */
dds_data.data = nullptr;
dds_data.ownership = IB_DO_NOT_TAKE_OWNERSHIP;
}
/* Allocate pixel storage of the given buffer. The buffer owns the allocated memory.
* Returns true of allocation succeeded, false otherwise. */
template<class BufferType>
@ -249,11 +270,7 @@ void IMB_freeImBuf(ImBuf *ibuf)
IMB_free_gpu_textures(ibuf);
IMB_metadata_free(ibuf->metadata);
colormanage_cache_free(ibuf);
if (ibuf->dds_data.data != nullptr) {
/* dds_data.data is allocated by DirectDrawSurface::readData(), so don't use MEM_freeN! */
free(ibuf->dds_data.data);
}
imb_free_dds_buffer(ibuf->dds_data);
MEM_freeN(ibuf);
}
}
@ -472,6 +489,32 @@ void IMB_assign_float_buffer(ImBuf *ibuf, float *buffer_data, const ImBufOwnersh
}
}
void IMB_assign_byte_buffer(ImBuf *ibuf,
const ImBufByteBuffer &buffer,
const ImBufOwnership ownership)
{
IMB_assign_byte_buffer(ibuf, buffer.data, ownership);
ibuf->byte_buffer.colorspace = buffer.colorspace;
}
void IMB_assign_float_buffer(ImBuf *ibuf,
const ImBufFloatBuffer &buffer,
const ImBufOwnership ownership)
{
IMB_assign_float_buffer(ibuf, buffer.data, ownership);
ibuf->float_buffer.colorspace = buffer.colorspace;
}
void IMB_assign_dds_data(ImBuf *ibuf, const DDSData &data, const ImBufOwnership ownership)
{
BLI_assert(ibuf->ftype == IMB_FTYPE_DDS);
imb_free_dds_buffer(ibuf->dds_data);
ibuf->dds_data = data;
ibuf->dds_data.ownership = ownership;
}
ImBuf *IMB_allocFromBufferOwn(
uint8_t *byte_buffer, float *float_buffer, uint w, uint h, uint channels)
{

View File

@ -330,6 +330,7 @@ static void LoadDXTCImage(ImBuf *ibuf, Filesystem::IOMemReader &mem_reader)
ibuf->dds_data.size = mem_reader.size() - dds_header_size;
ibuf->dds_data.data = (uchar *)malloc(ibuf->dds_data.size);
mem_reader.pread(ibuf->dds_data.data, ibuf->dds_data.size, dds_header_size);
ibuf->dds_data.ownership = IB_TAKE_OWNERSHIP;
/* Flip compressed image data to match OpenGL convention. */
FlipDXTCImage(ibuf);

View File

@ -7703,6 +7703,8 @@ static void rna_def_modifier_nodes_bake(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
RNA_define_lib_overridable(true);
srna = RNA_def_struct(brna, "NodesModifierBake", nullptr);
RNA_def_struct_ui_text(srna, "Nodes Modifier Bake", "");
@ -7758,6 +7760,8 @@ static void rna_def_modifier_nodes_bake(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "NodesModifierDataBlock");
RNA_def_property_collection_sdna(prop, nullptr, "data_blocks", "data_blocks_num");
RNA_def_property_srna(prop, "NodesModifierBakeDataBlocks");
RNA_define_lib_overridable(false);
}
static void rna_def_modifier_nodes_bakes(BlenderRNA *brna)

View File

@ -3574,7 +3574,7 @@ static void rna_def_tool_settings(BlenderRNA *brna)
prop, "rna_ToolSettings_snap_mode_get", "rna_ToolSettings_snap_mode_set", nullptr);
RNA_def_property_flag(prop, PROP_ENUM_FLAG);
RNA_def_property_ui_text(
prop, "Snap Element", "Type of element for the \"Snap With\" to snap to");
prop, "Snap Element", "Type of element for the \"Snap Base\" to snap to");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, nullptr); /* header redraw */
prop = RNA_def_property(srna, "snap_elements_individual", PROP_ENUM, PROP_NONE);

View File

@ -18,13 +18,13 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Bool>("Selection")
.default_value(true)
.hide_value()
.supports_field()
.field_on_all()
.description("Which top-level instances to realize");
b.add_input<decl::Bool>("Realize All")
.default_value(true)
.supports_field()
.field_on_all()
.description("Determine wether to realize nested instances completly");
b.add_input<decl::Int>("Depth").default_value(0).min(0).supports_field().description(
b.add_input<decl::Int>("Depth").default_value(0).min(0).field_on_all().description(
"Number of levels of nested instances to realize for each top-level instance");
b.add_output<decl::Geometry>("Geometry").propagate_all();
}
@ -38,24 +38,29 @@ static void node_geo_exec(GeoNodeExecParams params)
}
GeometryComponentEditData::remember_deformed_positions_if_necessary(geometry_set);
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
Field<bool> realize_all_filed = params.extract_input<Field<bool>>("Realize All");
Field<bool> realize_all_field = params.extract_input<Field<bool>>("Realize All");
Field<int> depth_field = params.extract_input<Field<int>>("Depth");
static auto depth_override = mf::build::SI2_SO<int, bool, int>(
"depth_override",
[](int value, bool realize) { return realize ? -1 : std::max(value, 0); },
[](int depth, bool realize_all_field) {
return realize_all_field ? geometry::VariedDepthOption::MAX_DEPTH : std::max(depth, 0);
},
mf::build::exec_presets::AllSpanOrSingle());
Field<int> depth_field_overrided(FieldOperation::Create(
depth_override, {std::move(depth_field), std::move(realize_all_field)}));
Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
static auto selection_override = mf::build::SI2_SO<int, bool, bool>(
"selection_override",
[](int value, bool selection) { return value == 0 ? false : selection; },
[](int depth_override, bool selection) { return depth_override == 0 ? false : selection; },
mf::build::exec_presets::AllSpanOrSingle());
Field<int> depth_field_overrided(
FieldOperation::Create(depth_override, {depth_field, realize_all_filed}));
Field<bool> selection_field_overrided(
FieldOperation::Create(selection_override, {depth_field_overrided, selection_field}));
Field<bool> selection_field_overrided(FieldOperation::Create(
selection_override, {depth_field_overrided, std::move(selection_field)}));
const bke::Instances &instances = *geometry_set.get_instances();
const bke::InstancesFieldContext field_context(instances);

View File

@ -21,7 +21,10 @@ static void node_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Geometry>("Curve").supported_type(
{GeometryComponent::Type::Curve, GeometryComponent::Type::GreasePencil});
b.add_input<decl::Bool>("Selection").default_value(true).hide_value().field_on_all();
b.add_input<decl::Vector>("Normal").default_value({0.0f, 0.0f, 1.0f}).field_on_all();
b.add_input<decl::Vector>("Normal")
.default_value({0.0f, 0.0f, 1.0f})
.subtype(PROP_XYZ)
.field_on_all();
b.add_output<decl::Geometry>("Curve").propagate_all();
}

View File

@ -241,7 +241,7 @@ static PyObject *pygpu_vertbuf__tp_new(PyTypeObject * /*type*/, PyObject *args,
PY_ARG_PARSER_HEAD_COMPAT()
"O!" /* `format` */
"I" /* `len` */
":blender::gpu::VertBuf.__new__",
":GPUVertBuf.__new__",
_keywords,
nullptr,
};
@ -307,7 +307,7 @@ static PyObject *pygpu_vertbuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, Py
return nullptr;
}
if (!pygpu_vertbuf_fill(self->buf, id, data, "blender::gpu::VertBuf.attr_fill")) {
if (!pygpu_vertbuf_fill(self->buf, id, data, "GPUVertBuf.attr_fill")) {
return nullptr;
}
@ -340,7 +340,7 @@ static void pygpu_vertbuf__tp_dealloc(BPyGPUVertBuf *self)
PyDoc_STRVAR(
/* Wrap. */
pygpu_vertbuf__tp_doc,
".. class:: blender::gpu::VertBuf(format, len)\n"
".. class:: GPUVertBuf(format, len)\n"
"\n"
" Contains a VBO.\n"
"\n"
@ -350,7 +350,7 @@ PyDoc_STRVAR(
" :type len: int\n");
PyTypeObject BPyGPUVertBuf_Type = {
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
/*tp_name*/ "blender::gpu::VertBuf",
/*tp_name*/ "GPUVertBuf",
/*tp_basicsize*/ sizeof(BPyGPUVertBuf),
/*tp_itemsize*/ 0,
/*tp_dealloc*/ (destructor)pygpu_vertbuf__tp_dealloc,

View File

@ -1314,8 +1314,8 @@ bool RE_engine_gpu_context_enable(RenderEngine *engine)
/* Activate RenderEngine System and Blender GPU Context. */
WM_system_gpu_context_activate(engine->system_gpu_context);
if (engine->blender_gpu_context) {
GPU_context_active_set(engine->blender_gpu_context);
GPU_render_begin();
GPU_context_active_set(engine->blender_gpu_context);
}
return true;
}
@ -1330,8 +1330,8 @@ void RE_engine_gpu_context_disable(RenderEngine *engine)
else {
if (engine->system_gpu_context) {
if (engine->blender_gpu_context) {
GPU_render_end();
GPU_context_active_set(nullptr);
GPU_render_end();
}
WM_system_gpu_context_release(engine->system_gpu_context);
/* Restore DRW state context if previously active. */

@ -1 +1 @@
Subproject commit 5038ad7165fd1a77e61e0d2d6efdadd6ea7c0dfb
Subproject commit bf5c70830540b215a3b1df21f28e0e80ead230f7