Texturing support for RECTANGLE textures.
authorbsalomon <bsalomon@google.com>
Wed, 20 Jan 2016 14:18:10 +0000 (06:18 -0800)
committerCommit bot <commit-bot@chromium.org>
Wed, 20 Jan 2016 14:18:10 +0000 (06:18 -0800)
Uses textureSize() to unnormalize texture coords when reading from a RECTANGLE texture. Because of this we also require a later GLSL version to use rectangle textures (1.40).

Note that this causes a issue with the bicubic effect. The texture coords seem to have poor precision and the result is ugly. textureSize() is intended as a workaround until effects can be updated to handle unnormalized coords themselves.

Updates places where we were looking for OpenGL version 3.2 for rectangle support. It was actually added in 3.1.

BUG=skia:3868

GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1594483003

Review URL: https://codereview.chromium.org/1594483003

gm/rectangletexture.cpp [new file with mode: 0644]
src/gpu/GrGpu.h
src/gpu/GrTextureParamsAdjuster.cpp
src/gpu/GrTextureParamsAdjuster.h
src/gpu/gl/GrGLCaps.cpp
src/gpu/gl/GrGLGpu.cpp
src/gpu/gl/GrGLGpu.h
src/gpu/gl/SkGLContext.cpp
src/gpu/glsl/GrGLSLShaderBuilder.cpp

