/*
 * 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.c
 *
 * Common image format, target and shader stage metadata.
 */

#include "image.h"

struct image_extent
image_optimal_extent(struct image_extent ext)
{
        const unsigned n = product(ext);
        const unsigned w = 1 << MIN2(ffs(n) - 1, (int)log2(n) / 2);
        const struct image_extent opt = {
                w, n / w, 1, 1
        };

        return opt;
}

const struct image_format_info image_formats_load_store[] = {
        { "rgba32f", GL_RGBA32F, GL_RGBA, GL_FLOAT, { 32, 32, 32, 32 } },
        { "rgba16f", GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, { 16, 16, 16, 16 } },
        { "rg32f", GL_RG32F, GL_RG, GL_FLOAT, { 32, 32, 0, 0 } },
        { "rg16f", GL_RG16F, GL_RG, GL_HALF_FLOAT, { 16, 16, 0, 0 } },
        { "r11f_g11f_b10f", GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, { 11, 11, 10, 0 } },
        { "r32f", GL_R32F, GL_RED, GL_FLOAT, { 32, 0, 0, 0 } },
        { "r16f", GL_R16F, GL_RED, GL_HALF_FLOAT, { 16, 0, 0, 0 } },
        { "rgba32ui", GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, { 32, 32, 32, 32 } },
        { "rgba16ui", GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, { 16, 16, 16, 16 } },
        { "rgb10_a2ui", GL_RGB10_A2UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT_2_10_10_10_REV, { 10, 10, 10, 2 } },
        { "rgba8ui", GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, { 8, 8, 8, 8 } },
        { "rg32ui", GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, { 32, 32, 0, 0 } },
        { "rg16ui", GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, { 16, 16, 0, 0 } },
        { "rg8ui", GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE, { 8, 8, 0, 0 } },
        { "r32ui", GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, { 32, 0, 0, 0 } },
        { "r16ui", GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, { 16, 0, 0, 0 } },
        { "r8ui", GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, { 8, 0, 0, 0 } },
        { "rgba32i", GL_RGBA32I, GL_RGBA_INTEGER, GL_INT, { 32, 32, 32, 32 } },
        { "rgba16i", GL_RGBA16I, GL_RGBA_INTEGER, GL_SHORT, { 16, 16, 16, 16 } },
        { "rgba8i", GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, { 8, 8, 8, 8 } },
        { "rg32i", GL_RG32I, GL_RG_INTEGER, GL_INT, { 32, 32, 0, 0 } },
        { "rg16i", GL_RG16I, GL_RG_INTEGER, GL_SHORT, { 16, 16, 0, 0 } },
        { "rg8i", GL_RG8I, GL_RG_INTEGER, GL_BYTE, { 8, 8, 0, 0 } },
        { "r32i", GL_R32I, GL_RED_INTEGER, GL_INT, { 32, 0, 0, 0 } },
        { "r16i", GL_R16I, GL_RED_INTEGER, GL_SHORT, { 16, 0, 0, 0 } },
        { "r8i", GL_R8I, GL_RED_INTEGER, GL_BYTE, { 8, 0, 0, 0 } },
        { "rgba16", GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, { 16, 16, 16, 16 } },
        { "rgb10_a2", GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, { 10, 10, 10, 2 } },
        { "rgba8", GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, { 8, 8, 8, 8 } },
        { "rg16", GL_RG16, GL_RG, GL_UNSIGNED_SHORT, { 16, 16, 0, 0 } },
        { "rg8", GL_RG8, GL_RG, GL_UNSIGNED_BYTE, { 8, 8, 0, 0 } },
        { "r16", GL_R16, GL_RED, GL_UNSIGNED_SHORT, { 16, 0, 0, 0 } },
        { "r8", GL_R8, GL_RED, GL_UNSIGNED_BYTE, { 8, 0, 0, 0 } },
        { "rgba16_snorm", GL_RGBA16_SNORM, GL_RGBA, GL_SHORT, { 16, 16, 16, 16 } },
        { "rgba8_snorm", GL_RGBA8_SNORM, GL_RGBA, GL_BYTE, { 8, 8, 8, 8 } },
        { "rg16_snorm", GL_RG16_SNORM, GL_RG, GL_SHORT, { 16, 16, 0, 0 } },
        { "rg8_snorm", GL_RG8_SNORM, GL_RG, GL_BYTE, { 8, 8, 0, 0 } },
        { "r16_snorm", GL_R16_SNORM, GL_RED, GL_SHORT, { 16, 0, 0, 0 } },
        { "r8_snorm", GL_R8_SNORM, GL_RED, GL_BYTE, { 8, 0, 0, 0 } },
        { 0 }
};

