Add GrFragmentProcessor::clone(), implementation for ColorTableEffect, and unit test.
We occasionally make copies of GrPaints. clone() fill facilitate this case when GrFragmentProcessors are non-shareable.. Change-Id: I004e34f6ce8c293f9e0664d26532e44bd6b9fdff Reviewed-on: https://skia-review.googlesource.com/26360 Commit-Queue: Brian Salomon <bsalomon@google.com> Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
parent
22115b4fc6
commit
0e05a823f6
@ -323,9 +323,7 @@ sk_sp<SkColorFilter> SkTable_ColorFilter::makeComposed(sk_sp<SkColorFilter> inne
|
||||
|
||||
class ColorTableEffect : public GrFragmentProcessor {
|
||||
public:
|
||||
static sk_sp<GrFragmentProcessor> Make(GrContext* context,
|
||||
const SkBitmap& bitmap,
|
||||
unsigned flags);
|
||||
static sk_sp<GrFragmentProcessor> Make(GrContext* context, const SkBitmap& bitmap);
|
||||
|
||||
~ColorTableEffect() override;
|
||||
|
||||
@ -334,6 +332,8 @@ public:
|
||||
const GrTextureStripAtlas* atlas() const { return fAtlas; }
|
||||
int atlasRow() const { return fRow; }
|
||||
|
||||
sk_sp<GrFragmentProcessor> clone() override;
|
||||
|
||||
private:
|
||||
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
|
||||
|
||||
@ -341,8 +341,7 @@ private:
|
||||
|
||||
bool onIsEqual(const GrFragmentProcessor&) const override;
|
||||
|
||||
ColorTableEffect(sk_sp<GrTextureProxy> proxy,
|
||||
GrTextureStripAtlas* atlas, int row, unsigned flags);
|
||||
ColorTableEffect(sk_sp<GrTextureProxy> proxy, GrTextureStripAtlas* atlas, int row);
|
||||
|
||||
GR_DECLARE_FRAGMENT_PROCESSOR_TEST
|
||||
|
||||
@ -437,9 +436,7 @@ void GLColorTableEffect::emitCode(EmitArgs& args) {
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
sk_sp<GrFragmentProcessor> ColorTableEffect::Make(GrContext* context, const SkBitmap& bitmap,
|
||||
unsigned flags) {
|
||||
|
||||
sk_sp<GrFragmentProcessor> ColorTableEffect::Make(GrContext* context, const SkBitmap& bitmap) {
|
||||
GrTextureStripAtlas::Desc desc;
|
||||
desc.fWidth = bitmap.width();
|
||||
desc.fHeight = 128;
|
||||
@ -463,11 +460,10 @@ sk_sp<GrFragmentProcessor> ColorTableEffect::Make(GrContext* context, const SkBi
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return sk_sp<GrFragmentProcessor>(new ColorTableEffect(std::move(proxy), atlas, row, flags));
|
||||
return sk_sp<GrFragmentProcessor>(new ColorTableEffect(std::move(proxy), atlas, row));
|
||||
}
|
||||
|
||||
ColorTableEffect::ColorTableEffect(sk_sp<GrTextureProxy> proxy,
|
||||
GrTextureStripAtlas* atlas, int row, unsigned flags)
|
||||
ColorTableEffect::ColorTableEffect(sk_sp<GrTextureProxy> proxy, GrTextureStripAtlas* atlas, int row)
|
||||
: INHERITED(kNone_OptimizationFlags) // Not bothering with table-specific optimizations.
|
||||
, fTextureSampler(std::move(proxy))
|
||||
, fAtlas(atlas)
|
||||
@ -482,6 +478,12 @@ ColorTableEffect::~ColorTableEffect() {
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<GrFragmentProcessor> ColorTableEffect::clone() {
|
||||
fAtlas->lockRow(fRow);
|
||||
return sk_sp<GrFragmentProcessor>(
|
||||
new ColorTableEffect(sk_ref_sp(fTextureSampler.proxy()), fAtlas, fRow));
|
||||
}
|
||||
|
||||
void ColorTableEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
|
||||
GrProcessorKeyBuilder* b) const {
|
||||
GLColorTableEffect::GenKey(*this, caps, b);
|
||||
@ -539,7 +541,7 @@ sk_sp<GrFragmentProcessor> SkTable_ColorFilter::asFragmentProcessor(GrContext* c
|
||||
SkBitmap bitmap;
|
||||
this->asComponentTable(&bitmap);
|
||||
|
||||
return ColorTableEffect::Make(context, bitmap, fFlags);
|
||||
return ColorTableEffect::Make(context, bitmap);
|
||||
}
|
||||
|
||||
#endif // SK_SUPPORT_GPU
|
||||
|
@ -86,6 +86,14 @@ public:
|
||||
|
||||
~GrFragmentProcessor() override;
|
||||
|
||||
/**
|
||||
* Makes a copy of this fragment processor that draws equivalently to the original.
|
||||
* If the processor has child processors they are cloned as well. Currently this
|
||||
* has a default implementation that fails. This is temporary until it can be implemented
|
||||
* for all fragemnt processor leaf classes.
|
||||
*/
|
||||
virtual sk_sp<GrFragmentProcessor> clone() { return nullptr; }
|
||||
|
||||
GrGLSLFragmentProcessor* createGLSLInstance() const;
|
||||
|
||||
void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const {
|
||||
|
@ -52,6 +52,11 @@ public:
|
||||
* is responsible for calling unlockRow() with this row index when it's done with it.
|
||||
*/
|
||||
int lockRow(const SkBitmap& data);
|
||||
/**
|
||||
* This is intended to be used when cloning a processor that already holds a lock. It is
|
||||
* assumed that the row already has at least one lock.
|
||||
*/
|
||||
void lockRow(int row);
|
||||
void unlockRow(int row);
|
||||
|
||||
/**
|
||||
|
@ -86,6 +86,13 @@ GrTextureStripAtlas::GrTextureStripAtlas(GrTextureStripAtlas::Desc desc)
|
||||
|
||||
GrTextureStripAtlas::~GrTextureStripAtlas() { delete[] fRows; }
|
||||
|
||||
void GrTextureStripAtlas::lockRow(int row) {
|
||||
// This should only be called on a row that is already locked.
|
||||
SkASSERT(fRows[row].fLocks);
|
||||
fRows[row].fLocks++;
|
||||
++fLockedRows;
|
||||
}
|
||||
|
||||
int GrTextureStripAtlas::lockRow(const SkBitmap& bitmap) {
|
||||
VALIDATE;
|
||||
if (0 == fLockedRows) {
|
||||
|
@ -261,13 +261,19 @@ DEF_GPUTEST_FOR_ALL_CONTEXTS(ProcessorRefTest, reporter, ctxInfo) {
|
||||
// This test uses the random GrFragmentProcessor test factory, which relies on static initializers.
|
||||
#if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
|
||||
|
||||
static GrColor texel_color(int i, int j) {
|
||||
SkASSERT((unsigned)i < 256 && (unsigned)j < 256);
|
||||
GrColor color = GrColorPackRGBA(j, (uint8_t)(i + j), (uint8_t)(2 * j - i), i);
|
||||
#include "SkCommandLineFlags.h"
|
||||
DEFINE_bool(randomProcessorTest, false, "Use non-deterministic seed for random processor tests?");
|
||||
|
||||
#if GR_TEST_UTILS
|
||||
|
||||
static GrColor input_texel_color(int i, int j) {
|
||||
GrColor color = GrColorPackRGBA((uint8_t)j, (uint8_t)(i + j), (uint8_t)(2 * j - i), (uint8_t)i);
|
||||
return GrPremulColor(color);
|
||||
}
|
||||
|
||||
static GrColor4f texel_color4f(int i, int j) { return GrColor4f::FromGrColor(texel_color(i, j)); }
|
||||
static GrColor4f input_texel_color4f(int i, int j) {
|
||||
return GrColor4f::FromGrColor(input_texel_color(i, j));
|
||||
}
|
||||
|
||||
void test_draw_op(GrRenderTargetContext* rtc, sk_sp<GrFragmentProcessor> fp,
|
||||
sk_sp<GrTextureProxy> inputDataProxy) {
|
||||
@ -282,10 +288,57 @@ void test_draw_op(GrRenderTargetContext* rtc, sk_sp<GrFragmentProcessor> fp,
|
||||
rtc->addDrawOp(GrNoClip(), std::move(op));
|
||||
}
|
||||
|
||||
#include "SkCommandLineFlags.h"
|
||||
DEFINE_bool(randomProcessorTest, false, "Use non-deterministic seed for random processor tests?");
|
||||
/** Initializes the two test texture proxies that are available to the FP test factories. */
|
||||
bool init_test_textures(GrContext* context, SkRandom* random, sk_sp<GrTextureProxy> proxies[2]) {
|
||||
static const int kTestTextureSize = 256;
|
||||
GrSurfaceDesc desc;
|
||||
desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
|
||||
desc.fWidth = kTestTextureSize;
|
||||
desc.fHeight = kTestTextureSize;
|
||||
desc.fConfig = kRGBA_8888_GrPixelConfig;
|
||||
|
||||
#if GR_TEST_UTILS
|
||||
// Put premul data into the RGBA texture that the test FPs can optionally use.
|
||||
std::unique_ptr<GrColor[]> rgbaData(new GrColor[kTestTextureSize * kTestTextureSize]);
|
||||
for (int y = 0; y < kTestTextureSize; ++y) {
|
||||
for (int x = 0; x < kTestTextureSize; ++x) {
|
||||
rgbaData[kTestTextureSize * y + x] =
|
||||
input_texel_color(random->nextULessThan(256), random->nextULessThan(256));
|
||||
}
|
||||
}
|
||||
proxies[0] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kYes,
|
||||
rgbaData.get(), kTestTextureSize * sizeof(GrColor));
|
||||
|
||||
// Put random values into the alpha texture that the test FPs can optionally use.
|
||||
desc.fConfig = kAlpha_8_GrPixelConfig;
|
||||
std::unique_ptr<uint8_t[]> alphaData(new uint8_t[kTestTextureSize * kTestTextureSize]);
|
||||
for (int y = 0; y < kTestTextureSize; ++y) {
|
||||
for (int x = 0; x < kTestTextureSize; ++x) {
|
||||
alphaData[kTestTextureSize * y + x] = random->nextULessThan(256);
|
||||
}
|
||||
}
|
||||
proxies[1] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kYes,
|
||||
alphaData.get(), kTestTextureSize);
|
||||
|
||||
return proxies[0] && proxies[1];
|
||||
}
|
||||
|
||||
// Creates a texture of premul colors used as the output of the fragment processor that precedes
|
||||
// the fragment processor under test. Color values are those provided by input_texel_color().
|
||||
sk_sp<GrTextureProxy> make_input_texture(GrContext* context, int width, int height) {
|
||||
std::unique_ptr<GrColor[]> data(new GrColor[width * height]);
|
||||
for (int y = 0; y < width; ++y) {
|
||||
for (int x = 0; x < height; ++x) {
|
||||
data.get()[width * y + x] = input_texel_color(x, y);
|
||||
}
|
||||
}
|
||||
GrSurfaceDesc desc;
|
||||
desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
|
||||
desc.fWidth = width;
|
||||
desc.fHeight = height;
|
||||
desc.fConfig = kRGBA_8888_GrPixelConfig;
|
||||
return GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kYes,
|
||||
data.get(), width * sizeof(GrColor));
|
||||
}
|
||||
DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, reporter, ctxInfo) {
|
||||
GrContext* context = ctxInfo.grContext();
|
||||
using FPFactory = GrFragmentProcessorTestFactory;
|
||||
@ -299,60 +352,22 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, repor
|
||||
// hard-code that value here:
|
||||
SkRandom random(seed);
|
||||
|
||||
// Make the destination context for the test.
|
||||
static constexpr int kRenderSize = 256;
|
||||
sk_sp<GrRenderTargetContext> rtc = context->makeDeferredRenderTargetContext(
|
||||
SkBackingFit::kExact, 256, 256, kRGBA_8888_GrPixelConfig, nullptr);
|
||||
GrSurfaceDesc desc;
|
||||
desc.fFlags = kRenderTarget_GrSurfaceFlag;
|
||||
desc.fOrigin = kBottomLeft_GrSurfaceOrigin;
|
||||
desc.fWidth = 256;
|
||||
desc.fHeight = 256;
|
||||
desc.fConfig = kRGBA_8888_GrPixelConfig;
|
||||
SkBackingFit::kExact, kRenderSize, kRenderSize, kRGBA_8888_GrPixelConfig, nullptr);
|
||||
|
||||
sk_sp<GrTextureProxy> proxies[2];
|
||||
|
||||
// Put premul data into the RGBA texture that the test FPs can optionally use.
|
||||
std::unique_ptr<GrColor[]> rgbaData(new GrColor[256 * 256]);
|
||||
for (int y = 0; y < 256; ++y) {
|
||||
for (int x = 0; x < 256; ++x) {
|
||||
rgbaData.get()[256 * y + x] =
|
||||
texel_color(random.nextULessThan(256), random.nextULessThan(256));
|
||||
}
|
||||
}
|
||||
proxies[0] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kYes,
|
||||
rgbaData.get(), 256 * sizeof(GrColor));
|
||||
|
||||
// Put random values into the alpha texture that the test FPs can optionally use.
|
||||
desc.fConfig = kAlpha_8_GrPixelConfig;
|
||||
std::unique_ptr<uint8_t[]> alphaData(new uint8_t[256 * 256]);
|
||||
for (int y = 0; y < 256; ++y) {
|
||||
for (int x = 0; x < 256; ++x) {
|
||||
alphaData.get()[256 * y + x] = random.nextULessThan(256);
|
||||
}
|
||||
}
|
||||
proxies[1] = GrSurfaceProxy::MakeDeferred(context->resourceProvider(), desc, SkBudgeted::kYes,
|
||||
alphaData.get(), 256);
|
||||
|
||||
if (!proxies[0] || !proxies[1]) {
|
||||
if (!init_test_textures(context, &random, proxies)) {
|
||||
ERRORF(reporter, "Could not create test textures");
|
||||
return;
|
||||
}
|
||||
|
||||
GrProcessorTestData testData(&random, context, rtc.get(), proxies);
|
||||
|
||||
// Use a different array of premul colors for the output of the fragment processor that preceeds
|
||||
// the fragment processor under test.
|
||||
for (int y = 0; y < 256; ++y) {
|
||||
for (int x = 0; x < 256; ++x) {
|
||||
rgbaData.get()[256 * y + x] = texel_color(x, y);
|
||||
}
|
||||
}
|
||||
desc.fConfig = kRGBA_8888_GrPixelConfig;
|
||||
auto inputTexture = make_input_texture(context, kRenderSize, kRenderSize);
|
||||
|
||||
sk_sp<GrTextureProxy> dataProxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
|
||||
desc, SkBudgeted::kYes,
|
||||
rgbaData.get(),
|
||||
256 * sizeof(GrColor));
|
||||
|
||||
// Because processors factories configure themselves in random ways, this is not exhaustive.
|
||||
std::unique_ptr<GrColor> readData(new GrColor[kRenderSize * kRenderSize]);
|
||||
// Because processor factories configure themselves in random ways, this is not exhaustive.
|
||||
for (int i = 0; i < FPFactory::Count(); ++i) {
|
||||
int timesToInvokeFactory = 5;
|
||||
// Increase the number of attempts if the FP has child FPs since optimizations likely depend
|
||||
@ -373,11 +388,11 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, repor
|
||||
!fp->compatibleWithCoverageAsAlpha()) {
|
||||
continue;
|
||||
}
|
||||
test_draw_op(rtc.get(), fp, dataProxy);
|
||||
memset(rgbaData.get(), 0x0, sizeof(GrColor) * 256 * 256);
|
||||
rtc->readPixels(
|
||||
SkImageInfo::Make(256, 256, kRGBA_8888_SkColorType, kPremul_SkAlphaType),
|
||||
rgbaData.get(), 0, 0, 0);
|
||||
test_draw_op(rtc.get(), fp, inputTexture);
|
||||
memset(readData.get(), 0x0, sizeof(GrColor) * kRenderSize * kRenderSize);
|
||||
rtc->readPixels(SkImageInfo::Make(kRenderSize, kRenderSize, kRGBA_8888_SkColorType,
|
||||
kPremul_SkAlphaType),
|
||||
readData.get(), 0, 0, 0);
|
||||
bool passing = true;
|
||||
if (0) { // Useful to see what FPs are being tested.
|
||||
SkString children;
|
||||
@ -390,10 +405,10 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, repor
|
||||
}
|
||||
SkDebugf("%s %s\n", fp->name(), children.c_str());
|
||||
}
|
||||
for (int y = 0; y < 256 && passing; ++y) {
|
||||
for (int x = 0; x < 256 && passing; ++x) {
|
||||
GrColor input = texel_color(x, y);
|
||||
GrColor output = rgbaData.get()[y * 256 + x];
|
||||
for (int y = 0; y < kRenderSize && passing; ++y) {
|
||||
for (int x = 0; x < kRenderSize && passing; ++x) {
|
||||
GrColor input = input_texel_color(x, y);
|
||||
GrColor output = readData.get()[y * kRenderSize + x];
|
||||
if (fp->compatibleWithCoverageAsAlpha()) {
|
||||
// A modulating processor is allowed to modulate either the input color or
|
||||
// just the input alpha.
|
||||
@ -415,7 +430,7 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, repor
|
||||
passing = false;
|
||||
}
|
||||
}
|
||||
GrColor4f input4f = texel_color4f(x, y);
|
||||
GrColor4f input4f = input_texel_color4f(x, y);
|
||||
GrColor4f output4f = GrColor4f::FromGrColor(output);
|
||||
GrColor4f expected4f;
|
||||
if (fp->hasConstantOutputForConstantInput(input4f, &expected4f)) {
|
||||
@ -455,6 +470,74 @@ DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorOptimizationValidationTest, repor
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that fragment processors returned by GrFragmentProcessor::clone() are equivalent to their
|
||||
// progenitors.
|
||||
DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(ProcessorCloneTest, reporter, ctxInfo) {
|
||||
GrContext* context = ctxInfo.grContext();
|
||||
|
||||
SkRandom random;
|
||||
|
||||
// Make the destination context for the test.
|
||||
static constexpr int kRenderSize = 1024;
|
||||
sk_sp<GrRenderTargetContext> rtc = context->makeDeferredRenderTargetContext(
|
||||
SkBackingFit::kExact, kRenderSize, kRenderSize, kRGBA_8888_GrPixelConfig, nullptr);
|
||||
|
||||
sk_sp<GrTextureProxy> proxies[2];
|
||||
if (!init_test_textures(context, &random, proxies)) {
|
||||
ERRORF(reporter, "Could not create test textures");
|
||||
return;
|
||||
}
|
||||
GrProcessorTestData testData(&random, context, rtc.get(), proxies);
|
||||
|
||||
auto inputTexture = make_input_texture(context, kRenderSize, kRenderSize);
|
||||
std::unique_ptr<GrColor[]> readData1(new GrColor[kRenderSize * kRenderSize]);
|
||||
std::unique_ptr<GrColor[]> readData2(new GrColor[kRenderSize * kRenderSize]);
|
||||
auto readInfo = SkImageInfo::Make(kRenderSize, kRenderSize, kRGBA_8888_SkColorType,
|
||||
kPremul_SkAlphaType);
|
||||
|
||||
// Because processor factories configure themselves in random ways, this is not exhaustive.
|
||||
for (int i = 0; i < GrFragmentProcessorTestFactory::Count(); ++i) {
|
||||
static constexpr int kTimesToInvokeFactory = 10;
|
||||
for (int j = 0; j < kTimesToInvokeFactory; ++j) {
|
||||
auto fp = GrFragmentProcessorTestFactory::MakeIdx(i, &testData);
|
||||
auto clone = fp->clone();
|
||||
// Currently clone() is allowed to fail.
|
||||
if (!clone) {
|
||||
continue;
|
||||
}
|
||||
if (!fp->instantiate(context->resourceProvider()) ||
|
||||
!clone->instantiate(context->resourceProvider())) {
|
||||
continue;
|
||||
}
|
||||
// Draw with original and read back the results.
|
||||
test_draw_op(rtc.get(), fp, inputTexture);
|
||||
memset(readData1.get(), 0x0, sizeof(GrColor) * kRenderSize * kRenderSize);
|
||||
rtc->readPixels(readInfo, readData1.get(), 0, 0, 0);
|
||||
|
||||
// Draw with clone and read back the results.
|
||||
test_draw_op(rtc.get(), clone, inputTexture);
|
||||
memset(readData2.get(), 0x0, sizeof(GrColor) * kRenderSize * kRenderSize);
|
||||
rtc->readPixels(readInfo, readData2.get(), 0, 0, 0);
|
||||
|
||||
// Check that the results are the same.
|
||||
bool passing = true;
|
||||
for (int y = 0; y < kRenderSize && passing; ++y) {
|
||||
for (int x = 0; x < kRenderSize && passing; ++x) {
|
||||
int idx = y * kRenderSize + x;
|
||||
if (readData1[idx] != readData2[idx]) {
|
||||
ERRORF(reporter,
|
||||
"Processor %s made clone produced different output. "
|
||||
"Input color: 0x%08x, Original Output Color: 0x%08x, "
|
||||
"Clone Output Color: 0x%08x..",
|
||||
fp->name(), input_texel_color(x, y), readData1[idx], readData2[idx]);
|
||||
passing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GR_TEST_UTILS
|
||||
#endif // SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
|
||||
#endif // SK_SUPPORT_GPU
|
||||
|
Loading…
Reference in New Issue
Block a user