Commit fd3dd436 by Jamie Madill Committed by Commit Bot

WebGL: Add 3D tex copying feedback loop detection.

When copying to and from the same texture, we need to reject only the feedback loops formed with the same levels of the texture - copying between different unrelated layers and levels is fine. This change also fixes a couple bugs in our D3D11 CopyTexSubImage3D implementation. We were missing some "!" operators, and we actually would hit an ASSERT when trying to blit from a level of a 3D texture. BUG=angleproject:1685 Change-Id: Id715bebafe8336cf8bb95d0d06275a8b95e522e1 Reviewed-on: https://chromium-review.googlesource.com/425494 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent c8a08f40
......@@ -1068,7 +1068,9 @@ bool Framebuffer::formsRenderingFeedbackLoopWith(const State &state) const
return false;
}
bool Framebuffer::formsCopyingFeedbackLoopWith(GLuint copyTextureID, GLint copyTextureLevel) const
bool Framebuffer::formsCopyingFeedbackLoopWith(GLuint copyTextureID,
GLint copyTextureLevel,
GLint copyTextureLayer) const
{
if (mId == 0)
{
......@@ -1081,10 +1083,13 @@ bool Framebuffer::formsCopyingFeedbackLoopWith(GLuint copyTextureID, GLint copyT
if (readAttachment->isTextureWithId(copyTextureID))
{
// TODO(jmadill): 3D/Array texture layers.
if (readAttachment->getTextureImageIndex().mipIndex == copyTextureLevel)
const auto &imageIndex = readAttachment->getTextureImageIndex();
if (imageIndex.mipIndex == copyTextureLevel)
{
return true;
// Check 3D/Array texture layers.
return imageIndex.layerIndex == ImageIndex::ENTIRE_LEVEL ||
copyTextureLayer == ImageIndex::ENTIRE_LEVEL ||
imageIndex.layerIndex == copyTextureLayer;
}
}
return false;
......
......@@ -223,7 +223,9 @@ class Framebuffer final : public LabeledObject, public angle::SignalReceiver
void signal(angle::SignalToken token) override;
bool formsRenderingFeedbackLoopWith(const State &state) const;
bool formsCopyingFeedbackLoopWith(GLuint copyTextureID, GLint copyTextureLevel) const;
bool formsCopyingFeedbackLoopWith(GLuint copyTextureID,
GLint copyTextureLevel,
GLint copyTextureLayer) const;
private:
void detachResourceById(GLenum resourceType, GLuint resourceId);
......
......@@ -490,27 +490,26 @@ gl::Error TextureD3D::ensureRenderTarget()
{
ANGLE_TRY(initializeStorage(true));
// initializeStorage can fail with NoError if the texture is not complete. This is not
// an error for incomplete sampling, but it is a big problem for rendering.
if (!mTexStorage)
{
UNREACHABLE();
return gl::InternalError() << "Cannot render to incomplete texture.";
}
if (!isBaseImageZeroSize())
{
ASSERT(mTexStorage);
if (!mTexStorage->isRenderTarget())
{
TextureStorage *newRenderTargetStorage = NULL;
TextureStorage *newRenderTargetStorage = nullptr;
ANGLE_TRY(createCompleteStorage(true, &newRenderTargetStorage));
gl::Error error = mTexStorage->copyToStorage(newRenderTargetStorage);
if (error.isError())
{
SafeDelete(newRenderTargetStorage);
return error;
}
error = setCompleteTexStorage(newRenderTargetStorage);
if (error.isError())
{
SafeDelete(newRenderTargetStorage);
return error;
}
std::unique_ptr<TextureStorage> newStorageRef(newRenderTargetStorage);
ANGLE_TRY(mTexStorage->copyToStorage(newRenderTargetStorage));
ANGLE_TRY(setCompleteTexStorage(newRenderTargetStorage));
newStorageRef.release();
}
}
......@@ -2256,24 +2255,29 @@ gl::Error TextureD3D_3D::copySubImage(ContextImpl *contextImpl,
ASSERT(target == GL_TEXTURE_3D);
GLint level = static_cast<GLint>(imageLevel);
gl::ImageIndex index = gl::ImageIndex::Make3D(level);
if (canCreateRenderTargetForImage(index))
// Currently, 3D single-layer blits are broken because we don't know how to make an SRV
// for a single layer of a 3D texture.
// TODO(jmadill): Investigate 3D blits in D3D11.
// gl::ImageIndex index = gl::ImageIndex::Make3D(level);
// if (!canCreateRenderTargetForImage(index))
{
ANGLE_TRY(mImageArray[level]->copyFromFramebuffer(destOffset, sourceArea, source));
mDirtyImages = true;
}
else
{
ANGLE_TRY(ensureRenderTarget());
if (isValidLevel(level))
{
ANGLE_TRY(updateStorageLevel(level));
ANGLE_TRY(mRenderer->copyImage3D(
source, sourceArea, gl::GetInternalFormatInfo(getBaseLevelInternalFormat()).format,
destOffset, mTexStorage, level));
}
}
// else
//{
// ANGLE_TRY(ensureRenderTarget());
// if (isValidLevel(level))
// {
// ANGLE_TRY(updateStorageLevel(level));
// ANGLE_TRY(mRenderer->copyImage3D(
// source, sourceArea,
// gl::GetInternalFormatInfo(getBaseLevelInternalFormat()).format,
// destOffset, mTexStorage, level));
// }
//}
return gl::NoError();
}
......@@ -2793,7 +2797,7 @@ gl::Error TextureD3D_2DArray::copySubImage(ContextImpl *contextImpl,
GLint level = static_cast<GLint>(imageLevel);
gl::ImageIndex index = gl::ImageIndex::Make2DArray(level, destOffset.z);
if (canCreateRenderTargetForImage(index))
if (!canCreateRenderTargetForImage(index))
{
gl::Offset destLayerOffset(destOffset.x, destOffset.y, 0);
ANGLE_TRY(mImageArray[level][destOffset.z]->copyFromFramebuffer(destLayerOffset, sourceArea,
......
......@@ -2797,59 +2797,57 @@ gl::Error TextureStorage11_3D::getRenderTarget(const gl::ImageIndex &index, Rend
*outRT = mLevelRenderTargets[mipLevel];
return gl::NoError();
}
else
{
const int layer = index.layerIndex;
LevelLayerKey key(mipLevel, layer);
if (mLevelLayerRenderTargets.find(key) == mLevelLayerRenderTargets.end())
{
ID3D11Device *device = mRenderer->getDevice();
HRESULT result;
const int layer = index.layerIndex;
ID3D11Resource *texture = nullptr;
ANGLE_TRY(getResource(&texture));
LevelLayerKey key(mipLevel, layer);
if (mLevelLayerRenderTargets.find(key) == mLevelLayerRenderTargets.end())
{
ID3D11Device *device = mRenderer->getDevice();
HRESULT result;
// TODO, what kind of SRV is expected here?
ID3D11ShaderResourceView *srv = nullptr;
ID3D11ShaderResourceView *blitSRV = nullptr;
ID3D11Resource *texture = nullptr;
ANGLE_TRY(getResource(&texture));
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
rtvDesc.Format = mFormatInfo.rtvFormat;
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
rtvDesc.Texture3D.MipSlice = mTopLevel + mipLevel;
rtvDesc.Texture3D.FirstWSlice = layer;
rtvDesc.Texture3D.WSize = 1;
// TODO, what kind of SRV is expected here?
ID3D11ShaderResourceView *srv = nullptr;
ID3D11ShaderResourceView *blitSRV = nullptr;
ID3D11RenderTargetView *rtv;
result = device->CreateRenderTargetView(texture, &rtvDesc, &rtv);
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
rtvDesc.Format = mFormatInfo.rtvFormat;
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
rtvDesc.Texture3D.MipSlice = mTopLevel + mipLevel;
rtvDesc.Texture3D.FirstWSlice = layer;
rtvDesc.Texture3D.WSize = 1;
ASSERT(result == E_OUTOFMEMORY || SUCCEEDED(result));
if (FAILED(result))
{
SafeRelease(srv);
SafeRelease(blitSRV);
return gl::Error(GL_OUT_OF_MEMORY,
"Failed to create internal render target view for texture "
"storage, result: 0x%X.",
result);
}
ASSERT(SUCCEEDED(result));
ID3D11RenderTargetView *rtv;
result = device->CreateRenderTargetView(texture, &rtvDesc, &rtv);
d3d11::SetDebugName(rtv, "TexStorage3D.LayerRTV");
ASSERT(result == E_OUTOFMEMORY || SUCCEEDED(result));
if (FAILED(result))
{
SafeRelease(srv);
SafeRelease(blitSRV);
return gl::Error(GL_OUT_OF_MEMORY,
"Failed to create internal render target view for texture "
"storage, result: 0x%X.",
result);
}
ASSERT(SUCCEEDED(result));
mLevelLayerRenderTargets[key] = new TextureRenderTarget11(
rtv, texture, srv, blitSRV, mFormatInfo.internalFormat, getFormatSet(),
getLevelWidth(mipLevel), getLevelHeight(mipLevel), 1, 0);
d3d11::SetDebugName(rtv, "TexStorage3D.LayerRTV");
// RenderTarget will take ownership of these resources
SafeRelease(rtv);
}
mLevelLayerRenderTargets[key] = new TextureRenderTarget11(
rtv, texture, srv, blitSRV, mFormatInfo.internalFormat, getFormatSet(),
getLevelWidth(mipLevel), getLevelHeight(mipLevel), 1, 0);
ASSERT(outRT);
*outRT = mLevelLayerRenderTargets[key];
return gl::NoError();
// RenderTarget will take ownership of these resources
SafeRelease(rtv);
}
ASSERT(outRT);
*outRT = mLevelLayerRenderTargets[key];
return gl::NoError();
}
gl::Error TextureStorage11_3D::getSwizzleTexture(ID3D11Resource **outTexture)
......
......@@ -3195,7 +3195,7 @@ bool ValidateCopyTexImageParametersBase(ValidationContext *context,
// Detect texture copying feedback loops for WebGL.
if (context->getExtensions().webglCompatibility)
{
if (readFramebuffer->formsCopyingFeedbackLoopWith(texture->id(), level))
if (readFramebuffer->formsCopyingFeedbackLoopWith(texture->id(), level, zoffset))
{
context->handleError(Error(GL_INVALID_OPERATION,
"Texture copying feedback loop formed between Framebuffer "
......
......@@ -1037,6 +1037,63 @@ TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDepthStencil)
EXPECT_GL_NO_ERROR();
}
// The source and the target for CopyTexSubImage3D are the same 3D texture.
// But the level of the 3D texture != the level of the read attachment.
TEST_P(WebGL2CompatibilityTest, NoTextureCopyingFeedbackLoopBetween3DLevels)
{
GLTexture texture;
GLFramebuffer framebuffer;
glBindTexture(GL_TEXTURE_3D, texture.get());
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.get(), 0, 0);
ASSERT_GL_NO_ERROR();
glCopyTexSubImage3D(GL_TEXTURE_3D, 1, 0, 0, 0, 0, 0, 2, 2);
EXPECT_GL_NO_ERROR();
}
// The source and the target for CopyTexSubImage3D are the same 3D texture.
// But the zoffset of the 3D texture != the layer of the read attachment.
TEST_P(WebGL2CompatibilityTest, NoTextureCopyingFeedbackLoopBetween3DLayers)
{
GLTexture texture;
GLFramebuffer framebuffer;
glBindTexture(GL_TEXTURE_3D, texture.get());
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.get(), 0, 1);
ASSERT_GL_NO_ERROR();
glCopyTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 0, 0, 2, 2);
EXPECT_GL_NO_ERROR();
}
// The source and the target for CopyTexSubImage3D are the same 3D texture.
// And the level / zoffset of the 3D texture is equal to the level / layer of the read attachment.
TEST_P(WebGL2CompatibilityTest, TextureCopyingFeedbackLoop3D)
{
GLTexture texture;
GLFramebuffer framebuffer;
glBindTexture(GL_TEXTURE_3D, texture.get());
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.get());
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 4, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexImage3D(GL_TEXTURE_3D, 2, GL_RGBA8, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture.get(), 1, 0);
ASSERT_GL_NO_ERROR();
glCopyTexSubImage3D(GL_TEXTURE_3D, 1, 0, 0, 0, 0, 0, 2, 2);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against.
ANGLE_INSTANTIATE_TEST(WebGLCompatibilityTest,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment