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>
This commit is contained in:
Brian Salomon 2019-02-08 12:33:08 -05:00 committed by Skia Commit-Bot
parent 3e8392e42c
commit 1f05d459e2
12 changed files with 246 additions and 23 deletions

View File

@ -263,6 +263,7 @@ tests_sources = [
"$_tests/TableColorFilterTest.cpp",
"$_tests/TemplatesTest.cpp",
"$_tests/TessellatingPathRendererTests.cpp",
"$_tests/TextureBindingsResetTest.cpp",
"$_tests/Test.cpp",
"$_tests/TestTest.cpp",
"$_tests/TestUtils.h",

View File

@ -89,6 +89,17 @@ public:
*/
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,

View File

@ -241,8 +241,8 @@ enum class GrScissorTest : bool {
};
struct GrMipLevel {
const void* fPixels;
size_t fRowBytes;
const void* fPixels = nullptr;
size_t fRowBytes = 0;
};
/**

View File

@ -193,6 +193,13 @@ void GrContext::releaseResourcesAndAbandonContext() {
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);

View File

@ -346,6 +346,11 @@ bool GrGpu::regenerateMipMapLevels(GrTexture* texture) {
return false;
}
void GrGpu::resetTextureBindings() {
this->handleDirtyContext();
this->onResetTextureBindings();
}
void GrGpu::resolveRenderTarget(GrRenderTarget* target) {
SkASSERT(target);
this->handleDirtyContext();

View File

@ -153,6 +153,11 @@ public:
*/
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.
*
@ -453,6 +458,9 @@ private:
// 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;

View File

@ -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

View File

@ -199,20 +199,29 @@ static int gl_target_to_binding_index(GrGLenum target) {
}
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 @@ void GrGLGpu::onResetContext(uint32_t resetBits) {
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 @@ void GrGLGpu::bindTexture(int unitIdx, GrSamplerState samplerState, GrGLTexture*
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 @@ void GrGLGpu::bindTextureToScratchUnit(GrGLenum target, GrGLint textureID) {
}
// 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 @@ GrBackendTexture GrGLGpu::createTestingOnlyBackendTexture(const void* pixels, in
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));

View File

@ -182,6 +182,8 @@ private:
// 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 @@ private:
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;

View File

@ -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
}

View File

@ -314,9 +314,9 @@ void GLTestContext::finish() {
}
}
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) &&

View File

@ -32,9 +32,8 @@ public:
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