Experimenting with safe inversion

Experimenting with safe inversion

/**********************************************************************
Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
********************************************************************/

#pragma OPENCL EXTENSION cl_khr_fp16 : enable

/*************************************************************************
INCLUDES
**************************************************************************/
#include <../RadeonRays/src/kernels/CL/common.cl>

/*************************************************************************
TYPE DEFINITIONS
**************************************************************************/

#define INVALID_ADDR 0xffffffffu
#define INTERNAL_NODE(node) ((node).aabb01_min_or_v0_and_addr0.w != INVALID_ADDR)

#define GROUP_SIZE 64
#define STACK_SIZE 32
#define LDS_STACK_SIZE 16

// BVH node
typedef struct
{
    // TODO: add explanatory comment (gboisse)

    uint4 aabb01_min_or_v0_and_addr0;
    uint4 aabb01_max_or_v1_and_addr1_or_mesh_id;
    uint4 aabb23_min_or_v2_and_addr2_or_prim_id;
    uint4 aabb23_max_and_addr3;

} bvh_node;

#define mymin3(a, b, c) min(min((a), (b)), (c))
#define mymax3(a, b, c) max(max((a), (b)), (c))

INLINE half2 unpackFloat2x16(uint v)
{
    return (half2)
        (as_half(convert_ushort(v & 0xffffu)),
         as_half(convert_ushort(v >> 16)));
}

INLINE half4 fast_intersect_bbox2(uint3 pmin, uint3 pmax, half3 invdir, half3 oxinvdir, float t_max)
{
    half2 pmin_x = unpackFloat2x16(pmin.x);
    half2 pmin_y = unpackFloat2x16(pmin.y);
    half2 pmin_z = unpackFloat2x16(pmin.z);
    half2 pmax_x = unpackFloat2x16(pmax.x);
    half2 pmax_y = unpackFloat2x16(pmax.y);
    half2 pmax_z = unpackFloat2x16(pmax.z);

    half2 f_x = fma(pmax_x, invdir.xx, oxinvdir.xx);
    half2 f_y = fma(pmax_y, invdir.yy, oxinvdir.yy);
    half2 f_z = fma(pmax_z, invdir.zz, oxinvdir.zz);

    half2 n_x = fma(pmin_x, invdir.xx, oxinvdir.xx);
    half2 n_y = fma(pmin_y, invdir.yy, oxinvdir.yy);
    half2 n_z = fma(pmin_z, invdir.zz, oxinvdir.zz);

    half2 t_max_x = max(f_x, n_x);
    half2 t_max_y = max(f_y, n_y);
    half2 t_max_z = max(f_z, n_z);

    half2 t_min_x = min(f_x, n_x);
    half2 t_min_y = min(f_y, n_y);
    half2 t_min_z = min(f_z, n_z);

    half2 t_zero = (half2)(0.0f, 0.0f);
    half2 t_max2 = (half2)(t_max, t_max);
    half2 t1 = min(mymin3(t_max_x, t_max_y, t_max_z), t_max2);
    half2 t0 = max(mymax3(t_min_x, t_min_y, t_min_z), t_zero);

    return (half4)(t0, t1);
}

INLINE float3 safe_invdir2(ray r)
{
    float const dirx = r.d.x;
    float const diry = r.d.y;
    float const dirz = r.d.z;
    float const ooeps = 1e-5;
    float3 invdir;
    invdir.x = 1.0f / (fabs(dirx) > ooeps ? dirx : copysign(ooeps, dirx));
    invdir.y = 1.0f / (fabs(diry) > ooeps ? diry : copysign(ooeps, diry));
    invdir.z = 1.0f / (fabs(dirz) > ooeps ? dirz : copysign(ooeps, dirz));
    return invdir;
}

INLINE void stack_push(
    __local uint *lds_stack,
    __private uint *lds_sptr,
    uint lds_stack_bottom,
    __global uint *stack,
    __private uint *sptr,
    uint idx)
{
    if (*lds_sptr - lds_stack_bottom >= LDS_STACK_SIZE)
    {
        for (int i = 1; i < LDS_STACK_SIZE; ++i)
        {
            stack[*sptr + i] = lds_stack[lds_stack_bottom + i];
        }

        *sptr = *sptr + LDS_STACK_SIZE;
        *lds_sptr = lds_stack_bottom + 1;
    }

    lds_stack[*lds_sptr] = idx;
    *lds_sptr = *lds_sptr + 1;
}

__attribute__((reqd_work_group_size(64, 1, 1)))
KERNEL void intersect_main(
    // Bvh nodes
    GLOBAL const bvh_node *restrict nodes,
    // Rays
    GLOBAL const ray *restrict rays,
    // Number of rays in rays buffer
    GLOBAL const int *restrict num_rays,
    // Stack memory
    GLOBAL uint *stack,
    // Hit data
    GLOBAL Intersection *hits)
{
    uint index = get_global_id(0);
    uint local_index = get_local_id(0);

    // Handle only working subset
    if (index < *num_rays)
    {
        const ray myRay = rays[index];

        if (ray_is_active(&myRay))
        {
            __local uint lds_stack[GROUP_SIZE * LDS_STACK_SIZE];

            // Precompute inverse direction and origin / dir for bbox testing
            const float3 invDir32 = safe_invdir2(myRay);
            const half3 invDir = convert_half3(invDir32);
            const half3 oxInvDir = convert_half3(-myRay.o.xyz * invDir32);

            // Intersection parametric distance
            float closest_t = myRay.o.w;

            // Current node address
            uint addr = 0;
            // Current closest address
            uint closest_addr = INVALID_ADDR;

            uint stack_bottom = STACK_SIZE * index;
            uint sptr = stack_bottom;
            uint lds_stack_bottom = local_index * LDS_STACK_SIZE;
            uint lds_sptr = lds_stack_bottom;

            lds_stack[lds_sptr++] = INVALID_ADDR;

            while (addr != INVALID_ADDR)
            {
                const bvh_node node = nodes[addr];

                if (INTERNAL_NODE(node))
                {
                    half4 s01 = fast_intersect_bbox2(
                        node.aabb01_min_or_v0_and_addr0.xyz,
                        node.aabb01_max_or_v1_and_addr1_or_mesh_id.xyz,
                        invDir, oxInvDir, closest_t);
                    half4 s23 = fast_intersect_bbox2(
                        node.aabb23_min_or_v2_and_addr2_or_prim_id.xyz,
                        node.aabb23_max_and_addr3.xyz,
                        invDir, oxInvDir, closest_t);

                    bool traverse_c0 = (s01.x <= s01.z);
                    bool traverse_c1 = (s01.y <= s01.w) && (node.aabb01_max_or_v1_and_addr1_or_mesh_id.w != INVALID_ADDR);
                    bool traverse_c2 = (s23.x <= s23.z);
                    bool traverse_c3 = (s23.y <= s23.w) && (node.aabb23_max_and_addr3.w != INVALID_ADDR);

                    if (traverse_c0 || traverse_c1 || traverse_c2 || traverse_c3)
                    {
                        uint a = INVALID_ADDR;
                        half d = 100000000.0f;

                        if (traverse_c0)
                        {
                            a = node.aabb01_min_or_v0_and_addr0.w;
                            d = s01.x;
                        }

                        if (traverse_c1)
                        {
                            if (a == INVALID_ADDR)
                                a = node.aabb01_max_or_v1_and_addr1_or_mesh_id.w;
                            else
                            {
                                uint topush = s01.y < d ? a : node.aabb01_max_or_v1_and_addr1_or_mesh_id.w;
                                d = min(s01.y, d);
                                a = topush == a ? node.aabb01_max_or_v1_and_addr1_or_mesh_id.w : a;
                                stack_push(lds_stack, &lds_sptr, lds_stack_bottom, stack, &sptr, topush);
                            }
                        }

                        if (traverse_c2)
                        {
                            if (a == INVALID_ADDR)
                                a = node.aabb23_min_or_v2_and_addr2_or_prim_id.w;
                            else
                            {
                                uint topush = s23.x < d ? a : node.aabb23_min_or_v2_and_addr2_or_prim_id.w;
                                d = min(s23.x, d);
                                a = topush == a ? node.aabb23_min_or_v2_and_addr2_or_prim_id.w : a;
                                stack_push(lds_stack, &lds_sptr, lds_stack_bottom, stack, &sptr, topush);
                            }
                        }

                        if (traverse_c3)
                        {
                            if (a == INVALID_ADDR)
                                a = node.aabb23_max_and_addr3.w;
                            else
                            {
                                uint topush = s23.y < d ? a : node.aabb23_max_and_addr3.w;
                                d = min(s23.y, d);
                                a = topush == a ? node.aabb23_max_and_addr3.w : a;
                                stack_push(lds_stack, &lds_sptr, lds_stack_bottom, stack, &sptr, topush);
                            }
                        }

                        addr = a;
                        continue;
                    }
                }
                else
                {
                    float t = fast_intersect_triangle(
                        myRay,
                        as_float3(node.aabb01_min_or_v0_and_addr0.xyz),
                        as_float3(node.aabb01_max_or_v1_and_addr1_or_mesh_id.xyz),
                        as_float3(node.aabb23_min_or_v2_and_addr2_or_prim_id.xyz),
                        closest_t);

                    if (t < closest_t)
                    {
                        closest_t = t;
                        closest_addr = addr;
                    }
                }

                addr = lds_stack[--lds_sptr];

                if (addr == INVALID_ADDR && sptr > stack_bottom)
                {
                    sptr -= LDS_STACK_SIZE;
                    for (int i = 1; i < LDS_STACK_SIZE; ++i)
                    {
                        lds_stack[lds_stack_bottom + i] = stack[sptr + i];
                    }

                    lds_sptr = lds_stack_bottom + LDS_STACK_SIZE - 1;
                    addr = lds_stack[lds_sptr];
                }
            }

            // Check if we have found an intersection
            if (closest_addr != INVALID_ADDR)
            {
                // Calculate hit position
                const bvh_node node = nodes[closest_addr];
                const float3 p = myRay.o.xyz + closest_t * myRay.d.xyz;

                // Calculte barycentric coordinates
                const float2 uv = triangle_calculate_barycentrics(
                    p,
                    as_float3(node.aabb01_min_or_v0_and_addr0.xyz),
                    as_float3(node.aabb01_max_or_v1_and_addr1_or_mesh_id.xyz),
                    as_float3(node.aabb23_min_or_v2_and_addr2_or_prim_id.xyz));

                // Update hit information
                hits[index].prim_id = node.aabb23_min_or_v2_and_addr2_or_prim_id.w;
                hits[index].shape_id = node.aabb01_max_or_v1_and_addr1_or_mesh_id.w;
                hits[index].uvwt = (float4)(uv.x, uv.y, 0.0f, closest_t);
            }
            else
            {
                // Miss here
                hits[index].prim_id = MISS_MARKER;
                hits[index].shape_id = MISS_MARKER;
            }
        }
    }
}

