From dc82994e3a76141cc037c92a644b7a6c54c87ad7 Mon Sep 17 00:00:00 2001 From: Brian Salomon Date: Tue, 23 Oct 2018 16:07:24 -0400 Subject: [PATCH] Revert "Reland "Reland "Revert "Use OpenGL sampler objects when available."""" This reverts commit 327955b1ba194eb010ff5a3ef1f32a913d52dd5c. Bug: skia:8471 Change-Id: I56eb9509f0e2d5c4aa9e2a110a73a09723f77c4a Reviewed-on: https://skia-review.googlesource.com/c/164617 Reviewed-by: Greg Daniel Commit-Queue: Brian Salomon --- include/gpu/GrTypes.h | 1 + src/gpu/GrSwizzle.h | 7 +- src/gpu/gl/GrGLCaps.cpp | 16 +- src/gpu/gl/GrGLCreateNullInterface.cpp | 4 + src/gpu/gl/GrGLGpu.cpp | 419 +++++++++++++++---------- src/gpu/gl/GrGLGpu.h | 7 +- src/gpu/gl/GrGLTexture.cpp | 3 +- src/gpu/gl/GrGLTexture.h | 79 +++-- 8 files changed, 344 insertions(+), 192 deletions(-) diff --git a/include/gpu/GrTypes.h b/include/gpu/GrTypes.h index 5eae26c487..1b841334ad 100644 --- a/include/gpu/GrTypes.h +++ b/include/gpu/GrTypes.h @@ -238,6 +238,7 @@ enum GrSurfaceOrigin { */ enum GrGLBackendState { kRenderTarget_GrGLBackendState = 1 << 0, + // Also includes samplers bound to texture units. kTextureBinding_GrGLBackendState = 1 << 1, // View state stands for scissor and viewport kView_GrGLBackendState = 1 << 2, diff --git a/src/gpu/GrSwizzle.h b/src/gpu/GrSwizzle.h index 922d3fd880..dfd4b8a444 100644 --- a/src/gpu/GrSwizzle.h +++ b/src/gpu/GrSwizzle.h @@ -52,6 +52,11 @@ public: /** 4 char null terminated string consisting only of chars 'r', 'g', 'b', 'a'. */ const char* c_str() const { return fSwiz; } + char operator[](int i) const { + SkASSERT(i >= 0 && i < 4); + return fSwiz[i]; + } + /** Applies this swizzle to the input color and returns the swizzled color. */ GrColor applyTo(GrColor color) const { int idx; @@ -116,7 +121,7 @@ private: case GrColor_SHIFT_G : return 'g'; case GrColor_SHIFT_B : return 'b'; case GrColor_SHIFT_A : return 'a'; - default: return -1; + default: return -1; } } diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index c16dc409d8..962e496faf 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -2316,6 +2316,13 @@ bool GrGLCaps::initDescForDstCopy(const GrRenderTargetProxy* src, GrSurfaceDesc* void GrGLCaps::applyDriverCorrectnessWorkarounds(const GrGLContextInfo& ctxInfo, const GrContextOptions& contextOptions, GrShaderCaps* shaderCaps) { + bool isX86PowerVRRogue = false; +#if defined(SK_CPU_X86) + if (kPowerVRRogue_GrGLRenderer == ctxInfo.renderer()) { + isX86PowerVRRogue = true; + } +#endif + // A driver but on the nexus 6 causes incorrect dst copies when invalidate is called beforehand. // Thus we are blacklisting this extension for now on Adreno4xx devices. if (kAdreno430_GrGLRenderer == ctxInfo.renderer() || @@ -2333,7 +2340,7 @@ void GrGLCaps::applyDriverCorrectnessWorkarounds(const GrGLContextInfo& ctxInfo, fClearTextureSupport = false; } - // Calling glClearTexImage crashes on the NexusPlayer. + // Calling glClearTexImage crashes on the NexusPlayer. TODO: Use isX86PowerVRRogue? if (kPowerVRRogue_GrGLRenderer == ctxInfo.renderer()) { fClearTextureSupport = false; } @@ -2383,6 +2390,7 @@ void GrGLCaps::applyDriverCorrectnessWorkarounds(const GrGLContextInfo& ctxInfo, fMipMapSupport = false; } + // TODO: Use isX86PowerVRRogue? if (kPowerVRRogue_GrGLRenderer == ctxInfo.renderer()) { // Temporarily disabling clip analytic fragments processors on Nexus player while we work // around a driver bug related to gl_FragCoord. @@ -2466,6 +2474,7 @@ void GrGLCaps::applyDriverCorrectnessWorkarounds(const GrGLContextInfo& ctxInfo, fDetachStencilFromMSAABuffersBeforeReadPixels = true; } + // TODO: Don't apply this on iOS? if (kPowerVRRogue_GrGLRenderer == ctxInfo.renderer()) { // Our Chromebook with kPowerVRRogue_GrGLRenderer crashes on large instanced draws. The // current minimum number of instances observed to crash is somewhere between 2^14 and 2^15. @@ -2701,6 +2710,11 @@ void GrGLCaps::applyDriverCorrectnessWorkarounds(const GrGLContextInfo& ctxInfo, // https://bugreport.apple.com/web/?problemID=39948888 fUnpackRowLengthSupport = false; #endif + + if (isX86PowerVRRogue) { + // On Nexus Player we get incorrect filter modes when using sampler objects. + fSamplerObjectSupport = false; + } } void GrGLCaps::onApplyOptionsOverrides(const GrContextOptions& options) { diff --git a/src/gpu/gl/GrGLCreateNullInterface.cpp b/src/gpu/gl/GrGLCreateNullInterface.cpp index a14fc67351..ebc11b443c 100644 --- a/src/gpu/gl/GrGLCreateNullInterface.cpp +++ b/src/gpu/gl/GrGLCreateNullInterface.cpp @@ -419,6 +419,10 @@ public: SK_ABORT("Not implemented"); } + GrGLvoid genSamplers(GrGLsizei n, GrGLuint* samplers) override { + this->genGenericIds(n, samplers); + } + GrGLvoid genTextures(GrGLsizei n, GrGLuint *textures) override { this->genGenericIds(n, textures); } diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp index 938ead170b..91f9208356 100644 --- a/src/gpu/gl/GrGLGpu.cpp +++ b/src/gpu/gl/GrGLGpu.cpp @@ -173,6 +173,123 @@ bool GrGLGpu::BlendCoeffReferencesConstant(GrBlendCoeff coeff) { GR_STATIC_ASSERT(kGrBlendCoeffCnt == SK_ARRAY_COUNT(gXfermodeCoeff2Blend)); } +static GrGLenum filter_to_gl_mag_filter(GrSamplerState::Filter filter) { + switch (filter) { + case GrSamplerState::Filter::kNearest: return GR_GL_NEAREST; + case GrSamplerState::Filter::kBilerp: return GR_GL_LINEAR; + case GrSamplerState::Filter::kMipMap: return GR_GL_LINEAR; + } + SK_ABORT("Unknown filter"); + return 0; +} + +static GrGLenum filter_to_gl_min_filter(GrSamplerState::Filter filter) { + switch (filter) { + case GrSamplerState::Filter::kNearest: return GR_GL_NEAREST; + case GrSamplerState::Filter::kBilerp: return GR_GL_LINEAR; + case GrSamplerState::Filter::kMipMap: return GR_GL_LINEAR_MIPMAP_LINEAR; + } + SK_ABORT("Unknown filter"); + return 0; +} + +static inline GrGLenum wrap_mode_to_gl_wrap(GrSamplerState::WrapMode wrapMode) { + switch (wrapMode) { + case GrSamplerState::WrapMode::kClamp: return GR_GL_CLAMP_TO_EDGE; + case GrSamplerState::WrapMode::kRepeat: return GR_GL_REPEAT; + case GrSamplerState::WrapMode::kMirrorRepeat: return GR_GL_MIRRORED_REPEAT; + }; + SK_ABORT("Unknown wrap mode"); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +class GrGLGpu::SamplerObjectCache { +public: + SamplerObjectCache(GrGLGpu* gpu) : fGpu(gpu) { + fNumTextureUnits = fGpu->glCaps().shaderCaps()->maxFragmentSamplers(); + fHWBoundSamplers.reset(new GrGLuint[fNumTextureUnits]); + std::fill_n(fHWBoundSamplers.get(), fNumTextureUnits, 0); + std::fill_n(fSamplers, kNumSamplers, 0); + } + + ~SamplerObjectCache() { + if (!fNumTextureUnits) { + // We've already been abandoned. + return; + } + GR_GL_CALL(fGpu->glInterface(), DeleteSamplers(kNumSamplers, fSamplers)); + } + + void bindSampler(int unitIdx, const GrSamplerState& state) { + int index = StateToIndex(state); + if (!fSamplers[index]) { + GrGLuint s; + GR_GL_CALL(fGpu->glInterface(), GenSamplers(1, &s)); + if (!s) { + return; + } + fSamplers[index] = s; + auto minFilter = filter_to_gl_min_filter(state.filter()); + auto magFilter = filter_to_gl_mag_filter(state.filter()); + auto wrapX = wrap_mode_to_gl_wrap(state.wrapModeX()); + auto wrapY = wrap_mode_to_gl_wrap(state.wrapModeY()); + GR_GL_CALL(fGpu->glInterface(), + SamplerParameteri(s, GR_GL_TEXTURE_MIN_FILTER, minFilter)); + GR_GL_CALL(fGpu->glInterface(), + SamplerParameteri(s, GR_GL_TEXTURE_MAG_FILTER, magFilter)); + GR_GL_CALL(fGpu->glInterface(), SamplerParameteri(s, GR_GL_TEXTURE_WRAP_S, wrapX)); + GR_GL_CALL(fGpu->glInterface(), SamplerParameteri(s, GR_GL_TEXTURE_WRAP_T, wrapY)); + } + if (fHWBoundSamplers[unitIdx] != fSamplers[index]) { + GR_GL_CALL(fGpu->glInterface(), BindSampler(unitIdx, fSamplers[index])); + fHWBoundSamplers[unitIdx] = fSamplers[index]; + } + } + + void invalidateBindings() { + // When we have sampler support we always use samplers. So setting these to zero will cause + // a rebind on next usage. + std::fill_n(fHWBoundSamplers.get(), fNumTextureUnits, 0); + } + + void abandon() { + fHWBoundSamplers.reset(); + fNumTextureUnits = 0; + } + + void release() { + if (!fNumTextureUnits) { + // We've already been abandoned. + return; + } + GR_GL_CALL(fGpu->glInterface(), DeleteSamplers(kNumSamplers, fSamplers)); + std::fill_n(fSamplers, kNumSamplers, 0); + // Deleting a bound sampler implicitly binds sampler 0. + std::fill_n(fHWBoundSamplers.get(), fNumTextureUnits, 0); + } + +private: + static int StateToIndex(const GrSamplerState& state) { + int filter = static_cast(state.filter()); + SkASSERT(filter >= 0 && filter < 3); + int wrapX = static_cast(state.wrapModeX()); + SkASSERT(wrapX >= 0 && wrapX < 3); + int wrapY = static_cast(state.wrapModeY()); + SkASSERT(wrapY >= 0 && wrapY < 3); + int idx = 9 * filter + 3 * wrapX + wrapY; + SkASSERT(idx < kNumSamplers); + return idx; + } + + GrGLGpu* fGpu; + static constexpr int kNumSamplers = 27; + std::unique_ptr fHWBoundSamplers; + GrGLuint fSamplers[kNumSamplers]; + int fNumTextureUnits; +}; + /////////////////////////////////////////////////////////////////////////////// sk_sp GrGLGpu::Make(sk_sp interface, const GrContextOptions& options, @@ -208,6 +325,7 @@ GrGLGpu::GrGLGpu(std::unique_ptr ctx, GrContext* context) , fStencilClearFBOID(0) , fHWMinSampleShading(0.0) { SkASSERT(fGLContext); + GrGLClearErr(this->glInterface()); fCaps = sk_ref_sp(fGLContext->caps()); fHWBoundTextureUniqueIDs.reset(this->caps()->shaderCaps()->maxFragmentSamplers()); @@ -234,7 +352,9 @@ GrGLGpu::GrGLGpu(std::unique_ptr ctx, GrContext* context) fPathRendering.reset(new GrGLPathRendering(this)); } - GrGLClearErr(this->glInterface()); + if (this->glCaps().samplerObjectSupport()) { + fSamplerObjectCache.reset(new SamplerObjectCache(this)); + } } GrGLGpu::~GrGLGpu() { @@ -283,6 +403,7 @@ GrGLGpu::~GrGLGpu() { } delete fProgramCache; + fSamplerObjectCache.reset(); } void GrGLGpu::disconnect(DisconnectType type) { @@ -317,10 +438,16 @@ void GrGLGpu::disconnect(DisconnectType type) { if (fClearColorProgram.fProgram) { GL_CALL(DeleteProgram(fClearColorProgram.fProgram)); } + if (fSamplerObjectCache) { + fSamplerObjectCache->release(); + } } else { if (fProgramCache) { fProgramCache->abandon(); } + if (fSamplerObjectCache) { + fSamplerObjectCache->abandon(); + } } fHWProgram.reset(); @@ -432,6 +559,9 @@ void GrGLGpu::onResetContext(uint32_t resetBits) { for (int s = 0; s < fHWBoundTextureUniqueIDs.count(); ++s) { fHWBoundTextureUniqueIDs[s].makeInvalid(); } + if (fSamplerObjectCache) { + fSamplerObjectCache->invalidateBindings(); + } } if (resetBits & kBlend_GrGLBackendState) { @@ -540,7 +670,10 @@ sk_sp GrGLGpu::onWrapBackendTexture(const GrBackendTexture& backendTe GrMipMapsStatus mipMapsStatus = backendTex.hasMipMaps() ? GrMipMapsStatus::kValid : GrMipMapsStatus::kNotAllocated; - return GrGLTexture::MakeWrapped(this, surfDesc, mipMapsStatus, idDesc); + auto texture = GrGLTexture::MakeWrapped(this, surfDesc, mipMapsStatus, idDesc); + // We don't know what parameters are already set on wrapped textures. + texture->textureParamsModified(); + return std::move(texture); } sk_sp GrGLGpu::onWrapRenderableBackendTexture(const GrBackendTexture& backendTex, @@ -586,6 +719,8 @@ sk_sp GrGLGpu::onWrapRenderableBackendTexture(const GrBackendTexture& sk_sp texRT( GrGLTextureRenderTarget::MakeWrapped(this, surfDesc, idDesc, rtIDDesc, mipMapsStatus)); texRT->baseLevelWasBoundToFBO(); + // We don't know what parameters are already set on wrapped textures. + texRT->textureParamsModified(); return std::move(texRT); } @@ -1247,36 +1382,21 @@ static sk_sp return_null_texture() { return nullptr; } -#if 0 && defined(SK_DEBUG) -static size_t as_size_t(int x) { - return x; -} -#endif - -static void set_initial_texture_params(const GrGLInterface* interface, - const GrGLTextureInfo& info, - GrGLTexture::TexParams* initialTexParams) { +static GrGLTexture::SamplerParams set_initial_texture_params(const GrGLInterface* interface, + const GrGLTextureInfo& info) { // Some drivers like to know filter/wrap before seeing glTexImage2D. Some // drivers have a bug where an FBO won't be complete if it includes a // texture that is not mipmap complete (considering the filter in use). - // we only set a subset here so invalidate first - initialTexParams->invalidate(); - initialTexParams->fMinFilter = GR_GL_NEAREST; - initialTexParams->fMagFilter = GR_GL_NEAREST; - initialTexParams->fWrapS = GR_GL_CLAMP_TO_EDGE; - initialTexParams->fWrapT = GR_GL_CLAMP_TO_EDGE; - GR_GL_CALL(interface, TexParameteri(info.fTarget, - GR_GL_TEXTURE_MAG_FILTER, - initialTexParams->fMagFilter)); - GR_GL_CALL(interface, TexParameteri(info.fTarget, - GR_GL_TEXTURE_MIN_FILTER, - initialTexParams->fMinFilter)); - GR_GL_CALL(interface, TexParameteri(info.fTarget, - GR_GL_TEXTURE_WRAP_S, - initialTexParams->fWrapS)); - GR_GL_CALL(interface, TexParameteri(info.fTarget, - GR_GL_TEXTURE_WRAP_T, - initialTexParams->fWrapT)); + GrGLTexture::SamplerParams params; + params.fMinFilter = GR_GL_NEAREST; + params.fMagFilter = GR_GL_NEAREST; + params.fWrapS = GR_GL_CLAMP_TO_EDGE; + params.fWrapT = GR_GL_CLAMP_TO_EDGE; + GR_GL_CALL(interface, TexParameteri(info.fTarget, GR_GL_TEXTURE_MAG_FILTER, params.fMagFilter)); + GR_GL_CALL(interface, TexParameteri(info.fTarget, GR_GL_TEXTURE_MIN_FILTER, params.fMinFilter)); + GR_GL_CALL(interface, TexParameteri(info.fTarget, GR_GL_TEXTURE_WRAP_S, params.fWrapS)); + GR_GL_CALL(interface, TexParameteri(info.fTarget, GR_GL_TEXTURE_WRAP_T, params.fWrapT)); + return params; } sk_sp GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc, @@ -1311,7 +1431,7 @@ sk_sp GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc, GrGLTexture::IDDesc idDesc; idDesc.fOwnership = GrBackendObjectOwnership::kOwned; GrMipMapsStatus mipMapsStatus; - GrGLTexture::TexParams initialTexParams; + GrGLTexture::SamplerParams initialTexParams; if (!this->createTextureImpl(desc, &idDesc.fInfo, isRenderTarget, &initialTexParams, texels, mipLevelCount, &mipMapsStatus)) { return return_null_texture(); @@ -1333,7 +1453,9 @@ sk_sp GrGLGpu::onCreateTexture(const GrSurfaceDesc& desc, } else { tex = sk_make_sp(this, budgeted, desc, idDesc, mipMapsStatus); } - tex->setCachedTexParams(initialTexParams, this->getResetTimestamp()); + + tex->setCachedParams(&initialTexParams, tex->getCachedNonSamplerParams(), + this->getResetTimestamp()); #ifdef TRACE_TEXTURE_CREATION SkDebugf("--- new texture [%d] size=(%d %d) config=%d\n", idDesc.fInfo.fID, desc.fWidth, desc.fHeight, desc.fConfig); @@ -1499,8 +1621,9 @@ int GrGLGpu::getCompatibleStencilIndex(GrPixelConfig config) { } bool GrGLGpu::createTextureImpl(const GrSurfaceDesc& desc, GrGLTextureInfo* info, bool renderTarget, - GrGLTexture::TexParams* initialTexParams, const GrMipLevel texels[], - int mipLevelCount, GrMipMapsStatus* mipMapsStatus) { + GrGLTexture::SamplerParams* initialTexParams, + const GrMipLevel texels[], int mipLevelCount, + GrMipMapsStatus* mipMapsStatus) { info->fID = 0; info->fTarget = GR_GL_TEXTURE_2D; GL_CALL(GenTextures(1, &(info->fID))); @@ -1520,7 +1643,7 @@ bool GrGLGpu::createTextureImpl(const GrSurfaceDesc& desc, GrGLTextureInfo* info } if (info) { - set_initial_texture_params(this->glInterface(), *info, initialTexParams); + *initialTexParams = set_initial_texture_params(this->glInterface(), *info); } if (!this->uploadTexData(desc.fConfig, desc.fWidth, desc.fHeight, info->fTarget, @@ -2690,73 +2813,19 @@ void GrGLGpu::flushBlend(const GrXferProcessor::BlendInfo& blendInfo, const GrSw } } -static inline GrGLenum wrap_mode_to_gl_wrap(GrSamplerState::WrapMode wrapMode) { - switch (wrapMode) { - case GrSamplerState::WrapMode::kClamp: - return GR_GL_CLAMP_TO_EDGE; - case GrSamplerState::WrapMode::kRepeat: - return GR_GL_REPEAT; - case GrSamplerState::WrapMode::kMirrorRepeat: - return GR_GL_MIRRORED_REPEAT; - }; - SK_ABORT("Unknown wrap mode"); - return 0; -} - -static GrGLenum get_component_enum_from_char(char component) { - switch (component) { - case 'r': - return GR_GL_RED; - case 'g': - return GR_GL_GREEN; - case 'b': - return GR_GL_BLUE; - case 'a': - return GR_GL_ALPHA; - default: - SK_ABORT("Unsupported component"); - return 0; - } -} - -/** If texture swizzling is available using tex parameters then it is preferred over mangling - the generated shader code. This potentially allows greater reuse of cached shaders. */ -static void get_tex_param_swizzle(GrPixelConfig config, - const GrGLCaps& caps, - GrGLenum* glSwizzle) { - const GrSwizzle& swizzle = caps.configSwizzle(config); +static void get_gl_swizzle_values(const GrSwizzle& swizzle, GrGLenum glValues[4]) { for (int i = 0; i < 4; ++i) { - glSwizzle[i] = get_component_enum_from_char(swizzle.c_str()[i]); + switch (swizzle[i]) { + case 'r': glValues[i] = GR_GL_RED; break; + case 'g': glValues[i] = GR_GL_GREEN; break; + case 'b': glValues[i] = GR_GL_BLUE; break; + case 'a': glValues[i] = GR_GL_ALPHA; break; + default: SK_ABORT("Unsupported component"); + } } } -static GrGLenum filter_to_gl_mag_filter(GrSamplerState::Filter filter) { - switch (filter) { - case GrSamplerState::Filter::kNearest: - return GR_GL_NEAREST; - case GrSamplerState::Filter::kBilerp: - return GR_GL_LINEAR; - case GrSamplerState::Filter::kMipMap: - return GR_GL_LINEAR; - } - SK_ABORT("Unknown filter"); - return 0; -} - -static GrGLenum filter_to_gl_min_filter(GrSamplerState::Filter filter) { - switch (filter) { - case GrSamplerState::Filter::kNearest: - return GR_GL_NEAREST; - case GrSamplerState::Filter::kBilerp: - return GR_GL_LINEAR; - case GrSamplerState::Filter::kMipMap: - return GR_GL_LINEAR_MIPMAP_LINEAR; - } - SK_ABORT("Unknown filter"); - return 0; -} - -void GrGLGpu::bindTexture(int unitIdx, const GrSamplerState& samplerState, GrGLTexture* texture) { +void GrGLGpu::bindTexture(int unitIdx, GrSamplerState samplerState, GrGLTexture* texture) { SkASSERT(texture); #ifdef SK_DEBUG @@ -2785,87 +2854,109 @@ void GrGLGpu::bindTexture(int unitIdx, const GrSamplerState& samplerState, GrGLT fHWBoundTextureUniqueIDs[unitIdx] = textureID; } - ResetTimestamp timestamp; - const GrGLTexture::TexParams& oldTexParams = texture->getCachedTexParams(×tamp); - bool setAll = timestamp < this->getResetTimestamp(); - GrGLTexture::TexParams newTexParams; - - GrSamplerState::Filter filterMode = samplerState.filter(); - - if (GrSamplerState::Filter::kMipMap == filterMode) { + if (samplerState.filter() == GrSamplerState::Filter::kMipMap) { if (!this->caps()->mipMapSupport() || texture->texturePriv().mipMapped() == GrMipMapped::kNo) { - filterMode = GrSamplerState::Filter::kBilerp; + samplerState.setFilterMode(GrSamplerState::Filter::kBilerp); } } - newTexParams.fMinFilter = filter_to_gl_min_filter(filterMode); - newTexParams.fMagFilter = filter_to_gl_mag_filter(filterMode); - #ifdef SK_DEBUG // We were supposed to ensure MipMaps were up-to-date before getting here. - if (GrSamplerState::Filter::kMipMap == filterMode) { + if (samplerState.filter() == GrSamplerState::Filter::kMipMap) { SkASSERT(!texture->texturePriv().mipMapsAreDirty()); } #endif - newTexParams.fMaxMipMapLevel = texture->texturePriv().maxMipMapLevel(); + ResetTimestamp timestamp = texture->getCachedParamsTimestamp(); + bool setAll = timestamp < this->getResetTimestamp(); - newTexParams.fWrapS = wrap_mode_to_gl_wrap(samplerState.wrapModeX()); - newTexParams.fWrapT = wrap_mode_to_gl_wrap(samplerState.wrapModeY()); - get_tex_param_swizzle(texture->config(), this->glCaps(), newTexParams.fSwizzleRGBA); - if (setAll || newTexParams.fMagFilter != oldTexParams.fMagFilter) { - this->setTextureUnit(unitIdx); - GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MAG_FILTER, newTexParams.fMagFilter)); - } - if (setAll || newTexParams.fMinFilter != oldTexParams.fMinFilter) { - this->setTextureUnit(unitIdx); - GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MIN_FILTER, newTexParams.fMinFilter)); - } - if (setAll || newTexParams.fMaxMipMapLevel != oldTexParams.fMaxMipMapLevel) { - // These are not supported in ES2 contexts + const GrGLTexture::SamplerParams* samplerParamsToRecord = nullptr; + GrGLTexture::SamplerParams newSamplerParams; + if (fSamplerObjectCache) { + fSamplerObjectCache->bindSampler(unitIdx, samplerState); + } else { + const GrGLTexture::SamplerParams& oldSamplerParams = texture->getCachedSamplerParams(); + samplerParamsToRecord = &newSamplerParams; + + newSamplerParams.fMinFilter = filter_to_gl_min_filter(samplerState.filter()); + newSamplerParams.fMagFilter = filter_to_gl_mag_filter(samplerState.filter()); + + newSamplerParams.fWrapS = wrap_mode_to_gl_wrap(samplerState.wrapModeX()); + newSamplerParams.fWrapT = wrap_mode_to_gl_wrap(samplerState.wrapModeY()); + + // These are the OpenGL default values. + newSamplerParams.fMinLOD = -1000; + newSamplerParams.fMaxLOD = 1000; + + if (setAll || newSamplerParams.fMagFilter != oldSamplerParams.fMagFilter) { + this->setTextureUnit(unitIdx); + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MAG_FILTER, newSamplerParams.fMagFilter)); + } + if (setAll || newSamplerParams.fMinFilter != oldSamplerParams.fMinFilter) { + this->setTextureUnit(unitIdx); + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MIN_FILTER, newSamplerParams.fMinFilter)); + } if (this->glCaps().mipMapLevelAndLodControlSupport()) { - if (newTexParams.fMaxMipMapLevel != 0) { + // Min and max LOD are actually floats. We don't curently support glTexParameterf. + // However, we only set these to integral floats (see above). + if (setAll || newSamplerParams.fMinLOD != oldSamplerParams.fMinLOD) { this->setTextureUnit(unitIdx); - GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MIN_LOD, 0)); - GL_CALL(TexParameteri(target, GR_GL_TEXTURE_BASE_LEVEL, 0)); - GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MAX_LOD, - newTexParams.fMaxMipMapLevel)); - GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MAX_LEVEL, - newTexParams.fMaxMipMapLevel)); + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MIN_LOD, newSamplerParams.fMinLOD)); + } + if (setAll || newSamplerParams.fMaxLOD != oldSamplerParams.fMaxLOD) { + this->setTextureUnit(unitIdx); + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MAX_LOD, newSamplerParams.fMaxLOD)); + } + } + if (setAll || newSamplerParams.fWrapS != oldSamplerParams.fWrapS) { + this->setTextureUnit(unitIdx); + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_WRAP_S, newSamplerParams.fWrapS)); + } + if (setAll || newSamplerParams.fWrapT != oldSamplerParams.fWrapT) { + this->setTextureUnit(unitIdx); + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_WRAP_T, newSamplerParams.fWrapT)); + } + } + GrGLTexture::NonSamplerParams newNonSamplerParams; + newNonSamplerParams.fBaseMipMapLevel = 0; + newNonSamplerParams.fMaxMipMapLevel = texture->texturePriv().maxMipMapLevel(); + + const GrGLTexture::NonSamplerParams& oldNonSamplerParams = texture->getCachedNonSamplerParams(); + if (this->glCaps().textureSwizzleSupport()) { + auto swizzle = this->glCaps().configSwizzle(texture->config()); + newNonSamplerParams.fSwizzleKey = swizzle.asKey(); + if (setAll || swizzle.asKey() != oldNonSamplerParams.fSwizzleKey) { + GrGLenum glValues[4]; + get_gl_swizzle_values(swizzle, glValues); + this->setTextureUnit(unitIdx); + if (this->glStandard() == kGLES_GrGLStandard) { + // ES3 added swizzle support but not GL_TEXTURE_SWIZZLE_RGBA. + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_SWIZZLE_R, glValues[0])); + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_SWIZZLE_G, glValues[1])); + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_SWIZZLE_B, glValues[2])); + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_SWIZZLE_A, glValues[3])); + } else { + GR_STATIC_ASSERT(sizeof(glValues[0]) == sizeof(GrGLint)); + GL_CALL(TexParameteriv(target, GR_GL_TEXTURE_SWIZZLE_RGBA, + reinterpret_cast(glValues))); } } } - if (setAll || newTexParams.fWrapS != oldTexParams.fWrapS) { - this->setTextureUnit(unitIdx); - GL_CALL(TexParameteri(target, GR_GL_TEXTURE_WRAP_S, newTexParams.fWrapS)); - } - if (setAll || newTexParams.fWrapT != oldTexParams.fWrapT) { - this->setTextureUnit(unitIdx); - GL_CALL(TexParameteri(target, GR_GL_TEXTURE_WRAP_T, newTexParams.fWrapT)); - } - if (this->glCaps().textureSwizzleSupport() && - (setAll || memcmp(newTexParams.fSwizzleRGBA, - oldTexParams.fSwizzleRGBA, - sizeof(newTexParams.fSwizzleRGBA)))) { - this->setTextureSwizzle(unitIdx, target, newTexParams.fSwizzleRGBA); - } - texture->setCachedTexParams(newTexParams, this->getResetTimestamp()); -} - -void GrGLGpu::setTextureSwizzle(int unitIdx, GrGLenum target, const GrGLenum swizzle[]) { - this->setTextureUnit(unitIdx); - if (this->glStandard() == kGLES_GrGLStandard) { - // ES3 added swizzle support but not GL_TEXTURE_SWIZZLE_RGBA. - GL_CALL(TexParameteri(target, GR_GL_TEXTURE_SWIZZLE_R, swizzle[0])); - GL_CALL(TexParameteri(target, GR_GL_TEXTURE_SWIZZLE_G, swizzle[1])); - GL_CALL(TexParameteri(target, GR_GL_TEXTURE_SWIZZLE_B, swizzle[2])); - GL_CALL(TexParameteri(target, GR_GL_TEXTURE_SWIZZLE_A, swizzle[3])); - } else { - GR_STATIC_ASSERT(sizeof(swizzle[0]) == sizeof(GrGLint)); - GL_CALL(TexParameteriv(target, GR_GL_TEXTURE_SWIZZLE_RGBA, - reinterpret_cast(swizzle))); + // These are not supported in ES2 contexts + if (this->glCaps().mipMapLevelAndLodControlSupport()) { + if (newNonSamplerParams.fBaseMipMapLevel != oldNonSamplerParams.fBaseMipMapLevel) { + this->setTextureUnit(unitIdx); + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_BASE_LEVEL, + newNonSamplerParams.fBaseMipMapLevel)); + } + if (newNonSamplerParams.fMaxMipMapLevel != oldNonSamplerParams.fMaxMipMapLevel) { + this->setTextureUnit(unitIdx); + GL_CALL(TexParameteri(target, GR_GL_TEXTURE_MAX_LEVEL, + newNonSamplerParams.fMaxMipMapLevel)); + } } + texture->setCachedParams(samplerParamsToRecord, newNonSamplerParams, this->getResetTimestamp()); } void GrGLGpu::flushColorWrite(bool writeColor) { @@ -3854,12 +3945,15 @@ bool GrGLGpu::onRegenerateMipMapLevels(GrTexture* texture) { 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"); + // Invalidate all params to cover base level change in a previous iteration. + glTex->textureParamsModified(); return false; } } @@ -3892,7 +3986,10 @@ bool GrGLGpu::onRegenerateMipMapLevels(GrTexture* texture) { GR_GL_TEXTURE_2D, 0, 0)); // We modified the base level param. - texture->textureParamsModified(); + GrGLTexture::NonSamplerParams params = glTex->getCachedNonSamplerParams(); + params.fBaseMipMapLevel = levelCount - 2; // we drew the 2nd to last level into the last level. + glTex->setCachedParams(nullptr, params, this->getResetTimestamp()); + return true; } diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h index 87f452fc98..1fa18c5146 100644 --- a/src/gpu/gl/GrGLGpu.h +++ b/src/gpu/gl/GrGLGpu.h @@ -56,7 +56,7 @@ public: } // Used by GrGLProgram to configure OpenGL state. - void bindTexture(int unitIdx, const GrSamplerState& samplerState, GrGLTexture* texture); + void bindTexture(int unitIdx, GrSamplerState samplerState, GrGLTexture* texture); // These functions should be used to bind GL objects. They track the GL state and skip redundant // bindings. Making the equivalent glBind calls directly will confuse the state tracking. @@ -208,7 +208,7 @@ private: // The texture is populated with |texels|, if it exists. // The texture parameters are cached in |initialTexParams|. bool createTextureImpl(const GrSurfaceDesc& desc, GrGLTextureInfo* info, bool renderTarget, - GrGLTexture::TexParams* initialTexParams, const GrMipLevel texels[], + GrGLTexture::SamplerParams* initialTexParams, const GrMipLevel texels[], int mipLevelCount, GrMipMapsStatus* mipMapsStatus); // Checks whether glReadPixels can be called to get pixel values in readConfig from the @@ -631,6 +631,9 @@ private: GrPrimitiveType fLastPrimitiveType; bool fRequiresFlushBeforeNextInstancedDraw = false; + class SamplerObjectCache; + std::unique_ptr fSamplerObjectCache; + std::unique_ptr fCachedRTCommandBuffer; std::unique_ptr fCachedTexCommandBuffer; diff --git a/src/gpu/gl/GrGLTexture.cpp b/src/gpu/gl/GrGLTexture.cpp index 7cdf5059a6..47878da42d 100644 --- a/src/gpu/gl/GrGLTexture.cpp +++ b/src/gpu/gl/GrGLTexture.cpp @@ -68,8 +68,7 @@ GrGLTexture::GrGLTexture(GrGLGpu* gpu, const GrSurfaceDesc& desc, const IDDesc& void GrGLTexture::init(const GrSurfaceDesc& desc, const IDDesc& idDesc) { SkASSERT(0 != idDesc.fInfo.fID); SkASSERT(0 != idDesc.fInfo.fFormat); - fTexParams.invalidate(); - fTexParamsTimestamp = GrGpu::kExpiredTimestamp; + fParamsTimestamp = GrGpu::kExpiredTimestamp; fID = idDesc.fInfo.fID; fFormat = idDesc.fInfo.fFormat; fTextureIDOwnership = idDesc.fOwnership; diff --git a/src/gpu/gl/GrGLTexture.h b/src/gpu/gl/GrGLTexture.h index cb029cca81..5f662e0c18 100644 --- a/src/gpu/gl/GrGLTexture.h +++ b/src/gpu/gl/GrGLTexture.h @@ -17,14 +17,37 @@ class GrGLGpu; class GrGLTexture : public GrTexture { public: - struct TexParams { - GrGLenum fMinFilter; - GrGLenum fMagFilter; - GrGLenum fWrapS; - GrGLenum fWrapT; - GrGLenum fMaxMipMapLevel; - GrGLenum fSwizzleRGBA[4]; - void invalidate() { memset(this, 0xff, sizeof(TexParams)); } + // Texture state that overlaps with sampler object state. We don't need to track this if we + // are using sampler objects. + struct SamplerParams { + // These are the OpenGL defaults. + GrGLenum fMinFilter = GR_GL_NEAREST_MIPMAP_LINEAR; + GrGLenum fMagFilter = GR_GL_LINEAR; + GrGLenum fWrapS = GR_GL_REPEAT; + GrGLenum fWrapT = GR_GL_REPEAT; + GrGLfloat fMinLOD = -1000.f; + GrGLfloat fMaxLOD = 1000.f; + void invalidate() { + fMinFilter = ~0U; + fMagFilter = ~0U; + fWrapS = ~0U; + fWrapT = ~0U; + fMinLOD = SK_ScalarNaN; + fMaxLOD = SK_ScalarNaN; + } + }; + + // Texture state that does not overlap with sampler object state. + struct NonSamplerParams { + // These are the OpenGL defaults. + uint32_t fSwizzleKey = GrSwizzle::RGBA().asKey(); + GrGLint fBaseMipMapLevel = 0; + GrGLint fMaxMipMapLevel = 1000; + void invalidate() { + fSwizzleKey = ~0U; + fBaseMipMapLevel = ~0; + fMaxMipMapLevel = ~0; + } }; struct IDDesc { @@ -43,22 +66,28 @@ public: GrBackendTexture getBackendTexture() const override; - void textureParamsModified() override { fTexParams.invalidate(); } + void textureParamsModified() override { + fSamplerParams.invalidate(); + fNonSamplerParams.invalidate(); + } void setRelease(sk_sp releaseHelper) override { fReleaseHelper = std::move(releaseHelper); } // These functions are used to track the texture parameters associated with the texture. - const TexParams& getCachedTexParams(GrGpu::ResetTimestamp* timestamp) const { - *timestamp = fTexParamsTimestamp; - return fTexParams; - } + GrGpu::ResetTimestamp getCachedParamsTimestamp() const { return fParamsTimestamp; } + const SamplerParams& getCachedSamplerParams() const { return fSamplerParams; } + const NonSamplerParams& getCachedNonSamplerParams() const { return fNonSamplerParams; } - void setCachedTexParams(const TexParams& texParams, - GrGpu::ResetTimestamp timestamp) { - fTexParams = texParams; - fTexParamsTimestamp = timestamp; + void setCachedParams(const SamplerParams* samplerParams, + const NonSamplerParams& nonSamplerParams, + GrGpu::ResetTimestamp currTimestamp) { + if (samplerParams) { + fSamplerParams = *samplerParams; + } + fNonSamplerParams = nonSamplerParams; + fParamsTimestamp = currTimestamp; } GrGLuint textureID() const { return fID; } @@ -97,14 +126,14 @@ private: } } - TexParams fTexParams; - GrGpu::ResetTimestamp fTexParamsTimestamp; - GrGLuint fID; - GrGLenum fFormat; - GrBackendObjectOwnership fTextureIDOwnership; - bool fBaseLevelHasBeenBoundToFBO = false; - - sk_sp fReleaseHelper; + SamplerParams fSamplerParams; + NonSamplerParams fNonSamplerParams; + GrGpu::ResetTimestamp fParamsTimestamp; + sk_sp fReleaseHelper; + GrGLuint fID; + GrGLenum fFormat; + GrBackendObjectOwnership fTextureIDOwnership; + bool fBaseLevelHasBeenBoundToFBO = false; typedef GrTexture INHERITED; };