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,160 @@
# SPDX-FileCopyrightText: 2011-2026 Blender Foundation
#
# SPDX-License-Identifier: Apache-2.0
set(INC
../../..
)
set(INC_SYS
)
set(SRC_KERNEL_DEVICE_OPTIX
kernel.cu
kernel_mnee.cu
kernel_shader_raytrace.cu
)
if(WITH_CYCLES_OSL)
math(EXPR OSL_LIBRARY_VERSION_CODE "${OSL_VERSION_MAJOR} * 10000 + ${OSL_VERSION_MINOR} * 100 + ${OSL_VERSION_PATCH}")
set(SRC_KERNEL_DEVICE_OPTIX
${SRC_KERNEL_DEVICE_OPTIX}
../../osl/services_optix.cu
kernel_osl.cu
kernel_osl_camera.cu
kernel_osl_mnee.cu
kernel_osl_shader_raytrace.cu
kernel_osl_volume.cu
)
endif()
set(SRC_KERNEL_DEVICE_OPTIX_HEADERS
bvh.h
compat.h
globals.h
)
set(LIB
)
if(WITH_CYCLES_CUDA_BINARIES AND WITH_CYCLES_DEVICE_OPTIX)
# CUDA version
cuda_get_version(CUDA_VERSION)
# Modifies in parent scope:
# - `optix_ptx`: appended with compressed ptx output.
function(cycles_optix_kernel_add name input flags)
set(output "${CMAKE_CURRENT_BINARY_DIR}/${name}.ptx")
set(output_compressed "${output}.zst")
set(cuda_flags ${flags}
-I "${OPTIX_INCLUDE_DIR}"
-I "${CMAKE_CURRENT_SOURCE_DIR}/../../.."
-o ${output})
if(WITH_CYCLES_OSL)
set(cuda_flags ${cuda_flags}
-D OSL_LIBRARY_VERSION_CODE=${OSL_LIBRARY_VERSION_CODE})
endif()
set(arch compute_50)
set(cuda_nvcc_executable ${CUDA_NVCC_EXECUTABLE})
set(cuda_version ${CUDA_VERSION})
if("${CUDA_VERSION}" GREATER_EQUAL 130) # Support for Maxwell, Pascal and Volta was dropped in CUDA 13
if(DEFINED CUDA11_NVCC_EXECUTABLE)
# Use CUDA 11 for the OptiX PTX kernel, to retain support for older architectures.
set(cuda_nvcc_executable ${CUDA11_NVCC_EXECUTABLE})
set(cuda_version 110)
else()
set(arch compute_75)
endif()
endif()
cuda_add_common_flags(${cuda_version} ${arch} "${cuda_flags}" cuda_flags)
add_custom_command(
OUTPUT
${output}
DEPENDS
${input}
${SRC_KERNEL_DEVICE_OPTIX_HEADERS}
$<TARGET_PROPERTY:cycles_kernel,INTERFACE_SOURCES>
COMMAND
${cuda_nvcc_executable}
--ptx
-arch=${arch}
${cuda_flags}
${input}
WORKING_DIRECTORY
"${CMAKE_CURRENT_SOURCE_DIR}"
)
unset(cuda_nvcc_executable)
add_custom_command(
OUTPUT ${output_compressed}
COMMAND "$<TARGET_FILE:zstd_compress>" ${output} ${output_compressed}
DEPENDS ${output})
list(APPEND optix_ptx ${output_compressed})
set(optix_ptx "${optix_ptx}" PARENT_SCOPE)
delayed_install("${CMAKE_CURRENT_BINARY_DIR}" "${output_compressed}" ${CYCLES_INSTALL_PATH}/lib)
endfunction()
cycles_optix_kernel_add(
kernel_optix
"kernel.cu"
"")
cycles_optix_kernel_add(
kernel_optix_mnee
"kernel_mnee.cu"
"")
cycles_optix_kernel_add(
kernel_optix_shader_raytrace
"kernel_shader_raytrace.cu"
"--keep-device-functions")
if(WITH_CYCLES_OSL)
cycles_optix_kernel_add(
kernel_optix_osl
"kernel_osl.cu"
"--relocatable-device-code=true")
cycles_optix_kernel_add(
kernel_optix_osl_shader_raytrace
"kernel_osl_shader_raytrace.cu"
"--relocatable-device-code=true")
cycles_optix_kernel_add(
kernel_optix_osl_mnee
"kernel_osl_mnee.cu"
"--relocatable-device-code=true")
cycles_optix_kernel_add(
kernel_optix_osl_volume
"kernel_osl_volume.cu"
"--relocatable-device-code=true")
cycles_optix_kernel_add(
kernel_optix_osl_camera
"kernel_osl_camera.cu"
"--relocatable-device-code=true")
cycles_optix_kernel_add(
kernel_optix_osl_services
"../../osl/services_optix.cu"
"--relocatable-device-code=true")
endif()
add_custom_target(cycles_kernel_optix
ALL
DEPENDS ${optix_ptx}
SOURCES ${SRC_KERNEL_DEVICE_OPTIX} ${SRC_KERNEL_DEVICE_OPTIX_HEADERS}
)
cycles_set_solution_folder(cycles_kernel_optix)
source_group("device\\optix" FILES ${SRC_KERNEL_DEVICE_OPTIX} ${SRC_KERNEL_DEVICE_OPTIX_HEADERS})
add_dependencies(cycles_kernel cycles_kernel_optix)
endif()
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_OPTIX}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix)
delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_OPTIX_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix)