__attribute__((reqd_work_group_size(64, 1, 1)))
KERNEL void occluded_main(
    // Bvh nodes
    GLOBAL const bvh_node *restrict nodes,
    // Rays
    GLOBAL const ray *restrict rays,
    // Number of rays in rays buffer
    GLOBAL const int *restrict num_rays,
    // Stack memory
    GLOBAL uint *stack,
    // Hit results: 1 for hit and -1 for miss
    GLOBAL int *hits)
{
    uint index = get_global_id(0);
    uint local_index = get_local_id(0);

    // Handle only working subset
    if (index < *num_rays)
    {
        const ray myRay = rays[index];

        if (ray_is_active(&myRay))
        {
            __local uint lds_stack[GROUP_SIZE * LDS_STACK_SIZE];

            // Precompute inverse direction and origin / dir for bbox testing
            const float3 invDir32 = safe_invdir2(myRay);
            const half3 invDir = convert_half3(invDir32);
            const half3 oxInvDir = convert_half3(-myRay.o.xyz * invDir32);

            // Intersection parametric distance
            float closest_t = myRay.o.w;

            // Current node address
            uint addr = 0;
            // Current closest address
            uint closest_addr = INVALID_ADDR;

            uint stack_bottom = STACK_SIZE * index;
            uint sptr = stack_bottom;
            uint lds_stack_bottom = local_index * LDS_STACK_SIZE;
            uint lds_sptr = lds_stack_bottom;

            lds_stack[lds_sptr++] = INVALID_ADDR;

            while (addr != INVALID_ADDR)
            {
                const bvh_node node = nodes[addr];

                if (INTERNAL_NODE(node))
                {
                    half4 s01 = fast_intersect_bbox2(
                        node.aabb01_min_or_v0_and_addr0.xyz,
                        node.aabb01_max_or_v1_and_addr1_or_mesh_id.xyz,
                        invDir, oxInvDir, closest_t);
                    half4 s23 = fast_intersect_bbox2(
                        node.aabb23_min_or_v2_and_addr2_or_prim_id.xyz,
                        node.aabb23_max_and_addr3.xyz,
                        invDir, oxInvDir, closest_t);

                    bool traverse_c0 = (s01.x <= s01.z);
                    bool traverse_c1 = (s01.y <= s01.w) && (node.aabb01_max_or_v1_and_addr1_or_mesh_id.w != INVALID_ADDR);
                    bool traverse_c2 = (s23.x <= s23.z);
                    bool traverse_c3 = (s23.y <= s23.w) && (node.aabb23_max_and_addr3.w != INVALID_ADDR);

                    if (traverse_c0 || traverse_c1 || traverse_c2 || traverse_c3)
                    {
                        uint a = INVALID_ADDR;
                        half d = 100000000.0f;

                        if (traverse_c0)
                        {
                            a = node.aabb01_min_or_v0_and_addr0.w;
                            d = s01.x;
                        }

                        if (traverse_c1)
                        {
                            if (a == INVALID_ADDR)
                                a = node.aabb01_max_or_v1_and_addr1_or_mesh_id.w;
                            else
                            {
                                uint topush = s01.y < d ? a : node.aabb01_max_or_v1_and_addr1_or_mesh_id.w;
                                d = min(s01.y, d);
                                a = topush == a ? node.aabb01_max_or_v1_and_addr1_or_mesh_id.w : a;
                                stack_push(lds_stack, &lds_sptr, lds_stack_bottom, stack, &sptr, topush);
                            }
                        }

                        if (traverse_c2)
                        {
                            if (a == INVALID_ADDR)
                                a = node.aabb23_min_or_v2_and_addr2_or_prim_id.w;
                            else
                            {
                                uint topush = s23.x < d ? a : node.aabb23_min_or_v2_and_addr2_or_prim_id.w;
                                d = min(s23.x, d);
                                a = topush == a ? node.aabb23_min_or_v2_and_addr2_or_prim_id.w : a;
                                stack_push(lds_stack, &lds_sptr, lds_stack_bottom, stack, &sptr, topush);
                            }
                        }

                        if (traverse_c3)
                        {
                            if (a == INVALID_ADDR)
                                a = node.aabb23_max_and_addr3.w;
                            else
                            {
                                uint topush = s23.y < d ? a : node.aabb23_max_and_addr3.w;
                                d = min(s23.y, d);
                                a = topush == a ? node.aabb23_max_and_addr3.w : a;
                                stack_push(lds_stack, &lds_sptr, lds_stack_bottom, stack, &sptr, topush);
                            }
                        }

                        addr = a;
                        continue;
                    }
                }
                else
                {
                    float t = fast_intersect_triangle(
                        myRay,
                        as_float3(node.aabb01_min_or_v0_and_addr0.xyz),
                        as_float3(node.aabb01_max_or_v1_and_addr1_or_mesh_id.xyz),
                        as_float3(node.aabb23_min_or_v2_and_addr2_or_prim_id.xyz),
                        closest_t);

                    if (t < closest_t)
                    {
                        hits[index] = HIT_MARKER;
                        return;
                    }
                }

                addr = lds_stack[--lds_sptr];

                if (addr == INVALID_ADDR && sptr > stack_bottom)
                {
                    sptr -= LDS_STACK_SIZE;
                    for (int i = 1; i < LDS_STACK_SIZE; ++i)
                    {
                        lds_stack[lds_stack_bottom + i] = stack[sptr + i];
                    }

                    lds_sptr = lds_stack_bottom + LDS_STACK_SIZE - 1;
                    addr = lds_stack[lds_sptr];
                }
            }

            // Finished traversal, but no intersection found
            hits[index] = MISS_MARKER;
        }
    }
}

/**********************************************************************
Copyright (c) 2016 Advanced Micro Devices, Inc. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
********************************************************************/

#pragma OPENCL EXTENSION cl_khr_fp16 : enable

/*************************************************************************
INCLUDES
**************************************************************************/
#include <../RadeonRays/src/kernels/CL/common.cl>

/*************************************************************************
TYPE DEFINITIONS
**************************************************************************/

#define INVALID_ADDR 0xffffffffu
#define INTERNAL_NODE(node) ((node).aabb01_min_or_v0_and_addr0.w != INVALID_ADDR)

#define GROUP_SIZE 64
#define STACK_SIZE 32
#define LDS_STACK_SIZE 16

// BVH node
typedef struct
{
    // TODO: add explanatory comment (gboisse)

    uint4 aabb01_min_or_v0_and_addr0;
    uint4 aabb01_max_or_v1_and_addr1_or_mesh_id;
    uint4 aabb23_min_or_v2_and_addr2_or_prim_id;
    uint4 aabb23_max_and_addr3;

} bvh_node;

#define mymin3(a, b, c) min(min((a), (b)), (c))
#define mymax3(a, b, c) max(max((a), (b)), (c))

INLINE half2 unpackFloat2x16(uint v)
{
    return (half2)
        (as_half(convert_ushort(v & 0xffffu)),
         as_half(convert_ushort(v >> 16)));
}

