C++程序  |  241行  |  9.11 KB

/*
 * Copyright 2017 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"

#if SK_SUPPORT_GPU

#include "GrContextFactory.h"
#include "Resources.h"
#include "SkAutoPixmapStorage.h"
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkCrossContextImageData.h"
#include "SkSemaphore.h"
#include "SkSurface.h"
#include "SkThreadUtils.h"
#include "Test.h"

using namespace sk_gpu_test;

static SkImageInfo read_pixels_info(SkImage* image) {
    return SkImageInfo::MakeN32(image->width(), image->height(), image->alphaType());
}

static bool colors_are_close(SkColor a, SkColor b, int error) {
    return SkTAbs((int)SkColorGetR(a) - (int)SkColorGetR(b)) <= error &&
           SkTAbs((int)SkColorGetG(a) - (int)SkColorGetG(b)) <= error &&
           SkTAbs((int)SkColorGetB(a) - (int)SkColorGetB(b)) <= error;
}

static void assert_equal(skiatest::Reporter* reporter, SkImage* a, SkImage* b, int error) {
    REPORTER_ASSERT(reporter, a->width() == b->width());
    REPORTER_ASSERT(reporter, a->height() == b->height());

    SkAutoPixmapStorage pmapA, pmapB;
    pmapA.alloc(read_pixels_info(a));
    pmapB.alloc(read_pixels_info(b));

    REPORTER_ASSERT(reporter, a->readPixels(pmapA, 0, 0));
    REPORTER_ASSERT(reporter, b->readPixels(pmapB, 0, 0));

    for (int y = 0; y < a->height(); ++y) {
        for (int x = 0; x < a->width(); ++x) {
            SkColor ca = pmapA.getColor(x, y);
            SkColor cb = pmapB.getColor(x, y);
            if (!error) {
                if (ca != cb) {
                    ERRORF(reporter, "Expected 0x%08x but got 0x%08x at (%d, %d)", ca, cb, x, y);
                    return;
                }
            } else {
                if (!colors_are_close(ca, cb, error)) {
                    ERRORF(reporter, "Expected 0x%08x +-%d but got 0x%08x at (%d, %d)",
                           ca, error, cb, x, y);
                    return;
                }
            }
        }
    }
}

static void draw_image_test_pattern(SkCanvas* canvas) {
    canvas->clear(SK_ColorWHITE);
    SkPaint paint;
    paint.setColor(SK_ColorBLACK);
    canvas->drawRect(SkRect::MakeXYWH(5, 5, 10, 10), paint);
}

static sk_sp<SkImage> create_test_image() {
    SkBitmap bm;
    bm.allocN32Pixels(20, 20, true);
    SkCanvas canvas(bm);
    draw_image_test_pattern(&canvas);

    return SkImage::MakeFromBitmap(bm);
}

static sk_sp<SkData> create_test_data(SkEncodedImageFormat format) {
    auto image = create_test_image();
    return sk_sp<SkData>(image->encode(format, 100));
}

DEF_GPUTEST(CrossContextImage_SameContext, reporter, /*factory*/) {
    GrContextFactory factory;
    sk_sp<SkImage> testImage = create_test_image();

    // Test both PNG and JPG, to exercise GPU YUV conversion
    for (auto format : { SkEncodedImageFormat::kPNG, SkEncodedImageFormat::kJPEG }) {
        sk_sp<SkData> encoded = create_test_data(format);

        for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) {
            GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i);
            if (!sk_gpu_test::GrContextFactory::IsRenderingContext(ctxType)) {
                continue;
            }

            ContextInfo info = factory.getContextInfo(ctxType);
            if (!info.grContext()) {
                continue;
            }

            auto ccid = SkCrossContextImageData::MakeFromEncoded(info.grContext(), encoded,
                                                                 nullptr);
            REPORTER_ASSERT(reporter, ccid != nullptr);

            auto image = SkImage::MakeFromCrossContextImageData(info.grContext(), std::move(ccid));
            REPORTER_ASSERT(reporter, image != nullptr);

            // JPEG encode -> decode won't round trip the image perfectly
            assert_equal(reporter, testImage.get(), image.get(),
                         SkEncodedImageFormat::kJPEG == format ? 2 : 0);
        }
    }
}