const struct image_format_info image_formats_atomic[] = {
        { "r32ui", GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, { 32, 0, 0, 0 } },
        { "r32i", GL_R32I, GL_RED_INTEGER, GL_INT, { 32, 0, 0, 0 } },
        { 0 }
};

const struct image_format_info *
get_image_format(GLenum f)
{
        const struct image_format_info *format;

        for (format = image_formats_load_store; format->format; ++format) {
                if (format->format == f)
                        return format;
        }

        return NULL;
}

/**
 * Information specific to an image base data type as seen by the
 * shader.
 */
struct image_type_info {
        /** Logical base format as seen by the shader. */
        GLenum base_format;

        /** Logical component type as seen by the shader. */
        GLenum base_type;

        /** Logical internal format as seen by the shader. */
        GLenum base_internal_format;

        /** Matching GLSL component data type. */
        const char *scalar_type_name;

        /** Matching GLSL vector data type. */
        const char *vector_type_name;

        /** GLSL image type prefix ("i", "u" or ""). */
        const char *image_type_name;
};

static const struct image_type_info *
get_image_type(const struct image_format_info *format)
{
        switch (format->pixel_format) {
        case GL_RGBA:
        case GL_RGB:
        case GL_RG:
        case GL_RED: {
                static const struct image_type_info type = {
                        GL_RGBA, GL_FLOAT, GL_RGBA32F,
                        "float", "vec4", "image"
                };
                return &type;
        }
        case GL_RGBA_INTEGER:
        case GL_RG_INTEGER:
        case GL_RED_INTEGER:
                switch (format->pixel_type) {
                case GL_INT:
                case GL_SHORT:
                case GL_BYTE: {
                        static const struct image_type_info type = {
                                GL_RGBA_INTEGER, GL_INT, GL_RGBA32I,
                                "int", "ivec4", "iimage"
                        };
                        return &type;
                }
                case GL_UNSIGNED_INT:
                case GL_UNSIGNED_SHORT:
                case GL_UNSIGNED_INT_2_10_10_10_REV:
                case GL_UNSIGNED_BYTE: {
                        static const struct image_type_info type = {
                                GL_RGBA_INTEGER, GL_UNSIGNED_INT, GL_RGBA32UI,
                                "uint", "uvec4", "uimage"
                        };
                        return &type;
                }
                default:
                        abort();
                }
        default:
                abort();
        }
}

GLenum
image_base_format(const struct image_format_info *format)
{
        return get_image_type(format)->base_format;
}

GLenum
image_base_type(const struct image_format_info *format)
{
        return get_image_type(format)->base_type;
}

GLenum
image_base_internal_format(const struct image_format_info *format)
{
        return get_image_type(format)->base_internal_format;
}

const char *
image_scalar_type_name(const struct image_format_info *format)
{
        return get_image_type(format)->scalar_type_name;
}

const char *
image_vector_type_name(const struct image_format_info *format)
{
        return get_image_type(format)->vector_type_name;
}

const char *
image_type_name(const struct image_format_info *format)
{
        return get_image_type(format)->image_type_name;
}

unsigned int
image_pixel_size(const struct image_format_info *format)
{
        return format->bits[0] + format->bits[1] +
               format->bits[2] + format->bits[3];
}

