Files
test/intern/cycles/kernel/geom/subd_triangle.h
William Leeson 6c03339e48 Cycles: reduce mesh memory usage by unflattening
To improve mesh upload speeds and reduce the size of the scene data which allows larger scenes to be rendered.

The meshes in Cycles are currently stored as flattened meshes, where each triangle is stored as a set of 3 vertices. Unflattening writes out the vertices in a list according to the index buffer. This uses a lot of memory and for current hardware does not provide a noticeable benefit. This change unflattens the mesh by directly using the meshes vertex and index buffers directly and skips the unflattening. This change allows for larger scenes and also a reduction in the sizes of the meshes. Further it results in a decrease the amount of time it takes to upload the data to a GPU. This is especially important for when multiple GPUs are used in a single machine.

Pull Request #105173
2023-02-27 10:39:19 +01:00

665 lines
20 KiB
C

/* SPDX-License-Identifier: Apache-2.0
* Copyright 2011-2022 Blender Foundation */
/* Functions for retrieving attributes on triangles produced from subdivision meshes */
#pragma once
CCL_NAMESPACE_BEGIN
/* UV coords of triangle within patch */
ccl_device_inline void subd_triangle_patch_uv(KernelGlobals kg,
ccl_private const ShaderData *sd,
float2 uv[3])
{
uint3 tri_vindex = kernel_data_fetch(tri_vindex, sd->prim);
uv[0] = kernel_data_fetch(tri_patch_uv, tri_vindex.x);
uv[1] = kernel_data_fetch(tri_patch_uv, tri_vindex.y);
uv[2] = kernel_data_fetch(tri_patch_uv, tri_vindex.z);
}
/* Vertex indices of patch */
ccl_device_inline uint4 subd_triangle_patch_indices(KernelGlobals kg, int patch)
{
uint4 indices;
indices.x = kernel_data_fetch(patches, patch + 0);
indices.y = kernel_data_fetch(patches, patch + 1);
indices.z = kernel_data_fetch(patches, patch + 2);
indices.w = kernel_data_fetch(patches, patch + 3);
return indices;
}
/* Originating face for patch */
ccl_device_inline uint subd_triangle_patch_face(KernelGlobals kg, int patch)
{
return kernel_data_fetch(patches, patch + 4);
}
/* Number of corners on originating face */
ccl_device_inline uint subd_triangle_patch_num_corners(KernelGlobals kg, int patch)
{
return kernel_data_fetch(patches, patch + 5) & 0xffff;
}
/* Indices of the four corners that are used by the patch */
ccl_device_inline void subd_triangle_patch_corners(KernelGlobals kg, int patch, int corners[4])
{
uint4 data;
data.x = kernel_data_fetch(patches, patch + 4);
data.y = kernel_data_fetch(patches, patch + 5);
data.z = kernel_data_fetch(patches, patch + 6);
data.w = kernel_data_fetch(patches, patch + 7);
int num_corners = data.y & 0xffff;
if (num_corners == 4) {
/* quad */
corners[0] = data.z;
corners[1] = data.z + 1;
corners[2] = data.z + 2;
corners[3] = data.z + 3;
}
else {
/* ngon */
int c = data.y >> 16;
corners[0] = data.z + c;
corners[1] = data.z + mod(c + 1, num_corners);
corners[2] = data.w;
corners[3] = data.z + mod(c - 1, num_corners);
}
}
/* Reading attributes on various subdivision triangle elements */
ccl_device_noinline float subd_triangle_attribute_float(KernelGlobals kg,
ccl_private const ShaderData *sd,
const AttributeDescriptor desc,
ccl_private float *dx,
ccl_private float *dy)
{
int patch = subd_triangle_patch(kg, sd->prim);
#ifdef __PATCH_EVAL__
if (desc.flags & ATTR_SUBDIVIDED) {
float2 uv[3];
subd_triangle_patch_uv(kg, sd, uv);
float2 dpdu = uv[1] - uv[0];
float2 dpdv = uv[2] - uv[0];
/* p is [s, t] */
float2 p = dpdu * sd->u + dpdv * sd->v + uv[0];
float a, dads, dadt;
a = patch_eval_float(kg, sd, desc.offset, patch, p.x, p.y, 0, &dads, &dadt);
# ifdef __RAY_DIFFERENTIALS__
if (dx || dy) {
float dsdu = dpdu.x;
float dtdu = dpdu.y;
float dsdv = dpdv.x;
float dtdv = dpdv.y;
if (dx) {
float dudx = sd->du.dx;
float dvdx = sd->dv.dx;
float dsdx = dsdu * dudx + dsdv * dvdx;
float dtdx = dtdu * dudx + dtdv * dvdx;
*dx = dads * dsdx + dadt * dtdx;
}
if (dy) {
float dudy = sd->du.dy;
float dvdy = sd->dv.dy;
float dsdy = dsdu * dudy + dsdv * dvdy;
float dtdy = dtdu * dudy + dtdv * dvdy;
*dy = dads * dsdy + dadt * dtdy;
}
}
# endif
return a;
}
else
#endif /* __PATCH_EVAL__ */
if (desc.element == ATTR_ELEMENT_FACE) {
if (dx)
*dx = 0.0f;
if (dy)
*dy = 0.0f;
return kernel_data_fetch(attributes_float, desc.offset + subd_triangle_patch_face(kg, patch));
}
else if (desc.element == ATTR_ELEMENT_VERTEX || desc.element == ATTR_ELEMENT_VERTEX_MOTION) {
float2 uv[3];
subd_triangle_patch_uv(kg, sd, uv);
uint4 v = subd_triangle_patch_indices(kg, patch);
float f0 = kernel_data_fetch(attributes_float, desc.offset + v.x);
float f1 = kernel_data_fetch(attributes_float, desc.offset + v.y);
float f2 = kernel_data_fetch(attributes_float, desc.offset + v.z);
float f3 = kernel_data_fetch(attributes_float, desc.offset + v.w);
if (subd_triangle_patch_num_corners(kg, patch) != 4) {
f1 = (f1 + f0) * 0.5f;
f3 = (f3 + f0) * 0.5f;
}
float a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
float b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
float c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
#ifdef __RAY_DIFFERENTIALS__
if (dx)
*dx = sd->du.dx * b + sd->dv.dx * c - (sd->du.dx + sd->dv.dx) * a;
if (dy)
*dy = sd->du.dy * b + sd->dv.dy * c - (sd->du.dy + sd->dv.dy) * a;
#endif
return sd->u * b + sd->v * c + (1.0f - sd->u - sd->v) * a;
}
else if (desc.element == ATTR_ELEMENT_CORNER) {
float2 uv[3];
subd_triangle_patch_uv(kg, sd, uv);
int corners[4];
subd_triangle_patch_corners(kg, patch, corners);
float f0 = kernel_data_fetch(attributes_float, corners[0] + desc.offset);
float f1 = kernel_data_fetch(attributes_float, corners[1] + desc.offset);
float f2 = kernel_data_fetch(attributes_float, corners[2] + desc.offset);
float f3 = kernel_data_fetch(attributes_float, corners[3] + desc.offset);
if (subd_triangle_patch_num_corners(kg, patch) != 4) {
f1 = (f1 + f0) * 0.5f;
f3 = (f3 + f0) * 0.5f;
}
float a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
float b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
float c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
#ifdef __RAY_DIFFERENTIALS__
if (dx)
*dx = sd->du.dx * b + sd->dv.dx * c - (sd->du.dx + sd->dv.dx) * a;
if (dy)
*dy = sd->du.dy * b + sd->dv.dy * c - (sd->du.dy + sd->dv.dy) * a;
#endif
return sd->u * b + sd->v * c + (1.0f - sd->u - sd->v) * a;
}
else if (desc.element == ATTR_ELEMENT_OBJECT || desc.element == ATTR_ELEMENT_MESH) {
if (dx)
*dx = 0.0f;
if (dy)
*dy = 0.0f;
return kernel_data_fetch(attributes_float, desc.offset);
}
else {
if (dx)
*dx = 0.0f;
if (dy)
*dy = 0.0f;
return 0.0f;
}
}
ccl_device_noinline float2 subd_triangle_attribute_float2(KernelGlobals kg,
ccl_private const ShaderData *sd,
const AttributeDescriptor desc,
ccl_private float2 *dx,
ccl_private float2 *dy)
{
int patch = subd_triangle_patch(kg, sd->prim);
#ifdef __PATCH_EVAL__
if (desc.flags & ATTR_SUBDIVIDED) {
float2 uv[3];
subd_triangle_patch_uv(kg, sd, uv);
float2 dpdu = uv[1] - uv[0];
float2 dpdv = uv[2] - uv[0];
/* p is [s, t] */
float2 p = dpdu * sd->u + dpdv * sd->v + uv[0];
float2 a, dads, dadt;
a = patch_eval_float2(kg, sd, desc.offset, patch, p.x, p.y, 0, &dads, &dadt);
# ifdef __RAY_DIFFERENTIALS__
if (dx || dy) {
float dsdu = dpdu.x;
float dtdu = dpdu.y;
float dsdv = dpdv.x;
float dtdv = dpdv.y;
if (dx) {
float dudx = sd->du.dx;
float dvdx = sd->dv.dx;
float dsdx = dsdu * dudx + dsdv * dvdx;
float dtdx = dtdu * dudx + dtdv * dvdx;
*dx = dads * dsdx + dadt * dtdx;
}
if (dy) {
float dudy = sd->du.dy;
float dvdy = sd->dv.dy;
float dsdy = dsdu * dudy + dsdv * dvdy;
float dtdy = dtdu * dudy + dtdv * dvdy;
*dy = dads * dsdy + dadt * dtdy;
}
}
# endif
return a;
}
else
#endif /* __PATCH_EVAL__ */
if (desc.element == ATTR_ELEMENT_FACE) {
if (dx)
*dx = make_float2(0.0f, 0.0f);
if (dy)
*dy = make_float2(0.0f, 0.0f);
return kernel_data_fetch(attributes_float2, desc.offset + subd_triangle_patch_face(kg, patch));
}
else if (desc.element == ATTR_ELEMENT_VERTEX || desc.element == ATTR_ELEMENT_VERTEX_MOTION) {
float2 uv[3];
subd_triangle_patch_uv(kg, sd, uv);
uint4 v = subd_triangle_patch_indices(kg, patch);
float2 f0 = kernel_data_fetch(attributes_float2, desc.offset + v.x);
float2 f1 = kernel_data_fetch(attributes_float2, desc.offset + v.y);
float2 f2 = kernel_data_fetch(attributes_float2, desc.offset + v.z);
float2 f3 = kernel_data_fetch(attributes_float2, desc.offset + v.w);
if (subd_triangle_patch_num_corners(kg, patch) != 4) {
f1 = (f1 + f0) * 0.5f;
f3 = (f3 + f0) * 0.5f;
}
float2 a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
float2 b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
float2 c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
#ifdef __RAY_DIFFERENTIALS__
if (dx)
*dx = sd->du.dx * b + sd->dv.dx * c - (sd->du.dx + sd->dv.dx) * a;
if (dy)
*dy = sd->du.dy * b + sd->dv.dy * c - (sd->du.dy + sd->dv.dy) * a;
#endif
return sd->u * b + sd->v * c + (1.0f - sd->u - sd->v) * a;
}
else if (desc.element == ATTR_ELEMENT_CORNER) {
float2 uv[3];
subd_triangle_patch_uv(kg, sd, uv);
int corners[4];
subd_triangle_patch_corners(kg, patch, corners);
float2 f0, f1, f2, f3;
f0 = kernel_data_fetch(attributes_float2, corners[0] + desc.offset);
f1 = kernel_data_fetch(attributes_float2, corners[1] + desc.offset);
f2 = kernel_data_fetch(attributes_float2, corners[2] + desc.offset);
f3 = kernel_data_fetch(attributes_float2, corners[3] + desc.offset);
if (subd_triangle_patch_num_corners(kg, patch) != 4) {
f1 = (f1 + f0) * 0.5f;
f3 = (f3 + f0) * 0.5f;
}
float2 a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
float2 b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
float2 c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
#ifdef __RAY_DIFFERENTIALS__
if (dx)
*dx = sd->du.dx * b + sd->dv.dx * c - (sd->du.dx + sd->dv.dx) * a;
if (dy)
*dy = sd->du.dy * b + sd->dv.dy * c - (sd->du.dy + sd->dv.dy) * a;
#endif
return sd->u * b + sd->v * c + (1.0f - sd->u - sd->v) * a;
}
else if (desc.element == ATTR_ELEMENT_OBJECT || desc.element == ATTR_ELEMENT_MESH) {
if (dx)
*dx = make_float2(0.0f, 0.0f);
if (dy)
*dy = make_float2(0.0f, 0.0f);
return kernel_data_fetch(attributes_float2, desc.offset);
}
else {
if (dx)
*dx = make_float2(0.0f, 0.0f);
if (dy)
*dy = make_float2(0.0f, 0.0f);
return make_float2(0.0f, 0.0f);
}
}
ccl_device_noinline float3 subd_triangle_attribute_float3(KernelGlobals kg,
ccl_private const ShaderData *sd,
const AttributeDescriptor desc,
ccl_private float3 *dx,
ccl_private float3 *dy)
{
int patch = subd_triangle_patch(kg, sd->prim);
#ifdef __PATCH_EVAL__
if (desc.flags & ATTR_SUBDIVIDED) {
float2 uv[3];
subd_triangle_patch_uv(kg, sd, uv);
float2 dpdu = uv[1] - uv[0];
float2 dpdv = uv[2] - uv[0];
/* p is [s, t] */
float2 p = dpdu * sd->u + dpdv * sd->v + uv[0];
float3 a, dads, dadt;
a = patch_eval_float3(kg, sd, desc.offset, patch, p.x, p.y, 0, &dads, &dadt);
# ifdef __RAY_DIFFERENTIALS__
if (dx || dy) {
float dsdu = dpdu.x;
float dtdu = dpdu.y;
float dsdv = dpdv.x;
float dtdv = dpdv.y;
if (dx) {
float dudx = sd->du.dx;
float dvdx = sd->dv.dx;
float dsdx = dsdu * dudx + dsdv * dvdx;
float dtdx = dtdu * dudx + dtdv * dvdx;
*dx = dads * dsdx + dadt * dtdx;
}
if (dy) {
float dudy = sd->du.dy;
float dvdy = sd->dv.dy;
float dsdy = dsdu * dudy + dsdv * dvdy;
float dtdy = dtdu * dudy + dtdv * dvdy;
*dy = dads * dsdy + dadt * dtdy;
}
}
# endif
return a;
}
else
#endif /* __PATCH_EVAL__ */
if (desc.element == ATTR_ELEMENT_FACE) {
if (dx)
*dx = make_float3(0.0f, 0.0f, 0.0f);
if (dy)
*dy = make_float3(0.0f, 0.0f, 0.0f);
return kernel_data_fetch(attributes_float3, desc.offset + subd_triangle_patch_face(kg, patch));
}
else if (desc.element == ATTR_ELEMENT_VERTEX || desc.element == ATTR_ELEMENT_VERTEX_MOTION) {
float2 uv[3];
subd_triangle_patch_uv(kg, sd, uv);
uint4 v = subd_triangle_patch_indices(kg, patch);
float3 f0 = kernel_data_fetch(attributes_float3, desc.offset + v.x);
float3 f1 = kernel_data_fetch(attributes_float3, desc.offset + v.y);
float3 f2 = kernel_data_fetch(attributes_float3, desc.offset + v.z);
float3 f3 = kernel_data_fetch(attributes_float3, desc.offset + v.w);
if (subd_triangle_patch_num_corners(kg, patch) != 4) {
f1 = (f1 + f0) * 0.5f;
f3 = (f3 + f0) * 0.5f;
}
float3 a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
float3 b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
float3 c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
#ifdef __RAY_DIFFERENTIALS__
if (dx)
*dx = sd->du.dx * b + sd->dv.dx * c - (sd->du.dx + sd->dv.dx) * a;
if (dy)
*dy = sd->du.dy * b + sd->dv.dy * c - (sd->du.dy + sd->dv.dy) * a;
#endif
return sd->u * b + sd->v * c + (1.0f - sd->u - sd->v) * a;
}
else if (desc.element == ATTR_ELEMENT_CORNER) {
float2 uv[3];
subd_triangle_patch_uv(kg, sd, uv);
int corners[4];
subd_triangle_patch_corners(kg, patch, corners);
float3 f0, f1, f2, f3;
f0 = kernel_data_fetch(attributes_float3, corners[0] + desc.offset);
f1 = kernel_data_fetch(attributes_float3, corners[1] + desc.offset);
f2 = kernel_data_fetch(attributes_float3, corners[2] + desc.offset);
f3 = kernel_data_fetch(attributes_float3, corners[3] + desc.offset);
if (subd_triangle_patch_num_corners(kg, patch) != 4) {
f1 = (f1 + f0) * 0.5f;
f3 = (f3 + f0) * 0.5f;
}
float3 a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
float3 b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
float3 c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
#ifdef __RAY_DIFFERENTIALS__
if (dx)
*dx = sd->du.dx * b + sd->dv.dx * c - (sd->du.dx + sd->dv.dx) * a;
if (dy)
*dy = sd->du.dy * b + sd->dv.dy * c - (sd->du.dy + sd->dv.dy) * a;
#endif
return sd->u * b + sd->v * c + (1.0f - sd->u - sd->v) * a;
}
else if (desc.element == ATTR_ELEMENT_OBJECT || desc.element == ATTR_ELEMENT_MESH) {
if (dx)
*dx = make_float3(0.0f, 0.0f, 0.0f);
if (dy)
*dy = make_float3(0.0f, 0.0f, 0.0f);
return kernel_data_fetch(attributes_float3, desc.offset);
}
else {
if (dx)
*dx = make_float3(0.0f, 0.0f, 0.0f);
if (dy)
*dy = make_float3(0.0f, 0.0f, 0.0f);
return make_float3(0.0f, 0.0f, 0.0f);
}
}
ccl_device_noinline float4 subd_triangle_attribute_float4(KernelGlobals kg,
ccl_private const ShaderData *sd,
const AttributeDescriptor desc,
ccl_private float4 *dx,
ccl_private float4 *dy)
{
int patch = subd_triangle_patch(kg, sd->prim);
#ifdef __PATCH_EVAL__
if (desc.flags & ATTR_SUBDIVIDED) {
float2 uv[3];
subd_triangle_patch_uv(kg, sd, uv);
float2 dpdu = uv[1] - uv[0];
float2 dpdv = uv[2] - uv[0];
/* p is [s, t] */
float2 p = dpdu * sd->u + dpdv * sd->v + uv[0];
float4 a, dads, dadt;
if (desc.type == NODE_ATTR_RGBA) {
a = patch_eval_uchar4(kg, sd, desc.offset, patch, p.x, p.y, 0, &dads, &dadt);
}
else {
a = patch_eval_float4(kg, sd, desc.offset, patch, p.x, p.y, 0, &dads, &dadt);
}
# ifdef __RAY_DIFFERENTIALS__
if (dx || dy) {
float dsdu = dpdu.x;
float dtdu = dpdu.y;
float dsdv = dpdv.x;
float dtdv = dpdv.y;
if (dx) {
float dudx = sd->du.dx;
float dvdx = sd->dv.dx;
float dsdx = dsdu * dudx + dsdv * dvdx;
float dtdx = dtdu * dudx + dtdv * dvdx;
*dx = dads * dsdx + dadt * dtdx;
}
if (dy) {
float dudy = sd->du.dy;
float dvdy = sd->dv.dy;
float dsdy = dsdu * dudy + dsdv * dvdy;
float dtdy = dtdu * dudy + dtdv * dvdy;
*dy = dads * dsdy + dadt * dtdy;
}
}
# endif
return a;
}
else
#endif /* __PATCH_EVAL__ */
if (desc.element == ATTR_ELEMENT_FACE) {
if (dx)
*dx = zero_float4();
if (dy)
*dy = zero_float4();
return kernel_data_fetch(attributes_float4, desc.offset + subd_triangle_patch_face(kg, patch));
}
else if (desc.element == ATTR_ELEMENT_VERTEX || desc.element == ATTR_ELEMENT_VERTEX_MOTION) {
float2 uv[3];
subd_triangle_patch_uv(kg, sd, uv);
uint4 v = subd_triangle_patch_indices(kg, patch);
float4 f0 = kernel_data_fetch(attributes_float4, desc.offset + v.x);
float4 f1 = kernel_data_fetch(attributes_float4, desc.offset + v.y);
float4 f2 = kernel_data_fetch(attributes_float4, desc.offset + v.z);
float4 f3 = kernel_data_fetch(attributes_float4, desc.offset + v.w);
if (subd_triangle_patch_num_corners(kg, patch) != 4) {
f1 = (f1 + f0) * 0.5f;
f3 = (f3 + f0) * 0.5f;
}
float4 a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
float4 b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
float4 c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
#ifdef __RAY_DIFFERENTIALS__
if (dx)
*dx = sd->du.dx * b + sd->dv.dx * c - (sd->du.dx + sd->dv.dx) * a;
if (dy)
*dy = sd->du.dy * b + sd->dv.dy * c - (sd->du.dy + sd->dv.dy) * a;
#endif
return sd->u * b + sd->v * c + (1.0f - sd->u - sd->v) * a;
}
else if (desc.element == ATTR_ELEMENT_CORNER || desc.element == ATTR_ELEMENT_CORNER_BYTE) {
float2 uv[3];
subd_triangle_patch_uv(kg, sd, uv);
int corners[4];
subd_triangle_patch_corners(kg, patch, corners);
float4 f0, f1, f2, f3;
if (desc.element == ATTR_ELEMENT_CORNER_BYTE) {
f0 = color_srgb_to_linear_v4(
color_uchar4_to_float4(kernel_data_fetch(attributes_uchar4, corners[0] + desc.offset)));
f1 = color_srgb_to_linear_v4(
color_uchar4_to_float4(kernel_data_fetch(attributes_uchar4, corners[1] + desc.offset)));
f2 = color_srgb_to_linear_v4(
color_uchar4_to_float4(kernel_data_fetch(attributes_uchar4, corners[2] + desc.offset)));
f3 = color_srgb_to_linear_v4(
color_uchar4_to_float4(kernel_data_fetch(attributes_uchar4, corners[3] + desc.offset)));
}
else {
f0 = kernel_data_fetch(attributes_float4, corners[0] + desc.offset);
f1 = kernel_data_fetch(attributes_float4, corners[1] + desc.offset);
f2 = kernel_data_fetch(attributes_float4, corners[2] + desc.offset);
f3 = kernel_data_fetch(attributes_float4, corners[3] + desc.offset);
}
if (subd_triangle_patch_num_corners(kg, patch) != 4) {
f1 = (f1 + f0) * 0.5f;
f3 = (f3 + f0) * 0.5f;
}
float4 a = mix(mix(f0, f1, uv[0].x), mix(f3, f2, uv[0].x), uv[0].y);
float4 b = mix(mix(f0, f1, uv[1].x), mix(f3, f2, uv[1].x), uv[1].y);
float4 c = mix(mix(f0, f1, uv[2].x), mix(f3, f2, uv[2].x), uv[2].y);
#ifdef __RAY_DIFFERENTIALS__
if (dx)
*dx = sd->du.dx * b + sd->dv.dx * c - (sd->du.dx + sd->dv.dx) * a;
if (dy)
*dy = sd->du.dy * b + sd->dv.dy * c - (sd->du.dy + sd->dv.dy) * a;
#endif
return sd->u * b + sd->v * c + (1.0f - sd->u - sd->v) * a;
}
else if (desc.element == ATTR_ELEMENT_OBJECT || desc.element == ATTR_ELEMENT_MESH) {
if (dx)
*dx = zero_float4();
if (dy)
*dy = zero_float4();
return kernel_data_fetch(attributes_float4, desc.offset);
}
else {
if (dx)
*dx = zero_float4();
if (dy)
*dy = zero_float4();
return zero_float4();
}
}
CCL_NAMESPACE_END