INLINE half4 fast_intersect_bbox2(uint3 pmin, uint3 pmax, half3 invdir, half3 oxinvdir, float t_max)
{
    half2 pmin_x = unpackFloat2x16(pmin.x);
    half2 pmin_y = unpackFloat2x16(pmin.y);
    half2 pmin_z = unpackFloat2x16(pmin.z);
    half2 pmax_x = unpackFloat2x16(pmax.x);
    half2 pmax_y = unpackFloat2x16(pmax.y);
    half2 pmax_z = unpackFloat2x16(pmax.z);

    half2 f_x = fma(pmax_x, invdir.xx, oxinvdir.xx);
    half2 f_y = fma(pmax_y, invdir.yy, oxinvdir.yy);
    half2 f_z = fma(pmax_z, invdir.zz, oxinvdir.zz);

    half2 n_x = fma(pmin_x, invdir.xx, oxinvdir.xx);
    half2 n_y = fma(pmin_y, invdir.yy, oxinvdir.yy);
    half2 n_z = fma(pmin_z, invdir.zz, oxinvdir.zz);

    half2 t_max_x = max(f_x, n_x);
    half2 t_max_y = max(f_y, n_y);
    half2 t_max_z = max(f_z, n_z);

    half2 t_min_x = min(f_x, n_x);
    half2 t_min_y = min(f_y, n_y);
    half2 t_min_z = min(f_z, n_z);

    half2 t_zero = (half2)(0.0f, 0.0f);
    half2 t_max2 = (half2)(t_max, t_max);
    half2 t1 = min(mymin3(t_max_x, t_max_y, t_max_z), t_max2);
    half2 t0 = max(mymax3(t_min_x, t_min_y, t_min_z), t_zero);

    return (half4)(t0, t1);
}

INLINE void stack_push(
    __local uint *lds_stack,
    __private uint *lds_sptr,
    uint lds_stack_bottom,
    __global uint *stack,
    __private uint *sptr,
    uint idx)
{
    if (*lds_sptr - lds_stack_bottom >= LDS_STACK_SIZE)
    {
        for (int i = 1; i < LDS_STACK_SIZE; ++i)
        {
            stack[*sptr + i] = lds_stack[lds_stack_bottom + i];
        }

        *sptr = *sptr + LDS_STACK_SIZE;
        *lds_sptr = lds_stack_bottom + 1;
    }

    lds_stack[*lds_sptr] = idx;
    *lds_sptr = *lds_sptr + 1;
}

__attribute__((reqd_work_group_size(64, 1, 1)))
KERNEL void intersect_main(
    // Bvh nodes
    GLOBAL const bvh_node *restrict nodes,
    // Rays
    GLOBAL const ray *restrict rays,
    // Number of rays in rays buffer
    GLOBAL const int *restrict num_rays,
    // Stack memory
    GLOBAL uint *stack,
    // Hit data
    GLOBAL Intersection *hits)
{
    uint index = get_global_id(0);
    uint local_index = get_local_id(0);

    // Handle only working subset
    if (index < *num_rays)
    {
        const ray myRay = rays[index];

        if (ray_is_active(&myRay))
        {
            __local uint lds_stack[GROUP_SIZE * LDS_STACK_SIZE];

            // Precompute inverse direction and origin / dir for bbox testing
            const float3 invDir32 = safe_invdir(myRay);
            const half3 invDir = convert_half3(invDir32);
            const half3 oxInvDir = convert_half3(-myRay.o.xyz * invDir32);

            // Intersection parametric distance
            float closest_t = myRay.o.w;

            // Current node address
            uint addr = 0;
            // Current closest address
            uint closest_addr = INVALID_ADDR;

            uint stack_bottom = STACK_SIZE * index;
            uint sptr = stack_bottom;
            uint lds_stack_bottom = local_index * LDS_STACK_SIZE;
            uint lds_sptr = lds_stack_bottom;

            lds_stack[lds_sptr++] = INVALID_ADDR;

            while (addr != INVALID_ADDR)
            {
                const bvh_node node = nodes[addr];

                if (INTERNAL_NODE(node))
                {
                    half4 s01 = fast_intersect_bbox2(
                        node.aabb01_min_or_v0_and_addr0.xyz,
                        node.aabb01_max_or_v1_and_addr1_or_mesh_id.xyz,
                        invDir, oxInvDir, closest_t);
                    half4 s23 = fast_intersect_bbox2(
                        node.aabb23_min_or_v2_and_addr2_or_prim_id.xyz,
                        node.aabb23_max_and_addr3.xyz,
                        invDir, oxInvDir, closest_t);

                    bool traverse_c0 = (s01.x <= s01.z);
                    bool traverse_c1 = (s01.y <= s01.w) && (node.aabb01_max_or_v1_and_addr1_or_mesh_id.w != INVALID_ADDR);
                    bool traverse_c2 = (s23.x <= s23.z);
                    bool traverse_c3 = (s23.y <= s23.w) && (node.aabb23_max_and_addr3.w != INVALID_ADDR);

                    if (traverse_c0 || traverse_c1 || traverse_c2 || traverse_c3)
                    {
                        uint a = INVALID_ADDR;
                        half d = 100000000.0f;

                        if (traverse_c0)
                        {
                            a = node.aabb01_min_or_v0_and_addr0.w;
                            d = s01.x;
                        }

                        if (traverse_c1)
                        {
                            if (a == INVALID_ADDR)
                                a = node.aabb01_max_or_v1_and_addr1_or_mesh_id.w;
                            else
                            {
                                uint topush = s01.y < d ? a : node.aabb01_max_or_v1_and_addr1_or_mesh_id.w;
                                d = min(s01.y, d);
                                a = topush == a ? node.aabb01_max_or_v1_and_addr1_or_mesh_id.w : a;
                                stack_push(lds_stack, &lds_sptr, lds_stack_bottom, stack, &sptr, topush);
                            }
                        }

                        if (traverse_c2)
                        {
                            if (a == INVALID_ADDR)
                                a = node.aabb23_min_or_v2_and_addr2_or_prim_id.w;
                            else
                            {
                                uint topush = s23.x < d ? a : node.aabb23_min_or_v2_and_addr2_or_prim_id.w;
                                d = min(s23.x, d);
                                a = topush == a ? node.aabb23_min_or_v2_and_addr2_or_prim_id.w : a;
                                stack_push(lds_stack, &lds_sptr, lds_stack_bottom, stack, &sptr, topush);
                            }
                        }

                        if (traverse_c3)
                        {
                            if (a == INVALID_ADDR)
                                a = node.aabb23_max_and_addr3.w;
                            else
                            {
                                uint topush = s23.y < d ? a : node.aabb23_max_and_addr3.w;
                                d = min(s23.y, d);
                                a = topush == a ? node.aabb23_max_and_addr3.w : a;
                                stack_push(lds_stack, &lds_sptr, lds_stack_bottom, stack, &sptr, topush);
                            }
                        }

                        addr = a;
                        continue;
                    }
                }
                else
                {
                    float t = fast_intersect_triangle(
                        myRay,
                        as_float3(node.aabb01_min_or_v0_and_addr0.xyz),
                        as_float3(node.aabb01_max_or_v1_and_addr1_or_mesh_id.xyz),
                        as_float3(node.aabb23_min_or_v2_and_addr2_or_prim_id.xyz),
                        closest_t);

                    if (t < closest_t)
                    {
                        closest_t = t;
                        closest_addr = addr;
                    }
                }

                addr = lds_stack[--lds_sptr];

                if (addr == INVALID_ADDR && sptr > stack_bottom)
                {
                    sptr -= LDS_STACK_SIZE;
                    for (int i = 1; i < LDS_STACK_SIZE; ++i)
                    {
                        lds_stack[lds_stack_bottom + i] = stack[sptr + i];
                    }

                    lds_sptr = lds_stack_bottom + LDS_STACK_SIZE - 1;
                    addr = lds_stack[lds_sptr];
                }
            }

            // Check if we have found an intersection
            if (closest_addr != INVALID_ADDR)
            {
                // Calculate hit position
                const bvh_node node = nodes[closest_addr];
                const float3 p = myRay.o.xyz + closest_t * myRay.d.xyz;

                // Calculte barycentric coordinates
                const float2 uv = triangle_calculate_barycentrics(
                    p,
                    as_float3(node.aabb01_min_or_v0_and_addr0.xyz),
                    as_float3(node.aabb01_max_or_v1_and_addr1_or_mesh_id.xyz),
                    as_float3(node.aabb23_min_or_v2_and_addr2_or_prim_id.xyz));

                // Update hit information
                hits[index].prim_id = node.aabb23_min_or_v2_and_addr2_or_prim_id.w;
                hits[index].shape_id = node.aabb01_max_or_v1_and_addr1_or_mesh_id.w;
                hits[index].uvwt = (float4)(uv.x, uv.y, 0.0f, closest_t);
            }
            else
            {
                // Miss here
                hits[index].prim_id = MISS_MARKER;
                hits[index].shape_id = MISS_MARKER;
            }
        }
    }
}

