diff options
Diffstat (limited to 'libs/hwui/SkiaShader.cpp')
| -rw-r--r-- | libs/hwui/SkiaShader.cpp | 301 |
1 files changed, 295 insertions, 6 deletions
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 9c929daf6400..81531e83ef77 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -16,17 +16,17 @@ #define LOG_TAG "OpenGLRenderer" -#include <utils/Log.h> - -#include <SkMatrix.h> +#include "SkiaShader.h" #include "Caches.h" #include "Extensions.h" #include "Layer.h" #include "Matrix.h" -#include "SkiaShader.h" #include "Texture.h" +#include <SkMatrix.h> +#include <utils/Log.h> + namespace android { namespace uirenderer { @@ -34,7 +34,7 @@ namespace uirenderer { // Support /////////////////////////////////////////////////////////////////////////////// -static const GLint gTileModes[] = { +static const GLenum gTileModes[] = { GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode GL_REPEAT, // == SkShader::kRepeat_Mode GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode @@ -56,6 +56,10 @@ static inline void bindUniformColor(int slot, uint32_t color) { a); } +static inline void bindUniformColor(int slot, FloatColor color) { + glUniform4fv(slot, 1, reinterpret_cast<const float*>(&color)); +} + static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) { caches->textureState().bindTexture(texture->id); texture->setWrapST(wrapS, wrapT); @@ -270,7 +274,7 @@ void SkiaBitmapShader::setupProgram(Caches* caches, const mat4& modelViewMatrix, } GLuint textureSlot = (*textureUnit)++; - Caches::getInstance().textureState().activateTexture(textureSlot); + caches->textureState().activateTexture(textureSlot); BitmapShaderInfo shaderInfo; if (!bitmapShaderHelper(caches, nullptr, &shaderInfo, extensions, bitmap, xy)) { @@ -470,5 +474,290 @@ void SkiaComposeShader::setupProgram(Caches* caches, const mat4& modelViewMatrix SkiaShader::setupProgram(caches, transform, textureUnit, extensions, *rec.fShaderB); } +/////////////////////////////////////////////////////////////////////////////// +// Store / apply +/////////////////////////////////////////////////////////////////////////////// + +bool tryStoreGradient(Caches& caches, const SkShader& shader, const Matrix4 modelViewMatrix, + GLuint* textureUnit, ProgramDescription* description, + SkiaShaderData::GradientShaderData* outData) { + SkShader::GradientInfo gradInfo; + gradInfo.fColorCount = 0; + gradInfo.fColors = nullptr; + gradInfo.fColorOffsets = nullptr; + + SkMatrix unitMatrix; + switch (shader.asAGradient(&gradInfo)) { + case SkShader::kLinear_GradientType: + description->gradientType = ProgramDescription::kGradientLinear; + + toUnitMatrix(gradInfo.fPoint, &unitMatrix); + break; + case SkShader::kRadial_GradientType: + description->gradientType = ProgramDescription::kGradientCircular; + + toCircularUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, + gradInfo.fRadius[0], &unitMatrix); + break; + case SkShader::kSweep_GradientType: + description->gradientType = ProgramDescription::kGradientSweep; + + toSweepUnitMatrix(gradInfo.fPoint[0].fX, gradInfo.fPoint[0].fY, &unitMatrix); + break; + default: + // Do nothing. This shader is unsupported. + return false; + } + description->hasGradient = true; + description->isSimpleGradient = isSimpleGradient(gradInfo); + + computeScreenSpaceMatrix(outData->screenSpace, unitMatrix, + shader.getLocalMatrix(), modelViewMatrix); + + // re-query shader to get full color / offset data + std::unique_ptr<SkColor[]> colorStorage(new SkColor[gradInfo.fColorCount]); + std::unique_ptr<SkScalar[]> colorOffsets(new SkScalar[gradInfo.fColorCount]); + gradInfo.fColors = &colorStorage[0]; + gradInfo.fColorOffsets = &colorOffsets[0]; + shader.asAGradient(&gradInfo); + + if (CC_UNLIKELY(!isSimpleGradient(gradInfo))) { + outData->gradientSampler = (*textureUnit)++; + +#ifndef SK_SCALAR_IS_FLOAT + #error Need to convert gradInfo.fColorOffsets to float! +#endif + outData->gradientTexture = caches.gradientCache.get( + gradInfo.fColors, gradInfo.fColorOffsets, gradInfo.fColorCount); + outData->wrapST = gTileModes[gradInfo.fTileMode]; + } else { + outData->gradientSampler = 0; + outData->gradientTexture = nullptr; + + outData->startColor.set(gradInfo.fColors[0]); + outData->endColor.set(gradInfo.fColors[1]); + } + + outData->ditherSampler = (*textureUnit)++; + return true; +} + +void applyGradient(Caches& caches, const SkiaShaderData::GradientShaderData& data) { + if (CC_UNLIKELY(data.gradientTexture)) { + caches.textureState().activateTexture(data.gradientSampler); + bindTexture(&caches, data.gradientTexture, data.wrapST, data.wrapST); + glUniform1i(caches.program().getUniform("gradientSampler"), data.gradientSampler); + } else { + bindUniformColor(caches.program().getUniform("startColor"), data.startColor); + bindUniformColor(caches.program().getUniform("endColor"), data.endColor); + } + + // TODO: remove sampler slot incrementing from dither.setupProgram, + // since this assignment of slots is done at store, not apply time + GLuint ditherSampler = data.ditherSampler; + caches.dither.setupProgram(caches.program(), &ditherSampler); + glUniformMatrix4fv(caches.program().getUniform("screenSpace"), 1, + GL_FALSE, &data.screenSpace.data[0]); +} + +bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, + GLuint* textureUnit, ProgramDescription* description, + SkiaShaderData::BitmapShaderData* outData) { + SkBitmap bitmap; + SkShader::TileMode xy[2]; + if (shader.asABitmap(&bitmap, nullptr, xy) != SkShader::kDefault_BitmapType) { + return false; + } + + outData->bitmapTexture = caches.textureCache.get(&bitmap); + if (!outData->bitmapTexture) return false; + + outData->bitmapSampler = (*textureUnit)++; + + const float width = outData->bitmapTexture->width; + const float height = outData->bitmapTexture->height; + + description->hasBitmap = true; + if (!caches.extensions().hasNPot() + && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) + && (xy[0] != SkShader::kClamp_TileMode || xy[1] != SkShader::kClamp_TileMode)) { + description->isBitmapNpot = true; + description->bitmapWrapS = gTileModes[xy[0]]; + description->bitmapWrapT = gTileModes[xy[1]]; + + outData->wrapS = GL_CLAMP_TO_EDGE; + outData->wrapT = GL_CLAMP_TO_EDGE; + } else { + outData->wrapS = gTileModes[xy[0]]; + outData->wrapT = gTileModes[xy[1]]; + } + + computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(), + modelViewMatrix); + outData->textureDimension[0] = 1.0f / width; + outData->textureDimension[1] = 1.0f / height; + + return true; +} + +void applyBitmap(Caches& caches, const SkiaShaderData::BitmapShaderData& data) { + caches.textureState().activateTexture(data.bitmapSampler); + bindTexture(&caches, data.bitmapTexture, data.wrapS, data.wrapT); + data.bitmapTexture->setFilter(GL_LINEAR); + + glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler); + glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, GL_FALSE, + &data.textureTransform.data[0]); + glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]); +} + +SkiaShaderType getComposeSubType(const SkShader& shader) { + // First check for a gradient shader. + switch (shader.asAGradient(nullptr)) { + case SkShader::kNone_GradientType: + // Not a gradient shader. Fall through to check for other types. + break; + case SkShader::kLinear_GradientType: + case SkShader::kRadial_GradientType: + case SkShader::kSweep_GradientType: + return kGradient_SkiaShaderType; + default: + // This is a Skia gradient that has no SkiaShader equivalent. Return None to skip. + return kNone_SkiaShaderType; + } + + // The shader is not a gradient. Check for a bitmap shader. + if (shader.asABitmap(nullptr, nullptr, nullptr) == SkShader::kDefault_BitmapType) { + return kBitmap_SkiaShaderType; + } + return kNone_SkiaShaderType; +} + +void storeCompose(Caches& caches, const SkShader& bitmapShader, const SkShader& gradientShader, + const Matrix4& modelViewMatrix, GLuint* textureUnit, + ProgramDescription* description, SkiaShaderData* outData) { + LOG_ALWAYS_FATAL_IF(!tryStoreBitmap(caches, bitmapShader, modelViewMatrix, + textureUnit, description, &outData->bitmapData), + "failed storing bitmap shader data"); + LOG_ALWAYS_FATAL_IF(!tryStoreGradient(caches, gradientShader, modelViewMatrix, + textureUnit, description, &outData->gradientData), + "failing storing gradient shader data"); +} + +bool tryStoreCompose(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, + GLuint* textureUnit, ProgramDescription* description, + SkiaShaderData* outData) { + + SkShader::ComposeRec rec; + if (!shader.asACompose(&rec)) return false; + + const SkiaShaderType shaderAType = getComposeSubType(*rec.fShaderA); + const SkiaShaderType shaderBType = getComposeSubType(*rec.fShaderB); + + // check that type enum values are the 2 flags that compose the kCompose value + if ((shaderAType & shaderBType) != 0) return false; + if ((shaderAType | shaderBType) != kCompose_SkiaShaderType) return false; + + mat4 transform; + computeScreenSpaceMatrix(transform, SkMatrix::I(), shader.getLocalMatrix(), modelViewMatrix); + if (shaderAType == kBitmap_SkiaShaderType) { + description->isBitmapFirst = true; + storeCompose(caches, *rec.fShaderA, *rec.fShaderB, + transform, textureUnit, description, outData); + } else { + description->isBitmapFirst = false; + storeCompose(caches, *rec.fShaderB, *rec.fShaderA, + transform, textureUnit, description, outData); + } + if (!SkXfermode::AsMode(rec.fMode, &description->shadersMode)) { + // TODO: Support other modes. + description->shadersMode = SkXfermode::kSrcOver_Mode; + } + return true; +} + +bool tryStoreLayer(Caches& caches, const SkShader& shader, const Matrix4& modelViewMatrix, + GLuint* textureUnit, ProgramDescription* description, + SkiaShaderData::LayerShaderData* outData) { + Layer* layer; + if (!shader.asACustomShader(reinterpret_cast<void**>(&layer))) { + return false; + } + + description->hasBitmap = true; + + outData->bitmapSampler = (*textureUnit)++; + + const float width = layer->getWidth(); + const float height = layer->getHeight(); + + computeScreenSpaceMatrix(outData->textureTransform, SkMatrix::I(), shader.getLocalMatrix(), + modelViewMatrix); + + outData->textureDimension[0] = 1.0f / width; + outData->textureDimension[1] = 1.0f / height; + return true; +} + +void applyLayer(Caches& caches, const SkiaShaderData::LayerShaderData& data) { + caches.textureState().activateTexture(data.bitmapSampler); + + data.layer->bindTexture(); + data.layer->setWrap(GL_CLAMP_TO_EDGE); + data.layer->setFilter(GL_LINEAR); + + glUniform1i(caches.program().getUniform("bitmapSampler"), data.bitmapSampler); + glUniformMatrix4fv(caches.program().getUniform("textureTransform"), 1, + GL_FALSE, &data.textureTransform.data[0]); + glUniform2fv(caches.program().getUniform("textureDimension"), 1, &data.textureDimension[0]); +} + +void SkiaShader::store(Caches& caches, const SkShader* shader, const Matrix4& modelViewMatrix, + GLuint* textureUnit, ProgramDescription* description, + SkiaShaderData* outData) { + if (!shader) { + outData->skiaShaderType = kNone_SkiaShaderType; + return; + } + + if (tryStoreGradient(caches, *shader, modelViewMatrix, + textureUnit, description, &outData->gradientData)) { + outData->skiaShaderType = kGradient_SkiaShaderType; + return; + } + + if (tryStoreBitmap(caches, *shader, modelViewMatrix, + textureUnit, description, &outData->bitmapData)) { + outData->skiaShaderType = kBitmap_SkiaShaderType; + return; + } + + if (tryStoreCompose(caches, *shader, modelViewMatrix, + textureUnit, description, outData)) { + outData->skiaShaderType = kCompose_SkiaShaderType; + return; + } + + if (tryStoreLayer(caches, *shader, modelViewMatrix, + textureUnit, description, &outData->layerData)) { + outData->skiaShaderType = kLayer_SkiaShaderType; + } +} + +void SkiaShader::apply(Caches& caches, const SkiaShaderData& data) { + if (!data.skiaShaderType) return; + + if (data.skiaShaderType & kGradient_SkiaShaderType) { + applyGradient(caches, data.gradientData); + } + if (data.skiaShaderType & kBitmap_SkiaShaderType) { + applyBitmap(caches, data.bitmapData); + } + + if (data.skiaShaderType == kLayer_SkiaShaderType) { + applyLayer(caches, data.layerData); + } +} + }; // namespace uirenderer }; // namespace android |