blob: e4989de507cb9851326ba218edda88ab659ef4ff [file] [log] [blame]
bsalomon@google.coma04e8e82012-08-27 12:53:13 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "GrConfigConversionEffect.h"
Brian Salomonc65aec92017-03-09 09:03:58 -05009#include "../private/GrGLSL.h"
10#include "GrClip.h"
bsalomon@google.comb1456d72012-11-02 18:23:45 +000011#include "GrContext.h"
Brian Osman11052242016-10-27 14:47:55 -040012#include "GrRenderTargetContext.h"
joshualitteb2a6762014-12-04 11:35:33 -080013#include "SkMatrix.h"
egdaniel64c47282015-11-13 06:54:19 -080014#include "glsl/GrGLSLFragmentProcessor.h"
egdaniel2d721d32015-11-11 13:06:05 -080015#include "glsl/GrGLSLFragmentShaderBuilder.h"
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000016
egdaniel64c47282015-11-13 06:54:19 -080017class GrGLConfigConversionEffect : public GrGLSLFragmentProcessor {
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000018public:
robertphillips9cdb9922016-02-03 12:25:40 -080019 void emitCode(EmitArgs& args) override {
bsalomon6c9cd552016-01-22 07:17:34 -080020 const GrConfigConversionEffect& cce = args.fFp.cast<GrConfigConversionEffect>();
cdalton85285412016-02-18 12:37:07 -080021 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
joshualitt30ba4362014-08-21 20:18:45 -070022
Brian Osmanee805322017-04-05 10:09:00 -040023 // Use highp throughout the shader to avoid some precision issues on specific GPUs.
24 fragBuilder->elevateDefaultPrecision(kHigh_GrSLPrecision);
joshualitt30ba4362014-08-21 20:18:45 -070025
Brian Osmanee805322017-04-05 10:09:00 -040026 if (nullptr == args.fInputColor) {
27 // could optimize this case, but we aren't for now.
28 args.fInputColor = "vec4(1)";
29 }
Brian Osman9f903e42017-04-10 13:42:48 -040030
31 // Aggressively round to the nearest exact (N / 255) floating point value. This lets us
32 // find a round-trip preserving pair on some GPUs that do odd byte to float conversion.
Brian Osman28804f32017-04-20 10:24:36 -040033 fragBuilder->codeAppendf("vec4 color = floor(%s * 255.0 + 0.5) / 255.0;", args.fInputColor);
changjun.yangcecc91c2014-08-19 18:24:30 -070034
Brian Osmanee805322017-04-05 10:09:00 -040035 switch (cce.pmConversion()) {
Brian Osman28804f32017-04-20 10:24:36 -040036 case GrConfigConversionEffect::kToPremul_PMConversion:
Brian Osman9f903e42017-04-10 13:42:48 -040037 fragBuilder->codeAppend(
38 "color.rgb = floor(color.rgb * color.a * 255.0 + 0.5) / 255.0;");
39 break;
40
Brian Osman28804f32017-04-20 10:24:36 -040041 case GrConfigConversionEffect::kToUnpremul_PMConversion:
Brian Osman9f903e42017-04-10 13:42:48 -040042 fragBuilder->codeAppend(
43 "color.rgb = color.a <= 0.0 ? vec3(0,0,0) : floor(color.rgb / color.a * 255.0 + 0.5) / 255.0;");
44 break;
45
Brian Osmance425512017-03-22 14:37:50 -040046 default:
47 SkFAIL("Unknown conversion op.");
48 break;
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000049 }
Brian Osmanee805322017-04-05 10:09:00 -040050 fragBuilder->codeAppendf("%s = color;", args.fOutputColor);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000051 }
52
Brian Salomon94efbf52016-11-29 13:43:05 -050053 static inline void GenKey(const GrProcessor& processor, const GrShaderCaps&,
joshualittb0a8a372014-09-23 09:50:21 -070054 GrProcessorKeyBuilder* b) {
bsalomon6c9cd552016-01-22 07:17:34 -080055 const GrConfigConversionEffect& cce = processor.cast<GrConfigConversionEffect>();
Brian Osmance425512017-03-22 14:37:50 -040056 uint32_t key = cce.pmConversion();
bsalomon63e99f72014-07-21 08:03:14 -070057 b->add32(key);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000058 }
59
60private:
egdaniel64c47282015-11-13 06:54:19 -080061 typedef GrGLSLFragmentProcessor INHERITED;
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000062
63};
64
65///////////////////////////////////////////////////////////////////////////////
Brian Osmanee805322017-04-05 10:09:00 -040066
67GrConfigConversionEffect::GrConfigConversionEffect(PMConversion pmConversion)
68 : INHERITED(kNone_OptimizationFlags)
Brian Osman31f96a62017-03-24 18:27:56 +000069 , fPMConversion(pmConversion) {
70 this->initClassID<GrConfigConversionEffect>();
Robert Phillips757914d2017-01-25 15:48:30 -050071}
72
bsalomon0e08fc12014-10-15 08:19:04 -070073bool GrConfigConversionEffect::onIsEqual(const GrFragmentProcessor& s) const {
joshualitt49586be2014-09-16 08:21:41 -070074 const GrConfigConversionEffect& other = s.cast<GrConfigConversionEffect>();
Brian Osmance425512017-03-22 14:37:50 -040075 return other.fPMConversion == fPMConversion;
bsalomon@google.com68b58c92013-01-17 16:50:08 +000076}
77
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000078///////////////////////////////////////////////////////////////////////////////
79
joshualittb0a8a372014-09-23 09:50:21 -070080GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrConfigConversionEffect);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000081
Hal Canary6f6961e2017-01-31 13:50:44 -050082#if GR_TEST_UTILS
bungeman06ca8ec2016-06-09 08:01:03 -070083sk_sp<GrFragmentProcessor> GrConfigConversionEffect::TestCreate(GrProcessorTestData* d) {
joshualitt0067ff52015-07-08 14:26:19 -070084 PMConversion pmConv = static_cast<PMConversion>(d->fRandom->nextULessThan(kPMConversionCnt));
Brian Osmanee805322017-04-05 10:09:00 -040085 return sk_sp<GrFragmentProcessor>(new GrConfigConversionEffect(pmConv));
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000086}
Hal Canary6f6961e2017-01-31 13:50:44 -050087#endif
bsalomon@google.coma04e8e82012-08-27 12:53:13 +000088
89///////////////////////////////////////////////////////////////////////////////
joshualitteb2a6762014-12-04 11:35:33 -080090
Brian Salomon94efbf52016-11-29 13:43:05 -050091void GrConfigConversionEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
egdaniel57d3b032015-11-13 11:57:27 -080092 GrProcessorKeyBuilder* b) const {
joshualitteb2a6762014-12-04 11:35:33 -080093 GrGLConfigConversionEffect::GenKey(*this, caps, b);
94}
95
egdaniel57d3b032015-11-13 11:57:27 -080096GrGLSLFragmentProcessor* GrConfigConversionEffect::onCreateGLSLInstance() const {
bsalomon6c9cd552016-01-22 07:17:34 -080097 return new GrGLConfigConversionEffect();
joshualitteb2a6762014-12-04 11:35:33 -080098}
99
robertphillipse85a32d2015-02-10 08:16:55 -0800100
Brian Osman28804f32017-04-20 10:24:36 -0400101bool GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context) {
bsalomon49b264c2016-07-19 08:38:09 -0700102 static constexpr int kSize = 256;
103 static constexpr GrPixelConfig kConfig = kRGBA_8888_GrPixelConfig;
104 SkAutoTMalloc<uint32_t> data(kSize * kSize * 3);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000105 uint32_t* srcData = data.get();
bsalomon49b264c2016-07-19 08:38:09 -0700106 uint32_t* firstRead = data.get() + kSize * kSize;
107 uint32_t* secondRead = data.get() + 2 * kSize * kSize;
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000108
109 // Fill with every possible premultiplied A, color channel value. There will be 256-y duplicate
110 // values in row y. We set r,g, and b to the same value since they are handled identically.
bsalomon49b264c2016-07-19 08:38:09 -0700111 for (int y = 0; y < kSize; ++y) {
112 for (int x = 0; x < kSize; ++x) {
113 uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[kSize*y + x]);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000114 color[3] = y;
commit-bot@chromium.org972f9cd2014-03-28 17:58:28 +0000115 color[2] = SkTMin(x, y);
116 color[1] = SkTMin(x, y);
117 color[0] = SkTMin(x, y);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000118 }
119 }
120
Robert Phillipsc949ce92017-01-19 16:59:04 -0500121 const SkImageInfo ii = SkImageInfo::Make(kSize, kSize,
122 kRGBA_8888_SkColorType, kPremul_SkAlphaType);
123
Robert Phillipsdd3b3f42017-04-24 10:57:28 -0400124 sk_sp<GrRenderTargetContext> readRTC(context->makeDeferredRenderTargetContext(
125 SkBackingFit::kExact,
Brian Osman693a5402016-10-27 15:13:22 -0400126 kSize, kSize,
127 kConfig, nullptr));
Robert Phillipsdd3b3f42017-04-24 10:57:28 -0400128 sk_sp<GrRenderTargetContext> tempRTC(context->makeDeferredRenderTargetContext(
129 SkBackingFit::kExact,
Brian Osman693a5402016-10-27 15:13:22 -0400130 kSize, kSize,
131 kConfig, nullptr));
Brian Osman28804f32017-04-20 10:24:36 -0400132 if (!readRTC || !readRTC->asTextureProxy() || !tempRTC) {
133 return false;
bsalomon49b264c2016-07-19 08:38:09 -0700134 }
bsalomonf2703d82014-10-28 14:33:06 -0700135 GrSurfaceDesc desc;
bsalomon49b264c2016-07-19 08:38:09 -0700136 desc.fWidth = kSize;
137 desc.fHeight = kSize;
138 desc.fConfig = kConfig;
Robert Phillips757914d2017-01-25 15:48:30 -0500139
Robert Phillipsfbcef6e2017-06-15 12:07:18 -0400140 sk_sp<GrTextureProxy> dataProxy = GrSurfaceProxy::MakeDeferred(context->resourceProvider(),
141 desc,
Brian Osmance425512017-03-22 14:37:50 -0400142 SkBudgeted::kYes, data, 0);
Robert Phillips2f493142017-03-02 18:18:38 -0500143 if (!dataProxy) {
Brian Osman28804f32017-04-20 10:24:36 -0400144 return false;
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000145 }
146
Brian Osman28804f32017-04-20 10:24:36 -0400147 static const SkRect kRect = SkRect::MakeIWH(kSize, kSize);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000148
Brian Osman28804f32017-04-20 10:24:36 -0400149 // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
150 // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
151 // We then verify that two reads produced the same values.
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000152
Brian Osman28804f32017-04-20 10:24:36 -0400153 GrPaint paint1;
154 GrPaint paint2;
155 GrPaint paint3;
156 sk_sp<GrFragmentProcessor> pmToUPM(new GrConfigConversionEffect(kToUnpremul_PMConversion));
157 sk_sp<GrFragmentProcessor> upmToPM(new GrConfigConversionEffect(kToPremul_PMConversion));
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000158
Robert Phillipsfbcef6e2017-06-15 12:07:18 -0400159 paint1.addColorTextureProcessor(dataProxy, nullptr, SkMatrix::I());
Brian Osman28804f32017-04-20 10:24:36 -0400160 paint1.addColorFragmentProcessor(pmToUPM);
161 paint1.setPorterDuffXPFactory(SkBlendMode::kSrc);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000162
Brian Osman28804f32017-04-20 10:24:36 -0400163 readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kRect, kRect);
164 if (!readRTC->readPixels(ii, firstRead, 0, 0, 0)) {
165 return false;
166 }
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000167
Robert Phillipsfbcef6e2017-06-15 12:07:18 -0400168 paint2.addColorTextureProcessor(readRTC->asTextureProxyRef(), nullptr,
Brian Osman28804f32017-04-20 10:24:36 -0400169 SkMatrix::I());
170 paint2.addColorFragmentProcessor(std::move(upmToPM));
171 paint2.setPorterDuffXPFactory(SkBlendMode::kSrc);
robertphillipsff0ca5e2015-07-22 11:54:44 -0700172
Brian Osman28804f32017-04-20 10:24:36 -0400173 tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kRect, kRect);
robertphillipsff0ca5e2015-07-22 11:54:44 -0700174
Robert Phillipsfbcef6e2017-06-15 12:07:18 -0400175 paint3.addColorTextureProcessor(tempRTC->asTextureProxyRef(), nullptr,
Brian Osman28804f32017-04-20 10:24:36 -0400176 SkMatrix::I());
177 paint3.addColorFragmentProcessor(std::move(pmToUPM));
178 paint3.setPorterDuffXPFactory(SkBlendMode::kSrc);
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000179
Brian Osman28804f32017-04-20 10:24:36 -0400180 readRTC->fillRectToRect(GrNoClip(), std::move(paint3), GrAA::kNo, SkMatrix::I(), kRect, kRect);
robertphillipsff0ca5e2015-07-22 11:54:44 -0700181
Brian Osman28804f32017-04-20 10:24:36 -0400182 if (!readRTC->readPixels(ii, secondRead, 0, 0, 0)) {
183 return false;
184 }
commit-bot@chromium.org42dacab2013-07-13 17:24:24 +0000185
Brian Osman28804f32017-04-20 10:24:36 -0400186 for (int y = 0; y < kSize; ++y) {
187 for (int x = 0; x <= y; ++x) {
188 if (firstRead[kSize * y + x] != secondRead[kSize * y + x]) {
189 return false;
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000190 }
191 }
192 }
Brian Osman28804f32017-04-20 10:24:36 -0400193
194 return true;
bsalomon@google.coma04e8e82012-08-27 12:53:13 +0000195}
196
Brian Osmanee805322017-04-05 10:09:00 -0400197sk_sp<GrFragmentProcessor> GrConfigConversionEffect::Make(sk_sp<GrFragmentProcessor> fp,
198 PMConversion pmConversion) {
199 if (!fp) {
Brian Osman31f96a62017-03-24 18:27:56 +0000200 return nullptr;
201 }
Brian Osmanee805322017-04-05 10:09:00 -0400202 sk_sp<GrFragmentProcessor> ccFP(new GrConfigConversionEffect(pmConversion));
203 sk_sp<GrFragmentProcessor> fpPipeline[] = { fp, ccFP };
204 return GrFragmentProcessor::RunInSeries(fpPipeline, 2);
Robert Phillips757914d2017-01-25 15:48:30 -0500205}