__attribute__((reqd_work_group_size(64, 1, 1)))
KERNEL void occluded_main(
    // Bvh nodes
    GLOBAL const bvh_node *restrict nodes,
    // Rays
    GLOBAL const ray *restrict rays,
    // Number of rays in rays buffer
    GLOBAL const int *restrict num_rays,
    // Stack memory
    GLOBAL uint *stack,
    // Hit results: 1 for hit and -1 for miss
    GLOBAL int *hits)
{
    uint index = get_global_id(0);
    uint local_index = get_local_id(0);

    // Handle only working subset
    if (index < *num_rays)
    {
        const ray myRay = rays[index];

        if (ray_is_active(&myRay))
        {
            __local uint lds_stack[GROUP_SIZE * LDS_STACK_SIZE];

            // Precompute inverse direction and origin / dir for bbox testing
            const float3 invDir32 = safe_invdir(myRay);
            const half3 invDir = convert_half3(invDir32);
            const half3 oxInvDir = convert_half3(-myRay.o.xyz * invDir32);

            // Intersection parametric distance
            float closest_t = myRay.o.w;

            // Current node address
            uint addr = 0;
            // Current closest address
            uint closest_addr = INVALID_ADDR;

            uint stack_bottom = STACK_SIZE * index;
            uint sptr = stack_bottom;
            uint lds_stack_bottom = local_index * LDS_STACK_SIZE;
            uint lds_sptr = lds_stack_bottom;

            lds_stack[lds_sptr++] = INVALID_ADDR;

            while (addr != INVALID_ADDR)
            {
                const bvh_node node = nodes[addr];

                if (INTERNAL_NODE(node))
                {
                    half4 s01 = fast_intersect_bbox2(
                        node.aabb01_min_or_v0_and_addr0.xyz,
                        node.aabb01_max_or_v1_and_addr1_or_mesh_id.xyz,
                        invDir, oxInvDir, closest_t);
                    half4 s23 = fast_intersect_bbox2(
                        node.aabb23_min_or_v2_and_addr2_or_prim_id.xyz,
                        node.aabb23_max_and_addr3.xyz,
                        invDir, oxInvDir, closest_t);

                    bool traverse_c0 = (s01.x <= s01.z);
                    bool traverse_c1 = (s01.y <= s01.w) && (node.aabb01_max_or_v1_and_addr1_or_mesh_id.w != INVALID_ADDR);
                    bool traverse_c2 = (s23.x <= s23.z);
                    bool traverse_c3 = (s23.y <= s23.w) && (node.aabb23_max_and_addr3.w != INVALID_ADDR);

                    if (traverse_c0 || traverse_c1 || traverse_c2 || traverse_c3)
                    {
                        uint a = INVALID_ADDR;
                        half d = 100000000.0f;

                        if (traverse_c0)
                        {
                            a = node.aabb01_min_or_v0_and_addr0.w;
                            d = s01.x;
                        }

                        if (traverse_c1)
                        {
                            if (a == INVALID_ADDR)
                                a = node.aabb01_max_or_v1_and_addr1_or_mesh_id.w;
                            else
                            {
                                uint topush = s01.y < d ? a : node.aabb01_max_or_v1_and_addr1_or_mesh_id.w;
                                d = min(s01.y, d);
                                a = topush == a ? node.aabb01_max_or_v1_and_addr1_or_mesh_id.w : a;
                                stack_push(lds_stack, &lds_sptr, lds_stack_bottom, stack, &sptr, topush);
                            }
                        }

                        if (traverse_c2)
                        {
                            if (a == INVALID_ADDR)
                                a = node.aabb23_min_or_v2_and_addr2_or_prim_id.w;
                            else
                            {
                                uint topush = s23.x < d ? a : node.aabb23_min_or_v2_and_addr2_or_prim_id.w;
                                d = min(s23.x, d);
                                a = topush == a ? node.aabb23_min_or_v2_and_addr2_or_prim_id.w : a;
                                stack_push(lds_stack, &lds_sptr, lds_stack_bottom, stack, &sptr, topush);
                            }
                        }

                        if (traverse_c3)
                        {
                            if (a == INVALID_ADDR)
                                a = node.aabb23_max_and_addr3.w;
                            else
                            {
                                uint topush = s23.y < d ? a : node.aabb23_max_and_addr3.w;
                                d = min(s23.y, d);
                                a = topush == a ? node.aabb23_max_and_addr3.w : a;
                                stack_push(lds_stack, &lds_sptr, lds_stack_bottom, stack, &sptr, topush);
                            }
                        }

                        addr = a;
                        continue;
                    }
                }
                else
                {
                    float t = fast_intersect_triangle(
                        myRay,
                        as_float3(node.aabb01_min_or_v0_and_addr0.xyz),
                        as_float3(node.aabb01_max_or_v1_and_addr1_or_mesh_id.xyz),
                        as_float3(node.aabb23_min_or_v2_and_addr2_or_prim_id.xyz),
                        closest_t);

                    if (t < closest_t)
                    {
                        hits[index] = HIT_MARKER;
                        return;
                    }
                }

                addr = lds_stack[--lds_sptr];

                if (addr == INVALID_ADDR && sptr > stack_bottom)
                {
                    sptr -= LDS_STACK_SIZE;
                    for (int i = 1; i < LDS_STACK_SIZE; ++i)
                    {
                        lds_stack[lds_stack_bottom + i] = stack[sptr + i];
                    }

                    lds_sptr = lds_stack_bottom + LDS_STACK_SIZE - 1;
                    addr = lds_stack[lds_sptr];
                }
            }

            // Finished traversal, but no intersection found
            hits[index] = MISS_MARKER;
        }
    }
}


open cl compiler error fix

open cl compiler error fix

__kernel void MinSquareVal(__global float* pA, __global float* pC, int square_size, int offset)
{
	const int x = get_global_id(0);
	const int y = get_global_id(1);
	const int width = get_global_size(0);
	const int height = get_global_size(1);

	int minH = offset > y ? 0 : y - offset;
	int maxH = min(y + square_size - offset, height);

	int minW = offset > x ? 0 : x - offset;
	int maxW = min(x + square_size - offset, width);

	float minValue = pA[minH * width + minW];

	for (int j = minH; j < maxH; j++)
	{
		for (int i = minW; i < maxW; i++)
		{
			float tmp = pA[j * width + i];
			if (tmp < minValue) minValue = tmp;
		}
	}

	pC[y * width + x] = minValue;
}

float calcWeight(__global float* multipliers, int len)
{
	float weight_no_border = 0;
	for (int j = 0; j < len; j++)
	{
		weight_no_border += multipliers[j];
	}
	return weight_no_border;
}

__kernel void Convolution(__global float* multipliers, __global float* inp, __global float* result,
							int xsize, int xstep, int len, int offset, float border_ratio)
{
	const int ox = get_global_id(0);
	const int y = get_global_id(1);

	const int oxsize = get_global_size(0);
	const int ysize = get_global_size(1);

	const int x = ox * xstep;
/*
	float weight_no_border = 0;
	for (int j = 0; j <= 2 * offset; j++)
	{
		weight_no_border += multipliers[j];
	}
*/
	float weight_no_border = calcWeight(multipliers, len);

	int minx = x < offset ? 0 : x - offset;
	int maxx = min(xsize, x + len - offset);

	float weight = 0.0;
	for (int j = minx; j < maxx; j++)
	{
		weight += multipliers[j - x + offset];
	}

	weight = (1.0 - border_ratio) * weight + border_ratio * weight_no_border;
	float scale = 1.0 / weight;

	float sum = 0.0;
	for (int j = minx; j < maxx; j++)
	{
		sum += inp[y * xsize + j] * multipliers[j - x + offset];
	}

	result[ox * ysize + y] = sum * scale;
}
/*
__kernel void ConvolutionX(__global float* multipliers, __global float* inp, __global float* result,
	int len, int offset, float border_ratio)
{
	const int x = get_global_id(0);
	const int y = get_global_id(1);

	const int xsize = get_global_size(0);
	const int ysize = get_global_size(1);

	float weight_no_border = 0;
	for (int j = 0; j <= 2 * offset; j++)
	{
		weight_no_border += multipliers[j];
	}

	int minx = x < offset ? 0 : x - offset;
	int maxx = min(xsize, x + len - offset);

	int miny = y < offset ? 0 : y - offset;
	int maxy = min(ysize, y + len - offset);

	float weightX = 0.0;
	for (int j = minx; j < maxx; j++)
	{
		weightX += multipliers[j - x + offset];
	}

	weightX = (1.0 - border_ratio) * weightX + border_ratio * weight_no_border;

	float weightY = 0.0;
	for (int j = miny; j < maxy; j++)
	{
		weightY += multipliers[j - y + offset];
	}

	weightY = (1.0 - border_ratio) * weightY + border_ratio * weight_no_border;

	float sum = 0.0;
	for (int j = miny; j < maxy; j++)
	{
		float sumx = 0.0;
		for (int i = minx; i < maxx; i++)
		{
			sumx += inp[j * xsize + i] * multipliers[i - x + offset];
		}

		sum += sumx * multipliers[j - y + offset];
	}

	result[y * xsize + x] = sum / weightY / weightX;
}
*/

__kernel void ConvolutionX(__global float* multipliers, __global float* inp, __global float* result,
	int len, int offset, float border_ratio)
{
	const int x = get_global_id(0);
	const int y = get_global_id(1);

	const int xsize = get_global_size(0);
	const int ysize = get_global_size(1);

	float weight_no_border = 0;
	for (int j = 0; j <= 2 * offset; j++)
	{
		weight_no_border += multipliers[j];
	}

	int minx = x < offset ? 0 : x - offset;
	int maxx = min(xsize, x + len - offset);

	float weight = 0.0;
	for (int j = minx; j < maxx; j++)
	{
		weight += multipliers[j - x + offset];
	}

	weight = (1.0 - border_ratio) * weight + border_ratio * weight_no_border;
	float scale = 1.0 / weight;

	float sum = 0.0;
	for (int j = minx; j < maxx; j++)
	{
		sum += inp[y * xsize + j] * multipliers[j - x + offset];
	}

	result[x * ysize + y] = sum * scale;
}

