/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkTypes.h"
#include "SkPoint.h"
#include "Test.h"
#include <vector>

#if SK_SUPPORT_GPU

#include "GrAppliedClip.h"
#include "GrRenderTargetContext.h"
#include "GrRenderTargetPriv.h"
#include "GrTypesPriv.h"
#include "gl/GrGLGpu.h"
#include "gl/debug/DebugGLTestContext.h"

typedef std::vector<SkPoint> SamplePattern;

static const SamplePattern kTestPatterns[] = {
    SamplePattern{ // Intel on mac, msaa8, offscreen.
        {0.562500, 0.312500},
        {0.437500, 0.687500},
        {0.812500, 0.562500},
        {0.312500, 0.187500},
        {0.187500, 0.812500},
        {0.062500, 0.437500},
        {0.687500, 0.937500},
        {0.937500, 0.062500}
    },

    SamplePattern{ // Intel on mac, msaa8, on-screen.
        {0.562500, 0.687500},
        {0.437500, 0.312500},
        {0.812500, 0.437500},
        {0.312500, 0.812500},
        {0.187500, 0.187500},
        {0.062500, 0.562500},
        {0.687500, 0.062500},
        {0.937500, 0.937500}
    },

    SamplePattern{ // NVIDIA, msaa16.
        {0.062500, 0.000000},
        {0.250000, 0.125000},
        {0.187500, 0.375000},
        {0.437500, 0.312500},
        {0.500000, 0.062500},
        {0.687500, 0.187500},
        {0.750000, 0.437500},
        {0.937500, 0.250000},
        {0.000000, 0.500000},
        {0.312500, 0.625000},
        {0.125000, 0.750000},
        {0.375000, 0.875000},
        {0.562500, 0.562500},
        {0.812500, 0.687500},
        {0.625000, 0.812500},
        {0.875000, 0.937500}
    },

    SamplePattern{ // NVIDIA, mixed samples, 16:1.
        {0.250000, 0.125000},
        {0.625000, 0.812500},
        {0.500000, 0.062500},
        {0.812500, 0.687500},
        {0.187500, 0.375000},
        {0.875000, 0.937500},
        {0.125000, 0.750000},
        {0.750000, 0.437500},
        {0.937500, 0.250000},
        {0.312500, 0.625000},
        {0.437500, 0.312500},
        {0.000000, 0.500000},
        {0.375000, 0.875000},
        {0.687500, 0.187500},
        {0.062500, 0.000000},
        {0.562500, 0.562500}
    }
};
constexpr int numTestPatterns = SK_ARRAY_COUNT(kTestPatterns);

class TestSampleLocationsInterface : public SkNoncopyable {
public:
    virtual void overrideSamplePattern(const SamplePattern&) = 0;
    virtual ~TestSampleLocationsInterface() {}
};

void assert_equal(skiatest::Reporter* reporter, const SamplePattern& pattern,
                  const GrGpu::MultisampleSpecs& specs, bool flipY) {
    GrAlwaysAssert(specs.fSampleLocations);
    if ((int)pattern.size() != specs.fEffectiveSampleCnt) {
        REPORT_FAILURE(reporter, "", SkString("Sample pattern has wrong number of samples."));
        return;
    }
    for (int i = 0; i < specs.fEffectiveSampleCnt; ++i) {
        SkPoint expectedLocation = specs.fSampleLocations[i];
        if (flipY) {
            expectedLocation.fY = 1 - expectedLocation.fY;
        }
        if (pattern[i] != expectedLocation) {
            REPORT_FAILURE(reporter, "", SkString("Sample pattern has wrong sample location."));
            return;
        }
    }
}

static int pick_random_sample_count(int testPatternSize, SkRandom* rand, const GrCaps* caps) {
    GrAlwaysAssert(testPatternSize > 1 && SkIsPow2(testPatternSize));
    int randSampCnt = rand->nextRangeU(1 + testPatternSize / 2, testPatternSize);
    do {
        int cnt = caps->getSampleCount(randSampCnt, kRGBA_8888_GrPixelConfig);
        if (cnt) {
            return cnt;
        }
        --randSampCnt;
    } while (randSampCnt);
    // This test assumes an MSAA kRGBA_8888 RTC can be created.
    GrAlwaysAssert(false);
    return 0;
}