View File

@@ -0,0 +1,644 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* OptiX implementation of ray-scene intersection.
*
* Note on the payload registers.
* Intersection and filtering functions might be sharing the same registers, even if it is not
* very obvious from the trace/traverse call. The registers that have special meaning and are to
* be kept "locked" to their meaning:
* uint p4 = visibility;
* uint p6 = pointer_pack_to_uint_0(ray);
* uint p7 = pointer_pack_to_uint_1(ray); */
#pragma once
#include "kernel/bvh/types.h"
#include "kernel/bvh/util.h"
#define OPTIX_DEFINE_ABI_VERSION_ONLY
#include <optix_function_table.h>
CCL_NAMESPACE_BEGIN
/* Utilities. */
template<typename T> ccl_device_forceinline T *get_payload_ptr_0()
{
return pointer_unpack_from_uint<T>(optixGetPayload_0(), optixGetPayload_1());
}
template<typename T> ccl_device_forceinline T *get_payload_ptr_2()
{
return pointer_unpack_from_uint<T>(optixGetPayload_2(), optixGetPayload_3());
}
template<typename T> ccl_device_forceinline T *get_payload_ptr_6()
{
return (T *)(((uint64_t)optixGetPayload_7() << 32) | optixGetPayload_6());
}
ccl_device_forceinline int get_object_id()
{
#ifdef __OBJECT_MOTION__
/* Always get the instance ID from the TLAS
* There might be a motion transform node between TLAS and BLAS which does not have one. */
return optixGetInstanceIdFromHandle(optixGetTransformListHandle(0));
#else
return optixGetInstanceId();
#endif
}
ccl_device_forceinline Intersection get_intersection()
{
Intersection isect;
isect.t = optixGetRayTmax();
isect.prim = optixGetPrimitiveIndex();
isect.object = get_object_id();
if (optixIsTriangleHit()) {
/* Triangle. */
const float2 barycentrics = optixGetTriangleBarycentrics();
isect.u = barycentrics.x;
isect.v = barycentrics.y;
isect.type = kernel_data_fetch(objects, isect.object).primitive_type;
}
#ifdef __HAIR__
else if ((optixGetHitKind() & (~PRIMITIVE_MOTION)) != PRIMITIVE_POINT) {
/* Curve. */
isect.u = __uint_as_float(optixGetAttribute_0());
isect.v = __uint_as_float(optixGetAttribute_1());
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, isect.prim);
isect.type = segment.type;
isect.prim = segment.prim;
}
#endif
else {
/* Point. */
isect.u = 0.0f;
isect.v = 0.0f;
isect.type = kernel_data_fetch(objects, isect.object).primitive_type;
}
return isect;
}
/* Hit/miss functions. */
extern "C" __global__ void __miss__kernel_optix_miss()
{
/* 'kernel_path_lamp_emission' checks intersection distance, so need to set it even on a miss. */
optixSetPayload_0(__float_as_uint(optixGetRayTmax()));
optixSetPayload_5(PRIMITIVE_NONE);
}
extern "C" __global__ void __anyhit__kernel_optix_ignore()
{
return optixIgnoreIntersection();
}
extern "C" __global__ void __closesthit__kernel_optix_ignore() {}
extern "C" __global__ void __anyhit__kernel_optix_local_hit()
{
#if defined(__HAIR__) || defined(__POINTCLOUD__)
if (!optixIsTriangleHit()) {
/* Ignore curves and points. */
return optixIgnoreIntersection();
}
#endif
#ifdef __BVH_LOCAL__
const int object = get_object_id();
if (object != optixGetPayload_4() /* local_object */) {
/* Only intersect with matching object. */
return optixIgnoreIntersection();
}
const int prim = optixGetPrimitiveIndex();
ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
if (intersection_skip_self_local(ray->self, prim)) {
return optixIgnoreIntersection();
}
const uint max_hits = optixGetPayload_5();
if (max_hits == 0) {
/* Special case for when no hit information is requested, just report that something was hit */
optixSetPayload_5(true);
return optixTerminateRay();
}
const float isect_t = optixGetRayTmax();
uint *const lcg_state = get_payload_ptr_0<uint>();
LocalIntersection *const local_isect = get_payload_ptr_2<LocalIntersection>();
const int hit_index = local_intersect_get_record_index(
local_isect, isect_t, lcg_state, max_hits);
if (hit_index == -1) {
return optixIgnoreIntersection();
}
Intersection *isect = &local_isect->hits[hit_index];
isect->t = isect_t;
isect->prim = prim;
isect->object = get_object_id();
isect->type = kernel_data_fetch(objects, isect->object).primitive_type;
const float2 barycentrics = optixGetTriangleBarycentrics();
isect->u = barycentrics.x;
isect->v = barycentrics.y;
/* Record geometric normal. */
const int position_offset = kernel_data_fetch(objects, object).position_offset;
const packed_uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
const float3 tri_a = kernel_data_fetch(tri_verts, position_offset + tri_vindex.x);
const float3 tri_b = kernel_data_fetch(tri_verts, position_offset + tri_vindex.y);
const float3 tri_c = kernel_data_fetch(tri_verts, position_offset + tri_vindex.z);
local_isect->Ng[hit_index] = normalize(cross(tri_b - tri_a, tri_c - tri_a));
/* Continue tracing (without this the trace call would return after the first hit). */
optixIgnoreIntersection();
#endif
}
extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit()
{
#ifdef __TRANSPARENT_SHADOWS__
KernelGlobals kg = nullptr;
ccl_private BVHShadowAllPayload *payload = get_payload_ptr_0<BVHShadowAllPayload>();
const uint ray_visibility = optixGetPayload_4();
ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
Intersection isect = get_intersection();
if (!bvh_shadow_all_anyhit_filter<ISECT_TEST_ALL>(
kg, payload->state, *payload, ray->self, ray_visibility, isect))
{
optixTerminateRay();
return;
}
/* The idea here is to accept the hit, so that traversal won't consider any more hits beyond the
* distance of the current hit anymore.
*
* We could accept the hit which is furthest away from the ones that are already recorded (for
* this `>` needs to be replaced with `>=`). However, doing so has a performance impact in the
* pabellon benchmark scene. The hypothesis here is that allowing to traverse one extra hit after
* the array is filled allows to hit an opaque surface and do early exit from the shadow shading.
*
* Similar to this logic (allowing an extra hit) was in the original OptiX integration, so we
* just keep following it to avoid performance regression. There is no the correct solution here,
* as it depends on the scene. For example, if there are many transparent surfaces with no opaque
* hit then it is faster to start accepting hits as soon as possible. However, if there are many
* transparent surfaces, followed up with an opaque surface, it is faster to not accept any hit
* and allow the opaque optimization to lead to an early output from the intersect-shade loop. */
if (isect.t > payload->max_record_isect_t) {
return;
}
optixIgnoreIntersection();
#endif
}
extern "C" __global__ void __anyhit__kernel_optix_volume_test()
{
#if defined(__HAIR__) || defined(__POINTCLOUD__)
if (!optixIsTriangleHit()) {
/* Ignore curves. */
return optixIgnoreIntersection();
}
#endif
KernelGlobals kg = nullptr;
const int object = get_object_id();
const int prim = optixGetPrimitiveIndex();
ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
const uint ray_visibility = optixGetPayload_4();
if (bvh_volume_anyhit_triangle_filter(kg, object, prim, ray->self, ray_visibility)) {
return optixIgnoreIntersection();
}
}
extern "C" __global__ void __anyhit__kernel_optix_visibility_test()
{
const uint object = get_object_id();
const uint visibility = optixGetPayload_4();
#ifdef __VISIBILITY_FLAG__
if ((kernel_data_fetch(objects, object).visibility & visibility) == 0) {
return optixIgnoreIntersection();
}
#endif
int prim = optixGetPrimitiveIndex();
if (optixIsTriangleHit()) {
/* Triangle. */
}
#ifdef __HAIR__
else if ((optixGetHitKind() & (~PRIMITIVE_MOTION)) != PRIMITIVE_POINT) {
/* Curve. */
prim = kernel_data_fetch(curve_segments, prim).prim;
}
#endif
ccl_private Ray *const ray = get_payload_ptr_6<Ray>();
if (visibility & PATH_RAY_VISIBILITY_SHADOW_OPAQUE) {
#ifdef __SHADOW_LINKING__
if (intersection_skip_shadow_link(nullptr, ray->self, object)) {
return optixIgnoreIntersection();
}
#endif
if (intersection_skip_self_shadow(ray->self, object, prim)) {
return optixIgnoreIntersection();
}
else {
/* Shadow ray early termination. */
return optixTerminateRay();
}
}
else {
if (intersection_skip_self(ray->self, object, prim)) {
return optixIgnoreIntersection();
}
}
}
extern "C" __global__ void __closesthit__kernel_optix_hit()
{
const int object = get_object_id();
const int prim = optixGetPrimitiveIndex();
optixSetPayload_0(__float_as_uint(optixGetRayTmax())); /* Intersection distance */
optixSetPayload_4(object);
if (optixIsTriangleHit()) {
const float2 barycentrics = optixGetTriangleBarycentrics();
optixSetPayload_1(__float_as_uint(barycentrics.x));
optixSetPayload_2(__float_as_uint(barycentrics.y));
optixSetPayload_3(prim);
optixSetPayload_5(kernel_data_fetch(objects, object).primitive_type);
}
else if ((optixGetHitKind() & (~PRIMITIVE_MOTION)) != PRIMITIVE_POINT) {
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, prim);
optixSetPayload_1(optixGetAttribute_0()); /* Same as 'optixGetCurveParameter()' */
optixSetPayload_2(optixGetAttribute_1());
optixSetPayload_3(segment.prim);
optixSetPayload_5(segment.type);
}
else {
optixSetPayload_1(0);
optixSetPayload_2(0);
optixSetPayload_3(prim);
optixSetPayload_5(kernel_data_fetch(objects, object).primitive_type);
}
}
/* Custom primitive intersection functions. */
#ifdef __HAIR__
ccl_device_inline void optix_intersection_curve(const int prim, const int type)
{
const int object = get_object_id();
# ifdef __VISIBILITY_FLAG__
const uint visibility = optixGetPayload_4();
if ((kernel_data_fetch(objects, object).visibility & visibility) == 0) {
return;
}
# endif
const float3 ray_P = optixGetObjectRayOrigin();
const float3 ray_D = optixGetObjectRayDirection();
const float ray_tmin = optixGetRayTmin();
# ifdef __OBJECT_MOTION__
const float time = optixGetRayTime();
# else
const float time = 0.0f;
# endif
Intersection isect;
isect.t = optixGetRayTmax();
if (curve_intersect(nullptr, &isect, ray_P, ray_D, ray_tmin, isect.t, object, prim, time, type))
{
static_assert(PRIMITIVE_ALL < 128, "Values >= 128 are reserved for OptiX internal use");
optixReportIntersection(isect.t,
type & PRIMITIVE_ALL,
__float_as_int(isect.u), /* Attribute_0 */
__float_as_int(isect.v)); /* Attribute_1 */
}
}
extern "C" __global__ void __intersection__curve_ribbon()
{
const KernelCurveSegment segment = kernel_data_fetch(curve_segments, optixGetPrimitiveIndex());
const int prim = segment.prim;
const int type = segment.type;
if ((type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) {
optix_intersection_curve(prim, type);
}
}
#endif
#ifdef __POINTCLOUD__
extern "C" __global__ void __intersection__point()
{
const int prim = optixGetPrimitiveIndex();
const int object = get_object_id();
const int type = kernel_data_fetch(objects, object).primitive_type;
# ifdef __VISIBILITY_FLAG__
const uint visibility = optixGetPayload_4();
if ((kernel_data_fetch(objects, object).visibility & visibility) == 0) {
return;
}
# endif
const float3 ray_P = optixGetObjectRayOrigin();
const float3 ray_D = optixGetObjectRayDirection();
const float ray_tmin = optixGetRayTmin();
# ifdef __OBJECT_MOTION__
const float time = optixGetRayTime();
# else
const float time = 0.0f;
# endif
Intersection isect;
isect.t = optixGetRayTmax();
if (point_intersect(nullptr, &isect, ray_P, ray_D, ray_tmin, isect.t, object, prim, time, type))
{
static_assert(PRIMITIVE_ALL < 128, "Values >= 128 are reserved for OptiX internal use");
optixReportIntersection(isect.t, type & PRIMITIVE_ALL);
}
}
#endif
/* Scene intersection. */
ccl_device_intersect bool scene_intersect(KernelGlobals kg,
const ccl_private Ray *ray,
const uint visibility,
ccl_private Intersection *isect)
{
/* Note: some registers have hardcoded meaning.
* Be careful when changing the values here. See the note at the top of this file for more
* details. */
uint p0 = 0;
uint p1 = 0;
uint p2 = 0;
uint p3 = 0;
uint p4 = visibility;
uint p5 = PRIMITIVE_NONE;
uint p6 = pointer_pack_to_uint_0(ray);
uint p7 = pointer_pack_to_uint_1(ray);
uint ray_mask = visibility & 0xFF;
uint ray_flags = OPTIX_RAY_FLAG_ENFORCE_ANYHIT;
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
ray_mask = 0xFF;
}
else if (visibility & PATH_RAY_VISIBILITY_SHADOW_OPAQUE) {
ray_flags |= OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT;
}
optixTrace(intersection_ray_valid(ray) ? kernel_data.device_bvh : 0,
ray->P,
ray->D,
ray->tmin,
ray->tmax,
ray->time,
ray_mask,
ray_flags,
0, /* SBT offset for PG_HITD */
0,
0,
p0,
p1,
p2,
p3,
p4,
p5,
p6,
p7);
isect->t = __uint_as_float(p0);
isect->u = __uint_as_float(p1);
isect->v = __uint_as_float(p2);
isect->prim = p3;
isect->object = p4;
isect->type = p5;
return p5 != PRIMITIVE_NONE;
}
ccl_device_intersect bool scene_intersect_shadow(KernelGlobals kg,
const ccl_private Ray *ray,
const uint visibility)
{
/* Note: some registers have hardcoded meaning.
* Be careful when changing the values here. See the note at the top of this file for more
* details. */
uint p0 = 0;
uint p1 = 0;
uint p2 = 0;
uint p3 = 0;
uint p4 = visibility;
uint p5 = PRIMITIVE_NONE;
uint p6 = pointer_pack_to_uint_0(ray);
uint p7 = pointer_pack_to_uint_1(ray);
uint ray_mask = visibility & 0xFF;
uint ray_flags = OPTIX_RAY_FLAG_ENFORCE_ANYHIT;
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
ray_mask = 0xFF;
}
else if (visibility & PATH_RAY_VISIBILITY_SHADOW_OPAQUE) {
ray_flags |= OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT;
}
optixTraverse(intersection_ray_valid(ray) ? kernel_data.device_bvh : 0,
ray->P,
ray->D,
ray->tmin,
ray->tmax,
ray->time,
ray_mask,
ray_flags,
0, /* SBT offset for PG_HITD */
0,
0,
p0,
p1,
p2,
p3,
p4,
p5,
p6,
p7);
return optixHitObjectIsHit();
}
#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)
{
/* Note: some registers have hardcoded meaning.
* Be careful when changing the values here. See the note at the top of this file for more
* details. */
uint p0 = pointer_pack_to_uint_0(lcg_state);
uint p1 = pointer_pack_to_uint_1(lcg_state);
uint p2 = pointer_pack_to_uint_0(local_isect);
uint p3 = pointer_pack_to_uint_1(local_isect);
uint p4 = local_object;
uint p6 = pointer_pack_to_uint_0(ray);
uint p7 = pointer_pack_to_uint_1(ray);
/* Is set to zero on miss or if ray is aborted, so can be used as return value. */
uint p5 = max_hits;
if (local_isect) {
local_isect->num_hits = 0; /* Initialize hit count to zero. */
}
optixTraverse(intersection_ray_valid(ray) ? kernel_data.device_bvh : 0,
ray->P,
ray->D,
ray->tmin,
ray->tmax,
ray->time,
0xFF,
/* Need to always call into __anyhit__kernel_optix_local_hit. */
OPTIX_RAY_FLAG_ENFORCE_ANYHIT,
2, /* SBT offset for PG_HITL */
0,
0,
p0,
p1,
p2,
p3,
p4,
p5,
p6,
p7);
return p5;
}
#endif
#ifdef __TRANSPARENT_SHADOWS__
ccl_device_intersect void scene_intersect_shadow_all_optix(
const ccl_private Ray *ccl_restrict ray,
const uint ray_visibility,
ccl_private BVHShadowAllPayload &ccl_restrict payload)
{
/* Note: some registers have hardcoded meaning.
* Be careful when changing the values here. See the note at the top of this file for more
* details. */
uint p0 = pointer_pack_to_uint_0(&payload);
uint p1 = pointer_pack_to_uint_1(&payload);
uint p2 = 0;
uint p3 = 0;
uint p4 = ray_visibility;
uint p5 = 0;
uint p6 = pointer_pack_to_uint_0(ray);
uint p7 = pointer_pack_to_uint_1(ray);
uint ray_mask = ray_visibility & 0xFF;
if (0 == ray_mask && (ray_visibility & ~0xFF) != 0) {
ray_mask = 0xFF;
}
optixTraverse(intersection_ray_valid(ray) ? kernel_data.device_bvh : 0,
ray->P,
ray->D,
ray->tmin,
ray->tmax,
ray->time,
ray_mask,
/* Need to always call into __anyhit__kernel_optix_shadow_all_hit. */
OPTIX_RAY_FLAG_ENFORCE_ANYHIT,
1, /* SBT offset for PG_HITS */
0,
0,
p0,
p1,
p2,
p3,
p4,
p5,
p6,
p7);
}
#endif
#ifdef __VOLUME__
ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg,
const ccl_private Ray *ray,
ccl_private Intersection *isect,
const uint visibility)
{
/* Note: some registers have hardcoded meaning.
* Be careful when changing the values here. See the note at the top of this file for more
* details. */
uint p0 = 0;
uint p1 = 0;
uint p2 = 0;
uint p3 = 0;
uint p4 = visibility;
uint p5 = PRIMITIVE_NONE;
uint p6 = pointer_pack_to_uint_0(ray);
uint p7 = pointer_pack_to_uint_1(ray);
uint ray_mask = visibility & 0xFF;
if (0 == ray_mask && (visibility & ~0xFF) != 0) {
ray_mask = 0xFF;
}
optixTrace(intersection_ray_valid(ray) ? kernel_data.device_bvh : 0,
ray->P,
ray->D,
ray->tmin,
ray->tmax,
ray->time,
ray_mask,
/* Need to always call into __anyhit__kernel_optix_volume_test. */
OPTIX_RAY_FLAG_ENFORCE_ANYHIT,
3, /* SBT offset for PG_HITV */
0,
0,
p0,
p1,
p2,
p3,
p4,
p5,
p6,
p7);
isect->t = __uint_as_float(p0);
isect->u = __uint_as_float(p1);
isect->v = __uint_as_float(p2);
isect->prim = p3;
isect->object = p4;
isect->type = p5;
return p5 != PRIMITIVE_NONE;
}
#endif
CCL_NAMESPACE_END

