Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
# SPDX-FileCopyrightText: 2011-2026 Blender Foundation
#
# SPDX-License-Identifier: Apache-2.0
set(INC
../../..
)
set(INC_SYS
)
set(SRC_KERNEL_DEVICE_METAL
kernel.metal
)
set(SRC_KERNEL_DEVICE_METAL_HEADERS
bvh.h
compat.h
context_begin.h
context_end.h
function_constants.h
globals.h
)
set(LIB
)
if(WITH_CYCLES_DEVICE_METAL)
add_library(cycles_kernel_metal INTERFACE)
target_sources(cycles_kernel_metal INTERFACE
${SRC_KERNEL_DEVICE_METAL}
${SRC_KERNEL_DEVICE_METAL_HEADERS}
)
cycles_set_solution_folder(cycles_kernel_metal)
source_group("device\\metal" FILES ${SRC_KERNEL_DEVICE_METAL} ${SRC_KERNEL_DEVICE_METAL_HEADERS})
add_dependencies(cycles_kernel cycles_kernel_metal)
endif()
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_METAL}" ${CYCLES_INSTALL_PATH}/source/kernel/device/metal)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_METAL_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/metal)

View File

@@ -0,0 +1,573 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* MetalRT implementation of ray-scene intersection. */
#pragma once
#include "kernel/bvh/types.h"
#include "kernel/bvh/util.h"
CCL_NAMESPACE_BEGIN
/* Payload types.
*
* Best practice is to minimize the size of MetalRT payloads to avoid heavy spilling during
* intersection tests.
*/
struct MetalRTIntersectionPayload {
int self_prim;
int self_object;
uint visibility;
};
struct MetalRTIntersectionLocalPayload_single_hit {
int self_prim;
#if defined(__METALRT_MOTION__)
int self_object;
#endif
};
struct MetalRTLocalHit {
uint prim;
float t, u, v;
};
/* Payload for the local intersection queries.
* It embeds a subset of storage that is typically found in the LocalIntersection. This is because
* it is not possible to store a pointer to the actual LocalIntersection in the payload. So some
* data is duplicated into the payload and then copied back to the LocalIntersection. */
struct MetalRTIntersectionLocalPayload {
int self_prim;
#if defined(__METALRT_MOTION__)
int self_object;
#endif
uint lcg_state;
MetalRTLocalHit hits[LOCAL_MAX_HITS];
int max_hits;
int num_hits;
bool has_lcg_state;
};
struct MetalRTIntersectionShadowPayload {
RaySelfPrimitives self;
uint visibility;
};
#ifdef __HAIR__
ccl_device_forceinline bool curve_ribbon_accept(KernelGlobals kg,
const float u,
float t,
const ccl_private Ray *ray,
const int object,
const int prim,
const int type)
{
KernelCurve kcurve = kernel_data_fetch(curves, prim);
int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(type);
int k1 = k0 + 1;
int ka = max(k0 - 1, kcurve.first_key);
int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1);
/* We can ignore motion blur here because we don't need the positions, and it doesn't affect the
* radius. */
const int position_offset = kernel_data_fetch(objects, object).position_offset;
float radius[4];
radius[0] = kernel_data_fetch(curve_keys, position_offset + ka).w;
radius[1] = kernel_data_fetch(curve_keys, position_offset + k0).w;
radius[2] = kernel_data_fetch(curve_keys, position_offset + k1).w;
radius[3] = kernel_data_fetch(curve_keys, position_offset + kb).w;
const float r = metal::catmull_rom(u, radius[0], radius[1], radius[2], radius[3]);
/* MPJ TODO: Can we ignore motion and/or object transforms here? Depends on scaling? */
float3 ray_P = ray->P;
float3 ray_D = ray->D;
if (!(kernel_data_fetch(object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED)) {
float3 idir;
# if defined(__METALRT_MOTION__)
bvh_instance_motion_push(nullptr, object, ray, &ray_P, &ray_D, &idir);
# else
bvh_instance_push(nullptr, object, ray, &ray_P, &ray_D, &idir);
# endif
}
/* ignore self intersections */
const float avoidance_factor = 2.0f;
return t * len(ray_D) > avoidance_factor * r;
}
ccl_device_forceinline float curve_ribbon_v(KernelGlobals kg,
const float u,
float t,
const ccl_private Ray *ray,
const int object,
const int prim,
const int type)
{
# if defined(__METALRT_MOTION__)
float time = ray->time;
# else
float time = 0.0f;
# endif
const bool is_motion = (type & PRIMITIVE_MOTION);
KernelCurve kcurve = kernel_data_fetch(curves, prim);
int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(type);
int k1 = k0 + 1;
int ka = max(k0 - 1, kcurve.first_key);
int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1);
float4 curve[4];
if (!is_motion) {
const int position_offset = kernel_data_fetch(objects, object).position_offset;
curve[0] = kernel_data_fetch(curve_keys, position_offset + ka);
curve[1] = kernel_data_fetch(curve_keys, position_offset + k0);
curve[2] = kernel_data_fetch(curve_keys, position_offset + k1);
curve[3] = kernel_data_fetch(curve_keys, position_offset + kb);
}
else {
motion_curve_keys(kg, object, time, ka, k0, k1, kb, curve);
}
float3 ray_P = ray->P;
float3 ray_D = ray->D;
if (!(kernel_data_fetch(object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED)) {
float3 idir;
# if defined(__METALRT_MOTION__)
bvh_instance_motion_push(nullptr, object, ray, &ray_P, &ray_D, &idir);
# else
bvh_instance_push(nullptr, object, ray, &ray_P, &ray_D, &idir);
# endif
}
const float4 P_curve4 = metal::catmull_rom(u, curve[0], curve[1], curve[2], curve[3]);
const float r_curve = P_curve4.w;
float3 P = ray_P + ray_D * t;
const float3 P_curve = make_float3(P_curve4);
const float4 dPdu4 = metal::catmull_rom_derivative(u, curve[0], curve[1], curve[2], curve[3]);
const float3 dPdu = make_float3(dPdu4);
const float3 tangent = normalize(dPdu);
const float3 bitangent = normalize(cross(tangent, -ray_D));
float v = dot(P - P_curve, bitangent) / r_curve;
return clamp(v, -1.0, 1.0f);
}
#endif /* __HAIR__ */
/* Scene intersection. */
ccl_device_intersect bool scene_intersect(KernelGlobals kg,
const ccl_private Ray *ray,
const uint visibility,
ccl_private Intersection *isect)
{
metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax);
metalrt_intersector_type metalrt_intersect;
metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque);
metalrt_intersect.assume_geometry_type(
metal::raytracing::geometry_type::triangle |
(kernel_data.bvh.have_curves ? metal::raytracing::geometry_type::curve :
metal::raytracing::geometry_type::none) |
(kernel_data.bvh.have_points ? metal::raytracing::geometry_type::bounding_box :
metal::raytracing::geometry_type::none));
typename metalrt_intersector_type::result_type intersection;
MetalRTIntersectionPayload payload;
payload.self_prim = ray->self.prim;
payload.self_object = ray->self.object;
payload.visibility = visibility;
uint ray_mask = visibility & 0xFF;
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
ray_mask = 0xFF;
}
#if defined(__METALRT_MOTION__)
intersection = metalrt_intersect.intersect(r,
metal_ancillaries->accel_struct,
ray_mask,
ray->time,
metal_ancillaries->ift_default,
payload);
#else
intersection = metalrt_intersect.intersect(
r, metal_ancillaries->accel_struct, ray_mask, metal_ancillaries->ift_default, payload);
#endif
if (intersection.type == intersection_type::none) {
isect->t = ray->tmax;
isect->type = PRIMITIVE_NONE;
return false;
}
isect->object = intersection.instance_id;
isect->t = intersection.distance;
if (intersection.type == intersection_type::triangle) {
isect->prim = intersection.primitive_id + intersection.user_instance_id;
isect->type = kernel_data_fetch(objects, intersection.instance_id).primitive_type;
isect->u = intersection.triangle_barycentric_coord.x;
isect->v = intersection.triangle_barycentric_coord.y;
}
#ifdef __HAIR__
else if (kernel_data.bvh.have_curves && intersection.type == intersection_type::curve) {
int prim = intersection.primitive_id + intersection.user_instance_id;
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, prim);
isect->prim = segment.prim;
isect->type = segment.type;
isect->u = intersection.curve_parameter;
if ((segment.type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) {
isect->v = curve_ribbon_v(kg,
intersection.curve_parameter,
intersection.distance,
ray,
intersection.instance_id,
segment.prim,
segment.type);
}
else {
isect->v = 0.0f;
}
}
#endif /* __HAIR__ */
#ifdef __POINTCLOUD__
else if (kernel_data.bvh.have_points && intersection.type == intersection_type::bounding_box) {
const int object = intersection.instance_id;
const uint prim = intersection.primitive_id + intersection.user_instance_id;
const int prim_type = kernel_data_fetch(objects, object).primitive_type;
if (!(kernel_data_fetch(object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED)) {
float3 idir;
# if defined(__METALRT_MOTION__)
bvh_instance_motion_push(nullptr, object, ray, &r.origin, &r.direction, &idir);
# else
bvh_instance_push(nullptr, object, ray, &r.origin, &r.direction, &idir);
# endif
}
if (prim_type & PRIMITIVE_POINT) {
if (!point_intersect(nullptr,
isect,
r.origin,
r.direction,
ray->tmin,
ray->tmax,
object,
prim,
ray->time,
prim_type))
{
/* Shouldn't get here */
kernel_assert(!"Intersection mismatch");
isect->t = ray->tmax;
isect->type = PRIMITIVE_NONE;
return false;
}
return true;
}
}
#endif /* __POINTCLOUD__ */
return true;
}
ccl_device_intersect bool scene_intersect_shadow(KernelGlobals kg,
const ccl_private Ray *ray,
const uint visibility)
{
metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax);
metalrt_intersector_type metalrt_intersect;
metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque);
metalrt_intersect.assume_geometry_type(
metal::raytracing::geometry_type::triangle |
(kernel_data.bvh.have_curves ? metal::raytracing::geometry_type::curve :
metal::raytracing::geometry_type::none) |
(kernel_data.bvh.have_points ? metal::raytracing::geometry_type::bounding_box :
metal::raytracing::geometry_type::none));
typename metalrt_intersector_type::result_type intersection;
metalrt_intersect.accept_any_intersection(true);
MetalRTIntersectionShadowPayload payload;
payload.self = ray->self;
payload.visibility = visibility;
uint ray_mask = visibility & 0xFF;
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
ray_mask = 0xFF;
}
#if defined(__METALRT_MOTION__)
intersection = metalrt_intersect.intersect(r,
metal_ancillaries->accel_struct,
ray_mask,
ray->time,
metal_ancillaries->ift_shadow,
payload);
#else
intersection = metalrt_intersect.intersect(
r, metal_ancillaries->accel_struct, ray_mask, metal_ancillaries->ift_shadow, payload);
#endif
return (intersection.type != intersection_type::none);
}
#ifdef __BVH_LOCAL__
template<bool single_hit = false>
ccl_device_intersect bool scene_intersect_local(KernelGlobals kg,
const ccl_private Ray *ray,
ccl_private LocalIntersection *local_isect,
const int local_object,
ccl_private uint *lcg_state,
const int max_hits)
{
uint primitive_id_offset = kernel_data_fetch(object_prim_offset, local_object);
metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax);
# if defined(__METALRT_MOTION__)
metalrt_intersector_type metalrt_intersect;
typename metalrt_intersector_type::result_type intersection;
# else
metalrt_blas_intersector_type metalrt_intersect;
typename metalrt_blas_intersector_type::result_type intersection;
if (!(kernel_data_fetch(object_flag, local_object) & SD_OBJECT_TRANSFORM_APPLIED)) {
/* Transform the ray into object's local space. */
Transform itfm = kernel_data_fetch(objects, local_object).itfm;
r.origin = transform_point(&itfm, r.origin);
r.direction = transform_direction(&itfm, r.direction);
}
# endif
metalrt_intersect.assume_geometry_type(metal::raytracing::geometry_type::triangle);
if (single_hit) {
MetalRTIntersectionLocalPayload_single_hit payload;
payload.self_prim = ray->self.prim - primitive_id_offset;
# if defined(__METALRT_MOTION__)
/* We can't skip over the top-level BVH in the motion blur case, so still need to do
* the self-object check. */
payload.self_object = local_object;
metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque);
intersection = metalrt_intersect.intersect(r,
metal_ancillaries->accel_struct,
~0,
ray->time,
metal_ancillaries->ift_local_single_hit_mblur,
payload);
# else
/* We only need custom intersection filtering (i.e. non_opaque) if we are performing a
* self-primitive intersection check. */
metalrt_intersect.force_opacity((ray->self.prim == PRIM_NONE) ?
metal::raytracing::forced_opacity::opaque :
metal::raytracing::forced_opacity::non_opaque);
intersection = metalrt_intersect.intersect(
r,
metal_ancillaries->blas_accel_structs[local_object].blas,
metal_ancillaries->ift_local_single_hit,
payload);
# endif
if (intersection.type == intersection_type::none) {
local_isect->num_hits = 0;
return false;
}
uint prim = intersection.primitive_id + primitive_id_offset;
int prim_type = kernel_data_fetch(objects, local_object).primitive_type;
local_isect->num_hits = 1;
local_isect->hits[0].prim = prim;
local_isect->hits[0].type = prim_type;
local_isect->hits[0].object = local_object;
local_isect->hits[0].u = intersection.triangle_barycentric_coord.x;
local_isect->hits[0].v = intersection.triangle_barycentric_coord.y;
local_isect->hits[0].t = intersection.distance;
const int position_offset = kernel_data_fetch(objects, local_object).position_offset;
const packed_uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
const float3 tri_a = float3(kernel_data_fetch(tri_verts, position_offset + tri_vindex.x));
const float3 tri_b = float3(kernel_data_fetch(tri_verts, position_offset + tri_vindex.y));
const float3 tri_c = float3(kernel_data_fetch(tri_verts, position_offset + tri_vindex.z));
local_isect->Ng[0] = normalize(cross(tri_b - tri_a, tri_c - tri_a));
return true;
}
else {
MetalRTIntersectionLocalPayload payload;
payload.self_prim = ray->self.prim - primitive_id_offset;
payload.max_hits = max_hits;
payload.num_hits = 0;
if (lcg_state) {
payload.has_lcg_state = true;
payload.lcg_state = *lcg_state;
}
else {
payload.has_lcg_state = false;
}
metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque);
# if defined(__METALRT_MOTION__)
/* We can't skip over the top-level BVH in the motion blur case, so still need to do
* the self-object check. */
payload.self_object = local_object;
intersection = metalrt_intersect.intersect(r,
metal_ancillaries->accel_struct,
~0,
ray->time,
metal_ancillaries->ift_local_mblur,
payload);
# else
intersection = metalrt_intersect.intersect(
r,
metal_ancillaries->blas_accel_structs[local_object].blas,
metal_ancillaries->ift_local,
payload);
# endif
if (max_hits == 0) {
/* Special case for when no hit information is requested, just report that something was hit
*/
return (intersection.type != intersection_type::none);
}
if (lcg_state) {
*lcg_state = payload.lcg_state;
}
const int num_hits = payload.num_hits;
if (local_isect) {
/* Record geometric normal */
int prim_type = kernel_data_fetch(objects, local_object).primitive_type;
/* Number of hits counted can be higher than recorded due to reservoir sampling. */
local_isect->num_hits = num_hits;
const int num_recorded_hits = min(payload.num_hits, max_hits);
for (int hit = 0; hit < num_recorded_hits; hit++) {
const uint prim = payload.hits[hit].prim + primitive_id_offset;
local_isect->hits[hit].prim = prim;
local_isect->hits[hit].t = payload.hits[hit].t;
local_isect->hits[hit].u = payload.hits[hit].u;
local_isect->hits[hit].v = payload.hits[hit].v;
local_isect->hits[hit].object = local_object;
local_isect->hits[hit].type = prim_type;
const int position_offset = kernel_data_fetch(objects, local_object).position_offset;
const packed_uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
const float3 tri_a = float3(kernel_data_fetch(tri_verts, position_offset + tri_vindex.x));
const float3 tri_b = float3(kernel_data_fetch(tri_verts, position_offset + tri_vindex.y));
const float3 tri_c = float3(kernel_data_fetch(tri_verts, position_offset + tri_vindex.z));
local_isect->Ng[hit] = normalize(cross(tri_b - tri_a, tri_c - tri_a));
}
}
return num_hits > 0;
}
}
#endif
#ifdef __TRANSPARENT_SHADOWS__
ccl_device_intersect void scene_intersect_shadow_all_metalrt(
const ccl_private Ray *ray, ccl_private BVHShadowAllPayload &ccl_restrict payload)
{
metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax);
metalrt_intersector_type metalrt_intersect;
metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque);
metalrt_intersect.assume_geometry_type(
metal::raytracing::geometry_type::triangle |
(kernel_data.bvh.have_curves ? metal::raytracing::geometry_type::curve :
metal::raytracing::geometry_type::none) |
(kernel_data.bvh.have_points ? metal::raytracing::geometry_type::bounding_box :
metal::raytracing::geometry_type::none));
uint ray_mask = payload.base.ray_visibility & 0xFF;
if (0 == ray_mask && (payload.base.ray_visibility & ~0xFF) != 0) {
ray_mask = 0xFF;
}
typename metalrt_intersector_type::result_type intersection;
# if defined(__METALRT_MOTION__)
intersection = metalrt_intersect.intersect(r,
metal_ancillaries->accel_struct,
ray_mask,
ray->time,
metal_ancillaries->ift_shadow_all,
payload);
# else
intersection = metalrt_intersect.intersect(
r, metal_ancillaries->accel_struct, ray_mask, metal_ancillaries->ift_shadow_all, payload);
# endif
(void)intersection;
}
#endif
#ifdef __VOLUME__
ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
const ccl_private Ray *ray,
ccl_private Intersection *isect,
const uint visibility)
{
metal::raytracing::ray r(ray->P, ray->D, ray->tmin, ray->tmax);
metalrt_intersector_type metalrt_intersect;
metalrt_intersect.force_opacity(metal::raytracing::forced_opacity::non_opaque);
metalrt_intersect.set_geometry_cull_mode(metal::raytracing::geometry_cull_mode::bounding_box |
metal::raytracing::geometry_cull_mode::curve);
metalrt_intersect.assume_geometry_type(
metal::raytracing::geometry_type::triangle |
(kernel_data.bvh.have_curves ? metal::raytracing::geometry_type::curve :
metal::raytracing::geometry_type::none) |
(kernel_data.bvh.have_points ? metal::raytracing::geometry_type::bounding_box :
metal::raytracing::geometry_type::none));
MetalRTIntersectionShadowPayload payload;
payload.self = ray->self;
payload.visibility = visibility;
uint ray_mask = visibility & 0xFF;
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
ray_mask = 0xFF;
}
typename metalrt_intersector_type::result_type intersection;
# if defined(__METALRT_MOTION__)
intersection = metalrt_intersect.intersect(r,
metal_ancillaries->accel_struct,
ray_mask,
ray->time,
metal_ancillaries->ift_volume,
payload);
# else
intersection = metalrt_intersect.intersect(
r, metal_ancillaries->accel_struct, ray_mask, metal_ancillaries->ift_volume, payload);
# endif
if (intersection.type == intersection_type::triangle) {
isect->prim = intersection.primitive_id + intersection.user_instance_id;
isect->type = kernel_data_fetch(objects, intersection.instance_id).primitive_type;
isect->u = intersection.triangle_barycentric_coord.x;
isect->v = intersection.triangle_barycentric_coord.y;
isect->object = intersection.instance_id;
isect->t = intersection.distance;
return true;
}
return false;
}
#endif
CCL_NAMESPACE_END