__kernel void ConvolutionY(__global float* multipliers, __global float* inp, __global float* result,
	int len, int offset, float border_ratio)
{
	const int x = get_global_id(0);
	const int y = get_global_id(1);

	const int xsize = get_global_size(0);
	const int ysize = get_global_size(1);

	float weight_no_border = 0;
	for (int j = 0; j <= 2 * offset; j++)
	{
		weight_no_border += multipliers[j];
	}

	int miny = y < offset ? 0 : y - offset;
	int maxy = min(ysize, y + len - offset);

	float weight = 0.0;
	for (int j = miny; j < maxy; j++)
	{
		weight += multipliers[j - y + offset];
	}

	weight = (1.0 - border_ratio) * weight + border_ratio * weight_no_border;
	float scale = 1.0 / weight;

	float sum = 0.0;
	for (int j = miny; j < maxy; j++)
	{
		sum += inp[j * xsize + x] * multipliers[j - y + offset];
	}

	result[y * xsize + x] = sum * scale;
}

__kernel void DownSample(__global float* pA, __global float* pC, int square)
{
	const int x = get_global_id(0);
	const int y = get_global_id(1);

	const int xsize = get_global_size(0);
	const int ysize = get_global_size(1);

	const int oxsize = xsize / square;

	const int sample_x = x / square;
	const int sample_y = y / square;

	pC[y * xsize + x] = pA[sample_y * oxsize + sample_x];
}

void OpsinAbsorbance(const double in[3], double out[3])
{
	const float mix[12] = {
	0.348036746003,
	0.577814843137,
	0.0544556093735,
	0.774145581713,
	0.26922717275,
	0.767247733938,
	0.0366922708552,
	0.920130265014,
	0.0882062883536,
	0.158581714673,
	0.712857943858,
	10.6524069248,
	};

	out[0] = mix[0] * in[0] + mix[1] * in[1] + mix[2] * in[2] + mix[3];
	out[1] = mix[4] * in[0] + mix[5] * in[1] + mix[6] * in[2] + mix[7];
	out[2] = mix[8] * in[0] + mix[9] * in[1] + mix[10] * in[2] + mix[11];
}

double EvaluatePolynomial(const double x, const double *coefficients, int n)
{
	double b1 = 0.0;
	double b2 = 0.0;

	for (int i = n - 1; i >= 0; i--)
	{
		if (i == 0) {
			const double x_b1 = x * b1;
			b1 = x_b1 - b2 + coefficients[0];
			break;
		}
		const double x_b1 = x * b1;
		const double t = (x_b1 + x_b1) - b2 + coefficients[i];
		b2 = b1;
		b1 = t;
	}

	return b1;
}

double Gamma(double v)
{
	double min_value = 0.770000000000000;
	double max_value = 274.579999999999984;

	const double p[5 + 1] = {
		881.979476556478289, 1496.058452015812463, 908.662212739659481,
		373.566100223287378, 85.840860336314364, 6.683258861509244,
	};
	const double q[5 + 1] = {
		12.262350348616792, 20.557285797683576, 12.161463238367844,
		4.711532733641639, 0.899112889751053, 0.035662329617191,
	};

	const double x01 = (v - min_value) / (max_value - min_value);
	const double xc = 2.0 * x01 - 1.0;

	const double yp = EvaluatePolynomial(xc, p, 6);
	const double yq = EvaluatePolynomial(xc, q, 6);
	if (yq == 0.0) return 0.0;
	return (float)(yp / yq);
}

void RgbToXyb(double r, double g, double b, double *valx, double *valy, double *valz)
{
	const double a0 = 1.01611726948;
	const double a1 = 0.982482243696;
	const double a2 = 1.43571362627;
	const double a3 = 0.896039849412;
	*valx = a0 * r - a1 * g;
	*valy = a2 * r + a3 * g;
	*valz = b;
}

__kernel void OpsinDynamicsImage(__global float *r, __global float *g, __global float *b, __global float *r_blurred, __global float *g_blurred, __global float *b_blurred, int size)
{
	const int i = get_global_id(0);
	double pre[3] = { r_blurred[i], g_blurred[i],  b_blurred[i] };
	double pre_mixed[3];
	OpsinAbsorbance(pre, pre_mixed);
	double sensitivity[3];
	sensitivity[0] = Gamma(pre_mixed[0]) / pre_mixed[0];
	sensitivity[1] = Gamma(pre_mixed[1]) / pre_mixed[1];
	sensitivity[2] = Gamma(pre_mixed[2]) / pre_mixed[2];

	double cur_rgb[3] = { r_blurred[i], g_blurred[i],  b_blurred[i] };
	double cur_mixed[3];
    OpsinAbsorbance(cur_rgb, cur_mixed);
    cur_mixed[0] *= sensitivity[0];
    cur_mixed[1] *= sensitivity[1];
    cur_mixed[2] *= sensitivity[2];
    double x, y, z;
	RgbToXyb(cur_mixed[0], cur_mixed[1], cur_mixed[2], &x, &y, &z);
    r[i] = x;
    g[i] = y;
    b[i] = z;
}

__kernel void MinSquareVal(__global float* pA, __global float* pC, int square_size, int offset)
{
	const int x = get_global_id(0);
	const int y = get_global_id(1);
	const int width = get_global_size(0);
	const int height = get_global_size(1);

	int minH = offset > y ? 0 : y - offset;
	int maxH = min(y + square_size - offset, height);

	int minW = offset > x ? 0 : x - offset;
	int maxW = min(x + square_size - offset, width);

	float minValue = pA[minH * width + minW];

	for (int j = minH; j < maxH; j++)
	{
		for (int i = minW; i < maxW; i++)
		{
			float tmp = pA[j * width + i];
			if (tmp < minValue) minValue = tmp;
		}
	}

	pC[y * width + x] = minValue;
}

float calcWeight(__global float* multipliers, int len)
{
	float weight_no_border = 0;
	for (int j = 0; j < len; j++)
	{
		weight_no_border += multipliers[j];
	}
	return weight_no_border;
}

__kernel void Convolution(__global float* multipliers, __global float* inp, __global float* result,
							int xsize, int xstep, int len, int offset, float border_ratio)
{
	const int ox = get_global_id(0);
	const int y = get_global_id(1);

	const int oxsize = get_global_size(0);
	const int ysize = get_global_size(1);

	const int x = ox * xstep;
/*
	float weight_no_border = 0;
	for (int j = 0; j <= 2 * offset; j++)
	{
		weight_no_border += multipliers[j];
	}
*/
	float weight_no_border = calcWeight(multipliers, len);

	int minx = x < offset ? 0 : x - offset;
	int maxx = min(xsize, x + len - offset);

	float weight = 0.0;
	for (int j = minx; j < maxx; j++)
	{
		weight += multipliers[j - x + offset];
	}

	weight = (1.0 - border_ratio) * weight + border_ratio * weight_no_border;
	float scale = 1.0 / weight;

	float sum = 0.0;
	for (int j = minx; j < maxx; j++)
	{
		sum += inp[y * xsize + j] * multipliers[j - x + offset];
	}

	result[ox * ysize + y] = sum * scale;
}
/*
__kernel void ConvolutionX(__global float* multipliers, __global float* inp, __global float* result,
	int len, int offset, float border_ratio)
{
	const int x = get_global_id(0);
	const int y = get_global_id(1);

	const int xsize = get_global_size(0);
	const int ysize = get_global_size(1);

	float weight_no_border = 0;
	for (int j = 0; j <= 2 * offset; j++)
	{
		weight_no_border += multipliers[j];
	}

	int minx = x < offset ? 0 : x - offset;
	int maxx = min(xsize, x + len - offset);

	int miny = y < offset ? 0 : y - offset;
	int maxy = min(ysize, y + len - offset);

	float weightX = 0.0;
	for (int j = minx; j < maxx; j++)
	{
		weightX += multipliers[j - x + offset];
	}

	weightX = (1.0 - border_ratio) * weightX + border_ratio * weight_no_border;

	float weightY = 0.0;
	for (int j = miny; j < maxy; j++)
	{
		weightY += multipliers[j - y + offset];
	}

	weightY = (1.0 - border_ratio) * weightY + border_ratio * weight_no_border;

	float sum = 0.0;
	for (int j = miny; j < maxy; j++)
	{
		float sumx = 0.0;
		for (int i = minx; i < maxx; i++)
		{
			sumx += inp[j * xsize + i] * multipliers[i - x + offset];
		}

		sum += sumx * multipliers[j - y + offset];
	}

	result[y * xsize + x] = sum / weightY / weightX;
}
*/