void test_sampleLocations(skiatest::Reporter* reporter, TestSampleLocationsInterface* testInterface,
                          GrContext* ctx) {
    SkRandom rand;
    sk_sp<GrRenderTargetContext> bottomUps[numTestPatterns];
    sk_sp<GrRenderTargetContext> topDowns[numTestPatterns];
    for (int i = 0; i < numTestPatterns; ++i) {
        int patternSize = (int)kTestPatterns[i].size();
        int randNumSamples = pick_random_sample_count(patternSize, &rand, ctx->caps());
        bottomUps[i] = ctx->makeDeferredRenderTargetContext(
                SkBackingFit::kExact, 100, 100, kRGBA_8888_GrPixelConfig, nullptr, randNumSamples,
                GrMipMapped::kNo, kBottomLeft_GrSurfaceOrigin);
        randNumSamples = pick_random_sample_count(patternSize, &rand, ctx->caps());
        topDowns[i] = ctx->makeDeferredRenderTargetContext(
                SkBackingFit::kExact, 100, 100, kRGBA_8888_GrPixelConfig, nullptr, randNumSamples,
                GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin);
    }

    // Ensure all sample locations get queried and/or cached properly.
    for (int repeat = 0; repeat < 2; ++repeat) {
        for (int i = 0; i < numTestPatterns; ++i) {
            testInterface->overrideSamplePattern(kTestPatterns[i]);
            for (GrRenderTargetContext* rtc : {bottomUps[i].get(), topDowns[i].get()}) {
                GrPipeline dummyPipeline(rtc->asRenderTargetProxy(),
                                         GrPipeline::ScissorState::kDisabled,
                                         SkBlendMode::kSrcOver);
                GrRenderTarget* rt = rtc->accessRenderTarget();
                assert_equal(reporter, kTestPatterns[i],
                             rt->renderTargetPriv().getMultisampleSpecs(dummyPipeline),
                             kBottomLeft_GrSurfaceOrigin == rtc->asSurfaceProxy()->origin());
            }
        }
    }

}

////////////////////////////////////////////////////////////////////////////////////////////////////

class GLTestSampleLocationsInterface : public TestSampleLocationsInterface, public GrGLInterface {
public:
    GLTestSampleLocationsInterface() : fTestContext(sk_gpu_test::CreateDebugGLTestContext()) {
        fStandard = fTestContext->gl()->fStandard;
        fExtensions = fTestContext->gl()->fExtensions;
        fFunctions = fTestContext->gl()->fFunctions;

        fFunctions.fGetIntegerv = [&](GrGLenum pname, GrGLint* params) {
            GrAlwaysAssert(GR_GL_EFFECTIVE_RASTER_SAMPLES != pname);
            if (GR_GL_SAMPLES == pname) {
                GrAlwaysAssert(!fSamplePattern.empty());
                *params = (int)fSamplePattern.size();
            } else {
                fTestContext->gl()->fFunctions.fGetIntegerv(pname, params);
            }
        };

        fFunctions.fGetMultisamplefv = [&](GrGLenum pname, GrGLuint index, GrGLfloat* val) {
            GrAlwaysAssert(GR_GL_SAMPLE_POSITION == pname);
            val[0] = fSamplePattern[index].fX;
            val[1] = fSamplePattern[index].fY;
        };
    }

    operator GrBackendContext() {
        return reinterpret_cast<GrBackendContext>(static_cast<GrGLInterface*>(this));
    }

    void overrideSamplePattern(const SamplePattern& newPattern) override {
        fSamplePattern = newPattern;
    }

private:
    std::unique_ptr<sk_gpu_test::GLTestContext> fTestContext;
    SamplePattern                               fSamplePattern;
};

DEF_GPUTEST(GLSampleLocations, reporter, /* options */) {
    auto testInterface = sk_make_sp<GLTestSampleLocationsInterface>();
    sk_sp<GrContext> ctx(GrContext::MakeGL(testInterface));

    // This test relies on at least 2 samples.
    int supportedSample = ctx->caps()->getSampleCount(2, kRGBA_8888_GrPixelConfig);
    if (supportedSample < 2) {
        return;
    }
    test_sampleLocations(reporter, testInterface.get(), ctx.get());
}

#endif