View File

@@ -0,0 +1,407 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#define __KERNEL_GPU__
#define __KERNEL_METAL__
#define CCL_NAMESPACE_BEGIN
#define CCL_NAMESPACE_END
#ifndef ATTR_FALLTHROUGH
# define ATTR_FALLTHROUGH
#endif
#include <metal_atomic>
#include <metal_pack>
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
#ifdef __KERNEL_METALRT__
using namespace metal::raytracing;
#endif
#pragma clang diagnostic ignored "-Wunused-variable"
#pragma clang diagnostic ignored "-Wsign-compare"
#pragma clang diagnostic ignored "-Wuninitialized"
#pragma clang diagnostic ignored "-Wc++17-extensions"
#pragma clang diagnostic ignored "-Wmacro-redefined"
/* Qualifiers */
#define ccl_device
#define ccl_device_inline ccl_device __attribute__((always_inline))
#define ccl_device_forceinline ccl_device __attribute__((always_inline))
#if defined(__KERNEL_METAL_APPLE__)
# define ccl_device_noinline ccl_device
#else
# define ccl_device_noinline ccl_device __attribute__((noinline))
#endif
#define ccl_device_extern extern "C"
#define ccl_device_noinline_cpu ccl_device
#define ccl_device_inline_method ccl_device
#define ccl_device_template_spec template<> ccl_device_inline
#define ccl_global device
#define ccl_inline_constant static constant constexpr
#define ccl_device_constant constant
#define ccl_static_constexpr static constant constexpr
#define ccl_constant constant
#define ccl_gpu_shared threadgroup
#define ccl_private thread
#ifdef __KERNEL_METALRT__
# define ccl_ray_data ray_data
#else
# define ccl_ray_data ccl_private
#endif
#define ccl_may_alias
#define ccl_restrict __restrict
#define ccl_align(n) alignas(n)
#define ccl_optional_struct_init
#define ccl_attr_maybe_unused
// Not supported by older MacOS versions (e.g., 13.0)
// #define ccl_attr_maybe_unused [[maybe_unused]]
/* No assert supported for Metal */
#define kernel_assert(cond)
#define offsetof(t, d) __builtin_offsetof(t, d)
#define ccl_gpu_global_id_x() metal_global_id
#define ccl_gpu_warp_size simdgroup_size
#define ccl_gpu_thread_idx_x simd_group_index
#define ccl_gpu_thread_mask(thread_warp) uint64_t((1ull << thread_warp) - 1)
#define ccl_gpu_ballot(predicate) ((uint64_t)((simd_vote::vote_t)simd_ballot(predicate)))
#define ccl_gpu_syncthreads() threadgroup_barrier(mem_flags::mem_threadgroup);
// clang-format off
/* kernel.h adapters */
#define ccl_gpu_kernel(block_num_threads, thread_num_registers)
#define ccl_gpu_kernel_threads(block_num_threads)
/* Convert a comma-separated list into a semicolon-separated list
* (so that we can generate a struct based on kernel entry-point parameters). */
#define FN0()
#define FN1(p1) p1;
#define FN2(p1, p2) p1; p2;
#define FN3(p1, p2, p3) p1; p2; p3;
#define FN4(p1, p2, p3, p4) p1; p2; p3; p4;
#define FN5(p1, p2, p3, p4, p5) p1; p2; p3; p4; p5;
#define FN6(p1, p2, p3, p4, p5, p6) p1; p2; p3; p4; p5; p6;
#define FN7(p1, p2, p3, p4, p5, p6, p7) p1; p2; p3; p4; p5; p6; p7;
#define FN8(p1, p2, p3, p4, p5, p6, p7, p8) p1; p2; p3; p4; p5; p6; p7; p8;
#define FN9(p1, p2, p3, p4, p5, p6, p7, p8, p9) p1; p2; p3; p4; p5; p6; p7; p8; p9;
#define FN10(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10;
#define FN11(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11;
#define FN12(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12;
#define FN13(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13;
#define FN14(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14;
#define FN15(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15;
#define FN16(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16;
#define FN17(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16; p17;
#define FN18(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16; p17; p18;
#define FN19(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16; p17; p18; p19;
#define FN20(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20) p1; p2; p3; p4; p5; p6; p7; p8; p9; p10; p11; p12; p13; p14; p15; p16; p17; p18; p19; p20;
#define GET_LAST_ARG(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20, ...) p20
#define PARAMS_MAKER(...) GET_LAST_ARG(__VA_ARGS__, FN20, FN19, FN18, FN17, FN16, FN15, FN14, FN13, FN12, FN11, FN10, FN9, FN8, FN7, FN6, FN5, FN4, FN3, FN2, FN1, FN0)
/* Generate a struct containing the entry-point parameters and a "run"
* method which can access them implicitly via this-> */
#ifdef __METAL_GLOBAL_BUILTINS__
#define ccl_gpu_kernel_signature(name, ...) \
struct kernel_gpu_##name \
{ \
PARAMS_MAKER(__VA_ARGS__)(__VA_ARGS__) \
void run(thread MetalKernelContext& context, \
threadgroup atomic_int *threadgroup_array) ccl_global const; \
}; \
kernel void cycles_metal_##name(device const kernel_gpu_##name *params_struct, \
constant KernelParamsMetal &ccl_restrict _launch_params_metal, \
constant MetalAncillaries *_metal_ancillaries, \
threadgroup atomic_int *threadgroup_array[[ threadgroup(0) ]]) { \
MetalKernelContext context(_launch_params_metal, _metal_ancillaries); \
params_struct->run(context, threadgroup_array); \
} \
void kernel_gpu_##name::run(thread MetalKernelContext& context, \
threadgroup atomic_int *threadgroup_array) ccl_global const
#else
/* On macOS versions before 14.x, builtin constants (e.g. metal_global_id) must
* be accessed through attributed entry-point parameters. */
#define ccl_gpu_kernel_signature(name, ...) \
struct kernel_gpu_##name \
{ \
PARAMS_MAKER(__VA_ARGS__)(__VA_ARGS__) \
void run(thread MetalKernelContext& context, \
threadgroup atomic_int *threadgroup_array, \
const uint metal_global_id, \
const ushort metal_local_id, \
const ushort metal_local_size, \
const uint metal_grid_id, \
uint simdgroup_size, \
uint simd_lane_index, \
uint simd_group_index, \
uint num_simd_groups) ccl_global const; \
}; \
kernel void cycles_metal_##name(device const kernel_gpu_##name *params_struct, \
constant KernelParamsMetal &ccl_restrict _launch_params_metal, \
constant MetalAncillaries *_metal_ancillaries, \
threadgroup atomic_int *threadgroup_array[[ threadgroup(0) ]], \
const uint metal_global_id [[thread_position_in_grid]], \
const ushort metal_local_id [[thread_position_in_threadgroup]], \
const ushort metal_local_size [[threads_per_threadgroup]], \
const uint metal_grid_id [[threadgroup_position_in_grid]], \
uint simdgroup_size [[threads_per_simdgroup]], \
uint simd_lane_index [[thread_index_in_simdgroup]], \
uint simd_group_index [[simdgroup_index_in_threadgroup]], \
uint num_simd_groups [[simdgroups_per_threadgroup]]) { \
MetalKernelContext context(_launch_params_metal, _metal_ancillaries); \
params_struct->run(context, threadgroup_array, metal_global_id, metal_local_id, metal_local_size, metal_grid_id, simdgroup_size, simd_lane_index, simd_group_index, num_simd_groups); \
} \
void kernel_gpu_##name::run(thread MetalKernelContext& context, \
threadgroup atomic_int *threadgroup_array, \
const uint metal_global_id, \
const ushort metal_local_id, \
const ushort metal_local_size, \
const uint metal_grid_id, \
uint simdgroup_size, \
uint simd_lane_index, \
uint simd_group_index, \
uint num_simd_groups) ccl_global const
#endif /* __METAL_GLOBAL_BUILTINS__ */
#define ccl_gpu_kernel_postfix
#define ccl_gpu_kernel_call(x) context.x
#define ccl_gpu_kernel_within_bounds(i,n) true
/* define a function object where "func" is the lambda body, and additional parameters are used to specify captured state. */
#define ccl_gpu_kernel_lambda(func, ...) \
struct KernelLambda \
{ \
KernelLambda(ccl_private MetalKernelContext &_context) : context(_context) {} \
ccl_private MetalKernelContext &context; \
__VA_ARGS__; \
int operator()(const int state) const { return (func); } \
} ccl_gpu_kernel_lambda_pass(context)
// clang-format on
/* make_type definitions with Metal style element initializers */
ccl_device_forceinline float2 make_float2(const float x, const float y)
{
return float2(x, y);
}
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
{
return float3(x, y, z);
}
ccl_device_forceinline float4 make_float4(const float x,
const float y,
const float z,
const float w)
{
return float4(x, y, z, w);
}
ccl_device_forceinline int2 make_int2(const int x, const int y)
{
return int2(x, y);
}
ccl_device_forceinline int3 make_int3(const int x, const int y, const int z)
{
return int3(x, y, z);
}
ccl_device_forceinline int4 make_int4(const int x, const int y, const int z, const int w)
{
return int4(x, y, z, w);
}
ccl_device_forceinline uint2 make_uint2(const uint x, const uint y)
{
return uint2(x, y);
}
ccl_device_forceinline uint3 make_uint3(const uint x, const uint y, const uint z)
{
return uint3(x, y, z);
}
ccl_device_forceinline uint4 make_uint4(const uint x, const uint y, const uint z, const uint w)
{
return uint4(x, y, z, w);
}
ccl_device_forceinline uchar4 make_uchar4(const uchar x,
const uchar y,
const uchar z,
const uchar w)
{
return uchar4(x, y, z, w);
}
/* Math functions */
#define __uint_as_float(x) as_type<float>(x)
#define __float_as_uint(x) as_type<uint>(x)
#define __int_as_float(x) as_type<float>(x)
#define __float_as_int(x) as_type<int>(x)
#define __float2half(x) half(x)
#define powf(x, y) pow(float(x), float(y))
#define fabsf(x) fabs(float(x))
#define copysignf(x, y) copysign(float(x), float(y))
#define asinf(x) asin(float(x))
#define acosf(x) acos(float(x))
#define atanf(x) atan(float(x))
#define floorf(x) floor(float(x))
#define ceilf(x) ceil(float(x))
#define roundf(x) round(float(x))
#define hypotf(x, y) hypot(float(x), float(y))
#define atan2f(x, y) atan2(float(x), float(y))
#define fmaxf(x, y) fmax(float(x), float(y))
#define fminf(x, y) fmin(float(x), float(y))
#define fmodf(x, y) fmod(float(x), float(y))
#define sinhf(x) sinh(float(x))
#define coshf(x) cosh(float(x))
#define tanhf(x) tanh(float(x))
#define saturatef(x) saturate(float(x))
#define ldexpf(x, y) ldexp(float(x), int(y))
/* Use native functions with possibly lower precision for performance,
* no issues found so far. */
#define trigmode fast
#define sinf(x) trigmode::sin(float(x))
#define cosf(x) trigmode::cos(float(x))
#define tanf(x) trigmode::tan(float(x))
#define expf(x) trigmode::exp(float(x))
#define sqrtf(x) trigmode::sqrt(float(x))
#define logf(x) trigmode::log(float(x))
#define __device__
#ifdef __KERNEL_METALRT__
# if defined(__METALRT_MOTION__)
# define METALRT_TAGS instancing, instance_motion, primitive_motion
# define METALRT_BLAS_TAGS , primitive_motion
# else
# define METALRT_TAGS instancing
# define METALRT_BLAS_TAGS
# endif /* __METALRT_MOTION__ */
# if defined(__METALRT_EXTENDED_LIMITS__)
# define METALRT_LIMITS , extended_limits
# else
# define METALRT_LIMITS
# endif /* __METALRT_MOTION__ */
typedef acceleration_structure<METALRT_TAGS> metalrt_as_type;
typedef intersection_function_table<triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS>
metalrt_ift_type;
typedef metal::raytracing::intersector<triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS>
metalrt_intersector_type;
# if defined(__METALRT_MOTION__)
typedef acceleration_structure<primitive_motion> metalrt_blas_as_type;
typedef intersection_function_table<triangle_data, curve_data, primitive_motion METALRT_LIMITS>
metalrt_blas_ift_type;
typedef metal::raytracing::intersector<triangle_data, curve_data, primitive_motion METALRT_LIMITS>
metalrt_blas_intersector_type;
# else
typedef acceleration_structure<> metalrt_blas_as_type;
typedef intersection_function_table<triangle_data, curve_data METALRT_LIMITS>
metalrt_blas_ift_type;
typedef metal::raytracing::intersector<triangle_data, curve_data METALRT_LIMITS>
metalrt_blas_intersector_type;
# endif
#endif /* __KERNEL_METALRT__ */
/* texture bindings and sampler setup */
/* TextureParamsMetal is reinterpreted as Texture2DParamsMetal. */
struct TextureParamsMetal {
uint64_t tex;
};
struct Texture2DParamsMetal {
texture2d<float, access::sample> tex;
};
#ifdef __KERNEL_METALRT__
struct MetalRTBlasWrapper {
metalrt_blas_as_type blas;
};
#endif
/* Additional Metal-specific resources which aren't encoded in KernelData.
* IMPORTANT: If this layout changes, ANCILLARY_SLOT_COUNT and the host-side encoding must change
* to match. */
struct MetalAncillaries {
device TextureParamsMetal *textures;
#ifdef __KERNEL_METALRT__
metalrt_as_type accel_struct;
constant MetalRTBlasWrapper *blas_accel_structs;
metalrt_ift_type ift_default;
metalrt_ift_type ift_shadow;
metalrt_ift_type ift_shadow_all;
metalrt_ift_type ift_volume;
metalrt_blas_ift_type ift_local;
metalrt_ift_type ift_local_mblur;
metalrt_blas_ift_type ift_local_single_hit;
metalrt_ift_type ift_local_single_hit_mblur;
#endif
};
#include "util/half.h"
#include "util/types.h"
enum SamplerType {
SamplerFilterNearest_AddressRepeat,
SamplerFilterNearest_AddressClampEdge,
SamplerFilterNearest_AddressClampZero,
SamplerFilterNearest_AddressMirroredRepeat,
SamplerFilterLinear_AddressRepeat,
SamplerFilterLinear_AddressClampEdge,
SamplerFilterLinear_AddressClampZero,
SamplerFilterLinear_AddressMirroredRepeat,
SamplerCount
};
constexpr constant array<sampler, SamplerCount> metal_samplers = {
sampler(address::repeat, filter::nearest),
sampler(address::clamp_to_edge, filter::nearest),
sampler(address::clamp_to_zero, filter::nearest),
sampler(address::mirrored_repeat, filter::nearest),
sampler(address::repeat, filter::linear),
sampler(address::clamp_to_edge, filter::linear),
sampler(address::clamp_to_zero, filter::linear),
sampler(address::mirrored_repeat, filter::linear),
};
#ifdef __METAL_GLOBAL_BUILTINS__
const uint metal_global_id [[thread_position_in_grid]];
const ushort metal_local_id [[thread_position_in_threadgroup]];
const ushort metal_local_size [[threads_per_threadgroup]];
const uint metal_grid_id [[threadgroup_position_in_grid]];
const uint simdgroup_size [[threads_per_simdgroup]];
const uint simd_lane_index [[thread_index_in_simdgroup]];
const uint simd_group_index [[simdgroup_index_in_threadgroup]];
const uint num_simd_groups [[simdgroups_per_threadgroup]];
#endif /* __METAL_GLOBAL_BUILTINS__ */