__kernel void ConvolutionX(__global float* multipliers, __global float* inp, __global float* result,
	int len, int offset, float border_ratio)
{
	const int x = get_global_id(0);
	const int y = get_global_id(1);

	const int xsize = get_global_size(0);
	const int ysize = get_global_size(1);

	float weight_no_border = 0;
	for (int j = 0; j <= 2 * offset; j++)
	{
		weight_no_border += multipliers[j];
	}

	int minx = x < offset ? 0 : x - offset;
	int maxx = min(xsize, x + len - offset);

	float weight = 0.0;
	for (int j = minx; j < maxx; j++)
	{
		weight += multipliers[j - x + offset];
	}

	weight = (1.0 - border_ratio) * weight + border_ratio * weight_no_border;
	float scale = 1.0 / weight;

	float sum = 0.0;
	for (int j = minx; j < maxx; j++)
	{
		sum += inp[y * xsize + j] * multipliers[j - x + offset];
	}

	result[x * ysize + y] = sum * scale;
}

__kernel void ConvolutionY(__global float* multipliers, __global float* inp, __global float* result,
	int len, int offset, float border_ratio)
{
	const int x = get_global_id(0);
	const int y = get_global_id(1);

	const int xsize = get_global_size(0);
	const int ysize = get_global_size(1);

	float weight_no_border = 0;
	for (int j = 0; j <= 2 * offset; j++)
	{
		weight_no_border += multipliers[j];
	}

	int miny = y < offset ? 0 : y - offset;
	int maxy = min(ysize, y + len - offset);

	float weight = 0.0;
	for (int j = miny; j < maxy; j++)
	{
		weight += multipliers[j - y + offset];
	}

	weight = (1.0 - border_ratio) * weight + border_ratio * weight_no_border;
	float scale = 1.0 / weight;

	float sum = 0.0;
	for (int j = miny; j < maxy; j++)
	{
		sum += inp[j * xsize + x] * multipliers[j - y + offset];
	}

	result[y * xsize + x] = sum * scale;
}

__kernel void DownSample(__global float* pA, __global float* pC, int square)
{
	const int x = get_global_id(0);
	const int y = get_global_id(1);

	const int xsize = get_global_size(0);
	const int ysize = get_global_size(1);

	const int oxsize = xsize / square;

	const int sample_x = x / square;
	const int sample_y = y / square;

	pC[y * xsize + x] = pA[sample_y * oxsize + sample_x];
}

void OpsinAbsorbance(const double in[3], double out[3])
{
	const float mix[12] = {
	0.348036746003,
	0.577814843137,
	0.0544556093735,
	0.774145581713,
	0.26922717275,
	0.767247733938,
	0.0366922708552,
	0.920130265014,
	0.0882062883536,
	0.158581714673,
	0.712857943858,
	10.6524069248,
	};

	out[0] = mix[0] * in[0] + mix[1] * in[1] + mix[2] * in[2] + mix[3];
	out[1] = mix[4] * in[0] + mix[5] * in[1] + mix[6] * in[2] + mix[7];
	out[2] = mix[8] * in[0] + mix[9] * in[1] + mix[10] * in[2] + mix[11];
}

double EvaluatePolynomial(const double x, const double *coefficients, int n)
{
	double b1 = 0.0;
	double b2 = 0.0;

	for (int i = n - 1; i >= 0; i--)
	{
		if (i == 0) {
			const double x_b1 = x * b1;
			b1 = x_b1 - b2 + coefficients[0];
			break;
		}
		const double x_b1 = x * b1;
		const double t = (x_b1 + x_b1) - b2 + coefficients[i];
		b2 = b1;
		b1 = t;
	}

	return b1;
}

float Gamma(double v)
{
	double min_value = 0.770000000000000;
	double max_value = 274.579999999999984;

	/*static*/ const double p[5 + 1] = {
		881.979476556478289, 1496.058452015812463, 908.662212739659481,
		373.566100223287378, 85.840860336314364, 6.683258861509244,
	};
	/*static*/ const double q[5 + 1] = {
		12.262350348616792, 20.557285797683576, 12.161463238367844,
		4.711532733641639, 0.899112889751053, 0.035662329617191,
	};

	const double x01 = (v - min_value) / (max_value - min_value);
	const double xc = 2.0 * x01 - 1.0;

	const double yp = EvaluatePolynomial(xc, p, 6);
	const double yq = EvaluatePolynomial(xc, q, 6);
	if (yq == 0.0) return 0.0;
	return (float)(yp / yq);
}

void RgbToXyb(double r, double g, double b, double *valx, double *valy, double *valz)
{
	/*static*/ const double a0 = 1.01611726948;
	/*static*/ const double a1 = 0.982482243696;
	/*static*/ const double a2 = 1.43571362627;
	/*static*/ const double a3 = 0.896039849412;
	*valx = a0 * r - a1 * g;
	*valy = a2 * r + a3 * g;
	*valz = b;
}

__kernel void OpsinDynamicsImage(__global float *r, __global float *g, __global float *b, __global float *r_blurred, __global float *g_blurred, __global float *b_blurred, int size)
{
	const int i = get_global_id(0);
	double pre[3] = { r_blurred[i], g_blurred[i],  b_blurred[i] };
	double pre_mixed[3];
	OpsinAbsorbance(pre, pre_mixed);
	double sensitivity[3];
	sensitivity[0] = Gamma(pre_mixed[0]) / pre_mixed[0];
	sensitivity[1] = Gamma(pre_mixed[1]) / pre_mixed[1];
	sensitivity[2] = Gamma(pre_mixed[2]) / pre_mixed[2];

	double cur_rgb[3] = { r_blurred[i], g_blurred[i],  b_blurred[i] };
	double cur_mixed[3];
    OpsinAbsorbance(cur_rgb, cur_mixed);
    cur_mixed[0] *= sensitivity[0];
    cur_mixed[1] *= sensitivity[1];
    cur_mixed[2] *= sensitivity[2];
    double x, y, z;
	RgbToXyb(cur_mixed[0], cur_mixed[1], cur_mixed[2], &x, &y, &z);
    r[i] = x;
    g[i] = y;
    b[i] = z;
}


Remove biaspathocl_kernels_mega.cl (not used anymore)

#line 2 "biaspatchocl_kernels_mega.cl"

/***************************************************************************
 * Copyright 1998-2015 by authors (see AUTHORS.txt)                        *
 *                                                                         *
 *   This file is part of LuxRender.                                       *
 *                                                                         *
 * 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.                                          *
 ***************************************************************************/

// List of symbols defined at compile time:
//  PARAM_TASK_COUNT
//  PARAM_TILE_WIDTH
//  PARAM_TILE_HEIGHT
//  PARAM_FIRST_VERTEX_DL_COUNT
//  PARAM_RADIANCE_CLAMP_MAXVALUE
//  PARAM_PDF_CLAMP_VALUE
//  PARAM_AA_SAMPLES
//  PARAM_DIRECT_LIGHT_SAMPLES
//  PARAM_DIFFUSE_SAMPLES
//  PARAM_GLOSSY_SAMPLES
//  PARAM_SPECULAR_SAMPLES
//  PARAM_DEPTH_MAX
//  PARAM_DEPTH_DIFFUSE_MAX
//  PARAM_DEPTH_GLOSSY_MAX
//  PARAM_DEPTH_SPECULAR_MAX
//  PARAM_IMAGE_FILTER_WIDTH
//  PARAM_LOW_LIGHT_THREASHOLD
//  PARAM_NEAR_START_LIGHT

//------------------------------------------------------------------------------
// RenderSample (Mega-Kernel)
//------------------------------------------------------------------------------

