Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2016 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 "Test.h"
      9 #if SK_SUPPORT_GPU
     10 #include "GrCaps.h"
     11 #include "GrClip.h"
     12 #include "GrContext.h"
     13 #include "GrContextPriv.h"
     14 #include "GrProxyProvider.h"
     15 #include "GrRenderTargetContext.h"
     16 #include "SkCanvas.h"
     17 #include "SkGr.h"
     18 #include "SkSurface.h"
     19 #include "gl/GrGLGpu.h"
     20 
     21 // using anonymous namespace because these functions are used as template params.
     22 namespace {
     23 /** convert 0..1 srgb value to 0..1 linear */
     24 float srgb_to_linear(float srgb) {
     25     if (srgb <= 0.04045f) {
     26         return srgb / 12.92f;
     27     } else {
     28         return powf((srgb + 0.055f) / 1.055f, 2.4f);
     29     }
     30 }
     31 
     32 /** convert 0..1 linear value to 0..1 srgb */
     33 float linear_to_srgb(float linear) {
     34     if (linear <= 0.0031308) {
     35         return linear * 12.92f;
     36     } else {
     37         return 1.055f * powf(linear, 1.f / 2.4f) - 0.055f;
     38     }
     39 }
     40 }
     41 
     42 static bool check_value(U8CPU value, U8CPU expected, U8CPU error) {
     43     if (value >= expected) {
     44         return (value - expected) <= error;
     45     } else {
     46         return (expected - value) <= error;
     47     }
     48 }
     49 
     50 void read_and_check_pixels(skiatest::Reporter* reporter, GrSurfaceContext* context,
     51                            U8CPU expected, const SkImageInfo& dstInfo,
     52                            U8CPU error, const char* subtestName) {
     53     int w = dstInfo.width();
     54     int h = dstInfo.height();
     55     SkAutoTMalloc<uint32_t> readData(w * h);
     56     memset(readData.get(), 0, sizeof(uint32_t) * w * h);
     57 
     58     if (!context->readPixels(dstInfo, readData.get(), 0, 0, 0)) {
     59         ERRORF(reporter, "Could not read pixels for %s.", subtestName);
     60         return;
     61     }
     62 
     63     for (int j = 0; j < h; ++j) {
     64         for (int i = 0; i < w; ++i) {
     65             uint32_t read = readData[j * w + i];
     66 
     67             bool success =
     68                 check_value(read & 0xff, expected, error) &&
     69                 check_value((read >> 8) & 0xff, expected, error) &&
     70                 check_value((read >> 16) & 0xff, expected, error);
     71 
     72             if (!success) {
     73                 ERRORF(reporter, "Expected 0xff%02x%02x%02x, read back as 0x%08x in %s at %d, %d.",
     74                        expected, expected, expected, read, subtestName, i, j);
     75                 return;
     76             }
     77         }
     78     }
     79 }
     80 
     81 DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(SRGBMipMaps, reporter, ctxInfo) {
     82     GrContext* context = ctxInfo.grContext();
     83     if (!context->caps()->srgbSupport()) {
     84         return;
     85     }
     86 
     87     const int rtS = 16;
     88     const int texS = rtS * 2;
     89 
     90     // Fill texture with a dither of black and 60% sRGB (~ 32.5% linear) gray. Although there is
     91     // only one likely failure mode (doing a direct downsample of the sRGB values), this pattern
     92     // maximizes the minimum error across all three conceivable failure modes:
     93     // 1) Likely incorrect:
     94     //      (A + B) / 2
     95     // 2) No input decode, decode output:
     96     //      linear_to_srgb((A + B) / 2)
     97     // 3) Decode input, no output encode:
     98     //      (srgb_to_linear(A) + srgb_to_linear(B)) / 2
     99 
    100     const U8CPU srgb60 = sk_float_round2int(0.6f * 255.0f);
    101     static const SkPMColor colors[2] = {
    102         SkPackARGB32(0xFF, srgb60, srgb60, srgb60),
    103         SkPackARGB32(0xFF, 0x00, 0x00, 0x00)
    104     };
    105     uint32_t texData[texS * texS];
    106     for (int y = 0; y < texS; ++y) {
    107         for (int x = 0; x < texS; ++x) {
    108             texData[y * texS + x] = colors[(x + y) % 2];
    109         }
    110     }
    111 
    112     // We can be pretty generous with the error detection, thanks to the choice of input.
    113     // The closest likely failure mode is off by > 0.1, so anything that encodes within
    114     // 10/255 of optimal is more than good enough for this test.
    115     const U8CPU expectedSRGB = sk_float_round2int(
    116         linear_to_srgb(srgb_to_linear(srgb60 / 255.0f) / 2.0f) * 255.0f);
    117     const U8CPU expectedLinear = srgb60 / 2;
    118     const U8CPU error = 10;
    119 
    120     const SkImageInfo iiSRGBA = SkImageInfo::Make(rtS, rtS, kRGBA_8888_SkColorType,
    121                                                   kPremul_SkAlphaType,
    122                                                   SkColorSpace::MakeSRGB());
    123     const SkImageInfo iiRGBA = SkImageInfo::Make(rtS, rtS, kRGBA_8888_SkColorType,
    124                                                  kPremul_SkAlphaType);
    125 
    126     // Create our test texture
    127     GrSurfaceDesc desc;
    128     desc.fFlags = kNone_GrSurfaceFlags;
    129     desc.fOrigin = kTopLeft_GrSurfaceOrigin;
    130     desc.fWidth = texS;
    131     desc.fHeight = texS;
    132     desc.fConfig = kSRGBA_8888_GrPixelConfig;
    133 
    134     GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
    135     sk_sp<GrTextureProxy> proxy = proxyProvider->createTextureProxy(
    136                                                                 desc, SkBudgeted::kNo, texData, 0);
    137 
    138     // Create two render target contexts (L32 and S32)
    139     sk_sp<SkColorSpace> srgbColorSpace = SkColorSpace::MakeSRGB();
    140     sk_sp<GrRenderTargetContext> l32RenderTargetContext = context->makeDeferredRenderTargetContext(
    141         SkBackingFit::kExact, rtS, rtS, kRGBA_8888_GrPixelConfig, nullptr);
    142     sk_sp<GrRenderTargetContext> s32RenderTargetContext = context->makeDeferredRenderTargetContext(
    143         SkBackingFit::kExact, rtS, rtS, kSRGBA_8888_GrPixelConfig, std::move(srgbColorSpace));
    144 
    145     SkRect rect = SkRect::MakeWH(SkIntToScalar(rtS), SkIntToScalar(rtS));
    146     GrNoClip noClip;
    147     GrPaint paint;
    148     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
    149     GrSamplerState mipMapSamplerState(GrSamplerState::WrapMode::kRepeat,
    150                                       GrSamplerState::Filter::kMipMap);
    151     paint.addColorTextureProcessor(std::move(proxy), SkMatrix::MakeScale(rtS), mipMapSamplerState);
    152 
    153     // 1) Draw texture to S32 surface (should generate/use sRGB mips)
    154     paint.setGammaCorrect(true);
    155     s32RenderTargetContext->drawRect(noClip, GrPaint::Clone(paint), GrAA::kNo, SkMatrix::I(), rect);
    156     read_and_check_pixels(reporter, s32RenderTargetContext.get(), expectedSRGB, iiSRGBA, error,
    157                           "first render of sRGB");
    158 
    159     // 2) Draw texture to L32 surface (should generate/use linear mips)
    160     paint.setGammaCorrect(false);
    161     l32RenderTargetContext->drawRect(noClip, GrPaint::Clone(paint), GrAA::kNo, SkMatrix::I(), rect);
    162 
    163     // Right now, this test only runs on GL (because Vulkan doesn't support legacy mip-mapping
    164     // skbug.com/5048). On GL, we may not have sRGB decode support. In that case, rendering sRGB
    165     // textures to a legacy surface produces nonsense, so this part of the test is meaningless.
    166     //
    167     // We also skip this part of the test on command buffer (via srgbDecodeDisableAffectsMipmaps),
    168     // because that implementation of the extension doesn't ensure that mips respect the setting.
    169     //
    170     // TODO: Once Vulkan supports legacy mip-mapping, we can promote this to GrCaps. Right now,
    171     // Vulkan has most of the functionality, but not the mip-mapping part that's being tested here.
    172     GrGLGpu* glGpu = static_cast<GrGLGpu*>(context->contextPriv().getGpu());
    173     if (glGpu->glCaps().srgbDecodeDisableSupport() &&
    174         glGpu->glCaps().srgbDecodeDisableAffectsMipmaps()) {
    175         read_and_check_pixels(reporter, l32RenderTargetContext.get(), expectedLinear, iiRGBA,
    176                               error, "re-render as linear");
    177     }
    178 
    179     // 3) Go back to sRGB
    180     paint.setGammaCorrect(true);
    181     s32RenderTargetContext->drawRect(noClip, std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
    182     read_and_check_pixels(reporter, s32RenderTargetContext.get(), expectedSRGB, iiSRGBA, error,
    183                           "re-render as sRGB");
    184 }
    185 #endif
    186