1/* 2 * Copyright 2018 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@header { 9 #include "include/gpu/GrContext.h" 10 #include "src/gpu/GrClip.h" 11 #include "src/gpu/GrContextPriv.h" 12 #include "src/gpu/GrProxyProvider.h" 13 #include "src/gpu/GrRenderTargetContext.h" 14} 15 16@class { 17 static bool TestForPreservingPMConversions(GrContext* context) { 18 static constexpr int kSize = 256; 19 static constexpr GrColorType kColorType = GrColorType::kRGBA_8888; 20 SkAutoTMalloc<uint32_t> data(kSize * kSize * 3); 21 uint32_t* srcData = data.get(); 22 uint32_t* firstRead = data.get() + kSize * kSize; 23 uint32_t* secondRead = data.get() + 2 * kSize * kSize; 24 25 // Fill with every possible premultiplied A, color channel value. There will be 256-y 26 // duplicate values in row y. We set r, g, and b to the same value since they are handled 27 // identically. 28 for (int y = 0; y < kSize; ++y) { 29 for (int x = 0; x < kSize; ++x) { 30 uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[kSize*y + x]); 31 color[3] = y; 32 color[2] = SkTMin(x, y); 33 color[1] = SkTMin(x, y); 34 color[0] = SkTMin(x, y); 35 } 36 } 37 memset(firstRead, 0, kSize * kSize * sizeof(uint32_t)); 38 memset(secondRead, 0, kSize * kSize * sizeof(uint32_t)); 39 40 const SkImageInfo ii = SkImageInfo::Make(kSize, kSize, 41 kRGBA_8888_SkColorType, kPremul_SkAlphaType); 42 43 sk_sp<GrRenderTargetContext> readRTC( 44 context->priv().makeDeferredRenderTargetContext(SkBackingFit::kExact, 45 kSize, kSize, 46 kColorType, nullptr)); 47 sk_sp<GrRenderTargetContext> tempRTC( 48 context->priv().makeDeferredRenderTargetContext(SkBackingFit::kExact, 49 kSize, kSize, 50 kColorType, nullptr)); 51 if (!readRTC || !readRTC->asTextureProxy() || !tempRTC) { 52 return false; 53 } 54 // Adding discard to appease vulkan validation warning about loading uninitialized data on 55 // draw 56 readRTC->discard(); 57 58 GrProxyProvider* proxyProvider = context->priv().proxyProvider(); 59 60 SkPixmap pixmap(ii, srcData, 4 * kSize); 61 62 // This function is only ever called if we are in a GrContext that has a GrGpu since we are 63 // calling read pixels here. Thus the pixel data will be uploaded immediately and we don't 64 // need to keep the pixel data alive in the proxy. Therefore the ReleaseProc is nullptr. 65 sk_sp<SkImage> image = SkImage::MakeFromRaster(pixmap, nullptr, nullptr); 66 67 sk_sp<GrTextureProxy> dataProxy = proxyProvider->createTextureProxy(std::move(image), 68 1, 69 SkBudgeted::kYes, 70 SkBackingFit::kExact); 71 if (!dataProxy) { 72 return false; 73 } 74 75 static const SkRect kRect = SkRect::MakeIWH(kSize, kSize); 76 77 // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw 78 // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data. 79 // We then verify that two reads produced the same values. 80 81 GrPaint paint1; 82 GrPaint paint2; 83 GrPaint paint3; 84 std::unique_ptr<GrFragmentProcessor> pmToUPM( 85 new GrConfigConversionEffect(PMConversion::kToUnpremul)); 86 std::unique_ptr<GrFragmentProcessor> upmToPM( 87 new GrConfigConversionEffect(PMConversion::kToPremul)); 88 89 paint1.addColorTextureProcessor(dataProxy, SkMatrix::I()); 90 paint1.addColorFragmentProcessor(pmToUPM->clone()); 91 paint1.setPorterDuffXPFactory(SkBlendMode::kSrc); 92 93 readRTC->fillRectToRect(GrNoClip(), std::move(paint1), GrAA::kNo, SkMatrix::I(), kRect, 94 kRect); 95 if (!readRTC->readPixels(ii, firstRead, 0, {0, 0})) { 96 return false; 97 } 98 99 // Adding discard to appease vulkan validation warning about loading uninitialized data on 100 // draw 101 tempRTC->discard(); 102 103 paint2.addColorTextureProcessor(readRTC->asTextureProxyRef(), SkMatrix::I()); 104 paint2.addColorFragmentProcessor(std::move(upmToPM)); 105 paint2.setPorterDuffXPFactory(SkBlendMode::kSrc); 106 107 tempRTC->fillRectToRect(GrNoClip(), std::move(paint2), GrAA::kNo, SkMatrix::I(), kRect, 108 kRect); 109 110 paint3.addColorTextureProcessor(tempRTC->asTextureProxyRef(), SkMatrix::I()); 111 paint3.addColorFragmentProcessor(std::move(pmToUPM)); 112 paint3.setPorterDuffXPFactory(SkBlendMode::kSrc); 113 114 readRTC->fillRectToRect(GrNoClip(), std::move(paint3), GrAA::kNo, SkMatrix::I(), kRect, 115 kRect); 116 117 if (!readRTC->readPixels(ii, secondRead, 0, {0, 0})) { 118 return false; 119 } 120 121 for (int y = 0; y < kSize; ++y) { 122 for (int x = 0; x <= y; ++x) { 123 if (firstRead[kSize * y + x] != secondRead[kSize * y + x]) { 124 return false; 125 } 126 } 127 } 128 129 return true; 130 } 131} 132 133@make { 134 static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> fp, 135 PMConversion pmConversion) { 136 if (!fp) { 137 return nullptr; 138 } 139 std::unique_ptr<GrFragmentProcessor> ccFP(new GrConfigConversionEffect(pmConversion)); 140 std::unique_ptr<GrFragmentProcessor> fpPipeline[] = { std::move(fp), std::move(ccFP) }; 141 return GrFragmentProcessor::RunInSeries(fpPipeline, 2); 142 } 143} 144 145layout(key) in PMConversion pmConversion; 146 147@emitCode { 148 fragBuilder->forceHighPrecision(); 149} 150 151void main() { 152 // Aggressively round to the nearest exact (N / 255) floating point value. This lets us find a 153 // round-trip preserving pair on some GPUs that do odd byte to float conversion. 154 sk_OutColor = floor(sk_InColor * 255 + 0.5) / 255; 155 156 @switch (pmConversion) { 157 case PMConversion::kToPremul: 158 sk_OutColor.rgb = floor(sk_OutColor.rgb * sk_OutColor.a * 255 + 0.5) / 255; 159 break; 160 161 case PMConversion::kToUnpremul: 162 sk_OutColor.rgb = sk_OutColor.a <= 0.0 ? 163 half3(0) : 164 floor(sk_OutColor.rgb / sk_OutColor.a * 255 + 0.5) / 255; 165 break; 166 } 167} 168 169@test(data) { 170 PMConversion pmConv = static_cast<PMConversion>(data->fRandom->nextULessThan( 171 (int) PMConversion::kPMConversionCnt)); 172 return std::unique_ptr<GrFragmentProcessor>(new GrConfigConversionEffect(pmConv)); 173} 174