/*
 * 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 coherency.c
 *
 * Images declared as "coherent" or "volatile" give certain guarantees
 * regarding the visibility of memory writes to other shader invocations
 * in the pipeline.  This test checks the following assertion of the
 * spec:
 * "When reading a variable declared as 'coherent', the values returned
 *  will reflect the results of previously completed writes performed by
 *  other shader invocations.  When writing a variable declared as
 *  'coherent', the values written will be reflected in subsequent
 *  coherent reads performed by other shader invocations."
 *
 * Together with:
 * "When executing a shader whose inputs are generated from a previous
 *  programmable stage, the shader invocations from the previous stage
 *  are guaranteed to have executed far enough to generate final values
 *  for all next-stage inputs."
 *
 * In order to test this we bind two shader programs at different
 * execution stages of the pipeline and check that the values written to
 * an image from the first stage are visible when the same primitive is
 * dispatched at the second stage.  This is repeated for all combinations
 * of dependent shader stages (what necessarily excludes the compute
 * shader), for the "coherent" and "volatile" qualifiers (the latter is
 * supposed to give stricter ordering guarantees), and for different
 * execution sizes between 4x4 and 1024x1024 to account for
 * implementations with varying levels of parallelism and with caches of
 * different sizes.
 *
 * Unless running in "quick" mode a series of control tests is
 * executed repeating the same process without any memory qualifiers.
 * This is useful to see if the cache is being hit since otherwise the
 * main test is not meaningful.  The control test always passes as it
 * is expected to misrender.
 */

#include "common.h"

/** Maximum image width. */
#define L 1024

/** Maximum number of pixels. */
#define N (L * L)

PIGLIT_GL_TEST_CONFIG_BEGIN

config.supports_gl_core_version = 32;

config.window_width = L;
config.window_height = L;
config.window_visual = PIGLIT_GL_VISUAL_DOUBLE | PIGLIT_GL_VISUAL_RGBA;
config.khr_no_error_support = PIGLIT_NO_ERRORS;

PIGLIT_GL_TEST_CONFIG_END

struct image_qualifier_info {
        /** Test name. */
        const char *name;

        /** Image qualifier keyword. */
        const char *qualifier;

        /** Informative "control" test with no memory qualifiers whose
         * result is ignored. */
        bool control_test;
};

const struct image_qualifier_info image_qualifiers[] = {
        { "control", "", true },
        { "'coherent' qualifier", "coherent", false },
        { "'volatile' qualifier", "volatile", false },
        { 0 }
};

char *
qualifier_hunk(const struct image_qualifier_info *qual)
{
        char *s = NULL;

        (void)!asprintf(&s, "#define IMAGE_Q %s\n", qual->qualifier);
        return s;
}

static bool
init_image(const struct image_info img)
{
        static uint32_t pixels[N][4];

        return init_pixels(img, pixels[0], 99, 99, 99, 99) &&
                upload_image(img, 0, pixels[0]);
}

static bool
check(const struct grid_info grid, const struct image_info img)
{
        static uint32_t pixels[N][4];

        return download_result(grid, pixels[0]) &&
                check_pixels(img, pixels[0], 33, 33, 33, 33);
}

static bool
run_test(const struct image_qualifier_info *qual,
         const struct image_stage_info *stage_w,
         const struct image_stage_info *stage_r,
         unsigned l)
{
        const struct grid_info grid = {
                stage_w->bit | stage_r->bit,
                get_image_format(GL_RGBA32UI),
                { l, l, 1, 1 }
        };
        const struct image_info img = image_info_for_grid(grid);
        GLuint prog = generate_program(
                grid,
                /*
                 * Write (11, 22, 33, 44) to some location on the
                 * image from the write stage.
                 */
                stage_w->stage,
                concat(qualifier_hunk(qual),
                       image_hunk(img, ""),
                       hunk("IMAGE_Q IMAGE_UNIFORM_T img;\n"
                            "\n"
                            "GRID_T op(ivec2 idx, GRID_T x) {\n"
                            "       imageStore(img, idx, DATA_T(11, 22, 33, 44));"
                            "       return x;"
                            "}\n"), NULL),
                /*
                 * The same location will read back the expected value
                 * if image access is coherent, as the shader inputs
                 * of the read stage are dependent on the outputs of
                 * the write stage and consequently they are
                 * guaranteed to be executed sequentially.
                 */
                stage_r->stage,
                concat(qualifier_hunk(qual),
                       image_hunk(img, ""),
                       hunk("IMAGE_Q IMAGE_UNIFORM_T img;\n"
                            "\n"
                            "GRID_T op(ivec2 idx, GRID_T x) {\n"
                            "       DATA_T v = imageLoad(img, idx);"
                            "       if (v == DATA_T(11, 22, 33, 44))"
                            "             return GRID_T(33, 33, 33, 33);"
                            "       else"
                            "             return GRID_T(77, 77, 77, 77);"
                            "}\n"), NULL));
        bool ret = prog &&
                init_fb(grid) &&
                init_image(img) &&
                set_uniform_int(prog, "img", 0) &&
                draw_grid(grid, prog) &&
                (check(grid, img) || qual->control_test);

        glDeleteProgram(prog);
        return ret;
}

void
piglit_init(int argc, char **argv)
{
        const bool quick = (argc >= 2 && !strcmp(argv[1], "--quick"));
        const struct image_qualifier_info *qual;
        const struct image_stage_info *stage_w;
        const struct image_stage_info *stage_r;
        enum piglit_result status = PIGLIT_PASS;
        unsigned l;

        piglit_require_extension("GL_ARB_shader_image_load_store");

        for (l = 4; l <= L; l *= (quick ? 8 : 2)) {
                for (qual = image_qualifiers; qual->name; ++qual) {
                        if (quick && qual->control_test)
                                continue;

                        /*
                         * Loop for each pair of shader stages
                         * skipping the compute shader: "coherent"
                         * gives no useful guarantees in that case
                         * since its execution ordering is undefined
                         * with respect to the other shader stages.
                         */
                        for (stage_w = image_stages();
                             stage_w->stage; ++stage_w) {
                                for (stage_r = stage_w + 1;
                                     stage_r->stage; ++stage_r) {
                                        if (stage_w->stage == GL_COMPUTE_SHADER ||
                                            stage_r->stage == GL_COMPUTE_SHADER)
                                                continue;

                                      subtest(&status, true,
                                                run_test(qual, stage_w, stage_r, l),
                                                "%s-%s shader/%s coherency test/%dx%d",
                                                stage_w->name, stage_r->name,
                                                qual->name, l, l);
                                }
                        }
                }
        }

        piglit_report_result(status);
}

enum piglit_result
piglit_display(void)
{
        return PIGLIT_FAIL;
}
