Index: src/gpu/gl/GrGLGpu.cpp |
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp |
index 6daf9551706bc335f8d6a5588efe82f4633536a9..1744e2503636db460b2e1254c7ebe371cbef5199 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, |