diff --git a/gm/rectangletexture.cpp b/gm/rectangletexture.cpp
new file mode 100644 (file)
index 0000000..0889063
--- /dev/null
@@ -0,0 +1,191 @@
+
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This test only works with the GPU backend.
+
+#include "gm.h"
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "GrTest.h"
+#include "SkBitmap.h"
+#include "SkGradientShader.h"
+#include "SkImage.h"
+
+namespace skiagm {
+class RectangleTexture : public GM {
+public:
+    RectangleTexture() {
+        this->setBGColor(0xFFFFFFFF);
+    }
+
+protected:
+    SkString onShortName() override {
+        return SkString("rectangle_texture");
+    }
+
+    SkISize onISize() override {
+        return SkISize::Make(1035, 240);
+    }
+
+    void fillPixels(int width, int height, void *pixels) {
+        SkBitmap bmp;
+        bmp.setInfo(SkImageInfo::MakeN32(width, height, kOpaque_SkAlphaType), width * 4);
+        bmp.setPixels(pixels);
+        SkPaint paint;
+        SkCanvas canvas(bmp);
+        SkPoint pts[] = { {0, 0}, {0, SkIntToScalar(height)} };
+        SkColor colors0[] = { 0xFF1060B0 , 0xFF102030 };
+        paint.setShader(SkGradientShader::CreateLinear(pts, colors0, nullptr, 2,
+                                                       SkShader::kClamp_TileMode))->unref();
+        canvas.drawPaint(paint);
+
+        SkColor colors1[] = { 0xFFA07010 , 0xFFA02080 };
+        paint.setAntiAlias(true);
+        paint.setShader(SkGradientShader::CreateLinear(pts, colors1, nullptr, 2,
+                                                       SkShader::kClamp_TileMode))->unref();
+        canvas.drawCircle(SkIntToScalar(width) / 2, SkIntToScalar(height) / 2,
+                          SkIntToScalar(width + height) / 5, paint);
+    }
+
+    SkImage* createRectangleTextureImg(GrContext* context, int width, int height, void* pixels) {
+        if (!context) {
+            return nullptr;
+        }
+        GrGpu* gpu = context->getGpu();
+        if (!gpu) {
+            return nullptr;
+        }
+        const GrGLContext* glCtx = gpu->glContextForTesting();
+        if (!glCtx) {
+            return nullptr;
+        }
+
+        if (!(kGL_GrGLStandard == glCtx->standard() && glCtx->version() >= GR_GL_VER(3, 1)) &&
+            !glCtx->hasExtension("GL_ARB_texture_rectangle")) {
+            return nullptr;
+        }
+
+        // We will always create the GL texture as GL_RGBA, however the pixels uploaded may be
+        // be RGBA or BGRA, depending on how SkPMColor was compiled.
+        GrGLenum format;
+        if (kSkia8888_GrPixelConfig == kBGRA_8888_GrPixelConfig) {
+            format = GR_GL_BGRA;
+        } else {
+            SkASSERT(kSkia8888_GrPixelConfig == kRGBA_8888_GrPixelConfig);
+            format = GR_GL_RGBA;
+        }
+
+        const GrGLInterface* gl = glCtx->interface();
+// Useful for debugging whether errors result from use of RECTANGLE
+// #define TARGET GR_GL_TEXTURE_2D
+#define TARGET GR_GL_TEXTURE_RECTANGLE
+        GrGLuint id;
+        GR_GL_CALL(gl, GenTextures(1, &id));
+        GR_GL_CALL(gl, BindTexture(TARGET, id));
+        GR_GL_CALL(gl, TexParameteri(TARGET, GR_GL_TEXTURE_MAG_FILTER,
+                                     GR_GL_NEAREST));
+        GR_GL_CALL(gl, TexParameteri(TARGET, GR_GL_TEXTURE_MIN_FILTER,
+                                     GR_GL_NEAREST));
+        GR_GL_CALL(gl, TexParameteri(TARGET, GR_GL_TEXTURE_WRAP_S,
+                                     GR_GL_CLAMP_TO_EDGE));    
+        GR_GL_CALL(gl, TexParameteri(TARGET, GR_GL_TEXTURE_WRAP_T,
+                                     GR_GL_CLAMP_TO_EDGE));
+        GR_GL_CALL(gl, TexImage2D(TARGET, 0, GR_GL_RGBA, width, height, 0,
+                                  format, GR_GL_UNSIGNED_BYTE, pixels));
+
+
+        context->resetContext();
+        GrGLTextureInfo info;
+        info.fID = id;
+        info.fTarget = TARGET;
+        GrBackendTextureDesc desc;
+        desc.fConfig = kRGBA_8888_GrPixelConfig;
+        desc.fWidth = width;
+        desc.fHeight = height;
+        desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+        desc.fTextureHandle = reinterpret_cast<GrBackendObject>(&info);
+        if (SkImage* image = SkImage::NewFromAdoptedTexture(context, desc)) {
+            return image;
+        }
+        GR_GL_CALL(gl, DeleteTextures(1, &id));
+        return nullptr;
+    }
+
+    void onDraw(SkCanvas* canvas) override {
+        GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
+        GrContext* context;
+        if (!rt || !(context = rt->getContext())) {
+            skiagm::GM::DrawGpuOnlyMessage(canvas);
+            return;
+        }
+
+        static const int kWidth = 50;
+        static const int kHeight = 50;
+        static const SkScalar kPad = 5.f;
+
+        SkPMColor pixels[kWidth * kHeight];
+        this->fillPixels(kWidth, kHeight, pixels);
+        SkAutoTUnref<SkImage> rectImg(this->createRectangleTextureImg(context, kWidth, kHeight,
+                                                                      pixels));
+
+        if (!rectImg) {
+            SkPaint paint;
+            paint.setAntiAlias(true);
+            static const char* kMsg = "Could not create rectangle texture image.";
+            canvas->drawText(kMsg, strlen(kMsg), 10, 100, paint);
+            return;
+        }
+
+        static const SkFilterQuality kQualities[] = {
+            kNone_SkFilterQuality,
+            kLow_SkFilterQuality,
+            kMedium_SkFilterQuality,
+            kHigh_SkFilterQuality,
+        };
+
+        static const SkScalar kScales[] = { 1.0f, 1.2f, 0.75f };
+
+        canvas->translate(kPad, kPad);
+        for (auto s : kScales) {
+            canvas->save();
+            canvas->scale(s, s);
+            for (auto q : kQualities) {
+                    SkPaint plainPaint;
+                    plainPaint.setFilterQuality(q);
+                    canvas->drawImage(rectImg, 0, 0, &plainPaint);
+                    canvas->translate(kWidth + kPad, 0);
+
+                    SkPaint clampPaint;
+                    clampPaint.setFilterQuality(q);
+                    clampPaint.setShader(rectImg->newShader(SkShader::kClamp_TileMode,
+                                                            SkShader::kClamp_TileMode))->unref();
+                    canvas->drawRect(SkRect::MakeWH(1.5f * kWidth, 1.5f * kHeight), clampPaint);
+                    canvas->translate(kWidth * 1.5f + kPad, 0);
+
+                    SkPaint repeatPaint;
+                    repeatPaint.setFilterQuality(q);
+                    repeatPaint.setShader(rectImg->newShader(SkShader::kRepeat_TileMode,
+                                                             SkShader::kMirror_TileMode))->unref();
+                    canvas->drawRect(SkRect::MakeWH(1.5f * kWidth, 1.5f * kHeight), repeatPaint);
+                    canvas->translate(1.5f * kWidth + kPad, 0);
+            }
+            canvas->restore();
+            canvas->translate(0, kPad + 1.5f * kHeight * s);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+DEF_GM(return new RectangleTexture;)
+}
+
+#endif
index 3eaa3fc035e27ebc821b16825f26d24443e02f8c..e744ec897a83fc783603a73fdcf65c2acaca34b7 100644 (file)
@@ -426,11 +426,24 @@ public:
     // draws an outline rectangle for debugging/visualization purposes.
     virtual void drawDebugWireRect(GrRenderTarget*, const SkIRect&, GrColor) = 0;
 
-    // Determines whether a copy of a texture must be made in order to be compatible with
-    // a given GrTextureParams. If so, the width, height and filter used for the copy are
-    // output via the CopyParams.
+    // Determines whether a texture will need to be rescaled in order to be used with the
+    // GrTextureParams. This variation is called when the caller will create a new texture using the
+    // texture provider from a non-texture src (cpu-backed image, ...).
     bool makeCopyForTextureParams(int width, int height, const GrTextureParams&,
-                                  GrTextureProducer::CopyParams*) const;
+                                 GrTextureProducer::CopyParams*) const;
+
+    // Like the above but this variation should be called when the caller is not creating the
+    // original texture but rather was handed the original texture. It adds additional checks
+    // relevant to original textures that were created external to Skia via
+    // GrTextureProvider::wrap methods.
+    bool makeCopyForTextureParams(GrTexture* texture, const GrTextureParams& params,
+                                  GrTextureProducer::CopyParams* copyParams) const {
+        if (this->makeCopyForTextureParams(texture->width(), texture->height(), params,
+                                           copyParams)) {
+            return true;
+        }
+        return this->onMakeCopyForTextureParams(texture, params, copyParams);
+    }
 
     // This is only to be used in GL-specific tests.
     virtual const GrGLContext* glContextForTesting() const { return nullptr; }
@@ -506,6 +519,9 @@ private:
     // overridden by backend-specific derived class to perform the draw call.
     virtual void onDraw(const DrawArgs&, const GrNonInstancedVertices&) = 0;
 
+    virtual bool onMakeCopyForTextureParams(GrTexture* texture, const GrTextureParams&,
+                                            GrTextureProducer::CopyParams*) const { return false; }
+
     virtual bool onGetReadPixelsInfo(GrSurface* srcSurface, int readWidth, int readHeight,
                                      size_t rowBytes, GrPixelConfig readConfig, DrawPreference*,
                                      ReadPixelTempDrawInfo*) = 0;
index 336ab6b10c4a8f37d9bf96763be57f7144e6c3c7..fd17d2adb47c6b2c8f6f79ed7563951da649eb68 100644 (file)
@@ -133,6 +133,28 @@ GrTextureAdjuster::GrTextureAdjuster(GrTexture* original,
     }
 }
 
+GrTexture* GrTextureAdjuster::refCopy(const CopyParams& copyParams) {
+    GrTexture* texture = this->originalTexture();
+    GrContext* context = texture->getContext();
+    const SkIRect* contentArea = this->contentAreaOrNull();
+    GrUniqueKey key;
+    this->makeCopyKey(copyParams, &key);
+    if (key.isValid()) {
+        GrTexture* cachedCopy = context->textureProvider()->findAndRefTextureByUniqueKey(key);
+        if (cachedCopy) {
+            return cachedCopy;
+        }
+    }
+    GrTexture* copy = copy_on_gpu(texture, contentArea, copyParams);
+    if (copy) {
+        if (key.isValid()) {
+            copy->resourcePriv().setUniqueKey(key);
+            this->didCacheCopy(key);
+        }
+    }
+    return copy;
+}
+
 GrTexture* GrTextureAdjuster::refTextureSafeForParams(const GrTextureParams& params,
                                                       SkIPoint* outOffset) {
     GrTexture* texture = this->originalTexture();
@@ -146,8 +168,7 @@ GrTexture* GrTextureAdjuster::refTextureSafeForParams(const GrTextureParams& par
         copyParams.fWidth = contentArea->width();
         copyParams.fHeight = contentArea->height();
         copyParams.fFilter = GrTextureParams::kBilerp_FilterMode;
-    } else if (!context->getGpu()->makeCopyForTextureParams(texture->width(), texture->height(),
-                                                            params, &copyParams)) {
+    } else if (!context->getGpu()->makeCopyForTextureParams(texture, params, &copyParams)) {
         if (outOffset) {
             if (contentArea) {
                 outOffset->set(contentArea->fLeft, contentArea->fRight);
@@ -157,25 +178,12 @@ GrTexture* GrTextureAdjuster::refTextureSafeForParams(const GrTextureParams& par
         }
         return SkRef(texture);
     }
-    GrUniqueKey key;
-    this->makeCopyKey(copyParams, &key);
-    if (key.isValid()) {
-        GrTexture* result = context->textureProvider()->findAndRefTextureByUniqueKey(key);
-        if (result) {
-            return result;
-        }
-    }
-    GrTexture* result = copy_on_gpu(texture, contentArea, copyParams);
-    if (result) {
-        if (key.isValid()) {
-            result->resourcePriv().setUniqueKey(key);
-            this->didCacheCopy(key);
-        }
-        if (outOffset) {
-            outOffset->set(0, 0);
-        }
+
+    GrTexture* copy = this->refCopy(copyParams);
+    if (copy && outOffset) {
+        outOffset->set(0, 0);
     }
-    return result;
+    return copy;
 }
 
 enum DomainMode {
@@ -352,7 +360,7 @@ static const GrFragmentProcessor* create_fp_for_domain_and_filter(
             return GrBicubicEffect::Create(texture, textureMatrix, domain);
         } else {
             static const SkShader::TileMode kClampClamp[] =
-            { SkShader::kClamp_TileMode, SkShader::kClamp_TileMode };
+                { SkShader::kClamp_TileMode, SkShader::kClamp_TileMode };
             return GrBicubicEffect::Create(texture, textureMatrix, kClampClamp);
         }
     }
@@ -378,7 +386,20 @@ const GrFragmentProcessor* GrTextureAdjuster::createFragmentProcessor(
     }
 
     SkRect domain;
-    GrTexture* texture = this->originalTexture();
+    GrTextureParams params;
+    if (filterOrNullForBicubic) {
+        params.setFilterMode(*filterOrNullForBicubic);
+    }
+    SkAutoTUnref<GrTexture> texture(this->refTextureSafeForParams(params, nullptr));
+    if (!texture) {
+        return nullptr;
+    }
+    // If we made a copy then we only copied the contentArea, in which case the new texture is all
+    // content.
+    if (texture != this->originalTexture()) {
+        contentArea = nullptr;
+    }
+
     DomainMode domainMode =
         determine_domain_mode(*constraintRect, filterConstraint, coordsLimitedToConstraintRect,
                               texture->width(), texture->height(),
index 103bcbf7208099d33ce73a92b4d1e3d34b84a1fa..7279ed625211bce222acec61b58901eb8575d1fc 100644 (file)
@@ -153,6 +153,8 @@ private:
     SkTLazy<SkIRect>    fContentArea;
     GrTexture*          fOriginal;
 
+    GrTexture* refCopy(const CopyParams &copyParams);
+
     typedef GrTextureProducer INHERITED;
 };
 
index 30181f692ae147ab6e06f2b122454583a79c0a8c..9a2a5acad9fe9142229cf6d7d76fa563cb864d95 100644 (file)
@@ -220,9 +220,13 @@ void GrGLCaps::init(const GrContextOptions& contextOptions,
         }
     }
 
-    if ((kGL_GrGLStandard == standard && version >= GR_GL_VER(3, 2)) ||
+    if ((kGL_GrGLStandard == standard && version >= GR_GL_VER(3, 1)) ||
         ctxInfo.hasExtension("GL_ARB_texture_rectangle")) {
-        fRectangleTextureSupport = true;
+        // We also require textureSize() support for rectangle 2D samplers which was added in GLSL
+        // 1.40.
+        if (ctxInfo.glslGeneration() >= k140_GrGLSLGeneration) {
+            fRectangleTextureSupport = true;
+        }
     }
 
     if (kGL_GrGLStandard == standard) {
index 185cdc26f30134ac8377cd6592ad82810394a112..96b1625dd21f297fb5a8f1a22266d67eaa6a17fc 100644 (file)
@@ -3491,3 +3491,19 @@ GrGLAttribArrayState* GrGLGpu::HWGeometryState::internalBind(GrGLGpu* gpu,
     }
     return attribState;
 }
+
+bool GrGLGpu::onMakeCopyForTextureParams(GrTexture* texture, const GrTextureParams& textureParams,
+                                         GrTextureProducer::CopyParams* copyParams) const {
+    if (textureParams.isTiled() ||
+        GrTextureParams::kMipMap_FilterMode == textureParams.filterMode()) {
+        GrGLTexture* glTexture = static_cast<GrGLTexture*>(texture);
+        if (GR_GL_TEXTURE_EXTERNAL == glTexture->target() ||
+            GR_GL_TEXTURE_RECTANGLE == glTexture->target()) {
+            copyParams->fFilter = GrTextureParams::kNone_FilterMode;
+            copyParams->fWidth = texture->width();
+            copyParams->fHeight = texture->height();
+            return true;
+        }
+    }
+    return false;
+}
index b2eec45835879737ea147201b7b642f7ed136117..255952020383a6c432c97916f62986987fe6e1f2 100644 (file)
@@ -161,6 +161,9 @@ private:
 
     void onClearStencilClip(GrRenderTarget*, const SkIRect& rect, bool insideClip) override;
 
+    bool onMakeCopyForTextureParams(GrTexture*, const GrTextureParams&,
+                                    GrTextureProducer::CopyParams*) const override;
+
     bool onReadPixels(GrSurface*,
                       int left, int top,
                       int width, int height,
index 07c61f7aa00beec15a171ad06396e97d6e143916..ad119aee6a30fa99ee2c8a31767bff0c62d6e320 100644 (file)
@@ -156,10 +156,15 @@ void SkGLContext::GLFenceSync::deleteFence(SkPlatformGpuFence fence) const {
 GrGLint SkGLContext::createTextureRectangle(int width, int height, GrGLenum internalFormat,
                                             GrGLenum externalFormat, GrGLenum externalType,
                                             GrGLvoid* data) {
-    if (!(kGL_GrGLStandard == fGL->fStandard && GrGLGetVersion(fGL) >= GR_GL_VER(3, 2)) &&
+    if (!(kGL_GrGLStandard == fGL->fStandard && GrGLGetVersion(fGL) >= GR_GL_VER(3, 1)) &&
         !fGL->fExtensions.has("GL_ARB_texture_rectangle")) {
         return 0;
     }
+
+    if  (GrGLGetGLSLVersion(fGL) < GR_GLSL_VER(1, 40)) {
+        return 0;
+    }
+
     GrGLuint id;
     GR_GL_CALL(fGL, GenTextures(1, &id));
     GR_GL_CALL(fGL, BindTexture(GR_GL_TEXTURE_RECTANGLE, id));
index f1ede1decd7ce6c589264a95e4805077d26c0a23..a2cf66cb4097c591f6a5014328356291d8acc09c 100644 (file)
@@ -63,10 +63,29 @@ void GrGLSLShaderBuilder::appendTextureLookup(SkString* out,
     const GrGLSLCaps* glslCaps = fProgramBuilder->glslCaps();
     GrGLSLUniformHandler* uniformHandler = fProgramBuilder->uniformHandler();
     GrSLType samplerType = uniformHandler->getUniformVariable(sampler.fSamplerUniform).getType();
-    out->appendf("%s(%s, %s)",
-                 GrGLSLTexture2DFunctionName(varyingType, samplerType, glslCaps->generation()),
-                 uniformHandler->getUniformCStr(sampler.fSamplerUniform),
-                 coordName);
+    if (samplerType == kSampler2DRect_GrSLType) {
+        if (varyingType == kVec2f_GrSLType) {
+            out->appendf("%s(%s, textureSize(%s) * %s)",
+                         GrGLSLTexture2DFunctionName(varyingType, samplerType,
+                                                     glslCaps->generation()),
+                         uniformHandler->getUniformCStr(sampler.fSamplerUniform),
+                         uniformHandler->getUniformCStr(sampler.fSamplerUniform),
+                         coordName);
+        } else {
+            out->appendf("%s(%s, vec3(textureSize(%s) * %s.xy, %s.z))",
+                         GrGLSLTexture2DFunctionName(varyingType, samplerType,
+                                                     glslCaps->generation()),
+                         uniformHandler->getUniformCStr(sampler.fSamplerUniform),
+                         uniformHandler->getUniformCStr(sampler.fSamplerUniform),
+                         coordName,
+                         coordName);
+        }
+    } else {
+        out->appendf("%s(%s, %s)",
+                     GrGLSLTexture2DFunctionName(varyingType, samplerType, glslCaps->generation()),
+                     uniformHandler->getUniformCStr(sampler.fSamplerUniform),
+                     coordName);
+    }
 
     // This refers to any swizzling we may need to get from some backend internal format to the
     // format used in GrPixelConfig. If this is implemented by the GrGpu object, then swizzle will