Fix #157384: Cycles attribute breaks under certain conditions

Caused by 4d6c7718c5.

Under certain conditions it is possible that the offset logic in the
AttributeTableBuilder::add() assigns offset of -1. This value just
happens to match ATTR_STD_NOT_FOUND, leading to false-positive check
that attribute is not found (for example, svm_node_attr_init() that
checks desc.offset != ATTR_STD_NOT_FOUND.

The simplest solution is to tweak the value of the ATTR_STD_NOT_FOUND
to make it very big negative value.

There is now an utility function to check whether AttributeDescriptor
points to an attribute that is found, to help possibly tweaking this
check in the future, and to make code a bit more semantically clear.

Pull Request: https://projects.blender.org/blender/blender/pulls/157410
This commit is contained in:
Sergey Sharybin 2026-04-20 16:57:59 +02:00 • committed by Sergey Sharybin
parent 50b08224cd
commit dcac328db3
18 changed files with 39 additions and 32 deletions

View file

@ -21,6 +21,11 @@ CCL_NAMESPACE_BEGIN
* Lookup of attributes is different between OSL and SVM, as OSL is ustring
* based while for SVM we use integer ids. */
ccl_device_forceinline bool is_attribute_found(const ccl_private AttributeDescriptor &desc)
{
return desc.offset != ATTR_STD_NOT_FOUND;
}
ccl_device_inline AttributeDescriptor attribute_not_found()
{
const AttributeDescriptor desc = {ATTR_ELEMENT_NONE, (NodeAttributeType)0, ATTR_STD_NOT_FOUND};

View file

@ -130,7 +130,7 @@ ccl_device float curve_random(KernelGlobals kg, const ccl_private ShaderData *sd
{
if (sd->type & PRIMITIVE_CURVE) {
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_CURVE_RANDOM);
return (desc.offset != ATTR_STD_NOT_FOUND) ? curve_attribute<float>(kg, sd, desc) : 0.0f;
return is_attribute_found(desc) ? curve_attribute<float>(kg, sd, desc) : 0.0f;
}
return 0.0f;
}

View file

@ -79,7 +79,7 @@ ccl_device float point_random(KernelGlobals kg, const ccl_private ShaderData *sd
{
if (sd->type & PRIMITIVE_POINT) {
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_POINT_RANDOM);
return (desc.offset != ATTR_STD_NOT_FOUND) ? point_attribute<float>(kg, sd, desc) : 0.0f;
return is_attribute_found(desc) ? point_attribute<float>(kg, sd, desc) : 0.0f;
}
return 0.0f;
}

View file

@ -68,7 +68,7 @@ ccl_device void primitive_normal_set_undisplaced(KernelGlobals kg,
if (sd->shader & SHADER_SMOOTH_NORMAL) {
const AttributeDescriptor ndesc = find_attribute(kg, sd, ATTR_STD_NORMAL_UNDISPLACED);
if (ndesc.offset == ATTR_STD_NOT_FOUND) {
if (!is_attribute_found(ndesc)) {
return;
}
N = safe_normalize(primitive_surface_attribute<float3>(kg, sd, ndesc));
@ -112,7 +112,7 @@ ccl_device_forceinline float3 primitive_uv(KernelGlobals kg, const ccl_private S
{
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_UV);
if (desc.offset == ATTR_STD_NOT_FOUND) {
if (!is_attribute_found(desc)) {
return make_float3(0.0f, 0.0f, 0.0f);
}
@ -131,7 +131,7 @@ ccl_device bool primitive_ptex(KernelGlobals kg,
const AttributeDescriptor desc_face_id = find_attribute(kg, sd, ATTR_STD_PTEX_FACE_ID);
const AttributeDescriptor desc_uv = find_attribute(kg, sd, ATTR_STD_PTEX_UV);
if (desc_face_id.offset == ATTR_STD_NOT_FOUND || desc_uv.offset == ATTR_STD_NOT_FOUND) {
if (!is_attribute_found(desc_face_id) || !is_attribute_found(desc_uv)) {
return false;
}
@ -162,7 +162,7 @@ ccl_device Float3Type primitive_tangent(KernelGlobals kg, ccl_private ShaderData
/* try to create spherical tangent from generated coordinates */
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_GENERATED);
if (desc.offset != ATTR_STD_NOT_FOUND) {
if (is_attribute_found(desc)) {
if constexpr (is_dual_v<Float3Type>) {
dual3 data = primitive_surface_attribute<dual3>(kg, sd, desc);
data = make_float3(-(data.y() - 0.5f), (data.x() - 0.5f), dual1());
@ -224,7 +224,7 @@ ccl_device_forceinline float4 primitive_motion_vector(KernelGlobals kg,
/* deformation motion */
AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_MOTION_VERTEX_POSITION);
if (desc.offset != ATTR_STD_NOT_FOUND) {
if (is_attribute_found(desc)) {
/* get motion info */
const int numverts = kernel_data_fetch(objects, sd->object).numverts;

View file

@ -35,7 +35,7 @@ ccl_device_inline Float3Type volume_normalized_position(KernelGlobals kg,
object_inverse_position_transform_if_object(kg, sd, &P);
if (desc.offset != ATTR_STD_NOT_FOUND) {
if (is_attribute_found(desc)) {
const Transform tfm = primitive_attribute_matrix(kg, desc);
P = transform_point(&tfm, P);
}

View file

@ -368,7 +368,7 @@ ccl_device_inline void volume_shader_motion_blur(KernelGlobals kg,
}
const AttributeDescriptor v_desc = find_attribute(kg, sd, ATTR_STD_VOLUME_VELOCITY);
kernel_assert(v_desc.offset != ATTR_STD_NOT_FOUND);
kernel_assert(is_attribute_found(v_desc));
const float3 P = sd->P;
const float velocity_scale = kernel_data_fetch(objects, sd->object).velocity_scale;

View file

@ -72,6 +72,7 @@ void OSLRenderServices::register_closures(OSL::ShadingSystem *ss)
#lower, OSL_CLOSURE_##Upper##_ID, osl_closure_##lower##_params(), nullptr, nullptr);
#include "closures_template.h"
ss->register_closure(
"layer", OSL_CLOSURE_LAYER_ID, osl_closure_layer_params(), nullptr, nullptr);
}
@ -135,7 +136,7 @@ void osl_eval_nodes_surface(const ThreadKernelGlobalsCPU *kg,
/* set state as if undisplaced */
if (sd->flag & SD_HAS_DISPLACEMENT) {
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_POSITION_UNDISPLACED);
kernel_assert(desc.offset != ATTR_STD_NOT_FOUND);
kernel_assert(is_attribute_found(desc));
dual3 P = primitive_surface_attribute<dual3>(kg, sd, desc);
object_position_transform(kg, sd, &P);

View file

@ -144,6 +144,7 @@ ccl_device void flatten_closure_tree(KernelGlobals kg,
break; \
}
#include "closures_template.h"
default:
break;
}
@ -218,7 +219,7 @@ ccl_device_inline void osl_eval_nodes(KernelGlobals kg,
/* Set position state as if undisplaced. */
if (sd->flag & SD_HAS_DISPLACEMENT) {
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_POSITION_UNDISPLACED);
kernel_assert(desc.offset != ATTR_STD_NOT_FOUND);
kernel_assert(is_attribute_found(desc));
dual3 P = primitive_surface_attribute<dual3>(kg, sd, desc);

View file

@ -229,7 +229,7 @@ bool OSLRenderServices::get_attribute(ShaderGlobals *globals,
/* find attribute on object */
const AttributeDescriptor desc = find_attribute(kg, object, sd->prim, name.hash());
if (desc.offset != ATTR_STD_NOT_FOUND) {
if (is_attribute_found(desc)) {
return osl_shared_get_object_attribute(kg, sd, desc, type, derivatives, val);
}

View file

@ -254,7 +254,7 @@ ccl_device_extern bool osl_get_attribute(ccl_private ShaderGlobals *sg,
const int object = sd->object;
const AttributeDescriptor desc = find_attribute(kg, object, sd->prim, name);
if (desc.offset != ATTR_STD_NOT_FOUND) {
if (is_attribute_found(desc)) {
return osl_shared_get_object_attribute(kg, sd, desc, type, derivatives, res);
}
return osl_shared_get_object_standard_attribute(kg, sg, sd, name, type, derivatives, res);

View file

@ -1179,7 +1179,7 @@ ccl_device_inline bool osl_shared_get_object_standard_attribute(KernelGlobals kg
if (sd->type & PRIMITIVE_TRIANGLE) {
const AttributeDescriptor desc = find_attribute(
kg, sd->object, sd->prim, ATTR_STD_NORMAL_UNDISPLACED);
if (desc.offset != ATTR_STD_NOT_FOUND) {
if (is_attribute_found(desc)) {
return osl_shared_get_object_attribute(kg, sd, desc, type, derivatives, val);
}
const float3 f = triangle_smooth_normal_unnormalized_object_space(kg, sd);

View file

@ -29,7 +29,7 @@ ccl_device AttributeDescriptor svm_node_attr_init(KernelGlobals kg,
if (sd->object != OBJECT_NONE) {
desc = find_attribute(kg, sd, node.attr);
if (desc.offset == ATTR_STD_NOT_FOUND) {
if (!is_attribute_found(desc)) {
desc = attribute_not_found();
desc.offset = 0;
desc.type = (NodeAttributeType)node.output_type;
@ -85,7 +85,7 @@ svm_node_attr_surface_eval(KernelGlobals kg,
return uv;
}
if (node.attr == ATTR_STD_GENERATED && desc.element == ATTR_ELEMENT_NONE) {
if (node.attr == ATTR_STD_GENERATED && !is_attribute_found(desc)) {
Float3Type f = shading_position<Float3Type>(sd);
object_inverse_position_transform_if_object(kg, sd, &f);
return f;

View file

@ -32,7 +32,7 @@ ccl_device_noinline void svm_node_enter_bump_eval(KernelGlobals kg,
/* Set position as if undisplaced. */
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_POSITION_UNDISPLACED);
if (desc.offset != ATTR_STD_NOT_FOUND) {
if (is_attribute_found(desc)) {
dual3 attr = primitive_surface_attribute<dual3>(kg, sd, desc);
object_position_transform(kg, sd, &attr);

View file

@ -858,7 +858,7 @@ ccl_device
const AttributeDescriptor attr_descr_random = find_attribute(kg, sd, hdata.attr_random);
float random = 0.0f;
if (attr_descr_random.offset != ATTR_STD_NOT_FOUND) {
if (is_attribute_found(attr_descr_random)) {
random = primitive_surface_attribute<float>(kg, sd, attr_descr_random);
}
else {
@ -1263,7 +1263,7 @@ ccl_device_noinline void svm_node_principled_volume(
if (density > 0.0f) {
/* Density and color attribute lookup if available. */
const AttributeDescriptor attr_density = find_attribute(kg, sd, node.attr_density);
if (attr_density.offset != ATTR_STD_NOT_FOUND) {
if (is_attribute_found(attr_density)) {
primitive_density = primitive_volume_attribute<float>(kg, sd, attr_density, true);
density = fmaxf(density * primitive_density, 0.0f);
}
@ -1274,7 +1274,7 @@ ccl_device_noinline void svm_node_principled_volume(
Spectrum color = closure_weight;
const AttributeDescriptor attr_color = find_attribute(kg, sd, node.attr_color);
if (attr_color.offset != ATTR_STD_NOT_FOUND) {
if (is_attribute_found(attr_color)) {
color *= rgb_to_spectrum(primitive_volume_attribute<float3>(kg, sd, attr_color, true));
}
@ -1317,7 +1317,7 @@ ccl_device_noinline void svm_node_principled_volume(
/* Add flame temperature from attribute if available. */
const AttributeDescriptor attr_temperature = find_attribute(kg, sd, node.attr_temperature);
if (attr_temperature.offset != ATTR_STD_NOT_FOUND) {
if (is_attribute_found(attr_temperature)) {
const float temperature = primitive_volume_attribute<float>(kg, sd, attr_temperature, true);
T *= fmaxf(temperature, 0.0f);
}

View file

@ -165,7 +165,7 @@ ccl_device_noinline void svm_node_vector_displacement(
const AttributeDescriptor attr = find_attribute(kg, sd, node.attr);
float3 tangent;
if (attr.offset != ATTR_STD_NOT_FOUND) {
if (is_attribute_found(attr)) {
tangent = primitive_surface_attribute<float3>(kg, sd, attr);
}
else {
@ -174,7 +174,7 @@ ccl_device_noinline void svm_node_vector_displacement(
float3 bitangent = safe_normalize(cross(normal, tangent));
const AttributeDescriptor attr_sign = find_attribute(kg, sd, node.attr_sign);
if (attr_sign.offset != ATTR_STD_NOT_FOUND) {
if (is_attribute_found(attr_sign)) {
const float sign = primitive_surface_attribute<float>(kg, sd, attr_sign);
bitangent *= sign;
}

View file

@ -245,7 +245,7 @@ ccl_device_noinline void svm_node_normal_map(KernelGlobals kg,
const AttributeDescriptor attr = find_attribute(kg, sd, node.attr);
const AttributeDescriptor attr_sign = find_attribute(kg, sd, node.attr_sign);
if (attr.offset == ATTR_STD_NOT_FOUND || attr_sign.offset == ATTR_STD_NOT_FOUND) {
if (!is_attribute_found(attr) || !is_attribute_found(attr_sign)) {
/* Fall back to unperturbed normal. */
stack_store_float3(stack, node.normal_offset, sd->N);
return;
@ -260,8 +260,8 @@ ccl_device_noinline void svm_node_normal_map(KernelGlobals kg,
const AttributeDescriptor attr_undisplaced_normal =
(node.use_original_base) ?
find_attribute(kg, sd->object, sd->prim, ATTR_STD_NORMAL_UNDISPLACED) :
AttributeDescriptor{ATTR_ELEMENT_NONE, NODE_ATTR_FLOAT3, ATTR_STD_NOT_FOUND};
if (attr_undisplaced_normal.offset != ATTR_STD_NOT_FOUND) {
attribute_not_found();
if (is_attribute_found(attr_undisplaced_normal)) {
normal = primitive_surface_attribute<float3>(kg, sd, attr_undisplaced_normal);
/* Can't interpolate in tangent space as the displaced normal is not used
* for the tangent frame. */
@ -351,7 +351,7 @@ ccl_device_noinline void svm_node_tangent(KernelGlobals kg,
Float3Type tangent;
if (node.direction_type == NODE_TANGENT_UVMAP) {
/* UV map */
if (desc.offset == ATTR_STD_NOT_FOUND) {
if (!is_attribute_found(desc)) {
stack_store(stack, node.tangent_offset, Float3Type());
return;
}
@ -370,7 +370,7 @@ ccl_device_noinline void svm_node_tangent(KernelGlobals kg,
else {
/* radial */
Float3Type generated;
if (desc.offset == ATTR_STD_NOT_FOUND) {
if (!is_attribute_found(desc)) {
generated = shading_position<Float3Type>(sd);
}
else if (desc.type == NODE_ATTR_FLOAT2) {

View file

@ -22,7 +22,7 @@ ccl_device_noinline void svm_node_vertex_color(KernelGlobals kg,
float alpha;
const AttributeDescriptor descriptor = find_attribute(kg, sd, node.layer_id);
if (descriptor.offset != ATTR_STD_NOT_FOUND) {
if (is_attribute_found(descriptor)) {
if (descriptor.type == NODE_ATTR_FLOAT4 || descriptor.type == NODE_ATTR_RGBA) {
const float4 vertex_color = primitive_surface_attribute<float4>(kg, sd, descriptor);
color = make_float3(vertex_color);
@ -56,7 +56,7 @@ ccl_device_noinline void svm_node_vertex_color_derivative(
float alpha;
const AttributeDescriptor descriptor = find_attribute(kg, sd, node.layer_id);
if (descriptor.offset != ATTR_STD_NOT_FOUND) {
if (is_attribute_found(descriptor)) {
if (descriptor.type == NODE_ATTR_FLOAT4 || descriptor.type == NODE_ATTR_RGBA) {
dual4 vertex_color = primitive_surface_attribute<dual4>(kg, sd, descriptor);
if (node.bump_offset == NODE_BUMP_OFFSET_DX) {

View file

@ -708,7 +708,7 @@ enum AttributeElement {
ATTR_ELEMENT_IS_MOTION,
};
enum AttributeStandard {
enum AttributeStandard : int {
ATTR_STD_NONE = 0,
ATTR_STD_VERTEX_NORMAL,
ATTR_STD_CORNER_NORMAL,
@ -747,7 +747,7 @@ enum AttributeStandard {
ATTR_STD_SHADOW_TRANSPARENCY,
ATTR_STD_NUM,
ATTR_STD_NOT_FOUND = ~0
ATTR_STD_NOT_FOUND = -0x7fffffff
};
enum AttributeFlag {