View File

@@ -0,0 +1,100 @@
/* SPDX-FileCopyrightText: 2019 NVIDIA Corporation
* SPDX-FileCopyrightText: 2019-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#define __KERNEL_GPU__
#define __KERNEL_CUDA__ /* OptiX kernels are implicitly CUDA kernels too */
#define __KERNEL_OPTIX__
#define CCL_NAMESPACE_BEGIN
#define CCL_NAMESPACE_END
#ifndef ATTR_FALLTHROUGH
# define ATTR_FALLTHROUGH
#endif
/* Manual definitions so we can compile without CUDA toolkit. */
#ifdef __CUDACC_RTC__
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
#else
# include <stdint.h>
#endif
#ifdef CYCLES_CUBIN_CC
# define FLT_MIN 1.175494350822287507969e-38f
# define FLT_MAX 340282346638528859811704183484516925440.0f
# define FLT_EPSILON 1.192092896e-07F
#endif
#define ccl_device \
static __device__ \
__forceinline__ // Function calls are bad for OptiX performance, so inline everything
#define ccl_device_extern extern "C" __device__
#define ccl_device_inline ccl_device
#define ccl_device_forceinline ccl_device
#define ccl_device_inline_method __device__ __forceinline__
#define ccl_device_template_spec template<> __device__ __forceinline__
#define ccl_device_noinline static __device__ __noinline__
#define ccl_device_noinline_cpu ccl_device
#define ccl_global
#define ccl_inline_constant static __constant__
#define ccl_device_constant __constant__ __device__
#define ccl_static_constexpr static constexpr
#define ccl_constant const
#define ccl_gpu_shared __shared__
#define ccl_private
#define ccl_ray_data ccl_private
#define ccl_may_alias
#define ccl_restrict __restrict__
#define ccl_align(n) __align__(n)
#define ccl_attr_maybe_unused [[maybe_unused]]
/* Zero initialize structs to help the compiler figure out scoping */
#define ccl_optional_struct_init = {}
/* No assert supported for CUDA */
#define kernel_assert(cond)
/* GPU texture objects */
typedef unsigned long long CUtexObject;
typedef CUtexObject ccl_gpu_image_object_2D;
template<typename T>
ccl_device_forceinline T ccl_gpu_image_object_read_2D(const ccl_gpu_image_object_2D texobj,
const float x,
const float y)
{
return tex2D<T>(texobj, x, y);
}
/* Half */
typedef unsigned short half;
ccl_device_forceinline half __float2half(const float f)
{
half val;
asm("{ cvt.rn.f16.f32 %0, %1;}\n" : "=h"(val) : "f"(f));
return val;
}
ccl_device_forceinline float __half2float(const half h)
{
float val;
asm("{ cvt.f32.f16 %0, %1;}\n" : "=f"(val) : "h"(h));
return val;
}
/* Types */
#include "util/half.h"
#include "util/types.h"
#define OPTIX_DONT_INCLUDE_CUDA
#include <optix.h>