View File

@@ -0,0 +1,54 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
// clang-format off
#ifdef WITH_NANOVDB
# include "kernel/util/nanovdb.h"
#endif
/* Open the Metal kernel context class
* Necessary to access resource bindings */
class MetalKernelContext {
public:
constant KernelParamsMetal &launch_params_metal;
constant MetalAncillaries *metal_ancillaries;
MetalKernelContext(constant KernelParamsMetal &_launch_params_metal, constant MetalAncillaries * _metal_ancillaries)
: launch_params_metal(_launch_params_metal), metal_ancillaries(_metal_ancillaries)
{}
MetalKernelContext(constant KernelParamsMetal &_launch_params_metal)
: launch_params_metal(_launch_params_metal)
{}
/* texture fetch adapter functions */
using ccl_gpu_image_object_2D = uint64_t;
template<typename T>
inline __attribute__((__always_inline__))
T ccl_gpu_image_object_read_2D(ccl_gpu_image_object_2D tex, const float x, float y) const {
kernel_assert(0);
return 0;
}
// texture2d
template<>
inline __attribute__((__always_inline__))
float4 ccl_gpu_image_object_read_2D(ccl_gpu_image_object_2D tex, const float x, float y) const {
const uint tid(tex);
const uint sid(tex >> 32);
return ((ccl_global Texture2DParamsMetal*)metal_ancillaries->textures)[tid].tex.sample(metal_samplers[sid], float2(x, y));
}
template<>
inline __attribute__((__always_inline__))
float ccl_gpu_image_object_read_2D(ccl_gpu_image_object_2D tex, const float x, float y) const {
const uint tid(tex);
const uint sid(tex >> 32);
return ((ccl_global Texture2DParamsMetal*)metal_ancillaries->textures)[tid].tex.sample(metal_samplers[sid], float2(x, y)).x;
}
# include "kernel/device/gpu/image.h"
// clang-format on

View File

@@ -0,0 +1,11 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
}
; /* end of MetalKernelContext class definition */
/* Silently redirect into the MetalKernelContext instance */
/* NOTE: These macros will need maintaining as entry-points change. */
#undef kernel_integrator_state
#define kernel_integrator_state context.launch_params_metal.integrator_state