GLenum
image_compat_format(const struct image_format_info *format)
{
        const unsigned bits = image_pixel_size(format);

        switch (bits) {
        case 128:
                return GL_RGBA32UI;

        case 64:
                return GL_RG32UI;

        case 32:
                return GL_R32UI;

        case 16:
                return GL_R16UI;

        case 8:
                return GL_R8UI;

        default:
                abort();
        }
}

unsigned
image_num_components(const struct image_format_info *format)
{
        return (!!format->bits[0] + !!format->bits[1] +
                !!format->bits[2] + !!format->bits[3]);
}

struct image_datum
image_format_scale(const struct image_format_info *format)
{
        struct image_datum v = { 0.0 };
        int i;

        for (i = 0; i < 4 && format->bits[i]; ++i) {
                switch (image_base_type(format)) {
                case GL_FLOAT:
                        set_idx(v, i, 1.0);
                        break;

                case GL_INT:
                        set_idx(v, i, 1u << (format->bits[i] - 2));
                        break;

                case GL_UNSIGNED_INT:
                        set_idx(v, i, 1u << (format->bits[i] - 1));
                        break;

                default:
                        abort();
                }
        }

        return v;
}

static unsigned
image_channel_fraction_bits(const struct image_format_info *format, unsigned i)
{
        if (image_base_type(format) == GL_FLOAT && format->bits[i]) {
                switch (format->pixel_type) {
                case GL_FLOAT:
                        return 23;

                case GL_HALF_FLOAT:
                        return 10;

                case GL_UNSIGNED_INT_10F_11F_11F_REV:
                        return format->bits[i] - 5;

                case GL_SHORT:
                case GL_BYTE:
                        return format->bits[i] - 1;

                case GL_UNSIGNED_SHORT:
                case GL_UNSIGNED_INT_2_10_10_10_REV:
                case GL_UNSIGNED_BYTE:
                        return format->bits[i];

                default:
                        abort();
                }
        } else {
                return 0;
        }
}

struct image_datum
image_format_epsilon(const struct image_format_info *format)
{
        struct image_datum v = { 0.0 };
        int i;

        for (i = 0; i < 4; ++i) {
                unsigned p = image_channel_fraction_bits(format, i);
                set_idx(v, i, (p ? MAX2(1.0 / ((1 << p) - 1), 1e-5) : 0));
        }

        return v;
}

uint32_t
encode(const struct image_format_info *format, double x)
{
        switch (image_base_type(format)) {
        case GL_UNSIGNED_INT:
                return x;

        case GL_INT:
                return (int32_t)x;

        case GL_FLOAT:
                return fui(x);

        default:
                abort();
        }
}

double
decode(const struct image_format_info *format, uint32_t x)
{
        switch (image_base_type(format)) {
        case GL_UNSIGNED_INT:
                return x;

        case GL_INT:
                return (int32_t)x;

        case GL_FLOAT:
                return uif(x);

        default:
                abort();
        }
}

const struct image_target_info *
image_targets(void)
{
        const struct image_target_info known[] = {
                { "1D", GL_TEXTURE_1D, "int" },
                { "2D", GL_TEXTURE_2D, "ivec2" },
                { "3D", GL_TEXTURE_3D, "ivec3" },
                { "2DRect", GL_TEXTURE_RECTANGLE, "ivec2" },
                { "Cube", GL_TEXTURE_CUBE_MAP, "ivec3" },
                { "Buffer", GL_TEXTURE_BUFFER, "int" },
                { "1DArray", GL_TEXTURE_1D_ARRAY, "ivec2" },
                { "2DArray", GL_TEXTURE_2D_ARRAY, "ivec3" },
                { "CubeArray", GL_TEXTURE_CUBE_MAP_ARRAY, "ivec3" },
                { "2DMS", GL_TEXTURE_2D_MULTISAMPLE, "ivec2" },
                { "2DMSArray", GL_TEXTURE_2D_MULTISAMPLE_ARRAY, "ivec3" },
                { 0 }
        };
        static struct image_target_info supported[ARRAY_SIZE(known)];

        if (!supported[0].name) {
                int max_samples = 0, i, n = 0;

                glGetIntegerv(GL_MAX_IMAGE_SAMPLES, &max_samples);

                for (i = 0; i < ARRAY_SIZE(known); ++i) {
                        if ((known[i].target != GL_TEXTURE_2D_MULTISAMPLE &&
                             known[i].target != GL_TEXTURE_2D_MULTISAMPLE_ARRAY) ||
                            max_samples > 1) {
                                supported[n++] = known[i];
                        }
                }
        }

        return supported;
}