__kernel __attribute__((work_group_size_hint(64, 1, 1))) void RenderSample(
		const uint tileStartX,
		const uint tileStartY,
		KERNEL_ARGS
		) {
	const size_t gid = get_global_id(0);

	const uint sampleIndex = gid % (PARAM_AA_SAMPLES * PARAM_AA_SAMPLES);
	const uint samplePixelIndex = gid / (PARAM_AA_SAMPLES * PARAM_AA_SAMPLES);
	const uint samplePixelX = samplePixelIndex % PARAM_TILE_WIDTH;
	const uint samplePixelY = samplePixelIndex / PARAM_TILE_WIDTH;

	if ((gid >= PARAM_TILE_WIDTH * PARAM_TILE_HEIGHT * PARAM_AA_SAMPLES * PARAM_AA_SAMPLES) ||
			(tileStartX + samplePixelX >= engineFilmWidth) ||
			(tileStartY + samplePixelY >= engineFilmHeight))
		return;

	__global GPUTask *task = &tasks[gid];
	__global GPUTaskDirectLight *taskDirectLight = &tasksDirectLight[gid];
	__global GPUTaskPathVertexN *taskPathVertexN = &tasksPathVertexN[gid];

	//--------------------------------------------------------------------------
	// Initialize image maps page pointer table
	//--------------------------------------------------------------------------

	INIT_IMAGEMAPS_PAGES

	//--------------------------------------------------------------------------
	// Initialize the eye ray
	//--------------------------------------------------------------------------

	// Read the seed
	Seed seed;
	seed.s1 = task->seed.s1;
	seed.s2 = task->seed.s2;
	seed.s3 = task->seed.s3;

#if defined(PARAM_HAS_VOLUMES)
	// Initialize the volume information
	PathVolumeInfo_Init(&task->volInfoPathVertex1);
#endif

	uint tracedRaysCount = 0;
	float3 throughputPathVertex1 = WHITE;

	__global SampleResult *sampleResult = &taskResults[gid];
	SampleResult_Init(sampleResult);
#if defined(PARAM_FILM_CHANNELS_HAS_DIRECT_SHADOW_MASK)
	sampleResult->directShadowMask = 1.f;
#endif
#if defined(PARAM_FILM_CHANNELS_HAS_INDIRECT_SHADOW_MASK)
	sampleResult->indirectShadowMask = 1.f;
#endif

	Ray ray;
	RayHit rayHit;
	GenerateCameraRay(&seed, task, sampleResult,
			camera, pixelFilterDistribution,
			samplePixelX, samplePixelY, sampleIndex,
			tileStartX, tileStartY,
			engineFilmWidth, engineFilmHeight, &ray);
	const float time = ray.time;

	//--------------------------------------------------------------------------
	// Trace the eye ray
	//--------------------------------------------------------------------------

	float3 connectionThroughput;
	tracedRaysCount += BIASPATHOCL_Scene_Intersect(
#if defined(PARAM_HAS_VOLUMES)
		&task->volInfoPathVertex1,
		&task->tmpHitPoint,
#endif
#if defined(PARAM_HAS_PASSTHROUGH)
		Rnd_FloatValue(&seed),
#endif
		&ray, &rayHit,
		&task->bsdfPathVertex1,
		&connectionThroughput, throughputPathVertex1,
		sampleResult,
		// BSDF_Init parameters
		meshDescs,
		meshMats,
#if (PARAM_TRIANGLE_LIGHT_COUNT > 0)
		meshTriLightDefsOffset,
#endif
		vertices,
#if defined(PARAM_HAS_NORMALS_BUFFER)
		vertNormals,
#endif
#if defined(PARAM_HAS_UVS_BUFFER)
		vertUVs,
#endif
#if defined(PARAM_HAS_COLS_BUFFER)
		vertCols,
#endif
#if defined(PARAM_HAS_ALPHAS_BUFFER)
		vertAlphas,
#endif
		triangles
		MATERIALS_PARAM
		// Accelerator_Intersect parameters
		ACCELERATOR_INTERSECT_PARAM);
	throughputPathVertex1 *= connectionThroughput;

	//--------------------------------------------------------------------------
	// Render the eye ray hit point
	//--------------------------------------------------------------------------

	if (rayHit.meshIndex != NULL_INDEX) {
		//----------------------------------------------------------------------
		// Something was hit
		//----------------------------------------------------------------------

		// Save the path first vertex information
#if defined(PARAM_FILM_CHANNELS_HAS_ALPHA)
		sampleResult->alpha = 1.f;
#endif
#if defined(PARAM_FILM_CHANNELS_HAS_DEPTH)
		sampleResult->depth = rayHit.t;
#endif
#if defined(PARAM_FILM_CHANNELS_HAS_POSITION)
		sampleResult->position = task->bsdfPathVertex1.hitPoint.p;
#endif
#if defined(PARAM_FILM_CHANNELS_HAS_GEOMETRY_NORMAL)
		sampleResult->geometryNormal = task->bsdfPathVertex1.hitPoint.geometryN;
#endif
#if defined(PARAM_FILM_CHANNELS_HAS_SHADING_NORMAL)
		sampleResult->shadingNormal = task->bsdfPathVertex1.hitPoint.shadeN;
#endif
#if defined(PARAM_FILM_CHANNELS_HAS_MATERIAL_ID)
		sampleResult->materialID = BSDF_GetMaterialID(&task->bsdfPathVertex1
			MATERIALS_PARAM);
#endif
#if defined(PARAM_FILM_CHANNELS_HAS_UV)
		sampleResult->uv = task->bsdfPathVertex1.hitPoint.uv;
#endif

#if (PARAM_TRIANGLE_LIGHT_COUNT > 0)
		// Check if it is a light source (note: I can hit only triangle area light sources)
		if (BSDF_IsLightSource(&task->bsdfPathVertex1) && (rayHit.t > PARAM_NEAR_START_LIGHT)) {
			// SPECULAR is required to avoid MIS
			DirectHitFiniteLight(SPECULAR,
					throughputPathVertex1,
					rayHit.t, &task->bsdfPathVertex1, 1.f,
					sampleResult
					LIGHTS_PARAM);
		}
#endif

		//----------------------------------------------------------------------
		// First path vertex direct light sampling
		//----------------------------------------------------------------------

		const BSDFEvent materialEventTypes = BSDF_GetEventTypes(&task->bsdfPathVertex1
			MATERIALS_PARAM);
		sampleResult->lastPathVertex =
				(PARAM_DEPTH_MAX <= 1) ||
				(!((PARAM_DEPTH_DIFFUSE_MAX > 0) && (materialEventTypes & DIFFUSE)) &&
				!((PARAM_DEPTH_GLOSSY_MAX > 0) && (materialEventTypes & GLOSSY)) &&
				!((PARAM_DEPTH_SPECULAR_MAX > 0) && (materialEventTypes & SPECULAR)));

		// Only if it is not a SPECULAR BSDF
		if (!BSDF_IsDelta(&task->bsdfPathVertex1
				MATERIALS_PARAM)) {
#if defined(PARAM_HAS_VOLUMES)
			// I need to work on a copy of volume information of the first path vertex
			taskDirectLight->directLightVolInfo = task->volInfoPathVertex1;
#endif

			tracedRaysCount +=
				DirectLightSampling_ALL(
				&seed,
#if defined(PARAM_HAS_VOLUMES)
				&taskDirectLight->directLightVolInfo,
#endif
#if (PARAM_TRIANGLE_LIGHT_COUNT > 0) || defined(PARAM_HAS_VOLUMES)
				&task->tmpHitPoint,
#endif
#if defined(PARAM_HAS_INFINITELIGHTS)
				worldCenterX, worldCenterY, worldCenterZ, worldRadius,
#endif
				time,
				throughputPathVertex1,
				&task->bsdfPathVertex1, &taskDirectLight->directLightBSDF,
				sampleResult,
				// BSDF_Init parameters
				meshDescs,
				meshMats,
				vertices,
#if defined(PARAM_HAS_NORMALS_BUFFER)
				vertNormals,
#endif
#if defined(PARAM_HAS_UVS_BUFFER)
				vertUVs,
#endif
#if defined(PARAM_HAS_COLS_BUFFER)
				vertCols,
#endif
#if defined(PARAM_HAS_ALPHAS_BUFFER)
				vertAlphas,
#endif
				triangles
				// Accelerator_Intersect parameters
				ACCELERATOR_INTERSECT_PARAM
				// Light related parameters
				LIGHTS_PARAM);
		}

		//----------------------------------------------------------------------
		// Sample the components of the BSDF of the first path vertex
		//----------------------------------------------------------------------

#if defined(PARAM_FILM_CHANNELS_HAS_INDIRECT_SHADOW_MASK)
		sampleResult->indirectShadowMask = 0.f;
#endif

		if (!sampleResult->lastPathVertex) {
			sampleResult->firstPathVertex = false;

			BSDFEvent vertex1SampleComponent = DIFFUSE;
			do {
				if (PathDepthInfo_CheckComponentDepths(vertex1SampleComponent) && (materialEventTypes & vertex1SampleComponent)) {
					const int matSamplesCount = mats[task->bsdfPathVertex1.materialIndex].samples;
					const uint globalMatSamplesCount = ((vertex1SampleComponent == DIFFUSE) ? PARAM_DIFFUSE_SAMPLES :
						((vertex1SampleComponent == GLOSSY) ? PARAM_GLOSSY_SAMPLES :
							PARAM_SPECULAR_SAMPLES));
					const uint sampleCount = (matSamplesCount < 0) ? globalMatSamplesCount : (uint)matSamplesCount;

					tracedRaysCount += SampleComponent(
							&seed,
#if defined(PARAM_HAS_VOLUMES)
							&task->volInfoPathVertex1,
							&taskPathVertexN->volInfoPathVertexN,
							&taskDirectLight->directLightVolInfo,
#endif
#if (PARAM_TRIANGLE_LIGHT_COUNT > 0) || defined(PARAM_HAS_VOLUMES)
							&task->tmpHitPoint,
#endif
#if defined(PARAM_HAS_INFINITELIGHTS)
							worldCenterX, worldCenterY, worldCenterZ, worldRadius,
#endif
							time,
							vertex1SampleComponent,
							sampleCount, throughputPathVertex1,
							&task->bsdfPathVertex1, &taskPathVertexN->bsdfPathVertexN,
							&taskDirectLight->directLightBSDF,
							sampleResult,
							// BSDF_Init parameters
							meshDescs,
							meshMats,
							vertices,
#if defined(PARAM_HAS_NORMALS_BUFFER)
							vertNormals,
#endif
#if defined(PARAM_HAS_UVS_BUFFER)
							vertUVs,
#endif
#if defined(PARAM_HAS_COLS_BUFFER)
							vertCols,
#endif
#if defined(PARAM_HAS_ALPHAS_BUFFER)
							vertAlphas,
#endif
							triangles
							// Accelerator_Intersect parameters
							ACCELERATOR_INTERSECT_PARAM
							// Light related parameters
							LIGHTS_PARAM);
				}

				// Move to the next BSDF component
				vertex1SampleComponent = (vertex1SampleComponent == DIFFUSE) ? GLOSSY :
					((vertex1SampleComponent == GLOSSY) ? SPECULAR : NONE);
			} while (vertex1SampleComponent != NONE);
		}
	} else {
		//----------------------------------------------------------------------
		// Nothing was hit
		//----------------------------------------------------------------------

#if defined(PARAM_HAS_ENVLIGHTS)
		// Add environmental lights radiance
		const float3 rayDir = (float3)(ray.d.x, ray.d.y, ray.d.z);
		// SPECULAR is required to avoid MIS
		DirectHitInfiniteLight(
				SPECULAR,
				throughputPathVertex1,
				-rayDir, 1.f,
				sampleResult
				LIGHTS_PARAM);
#endif

#if defined(PARAM_FILM_CHANNELS_HAS_ALPHA)
		sampleResult->alpha = 0.f;
#endif
#if defined(PARAM_FILM_CHANNELS_HAS_DEPTH)
		sampleResult->depth = INFINITY;
#endif
#if defined(PARAM_FILM_CHANNELS_HAS_POSITION)
		sampleResult->position.x = INFINITY;
		sampleResult->position.y = INFINITY;
		sampleResult->position.z = INFINITY;
#endif
#if defined(PARAM_FILM_CHANNELS_HAS_GEOMETRY_NORMAL)
		sampleResult->geometryNormal.x = INFINITY;
		sampleResult->geometryNormal.y = INFINITY;
		sampleResult->geometryNormal.z = INFINITY;
#endif
#if defined(PARAM_FILM_CHANNELS_HAS_SHADING_NORMAL)
		sampleResult->shadingNormal.x = INFINITY;
		sampleResult->shadingNormal.y = INFINITY;
		sampleResult->shadingNormal.z = INFINITY;
#endif
#if defined(PARAM_FILM_CHANNELS_HAS_MATERIAL_ID)
		sampleResult->materialID = NULL_INDEX;
#endif
#if defined(PARAM_FILM_CHANNELS_HAS_UV)
		sampleResult->uv.u = INFINITY;
		sampleResult->uv.v = INFINITY;
#endif
	}

	//--------------------------------------------------------------------------
	// Save the result
	//--------------------------------------------------------------------------

#if defined(PARAM_FILM_CHANNELS_HAS_RAYCOUNT)
	sampleResult->rayCount = tracedRaysCount;
#endif

	taskStats[gid].raysCount += tracedRaysCount;

	// Save the seed
	task->seed.s1 = seed.s1;
	task->seed.s2 = seed.s2;
	task->seed.s3 = seed.s3;
}