View File

@@ -0,0 +1,20 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
enum {
Kernel_DummyConstant,
#define KERNEL_STRUCT_MEMBER(parent, type, name) KernelData_##parent##_##name,
#include "kernel/data_template.h"
KernelData_kernel_features
};
#ifdef __KERNEL_METAL__
# define KERNEL_STRUCT_MEMBER(parent, type, name) \
constant type kernel_data_##parent##_##name \
[[function_constant(KernelData_##parent##_##name)]];
# include "kernel/data_template.h"
constant int kernel_data_kernel_features [[function_constant(KernelData_kernel_features)]];
#endif

View File

@@ -0,0 +1,40 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Constant Globals */
#include "kernel/types.h"
#include "kernel/integrator/state.h"
#include "kernel/util/profiler.h"
#include "util/color.h"
#include "util/types_image.h"
CCL_NAMESPACE_BEGIN
struct KernelParamsMetal {
#define KERNEL_DATA_ARRAY(type, name) const ccl_global type *name;
#define KERNEL_DATA_ARRAY_WRITABLE(type, name) ccl_global type *name;
#include "kernel/data_arrays.h"
const IntegratorStateGPU integrator_state;
const KernelData data;
};
struct KernelGlobalsGPU {
int unused[1];
};
using KernelGlobals = const ccl_global KernelGlobalsGPU *ccl_restrict;
/* Abstraction macros */
#define kernel_data launch_params_metal.data
#define kernel_data_fetch(name, index) launch_params_metal.name[index]
#define kernel_data_write(name, index, value) launch_params_metal.name[index] = (value)
#define kernel_data_array(name) launch_params_metal.name
#define kernel_integrator_state launch_params_metal.integrator_state
CCL_NAMESPACE_END

View File

@@ -0,0 +1,746 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Metal kernel entry points. */
/* NOTE: Must come prior to other includes. */
#include "kernel/device/metal/compat.h"
#include "kernel/device/metal/globals.h"
/* NOTE: Must come prior to the kernel.h. */
#include "kernel/device/metal/function_constants.h"
/* NOTE: Must come prior to the rest of the includes. */
#include "kernel/device/gpu/kernel.h"
/* The rest of the includes. */
#include "kernel/bvh/intersect_filter.h"
#include "kernel/geom/geom_intersect.h"
/* MetalRT intersection handlers. */
#ifdef __KERNEL_METALRT__
/* Intersection return types. */
/* For a bounding box intersection function. */
struct BoundingBoxIntersectionResult {
bool accept [[accept_intersection]];
bool continue_search [[continue_search]];
float distance [[distance]];
};
/* For a primitive intersection function. */
struct PrimitiveIntersectionResult {
bool accept [[accept_intersection]];
bool continue_search [[continue_search]];
};
enum { METALRT_HIT_TRIANGLE, METALRT_HIT_CURVE, METALRT_HIT_BOUNDING_BOX };
/* Hit functions. */
[[intersection(triangle, triangle_data, curve_data)]] PrimitiveIntersectionResult
__intersection__local_tri_single_hit(
ray_data MetalKernelContext::MetalRTIntersectionLocalPayload_single_hit &payload [[payload]],
uint primitive_id [[primitive_id]])
{
PrimitiveIntersectionResult result;
result.continue_search = true;
result.accept = (payload.self_prim != primitive_id);
return result;
}
[[intersection(
triangle, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
__intersection__local_tri_single_hit_mblur(
ray_data MetalKernelContext::MetalRTIntersectionLocalPayload_single_hit &payload [[payload]],
# if defined(__METALRT_MOTION__)
uint object [[instance_id]],
# endif
uint primitive_id [[primitive_id]])
{
PrimitiveIntersectionResult result;
result.continue_search = true;
# if defined(__METALRT_MOTION__)
result.accept = (payload.self_prim != primitive_id) && (payload.self_object == object);
# else
result.accept = (payload.self_prim != primitive_id);
# endif
return result;
}
template<typename TReturn, uint intersection_type>
TReturn metalrt_local_hit(constant KernelParamsMetal &launch_params_metal,
ray_data MetalKernelContext::MetalRTIntersectionLocalPayload &payload,
const uint prim,
const float2 barycentrics,
const float ray_tmax)
{
TReturn result;
# ifdef __BVH_LOCAL__
if (payload.self_prim == prim) {
/* Only intersect with matching object and skip self-intersection. */
result.accept = false;
result.continue_search = true;
return result;
}
const int max_hits = payload.max_hits;
if (max_hits == 0) {
/* Special case for when no hit information is requested, just report that something was hit.
*/
result.accept = true;
result.continue_search = false;
return result;
}
/* Make a copy of the lcg_state in the private address space, allowing to use utility function
* to find the hit index to write the intersection to. This function is used from both HW-RT
* code-path and non-HW-RT, making it hard to deal with the address spaces in the function
* signature. Hopefully, compiler is smart enough to eliminate this temporary copy. */
uint lcg_state = payload.lcg_state;
MetalKernelContext context(launch_params_metal);
const int hit_index = context.local_intersect_get_record_index(
&payload, ray_tmax, payload.has_lcg_state ? &lcg_state : nullptr, max_hits);
payload.lcg_state = lcg_state;
if (hit_index == -1) {
result.accept = false;
result.continue_search = true;
return result;
}
payload.hits[hit_index].prim = prim;
payload.hits[hit_index].t = ray_tmax;
payload.hits[hit_index].u = barycentrics.x;
payload.hits[hit_index].v = barycentrics.y;
/* Continue tracing (without this the trace call would return after the first hit). */
result.accept = false;
result.continue_search = true;
# endif
return result;
}
[[intersection(triangle, triangle_data, curve_data)]] PrimitiveIntersectionResult
__intersection__local_tri(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
ray_data MetalKernelContext::MetalRTIntersectionLocalPayload &payload
[[payload]],
uint primitive_id [[primitive_id]],
float2 barycentrics [[barycentric_coord]],
float ray_tmax [[distance]])
{
/* instance_id, aka the user_id has been removed. If we take this function we optimized the
* SSS for starting traversal from a primitive acceleration structure instead of the root of the
* global AS. this means we will always be intersecting the correct object no need for the
* user-id to check */
return metalrt_local_hit<PrimitiveIntersectionResult, METALRT_HIT_TRIANGLE>(
launch_params_metal, payload, primitive_id, barycentrics, ray_tmax);
}
[[intersection(
triangle, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
__intersection__local_tri_mblur(
constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
ray_data MetalKernelContext::MetalRTIntersectionLocalPayload &payload [[payload]],
uint primitive_id [[primitive_id]],
# if defined(__METALRT_MOTION__)
uint object [[instance_id]],
# endif
float2 barycentrics [[barycentric_coord]],
float ray_tmax [[distance]])
{
# if defined(__METALRT_MOTION__)
if (payload.self_object != object) {
PrimitiveIntersectionResult result;
result.continue_search = true;
result.accept = false;
return result;
}
# endif
return metalrt_local_hit<PrimitiveIntersectionResult, METALRT_HIT_TRIANGLE>(
launch_params_metal, payload, primitive_id, barycentrics, ray_tmax);
}
inline bool metalrt_curve_skip_end_cap(const int type, const float u)
{
return ((u == 0.0f || u == 1.0f) && (type & PRIMITIVE_CURVE) != PRIMITIVE_CURVE_THICK_LINEAR);
}
inline Intersection get_intersection(constant KernelParamsMetal &launch_params_metal,
const float t,
const float2 uv,
uint object,
uint prim)
{
Intersection isect;
isect.t = t;
isect.u = uv.x;
isect.v = uv.y;
isect.prim = prim;
isect.object = object;
isect.type = kernel_data_fetch(objects, object).primitive_type;
# ifdef __HAIR__
if (isect.type & PRIMITIVE_CURVE) {
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, prim);
isect.type = segment.type;
isect.prim = segment.prim;
}
# endif
if (isect.type & PRIMITIVE_POINT) {
isect.u = 0.0f;
isect.v = 0.0f;
}
return isect;
}
template<uint intersection_type>
bool metalrt_shadow_all_hit(constant KernelParamsMetal &launch_params_metal,
ray_data MetalKernelContext::BVHShadowAllPayload &payload,
uint object,
uint prim,
const float2 uv,
const float t,
const ccl_private Ray *ray = nullptr)
{
# if defined(__TRANSPARENT_SHADOWS__)
MetalKernelContext context(launch_params_metal);
KernelGlobals kg = nullptr;
const Intersection isect = get_intersection(launch_params_metal, t, uv, object, prim);
# ifdef __HAIR__
if constexpr (intersection_type == METALRT_HIT_CURVE) {
/* Filter out curve end-caps. */
if (metalrt_curve_skip_end_cap(isect.type, isect.u)) {
return true;
}
if ((isect.type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) {
if (!context.curve_ribbon_accept(
nullptr, isect.u, isect.t, ray, object, isect.prim, isect.type))
{
return true;
}
}
}
# endif /* __HAIR__ */
constexpr uint enabled_primitive_types = (intersection_type == METALRT_HIT_CURVE) ?
PRIMITIVE_CURVE :
(PRIMITIVE_ALL & ~PRIMITIVE_CURVE);
return context
.bvh_shadow_all_anyhit_filter<MetalKernelContext::ISECT_TEST_ALL, enabled_primitive_types>(
kg, payload.state, payload, payload.base.ray_self, payload.base.ray_visibility, isect);
# else /* __TRANSPARENT_SHADOWS__ */
payload.throughput = 0.0f;
return false;
# endif /* __TRANSPARENT_SHADOWS__ */
}
[[intersection(
triangle, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
__intersection__tri_shadow_all(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
ray_data MetalKernelContext::BVHShadowAllPayload &payload
[[payload]],
const unsigned int object [[instance_id]],
const unsigned int primitive_id [[primitive_id]],
const uint primitive_id_offset [[user_instance_id]],
const float2 uv [[barycentric_coord]],
const float t [[distance]])
{
uint prim = primitive_id + primitive_id_offset;
PrimitiveIntersectionResult result;
result.continue_search = metalrt_shadow_all_hit<METALRT_HIT_TRIANGLE>(
launch_params_metal, payload, object, prim, uv, t);
result.accept = !result.continue_search;
return result;
}
[[intersection(
triangle, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
__intersection__volume_tri(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
ray_data MetalKernelContext::MetalRTIntersectionShadowPayload &payload
[[payload]],
const unsigned int object [[instance_id]],
const unsigned int primitive_id [[primitive_id]],
const uint primitive_id_offset [[user_instance_id]])
{
PrimitiveIntersectionResult result;
result.continue_search = true;
KernelGlobals kg = nullptr;
MetalKernelContext context(launch_params_metal);
uint prim = primitive_id + primitive_id_offset;
if (context.bvh_volume_anyhit_triangle_filter(
kg, object, prim, payload.self, payload.visibility))
{
result.accept = false;
return result;
}
result.accept = true;
return result;
}
template<typename TReturnType, uint intersection_type>
inline TReturnType metalrt_visibility_test(
constant KernelParamsMetal &launch_params_metal,
ray_data MetalKernelContext::MetalRTIntersectionPayload &payload,
const uint object,
uint prim,
const float u,
const float t = 0.0f,
const ccl_private Ray *ray = nullptr)
{
TReturnType result;
if ((kernel_data_fetch(objects, object).visibility & payload.visibility) == 0) {
result.accept = false;
result.continue_search = true;
return result;
}
# ifdef __HAIR__
if constexpr (intersection_type == METALRT_HIT_CURVE) {
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, prim);
int type = segment.type;
prim = segment.prim;
/* Filter out curve end-caps. */
if (metalrt_curve_skip_end_cap(type, u)) {
result.accept = false;
result.continue_search = true;
return result;
}
if ((type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) {
MetalKernelContext context(launch_params_metal);
if (!context.curve_ribbon_accept(nullptr, u, t, ray, object, prim, type)) {
result.accept = false;
result.continue_search = true;
return result;
}
}
}
# endif
if (payload.self_object == object && payload.self_prim == prim) {
result.accept = false;
result.continue_search = true;
return result;
}
result.accept = true;
result.continue_search = true;
return result;
}
template<typename TReturnType, uint intersection_type>
inline TReturnType metalrt_visibility_test_shadow(
constant KernelParamsMetal &launch_params_metal,
ray_data MetalKernelContext::MetalRTIntersectionShadowPayload &payload,
const uint object,
uint prim,
const float u,
const float t = 0.0f,
const ccl_private Ray *ray = nullptr)
{
TReturnType result;
if ((kernel_data_fetch(objects, object).visibility & payload.visibility) == 0) {
result.accept = false;
return result;
}
# ifdef __HAIR__
if constexpr (intersection_type == METALRT_HIT_CURVE) {
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, prim);
int type = segment.type;
prim = segment.prim;
/* Filter out curve end-caps. */
if (metalrt_curve_skip_end_cap(type, u)) {
result.accept = false;
result.continue_search = true;
return result;
}
if ((type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) {
MetalKernelContext context(launch_params_metal);
if (!context.curve_ribbon_accept(nullptr, u, t, ray, object, prim, type)) {
result.accept = false;
result.continue_search = true;
return result;
}
}
}
# endif
MetalKernelContext context(launch_params_metal);
/* Shadow ray early termination. */
# ifdef __SHADOW_LINKING__
if (context.intersection_skip_shadow_link(nullptr, payload.self, object)) {
result.accept = false;
result.continue_search = true;
return result;
}
# endif
if (context.intersection_skip_self_shadow(payload.self, object, prim)) {
result.accept = false;
result.continue_search = true;
return result;
}
else {
result.accept = true;
result.continue_search = false;
return result;
}
result.accept = true;
result.continue_search = true;
return result;
}
[[intersection(
triangle, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
__intersection__tri(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
ray_data MetalKernelContext::MetalRTIntersectionPayload &payload [[payload]],
const unsigned int object [[instance_id]],
const uint primitive_id_offset [[user_instance_id]],
const unsigned int primitive_id [[primitive_id]])
{
PrimitiveIntersectionResult result;
result.continue_search = true;
if ((kernel_data_fetch(objects, object).visibility & payload.visibility) == 0) {
result.accept = false;
return result;
}
result.accept = (payload.self_object != object ||
payload.self_prim != (primitive_id + primitive_id_offset));
return result;
}
[[intersection(
triangle, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
__intersection__tri_shadow(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
ray_data MetalKernelContext::MetalRTIntersectionShadowPayload &payload
[[payload]],
const unsigned int object [[instance_id]],
const uint primitive_id_offset [[user_instance_id]],
const unsigned int primitive_id [[primitive_id]])
{
uint prim = primitive_id + primitive_id_offset;
PrimitiveIntersectionResult result =
metalrt_visibility_test_shadow<PrimitiveIntersectionResult, METALRT_HIT_TRIANGLE>(
launch_params_metal, payload, object, prim, 0.0f);
return result;
}
/* Primitive intersection functions. */
[[intersection(
curve, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
__intersection__curve(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
ray_data MetalKernelContext::MetalRTIntersectionPayload &payload [[payload]],
const uint object [[instance_id]],
const uint primitive_id [[primitive_id]],
const uint primitive_id_offset [[user_instance_id]],
float distance [[distance]],
const float3 ray_P [[origin]],
const float3 ray_D [[direction]],
float u [[curve_parameter]],
const float ray_tmin [[min_distance]],
const float ray_tmax [[max_distance]]
# if defined(__METALRT_MOTION__)
,
const float time [[time]]
# endif
)
{
uint prim = primitive_id + primitive_id_offset;
Ray ray;
ray.P = ray_P;
ray.D = ray_D;
# if defined(__METALRT_MOTION__)
ray.time = time;
# endif
PrimitiveIntersectionResult result =
metalrt_visibility_test<PrimitiveIntersectionResult, METALRT_HIT_CURVE>(
launch_params_metal, payload, object, prim, u, distance, &ray);
return result;
}
[[intersection(
curve, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
__intersection__curve_shadow(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
ray_data MetalKernelContext::MetalRTIntersectionShadowPayload &payload
[[payload]],
const uint object [[instance_id]],
const uint primitive_id [[primitive_id]],
const uint primitive_id_offset [[user_instance_id]],
float distance [[distance]],
const float3 ray_P [[origin]],
const float3 ray_D [[direction]],
float u [[curve_parameter]],
const float ray_tmin [[min_distance]],
const float ray_tmax [[max_distance]]
# if defined(__METALRT_MOTION__)
,
const float time [[time]]
# endif
)
{
uint prim = primitive_id + primitive_id_offset;
Ray ray;
ray.P = ray_P;
ray.D = ray_D;
# if defined(__METALRT_MOTION__)
ray.time = time;
# endif
PrimitiveIntersectionResult result =
metalrt_visibility_test_shadow<PrimitiveIntersectionResult, METALRT_HIT_CURVE>(
launch_params_metal, payload, object, prim, u, distance, &ray);
return result;
}
[[intersection(
curve, triangle_data, curve_data, METALRT_TAGS METALRT_LIMITS)]] PrimitiveIntersectionResult
__intersection__curve_shadow_all(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
ray_data MetalKernelContext::BVHShadowAllPayload &payload
[[payload]],
const uint object [[instance_id]],
const uint primitive_id [[primitive_id]],
const uint primitive_id_offset [[user_instance_id]],
const float3 ray_P [[origin]],
const float3 ray_D [[direction]],
# if defined(__METALRT_MOTION__)
const float time [[time]],
# endif
float u [[curve_parameter]],
float t [[distance]])
{
uint prim = primitive_id + primitive_id_offset;
PrimitiveIntersectionResult result;
Ray ray;
ray.P = ray_P;
ray.D = ray_D;
# if defined(__METALRT_MOTION__)
/* TODO(sergey): The time is not really needed.
* Only ray direction and origin are needed in curve_ribbon_accept(), so there might be a room
* for cleanup here. */
ray.time = time;
# endif
result.continue_search = metalrt_shadow_all_hit<METALRT_HIT_CURVE>(
launch_params_metal, payload, object, prim, float2(u, 0), t, &ray);
result.accept = !result.continue_search;
return result;
}
# ifdef __POINTCLOUD__
ccl_device_inline void metalrt_intersection_point_shadow_all(
constant KernelParamsMetal &launch_params_metal,
ray_data MetalKernelContext::BVHShadowAllPayload &payload,
const uint object,
const uint prim,
const uint type,
const float3 ray_P,
const float3 ray_D,
float time,
const float ray_tmin,
const float ray_tmax,
thread BoundingBoxIntersectionResult &result)
{
Intersection isect;
isect.t = ray_tmax;
MetalKernelContext context(launch_params_metal);
if (context.point_intersect(
nullptr, &isect, ray_P, ray_D, ray_tmin, isect.t, object, prim, time, type))
{
result.continue_search = metalrt_shadow_all_hit<METALRT_HIT_BOUNDING_BOX>(
launch_params_metal, payload, object, prim, float2(isect.u, isect.v), isect.t);
result.accept = !result.continue_search;
if (result.accept) {
result.distance = isect.t;
}
}
}
[[intersection(bounding_box,
triangle_data,
curve_data,
METALRT_TAGS METALRT_LIMITS)]] BoundingBoxIntersectionResult
__intersection__point(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
ray_data MetalKernelContext::MetalRTIntersectionPayload &payload [[payload]],
const uint object [[instance_id]],
const uint primitive_id [[primitive_id]],
const uint primitive_id_offset [[user_instance_id]],
const float3 ray_origin [[origin]],
const float3 ray_direction [[direction]],
# if defined(__METALRT_MOTION__)
const float time [[time]],
# endif
const float ray_tmin [[min_distance]],
const float ray_tmax [[max_distance]])
{
const uint prim = primitive_id + primitive_id_offset;
const int type = kernel_data_fetch(objects, object).primitive_type;
BoundingBoxIntersectionResult result;
result.accept = false;
result.continue_search = true;
result.distance = ray_tmax;
Intersection isect;
isect.t = ray_tmax;
# ifndef __METALRT_MOTION__
const float time = 0.0f;
# endif
MetalKernelContext context(launch_params_metal);
if (context.point_intersect(
nullptr, &isect, ray_origin, ray_direction, ray_tmin, isect.t, object, prim, time, type))
{
result = metalrt_visibility_test<BoundingBoxIntersectionResult, METALRT_HIT_BOUNDING_BOX>(
launch_params_metal, payload, object, prim, isect.u);
if (result.accept) {
result.distance = isect.t;
}
}
return result;
}
# endif /* __POINTCLOUD__ */
[[intersection(bounding_box,
triangle_data,
curve_data,
METALRT_TAGS METALRT_LIMITS)]] BoundingBoxIntersectionResult
__intersection__point_shadow(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
ray_data MetalKernelContext::MetalRTIntersectionShadowPayload &payload
[[payload]],
const uint object [[instance_id]],
const uint primitive_id [[primitive_id]],
const uint primitive_id_offset [[user_instance_id]],
const float3 ray_origin [[origin]],
const float3 ray_direction [[direction]],
# if defined(__METALRT_MOTION__)
const float time [[time]],
# endif
const float ray_tmin [[min_distance]],
const float ray_tmax [[max_distance]])
{
const uint prim = primitive_id + primitive_id_offset;
const int type = kernel_data_fetch(objects, object).primitive_type;
BoundingBoxIntersectionResult result;
result.accept = false;
result.continue_search = true;
result.distance = ray_tmax;
# ifdef __POINTCLOUD__
Intersection isect;
isect.t = ray_tmax;
# ifndef __METALRT_MOTION__
const float time = 0.0f;
# endif
MetalKernelContext context(launch_params_metal);
if (context.point_intersect(
nullptr, &isect, ray_origin, ray_direction, ray_tmin, isect.t, object, prim, time, type))
{
result =
metalrt_visibility_test_shadow<BoundingBoxIntersectionResult, METALRT_HIT_BOUNDING_BOX>(
launch_params_metal, payload, object, prim, isect.u);
if (result.accept) {
result.distance = isect.t;
}
}
# endif /* __POINTCLOUD__ */
return result;
}
[[intersection(bounding_box,
triangle_data,
curve_data,
METALRT_TAGS METALRT_LIMITS)]] BoundingBoxIntersectionResult
__intersection__point_shadow_all(constant KernelParamsMetal &launch_params_metal [[buffer(1)]],
ray_data MetalKernelContext::BVHShadowAllPayload &payload
[[payload]],
const uint object [[instance_id]],
const uint primitive_id [[primitive_id]],
const uint primitive_id_offset [[user_instance_id]],
const float3 ray_origin [[origin]],
const float3 ray_direction [[direction]],
# if defined(__METALRT_MOTION__)
const float time [[time]],
# endif
const float ray_tmin [[min_distance]],
const float ray_tmax [[max_distance]])
{
const uint prim = primitive_id + primitive_id_offset;
const int type = kernel_data_fetch(objects, object).primitive_type;
BoundingBoxIntersectionResult result;
result.accept = false;
result.continue_search = true;
result.distance = ray_tmax;
# ifdef __POINTCLOUD__
metalrt_intersection_point_shadow_all(launch_params_metal,
payload,
object,
prim,
type,
ray_origin,
ray_direction,
# if defined(__METALRT_MOTION__)
time,
# else
0.0f,
# endif
ray_tmin,
ray_tmax,
result);
# endif /* __POINTCLOUD__ */
return result;
}
#endif /* __KERNEL_METALRT__ */