View File

@@ -0,0 +1,65 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Constant Globals */
#pragma once
#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
/* Not actually used, just a nullptr pointer that gets passed everywhere, which we
* hope gets optimized out by the compiler. */
struct KernelGlobalsGPU {
int unused[1];
};
using KernelGlobals = const ccl_global KernelGlobalsGPU *ccl_restrict;
/* Launch parameters */
struct KernelParamsOptiX {
/* Kernel arguments */
const int *path_index_array;
float *render_buffer;
uint *shader_eval_cache_miss;
int shader_eval_offset;
/* Init kernel arguments */
int num_tiles;
int max_tile_work_size;
/* Global scene data and textures */
KernelData data;
#define KERNEL_DATA_ARRAY(type, name) const type *name;
#define KERNEL_DATA_ARRAY_WRITABLE(type, name) type *name;
#include "kernel/data_arrays.h"
/* Integrator state */
IntegratorStateGPU integrator_state;
void *osl_colorsystem;
};
#ifdef __NVCC__
extern "C"
# ifndef __CUDACC_RDC__
static
# endif
__constant__ KernelParamsOptiX kernel_params;
#endif
/* Abstraction macros */
#define kernel_data kernel_params.data
#define kernel_data_array(name) kernel_params.name
#define kernel_data_fetch(name, index) kernel_params.name[(index)]
#define kernel_data_write(name, index, value) kernel_params.name[(index)] = (value)
#define kernel_integrator_state kernel_params.integrator_state
CCL_NAMESPACE_END