const struct image_target_info *
get_image_target(GLenum t)
{
        const struct image_target_info *target;

        for (target = image_targets(); target->target; ++target) {
                if (target->target == t)
                        return target;
        }

        return NULL;
}

struct image_extent
image_target_limits(const struct image_target_info *target)
{
        struct image_extent ext = { 1, 1, 1, 1 };

        switch (target->target) {
        case GL_TEXTURE_1D:
                glGetIntegerv(GL_MAX_TEXTURE_SIZE, (int *)&ext.x);
                break;

        case GL_TEXTURE_2D:
                glGetIntegerv(GL_MAX_TEXTURE_SIZE, (int *)&ext.x);
                glGetIntegerv(GL_MAX_TEXTURE_SIZE, (int *)&ext.y);
                break;

        case GL_TEXTURE_3D:
                glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, (int *)&ext.x);
                glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, (int *)&ext.y);
                glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, (int *)&ext.z);
                break;

        case GL_TEXTURE_RECTANGLE:
                glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE, (int *)&ext.x);
                glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE, (int *)&ext.y);
                break;

        case GL_TEXTURE_CUBE_MAP:
                glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, (int *)&ext.x);
                glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, (int *)&ext.y);
                ext.z = 6;
                break;

        case GL_TEXTURE_BUFFER:
                glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, (int *)&ext.x);
                break;

        case GL_TEXTURE_1D_ARRAY:
                glGetIntegerv(GL_MAX_TEXTURE_SIZE, (int *)&ext.x);
                glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, (int *)&ext.y);
                break;

        case GL_TEXTURE_2D_ARRAY:
                glGetIntegerv(GL_MAX_TEXTURE_SIZE, (int *)&ext.x);
                glGetIntegerv(GL_MAX_TEXTURE_SIZE, (int *)&ext.y);
                glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, (int *)&ext.z);
                break;

        case GL_TEXTURE_CUBE_MAP_ARRAY:
                glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, (int *)&ext.x);
                glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, (int *)&ext.y);
                glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, (int *)&ext.z);
                break;

        case GL_TEXTURE_2D_MULTISAMPLE:
                glGetIntegerv(GL_MAX_IMAGE_SAMPLES, (int *)&ext.x);
                glGetIntegerv(GL_MAX_TEXTURE_SIZE, (int *)&ext.y);
                glGetIntegerv(GL_MAX_TEXTURE_SIZE, (int *)&ext.z);
                break;

        case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
                glGetIntegerv(GL_MAX_IMAGE_SAMPLES, (int *)&ext.x);
                glGetIntegerv(GL_MAX_TEXTURE_SIZE, (int *)&ext.y);
                glGetIntegerv(GL_MAX_TEXTURE_SIZE, (int *)&ext.z);
                glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, (int *)&ext.w);
                break;

        default:
                abort();
        }

        return ext;
}

unsigned
image_target_samples(const struct image_target_info *target)
{
        if (target->target == GL_TEXTURE_2D_MULTISAMPLE ||
            target->target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) {
                return image_target_limits(target).x;
        } else {
                return 1;
        }
}

