Nodes: Voronoi node CPU optimizations

Several small speedups for Voronoi node (no behavior change). This
affects Cycles and CPU execution of Voronoi node e.g. in Compositor.

- F1 mode: when evaluating distance for Voronoi cells, use a faster
  distance estimation, and only do final distance calculation on the
  resulting closest cell. This is only really relevant for the default
  Euclidian distance, where this saves a square root per evaluated cell
  (in 3D Voronoi case saves 26 square roots; in 4D case saves 80 square
  roots).
- N-Sphere Radius mode: speedup by doing squared distance calculations.
  We only need to find the closest one, so again doing the square root
  per cell is not needed here.

Something like 5%-10% speedup for F1 3D Voronoi; more performance details
in the PR.

Pull Request: https://projects.blender.org/blender/blender/pulls/139490
This commit is contained in:
Aras Pranckevicius
2025-05-29 15:40:31 +02:00
committed by Aras Pranckevicius
parent c56a855b9f
commit 4a328b5a63
2 changed files with 94 additions and 60 deletions

View File

@@ -71,6 +71,28 @@ ccl_device float voronoi_distance(const T a, const T b, const ccl_private Vorono
return 0.0f;
}
/* Possibly cheaper/faster version of Voronoi distance, in a way that does not change
* logic of "which distance is the closest?". */
template<typename T>
ccl_device float voronoi_distance_bound(const T a,
const T b,
const ccl_private VoronoiParams &params)
{
if (params.metric == NODE_VORONOI_EUCLIDEAN) {
return len_squared(a - b);
}
if (params.metric == NODE_VORONOI_MANHATTAN) {
return reduce_add(fabs(a - b));
}
if (params.metric == NODE_VORONOI_CHEBYCHEV) {
return reduce_max(fabs(a - b));
}
if (params.metric == NODE_VORONOI_MINKOWSKI) {
return reduce_add(power(fabs(a - b), params.exponent));
}
return 0.0f;
}
/* **** 1D Voronoi **** */
ccl_device float4 voronoi_position(const float coord)
@@ -256,7 +278,7 @@ ccl_device VoronoiOutput voronoi_f1(const ccl_private VoronoiParams &params, con
const float2 cellOffset = make_float2(i, j);
const float2 pointPosition = cellOffset + hash_float2_to_float2(cellPosition + cellOffset) *
params.randomness;
const float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
const float distanceToPoint = voronoi_distance_bound(pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
@@ -266,7 +288,7 @@ ccl_device VoronoiOutput voronoi_f1(const ccl_private VoronoiParams &params, con
}
VoronoiOutput octave;
octave.distance = minDistance;
octave.distance = voronoi_distance(targetPosition, localPosition, params);
octave.color = hash_float2_to_float3(cellPosition + targetOffset);
octave.position = voronoi_position(targetPosition + cellPosition);
return octave;
@@ -400,22 +422,22 @@ ccl_device float voronoi_n_sphere_radius(const ccl_private VoronoiParams &params
float2 closestPoint = make_float2(0.0f, 0.0f);
float2 closestPointOffset = make_float2(0.0f, 0.0f);
float minDistance = FLT_MAX;
float minDistanceSq = FLT_MAX;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
const float2 cellOffset = make_float2(i, j);
const float2 pointPosition = cellOffset + hash_float2_to_float2(cellPosition + cellOffset) *
params.randomness;
const float distanceToPoint = distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
const float distanceToPointSq = len_squared(pointPosition - localPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
}
minDistance = FLT_MAX;
minDistanceSq = FLT_MAX;
float2 closestPointToClosestPoint = make_float2(0.0f, 0.0f);
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
@@ -425,9 +447,9 @@ ccl_device float voronoi_n_sphere_radius(const ccl_private VoronoiParams &params
const float2 cellOffset = make_float2(i, j) + closestPointOffset;
const float2 pointPosition = cellOffset + hash_float2_to_float2(cellPosition + cellOffset) *
params.randomness;
const float distanceToPoint = distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
const float distanceToPointSq = len_squared(closestPoint - pointPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPointToClosestPoint = pointPosition;
}
}
@@ -458,7 +480,7 @@ ccl_device VoronoiOutput voronoi_f1(const ccl_private VoronoiParams &params, con
const float3 pointPosition = cellOffset +
hash_float3_to_float3(cellPosition + cellOffset) *
params.randomness;
const float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
const float distanceToPoint = voronoi_distance_bound(pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
@@ -469,7 +491,7 @@ ccl_device VoronoiOutput voronoi_f1(const ccl_private VoronoiParams &params, con
}
VoronoiOutput octave;
octave.distance = minDistance;
octave.distance = voronoi_distance(targetPosition, localPosition, params);
octave.color = hash_float3_to_float3(cellPosition + targetOffset);
octave.position = voronoi_position(targetPosition + cellPosition);
return octave;
@@ -613,7 +635,7 @@ ccl_device float voronoi_n_sphere_radius(const ccl_private VoronoiParams &params
float3 closestPoint = make_float3(0.0f, 0.0f, 0.0f);
float3 closestPointOffset = make_float3(0.0f, 0.0f, 0.0f);
float minDistance = FLT_MAX;
float minDistanceSq = FLT_MAX;
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
@@ -621,9 +643,9 @@ ccl_device float voronoi_n_sphere_radius(const ccl_private VoronoiParams &params
const float3 pointPosition = cellOffset +
hash_float3_to_float3(cellPosition + cellOffset) *
params.randomness;
const float distanceToPoint = distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
const float distanceToPointSq = len_squared(pointPosition - localPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
@@ -631,7 +653,7 @@ ccl_device float voronoi_n_sphere_radius(const ccl_private VoronoiParams &params
}
}
minDistance = FLT_MAX;
minDistanceSq = FLT_MAX;
float3 closestPointToClosestPoint = make_float3(0.0f, 0.0f, 0.0f);
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
@@ -643,9 +665,9 @@ ccl_device float voronoi_n_sphere_radius(const ccl_private VoronoiParams &params
const float3 pointPosition = cellOffset +
hash_float3_to_float3(cellPosition + cellOffset) *
params.randomness;
const float distanceToPoint = distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
const float distanceToPointSq = len_squared(closestPoint - pointPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPointToClosestPoint = pointPosition;
}
}
@@ -678,7 +700,8 @@ ccl_device VoronoiOutput voronoi_f1(const ccl_private VoronoiParams &params, con
const float4 pointPosition = cellOffset +
hash_float4_to_float4(cellPosition + cellOffset) *
params.randomness;
const float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
const float distanceToPoint = voronoi_distance_bound(
pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
@@ -690,7 +713,7 @@ ccl_device VoronoiOutput voronoi_f1(const ccl_private VoronoiParams &params, con
}
VoronoiOutput octave;
octave.distance = minDistance;
octave.distance = voronoi_distance(targetPosition, localPosition, params);
octave.color = hash_float4_to_float3(cellPosition + targetOffset);
octave.position = voronoi_position(targetPosition + cellPosition);
return octave;
@@ -842,7 +865,7 @@ ccl_device float voronoi_n_sphere_radius(const ccl_private VoronoiParams &params
float4 closestPoint = zero_float4();
float4 closestPointOffset = zero_float4();
float minDistance = FLT_MAX;
float minDistanceSq = FLT_MAX;
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
@@ -851,9 +874,9 @@ ccl_device float voronoi_n_sphere_radius(const ccl_private VoronoiParams &params
const float4 pointPosition = cellOffset +
hash_float4_to_float4(cellPosition + cellOffset) *
params.randomness;
const float distanceToPoint = distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
const float distanceToPointSq = len_squared(pointPosition - localPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
@@ -862,7 +885,7 @@ ccl_device float voronoi_n_sphere_radius(const ccl_private VoronoiParams &params
}
}
minDistance = FLT_MAX;
minDistanceSq = FLT_MAX;
float4 closestPointToClosestPoint = zero_float4();
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
@@ -875,9 +898,9 @@ ccl_device float voronoi_n_sphere_radius(const ccl_private VoronoiParams &params
const float4 pointPosition = cellOffset +
hash_float4_to_float4(cellPosition + cellOffset) *
params.randomness;
const float distanceToPoint = distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
const float distanceToPointSq = len_squared(closestPoint - pointPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPointToClosestPoint = pointPosition;
}
}

View File

@@ -1134,6 +1134,17 @@ float voronoi_distance(const float4 a, const float4 b, const VoronoiParams &para
return 0.0f;
}
/* Possibly cheaper/faster version of Voronoi distance, in a way that does not change
* logic of "which distance is the closest?". */
template<typename T>
static float voronoi_distance_bound(const T a, const T b, const VoronoiParams &params)
{
if (params.metric == NOISE_SHD_VORONOI_EUCLIDEAN) {
return math::length_squared(a - b);
}
return voronoi_distance(a, b, params);
}
/* **** 1D Voronoi **** */
float4 voronoi_position(const float coord)
@@ -1317,7 +1328,7 @@ VoronoiOutput voronoi_f1(const VoronoiParams &params, const float2 coord)
float2 cellOffset(i, j);
float2 pointPosition = cellOffset +
hash_float_to_float2(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
float distanceToPoint = voronoi_distance_bound(pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
@@ -1327,7 +1338,7 @@ VoronoiOutput voronoi_f1(const VoronoiParams &params, const float2 coord)
}
VoronoiOutput octave;
octave.distance = minDistance;
octave.distance = voronoi_distance(targetPosition, localPosition, params);
octave.color = hash_float_to_float3(cellPosition + targetOffset);
octave.position = voronoi_position(targetPosition + cellPosition);
return octave;
@@ -1461,22 +1472,22 @@ float voronoi_n_sphere_radius(const VoronoiParams &params, const float2 coord)
float2 closestPoint = {0.0f, 0.0f};
float2 closestPointOffset = {0.0f, 0.0f};
float minDistance = FLT_MAX;
float minDistanceSq = FLT_MAX;
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
float2 cellOffset(i, j);
float2 pointPosition = cellOffset +
hash_float_to_float2(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = math::distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
float distanceToPointSq = math::length_squared(pointPosition - localPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
}
}
minDistance = FLT_MAX;
minDistanceSq = FLT_MAX;
float2 closestPointToClosestPoint = {0.0f, 0.0f};
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
@@ -1486,9 +1497,9 @@ float voronoi_n_sphere_radius(const VoronoiParams &params, const float2 coord)
float2 cellOffset = float2(i, j) + closestPointOffset;
float2 pointPosition = cellOffset +
hash_float_to_float2(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = math::distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
float distanceToPointSq = math::length_squared(closestPoint - pointPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPointToClosestPoint = pointPosition;
}
}
@@ -1518,7 +1529,7 @@ VoronoiOutput voronoi_f1(const VoronoiParams &params, const float3 coord)
float3 cellOffset(i, j, k);
float3 pointPosition = cellOffset +
hash_float_to_float3(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
float distanceToPoint = voronoi_distance_bound(pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
@@ -1529,7 +1540,7 @@ VoronoiOutput voronoi_f1(const VoronoiParams &params, const float3 coord)
}
VoronoiOutput octave;
octave.distance = minDistance;
octave.distance = voronoi_distance(targetPosition, localPosition, params);
octave.color = hash_float_to_float3(cellPosition + targetOffset);
octave.position = voronoi_position(targetPosition + cellPosition);
return octave;
@@ -1673,16 +1684,16 @@ float voronoi_n_sphere_radius(const VoronoiParams &params, const float3 coord)
float3 closestPoint = {0.0f, 0.0f, 0.0f};
float3 closestPointOffset = {0.0f, 0.0f, 0.0f};
float minDistance = FLT_MAX;
float minDistanceSq = FLT_MAX;
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
float3 cellOffset(i, j, k);
float3 pointPosition = cellOffset +
hash_float_to_float3(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = math::distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
float distanceToPointSq = math::length_squared(pointPosition - localPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
@@ -1690,7 +1701,7 @@ float voronoi_n_sphere_radius(const VoronoiParams &params, const float3 coord)
}
}
minDistance = FLT_MAX;
minDistanceSq = FLT_MAX;
float3 closestPointToClosestPoint = {0.0f, 0.0f, 0.0f};
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
@@ -1701,9 +1712,9 @@ float voronoi_n_sphere_radius(const VoronoiParams &params, const float3 coord)
float3 cellOffset = float3(i, j, k) + closestPointOffset;
float3 pointPosition = cellOffset +
hash_float_to_float3(cellPosition + cellOffset) * params.randomness;
float distanceToPoint = math::distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
float distanceToPointSq = math::length_squared(closestPoint - pointPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPointToClosestPoint = pointPosition;
}
}
@@ -1735,7 +1746,7 @@ VoronoiOutput voronoi_f1(const VoronoiParams &params, const float4 coord)
float4 cellOffset(i, j, k, u);
float4 pointPosition = cellOffset + hash_float_to_float4(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = voronoi_distance(pointPosition, localPosition, params);
float distanceToPoint = voronoi_distance_bound(pointPosition, localPosition, params);
if (distanceToPoint < minDistance) {
targetOffset = cellOffset;
minDistance = distanceToPoint;
@@ -1747,7 +1758,7 @@ VoronoiOutput voronoi_f1(const VoronoiParams &params, const float4 coord)
}
VoronoiOutput octave;
octave.distance = minDistance;
octave.distance = voronoi_distance(targetPosition, localPosition, params);
octave.color = hash_float_to_float3(cellPosition + targetOffset);
octave.position = voronoi_position(targetPosition + cellPosition);
return octave;
@@ -1899,7 +1910,7 @@ float voronoi_n_sphere_radius(const VoronoiParams &params, const float4 coord)
float4 closestPoint = {0.0f, 0.0f, 0.0f, 0.0f};
float4 closestPointOffset = {0.0f, 0.0f, 0.0f, 0.0f};
float minDistance = FLT_MAX;
float minDistanceSq = FLT_MAX;
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
@@ -1907,9 +1918,9 @@ float voronoi_n_sphere_radius(const VoronoiParams &params, const float4 coord)
float4 cellOffset(i, j, k, u);
float4 pointPosition = cellOffset + hash_float_to_float4(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = math::distance(pointPosition, localPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
float distanceToPointSq = math::length_squared(pointPosition - localPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPoint = pointPosition;
closestPointOffset = cellOffset;
}
@@ -1918,7 +1929,7 @@ float voronoi_n_sphere_radius(const VoronoiParams &params, const float4 coord)
}
}
minDistance = FLT_MAX;
minDistanceSq = FLT_MAX;
float4 closestPointToClosestPoint = {0.0f, 0.0f, 0.0f, 0.0f};
for (int u = -1; u <= 1; u++) {
for (int k = -1; k <= 1; k++) {
@@ -1930,9 +1941,9 @@ float voronoi_n_sphere_radius(const VoronoiParams &params, const float4 coord)
float4 cellOffset = float4(i, j, k, u) + closestPointOffset;
float4 pointPosition = cellOffset + hash_float_to_float4(cellPosition + cellOffset) *
params.randomness;
float distanceToPoint = math::distance(closestPoint, pointPosition);
if (distanceToPoint < minDistance) {
minDistance = distanceToPoint;
float distanceToPointSq = math::length_squared(closestPoint - pointPosition);
if (distanceToPointSq < minDistanceSq) {
minDistanceSq = distanceToPointSq;
closestPointToClosestPoint = pointPosition;
}
}