View File

@@ -0,0 +1,68 @@
/* SPDX-FileCopyrightText: 2019, NVIDIA Corporation
* SPDX-FileCopyrightText: 2019-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
// clang-format off
#include "kernel/device/optix/compat.h"
#include "kernel/device/optix/globals.h"
#include "kernel/device/gpu/image.h" /* Texture lookup uses normal CUDA intrinsics. */
#include "kernel/tables.h"
#include "kernel/integrator/state.h"
#include "kernel/integrator/state_flow.h"
#include "kernel/integrator/state_util.h"
#include "kernel/integrator/intersect_closest.h"
#include "kernel/integrator/intersect_shadow.h"
#include "kernel/integrator/intersect_subsurface.h"
#include "kernel/integrator/intersect_volume_stack.h"
#include "kernel/integrator/intersect_dedicated_light.h"
// clang-format on
extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_closest()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_intersect_closest(nullptr, path_index, kernel_params.render_buffer);
}
extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_shadow()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_intersect_shadow(nullptr, path_index);
}
extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_subsurface()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_intersect_subsurface(nullptr, path_index);
}
extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_volume_stack()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_intersect_volume_stack(nullptr, path_index);
}
extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_dedicated_light()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_intersect_dedicated_light(nullptr, path_index);
}

