aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gpu/GrTexture.cpp6
-rw-r--r--src/gpu/GrTexturePriv.h8
-rw-r--r--src/gpu/gl/GrGLGpu.cpp464
-rw-r--r--src/gpu/gl/GrGLGpu.h20
-rw-r--r--src/gpu/gl/GrGLProgram.cpp26
-rw-r--r--src/gpu/gl/GrGLProgram.h9
6 files changed, 500 insertions, 33 deletions
diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp
index 033f1da891..9209dbd053 100644
--- a/src/gpu/GrTexture.cpp
+++ b/src/gpu/GrTexture.cpp
@@ -18,7 +18,7 @@
#include "SkMipMap.h"
#include "SkTypes.h"
-void GrTexture::dirtyMipMaps(bool mipMapsDirty) {
+void GrTexture::dirtyMipMaps(bool mipMapsDirty, bool sRGBCorrect) {
if (mipMapsDirty) {
if (kValid_MipMapsStatus == fMipMapsStatus) {
fMipMapsStatus = kAllocated_MipMapsStatus;
@@ -26,6 +26,7 @@ void GrTexture::dirtyMipMaps(bool mipMapsDirty) {
} else {
const bool sizeChanged = kNotAllocated_MipMapsStatus == fMipMapsStatus;
fMipMapsStatus = kValid_MipMapsStatus;
+ fMipMapsAreSRGBCorrect = sRGBCorrect;
if (sizeChanged) {
// This must not be called until after changing fMipMapsStatus.
this->didChangeGpuMemorySize();
@@ -93,9 +94,12 @@ GrTexture::GrTexture(GrGpu* gpu, const GrSurfaceDesc& desc, GrSLType samplerType
if (wasMipMapDataProvided) {
fMipMapsStatus = kValid_MipMapsStatus;
fMaxMipMapLevel = SkMipMap::ComputeLevelCount(fDesc.fWidth, fDesc.fHeight);
+ // At the moment, the CPU code for generating mipmaps doesn't account for sRGB:
+ fMipMapsAreSRGBCorrect = false;
} else {
fMipMapsStatus = kNotAllocated_MipMapsStatus;
fMaxMipMapLevel = 0;
+ fMipMapsAreSRGBCorrect = false;
}
}
diff --git a/src/gpu/GrTexturePriv.h b/src/gpu/GrTexturePriv.h
index fee7ed1f96..762890fcc0 100644
--- a/src/gpu/GrTexturePriv.h
+++ b/src/gpu/GrTexturePriv.h
@@ -29,7 +29,9 @@ public:
return 0 != (fTexture->fDesc.fFlags & flags);
}
- void dirtyMipMaps(bool mipMapsDirty) { fTexture->dirtyMipMaps(mipMapsDirty); }
+ void dirtyMipMaps(bool mipMapsDirty, bool sRGBCorrect = false) {
+ fTexture->dirtyMipMaps(mipMapsDirty, sRGBCorrect);
+ }
bool mipMapsAreDirty() const {
return GrTexture::kValid_MipMapsStatus != fTexture->fMipMapsStatus;
@@ -47,6 +49,10 @@ public:
return fTexture->fMaxMipMapLevel;
}
+ bool mipMapsAreSRGBCorrect() const {
+ return fTexture->fMipMapsAreSRGBCorrect;
+ }
+
static void ComputeScratchKey(const GrSurfaceDesc&, GrScratchKey*);
private:
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 6daf955170..1744e25036 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -201,6 +201,9 @@ GrGLGpu::GrGLGpu(GrGLContext* ctx, GrContext* context)
for (size_t i = 0; i < SK_ARRAY_COUNT(fCopyPrograms); ++i) {
fCopyPrograms[i].fProgram = 0;
}
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fMipmapPrograms); ++i) {
+ fMipmapPrograms[i].fProgram = 0;
+ }
fWireRectProgram.fProgram = 0;
fPLSSetupProgram.fProgram = 0;
@@ -257,6 +260,7 @@ GrGLGpu::~GrGLGpu() {
// to release the resources held by the objects themselves.
fPathRendering.reset();
fCopyProgramArrayBuffer.reset();
+ fMipmapProgramArrayBuffer.reset();
fWireRectArrayBuffer.reset();
fPLSSetupProgram.fArrayBuffer.reset();
@@ -282,6 +286,12 @@ GrGLGpu::~GrGLGpu() {
}
}
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fMipmapPrograms); ++i) {
+ if (0 != fMipmapPrograms[i].fProgram) {
+ GL_CALL(DeleteProgram(fMipmapPrograms[i].fProgram));
+ }
+ }
+
if (0 != fWireRectProgram.fProgram) {
GL_CALL(DeleteProgram(fWireRectProgram.fProgram));
}
@@ -421,6 +431,11 @@ void GrGLGpu::disconnect(DisconnectType type) {
GL_CALL(DeleteProgram(fCopyPrograms[i].fProgram));
}
}
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fMipmapPrograms); ++i) {
+ if (fMipmapPrograms[i].fProgram) {
+ GL_CALL(DeleteProgram(fMipmapPrograms[i].fProgram));
+ }
+ }
if (fWireRectProgram.fProgram) {
GL_CALL(DeleteProgram(fWireRectProgram.fProgram));
}
@@ -444,6 +459,10 @@ void GrGLGpu::disconnect(DisconnectType type) {
for (size_t i = 0; i < SK_ARRAY_COUNT(fCopyPrograms); ++i) {
fCopyPrograms[i].fProgram = 0;
}
+ fMipmapProgramArrayBuffer.reset();
+ for (size_t i = 0; i < SK_ARRAY_COUNT(fMipmapPrograms); ++i) {
+ fMipmapPrograms[i].fProgram = 0;
+ }
fWireRectProgram.fProgram = 0;
fWireRectArrayBuffer.reset();
fPLSSetupProgram.fProgram = 0;
@@ -1981,6 +2000,14 @@ void GrGLGpu::flushMinSampleShading(float minSampleShading) {
}
bool GrGLGpu::flushGLState(const GrPipeline& pipeline, const GrPrimitiveProcessor& primProc) {
+ SkAutoTUnref<GrGLProgram> program(fProgramCache->refProgram(this, pipeline, primProc));
+ if (!program) {
+ GrCapsDebugf(this->caps(), "Failed to create program!\n");
+ return false;
+ }
+
+ program->generateMipmaps(primProc, pipeline);
+
GrXferProcessor::BlendInfo blendInfo;
pipeline.getXferProcessor().getBlendInfo(&blendInfo);
@@ -1988,12 +2015,6 @@ bool GrGLGpu::flushGLState(const GrPipeline& pipeline, const GrPrimitiveProcesso
this->flushDrawFace(pipeline.getDrawFace());
this->flushMinSampleShading(primProc.getSampleShading());
- SkAutoTUnref<GrGLProgram> program(fProgramCache->refProgram(this, pipeline, primProc));
- if (!program) {
- GrCapsDebugf(this->caps(), "Failed to create program!\n");
- return false;
- }
-
GrGLuint programID = program->programID();
if (fHWProgramID != programID) {
GL_CALL(UseProgram(programID));
@@ -2645,19 +2666,22 @@ void GrGLGpu::flushRenderTarget(GrGLRenderTarget* target, const SkIRect* bounds,
}
if (this->glCaps().srgbWriteControl()) {
- bool enableSRGBWrite = GrPixelConfigIsSRGB(target->config()) && !disableSRGB;
- if (enableSRGBWrite && kYes_TriState != fHWSRGBFramebuffer) {
- GL_CALL(Enable(GR_GL_FRAMEBUFFER_SRGB));
- fHWSRGBFramebuffer = kYes_TriState;
- } else if (!enableSRGBWrite && kNo_TriState != fHWSRGBFramebuffer) {
- GL_CALL(Disable(GR_GL_FRAMEBUFFER_SRGB));
- fHWSRGBFramebuffer = kNo_TriState;
- }
+ this->flushFramebufferSRGB(GrPixelConfigIsSRGB(target->config()) && !disableSRGB);
}
this->didWriteToSurface(target, bounds);
}
+void GrGLGpu::flushFramebufferSRGB(bool enable) {
+ if (enable && kYes_TriState != fHWSRGBFramebuffer) {
+ GL_CALL(Enable(GR_GL_FRAMEBUFFER_SRGB));
+ fHWSRGBFramebuffer = kYes_TriState;
+ } else if (!enable && kNo_TriState != fHWSRGBFramebuffer) {
+ GL_CALL(Disable(GR_GL_FRAMEBUFFER_SRGB));
+ fHWSRGBFramebuffer = kNo_TriState;
+ }
+}
+
void GrGLGpu::flushViewport(const GrGLIRect& viewport) {
if (fHWViewport != viewport) {
viewport.pushToGLViewport(this->glInterface());
@@ -3138,17 +3162,6 @@ void GrGLGpu::bindTexture(int unitIdx, const GrTextureParams& params, bool allow
bool setAll = timestamp < this->getResetTimestamp();
GrGLTexture::TexParams newTexParams;
- if (this->caps()->srgbSupport()) {
- // By default, the decision to allow SRGB decode is based on the destination config.
- // A texture can override that by specifying a value in GrTextureParams.
- newTexParams.fSRGBDecode = allowSRGBInputs ? GR_GL_DECODE_EXT : GR_GL_SKIP_DECODE_EXT;
-
- if (setAll || newTexParams.fSRGBDecode != oldTexParams.fSRGBDecode) {
- this->setTextureUnit(unitIdx);
- GL_CALL(TexParameteri(target, GR_GL_TEXTURE_SRGB_DECODE_EXT, newTexParams.fSRGBDecode));
- }
- }
-
static GrGLenum glMinFilterModes[] = {
GR_GL_NEAREST,
GR_GL_LINEAR,
@@ -3170,16 +3183,27 @@ void GrGLGpu::bindTexture(int unitIdx, const GrTextureParams& params, bool allow
newTexParams.fMinFilter = glMinFilterModes[filterMode];
newTexParams.fMagFilter = glMagFilterModes[filterMode];
- if (GrTextureParams::kMipMap_FilterMode == filterMode) {
- if (texture->texturePriv().mipMapsAreDirty()) {
+ bool enableSRGBDecode = false;
+ if (GrPixelConfigIsSRGB(texture->config())) {
+ enableSRGBDecode = allowSRGBInputs;
+
+ newTexParams.fSRGBDecode = enableSRGBDecode ? GR_GL_DECODE_EXT : GR_GL_SKIP_DECODE_EXT;
+ if (setAll || newTexParams.fSRGBDecode != oldTexParams.fSRGBDecode) {
this->setTextureUnit(unitIdx);
- GL_CALL(GenerateMipmap(target));
- texture->texturePriv().dirtyMipMaps(false);
- texture->texturePriv().setMaxMipMapLevel(SkMipMap::ComputeLevelCount(
- texture->width(), texture->height()));
+ GL_CALL(TexParameteri(target, GR_GL_TEXTURE_SRGB_DECODE_EXT, newTexParams.fSRGBDecode));
}
}
+#ifdef SK_DEBUG
+ // We were supposed to ensure MipMaps were up-to-date and built correctly before getting here.
+ if (GrTextureParams::kMipMap_FilterMode == filterMode) {
+ SkASSERT(!texture->texturePriv().mipMapsAreDirty());
+ if (GrPixelConfigIsSRGB(texture->config())) {
+ SkASSERT(texture->texturePriv().mipMapsAreSRGBCorrect() == enableSRGBDecode);
+ }
+ }
+#endif
+
newTexParams.fMaxMipMapLevel = texture->texturePriv().maxMipMapLevel();
newTexParams.fWrapS = tile_to_gl_wrap(params.getTileModeX());
@@ -3278,6 +3302,67 @@ void GrGLGpu::bindTexelBuffer(int unitIdx, intptr_t offsetInBytes, GrPixelConfig
}
}
+void GrGLGpu::generateMipmaps(const GrTextureParams& params, bool allowSRGBInputs,
+ GrGLTexture* texture) {
+ SkASSERT(texture);
+
+ // First, figure out if we need mips for this texture at all:
+ GrTextureParams::FilterMode filterMode = params.filterMode();
+
+ if (GrTextureParams::kMipMap_FilterMode == filterMode) {
+ if (!this->caps()->mipMapSupport() || GrPixelConfigIsCompressed(texture->config())) {
+ filterMode = GrTextureParams::kBilerp_FilterMode;
+ }
+ }
+
+ if (GrTextureParams::kMipMap_FilterMode != filterMode) {
+ return;
+ }
+
+ // If this is an sRGB texture and the mips were previously built the "other" way
+ // (gamma-correct vs. not), then we need to rebuild them. We don't need to check for
+ // srgbSupport - we'll *never* get an sRGB pixel config if we don't support it.
+ if (GrPixelConfigIsSRGB(texture->config()) &&
+ allowSRGBInputs != texture->texturePriv().mipMapsAreSRGBCorrect()) {
+ texture->texturePriv().dirtyMipMaps(true);
+ }
+
+ // If the mips aren't dirty, we're done:
+ if (!texture->texturePriv().mipMapsAreDirty()) {
+ return;
+ }
+
+ // If we created a rt/tex and rendered to it without using a texture and now we're texturing
+ // from the rt it will still be the last bound texture, but it needs resolving.
+ GrGLRenderTarget* texRT = static_cast<GrGLRenderTarget*>(texture->asRenderTarget());
+ if (texRT) {
+ this->onResolveRenderTarget(texRT);
+ }
+
+ GrGLenum target = texture->target();
+ this->setScratchTextureUnit();
+ GL_CALL(BindTexture(target, texture->textureID()));
+
+ // Configure sRGB decode, if necessary. This state is the only thing needed for the driver
+ // call (glGenerateMipmap) to work correctly. Our manual method dirties other state, too.
+ if (GrPixelConfigIsSRGB(texture->config())) {
+ GL_CALL(TexParameteri(target, GR_GL_TEXTURE_SRGB_DECODE_EXT,
+ allowSRGBInputs ? GR_GL_DECODE_EXT : GR_GL_SKIP_DECODE_EXT));
+ }
+
+ // Either do manual mipmap generation or (if that fails), just rely on the driver:
+ if (!this->generateMipmap(texture, allowSRGBInputs)) {
+ GL_CALL(GenerateMipmap(target));
+ }
+
+ texture->texturePriv().dirtyMipMaps(false, allowSRGBInputs);
+ texture->texturePriv().setMaxMipMapLevel(SkMipMap::ComputeLevelCount(
+ texture->width(), texture->height()));
+
+ // We have potentially set lots of state on the texture. Easiest to dirty it all:
+ texture->textureParamsModified();
+}
+
void GrGLGpu::setTextureSwizzle(int unitIdx, GrGLenum target, const GrGLenum swizzle[]) {
this->setTextureUnit(unitIdx);
if (this->glStandard() == kGLES_GrGLStandard) {
@@ -3711,6 +3796,167 @@ bool GrGLGpu::createCopyProgram(int progIdx) {
return true;
}
+bool GrGLGpu::createMipmapProgram(int progIdx) {
+ const bool oddWidth = SkToBool(progIdx & 0x2);
+ const bool oddHeight = SkToBool(progIdx & 0x1);
+ const int numTaps = (oddWidth ? 2 : 1) * (oddHeight ? 2 : 1);
+
+ const GrGLSLCaps* glslCaps = this->glCaps().glslCaps();
+
+ SkASSERT(!fMipmapPrograms[progIdx].fProgram);
+ GL_CALL_RET(fMipmapPrograms[progIdx].fProgram, CreateProgram());
+ if (!fMipmapPrograms[progIdx].fProgram) {
+ return false;
+ }
+
+ const char* version = glslCaps->versionDeclString();
+ GrGLSLShaderVar aVertex("a_vertex", kVec2f_GrSLType, GrShaderVar::kAttribute_TypeModifier);
+ GrGLSLShaderVar uTexCoordXform("u_texCoordXform", kVec4f_GrSLType,
+ GrShaderVar::kUniform_TypeModifier);
+ GrGLSLShaderVar uTexture("u_texture", kSampler2D_GrSLType, GrShaderVar::kUniform_TypeModifier);
+ // We need 1, 2, or 4 texture coordinates (depending on parity of each dimension):
+ GrGLSLShaderVar vTexCoords[] = {
+ GrGLSLShaderVar("v_texCoord0", kVec2f_GrSLType, GrShaderVar::kVaryingOut_TypeModifier),
+ GrGLSLShaderVar("v_texCoord1", kVec2f_GrSLType, GrShaderVar::kVaryingOut_TypeModifier),
+ GrGLSLShaderVar("v_texCoord2", kVec2f_GrSLType, GrShaderVar::kVaryingOut_TypeModifier),
+ GrGLSLShaderVar("v_texCoord3", kVec2f_GrSLType, GrShaderVar::kVaryingOut_TypeModifier),
+ };
+ GrGLSLShaderVar oFragColor("o_FragColor", kVec4f_GrSLType,
+ GrShaderVar::kOut_TypeModifier);
+
+ SkString vshaderTxt(version);
+ if (glslCaps->noperspectiveInterpolationSupport()) {
+ if (const char* extension = glslCaps->noperspectiveInterpolationExtensionString()) {
+ vshaderTxt.appendf("#extension %s : require\n", extension);
+ }
+ vTexCoords[0].addModifier("noperspective");
+ vTexCoords[1].addModifier("noperspective");
+ vTexCoords[2].addModifier("noperspective");
+ vTexCoords[3].addModifier("noperspective");
+ }
+
+ aVertex.appendDecl(glslCaps, &vshaderTxt);
+ vshaderTxt.append(";");
+ uTexCoordXform.appendDecl(glslCaps, &vshaderTxt);
+ vshaderTxt.append(";");
+ for (int i = 0; i < numTaps; ++i) {
+ vTexCoords[i].appendDecl(glslCaps, &vshaderTxt);
+ vshaderTxt.append(";");
+ }
+
+ vshaderTxt.append(
+ "// Mipmap Program VS\n"
+ "void main() {"
+ " gl_Position.xy = a_vertex * vec2(2, 2) - vec2(1, 1);"
+ " gl_Position.zw = vec2(0, 1);"
+ );
+
+ // Insert texture coordinate computation:
+ if (oddWidth && oddHeight) {
+ vshaderTxt.append(
+ " v_texCoord0 = a_vertex.xy * u_texCoordXform.yw;"
+ " v_texCoord1 = a_vertex.xy * u_texCoordXform.yw + vec2(u_texCoordXform.x, 0);"
+ " v_texCoord2 = a_vertex.xy * u_texCoordXform.yw + vec2(0, u_texCoordXform.z);"
+ " v_texCoord3 = a_vertex.xy * u_texCoordXform.yw + u_texCoordXform.xz;"
+ );
+ } else if (oddWidth) {
+ vshaderTxt.append(
+ " v_texCoord0 = a_vertex.xy * vec2(u_texCoordXform.y, 1);"
+ " v_texCoord1 = a_vertex.xy * vec2(u_texCoordXform.y, 1) + vec2(u_texCoordXform.x, 0);"
+ );
+ } else if (oddHeight) {
+ vshaderTxt.append(
+ " v_texCoord0 = a_vertex.xy * vec2(1, u_texCoordXform.w);"
+ " v_texCoord1 = a_vertex.xy * vec2(1, u_texCoordXform.w) + vec2(0, u_texCoordXform.z);"
+ );
+ } else {
+ vshaderTxt.append(
+ " v_texCoord0 = a_vertex.xy;"
+ );
+ }
+
+ vshaderTxt.append("}");
+
+ SkString fshaderTxt(version);
+ if (glslCaps->noperspectiveInterpolationSupport()) {
+ if (const char* extension = glslCaps->noperspectiveInterpolationExtensionString()) {
+ fshaderTxt.appendf("#extension %s : require\n", extension);
+ }
+ }
+ GrGLSLAppendDefaultFloatPrecisionDeclaration(kDefault_GrSLPrecision, *glslCaps,
+ &fshaderTxt);
+ for (int i = 0; i < numTaps; ++i) {
+ vTexCoords[i].setTypeModifier(GrShaderVar::kVaryingIn_TypeModifier);
+ vTexCoords[i].appendDecl(glslCaps, &fshaderTxt);
+ fshaderTxt.append(";");
+ }
+ uTexture.appendDecl(glslCaps, &fshaderTxt);
+ fshaderTxt.append(";");
+ const char* fsOutName;
+ if (glslCaps->mustDeclareFragmentShaderOutput()) {
+ oFragColor.appendDecl(glslCaps, &fshaderTxt);
+ fshaderTxt.append(";");
+ fsOutName = oFragColor.c_str();
+ } else {
+ fsOutName = "gl_FragColor";
+ }
+ const char* sampleFunction = GrGLSLTexture2DFunctionName(kVec2f_GrSLType, kSampler2D_GrSLType,
+ this->glslGeneration());
+ fshaderTxt.append(
+ "// Mipmap Program FS\n"
+ "void main() {"
+ );
+
+ if (oddWidth && oddHeight) {
+ fshaderTxt.appendf(
+ " %s = (%s(u_texture, v_texCoord0) + %s(u_texture, v_texCoord1) + "
+ " %s(u_texture, v_texCoord2) + %s(u_texture, v_texCoord3)) * 0.25;",
+ fsOutName, sampleFunction, sampleFunction, sampleFunction, sampleFunction
+ );
+ } else if (oddWidth || oddHeight) {
+ fshaderTxt.appendf(
+ " %s = (%s(u_texture, v_texCoord0) + %s(u_texture, v_texCoord1)) * 0.5;",
+ fsOutName, sampleFunction, sampleFunction
+ );
+ } else {
+ fshaderTxt.appendf(
+ " %s = %s(u_texture, v_texCoord0);",
+ fsOutName, sampleFunction
+ );
+ }
+
+ fshaderTxt.append("}");
+
+ const char* str;
+ GrGLint length;
+
+ str = vshaderTxt.c_str();
+ length = SkToInt(vshaderTxt.size());
+ GrGLuint vshader = GrGLCompileAndAttachShader(*fGLContext, fMipmapPrograms[progIdx].fProgram,
+ GR_GL_VERTEX_SHADER, &str, &length, 1,
+ &fStats);
+
+ str = fshaderTxt.c_str();
+ length = SkToInt(fshaderTxt.size());
+ GrGLuint fshader = GrGLCompileAndAttachShader(*fGLContext, fMipmapPrograms[progIdx].fProgram,
+ GR_GL_FRAGMENT_SHADER, &str, &length, 1,
+ &fStats);
+
+ GL_CALL(LinkProgram(fMipmapPrograms[progIdx].fProgram));
+
+ GL_CALL_RET(fMipmapPrograms[progIdx].fTextureUniform,
+ GetUniformLocation(fMipmapPrograms[progIdx].fProgram, "u_texture"));
+ GL_CALL_RET(fMipmapPrograms[progIdx].fTexCoordXformUniform,
+ GetUniformLocation(fMipmapPrograms[progIdx].fProgram, "u_texCoordXform"));
+
+ GL_CALL(BindAttribLocation(fMipmapPrograms[progIdx].fProgram, 0, "a_vertex"));
+
+ GL_CALL(DeleteShader(vshader));
+ GL_CALL(DeleteShader(fshader));
+
+ return true;
+}
+
bool GrGLGpu::createWireRectProgram() {
if (!fWireRectArrayBuffer) {
static const GrGLfloat vdata[] = {
@@ -4068,6 +4314,162 @@ bool GrGLGpu::copySurfaceAsBlitFramebuffer(GrSurface* dst,
return true;
}
+bool gManualMipmaps = true;
+
+// Manual implementation of mipmap generation, to work around driver bugs w/sRGB.
+// Uses draw calls to do a series of downsample operations to successive mips.
+// If this returns false, then the calling code falls back to using glGenerateMipmap.
+bool GrGLGpu::generateMipmap(GrGLTexture* texture, bool gammaCorrect) {
+ // Global switch for manual mipmap generation:
+ if (!gManualMipmaps) {
+ return false;
+ }
+
+ // Mipmaps are only supported on 2D textures:
+ if (GR_GL_TEXTURE_2D != texture->target()) {
+ return false;
+ }
+
+ // We need to be able to render to the texture for this to work:
+ if (!this->caps()->isConfigRenderable(texture->config(), false)) {
+ return false;
+ }
+
+ // Our iterative downsample requires the ability to limit which level we're sampling:
+ if (!this->glCaps().mipMapLevelAndLodControlSupport()) {
+ return false;
+ }
+
+ // If we're mipping an sRGB texture, we need to ensure FB sRGB is correct:
+ if (GrPixelConfigIsSRGB(texture->config())) {
+ // If we have write-control, just set the state that we want:
+ if (this->glCaps().srgbWriteControl()) {
+ this->flushFramebufferSRGB(gammaCorrect);
+ } else if (!gammaCorrect) {
+ // If we don't have write-control we can't do non-gamma-correct mipmapping:
+ return false;
+ }
+ }
+
+ int width = texture->width();
+ int height = texture->height();
+ int levelCount = SkMipMap::ComputeLevelCount(width, height) + 1;
+
+ // Define all mips, if we haven't previously done so:
+ if (0 == texture->texturePriv().maxMipMapLevel()) {
+ GrGLenum internalFormat;
+ GrGLenum externalFormat;
+ GrGLenum externalType;
+ if (!this->glCaps().getTexImageFormats(texture->config(), texture->config(),
+ &internalFormat, &externalFormat, &externalType)) {
+ return false;
+ }
+
+ for (GrGLint level = 1; level < levelCount; ++level) {
+ // Define the next mip:
+ width = SkTMax(1, width / 2);
+ height = SkTMax(1, height / 2);
+ GL_ALLOC_CALL(this->glInterface(), TexImage2D(GR_GL_TEXTURE_2D, level, internalFormat,
+ width, height, 0,
+ externalFormat, externalType, nullptr));
+ }
+ }
+
+ // Create (if necessary), then bind temporary FBO:
+ if (0 == fTempDstFBOID) {
+ GL_CALL(GenFramebuffers(1, &fTempDstFBOID));
+ }
+ GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, fTempDstFBOID));
+ fHWBoundRenderTargetUniqueID = SK_InvalidUniqueID;
+
+ // Bind the texture, to get things configured for filtering.
+ // We'll be changing our base level further below:
+ this->setTextureUnit(0);
+ GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
+ this->bindTexture(0, params, gammaCorrect, texture);
+
+ // Vertex data:
+ if (!fMipmapProgramArrayBuffer) {
+ static const GrGLfloat vdata[] = {
+ 0, 0,
+ 0, 1,
+ 1, 0,
+ 1, 1
+ };
+ fMipmapProgramArrayBuffer.reset(GrGLBuffer::Create(this, sizeof(vdata),
+ kVertex_GrBufferType,
+ kStatic_GrAccessPattern, vdata));
+ }
+ if (!fMipmapProgramArrayBuffer) {
+ return false;
+ }
+
+ fHWVertexArrayState.setVertexArrayID(this, 0);
+
+ GrGLAttribArrayState* attribs = fHWVertexArrayState.bindInternalVertexArray(this);
+ attribs->set(this, 0, fMipmapProgramArrayBuffer, kVec2f_GrVertexAttribType,
+ 2 * sizeof(GrGLfloat), 0);
+ attribs->disableUnusedArrays(this, 0x1);
+
+ // Set "simple" state once:
+ GrXferProcessor::BlendInfo blendInfo;
+ blendInfo.reset();
+ this->flushBlend(blendInfo, GrSwizzle::RGBA());
+ this->flushColorWrite(true);
+ this->flushDrawFace(GrPipelineBuilder::kBoth_DrawFace);
+ this->flushHWAAState(nullptr, false, false);
+ this->disableScissor();
+ GrStencilSettings stencil;
+ stencil.setDisabled();
+ this->flushStencil(stencil);
+
+ // Do all the blits:
+ width = texture->width();
+ height = texture->height();
+ GrGLIRect viewport;
+ viewport.fLeft = 0;
+ viewport.fBottom = 0;
+ for (GrGLint level = 1; level < levelCount; ++level) {
+ // Get and bind the program for this particular downsample (filter shape can vary):
+ int progIdx = TextureSizeToMipmapProgramIdx(width, height);
+ if (!fMipmapPrograms[progIdx].fProgram) {
+ if (!this->createMipmapProgram(progIdx)) {
+ SkDebugf("Failed to create mipmap program.\n");
+ return false;
+ }
+ }
+ GL_CALL(UseProgram(fMipmapPrograms[progIdx].fProgram));
+ fHWProgramID = fMipmapPrograms[progIdx].fProgram;
+
+ // Texcoord uniform is expected to contain (1/w, (w-1)/w, 1/h, (h-1)/h)
+ const float invWidth = 1.0f / width;
+ const float invHeight = 1.0f / height;
+ GL_CALL(Uniform4f(fMipmapPrograms[progIdx].fTexCoordXformUniform,
+ invWidth, (width - 1) * invWidth, invHeight, (height - 1) * invHeight));
+ GL_CALL(Uniform1i(fMipmapPrograms[progIdx].fTextureUniform, 0));
+
+ // Only sample from previous mip
+ GL_CALL(TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_BASE_LEVEL, level - 1));
+
+ GL_CALL(FramebufferTexture2D(GR_GL_FRAMEBUFFER, GR_GL_COLOR_ATTACHMENT0,
+ GR_GL_TEXTURE_2D, texture->textureID(), level));
+
+ width = SkTMax(1, width / 2);
+ height = SkTMax(1, height / 2);
+ viewport.fWidth = width;
+ viewport.fHeight = height;
+ this->flushViewport(viewport);
+
+ GL_CALL(DrawArrays(GR_GL_TRIANGLE_STRIP, 0, 4));
+ }
+
+ // Unbind:
+ GL_CALL(FramebufferTexture2D(GR_GL_FRAMEBUFFER, GR_GL_COLOR_ATTACHMENT0,
+ GR_GL_TEXTURE_2D, 0, 0));
+
+ return true;
+}
+
void GrGLGpu::onGetMultisampleSpecs(GrRenderTarget* rt,
const GrStencilSettings& stencil,
int* effectiveSampleCnt,
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index 0b2198a834..06d4bd357b 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -62,6 +62,8 @@ public:
void bindTexelBuffer(int unitIdx, intptr_t offsetInBytes, GrPixelConfig, GrGLBuffer*);
+ void generateMipmaps(const GrTextureParams& params, bool allowSRGBInputs, GrGLTexture* texture);
+
bool onGetReadPixelsInfo(GrSurface* srcSurface, int readWidth, int readHeight, size_t rowBytes,
GrPixelConfig readConfig, DrawPreference*,
ReadPixelTempDrawInfo*) override;
@@ -237,6 +239,7 @@ private:
GrSurface* src,
const SkIRect& srcRect,
const SkIPoint& dstPoint);
+ bool generateMipmap(GrGLTexture* texture, bool gammaCorrect);
void stampPLSSetupRect(const SkRect& bounds);
@@ -319,6 +322,8 @@ private:
void flushMinSampleShading(float minSampleShading);
+ void flushFramebufferSRGB(bool enable);
+
// helper for onCreateTexture and writeTexturePixels
enum UploadType {
kNewTexture_UploadType, // we are creating a new texture
@@ -365,6 +370,7 @@ private:
SkAutoTUnref<GrGLContext> fGLContext;
bool createCopyProgram(int progIdx);
+ bool createMipmapProgram(int progIdx);
bool createWireRectProgram();
bool createPLSSetupProgram();
@@ -532,6 +538,14 @@ private:
} fCopyPrograms[3];
SkAutoTUnref<GrGLBuffer> fCopyProgramArrayBuffer;
+ /** IDs for texture mipmap program. (4 filter configurations) */
+ struct {
+ GrGLuint fProgram;
+ GrGLint fTextureUniform;
+ GrGLint fTexCoordXformUniform;
+ } fMipmapPrograms[4];
+ SkAutoTUnref<GrGLBuffer> fMipmapProgramArrayBuffer;
+
struct {
GrGLuint fProgram;
GrGLint fColorUniform;
@@ -553,6 +567,12 @@ private:
}
}
+ static int TextureSizeToMipmapProgramIdx(int width, int height) {
+ const bool wide = (width > 1) && SkToBool(width & 0x1);
+ const bool tall = (height > 1) && SkToBool(height & 0x1);
+ return (wide ? 0x2 : 0x0) | (tall ? 0x1 : 0x0);
+ }
+
struct {
GrGLuint fProgram;
GrGLint fPosXformUniform;
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index c270858ec3..040c57de3a 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -83,6 +83,23 @@ void GrGLProgram::setData(const GrPrimitiveProcessor& primProc, const GrPipeline
}
}
+void GrGLProgram::generateMipmaps(const GrPrimitiveProcessor& primProc,
+ const GrPipeline& pipeline) {
+ this->generateMipmaps(primProc, pipeline.getAllowSRGBInputs());
+
+ int numProcessors = fFragmentProcessors.count();
+ for (int i = 0; i < numProcessors; ++i) {
+ const GrFragmentProcessor& processor = pipeline.getFragmentProcessor(i);
+ this->generateMipmaps(processor, pipeline.getAllowSRGBInputs());
+ }
+
+ if (primProc.getPixelLocalStorageState() !=
+ GrPixelLocalStorageState::kDraw_GrPixelLocalStorageState) {
+ const GrXferProcessor& xp = pipeline.getXferProcessor();
+ this->generateMipmaps(xp, pipeline.getAllowSRGBInputs());
+ }
+}
+
void GrGLProgram::setFragmentData(const GrPrimitiveProcessor& primProc,
const GrPipeline& pipeline,
int* nextSamplerIdx) {
@@ -146,3 +163,12 @@ void GrGLProgram::bindTextures(const GrProcessor& processor,
static_cast<GrGLBuffer*>(access.buffer()));
}
}
+
+void GrGLProgram::generateMipmaps(const GrProcessor& processor,
+ bool allowSRGBInputs) {
+ for (int i = 0; i < processor.numTextures(); ++i) {
+ const GrTextureAccess& access = processor.textureAccess(i);
+ fGpu->generateMipmaps(access.getParams(), allowSRGBInputs,
+ static_cast<GrGLTexture*>(access.getTexture()));
+ }
+}
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 7487a1e900..9f2b2e9b74 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -96,6 +96,12 @@ public:
*/
void setData(const GrPrimitiveProcessor&, const GrPipeline&);
+ /**
+ * This function retrieves the textures that need to be used by each GrGL*Processor, and
+ * ensures that any textures requiring mipmaps have their mipmaps correctly built.
+ */
+ void generateMipmaps(const GrPrimitiveProcessor&, const GrPipeline&);
+
protected:
typedef GrGLSLProgramDataManager::UniformHandle UniformHandle;
typedef GrGLProgramDataManager::UniformInfoArray UniformInfoArray;
@@ -122,6 +128,9 @@ protected:
// Helper for setData() that binds textures and texel buffers to the appropriate texture units
void bindTextures(const GrProcessor&, bool allowSRGBInputs, int* nextSamplerIdx);
+ // Helper for generateMipmaps() that ensures mipmaps are up to date
+ void generateMipmaps(const GrProcessor&, bool allowSRGBInputs);
+
// these reflect the current values of uniforms (GL uniform values travel with program)
RenderTargetState fRenderTargetState;
BuiltinUniformHandles fBuiltinUniformHandles;