9ad1f92e2f
GrExternalTextureData is an API for exporting the backend-specific information about a texture in a type-safe way, and without pointing into the GrTexture. The new detachBackendTexture API lets us release ownership of a texture to the client. SkCrossContextImageData is the public API that lets clients upload textures on one thread/GrContext, then safely transfer ownership to another thread and GrContext for rendering. Only GL is implemented/supported right now. Vulkan support requires that we add thread-safe memory pools, or otherwise transfer the actual memory block containing the texture to the new context. BUG=skia: Change-Id: I784a3a74be69807df038c7d192eaed002c7e45ca Reviewed-on: https://skia-review.googlesource.com/8529 Commit-Queue: Brian Osman <brianosman@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com>
219 lines
7.8 KiB
C++
219 lines
7.8 KiB
C++
/*
|
|
* 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());
|
|
auto image = SkImage::MakeFromCrossContextImageData(info2.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);
|
|
}
|
|
}
|
|
}
|
|
|
|
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*/) {
|
|
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;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|