struct image_extent
image_extent_for_target(const struct image_target_info *target,
                        unsigned w, unsigned h)
{
        switch (target->target) {
        case GL_TEXTURE_1D: {
                struct image_extent ext = { w * h, 1, 1, 1 };
                return ext;
        }
        case GL_TEXTURE_2D: {
                struct image_extent ext = { w, h, 1, 1 };
                return ext;
        }
        case GL_TEXTURE_3D: {
                struct image_extent ext = { w, w, h / w, 1 };
                return ext;
        }
        case GL_TEXTURE_RECTANGLE: {
                struct image_extent ext = { w, h, 1, 1 };
                return ext;
        }
        case GL_TEXTURE_CUBE_MAP: {
                struct image_extent ext = { w, w, h / w, 1 };
                assert(ext.z == 6);
                return ext;
        }
        case GL_TEXTURE_BUFFER: {
                struct image_extent ext = { w * h, 1, 1, 1 };
                return ext;
        }
        case GL_TEXTURE_1D_ARRAY: {
                struct image_extent ext = { w, h, 1, 1 };
                return ext;
        }
        case GL_TEXTURE_2D_ARRAY: {
                struct image_extent ext = { w, w, h / w, 1 };
                return ext;
        }
        case GL_TEXTURE_CUBE_MAP_ARRAY: {
                struct image_extent ext = { w, w, h / w, 1 };
                assert(ext.z % 6 == 0);
                return ext;
        }
        case GL_TEXTURE_2D_MULTISAMPLE: {
                struct image_extent ext = { 2, w / 2, h, 1 };
                return ext;
        }
        case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: {
                struct image_extent ext = { 2, w / 2, w, h / w };
                return ext;
        }
        default:
                abort();
        }
}

GLenum
image_layer_target(const struct image_target_info *target)
{
        switch (target->target) {
        case GL_TEXTURE_1D:
        case GL_TEXTURE_1D_ARRAY:
                return GL_TEXTURE_1D;

        case GL_TEXTURE_2D:
        case GL_TEXTURE_3D:
        case GL_TEXTURE_CUBE_MAP:
        case GL_TEXTURE_2D_ARRAY:
        case GL_TEXTURE_CUBE_MAP_ARRAY:
                return GL_TEXTURE_2D;

        case GL_TEXTURE_RECTANGLE:
                return GL_TEXTURE_RECTANGLE;

        case GL_TEXTURE_BUFFER:
                return GL_TEXTURE_BUFFER;

        case GL_TEXTURE_2D_MULTISAMPLE:
        case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
                return GL_TEXTURE_2D_MULTISAMPLE;

        default:
                abort();
        }
}

unsigned
image_target_mipmapping_dimensions(const struct image_target_info *target)
{
        switch (target->target) {
        case GL_TEXTURE_RECTANGLE:
        case GL_TEXTURE_BUFFER:
        case GL_TEXTURE_2D_MULTISAMPLE:
        case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
                return 0;

        case GL_TEXTURE_1D:
        case GL_TEXTURE_1D_ARRAY:
                return 1;

        case GL_TEXTURE_2D:
        case GL_TEXTURE_CUBE_MAP:
        case GL_TEXTURE_2D_ARRAY:
        case GL_TEXTURE_CUBE_MAP_ARRAY:
                return 2;

        case GL_TEXTURE_3D:
                return 3;

        default:
                abort();
        }
}

const struct image_stage_info *
known_image_stages(void)
{
        static const struct image_stage_info known[] = {
                { "Vertex", GL_VERTEX_SHADER, GL_VERTEX_SHADER_BIT },
                { "Tessellation control", GL_TESS_CONTROL_SHADER,
                  GL_TESS_CONTROL_SHADER_BIT },
                { "Tessellation evaluation", GL_TESS_EVALUATION_SHADER,
                  GL_TESS_EVALUATION_SHADER_BIT },
                { "Geometry", GL_GEOMETRY_SHADER, GL_GEOMETRY_SHADER_BIT },
                { "Fragment", GL_FRAGMENT_SHADER, GL_FRAGMENT_SHADER_BIT },
                { "Compute", GL_COMPUTE_SHADER, GL_COMPUTE_SHADER_BIT },
                { 0 }
        };
        return known;
}

