2016-05-20 16:46:49 +02:00
|
|
|
/*
|
|
|
|
|
* Copyright 2011-2016 Blender Foundation
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef __KERNEL_CPU_IMAGE_H__
|
|
|
|
|
#define __KERNEL_CPU_IMAGE_H__
|
|
|
|
|
|
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
|
|
2017-10-06 21:47:41 +02:00
|
|
|
template<typename T> struct TextureInterpolator {
|
|
|
|
|
#define SET_CUBIC_SPLINE_WEIGHTS(u, t) \
|
|
|
|
|
{ \
|
|
|
|
|
u[0] = (((-1.0f/6.0f)* t + 0.5f) * t - 0.5f) * t + (1.0f/6.0f); \
|
|
|
|
|
u[1] = (( 0.5f * t - 1.0f) * t ) * t + (2.0f/3.0f); \
|
|
|
|
|
u[2] = (( -0.5f * t + 0.5f) * t + 0.5f) * t + (1.0f/6.0f); \
|
|
|
|
|
u[3] = (1.0f / 6.0f) * t * t * t; \
|
|
|
|
|
} (void)0
|
|
|
|
|
|
|
|
|
|
static ccl_always_inline float4 read(float4 r)
|
|
|
|
|
{
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ccl_always_inline float4 read(uchar4 r)
|
|
|
|
|
{
|
2018-07-05 12:37:52 +02:00
|
|
|
float f = 1.0f / 255.0f;
|
2017-10-06 21:47:41 +02:00
|
|
|
return make_float4(r.x*f, r.y*f, r.z*f, r.w*f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ccl_always_inline float4 read(uchar r)
|
|
|
|
|
{
|
2018-07-05 12:37:52 +02:00
|
|
|
float f = r * (1.0f / 255.0f);
|
2017-10-06 21:47:41 +02:00
|
|
|
return make_float4(f, f, f, 1.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ccl_always_inline float4 read(float r)
|
|
|
|
|
{
|
|
|
|
|
/* TODO(dingto): Optimize this, so interpolation
|
|
|
|
|
* happens on float instead of float4 */
|
|
|
|
|
return make_float4(r, r, r, 1.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ccl_always_inline float4 read(half4 r)
|
|
|
|
|
{
|
|
|
|
|
return half4_to_float4(r);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ccl_always_inline float4 read(half r)
|
|
|
|
|
{
|
|
|
|
|
float f = half_to_float(r);
|
|
|
|
|
return make_float4(f, f, f, 1.0f);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-05 12:37:52 +02:00
|
|
|
static ccl_always_inline float4 read(uint16_t r)
|
|
|
|
|
{
|
|
|
|
|
float f = r*(1.0f/65535.0f);
|
|
|
|
|
return make_float4(f, f, f, 1.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ccl_always_inline float4 read(ushort4 r)
|
|
|
|
|
{
|
|
|
|
|
float f = 1.0f/65535.0f;
|
|
|
|
|
return make_float4(r.x*f, r.y*f, r.z*f, r.w*f);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-08 11:20:12 +01:00
|
|
|
static ccl_always_inline float4 read(const T *data,
|
|
|
|
|
int x, int y,
|
|
|
|
|
int width, int height)
|
|
|
|
|
{
|
|
|
|
|
if(x < 0 || y < 0 || x >= width || y >= height) {
|
|
|
|
|
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
|
}
|
|
|
|
|
return read(data[y * width + x]);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-06 21:47:41 +02:00
|
|
|
static ccl_always_inline int wrap_periodic(int x, int width)
|
|
|
|
|
{
|
|
|
|
|
x %= width;
|
|
|
|
|
if(x < 0)
|
|
|
|
|
x += width;
|
|
|
|
|
return x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ccl_always_inline int wrap_clamp(int x, int width)
|
|
|
|
|
{
|
|
|
|
|
return clamp(x, 0, width-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ccl_always_inline float frac(float x, int *ix)
|
|
|
|
|
{
|
|
|
|
|
int i = float_to_int(x) - ((x < 0.0f)? 1: 0);
|
|
|
|
|
*ix = i;
|
|
|
|
|
return x - (float)i;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-07 17:45:37 +01:00
|
|
|
/* ******** 2D interpolation ******** */
|
2017-10-06 21:47:41 +02:00
|
|
|
|
2017-12-07 17:45:37 +01:00
|
|
|
static ccl_always_inline float4 interp_closest(const TextureInfo& info,
|
|
|
|
|
float x, float y)
|
|
|
|
|
{
|
2017-10-06 21:47:41 +02:00
|
|
|
const T *data = (const T*)info.data;
|
2017-12-07 17:45:37 +01:00
|
|
|
const int width = info.width;
|
|
|
|
|
const int height = info.height;
|
|
|
|
|
int ix, iy;
|
|
|
|
|
frac(x*(float)width, &ix);
|
|
|
|
|
frac(y*(float)height, &iy);
|
|
|
|
|
switch(info.extension) {
|
|
|
|
|
case EXTENSION_REPEAT:
|
|
|
|
|
ix = wrap_periodic(ix, width);
|
|
|
|
|
iy = wrap_periodic(iy, height);
|
|
|
|
|
break;
|
|
|
|
|
case EXTENSION_CLIP:
|
|
|
|
|
if(x < 0.0f || y < 0.0f || x > 1.0f || y > 1.0f) {
|
2017-10-06 21:47:41 +02:00
|
|
|
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
2017-12-07 17:45:37 +01:00
|
|
|
}
|
|
|
|
|
ATTR_FALLTHROUGH;
|
|
|
|
|
case EXTENSION_EXTEND:
|
|
|
|
|
ix = wrap_clamp(ix, width);
|
|
|
|
|
iy = wrap_clamp(iy, height);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
kernel_assert(0);
|
|
|
|
|
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
2017-10-06 21:47:41 +02:00
|
|
|
}
|
2017-12-07 17:45:37 +01:00
|
|
|
return read(data[ix + iy*width]);
|
|
|
|
|
}
|
2017-10-06 21:47:41 +02:00
|
|
|
|
2017-12-07 17:45:37 +01:00
|
|
|
static ccl_always_inline float4 interp_linear(const TextureInfo& info,
|
|
|
|
|
float x, float y)
|
|
|
|
|
{
|
|
|
|
|
const T *data = (const T*)info.data;
|
|
|
|
|
const int width = info.width;
|
|
|
|
|
const int height = info.height;
|
|
|
|
|
int ix, iy, nix, niy;
|
|
|
|
|
const float tx = frac(x*(float)width - 0.5f, &ix);
|
|
|
|
|
const float ty = frac(y*(float)height - 0.5f, &iy);
|
|
|
|
|
switch(info.extension) {
|
|
|
|
|
case EXTENSION_REPEAT:
|
|
|
|
|
ix = wrap_periodic(ix, width);
|
|
|
|
|
iy = wrap_periodic(iy, height);
|
|
|
|
|
nix = wrap_periodic(ix+1, width);
|
|
|
|
|
niy = wrap_periodic(iy+1, height);
|
|
|
|
|
break;
|
|
|
|
|
case EXTENSION_CLIP:
|
2017-12-08 11:20:12 +01:00
|
|
|
nix = ix + 1;
|
|
|
|
|
niy = iy + 1;
|
|
|
|
|
break;
|
2017-12-07 17:45:37 +01:00
|
|
|
case EXTENSION_EXTEND:
|
|
|
|
|
nix = wrap_clamp(ix+1, width);
|
|
|
|
|
niy = wrap_clamp(iy+1, height);
|
|
|
|
|
ix = wrap_clamp(ix, width);
|
|
|
|
|
iy = wrap_clamp(iy, height);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
kernel_assert(0);
|
|
|
|
|
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
2017-10-06 21:47:41 +02:00
|
|
|
}
|
2017-12-08 11:20:12 +01:00
|
|
|
return (1.0f - ty) * (1.0f - tx) * read(data, ix, iy, width, height) +
|
|
|
|
|
(1.0f - ty) * tx * read(data, nix, iy, width, height) +
|
|
|
|
|
ty * (1.0f - tx) * read(data, ix, niy, width, height) +
|
|
|
|
|
ty * tx * read(data, nix, niy, width, height);
|
2017-12-07 17:45:37 +01:00
|
|
|
}
|
2017-10-06 21:47:41 +02:00
|
|
|
|
2017-12-07 17:45:37 +01:00
|
|
|
static ccl_always_inline float4 interp_cubic(const TextureInfo& info,
|
|
|
|
|
float x, float y)
|
|
|
|
|
{
|
|
|
|
|
const T *data = (const T*)info.data;
|
|
|
|
|
const int width = info.width;
|
|
|
|
|
const int height = info.height;
|
|
|
|
|
int ix, iy, nix, niy;
|
|
|
|
|
const float tx = frac(x*(float)width - 0.5f, &ix);
|
|
|
|
|
const float ty = frac(y*(float)height - 0.5f, &iy);
|
|
|
|
|
int pix, piy, nnix, nniy;
|
|
|
|
|
switch(info.extension) {
|
|
|
|
|
case EXTENSION_REPEAT:
|
|
|
|
|
ix = wrap_periodic(ix, width);
|
|
|
|
|
iy = wrap_periodic(iy, height);
|
|
|
|
|
pix = wrap_periodic(ix-1, width);
|
|
|
|
|
piy = wrap_periodic(iy-1, height);
|
|
|
|
|
nix = wrap_periodic(ix+1, width);
|
|
|
|
|
niy = wrap_periodic(iy+1, height);
|
|
|
|
|
nnix = wrap_periodic(ix+2, width);
|
|
|
|
|
nniy = wrap_periodic(iy+2, height);
|
|
|
|
|
break;
|
|
|
|
|
case EXTENSION_CLIP:
|
2017-12-08 11:20:12 +01:00
|
|
|
pix = ix - 1;
|
|
|
|
|
piy = iy - 1;
|
|
|
|
|
nix = ix + 1;
|
|
|
|
|
niy = iy + 1;
|
|
|
|
|
nnix = ix + 2;
|
|
|
|
|
nniy = iy + 2;
|
|
|
|
|
break;
|
2017-12-07 17:45:37 +01:00
|
|
|
case EXTENSION_EXTEND:
|
|
|
|
|
pix = wrap_clamp(ix-1, width);
|
|
|
|
|
piy = wrap_clamp(iy-1, height);
|
|
|
|
|
nix = wrap_clamp(ix+1, width);
|
|
|
|
|
niy = wrap_clamp(iy+1, height);
|
|
|
|
|
nnix = wrap_clamp(ix+2, width);
|
|
|
|
|
nniy = wrap_clamp(iy+2, height);
|
|
|
|
|
ix = wrap_clamp(ix, width);
|
|
|
|
|
iy = wrap_clamp(iy, height);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
kernel_assert(0);
|
|
|
|
|
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
|
}
|
|
|
|
|
const int xc[4] = {pix, ix, nix, nnix};
|
2017-12-08 11:20:12 +01:00
|
|
|
const int yc[4] = {piy, iy, niy, nniy};
|
2017-12-07 17:45:37 +01:00
|
|
|
float u[4], v[4];
|
|
|
|
|
/* Some helper macro to keep code reasonable size,
|
|
|
|
|
* let compiler to inline all the matrix multiplications.
|
|
|
|
|
*/
|
2017-12-08 11:20:12 +01:00
|
|
|
#define DATA(x, y) (read(data, xc[x], yc[y], width, height))
|
2017-10-06 21:47:41 +02:00
|
|
|
#define TERM(col) \
|
2017-12-07 17:45:37 +01:00
|
|
|
(v[col] * (u[0] * DATA(0, col) + \
|
|
|
|
|
u[1] * DATA(1, col) + \
|
|
|
|
|
u[2] * DATA(2, col) + \
|
|
|
|
|
u[3] * DATA(3, col)))
|
2017-10-06 21:47:41 +02:00
|
|
|
|
2017-12-07 17:45:37 +01:00
|
|
|
SET_CUBIC_SPLINE_WEIGHTS(u, tx);
|
|
|
|
|
SET_CUBIC_SPLINE_WEIGHTS(v, ty);
|
2017-10-06 21:47:41 +02:00
|
|
|
|
2017-12-07 17:45:37 +01:00
|
|
|
/* Actual interpolation. */
|
|
|
|
|
return TERM(0) + TERM(1) + TERM(2) + TERM(3);
|
2017-10-06 21:47:41 +02:00
|
|
|
#undef TERM
|
|
|
|
|
#undef DATA
|
2017-12-07 17:45:37 +01:00
|
|
|
}
|
|
|
|
|
|
2017-12-08 11:20:12 +01:00
|
|
|
static ccl_always_inline float4 interp(const TextureInfo& info,
|
|
|
|
|
float x, float y)
|
2017-12-07 17:45:37 +01:00
|
|
|
{
|
|
|
|
|
if(UNLIKELY(!info.data)) {
|
|
|
|
|
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
|
}
|
|
|
|
|
switch(info.interpolation) {
|
|
|
|
|
case INTERPOLATION_CLOSEST:
|
|
|
|
|
return interp_closest(info, x, y);
|
|
|
|
|
case INTERPOLATION_LINEAR:
|
|
|
|
|
return interp_linear(info, x, y);
|
|
|
|
|
default:
|
|
|
|
|
return interp_cubic(info, x, y);
|
2017-10-06 21:47:41 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-07 17:45:37 +01:00
|
|
|
/* ******** 3D interpolation ******** */
|
|
|
|
|
|
|
|
|
|
static ccl_always_inline float4 interp_3d_closest(const TextureInfo& info,
|
|
|
|
|
float x, float y, float z)
|
2017-10-06 21:47:41 +02:00
|
|
|
{
|
|
|
|
|
int width = info.width;
|
|
|
|
|
int height = info.height;
|
|
|
|
|
int depth = info.depth;
|
|
|
|
|
int ix, iy, iz;
|
|
|
|
|
|
|
|
|
|
frac(x*(float)width, &ix);
|
|
|
|
|
frac(y*(float)height, &iy);
|
|
|
|
|
frac(z*(float)depth, &iz);
|
|
|
|
|
|
|
|
|
|
switch(info.extension) {
|
|
|
|
|
case EXTENSION_REPEAT:
|
|
|
|
|
ix = wrap_periodic(ix, width);
|
|
|
|
|
iy = wrap_periodic(iy, height);
|
|
|
|
|
iz = wrap_periodic(iz, depth);
|
|
|
|
|
break;
|
|
|
|
|
case EXTENSION_CLIP:
|
|
|
|
|
if(x < 0.0f || y < 0.0f || z < 0.0f ||
|
|
|
|
|
x > 1.0f || y > 1.0f || z > 1.0f)
|
|
|
|
|
{
|
|
|
|
|
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
|
}
|
|
|
|
|
ATTR_FALLTHROUGH;
|
|
|
|
|
case EXTENSION_EXTEND:
|
|
|
|
|
ix = wrap_clamp(ix, width);
|
|
|
|
|
iy = wrap_clamp(iy, height);
|
|
|
|
|
iz = wrap_clamp(iz, depth);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
kernel_assert(0);
|
|
|
|
|
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const T *data = (const T*)info.data;
|
|
|
|
|
return read(data[ix + iy*width + iz*width*height]);
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-07 17:45:37 +01:00
|
|
|
static ccl_always_inline float4 interp_3d_linear(const TextureInfo& info,
|
|
|
|
|
float x, float y, float z)
|
2017-10-06 21:47:41 +02:00
|
|
|
{
|
|
|
|
|
int width = info.width;
|
|
|
|
|
int height = info.height;
|
|
|
|
|
int depth = info.depth;
|
|
|
|
|
int ix, iy, iz;
|
|
|
|
|
int nix, niy, niz;
|
|
|
|
|
|
|
|
|
|
float tx = frac(x*(float)width - 0.5f, &ix);
|
|
|
|
|
float ty = frac(y*(float)height - 0.5f, &iy);
|
|
|
|
|
float tz = frac(z*(float)depth - 0.5f, &iz);
|
|
|
|
|
|
|
|
|
|
switch(info.extension) {
|
|
|
|
|
case EXTENSION_REPEAT:
|
|
|
|
|
ix = wrap_periodic(ix, width);
|
|
|
|
|
iy = wrap_periodic(iy, height);
|
|
|
|
|
iz = wrap_periodic(iz, depth);
|
|
|
|
|
|
|
|
|
|
nix = wrap_periodic(ix+1, width);
|
|
|
|
|
niy = wrap_periodic(iy+1, height);
|
|
|
|
|
niz = wrap_periodic(iz+1, depth);
|
|
|
|
|
break;
|
|
|
|
|
case EXTENSION_CLIP:
|
|
|
|
|
if(x < 0.0f || y < 0.0f || z < 0.0f ||
|
|
|
|
|
x > 1.0f || y > 1.0f || z > 1.0f)
|
|
|
|
|
{
|
|
|
|
|
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
|
}
|
|
|
|
|
ATTR_FALLTHROUGH;
|
|
|
|
|
case EXTENSION_EXTEND:
|
|
|
|
|
nix = wrap_clamp(ix+1, width);
|
|
|
|
|
niy = wrap_clamp(iy+1, height);
|
|
|
|
|
niz = wrap_clamp(iz+1, depth);
|
|
|
|
|
|
|
|
|
|
ix = wrap_clamp(ix, width);
|
|
|
|
|
iy = wrap_clamp(iy, height);
|
|
|
|
|
iz = wrap_clamp(iz, depth);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
kernel_assert(0);
|
|
|
|
|
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const T *data = (const T*)info.data;
|
|
|
|
|
float4 r;
|
|
|
|
|
|
|
|
|
|
r = (1.0f - tz)*(1.0f - ty)*(1.0f - tx)*read(data[ix + iy*width + iz*width*height]);
|
|
|
|
|
r += (1.0f - tz)*(1.0f - ty)*tx*read(data[nix + iy*width + iz*width*height]);
|
|
|
|
|
r += (1.0f - tz)*ty*(1.0f - tx)*read(data[ix + niy*width + iz*width*height]);
|
|
|
|
|
r += (1.0f - tz)*ty*tx*read(data[nix + niy*width + iz*width*height]);
|
|
|
|
|
|
|
|
|
|
r += tz*(1.0f - ty)*(1.0f - tx)*read(data[ix + iy*width + niz*width*height]);
|
|
|
|
|
r += tz*(1.0f - ty)*tx*read(data[nix + iy*width + niz*width*height]);
|
|
|
|
|
r += tz*ty*(1.0f - tx)*read(data[ix + niy*width + niz*width*height]);
|
|
|
|
|
r += tz*ty*tx*read(data[nix + niy*width + niz*width*height]);
|
|
|
|
|
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* TODO(sergey): For some unspeakable reason both GCC-6 and Clang-3.9 are
|
|
|
|
|
* causing stack overflow issue in this function unless it is inlined.
|
|
|
|
|
*
|
|
|
|
|
* Only happens for AVX2 kernel and global __KERNEL_SSE__ vectorization
|
|
|
|
|
* enabled.
|
|
|
|
|
*/
|
Windows: Add support for building with clang.
This commit contains the minimum to make clang build/work with blender, asan and ninja build support is forthcoming
Things to note:
1) Builds and runs, and is able to pass all tests (except for the freestyle_stroke_material.blend test which was broken at that time for all platforms by the looks of it)
2) It's slightly faster than msvc when using cycles. (time in seconds, on an i7-3370)
victor_cpu
msvc:3099.51
clang:2796.43
pavillon_barcelona_cpu
msvc:1872.05
clang:1827.72
koro_cpu
msvc:1097.58
clang:1006.51
fishy_cat_cpu
msvc:815.37
clang:722.2
classroom_cpu
msvc:1705.39
clang:1575.43
bmw27_cpu
msvc:552.38
clang:561.53
barbershop_interior_cpu
msvc:2134.93
clang:1922.33
3) clang on windows uses a drop in replacement for the Microsoft cl.exe (takes some of the Microsoft parameters, but not all, and takes some of the clang parameters but not all) and uses ms headers + libraries + linker, so you still need visual studio installed and will use our existing vc14 svn libs.
4) X64 only currently, X86 builds but crashes on startup.
5) Tested with llvm/clang 6.0.0
6) Requires visual studio integration, available at https://github.com/LazyDodo/llvm-vs2017-integration
7) The Microsoft compiler spawns a few copies of cl in parallel to get faster build times, clang doesn't, so the build time is 3-4x slower than with msvc.
8) No openmp support yet. Have not looked at this much, the binary distribution of clang doesn't seem to include it on windows.
9) No ASAN support yet, some of the sanitizers can be made to work, but it was decided to leave support out of this commit.
Reviewers: campbellbarton
Differential Revision: https://developer.blender.org/D3304
2018-05-28 14:34:47 -06:00
|
|
|
#if defined(__GNUC__) || defined(__clang__)
|
2017-10-06 21:47:41 +02:00
|
|
|
static ccl_always_inline
|
|
|
|
|
#else
|
|
|
|
|
static ccl_never_inline
|
|
|
|
|
#endif
|
|
|
|
|
float4 interp_3d_tricubic(const TextureInfo& info, float x, float y, float z)
|
|
|
|
|
{
|
|
|
|
|
int width = info.width;
|
|
|
|
|
int height = info.height;
|
|
|
|
|
int depth = info.depth;
|
|
|
|
|
int ix, iy, iz;
|
|
|
|
|
int nix, niy, niz;
|
|
|
|
|
/* Tricubic b-spline interpolation. */
|
|
|
|
|
const float tx = frac(x*(float)width - 0.5f, &ix);
|
|
|
|
|
const float ty = frac(y*(float)height - 0.5f, &iy);
|
|
|
|
|
const float tz = frac(z*(float)depth - 0.5f, &iz);
|
|
|
|
|
int pix, piy, piz, nnix, nniy, nniz;
|
|
|
|
|
|
|
|
|
|
switch(info.extension) {
|
|
|
|
|
case EXTENSION_REPEAT:
|
|
|
|
|
ix = wrap_periodic(ix, width);
|
|
|
|
|
iy = wrap_periodic(iy, height);
|
|
|
|
|
iz = wrap_periodic(iz, depth);
|
|
|
|
|
|
|
|
|
|
pix = wrap_periodic(ix-1, width);
|
|
|
|
|
piy = wrap_periodic(iy-1, height);
|
|
|
|
|
piz = wrap_periodic(iz-1, depth);
|
|
|
|
|
|
|
|
|
|
nix = wrap_periodic(ix+1, width);
|
|
|
|
|
niy = wrap_periodic(iy+1, height);
|
|
|
|
|
niz = wrap_periodic(iz+1, depth);
|
|
|
|
|
|
|
|
|
|
nnix = wrap_periodic(ix+2, width);
|
|
|
|
|
nniy = wrap_periodic(iy+2, height);
|
|
|
|
|
nniz = wrap_periodic(iz+2, depth);
|
|
|
|
|
break;
|
|
|
|
|
case EXTENSION_CLIP:
|
|
|
|
|
if(x < 0.0f || y < 0.0f || z < 0.0f ||
|
|
|
|
|
x > 1.0f || y > 1.0f || z > 1.0f)
|
|
|
|
|
{
|
|
|
|
|
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
|
}
|
|
|
|
|
ATTR_FALLTHROUGH;
|
|
|
|
|
case EXTENSION_EXTEND:
|
|
|
|
|
pix = wrap_clamp(ix-1, width);
|
|
|
|
|
piy = wrap_clamp(iy-1, height);
|
|
|
|
|
piz = wrap_clamp(iz-1, depth);
|
|
|
|
|
|
|
|
|
|
nix = wrap_clamp(ix+1, width);
|
|
|
|
|
niy = wrap_clamp(iy+1, height);
|
|
|
|
|
niz = wrap_clamp(iz+1, depth);
|
|
|
|
|
|
|
|
|
|
nnix = wrap_clamp(ix+2, width);
|
|
|
|
|
nniy = wrap_clamp(iy+2, height);
|
|
|
|
|
nniz = wrap_clamp(iz+2, depth);
|
|
|
|
|
|
|
|
|
|
ix = wrap_clamp(ix, width);
|
|
|
|
|
iy = wrap_clamp(iy, height);
|
|
|
|
|
iz = wrap_clamp(iz, depth);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
kernel_assert(0);
|
|
|
|
|
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int xc[4] = {pix, ix, nix, nnix};
|
|
|
|
|
const int yc[4] = {width * piy,
|
|
|
|
|
width * iy,
|
|
|
|
|
width * niy,
|
|
|
|
|
width * nniy};
|
|
|
|
|
const int zc[4] = {width * height * piz,
|
|
|
|
|
width * height * iz,
|
|
|
|
|
width * height * niz,
|
|
|
|
|
width * height * nniz};
|
|
|
|
|
float u[4], v[4], w[4];
|
|
|
|
|
|
|
|
|
|
/* Some helper macro to keep code reasonable size,
|
|
|
|
|
* let compiler to inline all the matrix multiplications.
|
|
|
|
|
*/
|
|
|
|
|
#define DATA(x, y, z) (read(data[xc[x] + yc[y] + zc[z]]))
|
|
|
|
|
#define COL_TERM(col, row) \
|
|
|
|
|
(v[col] * (u[0] * DATA(0, col, row) + \
|
|
|
|
|
u[1] * DATA(1, col, row) + \
|
|
|
|
|
u[2] * DATA(2, col, row) + \
|
|
|
|
|
u[3] * DATA(3, col, row)))
|
|
|
|
|
#define ROW_TERM(row) \
|
|
|
|
|
(w[row] * (COL_TERM(0, row) + \
|
|
|
|
|
COL_TERM(1, row) + \
|
|
|
|
|
COL_TERM(2, row) + \
|
|
|
|
|
COL_TERM(3, row)))
|
|
|
|
|
|
|
|
|
|
SET_CUBIC_SPLINE_WEIGHTS(u, tx);
|
|
|
|
|
SET_CUBIC_SPLINE_WEIGHTS(v, ty);
|
|
|
|
|
SET_CUBIC_SPLINE_WEIGHTS(w, tz);
|
|
|
|
|
|
|
|
|
|
/* Actual interpolation. */
|
|
|
|
|
const T *data = (const T*)info.data;
|
|
|
|
|
return ROW_TERM(0) + ROW_TERM(1) + ROW_TERM(2) + ROW_TERM(3);
|
|
|
|
|
|
|
|
|
|
#undef COL_TERM
|
|
|
|
|
#undef ROW_TERM
|
|
|
|
|
#undef DATA
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ccl_always_inline float4 interp_3d(const TextureInfo& info,
|
|
|
|
|
float x, float y, float z,
|
2017-10-07 02:15:12 +02:00
|
|
|
InterpolationType interp)
|
2017-10-06 21:47:41 +02:00
|
|
|
{
|
|
|
|
|
if(UNLIKELY(!info.data))
|
|
|
|
|
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
|
|
2017-10-07 02:15:12 +02:00
|
|
|
switch((interp == INTERPOLATION_NONE)? info.interpolation: interp) {
|
2017-10-06 21:47:41 +02:00
|
|
|
case INTERPOLATION_CLOSEST:
|
|
|
|
|
return interp_3d_closest(info, x, y, z);
|
|
|
|
|
case INTERPOLATION_LINEAR:
|
|
|
|
|
return interp_3d_linear(info, x, y, z);
|
|
|
|
|
default:
|
|
|
|
|
return interp_3d_tricubic(info, x, y, z);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#undef SET_CUBIC_SPLINE_WEIGHTS
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y)
|
2016-05-20 16:46:49 +02:00
|
|
|
{
|
2017-10-06 21:47:41 +02:00
|
|
|
const TextureInfo& info = kernel_tex_fetch(__texture_info, id);
|
|
|
|
|
|
|
|
|
|
switch(kernel_tex_type(id)) {
|
Unlimited number of textures for Cycles
This patch allows for an unlimited number of textures in Cycles where the hardware allows. It replaces a number static arrays with dynamic arrays and changes the way the flat_slot indices are calculated. Eventually, I'd like to get to a point where there are only flat slots left and textures off all kinds are stored in a single array.
Note that the arrays in DeviceScene are changed from containing device_vector<T> objects to device_vector<T>* pointers. Ideally, I'd like to store objects, but dynamic resizing of a std:vector in pre-C++11 calls the copy constructor, which for a good reason is not implemented for device_vector. Once we require C++11 for Cycles builds, we can implement a move constructor for device_vector and store objects again.
The limits for CUDA Fermi hardware still apply.
Reviewers: tod_baudais, InsigMathK, dingto, #cycles
Reviewed By: dingto, #cycles
Subscribers: dingto, smellslikedonkey
Differential Revision: https://developer.blender.org/D2650
2017-04-27 09:34:51 +02:00
|
|
|
case IMAGE_DATA_TYPE_HALF:
|
2017-10-06 21:47:41 +02:00
|
|
|
return TextureInterpolator<half>::interp(info, x, y);
|
Unlimited number of textures for Cycles
This patch allows for an unlimited number of textures in Cycles where the hardware allows. It replaces a number static arrays with dynamic arrays and changes the way the flat_slot indices are calculated. Eventually, I'd like to get to a point where there are only flat slots left and textures off all kinds are stored in a single array.
Note that the arrays in DeviceScene are changed from containing device_vector<T> objects to device_vector<T>* pointers. Ideally, I'd like to store objects, but dynamic resizing of a std:vector in pre-C++11 calls the copy constructor, which for a good reason is not implemented for device_vector. Once we require C++11 for Cycles builds, we can implement a move constructor for device_vector and store objects again.
The limits for CUDA Fermi hardware still apply.
Reviewers: tod_baudais, InsigMathK, dingto, #cycles
Reviewed By: dingto, #cycles
Subscribers: dingto, smellslikedonkey
Differential Revision: https://developer.blender.org/D2650
2017-04-27 09:34:51 +02:00
|
|
|
case IMAGE_DATA_TYPE_BYTE:
|
2017-10-06 21:47:41 +02:00
|
|
|
return TextureInterpolator<uchar>::interp(info, x, y);
|
2018-07-05 12:37:52 +02:00
|
|
|
case IMAGE_DATA_TYPE_USHORT:
|
|
|
|
|
return TextureInterpolator<uint16_t>::interp(info, x, y);
|
Unlimited number of textures for Cycles
This patch allows for an unlimited number of textures in Cycles where the hardware allows. It replaces a number static arrays with dynamic arrays and changes the way the flat_slot indices are calculated. Eventually, I'd like to get to a point where there are only flat slots left and textures off all kinds are stored in a single array.
Note that the arrays in DeviceScene are changed from containing device_vector<T> objects to device_vector<T>* pointers. Ideally, I'd like to store objects, but dynamic resizing of a std:vector in pre-C++11 calls the copy constructor, which for a good reason is not implemented for device_vector. Once we require C++11 for Cycles builds, we can implement a move constructor for device_vector and store objects again.
The limits for CUDA Fermi hardware still apply.
Reviewers: tod_baudais, InsigMathK, dingto, #cycles
Reviewed By: dingto, #cycles
Subscribers: dingto, smellslikedonkey
Differential Revision: https://developer.blender.org/D2650
2017-04-27 09:34:51 +02:00
|
|
|
case IMAGE_DATA_TYPE_FLOAT:
|
2017-10-06 21:47:41 +02:00
|
|
|
return TextureInterpolator<float>::interp(info, x, y);
|
Unlimited number of textures for Cycles
This patch allows for an unlimited number of textures in Cycles where the hardware allows. It replaces a number static arrays with dynamic arrays and changes the way the flat_slot indices are calculated. Eventually, I'd like to get to a point where there are only flat slots left and textures off all kinds are stored in a single array.
Note that the arrays in DeviceScene are changed from containing device_vector<T> objects to device_vector<T>* pointers. Ideally, I'd like to store objects, but dynamic resizing of a std:vector in pre-C++11 calls the copy constructor, which for a good reason is not implemented for device_vector. Once we require C++11 for Cycles builds, we can implement a move constructor for device_vector and store objects again.
The limits for CUDA Fermi hardware still apply.
Reviewers: tod_baudais, InsigMathK, dingto, #cycles
Reviewed By: dingto, #cycles
Subscribers: dingto, smellslikedonkey
Differential Revision: https://developer.blender.org/D2650
2017-04-27 09:34:51 +02:00
|
|
|
case IMAGE_DATA_TYPE_HALF4:
|
2017-10-06 21:47:41 +02:00
|
|
|
return TextureInterpolator<half4>::interp(info, x, y);
|
Unlimited number of textures for Cycles
This patch allows for an unlimited number of textures in Cycles where the hardware allows. It replaces a number static arrays with dynamic arrays and changes the way the flat_slot indices are calculated. Eventually, I'd like to get to a point where there are only flat slots left and textures off all kinds are stored in a single array.
Note that the arrays in DeviceScene are changed from containing device_vector<T> objects to device_vector<T>* pointers. Ideally, I'd like to store objects, but dynamic resizing of a std:vector in pre-C++11 calls the copy constructor, which for a good reason is not implemented for device_vector. Once we require C++11 for Cycles builds, we can implement a move constructor for device_vector and store objects again.
The limits for CUDA Fermi hardware still apply.
Reviewers: tod_baudais, InsigMathK, dingto, #cycles
Reviewed By: dingto, #cycles
Subscribers: dingto, smellslikedonkey
Differential Revision: https://developer.blender.org/D2650
2017-04-27 09:34:51 +02:00
|
|
|
case IMAGE_DATA_TYPE_BYTE4:
|
2017-10-06 21:47:41 +02:00
|
|
|
return TextureInterpolator<uchar4>::interp(info, x, y);
|
2018-07-05 12:37:52 +02:00
|
|
|
case IMAGE_DATA_TYPE_USHORT4:
|
|
|
|
|
return TextureInterpolator<ushort4>::interp(info, x, y);
|
Unlimited number of textures for Cycles
This patch allows for an unlimited number of textures in Cycles where the hardware allows. It replaces a number static arrays with dynamic arrays and changes the way the flat_slot indices are calculated. Eventually, I'd like to get to a point where there are only flat slots left and textures off all kinds are stored in a single array.
Note that the arrays in DeviceScene are changed from containing device_vector<T> objects to device_vector<T>* pointers. Ideally, I'd like to store objects, but dynamic resizing of a std:vector in pre-C++11 calls the copy constructor, which for a good reason is not implemented for device_vector. Once we require C++11 for Cycles builds, we can implement a move constructor for device_vector and store objects again.
The limits for CUDA Fermi hardware still apply.
Reviewers: tod_baudais, InsigMathK, dingto, #cycles
Reviewed By: dingto, #cycles
Subscribers: dingto, smellslikedonkey
Differential Revision: https://developer.blender.org/D2650
2017-04-27 09:34:51 +02:00
|
|
|
case IMAGE_DATA_TYPE_FLOAT4:
|
2017-10-06 21:47:41 +02:00
|
|
|
return TextureInterpolator<float4>::interp(info, x, y);
|
2018-07-05 12:37:52 +02:00
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
return make_float4(TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A);
|
Unlimited number of textures for Cycles
This patch allows for an unlimited number of textures in Cycles where the hardware allows. It replaces a number static arrays with dynamic arrays and changes the way the flat_slot indices are calculated. Eventually, I'd like to get to a point where there are only flat slots left and textures off all kinds are stored in a single array.
Note that the arrays in DeviceScene are changed from containing device_vector<T> objects to device_vector<T>* pointers. Ideally, I'd like to store objects, but dynamic resizing of a std:vector in pre-C++11 calls the copy constructor, which for a good reason is not implemented for device_vector. Once we require C++11 for Cycles builds, we can implement a move constructor for device_vector and store objects again.
The limits for CUDA Fermi hardware still apply.
Reviewers: tod_baudais, InsigMathK, dingto, #cycles
Reviewed By: dingto, #cycles
Subscribers: dingto, smellslikedonkey
Differential Revision: https://developer.blender.org/D2650
2017-04-27 09:34:51 +02:00
|
|
|
}
|
2016-05-20 16:46:49 +02:00
|
|
|
}
|
|
|
|
|
|
2017-10-07 02:15:12 +02:00
|
|
|
ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, int id, float x, float y, float z, InterpolationType interp)
|
2016-05-20 16:46:49 +02:00
|
|
|
{
|
2017-10-06 21:47:41 +02:00
|
|
|
const TextureInfo& info = kernel_tex_fetch(__texture_info, id);
|
|
|
|
|
|
|
|
|
|
switch(kernel_tex_type(id)) {
|
Unlimited number of textures for Cycles
This patch allows for an unlimited number of textures in Cycles where the hardware allows. It replaces a number static arrays with dynamic arrays and changes the way the flat_slot indices are calculated. Eventually, I'd like to get to a point where there are only flat slots left and textures off all kinds are stored in a single array.
Note that the arrays in DeviceScene are changed from containing device_vector<T> objects to device_vector<T>* pointers. Ideally, I'd like to store objects, but dynamic resizing of a std:vector in pre-C++11 calls the copy constructor, which for a good reason is not implemented for device_vector. Once we require C++11 for Cycles builds, we can implement a move constructor for device_vector and store objects again.
The limits for CUDA Fermi hardware still apply.
Reviewers: tod_baudais, InsigMathK, dingto, #cycles
Reviewed By: dingto, #cycles
Subscribers: dingto, smellslikedonkey
Differential Revision: https://developer.blender.org/D2650
2017-04-27 09:34:51 +02:00
|
|
|
case IMAGE_DATA_TYPE_HALF:
|
2017-10-06 21:47:41 +02:00
|
|
|
return TextureInterpolator<half>::interp_3d(info, x, y, z, interp);
|
Unlimited number of textures for Cycles
This patch allows for an unlimited number of textures in Cycles where the hardware allows. It replaces a number static arrays with dynamic arrays and changes the way the flat_slot indices are calculated. Eventually, I'd like to get to a point where there are only flat slots left and textures off all kinds are stored in a single array.
Note that the arrays in DeviceScene are changed from containing device_vector<T> objects to device_vector<T>* pointers. Ideally, I'd like to store objects, but dynamic resizing of a std:vector in pre-C++11 calls the copy constructor, which for a good reason is not implemented for device_vector. Once we require C++11 for Cycles builds, we can implement a move constructor for device_vector and store objects again.
The limits for CUDA Fermi hardware still apply.
Reviewers: tod_baudais, InsigMathK, dingto, #cycles
Reviewed By: dingto, #cycles
Subscribers: dingto, smellslikedonkey
Differential Revision: https://developer.blender.org/D2650
2017-04-27 09:34:51 +02:00
|
|
|
case IMAGE_DATA_TYPE_BYTE:
|
2017-10-06 21:47:41 +02:00
|
|
|
return TextureInterpolator<uchar>::interp_3d(info, x, y, z, interp);
|
2018-07-05 12:37:52 +02:00
|
|
|
case IMAGE_DATA_TYPE_USHORT:
|
|
|
|
|
return TextureInterpolator<uint16_t>::interp_3d(info, x, y, z, interp);
|
Unlimited number of textures for Cycles
This patch allows for an unlimited number of textures in Cycles where the hardware allows. It replaces a number static arrays with dynamic arrays and changes the way the flat_slot indices are calculated. Eventually, I'd like to get to a point where there are only flat slots left and textures off all kinds are stored in a single array.
Note that the arrays in DeviceScene are changed from containing device_vector<T> objects to device_vector<T>* pointers. Ideally, I'd like to store objects, but dynamic resizing of a std:vector in pre-C++11 calls the copy constructor, which for a good reason is not implemented for device_vector. Once we require C++11 for Cycles builds, we can implement a move constructor for device_vector and store objects again.
The limits for CUDA Fermi hardware still apply.
Reviewers: tod_baudais, InsigMathK, dingto, #cycles
Reviewed By: dingto, #cycles
Subscribers: dingto, smellslikedonkey
Differential Revision: https://developer.blender.org/D2650
2017-04-27 09:34:51 +02:00
|
|
|
case IMAGE_DATA_TYPE_FLOAT:
|
2017-10-06 21:47:41 +02:00
|
|
|
return TextureInterpolator<float>::interp_3d(info, x, y, z, interp);
|
Unlimited number of textures for Cycles
This patch allows for an unlimited number of textures in Cycles where the hardware allows. It replaces a number static arrays with dynamic arrays and changes the way the flat_slot indices are calculated. Eventually, I'd like to get to a point where there are only flat slots left and textures off all kinds are stored in a single array.
Note that the arrays in DeviceScene are changed from containing device_vector<T> objects to device_vector<T>* pointers. Ideally, I'd like to store objects, but dynamic resizing of a std:vector in pre-C++11 calls the copy constructor, which for a good reason is not implemented for device_vector. Once we require C++11 for Cycles builds, we can implement a move constructor for device_vector and store objects again.
The limits for CUDA Fermi hardware still apply.
Reviewers: tod_baudais, InsigMathK, dingto, #cycles
Reviewed By: dingto, #cycles
Subscribers: dingto, smellslikedonkey
Differential Revision: https://developer.blender.org/D2650
2017-04-27 09:34:51 +02:00
|
|
|
case IMAGE_DATA_TYPE_HALF4:
|
2017-10-06 21:47:41 +02:00
|
|
|
return TextureInterpolator<half4>::interp_3d(info, x, y, z, interp);
|
Unlimited number of textures for Cycles
This patch allows for an unlimited number of textures in Cycles where the hardware allows. It replaces a number static arrays with dynamic arrays and changes the way the flat_slot indices are calculated. Eventually, I'd like to get to a point where there are only flat slots left and textures off all kinds are stored in a single array.
Note that the arrays in DeviceScene are changed from containing device_vector<T> objects to device_vector<T>* pointers. Ideally, I'd like to store objects, but dynamic resizing of a std:vector in pre-C++11 calls the copy constructor, which for a good reason is not implemented for device_vector. Once we require C++11 for Cycles builds, we can implement a move constructor for device_vector and store objects again.
The limits for CUDA Fermi hardware still apply.
Reviewers: tod_baudais, InsigMathK, dingto, #cycles
Reviewed By: dingto, #cycles
Subscribers: dingto, smellslikedonkey
Differential Revision: https://developer.blender.org/D2650
2017-04-27 09:34:51 +02:00
|
|
|
case IMAGE_DATA_TYPE_BYTE4:
|
2017-10-06 21:47:41 +02:00
|
|
|
return TextureInterpolator<uchar4>::interp_3d(info, x, y, z, interp);
|
2018-07-05 12:37:52 +02:00
|
|
|
case IMAGE_DATA_TYPE_USHORT4:
|
|
|
|
|
return TextureInterpolator<ushort4>::interp_3d(info, x, y, z, interp);
|
Unlimited number of textures for Cycles
This patch allows for an unlimited number of textures in Cycles where the hardware allows. It replaces a number static arrays with dynamic arrays and changes the way the flat_slot indices are calculated. Eventually, I'd like to get to a point where there are only flat slots left and textures off all kinds are stored in a single array.
Note that the arrays in DeviceScene are changed from containing device_vector<T> objects to device_vector<T>* pointers. Ideally, I'd like to store objects, but dynamic resizing of a std:vector in pre-C++11 calls the copy constructor, which for a good reason is not implemented for device_vector. Once we require C++11 for Cycles builds, we can implement a move constructor for device_vector and store objects again.
The limits for CUDA Fermi hardware still apply.
Reviewers: tod_baudais, InsigMathK, dingto, #cycles
Reviewed By: dingto, #cycles
Subscribers: dingto, smellslikedonkey
Differential Revision: https://developer.blender.org/D2650
2017-04-27 09:34:51 +02:00
|
|
|
case IMAGE_DATA_TYPE_FLOAT4:
|
2017-10-06 21:47:41 +02:00
|
|
|
return TextureInterpolator<float4>::interp_3d(info, x, y, z, interp);
|
2018-07-05 12:37:52 +02:00
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
return make_float4(TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A);
|
Unlimited number of textures for Cycles
This patch allows for an unlimited number of textures in Cycles where the hardware allows. It replaces a number static arrays with dynamic arrays and changes the way the flat_slot indices are calculated. Eventually, I'd like to get to a point where there are only flat slots left and textures off all kinds are stored in a single array.
Note that the arrays in DeviceScene are changed from containing device_vector<T> objects to device_vector<T>* pointers. Ideally, I'd like to store objects, but dynamic resizing of a std:vector in pre-C++11 calls the copy constructor, which for a good reason is not implemented for device_vector. Once we require C++11 for Cycles builds, we can implement a move constructor for device_vector and store objects again.
The limits for CUDA Fermi hardware still apply.
Reviewers: tod_baudais, InsigMathK, dingto, #cycles
Reviewed By: dingto, #cycles
Subscribers: dingto, smellslikedonkey
Differential Revision: https://developer.blender.org/D2650
2017-04-27 09:34:51 +02:00
|
|
|
}
|
2016-05-20 16:46:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CCL_NAMESPACE_END
|
|
|
|
|
|
|
|
|
|
#endif // __KERNEL_CPU_IMAGE_H__
|