Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2015 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 "GrYUVProvider.h"
      9 #include "GrClip.h"
     10 #include "GrContext.h"
     11 #include "GrContextPriv.h"
     12 #include "GrProxyProvider.h"
     13 #include "GrRenderTargetContext.h"
     14 #include "GrTextureProxy.h"
     15 #include "SkAutoMalloc.h"
     16 #include "SkCachedData.h"
     17 #include "SkRefCnt.h"
     18 #include "SkResourceCache.h"
     19 #include "SkYUVPlanesCache.h"
     20 #include "effects/GrNonlinearColorSpaceXformEffect.h"
     21 #include "effects/GrSRGBEffect.h"
     22 #include "effects/GrYUVtoRGBEffect.h"
     23 
     24 sk_sp<SkCachedData> init_provider(GrYUVProvider* provider, SkYUVPlanesCache::Info* yuvInfo,
     25                                   void* planes[3]) {
     26     sk_sp<SkCachedData> data;
     27     data.reset(SkYUVPlanesCache::FindAndRef(provider->onGetID(), yuvInfo));
     28 
     29     if (data.get()) {
     30         planes[0] = (void*)data->data();
     31         planes[1] = (uint8_t*)planes[0] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kY] *
     32                                            yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
     33         planes[2] = (uint8_t*)planes[1] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kU] *
     34                                            yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kU].fHeight);
     35     } else {
     36         // Fetch yuv plane sizes for memory allocation.
     37         if (!provider->onQueryYUV8(&yuvInfo->fSizeInfo, &yuvInfo->fColorSpace)) {
     38             return nullptr;
     39         }
     40 
     41         // Allocate the memory for YUV
     42         size_t totalSize(0);
     43         for (int i = 0; i < 3; i++) {
     44             totalSize += yuvInfo->fSizeInfo.fWidthBytes[i] * yuvInfo->fSizeInfo.fSizes[i].fHeight;
     45         }
     46         data.reset(SkResourceCache::NewCachedData(totalSize));
     47         planes[0] = data->writable_data();
     48         planes[1] = (uint8_t*)planes[0] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kY] *
     49                                            yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
     50         planes[2] = (uint8_t*)planes[1] + (yuvInfo->fSizeInfo.fWidthBytes[SkYUVSizeInfo::kU] *
     51                                            yuvInfo->fSizeInfo.fSizes[SkYUVSizeInfo::kU].fHeight);
     52 
     53         // Get the YUV planes.
     54         if (!provider->onGetYUV8Planes(yuvInfo->fSizeInfo, planes)) {
     55             return nullptr;
     56         }
     57 
     58         // Decoding is done, cache the resulting YUV planes
     59         SkYUVPlanesCache::Add(provider->onGetID(), data.get(), yuvInfo);
     60     }
     61     return data;
     62 }
     63 
     64 void GrYUVProvider::YUVGen_DataReleaseProc(const void*, void* data) {
     65     SkCachedData* cachedData = static_cast<SkCachedData*>(data);
     66     SkASSERT(cachedData);
     67     cachedData->unref();
     68 }
     69 
     70 sk_sp<GrTextureProxy> GrYUVProvider::refAsTextureProxy(GrContext* ctx, const GrSurfaceDesc& desc,
     71                                                        const SkColorSpace* srcColorSpace,
     72                                                        const SkColorSpace* dstColorSpace) {
     73     SkYUVPlanesCache::Info yuvInfo;
     74     void* planes[3];
     75 
     76     sk_sp<SkCachedData>  dataStorage = init_provider(this, &yuvInfo, planes);
     77     if (!dataStorage) {
     78         return nullptr;
     79     }
     80 
     81     sk_sp<GrTextureProxy> yuvTextureProxies[3];
     82     for (int i = 0; i < 3; i++) {
     83         int componentWidth  = yuvInfo.fSizeInfo.fSizes[i].fWidth;
     84         int componentHeight = yuvInfo.fSizeInfo.fSizes[i].fHeight;
     85         // If the sizes of the components are not all the same we choose to create exact-match
     86         // textures for the smaller onces rather than add a texture domain to the draw.
     87         // TODO: revisit this decision to imporve texture reuse?
     88         SkBackingFit fit =
     89                 (componentWidth  != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth) ||
     90                 (componentHeight != yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight)
     91                     ? SkBackingFit::kExact : SkBackingFit::kApprox;
     92 
     93         SkImageInfo imageInfo = SkImageInfo::MakeA8(componentWidth, componentHeight);
     94         SkPixmap pixmap(imageInfo, planes[i], yuvInfo.fSizeInfo.fWidthBytes[i]);
     95         SkCachedData* dataStoragePtr = dataStorage.get();
     96         // We grab a ref to cached yuv data. When the SkImage we create below goes away it will call
     97         // the YUVGen_DataReleaseProc which will release this ref.
     98         // DDL TODO: Currently we end up creating a lazy proxy that will hold onto a ref to the
     99         // SkImage in its lambda. This means that we'll keep the ref on the YUV data around for the
    100         // life time of the proxy and not just upload. For non-DDL draws we should look into
    101         // releasing this SkImage after uploads (by deleting the lambda after instantiation).
    102         dataStoragePtr->ref();
    103         sk_sp<SkImage> yuvImage = SkImage::MakeFromRaster(pixmap, YUVGen_DataReleaseProc,
    104                                                           dataStoragePtr);
    105 
    106         auto proxyProvider = ctx->contextPriv().proxyProvider();
    107         yuvTextureProxies[i] = proxyProvider->createTextureProxy(yuvImage, kNone_GrSurfaceFlags,
    108                                                                  kTopLeft_GrSurfaceOrigin,
    109                                                                  1, SkBudgeted::kYes, fit);
    110     }
    111 
    112     // We never want to perform color-space conversion during the decode. However, if the proxy
    113     // config is sRGB then we must use a sRGB color space.
    114     sk_sp<SkColorSpace> colorSpace;
    115     if (GrPixelConfigIsSRGB(desc.fConfig)) {
    116         colorSpace = SkColorSpace::MakeSRGB();
    117     }
    118     // TODO: investigate preallocating mip maps here
    119     sk_sp<GrRenderTargetContext> renderTargetContext(ctx->makeDeferredRenderTargetContext(
    120             SkBackingFit::kExact, desc.fWidth, desc.fHeight, desc.fConfig, std::move(colorSpace),
    121             desc.fSampleCnt, GrMipMapped::kNo, kTopLeft_GrSurfaceOrigin));
    122     if (!renderTargetContext) {
    123         return nullptr;
    124     }
    125 
    126     GrPaint paint;
    127     auto yuvToRgbProcessor =
    128             GrYUVtoRGBEffect::Make(std::move(yuvTextureProxies[0]),
    129                                    std::move(yuvTextureProxies[1]),
    130                                    std::move(yuvTextureProxies[2]),
    131                                    yuvInfo.fSizeInfo.fSizes, yuvInfo.fColorSpace, false);
    132     paint.addColorFragmentProcessor(std::move(yuvToRgbProcessor));
    133 
    134     // If we're decoding an sRGB image, the result of our linear math on the YUV planes is already
    135     // in sRGB. (The encoding is just math on bytes, with no concept of color spaces.) So, we need
    136     // to output the results of that math directly to the buffer that we will then consider sRGB.
    137     // If we have sRGB write control, we can just tell the HW not to do the Linear -> sRGB step.
    138     // Otherwise, we do our shader math to go from YUV -> sRGB, manually convert sRGB -> Linear,
    139     // then let the HW convert Linear -> sRGB.
    140     if (GrPixelConfigIsSRGB(desc.fConfig)) {
    141         if (ctx->caps()->srgbWriteControl()) {
    142             paint.setDisableOutputConversionToSRGB(true);
    143         } else {
    144             paint.addColorFragmentProcessor(GrSRGBEffect::Make(GrSRGBEffect::Mode::kSRGBToLinear,
    145                                                                GrSRGBEffect::Alpha::kOpaque));
    146         }
    147     }
    148 
    149     // If the caller expects the pixels in a different color space than the one from the image,
    150     // apply a color conversion to do this.
    151     std::unique_ptr<GrFragmentProcessor> colorConversionProcessor =
    152             GrNonlinearColorSpaceXformEffect::Make(srcColorSpace, dstColorSpace);
    153     if (colorConversionProcessor) {
    154         paint.addColorFragmentProcessor(std::move(colorConversionProcessor));
    155     }
    156 
    157     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
    158     const SkRect r = SkRect::MakeIWH(yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth,
    159                                      yuvInfo.fSizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight);
    160 
    161     renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), r);
    162 
    163     return renderTargetContext->asTextureProxyRef();
    164 }
    165