Add GrExternalTextureData and SkCrossContextImageData

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>
This commit is contained in:
Brian Osman 2017-02-23 17:06:10 -05:00 committed by Skia Commit-Bot
parent 20de615e3d
commit 9ad1f92e2f
30 changed files with 553 additions and 2 deletions

View File

@ -390,6 +390,7 @@ skia_core_sources = [
"$_include/core/SkColor.h",
"$_include/core/SkColorFilter.h",
"$_include/core/SkColorPriv.h",
"$_include/core/SkCrossContextImageData.h",
"$_include/core/SkData.h",
"$_include/core/SkDeque.h",
"$_include/core/SkDrawable.h",

View File

@ -18,6 +18,7 @@ skia_gpu_sources = [
"$_include/gpu/GrContextOptions.h",
"$_include/gpu/GrContext.h",
"$_include/gpu/GrCoordTransform.h",
"$_include/gpu/GrExternalTextureData.h",
"$_include/gpu/GrFragmentProcessor.h",
"$_include/gpu/GrGpuResource.h",
"$_include/gpu/GrPaint.h",

View File

@ -44,6 +44,7 @@ tests_sources = [
"$_tests/ColorTest.cpp",
"$_tests/CopySurfaceTest.cpp",
"$_tests/CPlusPlusEleven.cpp",
"$_tests/CrossContextImageTest.cpp",
"$_tests/CTest.cpp",
"$_tests/DashPathEffectTest.cpp",
"$_tests/DataRefTest.cpp",

View File

@ -0,0 +1,68 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkCrossContextImageData_DEFINED
#define SkCrossContextImageData_DEFINED
#include "SkImage.h"
#if SK_SUPPORT_GPU
#include "GrExternalTextureData.h"
#endif
class SK_API SkCrossContextImageData : SkNoncopyable {
public:
/**
* Decodes and uploads the encoded data to a texture using the supplied GrContext, then
* returns an instance of SkCrossContextImageData that can be used to transport that texture
* to a different GrContext, across thread boundaries. The GrContext used here, and the one
* used to reconstruct the texture-backed image later must be in the same GL share group,
* or otherwise be able to share resources. After calling this, you *must* construct exactly
* one SkImage from the returned value, using SkImage::MakeFromCrossContextImageData.
*
* The texture will be decoded and uploaded to be suitable for use with surfaces that have the
* supplied destination color space. The color space of the texture itself will be determined
* from the encoded data.
*/
static std::unique_ptr<SkCrossContextImageData> MakeFromEncoded(
GrContext*, sk_sp<SkData>, SkColorSpace* dstColorSpace);
private:
SkCrossContextImageData(sk_sp<SkImage> image) : fImage(std::move(image)) {
SkASSERT(!fImage->isTextureBacked());
}
#if SK_SUPPORT_GPU
SkCrossContextImageData(const GrBackendTextureDesc& desc,
std::unique_ptr<GrExternalTextureData> textureData,
SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace)
: fAlphaType(alphaType)
, fColorSpace(std::move(colorSpace))
, fDesc(desc)
, fTextureData(std::move(textureData)) {
// Point our texture desc at our copy of the backend information
fDesc.fTextureHandle = fTextureData->getBackendObject();
}
#endif
// For non-GPU backed images
sk_sp<SkImage> fImage;
#if SK_SUPPORT_GPU
// GPU-backed images store some generic information (needed to reconstruct the SkImage),
// and some backend-specific info (to reconstruct the texture).
SkAlphaType fAlphaType;
sk_sp<SkColorSpace> fColorSpace;
GrBackendTextureDesc fDesc;
std::unique_ptr<GrExternalTextureData> fTextureData;
#endif
friend class SkImage;
};
#endif

View File

@ -18,6 +18,7 @@
class SkData;
class SkCanvas;
class SkColorTable;
class SkCrossContextImageData;
class SkImageGenerator;
class SkPaint;
class SkPicture;
@ -323,6 +324,14 @@ public:
*/
sk_sp<SkImage> makeTextureImage(GrContext*, SkColorSpace* dstColorSpace) const;
/**
* Constructs a texture backed image from data that was previously uploaded on another thread
* and GrContext. The GrContext used to upload the data must be in the same GL share group as
* the one passed in here, or otherwise be able to share resources with the passed in context.
*/
static sk_sp<SkImage> MakeFromCrossContextImageData(GrContext*,
std::unique_ptr<SkCrossContextImageData>);
/**
* If the image is texture-backed this will make a raster copy of it (or nullptr if reading back
* the pixels fails). Otherwise, it returns the original image.

View File

@ -324,6 +324,12 @@ public:
*/
void prepareSurfaceForExternalIO(GrSurface*);
/**
* As above, but additionally flushes the backend API (eg calls glFlush), and returns a fence
* that can be used to determine if the surface is safe to use on another context or thread.
*/
GrFence SK_WARN_UNUSED_RESULT prepareSurfaceForExternalIOAndFlush(GrSurface*);
/**
* An ID associated with this context, guaranteed to be unique.
*/

View File

@ -0,0 +1,30 @@
/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrExternalTextureData_DEFINED
#define GrExternalTextureData_DEFINED
#include "GrTypes.h"
#include "GrTypesPriv.h"
class SK_API GrExternalTextureData : SkNoncopyable {
public:
GrExternalTextureData(GrFence fence) : fFence(fence) {}
virtual ~GrExternalTextureData() {}
virtual GrBackend getBackend() const = 0;
GrFence getFence() const { return fFence; }
protected:
virtual GrBackendObject getBackendObject() const = 0;
GrFence fFence;
friend class SkCrossContextImageData;
};
#endif

View File

@ -260,6 +260,11 @@ protected:
// final class).
void registerWithCacheWrapped();
// This is only called by resources that are being exported from Ganesh to client code. It
// ensures that the cache can no longer reach this resource, and that it no longer counts
// against the budget.
void detachFromCache();
GrGpuResource(GrGpu*);
virtual ~GrGpuResource();

View File

@ -14,6 +14,7 @@
#include "SkPoint.h"
#include "SkRefCnt.h"
class GrExternalTextureData;
class GrTexturePriv;
class GrTexture : virtual public GrSurface {
@ -49,6 +50,7 @@ protected:
GrSamplerParams::FilterMode highestFilterMode, bool wasMipMapDataProvided);
void validateDesc() const;
virtual std::unique_ptr<GrExternalTextureData> detachBackendTexture() = 0;
private:
void computeScratchKey(GrScratchKey*) const override;

View File

@ -9,6 +9,7 @@
#ifndef GrGLTypes_DEFINED
#define GrGLTypes_DEFINED
#include "GrExternalTextureData.h"
#include "GrGLConfig.h"
/**
@ -112,6 +113,23 @@ struct GrGLTextureInfo {
GrGLuint fID;
};
class GrGLExternalTextureData : public GrExternalTextureData {
public:
GrGLExternalTextureData(const GrGLTextureInfo& info, GrFence fence)
: INHERITED(fence)
, fInfo(info) {}
GrBackend getBackend() const override { return kOpenGL_GrBackend; }
protected:
GrBackendObject getBackendObject() const override {
return reinterpret_cast<GrBackendObject>(&fInfo);
}
GrGLTextureInfo fInfo;
typedef GrExternalTextureData INHERITED;
};
GR_STATIC_ASSERT(sizeof(GrBackendObject) >= sizeof(const GrGLTextureInfo*));
#endif

View File

@ -9,6 +9,7 @@
#ifndef GrVkTypes_DEFINED
#define GrVkTypes_DEFINED
#include "GrExternalTextureData.h"
#include "GrTypes.h"
#include "vk/GrVkDefines.h"
@ -59,6 +60,23 @@ struct GrVkImageInfo {
void updateImageLayout(VkImageLayout layout) { fImageLayout = layout; }
};
class GrVkExternalTextureData : public GrExternalTextureData {
public:
GrVkExternalTextureData(const GrVkImageInfo& info, GrFence fence)
: INHERITED(fence)
, fInfo(info) {}
GrBackend getBackend() const override { return kVulkan_GrBackend; }
protected:
GrBackendObject getBackendObject() const override {
return reinterpret_cast<GrBackendObject>(&fInfo);
}
GrVkImageInfo fInfo;
typedef GrExternalTextureData INHERITED;
};
GR_STATIC_ASSERT(sizeof(GrBackendObject) >= sizeof(const GrVkImageInfo*));
#endif

View File

@ -545,6 +545,13 @@ void GrContext::prepareSurfaceForExternalIO(GrSurface* surface) {
fDrawingManager->prepareSurfaceForExternalIO(surface);
}
GrFence GrContext::prepareSurfaceForExternalIOAndFlush(GrSurface* surface) {
this->prepareSurfaceForExternalIO(surface);
GrFence fence = fGpu->insertFence();
fGpu->flush();
return fence;
}
void GrContext::flushSurfaceWrites(GrSurface* surface) {
ASSERT_SINGLE_OWNER
RETURN_IF_ABANDONED

View File

@ -382,6 +382,10 @@ public:
virtual bool waitFence(GrFence, uint64_t timeout = 1000) const = 0;
virtual void deleteFence(GrFence) const = 0;
// Ensures that all queued up driver-level commands have been sent to the GPU. For example, on
// OpenGL, this calls glFlush.
virtual void flush() = 0;
///////////////////////////////////////////////////////////////////////////
// Debugging and Stats

View File

@ -43,6 +43,17 @@ void GrGpuResource::registerWithCacheWrapped() {
get_resource_cache(fGpu)->resourceAccess().insertResource(this);
}
void GrGpuResource::detachFromCache() {
if (this->wasDestroyed()) {
return;
}
if (fUniqueKey.isValid()) {
this->removeUniqueKey();
}
this->removeScratchKey();
this->makeUnbudgeted();
}
GrGpuResource::~GrGpuResource() {
// The cache should have released or destroyed this resource.
SkASSERT(this->wasDestroyed());

View File

@ -8,6 +8,7 @@
#ifndef GrTexturePriv_DEFINED
#define GrTexturePriv_DEFINED
#include "GrExternalTextureData.h"
#include "GrTexture.h"
/** Class that adds methods to GrTexture that are only intended for use internal to Skia.
@ -67,6 +68,15 @@ public:
}
SkDestinationSurfaceColorMode mipColorMode() const { return fTexture->fMipColorMode; }
/**
* Return the native bookkeeping data for this texture, and detach the backend object from
* this GrTexture. It's lifetime will no longer be managed by Ganesh, and this GrTexture will
* no longer refer to it. Leaves this GrTexture in an orphan state.
*/
std::unique_ptr<GrExternalTextureData> detachBackendTexture() {
return fTexture->detachBackendTexture();
}
static void ComputeScratchKey(const GrSurfaceDesc&, GrScratchKey*);
private:

View File

@ -4741,3 +4741,7 @@ bool GrGLGpu::waitFence(GrFence fence, uint64_t timeout) const {
void GrGLGpu::deleteFence(GrFence fence) const {
GL_CALL(DeleteSync((GrGLsync)fence));
}
void GrGLGpu::flush() {
GL_CALL(Flush());
}

View File

@ -150,6 +150,8 @@ public:
bool waitFence(GrFence, uint64_t timeout) const override;
void deleteFence(GrFence) const override;
void flush() override;
private:
GrGLGpu(GrGLContext* ctx, GrContext* context);

View File

@ -5,9 +5,11 @@
* found in the LICENSE file.
*/
#include "GrContext.h"
#include "GrGLTexture.h"
#include "GrGLGpu.h"
#include "GrShaderCaps.h"
#include "SkMakeUnique.h"
#include "SkTraceMemoryDump.h"
#define GPUGL static_cast<GrGLGpu*>(this->getGpu())
@ -111,6 +113,23 @@ GrBackendObject GrGLTexture::getTextureHandle() const {
return reinterpret_cast<GrBackendObject>(&fInfo);
}
std::unique_ptr<GrExternalTextureData> GrGLTexture::detachBackendTexture() {
// Flush any pending writes to this texture, as well GL itself
GrFence fence = this->getContext()->prepareSurfaceForExternalIOAndFlush(this);
// Make a copy of our GL-specific information
auto data = skstd::make_unique<GrGLExternalTextureData>(fInfo, fence);
// Ensure the cache can't reach this texture anymore
this->detachFromCache();
// Detach from the GL object, so we don't use it (or try to delete it when we're freed)
fInfo.fTarget = 0;
fInfo.fID = 0;
return std::move(data);
}
void GrGLTexture::setMemoryBacking(SkTraceMemoryDump* traceMemoryDump,
const SkString& dumpName) const {
SkString texture_id;

View File

@ -71,6 +71,7 @@ protected:
void onRelease() override;
void setMemoryBacking(SkTraceMemoryDump* traceMemoryDump,
const SkString& dumpName) const override;
std::unique_ptr<GrExternalTextureData> detachBackendTexture() override;
private:
TexParams fTexParams;

View File

@ -1860,3 +1860,6 @@ void GrVkGpu::deleteFence(GrFence fence) const {
GR_VK_CALL(this->vkInterface(), DestroyFence(this->device(), (VkFence)fence, nullptr));
}
void GrVkGpu::flush() {
// We submit the command buffer to the queue whenever Ganesh is flushed, so nothing is needed
}

View File

@ -133,6 +133,8 @@ public:
bool waitFence(GrFence, uint64_t timeout) const override;
void deleteFence(GrFence) const override;
void flush() override;
void generateMipmap(GrVkTexture* tex);
bool updateBuffer(GrVkBuffer* buffer, const void* src, VkDeviceSize offset, VkDeviceSize size);

View File

@ -144,6 +144,12 @@ GrBackendObject GrVkTexture::getTextureHandle() const {
return (GrBackendObject)&fInfo;
}
std::unique_ptr<GrExternalTextureData> GrVkTexture::detachBackendTexture() {
// Not supported on Vulkan yet
// TODO: Add thread-safe memory pools, and implement this.
return nullptr;
}
GrVkGpu* GrVkTexture::getVkGpu() const {
SkASSERT(!this->wasDestroyed());
return static_cast<GrVkGpu*>(this->getGpu());

View File

@ -42,6 +42,7 @@ protected:
void onAbandon() override;
void onRelease() override;
std::unique_ptr<GrExternalTextureData> detachBackendTexture() override;
private:
enum Wrapped { kWrapped };

View File

@ -8,6 +8,7 @@
#include "SkBitmap.h"
#include "SkBitmapCache.h"
#include "SkCanvas.h"
#include "SkCrossContextImageData.h"
#include "SkData.h"
#include "SkImageEncoder.h"
#include "SkImageFilter.h"
@ -357,6 +358,21 @@ sk_sp<SkImage> SkImage::makeTextureImage(GrContext*, SkColorSpace* dstColorSpace
return nullptr;
}
std::unique_ptr<SkCrossContextImageData> SkCrossContextImageData::MakeFromEncoded(
GrContext*, sk_sp<SkData> encoded, SkColorSpace* dstColorSpace) {
sk_sp<SkImage> image = SkImage::MakeFromEncoded(std::move(encoded));
if (!image) {
return nullptr;
}
// TODO: Force decode to raster here?
return std::unique_ptr<SkCrossContextImageData>(new SkCrossContextImageData(std::move(image)));
}
sk_sp<SkImage> SkImage::MakeFromCrossContextImageData(
GrContext*, std::unique_ptr<SkCrossContextImageData> ccid) {
return ccid->fImage;
}
sk_sp<SkImage> SkImage::makeNonTextureImage() const {
return sk_ref_sp(const_cast<SkImage*>(this));
}

View File

@ -14,6 +14,7 @@
#include "GrCaps.h"
#include "GrContext.h"
#include "GrContextPriv.h"
#include "GrGpu.h"
#include "GrImageTextureMaker.h"
#include "GrRenderTargetContext.h"
#include "GrTextureAdjuster.h"
@ -21,6 +22,7 @@
#include "GrTextureProxy.h"
#include "effects/GrYUVEffect.h"
#include "SkCanvas.h"
#include "SkCrossContextImageData.h"
#include "SkBitmapCache.h"
#include "SkGrPriv.h"
#include "SkImage_Gpu.h"
@ -357,7 +359,7 @@ static sk_sp<SkImage> create_image_from_maker(GrTextureMaker* maker, SkAlphaType
std::move(texColorSpace), SkBudgeted::kNo);
}
sk_sp<SkImage> SkImage::makeTextureImage(GrContext *context, SkColorSpace* dstColorSpace) const {
sk_sp<SkImage> SkImage::makeTextureImage(GrContext* context, SkColorSpace* dstColorSpace) const {
if (!context) {
return nullptr;
}
@ -377,6 +379,70 @@ sk_sp<SkImage> SkImage::makeTextureImage(GrContext *context, SkColorSpace* dstCo
return nullptr;
}
std::unique_ptr<SkCrossContextImageData> SkCrossContextImageData::MakeFromEncoded(
GrContext* context, sk_sp<SkData> encoded, SkColorSpace* dstColorSpace) {
sk_sp<SkImage> codecImage = SkImage::MakeFromEncoded(std::move(encoded));
if (!codecImage) {
return nullptr;
}
// If we don't have the ability to use fences, we can't safely transfer a texture between
// threads, so just hand off the codec image
if (!context->caps()->fenceSyncSupport()) {
return std::unique_ptr<SkCrossContextImageData>(
new SkCrossContextImageData(std::move(codecImage)));
}
sk_sp<SkImage> textureImage = codecImage->makeTextureImage(context, dstColorSpace);
if (!textureImage) {
// TODO: Force decode to raster here? Do mip-mapping, like getDeferredTextureImageData?
return std::unique_ptr<SkCrossContextImageData>(
new SkCrossContextImageData(std::move(codecImage)));
}
// Crack open the gpu image, extract the backend data, stick it in the SkCCID
GrTexture* texture = as_IB(textureImage)->peekTexture();
SkASSERT(texture);
GrBackendTextureDesc desc;
desc.fFlags = kNone_GrBackendTextureFlag;
desc.fOrigin = texture->origin();
desc.fWidth = texture->width();
desc.fHeight = texture->height();
desc.fConfig = texture->config();
desc.fSampleCnt = 0;
auto textureData = texture->texturePriv().detachBackendTexture();
if (!textureData) {
// Handles backends that don't support this feature (currently Vulkan). Do a raster decode
// here?
return std::unique_ptr<SkCrossContextImageData>(
new SkCrossContextImageData(std::move(codecImage)));
}
SkImageInfo info = as_IB(textureImage)->onImageInfo();
return std::unique_ptr<SkCrossContextImageData>(new SkCrossContextImageData(
desc, std::move(textureData), info.alphaType(), info.refColorSpace()));
}
sk_sp<SkImage> SkImage::MakeFromCrossContextImageData(
GrContext* context, std::unique_ptr<SkCrossContextImageData> ccid) {
if (ccid->fImage) {
// No pre-existing GPU resource. We could upload it now (with makeTextureImage),
// but we'd need a dstColorSpace.
return ccid->fImage;
}
if (ccid->fTextureData) {
GrFence fence = ccid->fTextureData->getFence();
context->getGpu()->waitFence(fence);
context->getGpu()->deleteFence(fence);
}
return MakeFromAdoptedTexture(context, ccid->fDesc, ccid->fAlphaType,
std::move(ccid->fColorSpace));
}
sk_sp<SkImage> SkImage::makeNonTextureImage() const {
if (!this->isTextureBacked()) {
return sk_ref_sp(const_cast<SkImage*>(this));

View File

@ -0,0 +1,218 @@
/*
* 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

View File

@ -273,4 +273,16 @@ ContextInfo GrContextFactory::getContextInfo(ContextType type, ContextOverrides
return ContextInfo(context.fBackend, context.fTestContext, context.fGrContext);
}
ContextInfo GrContextFactory::getSharedContextInfo(GrContext* shareContext, uint32_t shareIndex) {
SkASSERT(shareContext);
for (int i = 0; i < fContexts.count(); ++i) {
if (!fContexts[i].fAbandoned && fContexts[i].fGrContext == shareContext) {
return this->getContextInfo(fContexts[i].fType, fContexts[i].fOverrides,
shareContext, shareIndex);
}
}
return ContextInfo();
}
} // namespace sk_gpu_test

View File

@ -151,6 +151,11 @@ public:
ContextInfo getContextInfo(ContextType type,
ContextOverrides overrides = ContextOverrides::kNone,
GrContext* shareContext = nullptr, uint32_t shareIndex = 0);
/**
* Get a context in the same share group as the passed in GrContext, with the same type and
* overrides.
*/
ContextInfo getSharedContextInfo(GrContext* shareContext, uint32_t shareIndex = 0);
/**
* Get a GrContext initialized with a type of GL context. It also makes the GL context current.
*/

View File

@ -313,6 +313,7 @@ public:
GrFence SK_WARN_UNUSED_RESULT insertFence() const override { return 0; }
bool waitFence(GrFence, uint64_t) const override { return true; }
void deleteFence(GrFence) const override {}
void flush() override {}
private:
void onResetContext(uint32_t resetBits) override {}

View File

@ -85,7 +85,11 @@ WinGLTestContext::WinGLTestContext(GrGLStandard forcedGpuAPI, WinGLTestContext*
kGLES_GrGLStandard == forcedGpuAPI ?
kGLES_SkWGLContextRequest : kGLPreferCompatibilityProfile_SkWGLContextRequest;
HGLRC winShareContext = shareContext ? shareContext->fGlRenderContext : nullptr;
HGLRC winShareContext = nullptr;
if (shareContext) {
winShareContext = shareContext->fPbufferContext ? shareContext->fPbufferContext->getGLRC()
: shareContext->fGlRenderContext;
}
fPbufferContext = SkWGLPbufferContext::Create(fDeviceContext, 0, contextType, winShareContext);
HDC dc;