blender/intern/cycles/kernel/geom/geom_intersect.h
Sergey Sharybin 2b4c0730b3 Refactor: De-duplicate local intersection hit index calculation
Metal-RT backend required a bit of not-so-obvious tricks:

- The LocalIntersection pointer can not be stored in the payload, so
  the utility function has been templated and the Metal-RT payload has
  been modified to match the actual LocalIntersection better.

- Explicit address space is required, and the utility function which
  calculates the index to store the intersection at is used from
  multiple code-paths, some of them require LCG state to be private,
  others require it to be ray_data. Address space can not be a part
  of a template type, so a temporary copy of the lcg_state is done
  in the intersection function. Since the utility is foeceinline the
  hope is that the compiler eliminates it.

- Using bit-packing for the payload fields lead to some unexpected
  test failures: non-deterministic render results, as if something
  was not properly initialized. It is unclear why it started to
  happen now. Using regular int fields avoids sign-comparison warning
  and solves the test failures.

- There was actually one potentially uninitialized member in the
  payload: has_lcg_state was never set to false.

Even with all this extra complexity it does not seem that there is
negative performance impact measured on Apple M4 Max.

Pull Request: https://projects.blender.org/blender/blender/pulls/151540
2026-01-26 14:20:28 +01:00

64 lines
2 KiB
C++

/* SPDX-FileCopyrightText: 2025 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Common utilities for various geometry type intersections. */
#pragma once
#include "kernel/globals.h"
#include "kernel/sample/lcg.h"
CCL_NAMESPACE_BEGIN
/* For an intersection with the given distance isect_t from the ray origin, increase the number
* of hits (when needed) and return an index within local_isect->hits where the intersection is to
* be stored. If the return value -1, then the intersection is to be ignored (nothing is to be
* written to the local_isect->hits, and the intersection test function is to return false.
*
* The LocalIntersection is a templated type, allowing different types that implement similar data
* layout to be passed here. This is needed for the MetalRT, where it is not possible to access the
* pointer to actual LocalIntersection from the git function. */
#ifdef __BVH_LOCAL__
template<class LocalIntersection>
ccl_device_forceinline int local_intersect_get_record_index(
ccl_private LocalIntersection *local_isect,
const float isect_t,
ccl_private uint *lcg_state,
const int max_hits)
{
if (lcg_state) {
/* Record up to max_hits intersections. */
for (int i = min(max_hits, int(local_isect->num_hits)) - 1; i >= 0; --i) {
if (local_isect->hits[i].t == isect_t) {
return -1;
}
}
local_isect->num_hits++;
int hit;
if (local_isect->num_hits <= max_hits) {
hit = local_isect->num_hits - 1;
}
else {
/* Reservoir sampling: if we are at the maximum number of hits, randomly replace element or
* skip it. */
hit = lcg_step_uint(lcg_state) % local_isect->num_hits;
if (hit >= max_hits) {
return -1;
}
}
return hit;
}
/* Record closest intersection only. */
if (local_isect->num_hits && isect_t > local_isect->hits[0].t) {
return -1;
}
local_isect->num_hits = 1;
return 0;
}
#endif /* __BVH_LOCAL__ */
CCL_NAMESPACE_END