View File

@@ -0,0 +1,19 @@
/* SPDX-FileCopyrightText: 2011-2026 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "kernel/device/optix/compat.h"
#include "kernel/device/optix/globals.h"
#include "kernel/device/gpu/image.h" /* Texture lookup uses normal CUDA intrinsics. */
#include "kernel/integrator/intersect_mnee.h"
extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_mnee()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_intersect_mnee(nullptr, path_index);
}

View File

@@ -0,0 +1,108 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#define WITH_OSL
/* Copy of the regular OptiX kernels with additional OSL support. */
#include "kernel/device/optix/kernel.cu"
#include "kernel/bake/bake.h"
#include "kernel/integrator/shade_background.h"
#include "kernel/integrator/shade_dedicated_light.h"
#include "kernel/integrator/shade_light.h"
#include "kernel/integrator/shade_shadow.h"
#include "kernel/integrator/shade_surface.h"
#include "kernel/device/gpu/work_stealing.h"
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_background()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_shade_background(nullptr, path_index, kernel_params.render_buffer);
}
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_light_nee()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_shade_light_nee(nullptr, path_index, kernel_params.render_buffer);
}
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_light_forward()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_shade_light_forward(nullptr, path_index, kernel_params.render_buffer);
}
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_surface()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_shade_surface(nullptr, path_index, kernel_params.render_buffer);
}
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_shadow()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_shade_shadow(nullptr, path_index, kernel_params.render_buffer);
}
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_dedicated_light()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_shade_dedicated_light(nullptr, path_index, kernel_params.render_buffer);
}
extern "C" __global__ void __raygen__kernel_optix_shader_eval_displace()
{
KernelShaderEvalInput *const input = (KernelShaderEvalInput *)kernel_params.path_index_array;
float *const output = kernel_params.render_buffer;
uint *const cache_miss = kernel_params.shader_eval_cache_miss;
const int global_index = kernel_params.shader_eval_offset + optixGetLaunchIndex().x;
kernel_displace_evaluate(nullptr, input, output, cache_miss, global_index);
}
extern "C" __global__ void __raygen__kernel_optix_shader_eval_background()
{
KernelShaderEvalInput *const input = (KernelShaderEvalInput *)kernel_params.path_index_array;
float *const output = kernel_params.render_buffer;
uint *const cache_miss = kernel_params.shader_eval_cache_miss;
const int global_index = kernel_params.shader_eval_offset + optixGetLaunchIndex().x;
kernel_background_evaluate(nullptr, input, output, cache_miss, global_index);
}
extern "C" __global__ void __raygen__kernel_optix_shader_eval_curve_shadow_transparency()
{
KernelShaderEvalInput *const input = (KernelShaderEvalInput *)kernel_params.path_index_array;
float *const output = kernel_params.render_buffer;
uint *const cache_miss = kernel_params.shader_eval_cache_miss;
const int global_index = kernel_params.shader_eval_offset + optixGetLaunchIndex().x;
kernel_curve_shadow_transparency_evaluate(nullptr, input, output, cache_miss, global_index);
}
extern "C" __global__ void __raygen__kernel_optix_shader_eval_volume_density()
{
KernelShaderEvalInput *const input = (KernelShaderEvalInput *)kernel_params.path_index_array;
float *const output = kernel_params.render_buffer;
uint *const cache_miss = kernel_params.shader_eval_cache_miss;
const int global_index = kernel_params.shader_eval_offset + optixGetLaunchIndex().x;
kernel_volume_density_evaluate(nullptr, input, output, cache_miss, global_index);
}

