From 459c367ed5a524ce0aad1877c1e39e82126f2707 Mon Sep 17 00:00:00 2001 From: Geoff Lang Date: Fri, 21 Jan 2022 10:02:33 -0500 Subject: [PATCH] D3D: Initialize storage after generating mipmap images. When TextureD3D generates mipmaps without a storage present, it initializes all the images and then generates mipmaps on the CPU. Attempt to generate a storage after initializing all the mip images so that the mipmap generation can be done on the GPU. Fix several edge cases where texture storage was not copied back to images when redefining for mipmap generation. This resulted in the newly generated mipmaps not using the correct data. Bug: chromium:1256340 Change-Id: I76122d36385450d973bf00f7d07402a1f975492c Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3406643 Reviewed-by: Kenneth Russell Commit-Queue: Geoff Lang Reviewed-by: Jamie Madill --- src/libANGLE/renderer/d3d/TextureD3D.cpp | 147 +++++++++++++++++------ src/libANGLE/renderer/d3d/TextureD3D.h | 3 +- src/tests/gl_tests/TextureTest.cpp | 29 +++-- 3 files changed, 129 insertions(+), 50 deletions(-) diff --git a/src/libANGLE/renderer/d3d/TextureD3D.cpp b/src/libANGLE/renderer/d3d/TextureD3D.cpp index 32a398bd3..098b17393 100644 --- a/src/libANGLE/renderer/d3d/TextureD3D.cpp +++ b/src/libANGLE/renderer/d3d/TextureD3D.cpp @@ -521,6 +521,10 @@ angle::Result TextureD3D::generateMipmapUsingImages(const gl::Context *context, // CPU-side mipmap generation, or something else. bool renderableStorage = (mTexStorage && mTexStorage->isRenderTarget() && !(mRenderer->getFeatures().zeroMaxLodWorkaround.enabled)); + if (renderableStorage) + { + ANGLE_TRY(updateStorage(context)); + } for (GLint layer = 0; layer < layerCount; ++layer) { @@ -691,7 +695,7 @@ angle::Result TextureD3D::setBaseLevel(const gl::Context *context, GLuint baseLe } } - ANGLE_TRY(releaseTexStorage(context)); + ANGLE_TRY(releaseTexStorage(context, gl::TexLevelMask())); } return angle::Result::Continue; @@ -713,13 +717,37 @@ angle::Result TextureD3D::syncState(const gl::Context *context, return angle::Result::Continue; } -angle::Result TextureD3D::releaseTexStorage(const gl::Context *context) +angle::Result TextureD3D::releaseTexStorage(const gl::Context *context, + const gl::TexLevelMask ©StorageToImagesMask) { if (!mTexStorage) { return angle::Result::Continue; } + if (mTexStorage->isRenderTarget()) + { + const GLenum storageFormat = getBaseLevelInternalFormat(); + const size_t storageLevels = mTexStorage->getLevelCount(); + + gl::ImageIndexIterator iterator = imageIterator(); + while (iterator.hasNext()) + { + const gl::ImageIndex index = iterator.next(); + ImageD3D *image = getImage(index); + const int storageWidth = std::max(1, getLevelZeroWidth() >> index.getLevelIndex()); + const int storageHeight = std::max(1, getLevelZeroHeight() >> index.getLevelIndex()); + if (image && isImageComplete(index) && image->getWidth() == storageWidth && + image->getHeight() == storageHeight && + image->getInternalFormat() == storageFormat && + index.getLevelIndex() < static_cast(storageLevels) && + copyStorageToImagesMask[index.getLevelIndex()]) + { + ANGLE_TRY(image->copyFromTexStorage(context, index, mTexStorage)); + } + } + } + onStateChange(angle::SubjectMessage::StorageReleased); auto err = mTexStorage->onDestroy(context); @@ -729,7 +757,7 @@ angle::Result TextureD3D::releaseTexStorage(const gl::Context *context) void TextureD3D::onDestroy(const gl::Context *context) { - (void)releaseTexStorage(context); + (void)releaseTexStorage(context, gl::TexLevelMask()); } angle::Result TextureD3D::initializeContents(const gl::Context *context, @@ -1340,7 +1368,7 @@ angle::Result TextureD3D_2D::bindTexImage(const gl::Context *context, egl::Surfa gl::Extents size(surface->getWidth(), surface->getHeight(), 1); ANGLE_TRY(redefineImage(context, 0, internalformat, size, true)); - ANGLE_TRY(releaseTexStorage(context)); + ANGLE_TRY(releaseTexStorage(context, gl::TexLevelMask())); SurfaceD3D *surfaceD3D = GetImplAs(surface); ASSERT(surfaceD3D); @@ -1358,7 +1386,7 @@ angle::Result TextureD3D_2D::releaseTexImage(const gl::Context *context) { if (mTexStorage) { - ANGLE_TRY(releaseTexStorage(context)); + ANGLE_TRY(releaseTexStorage(context, gl::TexLevelMask())); } for (int i = 0; i < gl::IMPLEMENTATION_MAX_TEXTURE_LEVELS; i++) @@ -1386,7 +1414,7 @@ angle::Result TextureD3D_2D::setEGLImageTarget(const gl::Context *context, ANGLE_TRY(redefineImage(context, level, GL_NONE, gl::Extents(0, 0, 1), true)); } - ANGLE_TRY(releaseTexStorage(context)); + ANGLE_TRY(releaseTexStorage(context, gl::TexLevelMask())); mImageArray[0]->markClean(); // Pass in the RenderTargetD3D here: createTextureStorage can't generate an error. @@ -1413,6 +1441,10 @@ angle::Result TextureD3D_2D::initMipmapImages(const gl::Context *context) ANGLE_TRY(redefineImage(context, level, getBaseLevelInternalFormat(), levelSize, false)); } + + // We should be mip-complete now so generate the storage. + ANGLE_TRY(initializeStorage(context, true)); + return angle::Result::Continue; } @@ -1559,7 +1591,10 @@ angle::Result TextureD3D_2D::setCompleteTexStorage(const gl::Context *context, } } - ANGLE_TRY(releaseTexStorage(context)); + gl::TexLevelMask copyImageMask; + copyImageMask.set(); + + ANGLE_TRY(releaseTexStorage(context, copyImageMask)); mTexStorage = newCompleteTexStorage; mTexStorageObserverBinding.bind(mTexStorage); @@ -1617,9 +1652,6 @@ angle::Result TextureD3D_2D::redefineImage(const gl::Context *context, const int storageHeight = std::max(1, getLevelZeroHeight() >> level); const GLenum storageFormat = getBaseLevelInternalFormat(); - mImageArray[level]->redefine(gl::TextureType::_2D, internalformat, size, forceRelease); - mDirtyImages = mDirtyImages || mImageArray[level]->isDirty(); - if (mTexStorage) { const size_t storageLevels = mTexStorage->getLevelCount(); @@ -1636,11 +1668,18 @@ angle::Result TextureD3D_2D::redefineImage(const gl::Context *context, size.height != storageHeight || internalformat != storageFormat || mEGLImageTarget) // Discard mismatched storage { - ANGLE_TRY(releaseTexStorage(context)); + gl::TexLevelMask copyImageMask; + copyImageMask.set(); + copyImageMask.set(level, false); + + ANGLE_TRY(releaseTexStorage(context, copyImageMask)); markAllImagesDirty(); } } + mImageArray[level]->redefine(gl::TextureType::_2D, internalformat, size, forceRelease); + mDirtyImages = mDirtyImages || mImageArray[level]->isDirty(); + // Can't be an EGL image target after being redefined mEGLImageTarget = false; @@ -2132,6 +2171,10 @@ angle::Result TextureD3D_Cube::initMipmapImages(const gl::Context *context) gl::Extents(faceLevelSize, faceLevelSize, 1), false)); } } + + // We should be mip-complete now so generate the storage. + ANGLE_TRY(initializeStorage(context, true)); + return angle::Result::Continue; } @@ -2231,7 +2274,10 @@ angle::Result TextureD3D_Cube::setCompleteTexStorage(const gl::Context *context, } } - ANGLE_TRY(releaseTexStorage(context)); + gl::TexLevelMask copyImageMask; + copyImageMask.set(); + + ANGLE_TRY(releaseTexStorage(context, copyImageMask)); mTexStorage = newCompleteTexStorage; mTexStorageObserverBinding.bind(mTexStorage); @@ -2343,10 +2389,6 @@ angle::Result TextureD3D_Cube::redefineImage(const gl::Context *context, const int storageHeight = std::max(1, getLevelZeroHeight() >> level); const GLenum storageFormat = getBaseLevelInternalFormat(); - mImageArray[faceIndex][level]->redefine(gl::TextureType::CubeMap, internalformat, size, - forceRelease); - mDirtyImages = mDirtyImages || mImageArray[faceIndex][level]->isDirty(); - if (mTexStorage) { const int storageLevels = mTexStorage->getLevelCount(); @@ -2356,10 +2398,19 @@ angle::Result TextureD3D_Cube::redefineImage(const gl::Context *context, internalformat != storageFormat) // Discard mismatched storage { markAllImagesDirty(); - ANGLE_TRY(releaseTexStorage(context)); + + gl::TexLevelMask copyImageMask; + copyImageMask.set(); + copyImageMask.set(level, false); + + ANGLE_TRY(releaseTexStorage(context, copyImageMask)); } } + mImageArray[faceIndex][level]->redefine(gl::TextureType::CubeMap, internalformat, size, + forceRelease); + mDirtyImages = mDirtyImages || mImageArray[faceIndex][level]->isDirty(); + return angle::Result::Continue; } @@ -2820,6 +2871,9 @@ angle::Result TextureD3D_3D::initMipmapImages(const gl::Context *context) ANGLE_TRY(redefineImage(context, level, getBaseLevelInternalFormat(), levelSize, false)); } + // We should be mip-complete now so generate the storage. + ANGLE_TRY(initializeStorage(context, true)); + return angle::Result::Continue; } @@ -2899,7 +2953,10 @@ angle::Result TextureD3D_3D::createCompleteStorage(const gl::Context *context, angle::Result TextureD3D_3D::setCompleteTexStorage(const gl::Context *context, TextureStorage *newCompleteTexStorage) { - ANGLE_TRY(releaseTexStorage(context)); + gl::TexLevelMask copyImageMask; + copyImageMask.set(); + + ANGLE_TRY(releaseTexStorage(context, copyImageMask)); mTexStorage = newCompleteTexStorage; mTexStorageObserverBinding.bind(mTexStorage); mDirtyImages = true; @@ -3018,9 +3075,6 @@ angle::Result TextureD3D_3D::redefineImage(const gl::Context *context, const int storageDepth = std::max(1, getLevelZeroDepth() >> level); const GLenum storageFormat = getBaseLevelInternalFormat(); - mImageArray[level]->redefine(gl::TextureType::_3D, internalformat, size, forceRelease); - mDirtyImages = mDirtyImages || mImageArray[level]->isDirty(); - if (mTexStorage) { const int storageLevels = mTexStorage->getLevelCount(); @@ -3030,10 +3084,18 @@ angle::Result TextureD3D_3D::redefineImage(const gl::Context *context, internalformat != storageFormat) // Discard mismatched storage { markAllImagesDirty(); - ANGLE_TRY(releaseTexStorage(context)); + + gl::TexLevelMask copyImageMask; + copyImageMask.set(); + copyImageMask.set(level, false); + + ANGLE_TRY(releaseTexStorage(context, copyImageMask)); } } + mImageArray[level]->redefine(gl::TextureType::_3D, internalformat, size, forceRelease); + mDirtyImages = mDirtyImages || mImageArray[level]->isDirty(); + return angle::Result::Continue; } @@ -3560,6 +3622,9 @@ angle::Result TextureD3D_2DArray::initMipmapImages(const gl::Context *context) ANGLE_TRY(redefineImage(context, level, baseFormat, levelLayerSize, false)); } + // We should be mip-complete now so generate the storage. + ANGLE_TRY(initializeStorage(context, true)); + return angle::Result::Continue; } @@ -3629,7 +3694,10 @@ angle::Result TextureD3D_2DArray::createCompleteStorage(const gl::Context *conte angle::Result TextureD3D_2DArray::setCompleteTexStorage(const gl::Context *context, TextureStorage *newCompleteTexStorage) { - ANGLE_TRY(releaseTexStorage(context)); + gl::TexLevelMask copyImageMask; + copyImageMask.set(); + + ANGLE_TRY(releaseTexStorage(context, copyImageMask)); mTexStorage = newCompleteTexStorage; mTexStorageObserverBinding.bind(mTexStorage); mDirtyImages = true; @@ -3796,17 +3864,6 @@ angle::Result TextureD3D_2DArray::redefineImage(const gl::Context *context, } } - if (size.depth > 0) - { - for (int layer = 0; layer < mLayerCounts[level]; layer++) - { - mImageArray[level][layer]->redefine(gl::TextureType::_2DArray, internalformat, - gl::Extents(size.width, size.height, 1), - forceRelease); - mDirtyImages = mDirtyImages || mImageArray[level][layer]->isDirty(); - } - } - if (mTexStorage) { const int storageLevels = mTexStorage->getLevelCount(); @@ -3816,7 +3873,23 @@ angle::Result TextureD3D_2DArray::redefineImage(const gl::Context *context, internalformat != storageFormat) // Discard mismatched storage { markAllImagesDirty(); - ANGLE_TRY(releaseTexStorage(context)); + + gl::TexLevelMask copyImageMask; + copyImageMask.set(); + copyImageMask.set(level, false); + + ANGLE_TRY(releaseTexStorage(context, copyImageMask)); + } + } + + if (size.depth > 0) + { + for (int layer = 0; layer < mLayerCounts[level]; layer++) + { + mImageArray[level][layer]->redefine(gl::TextureType::_2DArray, internalformat, + gl::Extents(size.width, size.height, 1), + forceRelease); + mDirtyImages = mDirtyImages || mImageArray[level][layer]->isDirty(); } } @@ -3978,7 +4051,7 @@ angle::Result TextureD3D_External::setImageExternal(const gl::Context *context, { ASSERT(type == gl::TextureType::External); - ANGLE_TRY(releaseTexStorage(context)); + ANGLE_TRY(releaseTexStorage(context, gl::TexLevelMask())); // If the stream is null, the external image is unbound and we release the storage if (stream != nullptr) @@ -3999,7 +4072,7 @@ angle::Result TextureD3D_External::setEGLImageTarget(const gl::Context *context, RenderTargetD3D *renderTargetD3D = nullptr; ANGLE_TRY(eglImaged3d->getRenderTarget(context, &renderTargetD3D)); - ANGLE_TRY(releaseTexStorage(context)); + ANGLE_TRY(releaseTexStorage(context, gl::TexLevelMask())); mTexStorage = mRenderer->createTextureStorageEGLImage(eglImaged3d, renderTargetD3D, mState.getLabel()); diff --git a/src/libANGLE/renderer/d3d/TextureD3D.h b/src/libANGLE/renderer/d3d/TextureD3D.h index 4db8dfab8..3b4cf3da4 100644 --- a/src/libANGLE/renderer/d3d/TextureD3D.h +++ b/src/libANGLE/renderer/d3d/TextureD3D.h @@ -186,7 +186,8 @@ class TextureD3D : public TextureImpl, public angle::ObserverInterface const gl::ImageIndex &index, const gl::Box ®ion); - angle::Result releaseTexStorage(const gl::Context *context); + angle::Result releaseTexStorage(const gl::Context *context, + const gl::TexLevelMask ©StorageToImagesMask); GLuint getBaseLevel() const { return mBaseLevel; } diff --git a/src/tests/gl_tests/TextureTest.cpp b/src/tests/gl_tests/TextureTest.cpp index d84f2c4ca..5aa4b5090 100644 --- a/src/tests/gl_tests/TextureTest.cpp +++ b/src/tests/gl_tests/TextureTest.cpp @@ -2058,6 +2058,11 @@ TEST_P(Texture2DTestWithDrawScale, MipmapsTwice) pixelsBlue.data()); glGenerateMipmap(GL_TEXTURE_2D); + drawQuad(mProgram, "position", 0.5f); + + EXPECT_GL_NO_ERROR(); + EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::blue); + std::vector pixelsGreen(16u * 16u, GLColor::green); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, @@ -4208,24 +4213,24 @@ void Texture2DBaseMaxTestES3::testGenerateMipmapAfterRebase(bool immutable) drawQuad(mProgram, essl3_shaders::PositionAttrib(), 0.5f); if (lod == 0) { - EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[lod]); - EXPECT_PIXEL_COLOR_EQ(w, 0, kMipColors[lod]); - EXPECT_PIXEL_COLOR_EQ(0, h, kMipColors[lod]); - EXPECT_PIXEL_COLOR_EQ(w, h, kMipColors[lod]); + EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[lod]) << "lod " << lod; + EXPECT_PIXEL_COLOR_EQ(w, 0, kMipColors[lod]) << "lod " << lod; + EXPECT_PIXEL_COLOR_EQ(0, h, kMipColors[lod]) << "lod " << lod; + EXPECT_PIXEL_COLOR_EQ(w, h, kMipColors[lod]) << "lod " << lod; } else if (lod == kMipCount - 1) { - EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[lod]); - EXPECT_PIXEL_COLOR_EQ(w, 0, kMipColors[lod]); - EXPECT_PIXEL_COLOR_EQ(0, h, kMipColors[lod]); - EXPECT_PIXEL_COLOR_EQ(w, h, kMipColors[lod]); + EXPECT_PIXEL_COLOR_EQ(0, 0, kMipColors[lod]) << "lod " << lod; + EXPECT_PIXEL_COLOR_EQ(w, 0, kMipColors[lod]) << "lod " << lod; + EXPECT_PIXEL_COLOR_EQ(0, h, kMipColors[lod]) << "lod " << lod; + EXPECT_PIXEL_COLOR_EQ(w, h, kMipColors[lod]) << "lod " << lod; } else { - EXPECT_PIXEL_COLOR_EQ(0, 0, kNewMipColor); - EXPECT_PIXEL_COLOR_EQ(w, 0, kNewMipColor); - EXPECT_PIXEL_COLOR_EQ(0, h, kNewMipColor); - EXPECT_PIXEL_COLOR_EQ(w, h, kNewMipColor); + EXPECT_PIXEL_COLOR_EQ(0, 0, kNewMipColor) << "lod " << lod; + EXPECT_PIXEL_COLOR_EQ(w, 0, kNewMipColor) << "lod " << lod; + EXPECT_PIXEL_COLOR_EQ(0, h, kNewMipColor) << "lod " << lod; + EXPECT_PIXEL_COLOR_EQ(w, h, kNewMipColor) << "lod " << lod; } } } -- 2.23.0.windows.1