DEF_GPUTEST(CrossContextImage_SharedContextSameThread, reporter, /*factory*/) {
    GrContextFactory factory;
    sk_sp<SkImage> testImage = create_test_image();

    // Test both PNG and JPG, to exercise GPU YUV conversion
    for (auto format : { SkEncodedImageFormat::kPNG, SkEncodedImageFormat::kJPEG }) {
        sk_sp<SkData> encoded = create_test_data(format);

        for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) {
            GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i);
            if (!sk_gpu_test::GrContextFactory::IsRenderingContext(ctxType)) {
                continue;
            }

            ContextInfo info = factory.getContextInfo(ctxType);
            if (!info.grContext()) {
                continue;
            }
            auto ccid = SkCrossContextImageData::MakeFromEncoded(info.grContext(), encoded,
                                                                 nullptr);
            REPORTER_ASSERT(reporter, ccid != nullptr);

            ContextInfo info2 = factory.getSharedContextInfo(info.grContext());
            GrContext* ctx2 = info2.grContext();
            int resourceCountBefore = 0, resourceCountAfter = 0;
            size_t resourceBytesBefore = 0, resourceBytesAfter = 0;
            if (ctx2 && info.grContext()->caps()->crossContextTextureSupport()) {
                ctx2->getResourceCacheUsage(&resourceCountBefore, &resourceBytesBefore);
            }

            auto image = SkImage::MakeFromCrossContextImageData(ctx2, std::move(ccid));
            REPORTER_ASSERT(reporter, image != nullptr);

            if (ctx2 && info.grContext()->caps()->crossContextTextureSupport()) {
                // MakeFromCrossContextImageData should have imported the texture back into our
                // cache, so we should see an uptick. (If we have crossContextTextureSupport,
                // otherwise we're just handing around a CPU or codec-backed image, so no cache
                // impact will occur).
                ctx2->getResourceCacheUsage(&resourceCountAfter, &resourceBytesAfter);
                REPORTER_ASSERT(reporter, resourceCountAfter == resourceCountBefore + 1);
                REPORTER_ASSERT(reporter, resourceBytesAfter > resourceBytesBefore);
            }

            // JPEG encode -> decode won't round trip the image perfectly
            assert_equal(reporter, testImage.get(), image.get(),
                         SkEncodedImageFormat::kJPEG == format ? 2 : 0);
        }
    }
}

namespace {
struct CrossContextImage_ThreadContext {
    GrContext* fGrContext;
    sk_gpu_test::TestContext* fTestContext;
    SkSemaphore fSemaphore;
    std::unique_ptr<SkCrossContextImageData> fCCID;
    sk_sp<SkData> fEncoded;
};
}

static void upload_image_thread_proc(void* data) {
    CrossContextImage_ThreadContext* ctx = static_cast<CrossContextImage_ThreadContext*>(data);
    ctx->fTestContext->makeCurrent();
    ctx->fCCID = SkCrossContextImageData::MakeFromEncoded(ctx->fGrContext, ctx->fEncoded, nullptr);
    ctx->fSemaphore.signal();
}

DEF_GPUTEST(CrossContextImage_SharedContextOtherThread, reporter, /*factory*/) {
    sk_sp<SkImage> testImage = create_test_image();

    // Test both PNG and JPG, to exercise GPU YUV conversion
    for (auto format : { SkEncodedImageFormat::kPNG, SkEncodedImageFormat::kJPEG }) {
        // Use a new factory for each batch of tests. Otherwise the shared context will still be
        // current on the upload thread when we do the second iteration, and we get undefined
        // behavior.
        GrContextFactory factory;
        sk_sp<SkData> encoded = create_test_data(format);

        for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) {
            GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i);
            if (!sk_gpu_test::GrContextFactory::IsRenderingContext(ctxType)) {
                continue;
            }

            // Create two GrContexts in a share group
            ContextInfo info = factory.getContextInfo(ctxType);
            if (!info.grContext()) {
                continue;
            }
            ContextInfo info2 = factory.getSharedContextInfo(info.grContext());
            if (!info2.grContext()) {
                continue;
            }

            // Make the first one current (on this thread) again
            info.testContext()->makeCurrent();

            // Bundle up data for the worker thread
            CrossContextImage_ThreadContext ctx;
            ctx.fGrContext = info2.grContext();
            ctx.fTestContext = info2.testContext();
            ctx.fEncoded = encoded;

            SkThread uploadThread(upload_image_thread_proc, &ctx);
            SkAssertResult(uploadThread.start());

            ctx.fSemaphore.wait();
            auto image = SkImage::MakeFromCrossContextImageData(info.grContext(),
                                                                std::move(ctx.fCCID));
            REPORTER_ASSERT(reporter, image != nullptr);

            // JPEG encode -> decode won't round trip the image perfectly
            assert_equal(reporter, testImage.get(), image.get(),
                         SkEncodedImageFormat::kJPEG == format ? 2 : 0);

            uploadThread.join();
        }
    }
}

#endif