/*
 * Copyright (C) 2014 Intel Corporation
 *
 * 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 (including the next
 * paragraph) 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.
 */

/** @file image.h
 *
 * Common image format, target and shader stage metadata.
 */

#ifndef __PIGLIT_ARB_SHADER_IMAGE_LOAD_STORE_IMAGE_H__
#define __PIGLIT_ARB_SHADER_IMAGE_LOAD_STORE_IMAGE_H__

#include "piglit-util-gl.h"

/**
 * Image color value.
 */
struct image_datum {
        double x;
        double y;
        double z;
        double w;
};

/**
 * Size of an image.
 *
 * Note that most tests treat images as a 4-dimensional array of
 * pixels with no specific semantics attached to each dimension
 * (e.g. the x dimension will be the number of samples for multisample
 * images but the horizontal coordinate for 2D textures).  This is a
 * deliberate decision that greatly reduces the amount of duplication,
 * as in many cases you can just run the same test in a loop for all
 * image targets.
 *
 * Unused dimensions equal 1 by convention.
 */
struct image_extent {
        unsigned x;
        unsigned y;
        unsigned z;
        unsigned w;
};

#define get_idx(v, i)                          \
        ((i) == 0 ? (v).x :                    \
         (i) == 1 ? (v).y :                    \
         (i) == 2 ? (v).z :                    \
         (i) == 3 ? (v).w : 0)

#define set_idx(v, i, a)                       \
        (*((i) == 0 ? &(v).x :                 \
           (i) == 1 ? &(v).y :                 \
           (i) == 2 ? &(v).z :                 \
           (i) == 3 ? &(v).w : NULL)) = (a)

#define product(v) ((v).x * (v).y * (v).z * (v).w)

/**
 * Get a two-dimensional image_extent with the same number of elements
 * as the argument, where each dimension is reasonably close to the
 * square root of the total number of elements, e.g. for use as grid
 * invocation size.
 */
struct image_extent
image_optimal_extent(struct image_extent ext);

struct image_format_info {
        /** Format name as specified by GLSL. */
        const char *name;

        /** Format enum. */
        GLenum format;

        /** Pixel transfer format (e.g. as specified for glGetTexImage()). */
        GLenum pixel_format;

        /** Pixel transfer type (e.g. as specified for glGetTexImage()). */
        GLenum pixel_type;

        /** Number of storage bits for each component. */
        unsigned bits[4];
};

/**
 * Image formats supported by image load and store built-ins.
 */
extern const struct image_format_info image_formats_load_store[];

/**
 * Image formats supported by image atomic built-ins.
 */
extern const struct image_format_info image_formats_atomic[];

/**
 * Get information for the specified image format.
 */
const struct image_format_info *
get_image_format(GLenum format);

/**
 * Get the logical base format as seen by the shader (either GL_RGBA
 * or GL_RGBA_INTEGER).
 */
GLenum
image_base_format(const struct image_format_info *format);

/**
 * Get the logical component type as seen by the shader.
 */
GLenum
image_base_type(const struct image_format_info *format);

/**
 * Get the logical internal format as seen by the shader.
 */
GLenum
image_base_internal_format(const struct image_format_info *format);

/**
 * Get the GLSL component data type for an image format.
 */
const char *
image_scalar_type_name(const struct image_format_info *format);

/**
 * Get the GLSL vector data type for an image format.
 */
const char *
image_vector_type_name(const struct image_format_info *format);

/**
 * Get the GLSL image type prefix for an image format ("i", "u" or
 * "").
 */
const char *
image_type_name(const struct image_format_info *format);

/**
 * Get the size in bits of one pixel in the specified format.
 */
unsigned int
image_pixel_size(const struct image_format_info *format);

/**
 * Get a compatible unsigned integer format of the same size.
 */
GLenum
image_compat_format(const struct image_format_info *format);

/**
 * Get the number of color components representable in an image format.
 */
unsigned
image_num_components(const struct image_format_info *format);

/**
 * Get an arbitrary per-component test scale used to make sure that we
 * exercise a significant portion of the representable range without
 * overflowing it.
 */
struct image_datum
image_format_scale(const struct image_format_info *format);

/**
 * Get the per-component error tolerance for an image format.
 */
struct image_datum
image_format_epsilon(const struct image_format_info *format);

/**
 * Convert \a x to the base data type of the specified image format.
 */