Fix up local memory-based filter for integer-based loads/stores.


Implement the checker with antialias

Implement the checker with antialias

It also handles the rop by itself. We need to create
a generic library with all the helper methods this
kernel has.

ENESIM_OPENCL_KERNEL(

typedef enum _Enesim_Rop
{
	ENESIM_ROP_BLEND,
	ENESIM_ROP_FILL,
} Enesim_Rop;

float point_transform(float2 xy, float3 mc)
{
	return (xy.x * mc.x) + (xy.y * mc.y) + mc.z;
}

float3 projective_setup(float2 xy, float2 oxy, float3 mx, float3 my, float3 mz)
{
	float3 xyz;
	xyz.x = point_transform(xy, mx) - oxy.x;
	xyz.y = point_transform(xy, my) - oxy.y;
	xyz.z = point_transform(xy, mz);

	return xyz;
}

uchar4 color_mul_256(ushort a, uchar4 c)
{
	uchar4 ret;
	ushort4 cs = upsample((uchar4)(0, 0, 0, 0), c);

	ret = convert_uchar4((a * cs) >> 8);
	//printf("color_mul_256: a = %#hx, c = %#v4hhx, ret = %#v4hhx\n", a, c, ret);
	return ret;
}

uchar4 color_interp_256(ushort a, uchar4 c0, uchar4 c1)
{
	uchar4 ret;
	ushort4 c0s = upsample((uchar4)(0, 0, 0, 0), c0);
	ushort4 c1s = upsample((uchar4)(0, 0, 0, 0), c1);

	ret = convert_uchar4((((c0s - c1s) * a) >> 8) + c1s);
	//printf("color_interp256: a = %#hx, c0 = %#v4hhx, c1 = %#v4hhx, ret = %#v4hhx\n", a, c0, c1, ret);
	return ret;
}

uchar4 color_blend(uchar4 d, ushort a, uchar4 s)
{
	uchar4 ret;

	ret = s + color_mul_256(a, d);
	//printf("color_blend: d = %#v4hhx, a = %#hx, s = %#v4hhx, ret = %#v4hhx\n", d, a, s, ret);
	return ret;
}

uchar4 pt_blend(uchar4 d, uchar4 s)
{
	ushort a;
	uchar4 ret;

	a = 256 - s.w;
	ret = color_blend(d, a, s);
	//printf("pt_blend: d = %#v4hhx, s = %#v44hx, a = %#hx, ret = %#v4hhx\n", d, s, a, ret);
	return ret;
}

__kernel void checker(read_write image2d_t out, int rop,
		__constant float *matrix, float2 oxy, uchar4 even_color,
		uchar4 odd_color, uint w, uint h)
{
	int x = get_global_id(0);
	int y = get_global_id(1);
	float fx;
	float fy;
	float ix;
	float iy;
	float xx;
	float yy;
	uchar4 color0 = even_color;
	uchar4 color1 = odd_color;
	uchar4 p0;
	uint h2 = h * 2;
	uint w2 = w * 2;
	uchar4 d0;

	float3 xyz = projective_setup((float2)(x, y), oxy,
			(float3)(matrix[0], matrix[1], matrix[2]),
			(float3)(matrix[3], matrix[4], matrix[5]),
			(float3)(matrix[6], matrix[7], matrix[8]));
	/* project */
	xx = xyz.x / xyz.z;
	yy = xyz.y / xyz.z;
	/* normalize the coordinate */
	yy = fmod(yy, h2);
	if (yy < 0)
		yy += h2;
	xx = fmod(xx, w2);
	if (xx < 0)
		xx += w2;
	/* choose the correct color */
	if (yy >= h)
	{
		color0 = odd_color;
		color1 = even_color;
	}
	if (xx >= w)
	{
		uchar4 tmp;

		tmp = color0;
		color0 = color1;
		color1 = tmp;
	}

	p0 = color0;

	fx = fract(xx, &ix);
	fy = fract(yy, &iy);
	if (iy == 0 || iy == h)
	{
		ushort a = 1 + convert_ushort(255 * fy);
		p0 = color_interp_256(a, color0, color1);
		if (ix == 0 || ix == w)
		{
			ushort a = 1 + convert_ushort(255 * fx);
			p0 = color_interp_256(a, color1, color0);
		}
	}
	else if (ix == 0 || ix == w)
	{
		ushort a = 1 + convert_ushort(255 * fx);
		p0 = color_interp_256(a, color0, color1);
	}

	if (rop == ENESIM_ROP_BLEND)
	{
		d0 = convert_uchar4(read_imageui(out, (int2)(x, y)));
		p0 = pt_blend(d0, p0);
	}
	write_imageui(out, (int2)(x, y), (uint4)(p0.x, p0.y, p0.z, p0.w));
}
);

ENESIM_OPENCL_KERNEL(

float point_transform(float2 xy, float3 mc)
{
	return (xy.x * mc.x) + (xy.y * mc.y) + mc.z;
}

float3 projective_setup(float2 xy, float2 oxy, float3 mx, float3 my, float3 mz)
{
	float3 xyz;
	xyz.x = point_transform(xy, mx) - oxy.x;
	xyz.y = point_transform(xy, my) - oxy.y;
	xyz.z = point_transform(xy, mz);

	return xyz;
}

__kernel void checker(write_only image2d_t out, __constant float *matrix,
		float2 oxy, uchar4 even_color, uchar4 odd_color, uint w,
		uint h)
{
	int x = get_global_id(0);
	int y = get_global_id(1);
	uchar4 color0 = even_color;
	uchar4 color1 = odd_color;
	uchar4 p0;
	uint h2 = h * 2;
	uint w2 = w * 2;

	float3 xyz = projective_setup((float2)(x, y), oxy,
			(float3)(matrix[0], matrix[1], matrix[2]),
			(float3)(matrix[3], matrix[4], matrix[5]),
			(float3)(matrix[6], matrix[7], matrix[8]));
	/* project */
	float xx = xyz.x / xyz.z;
	float yy = xyz.y / xyz.z;
	/* normalize the coordinate */
	yy = fmod(yy, h2);
	if (yy < 0)
		yy += h2;
	xx = fmod(xx, w2);
	if (xx < 0)
		xx += w2;
	/* choose the correct color */
	if (yy >= h)
	{
		color0 = odd_color;
		color1 = even_color;
	}
	if (xx >= w)
	{
		p0 = color0;
	}
	else
	{
		p0 = color1;
	}
	write_imageui(out, (int2)(x, y), (uint4)(p0.x, p0.y, p0.z, p0.w));
}
);


Try to fix some strange NVIDIA bug with surface extraction and new driver