const struct image_stage_info *
image_stages(void)
{
        static struct image_stage_info supported[7];

        if (!supported[0].name) {
                const struct image_stage_info *stage;
                int n = 0;

                for (stage = known_image_stages(); stage->stage; ++stage) {
                        if (image_stage_max_images(stage)) {
                                assert(n < ARRAY_SIZE(supported) - 1);
                                supported[n++] = *stage;
                        }
                }
        }

        return supported;
}

const struct image_stage_info *
get_image_stage(GLenum s)
{
        const struct image_stage_info *stage;

        for (stage = image_stages(); stage->stage; ++stage) {
                if (stage->stage == s)
                        return stage;
        }

        return NULL;
}

/**
 * Get the number of images internally required by the framework for a
 * pipeline with the given set of stages.
 */
static int
num_reserved_images(GLbitfield stages)
{
        /* One image uniform is reserved in the compute stage for the
         * grid framework to read back the result of the program.
         */
        return (stages & GL_COMPUTE_SHADER_BIT ? 1 : 0);
}

unsigned
image_stage_max_images(const struct image_stage_info *stage)
{
        int n = 0;

        switch (stage->stage) {
        case GL_FRAGMENT_SHADER:
                glGetIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &n);
                break;

        case GL_VERTEX_SHADER:
                glGetIntegerv(GL_MAX_VERTEX_IMAGE_UNIFORMS, &n);
                break;

        case GL_GEOMETRY_SHADER:
                if (piglit_get_gl_version() >= 32)
                        glGetIntegerv(GL_MAX_GEOMETRY_IMAGE_UNIFORMS, &n);
                break;

        case GL_TESS_CONTROL_SHADER:
                if (piglit_is_extension_supported("GL_ARB_tessellation_shader"))
                        glGetIntegerv(GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS, &n);
                break;

        case GL_TESS_EVALUATION_SHADER:
                if (piglit_is_extension_supported("GL_ARB_tessellation_shader"))
                        glGetIntegerv(GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS,
                                      &n);
                break;

        case GL_COMPUTE_SHADER:
                if (piglit_is_extension_supported("GL_ARB_compute_shader"))
                        glGetIntegerv(GL_MAX_COMPUTE_IMAGE_UNIFORMS, &n);
                break;
        }

        return MAX2(0, n - num_reserved_images(stage->bit));
}

unsigned
max_combined_images(void)
{
        int n;

        glGetIntegerv(GL_MAX_COMBINED_IMAGE_UNIFORMS, &n);
        return MAX2(0, n - num_reserved_images(~0));
}

unsigned
max_image_units(void)
{
        int n;

        glGetIntegerv(GL_MAX_IMAGE_UNITS, &n);
        return MAX2(0, n - num_reserved_images(~0));
}

unsigned
image_num_layers(const struct image_info img)
{
        switch (image_layer_target(img.target)) {
        case GL_TEXTURE_1D:
                return img.size.y;

        case GL_TEXTURE_2D:
                return img.size.z;

        case GL_TEXTURE_2D_MULTISAMPLE:
                return img.size.w;

        default:
                return 1;
        }
}

unsigned
image_num_levels(const struct image_info img)
{
        const unsigned d = image_target_mipmapping_dimensions(img.target);
        unsigned i, size = 1;

        for (i = 0; i < d; ++i)
                size = MAX2(size, get_idx(img.size, i));

        return (unsigned)log2(size) + 1;
}

struct image_extent
image_level_size(const struct image_info img, unsigned l)
{
        const unsigned d = image_target_mipmapping_dimensions(img.target);
        struct image_extent size;
        int i;

        for (i = 0; i < d; ++i)
                set_idx(size, i, MAX2(get_idx(img.size, i) >> l, 1));

        for (i = d; i < 4; ++i)
                set_idx(size, i, get_idx(img.size, i));

        return size;
}
