Add GrContext::resetGLTextureBindings().
This function binds texture ID 0 to any texture unit/target combo that
Skia has modified.
Bug: chromium:926017
Change-Id: I3ac8f8050c863232886102886e60d3b91a5380c9
Reviewed-on: https://skia-review.googlesource.com/c/190663
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/gn/tests.gni b/gn/tests.gni
index 4df8bae..c3901af 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -263,6 +263,7 @@
"$_tests/TableColorFilterTest.cpp",
"$_tests/TemplatesTest.cpp",
"$_tests/TessellatingPathRendererTests.cpp",
+ "$_tests/TextureBindingsResetTest.cpp",
"$_tests/Test.cpp",
"$_tests/TestTest.cpp",
"$_tests/TestUtils.h",
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 67319d5..0ff2e0d 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -90,6 +90,17 @@
void resetContext(uint32_t state = kAll_GrBackendState);
/**
+ * If the backend is GrBackendApi::kOpenGL, then all texture unit/target combinations for which
+ * the GrContext has modified the bound texture will have texture id 0 bound. This does not
+ * flush the GrContext. Calling resetContext() does not change the set that will be bound
+ * to texture id 0 on the next call to resetGLTextureBindings(). After this is called
+ * all unit/target combinations are considered to have unmodified bindings until the GrContext
+ * subsequently modifies them (meaning if this is called twice in a row with no intervening
+ * GrContext usage then the second call is a no-op.)
+ */
+ void resetGLTextureBindings();
+
+ /**
* Abandons all GPU resources and assumes the underlying backend 3D API context is no longer
* usable. Call this if you have lost the associated GPU context, and thus internal texture,
* buffer, etc. references/IDs are now invalid. Calling this ensures that the destructors of the
diff --git a/include/private/GrTypesPriv.h b/include/private/GrTypesPriv.h
index f0f85c7..06f14a7 100644
--- a/include/private/GrTypesPriv.h
+++ b/include/private/GrTypesPriv.h
@@ -241,8 +241,8 @@
};
struct GrMipLevel {
- const void* fPixels;
- size_t fRowBytes;
+ const void* fPixels = nullptr;
+ size_t fRowBytes = 0;
};
/**
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 8d7f945..fc8c7f5 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -193,6 +193,13 @@
fTextBlobCache->freeAll();
}
+void GrContext::resetGLTextureBindings() {
+ if (this->abandoned() || this->backend() != GrBackendApi::kOpenGL) {
+ return;
+ }
+ fGpu->resetTextureBindings();
+}
+
void GrContext::resetContext(uint32_t state) {
ASSERT_SINGLE_OWNER
fGpu->markContextDirty(state);
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 2600076..a1a0ea1 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -346,6 +346,11 @@
return false;
}
+void GrGpu::resetTextureBindings() {
+ this->handleDirtyContext();
+ this->onResetTextureBindings();
+}
+
void GrGpu::resolveRenderTarget(GrRenderTarget* target) {
SkASSERT(target);
this->handleDirtyContext();
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 867af54..8d1a803 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -154,6 +154,11 @@
bool regenerateMipMapLevels(GrTexture*);
/**
+ * If the backend API has stateful texture bindings, this resets them back to defaults.
+ */
+ void resetTextureBindings();
+
+ /**
* Reads a rectangle of pixels from a render target. No sRGB/linear conversions are performed.
*
* @param surface The surface to read from
@@ -453,6 +458,9 @@
// assumed 3D context state and dirty any state cache.
virtual void onResetContext(uint32_t resetBits) = 0;
+ // Implementation of resetTextureBindings.
+ virtual void onResetTextureBindings() {}
+
// Called before certain draws in order to guarantee coherent results from dst reads.
virtual void xferBarrier(GrRenderTarget*, GrXferBarrierType) = 0;
diff --git a/src/gpu/gl/GrGLDefines.h b/src/gpu/gl/GrGLDefines.h
index 995dbf3..aa7e883 100644
--- a/src/gpu/gl/GrGLDefines.h
+++ b/src/gpu/gl/GrGLDefines.h
@@ -1038,9 +1038,11 @@
/* GL_OES_EGL_image_external */
#define GR_GL_TEXTURE_EXTERNAL 0x8D65
+#define GR_GL_TEXTURE_BINDING_EXTERNAL 0x8D67
/* GL_ARB_texture_rectangle or GL_ANGLE_texture_rectangle */
#define GR_GL_TEXTURE_RECTANGLE 0x84F5
+#define GR_GL_TEXTURE_BINDING_RECTANGLE 0x84F6
/* GL_EXT_window_rectangles */
#define GR_GL_MAX_WINDOW_RECTANGLES 0x8f14
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index d8c43f3..a2cab2e 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -199,20 +199,29 @@
}
GrGpuResource::UniqueID GrGLGpu::TextureUnitBindings::boundID(GrGLenum target) const {
- return fBoundResourceIDs[gl_target_to_binding_index(target)];
+ return fTargetBindings[gl_target_to_binding_index(target)].fBoundResourceID;
+}
+
+bool GrGLGpu::TextureUnitBindings::hasBeenModified(GrGLenum target) const {
+ return fTargetBindings[gl_target_to_binding_index(target)].fHasBeenModified;
}
void GrGLGpu::TextureUnitBindings::setBoundID(GrGLenum target, GrGpuResource::UniqueID resourceID) {
- fBoundResourceIDs[gl_target_to_binding_index(target)] = resourceID;
+ int targetIndex = gl_target_to_binding_index(target);
+ fTargetBindings[targetIndex].fBoundResourceID = resourceID;
+ fTargetBindings[targetIndex].fHasBeenModified = true;
}
-void GrGLGpu::TextureUnitBindings::invalidate(GrGLenum target) {
- fBoundResourceIDs[gl_target_to_binding_index(target)].makeInvalid();
+void GrGLGpu::TextureUnitBindings::invalidateForScratchUse(GrGLenum target) {
+ this->setBoundID(target, GrGpuResource::UniqueID());
}
-void GrGLGpu::TextureUnitBindings::invalidateAllTargets() {
- for (auto& resourceID : fBoundResourceIDs) {
- resourceID.makeInvalid();
+void GrGLGpu::TextureUnitBindings::invalidateAllTargets(bool markUnmodified) {
+ for (auto& targetBinding : fTargetBindings) {
+ targetBinding.fBoundResourceID.makeInvalid();
+ if (markUnmodified) {
+ targetBinding.fHasBeenModified = false;
+ }
}
}
@@ -586,7 +595,7 @@
if (resetBits & kTextureBinding_GrGLBackendState) {
for (int s = 0; s < this->numTextureUnits(); ++s) {
- fHWTextureUnitBindings[s].invalidateAllTargets();
+ fHWTextureUnitBindings[s].invalidateAllTargets(false);
}
if (fSamplerObjectCache) {
fSamplerObjectCache->invalidateBindings();
@@ -3107,6 +3116,20 @@
texture->setCachedParams(samplerParamsToRecord, newNonSamplerParams, this->getResetTimestamp());
}
+void GrGLGpu::onResetTextureBindings() {
+ static constexpr GrGLenum kTargets[] = {GR_GL_TEXTURE_2D, GR_GL_TEXTURE_RECTANGLE,
+ GR_GL_TEXTURE_EXTERNAL};
+ for (int i = 0; i < this->numTextureUnits(); ++i) {
+ this->setTextureUnit(i);
+ for (auto target : kTargets) {
+ if (fHWTextureUnitBindings[i].hasBeenModified(target)) {
+ GL_CALL(BindTexture(target, 0));
+ }
+ }
+ fHWTextureUnitBindings[i].invalidateAllTargets(true);
+ }
+}
+
void GrGLGpu::flushColorWrite(bool writeColor) {
if (!writeColor) {
if (kNo_TriState != fHWWriteToColor) {
@@ -3150,7 +3173,7 @@
}
// Clear out the this field so that if a GrGLProgram does use this unit it will rebind the
// correct texture.
- fHWTextureUnitBindings[lastUnitIdx].invalidate(target);
+ fHWTextureUnitBindings[lastUnitIdx].invalidateForScratchUse(target);
GL_CALL(BindTexture(target, textureID));
}
@@ -3980,10 +4003,8 @@
info.fTarget = GR_GL_TEXTURE_2D;
info.fID = 0;
GL_CALL(GenTextures(1, &info.fID));
- GL_CALL(ActiveTexture(GR_GL_TEXTURE0));
+ this->bindTextureToScratchUnit(info.fTarget, info.fID);
GL_CALL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, 1));
- GL_CALL(BindTexture(info.fTarget, info.fID));
- fHWTextureUnitBindings[0].invalidate(info.fTarget);
GL_CALL(TexParameteri(info.fTarget, GR_GL_TEXTURE_MAG_FILTER, GR_GL_NEAREST));
GL_CALL(TexParameteri(info.fTarget, GR_GL_TEXTURE_MIN_FILTER, GR_GL_NEAREST));
GL_CALL(TexParameteri(info.fTarget, GR_GL_TEXTURE_WRAP_S, GR_GL_CLAMP_TO_EDGE));
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index b559dee..f14bd23 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -182,6 +182,8 @@
// GrGpu overrides
void onResetContext(uint32_t resetBits) override;
+ void onResetTextureBindings() override;
+
void xferBarrier(GrRenderTarget*, GrXferBarrierType) override;
sk_sp<GrTexture> onCreateTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted,
@@ -603,12 +605,17 @@
TextureUnitBindings& operator=(const TextureUnitBindings&) = delete;
GrGpuResource::UniqueID boundID(GrGLenum target) const;
+ bool hasBeenModified(GrGLenum target) const;
void setBoundID(GrGLenum target, GrGpuResource::UniqueID);
- void invalidate(GrGLenum target);
- void invalidateAllTargets();
+ void invalidateForScratchUse(GrGLenum target);
+ void invalidateAllTargets(bool markUnmodified);
private:
- GrGpuResource::UniqueID fBoundResourceIDs[3];
+ struct TargetBinding {
+ GrGpuResource::UniqueID fBoundResourceID;
+ bool fHasBeenModified = false;
+ };
+ TargetBinding fTargetBindings[3];
};
SkAutoTArray<TextureUnitBindings> fHWTextureUnitBindings;
diff --git a/tests/TextureBindingsResetTest.cpp b/tests/TextureBindingsResetTest.cpp
new file mode 100644
index 0000000..b338754
--- /dev/null
+++ b/tests/TextureBindingsResetTest.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrContextPriv.h"
+#include "SkSurface.h"
+#include "Test.h"
+#include "gl/GrGLDefines.h"
+#include "gl/GrGLGpu.h"
+#include "gl/GrGLUtil.h"
+
+DEF_GPUTEST_FOR_GL_RENDERING_CONTEXTS(TextureBindingsResetTest, reporter, ctxInfo) {
+#define GL(F) GR_GL_CALL(ctxInfo.glContext()->gl(), F)
+
+ GrContext* context = ctxInfo.grContext();
+ GrGLGpu* gpu = static_cast<GrGLGpu*>(context->priv().getGpu());
+
+ struct Target {
+ GrGLenum fName;
+ GrGLenum fQuery;
+ };
+ SkTDArray<Target> targets;
+ targets.push_back({GR_GL_TEXTURE_2D, GR_GL_TEXTURE_BINDING_2D});
+ bool supportExternal;
+ if ((supportExternal = gpu->glCaps().shaderCaps()->externalTextureSupport())) {
+ targets.push_back({GR_GL_TEXTURE_EXTERNAL, GR_GL_TEXTURE_BINDING_EXTERNAL});
+ }
+ bool supportRectangle;
+ if ((supportRectangle = gpu->glCaps().rectangleTextureSupport())) {
+ targets.push_back({GR_GL_TEXTURE_RECTANGLE, GR_GL_TEXTURE_BINDING_RECTANGLE});
+ }
+ GrGLint numUnits;
+ GL(GetIntegerv(GR_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numUnits));
+ SkTDArray<GrGLuint> claimedIDs;
+ claimedIDs.setCount(numUnits * targets.count());
+ GL(GenTextures(claimedIDs.count(), claimedIDs.begin()));
+
+ auto resetBindings = [&] {
+ int i = 0;
+ for (int u = 0; u < numUnits; ++u) {
+ GL(ActiveTexture(GR_GL_TEXTURE0 + u));
+ for (auto target : targets) {
+ GL(BindTexture(target.fName, claimedIDs[i++]));
+ }
+ }
+ };
+ auto checkBindings = [&] {
+ int i = 0;
+ for (int u = 0; u < numUnits; ++u) {
+ GL(ActiveTexture(GR_GL_TEXTURE0 + u));
+ for (auto target : targets) {
+ GrGLuint boundID = ~0;
+ GL(GetIntegerv(target.fQuery, reinterpret_cast<GrGLint*>(&boundID)));
+ if (boundID != claimedIDs[i] && boundID != 0) {
+ ERRORF(reporter, "Unit %d, target 0x%04x has ID %d bound. Expected %d or 0.", u,
+ target.fName, boundID, claimedIDs[i]);
+ return;
+ }
+ ++i;
+ }
+ }
+ };
+
+ // Initialize texture unit/target combo bindings to 0.
+ context->flush();
+ resetBindings();
+ context->resetContext();
+
+ // Test creating a texture and then resetting bindings.
+ GrSurfaceDesc desc;
+ desc.fWidth = desc.fHeight = 10;
+ desc.fConfig = kRGBA_8888_GrPixelConfig;
+ auto tex = gpu->createTexture(desc, SkBudgeted::kNo);
+ REPORTER_ASSERT(reporter, tex);
+ context->resetGLTextureBindings();
+ checkBindings();
+ resetBindings();
+ context->resetContext();
+
+ // Test drawing and then resetting bindings. This should force a MIP regeneration if MIP
+ // maps are supported as well.
+ auto info = SkImageInfo::Make(10, 10, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+ auto surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kYes, info, 1, nullptr);
+ surf->getCanvas()->clear(0x80FF0000);
+ auto img = surf->makeImageSnapshot();
+ surf->getCanvas()->clear(SK_ColorBLUE);
+ surf->getCanvas()->save();
+ surf->getCanvas()->scale(0.25, 0.25);
+ SkPaint paint;
+ paint.setFilterQuality(kHigh_SkFilterQuality);
+ surf->getCanvas()->drawImage(img, 0, 0, &paint);
+ surf->getCanvas()->restore();
+ surf->flush();
+ context->resetGLTextureBindings();
+ checkBindings();
+ resetBindings();
+ context->resetContext();
+
+ if (supportExternal) {
+ GrBackendTexture texture2D = gpu->createTestingOnlyBackendTexture(
+ nullptr, 10, 10, GrColorType::kRGBA_8888, false, GrMipMapped::kNo);
+ GrGLTextureInfo info2D;
+ REPORTER_ASSERT(reporter, texture2D.getGLTextureInfo(&info2D));
+ GrEGLImage eglImage = ctxInfo.glContext()->texture2DToEGLImage(info2D.fID);
+ REPORTER_ASSERT(reporter, eglImage);
+ GrGLTextureInfo infoExternal;
+ infoExternal.fID = ctxInfo.glContext()->eglImageToExternalTexture(eglImage);
+ infoExternal.fTarget = GR_GL_TEXTURE_EXTERNAL;
+ infoExternal.fFormat = info2D.fFormat;
+ REPORTER_ASSERT(reporter, infoExternal.fID);
+ GrBackendTexture backendTexture(10, 10, GrMipMapped::kNo, infoExternal);
+ // Above texture creation will have messed with GL state and bindings.
+ resetBindings();
+ context->resetContext();
+ img = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
+ kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
+ REPORTER_ASSERT(reporter, img);
+ surf->getCanvas()->drawImage(img, 0, 0);
+ img.reset();
+ surf->flush();
+ context->resetGLTextureBindings();
+ checkBindings();
+ resetBindings();
+ GL(DeleteTextures(1, &infoExternal.fID));
+ ctxInfo.glContext()->destroyEGLImage(eglImage);
+ gpu->deleteTestingOnlyBackendTexture(texture2D);
+ context->resetContext();
+ }
+
+ if (supportRectangle) {
+ GrGLuint id = ctxInfo.glContext()->createTextureRectangle(10, 10, GR_GL_RGBA, GR_GL_RGBA,
+ GR_GL_UNSIGNED_BYTE, nullptr);
+ // Above texture creation will have messed with GL state and bindings.
+ resetBindings();
+ context->resetContext();
+ if (id) {
+ GrGLTextureInfo info;
+ info.fTarget = GR_GL_TEXTURE_RECTANGLE;
+ info.fFormat = GR_GL_RGBA8;
+ info.fID = id;
+ GrBackendTexture backendTexture(10, 10, GrMipMapped::kNo, info);
+ img = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
+ kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
+ REPORTER_ASSERT(reporter, img);
+ surf->getCanvas()->drawImage(img, 0, 0);
+ img.reset();
+ surf->flush();
+ context->resetGLTextureBindings();
+ checkBindings();
+ resetBindings();
+ GL(DeleteTextures(1, &id));
+ context->resetContext();
+ }
+ }
+
+ GL(DeleteTextures(claimedIDs.count(), claimedIDs.begin()));
+
+#undef GL
+}
diff --git a/tools/gpu/gl/GLTestContext.cpp b/tools/gpu/gl/GLTestContext.cpp
index e11f1b0..2e12b47 100644
--- a/tools/gpu/gl/GLTestContext.cpp
+++ b/tools/gpu/gl/GLTestContext.cpp
@@ -314,9 +314,9 @@
}
}
-GrGLint GLTestContext::createTextureRectangle(int width, int height, GrGLenum internalFormat,
- GrGLenum externalFormat, GrGLenum externalType,
- GrGLvoid* data) {
+GrGLuint GLTestContext::createTextureRectangle(int width, int height, GrGLenum internalFormat,
+ GrGLenum externalFormat, GrGLenum externalType,
+ GrGLvoid* data) {
// Should match GrGLCaps check for fRectangleTextureSupport.
if (kGL_GrGLStandard != fGL->fStandard ||
(GrGLGetVersion(fGL.get()) < GR_GL_VER(3, 1) &&
diff --git a/tools/gpu/gl/GLTestContext.h b/tools/gpu/gl/GLTestContext.h
index d376ed6..c431664 100644
--- a/tools/gpu/gl/GLTestContext.h
+++ b/tools/gpu/gl/GLTestContext.h
@@ -32,9 +32,8 @@
virtual void destroyEGLImage(GrEGLImage) const { }
/** Used for testing GL_TEXTURE_RECTANGLE integration. */
- GrGLint createTextureRectangle(int width, int height, GrGLenum internalFormat,
- GrGLenum externalFormat, GrGLenum externalType,
- GrGLvoid *data);
+ GrGLuint createTextureRectangle(int width, int height, GrGLenum internalFormat,
+ GrGLenum externalFormat, GrGLenum externalType, GrGLvoid* data);
/**
* Used for testing EGLImage integration. Takes a EGLImage and wraps it in a