View File

@@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2011-2025 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#define WITH_OSL
#include "kernel/device/optix/compat.h"
#include "kernel/device/optix/globals.h"
#include "kernel/integrator/init_from_camera.h"
#include "kernel/device/gpu/work_stealing.h"
extern "C" __global__ void __raygen__kernel_optix_integrator_init_from_camera()
{
const int global_index = optixGetLaunchIndex().x;
const KernelWorkTile *tiles = (const KernelWorkTile *)kernel_params.path_index_array;
const int tile_index = global_index / kernel_params.max_tile_work_size;
const int tile_work_index = global_index - tile_index * kernel_params.max_tile_work_size;
const KernelWorkTile *tile = &tiles[tile_index];
if (tile_work_index >= tile->work_size) {
return;
}
const int path_index = tile->path_index_offset + tile_work_index;
uint x, y, sample;
get_work_pixel(tile, tile_work_index, &x, &y, &sample);
integrator_init_from_camera(nullptr, path_index, tile, kernel_params.render_buffer, x, y, sample);
}

View File

@@ -0,0 +1,22 @@
/* SPDX-FileCopyrightText: 2011-2026 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#define WITH_OSL
#include "kernel/device/optix/compat.h"
#include "kernel/device/optix/globals.h"
#include "kernel/bvh/bvh.h"
#include "kernel/integrator/path_state.h"
#include "kernel/integrator/intersect_mnee.h"
extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_mnee()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_intersect_mnee(nullptr, path_index);
}

View File

@@ -0,0 +1,19 @@
/* SPDX-FileCopyrightText: 2011-2026 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#define WITH_OSL
#include "kernel/device/optix/compat.h"
#include "kernel/device/optix/globals.h"
#include "kernel/integrator/shade_surface.h"
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_surface_raytrace()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_shade_surface_raytrace(nullptr, path_index, kernel_params.render_buffer);
}

View File

@@ -0,0 +1,34 @@
/* SPDX-FileCopyrightText: 2011-2026 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#define WITH_OSL
/* Volume shading raygens for OSL, loaded as a separate optix module so they
* can be compiled in parallel with the base OSL module and skipped for
* scenes without volumes. */
#include "kernel/device/optix/compat.h"
#include "kernel/device/optix/globals.h"
#include "kernel/film/data_passes.h"
#include "kernel/integrator/shade_volume.h"
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_volume()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_shade_volume(nullptr, path_index, kernel_params.render_buffer);
}
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_volume_ray_marching()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_shade_volume_ray_marching(nullptr, path_index, kernel_params.render_buffer);
}

View File

@@ -0,0 +1,22 @@
/* SPDX-FileCopyrightText: 2021-2026 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Copy of the regular kernels with additional shader ray-tracing kernel that takes
* much longer to compiler. This is only loaded when needed by the scene. */
#include "kernel/device/optix/compat.h"
#include "kernel/device/optix/globals.h"
#include "kernel/device/gpu/image.h"
#include "kernel/integrator/shade_surface.h"
extern "C" __global__ void __raygen__kernel_optix_integrator_shade_surface_raytrace()
{
const int global_index = optixGetLaunchIndex().x;
const int path_index = (kernel_params.path_index_array) ?
kernel_params.path_index_array[global_index] :
global_index;
integrator_shade_surface_raytrace(nullptr, path_index, kernel_params.render_buffer);
}