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:
parent
3e8392e42c
commit
1f05d459e2
@ -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",
|
||||
|
@ -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,
|
||||
|
@ -241,8 +241,8 @@ enum class GrScissorTest : bool {
|
||||
};
|
||||
|
||||
struct GrMipLevel {
|
||||
const void* fPixels;
|
||||
size_t fRowBytes;
|
||||
const void* fPixels = nullptr;
|
||||
size_t fRowBytes = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
||||
|
162
tests/TextureBindingsResetTest.cpp
Normal file
162
tests/TextureBindingsResetTest.cpp
Normal 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
|
||||
}
|
@ -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) &&
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user