1 /*
2  * Copyright 2011-2016 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef __KERNEL_CPU_IMAGE_H__
18 #define __KERNEL_CPU_IMAGE_H__
19 
20 #ifdef WITH_NANOVDB
21 #  include <nanovdb/NanoVDB.h>
22 #  include <nanovdb/util/SampleFromVoxels.h>
23 #endif
24 
25 CCL_NAMESPACE_BEGIN
26 
27 /* Make template functions private so symbols don't conflict between kernels with different
28  * instruction sets. */
29 namespace {
30 
31 template<typename T> struct TextureInterpolator {
32 #define SET_CUBIC_SPLINE_WEIGHTS(u, t) \
33   { \
34     u[0] = (((-1.0f / 6.0f) * t + 0.5f) * t - 0.5f) * t + (1.0f / 6.0f); \
35     u[1] = ((0.5f * t - 1.0f) * t) * t + (2.0f / 3.0f); \
36     u[2] = ((-0.5f * t + 0.5f) * t + 0.5f) * t + (1.0f / 6.0f); \
37     u[3] = (1.0f / 6.0f) * t * t * t; \
38   } \
39   (void)0
40 
readTextureInterpolator41   static ccl_always_inline float4 read(float4 r)
42   {
43     return r;
44   }
45 
readTextureInterpolator46   static ccl_always_inline float4 read(uchar4 r)
47   {
48     float f = 1.0f / 255.0f;
49     return make_float4(r.x * f, r.y * f, r.z * f, r.w * f);
50   }
51 
readTextureInterpolator52   static ccl_always_inline float4 read(uchar r)
53   {
54     float f = r * (1.0f / 255.0f);
55     return make_float4(f, f, f, 1.0f);
56   }
57 
readTextureInterpolator58   static ccl_always_inline float4 read(float r)
59   {
60     /* TODO(dingto): Optimize this, so interpolation
61      * happens on float instead of float4 */
62     return make_float4(r, r, r, 1.0f);
63   }
64 
readTextureInterpolator65   static ccl_always_inline float4 read(half4 r)
66   {
67     return half4_to_float4(r);
68   }
69 
readTextureInterpolator70   static ccl_always_inline float4 read(half r)
71   {
72     float f = half_to_float(r);
73     return make_float4(f, f, f, 1.0f);
74   }
75 
readTextureInterpolator76   static ccl_always_inline float4 read(uint16_t r)
77   {
78     float f = r * (1.0f / 65535.0f);
79     return make_float4(f, f, f, 1.0f);
80   }
81 
readTextureInterpolator82   static ccl_always_inline float4 read(ushort4 r)
83   {
84     float f = 1.0f / 65535.0f;
85     return make_float4(r.x * f, r.y * f, r.z * f, r.w * f);
86   }
87 
readTextureInterpolator88   static ccl_always_inline float4 read(const T *data, int x, int y, int width, int height)
89   {
90     if (x < 0 || y < 0 || x >= width || y >= height) {
91       return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
92     }
93     return read(data[y * width + x]);
94   }
95 
wrap_periodicTextureInterpolator96   static ccl_always_inline int wrap_periodic(int x, int width)
97   {
98     x %= width;
99     if (x < 0)
100       x += width;
101     return x;
102   }
103 
wrap_clampTextureInterpolator104   static ccl_always_inline int wrap_clamp(int x, int width)
105   {
106     return clamp(x, 0, width - 1);
107   }
108 
fracTextureInterpolator109   static ccl_always_inline float frac(float x, int *ix)
110   {
111     int i = float_to_int(x) - ((x < 0.0f) ? 1 : 0);
112     *ix = i;
113     return x - (float)i;
114   }
115 
116   /* ********  2D interpolation ******** */
117 
interp_closestTextureInterpolator118   static ccl_always_inline float4 interp_closest(const TextureInfo &info, float x, float y)
119   {
120     const T *data = (const T *)info.data;
121     const int width = info.width;
122     const int height = info.height;
123     int ix, iy;
124     frac(x * (float)width, &ix);
125     frac(y * (float)height, &iy);
126     switch (info.extension) {
127       case EXTENSION_REPEAT:
128         ix = wrap_periodic(ix, width);
129         iy = wrap_periodic(iy, height);
130         break;
131       case EXTENSION_CLIP:
132         if (x < 0.0f || y < 0.0f || x > 1.0f || y > 1.0f) {
133           return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
134         }
135         ATTR_FALLTHROUGH;
136       case EXTENSION_EXTEND:
137         ix = wrap_clamp(ix, width);
138         iy = wrap_clamp(iy, height);
139         break;
140       default:
141         kernel_assert(0);
142         return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
143     }
144     return read(data[ix + iy * width]);
145   }
146 
interp_linearTextureInterpolator147   static ccl_always_inline float4 interp_linear(const TextureInfo &info, float x, float y)
148   {
149     const T *data = (const T *)info.data;
150     const int width = info.width;
151     const int height = info.height;
152     int ix, iy, nix, niy;
153     const float tx = frac(x * (float)width - 0.5f, &ix);
154     const float ty = frac(y * (float)height - 0.5f, &iy);
155     switch (info.extension) {
156       case EXTENSION_REPEAT:
157         ix = wrap_periodic(ix, width);
158         iy = wrap_periodic(iy, height);
159         nix = wrap_periodic(ix + 1, width);
160         niy = wrap_periodic(iy + 1, height);
161         break;
162       case EXTENSION_CLIP:
163         nix = ix + 1;
164         niy = iy + 1;
165         break;
166       case EXTENSION_EXTEND:
167         nix = wrap_clamp(ix + 1, width);
168         niy = wrap_clamp(iy + 1, height);
169         ix = wrap_clamp(ix, width);
170         iy = wrap_clamp(iy, height);
171         break;
172       default:
173         kernel_assert(0);
174         return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
175     }
176     return (1.0f - ty) * (1.0f - tx) * read(data, ix, iy, width, height) +
177            (1.0f - ty) * tx * read(data, nix, iy, width, height) +
178            ty * (1.0f - tx) * read(data, ix, niy, width, height) +
179            ty * tx * read(data, nix, niy, width, height);
180   }
181 
interp_cubicTextureInterpolator182   static ccl_always_inline float4 interp_cubic(const TextureInfo &info, float x, float y)
183   {
184     const T *data = (const T *)info.data;
185     const int width = info.width;
186     const int height = info.height;
187     int ix, iy, nix, niy;
188     const float tx = frac(x * (float)width - 0.5f, &ix);
189     const float ty = frac(y * (float)height - 0.5f, &iy);
190     int pix, piy, nnix, nniy;
191     switch (info.extension) {
192       case EXTENSION_REPEAT:
193         ix = wrap_periodic(ix, width);
194         iy = wrap_periodic(iy, height);
195         pix = wrap_periodic(ix - 1, width);
196         piy = wrap_periodic(iy - 1, height);
197         nix = wrap_periodic(ix + 1, width);
198         niy = wrap_periodic(iy + 1, height);
199         nnix = wrap_periodic(ix + 2, width);
200         nniy = wrap_periodic(iy + 2, height);
201         break;
202       case EXTENSION_CLIP:
203         pix = ix - 1;
204         piy = iy - 1;
205         nix = ix + 1;
206         niy = iy + 1;
207         nnix = ix + 2;
208         nniy = iy + 2;
209         break;
210       case EXTENSION_EXTEND:
211         pix = wrap_clamp(ix - 1, width);
212         piy = wrap_clamp(iy - 1, height);
213         nix = wrap_clamp(ix + 1, width);
214         niy = wrap_clamp(iy + 1, height);
215         nnix = wrap_clamp(ix + 2, width);
216         nniy = wrap_clamp(iy + 2, height);
217         ix = wrap_clamp(ix, width);
218         iy = wrap_clamp(iy, height);
219         break;
220       default:
221         kernel_assert(0);
222         return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
223     }
224     const int xc[4] = {pix, ix, nix, nnix};
225     const int yc[4] = {piy, iy, niy, nniy};
226     float u[4], v[4];
227     /* Some helper macro to keep code reasonable size,
228      * let compiler to inline all the matrix multiplications.
229      */
230 #define DATA(x, y) (read(data, xc[x], yc[y], width, height))
231 #define TERM(col) \
232   (v[col] * \
233    (u[0] * DATA(0, col) + u[1] * DATA(1, col) + u[2] * DATA(2, col) + u[3] * DATA(3, col)))
234 
235     SET_CUBIC_SPLINE_WEIGHTS(u, tx);
236     SET_CUBIC_SPLINE_WEIGHTS(v, ty);
237 
238     /* Actual interpolation. */
239     return TERM(0) + TERM(1) + TERM(2) + TERM(3);
240 #undef TERM
241 #undef DATA
242   }
243 
interpTextureInterpolator244   static ccl_always_inline float4 interp(const TextureInfo &info, float x, float y)
245   {
246     if (UNLIKELY(!info.data)) {
247       return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
248     }
249     switch (info.interpolation) {
250       case INTERPOLATION_CLOSEST:
251         return interp_closest(info, x, y);
252       case INTERPOLATION_LINEAR:
253         return interp_linear(info, x, y);
254       default:
255         return interp_cubic(info, x, y);
256     }
257   }
258 
259   /* ********  3D interpolation ******** */
260 
interp_3d_closestTextureInterpolator261   static ccl_always_inline float4 interp_3d_closest(const TextureInfo &info,
262                                                     float x,
263                                                     float y,
264                                                     float z)
265   {
266     int width = info.width;
267     int height = info.height;
268     int depth = info.depth;
269     int ix, iy, iz;
270 
271     frac(x * (float)width, &ix);
272     frac(y * (float)height, &iy);
273     frac(z * (float)depth, &iz);
274 
275     switch (info.extension) {
276       case EXTENSION_REPEAT:
277         ix = wrap_periodic(ix, width);
278         iy = wrap_periodic(iy, height);
279         iz = wrap_periodic(iz, depth);
280         break;
281       case EXTENSION_CLIP:
282         if (x < 0.0f || y < 0.0f || z < 0.0f || x > 1.0f || y > 1.0f || z > 1.0f) {
283           return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
284         }
285         ATTR_FALLTHROUGH;
286       case EXTENSION_EXTEND:
287         ix = wrap_clamp(ix, width);
288         iy = wrap_clamp(iy, height);
289         iz = wrap_clamp(iz, depth);
290         break;
291       default:
292         kernel_assert(0);
293         return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
294     }
295 
296     const T *data = (const T *)info.data;
297     return read(data[ix + iy * width + iz * width * height]);
298   }
299 
interp_3d_linearTextureInterpolator300   static ccl_always_inline float4 interp_3d_linear(const TextureInfo &info,
301                                                    float x,
302                                                    float y,
303                                                    float z)
304   {
305     int width = info.width;
306     int height = info.height;
307     int depth = info.depth;
308     int ix, iy, iz;
309     int nix, niy, niz;
310 
311     float tx = frac(x * (float)width - 0.5f, &ix);
312     float ty = frac(y * (float)height - 0.5f, &iy);
313     float tz = frac(z * (float)depth - 0.5f, &iz);
314 
315     switch (info.extension) {
316       case EXTENSION_REPEAT:
317         ix = wrap_periodic(ix, width);
318         iy = wrap_periodic(iy, height);
319         iz = wrap_periodic(iz, depth);
320 
321         nix = wrap_periodic(ix + 1, width);
322         niy = wrap_periodic(iy + 1, height);
323         niz = wrap_periodic(iz + 1, depth);
324         break;
325       case EXTENSION_CLIP:
326         if (x < 0.0f || y < 0.0f || z < 0.0f || x > 1.0f || y > 1.0f || z > 1.0f) {
327           return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
328         }
329         ATTR_FALLTHROUGH;
330       case EXTENSION_EXTEND:
331         nix = wrap_clamp(ix + 1, width);
332         niy = wrap_clamp(iy + 1, height);
333         niz = wrap_clamp(iz + 1, depth);
334 
335         ix = wrap_clamp(ix, width);
336         iy = wrap_clamp(iy, height);
337         iz = wrap_clamp(iz, depth);
338         break;
339       default:
340         kernel_assert(0);
341         return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
342     }
343 
344     const T *data = (const T *)info.data;
345     float4 r;
346 
347     r = (1.0f - tz) * (1.0f - ty) * (1.0f - tx) *
348         read(data[ix + iy * width + iz * width * height]);
349     r += (1.0f - tz) * (1.0f - ty) * tx * read(data[nix + iy * width + iz * width * height]);
350     r += (1.0f - tz) * ty * (1.0f - tx) * read(data[ix + niy * width + iz * width * height]);
351     r += (1.0f - tz) * ty * tx * read(data[nix + niy * width + iz * width * height]);
352 
353     r += tz * (1.0f - ty) * (1.0f - tx) * read(data[ix + iy * width + niz * width * height]);
354     r += tz * (1.0f - ty) * tx * read(data[nix + iy * width + niz * width * height]);
355     r += tz * ty * (1.0f - tx) * read(data[ix + niy * width + niz * width * height]);
356     r += tz * ty * tx * read(data[nix + niy * width + niz * width * height]);
357 
358     return r;
359   }
360 
361   /* TODO(sergey): For some unspeakable reason both GCC-6 and Clang-3.9 are
362    * causing stack overflow issue in this function unless it is inlined.
363    *
364    * Only happens for AVX2 kernel and global __KERNEL_SSE__ vectorization
365    * enabled.
366    */
367 #if defined(__GNUC__) || defined(__clang__)
368   static ccl_always_inline
369 #else
370   static ccl_never_inline
371 #endif
372       float4
interp_3d_tricubicTextureInterpolator373       interp_3d_tricubic(const TextureInfo &info, float x, float y, float z)
374   {
375     int width = info.width;
376     int height = info.height;
377     int depth = info.depth;
378     int ix, iy, iz;
379     int nix, niy, niz;
380     /* Tricubic b-spline interpolation. */
381     const float tx = frac(x * (float)width - 0.5f, &ix);
382     const float ty = frac(y * (float)height - 0.5f, &iy);
383     const float tz = frac(z * (float)depth - 0.5f, &iz);
384     int pix, piy, piz, nnix, nniy, nniz;
385 
386     switch (info.extension) {
387       case EXTENSION_REPEAT:
388         ix = wrap_periodic(ix, width);
389         iy = wrap_periodic(iy, height);
390         iz = wrap_periodic(iz, depth);
391 
392         pix = wrap_periodic(ix - 1, width);
393         piy = wrap_periodic(iy - 1, height);
394         piz = wrap_periodic(iz - 1, depth);
395 
396         nix = wrap_periodic(ix + 1, width);
397         niy = wrap_periodic(iy + 1, height);
398         niz = wrap_periodic(iz + 1, depth);
399 
400         nnix = wrap_periodic(ix + 2, width);
401         nniy = wrap_periodic(iy + 2, height);
402         nniz = wrap_periodic(iz + 2, depth);
403         break;
404       case EXTENSION_CLIP:
405         if (x < 0.0f || y < 0.0f || z < 0.0f || x > 1.0f || y > 1.0f || z > 1.0f) {
406           return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
407         }
408         ATTR_FALLTHROUGH;
409       case EXTENSION_EXTEND:
410         pix = wrap_clamp(ix - 1, width);
411         piy = wrap_clamp(iy - 1, height);
412         piz = wrap_clamp(iz - 1, depth);
413 
414         nix = wrap_clamp(ix + 1, width);
415         niy = wrap_clamp(iy + 1, height);
416         niz = wrap_clamp(iz + 1, depth);
417 
418         nnix = wrap_clamp(ix + 2, width);
419         nniy = wrap_clamp(iy + 2, height);
420         nniz = wrap_clamp(iz + 2, depth);
421 
422         ix = wrap_clamp(ix, width);
423         iy = wrap_clamp(iy, height);
424         iz = wrap_clamp(iz, depth);
425         break;
426       default:
427         kernel_assert(0);
428         return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
429     }
430 
431     const int xc[4] = {pix, ix, nix, nnix};
432     const int yc[4] = {width * piy, width * iy, width * niy, width * nniy};
433     const int zc[4] = {
434         width * height * piz, width * height * iz, width * height * niz, width * height * nniz};
435     float u[4], v[4], w[4];
436 
437     /* Some helper macro to keep code reasonable size,
438      * let compiler to inline all the matrix multiplications.
439      */
440 #define DATA(x, y, z) (read(data[xc[x] + yc[y] + zc[z]]))
441 #define COL_TERM(col, row) \
442   (v[col] * (u[0] * DATA(0, col, row) + u[1] * DATA(1, col, row) + u[2] * DATA(2, col, row) + \
443              u[3] * DATA(3, col, row)))
444 #define ROW_TERM(row) \
445   (w[row] * (COL_TERM(0, row) + COL_TERM(1, row) + COL_TERM(2, row) + COL_TERM(3, row)))
446 
447     SET_CUBIC_SPLINE_WEIGHTS(u, tx);
448     SET_CUBIC_SPLINE_WEIGHTS(v, ty);
449     SET_CUBIC_SPLINE_WEIGHTS(w, tz);
450 
451     /* Actual interpolation. */
452     const T *data = (const T *)info.data;
453     return ROW_TERM(0) + ROW_TERM(1) + ROW_TERM(2) + ROW_TERM(3);
454 
455 #undef COL_TERM
456 #undef ROW_TERM
457 #undef DATA
458   }
459 
460   static ccl_always_inline float4
interp_3dTextureInterpolator461   interp_3d(const TextureInfo &info, float x, float y, float z, InterpolationType interp)
462   {
463     if (UNLIKELY(!info.data))
464       return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
465 
466     switch ((interp == INTERPOLATION_NONE) ? info.interpolation : interp) {
467       case INTERPOLATION_CLOSEST:
468         return interp_3d_closest(info, x, y, z);
469       case INTERPOLATION_LINEAR:
470         return interp_3d_linear(info, x, y, z);
471       default:
472         return interp_3d_tricubic(info, x, y, z);
473     }
474   }
475 #undef SET_CUBIC_SPLINE_WEIGHTS
476 };
477 
478 #ifdef WITH_NANOVDB
479 template<typename T> struct NanoVDBInterpolator {
readNanoVDBInterpolator480   static ccl_always_inline float4 read(float r)
481   {
482     return make_float4(r, r, r, 1.0f);
483   }
484 
readNanoVDBInterpolator485   static ccl_always_inline float4 read(nanovdb::Vec3f r)
486   {
487     return make_float4(r[0], r[1], r[2], 1.0f);
488   }
489 
490   static ccl_always_inline float4
interp_3dNanoVDBInterpolator491   interp_3d(const TextureInfo &info, float x, float y, float z, InterpolationType interp)
492   {
493     nanovdb::NanoGrid<T> *const grid = (nanovdb::NanoGrid<T> *)info.data;
494     const nanovdb::NanoRoot<T> &root = grid->tree().root();
495 
496     const nanovdb::Coord off(root.bbox().min());
497     const nanovdb::Coord dim(root.bbox().dim());
498     const nanovdb::Vec3f xyz(off[0] + x * dim[0], off[1] + y * dim[1], off[2] + z * dim[2]);
499 
500     typedef nanovdb::ReadAccessor<nanovdb::NanoRoot<T>> ReadAccessorT;
501     switch ((interp == INTERPOLATION_NONE) ? info.interpolation : interp) {
502       default:
503       case INTERPOLATION_LINEAR:
504         return read(nanovdb::SampleFromVoxels<ReadAccessorT, 1, false>(root)(xyz));
505       case INTERPOLATION_CLOSEST:
506         return read(nanovdb::SampleFromVoxels<ReadAccessorT, 0, false>(root)(xyz));
507       case INTERPOLATION_CUBIC:
508         return read(nanovdb::SampleFromVoxels<ReadAccessorT, 3, false>(root)(xyz));
509     }
510   }
511 };
512 #endif
513 
kernel_tex_image_interp(KernelGlobals * kg,int id,float x,float y)514 ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y)
515 {
516   const TextureInfo &info = kernel_tex_fetch(__texture_info, id);
517 
518   switch (info.data_type) {
519     case IMAGE_DATA_TYPE_HALF:
520       return TextureInterpolator<half>::interp(info, x, y);
521     case IMAGE_DATA_TYPE_BYTE:
522       return TextureInterpolator<uchar>::interp(info, x, y);
523     case IMAGE_DATA_TYPE_USHORT:
524       return TextureInterpolator<uint16_t>::interp(info, x, y);
525     case IMAGE_DATA_TYPE_FLOAT:
526       return TextureInterpolator<float>::interp(info, x, y);
527     case IMAGE_DATA_TYPE_HALF4:
528       return TextureInterpolator<half4>::interp(info, x, y);
529     case IMAGE_DATA_TYPE_BYTE4:
530       return TextureInterpolator<uchar4>::interp(info, x, y);
531     case IMAGE_DATA_TYPE_USHORT4:
532       return TextureInterpolator<ushort4>::interp(info, x, y);
533     case IMAGE_DATA_TYPE_FLOAT4:
534       return TextureInterpolator<float4>::interp(info, x, y);
535     default:
536       assert(0);
537       return make_float4(
538           TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A);
539   }
540 }
541 
kernel_tex_image_interp_3d(KernelGlobals * kg,int id,float3 P,InterpolationType interp)542 ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg,
543                                              int id,
544                                              float3 P,
545                                              InterpolationType interp)
546 {
547   const TextureInfo &info = kernel_tex_fetch(__texture_info, id);
548 
549   if (info.use_transform_3d) {
550     P = transform_point(&info.transform_3d, P);
551   }
552 
553   switch (info.data_type) {
554     case IMAGE_DATA_TYPE_HALF:
555       return TextureInterpolator<half>::interp_3d(info, P.x, P.y, P.z, interp);
556     case IMAGE_DATA_TYPE_BYTE:
557       return TextureInterpolator<uchar>::interp_3d(info, P.x, P.y, P.z, interp);
558     case IMAGE_DATA_TYPE_USHORT:
559       return TextureInterpolator<uint16_t>::interp_3d(info, P.x, P.y, P.z, interp);
560     case IMAGE_DATA_TYPE_FLOAT:
561       return TextureInterpolator<float>::interp_3d(info, P.x, P.y, P.z, interp);
562     case IMAGE_DATA_TYPE_HALF4:
563       return TextureInterpolator<half4>::interp_3d(info, P.x, P.y, P.z, interp);
564     case IMAGE_DATA_TYPE_BYTE4:
565       return TextureInterpolator<uchar4>::interp_3d(info, P.x, P.y, P.z, interp);
566     case IMAGE_DATA_TYPE_USHORT4:
567       return TextureInterpolator<ushort4>::interp_3d(info, P.x, P.y, P.z, interp);
568     case IMAGE_DATA_TYPE_FLOAT4:
569       return TextureInterpolator<float4>::interp_3d(info, P.x, P.y, P.z, interp);
570 #ifdef WITH_NANOVDB
571     case IMAGE_DATA_TYPE_NANOVDB_FLOAT:
572       return NanoVDBInterpolator<float>::interp_3d(info, P.x, P.y, P.z, interp);
573     case IMAGE_DATA_TYPE_NANOVDB_FLOAT3:
574       return NanoVDBInterpolator<nanovdb::Vec3f>::interp_3d(info, P.x, P.y, P.z, interp);
575 #endif
576     default:
577       assert(0);
578       return make_float4(
579           TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A);
580   }
581 }
582 
583 } /* Namespace. */
584 
585 CCL_NAMESPACE_END
586 
587 #endif  // __KERNEL_CPU_IMAGE_H__
588