uint32_t
encode(const struct image_format_info *format, double x);

/**
 * Convert \a x from the base data type of the specified image format.
 */
double
decode(const struct image_format_info *format, uint32_t x);

struct image_target_info {
        /** Target name and GLSL image type suffix. */
        const char *name;

        /** Target enum. */
        GLenum target;

        /** Vector type used as address argument for this target. */
        const char *addr_type_name;
};

/**
 * Get all image targets supported by the implementation.
 */
const struct image_target_info *
image_targets(void);

/**
 * Get information for the specified target.
 */
const struct image_target_info *
get_image_target(GLenum t);

/**
 * Get the maximum supported dimensions for the specified target.
 */
struct image_extent
image_target_limits(const struct image_target_info *target);

/**
 * Get the maximum supported number of samples for the specified
 * target.
 */
unsigned
image_target_samples(const struct image_target_info *target);

/**
 * Get reasonable dimensions for an image of type \a target intended
 * to be in one-to-one mapping to a two-dimensional grid of dimensions
 * \a w and \a h.
 */
struct image_extent
image_extent_for_target(const struct image_target_info *target,
                        unsigned w, unsigned h);

/**
 * Get the target type for a single layer of the specified image
 * target.
 */
GLenum
image_layer_target(const struct image_target_info *target);

/**
 * Get the number of dimensions of an image target that are minified
 * in higher mipmap levels.
 */
unsigned
image_target_mipmapping_dimensions(const struct image_target_info *target);

struct image_stage_info {
        /** Shader stage name. */
        const char *name;

        /** Target enum. */
        GLenum stage;

        /** Value used in bit sets for this shader stage. */
        GLbitfield bit;
};

/**
 * Get all shader stages in pipeline order regardless of whether they
 * support image access.
 */
const struct image_stage_info *
known_image_stages(void);

/**
 * Get all shader stages that support image access in pipeline order.
 */
const struct image_stage_info *
image_stages(void);

/**
 * Get information for the specified stage, or NULL if the specified
 * stage doesn't support images.
 */
const struct image_stage_info *
get_image_stage(GLenum s);

/**
 * Get the maximum number of supported image uniforms from the
 * specified stage.
 */
unsigned
image_stage_max_images(const struct image_stage_info *stage);

/**
 * Get the maximum sum of image uniforms from all shaders.
 */
unsigned
max_combined_images(void);

/**
 * Get the maximum number of independent image units.
 */
unsigned
max_image_units(void);

struct image_info {
        /** Texture target of this image object. */
        const struct image_target_info *target;

        /** Format of this image object. */
        const struct image_format_info *format;

        /** Dimensions of this image object. */
        struct image_extent size;

        /** Error tolerance for this image object. */
        struct image_datum epsilon;
};

/**
 * Construct an image_info object.
 */
static inline struct image_info
image_info(GLenum target, GLenum format, unsigned w, unsigned h)
{
        const struct image_target_info *t = get_image_target(target);
        const struct image_format_info *f = get_image_format(format);
        const struct image_info img = {
                t, f,
                image_extent_for_target(t, w, h),
                image_format_epsilon(f)
        };

        return img;
}

/**
 * Set the dimensions of an image.
 */
static inline struct image_info
set_image_size(struct image_info img,
               unsigned x, unsigned y, unsigned z, unsigned w)
{
        const struct image_extent size = { x, y, z, w };
        img.size = size;
        return img;
}

/**
 * Get the number of layers of an image.
 */
unsigned
image_num_layers(const struct image_info img);

/**
 * Get the maximum number of mipmap levels for an image.
 */
unsigned
image_num_levels(const struct image_info img);

/**
 * Get the dimensions of the specified mipmap level of an image.
 */
struct image_extent
image_level_size(const struct image_info img, unsigned l);

/**
 * Get the offset in texels of the specified mipmap level of an
 * image.
 */
static inline unsigned
image_level_offset(const struct image_info img, unsigned l)
{
        return (l == 0 ? 0 :
                image_level_offset(img, l - 1) +
                product(image_level_size(img, l - 1)));
}

/**
 * Construct an image_info object for mipmap level \a l of the
 * specified base image.
 */
static inline struct image_info
image_info_for_level(struct image_info img, unsigned l)
{
        const struct image_info level_img = {
                img.target, img.format,
                image_level_size(img, l),
                img.epsilon
        };

        return level_img;
}

#endif
