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 ...@@ -1068,7 +1068,9 @@ bool Framebuffer::formsRenderingFeedbackLoopWith(const State &state) const
return false; return false;
} }
bool Framebuffer::formsCopyingFeedbackLoopWith(GLuint copyTextureID, GLint copyTextureLevel) const bool Framebuffer::formsCopyingFeedbackLoopWith(GLuint copyTextureID,
GLint copyTextureLevel,
GLint copyTextureLayer) const
{ {
if (mId == 0) if (mId == 0)
{ {
...@@ -1081,10 +1083,13 @@ bool Framebuffer::formsCopyingFeedbackLoopWith(GLuint copyTextureID, GLint copyT ...@@ -1081,10 +1083,13 @@ bool Framebuffer::formsCopyingFeedbackLoopWith(GLuint copyTextureID, GLint copyT
if (readAttachment->isTextureWithId(copyTextureID)) if (readAttachment->isTextureWithId(copyTextureID))
{ {
// TODO(jmadill): 3D/Array texture layers. const auto &imageIndex = readAttachment->getTextureImageIndex();
if (readAttachment->getTextureImageIndex().mipIndex == copyTextureLevel) 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; return false;
......
...@@ -223,7 +223,9 @@ class Framebuffer final : public LabeledObject, public angle::SignalReceiver ...@@ -223,7 +223,9 @@ class Framebuffer final : public LabeledObject, public angle::SignalReceiver
void signal(angle::SignalToken token) override; void signal(angle::SignalToken token) override;
bool formsRenderingFeedbackLoopWith(const State &state) const; bool formsRenderingFeedbackLoopWith(const State &state) const;
bool formsCopyingFeedbackLoopWith(GLuint copyTextureID, GLint copyTextureLevel) const; bool formsCopyingFeedbackLoopWith(GLuint copyTextureID,
GLint copyTextureLevel,
GLint copyTextureLayer) const;
private: private:
void detachResourceById(GLenum resourceType, GLuint resourceId); void detachResourceById(GLenum resourceType, GLuint resourceId);
......
...@@ -490,27 +490,26 @@ gl::Error TextureD3D::ensureRenderTarget() ...@@ -490,27 +490,26 @@ gl::Error TextureD3D::ensureRenderTarget()
{ {
ANGLE_TRY(initializeStorage(true)); 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()) if (!isBaseImageZeroSize())
{ {
ASSERT(mTexStorage); ASSERT(mTexStorage);
if (!mTexStorage->isRenderTarget()) if (!mTexStorage->isRenderTarget())
{ {
TextureStorage *newRenderTargetStorage = NULL; TextureStorage *newRenderTargetStorage = nullptr;
ANGLE_TRY(createCompleteStorage(true, &newRenderTargetStorage)); ANGLE_TRY(createCompleteStorage(true, &newRenderTargetStorage));
gl::Error error = mTexStorage->copyToStorage(newRenderTargetStorage); std::unique_ptr<TextureStorage> newStorageRef(newRenderTargetStorage);
if (error.isError()) ANGLE_TRY(mTexStorage->copyToStorage(newRenderTargetStorage));
{ ANGLE_TRY(setCompleteTexStorage(newRenderTargetStorage));
SafeDelete(newRenderTargetStorage); newStorageRef.release();
return error;
}
error = setCompleteTexStorage(newRenderTargetStorage);
if (error.isError())
{
SafeDelete(newRenderTargetStorage);
return error;
}
} }
} }
...@@ -2256,24 +2255,29 @@ gl::Error TextureD3D_3D::copySubImage(ContextImpl *contextImpl, ...@@ -2256,24 +2255,29 @@ gl::Error TextureD3D_3D::copySubImage(ContextImpl *contextImpl,
ASSERT(target == GL_TEXTURE_3D); ASSERT(target == GL_TEXTURE_3D);
GLint level = static_cast<GLint>(imageLevel); 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)); ANGLE_TRY(mImageArray[level]->copyFromFramebuffer(destOffset, sourceArea, source));
mDirtyImages = true; mDirtyImages = true;
} }
else // else
{ //{
ANGLE_TRY(ensureRenderTarget()); // ANGLE_TRY(ensureRenderTarget());
if (isValidLevel(level)) // if (isValidLevel(level))
{ // {
ANGLE_TRY(updateStorageLevel(level)); // ANGLE_TRY(updateStorageLevel(level));
ANGLE_TRY(mRenderer->copyImage3D( // ANGLE_TRY(mRenderer->copyImage3D(
source, sourceArea, gl::GetInternalFormatInfo(getBaseLevelInternalFormat()).format, // source, sourceArea,
destOffset, mTexStorage, level)); // gl::GetInternalFormatInfo(getBaseLevelInternalFormat()).format,
} // destOffset, mTexStorage, level));
} // }
//}
return gl::NoError(); return gl::NoError();
} }
...@@ -2793,7 +2797,7 @@ gl::Error TextureD3D_2DArray::copySubImage(ContextImpl *contextImpl, ...@@ -2793,7 +2797,7 @@ gl::Error TextureD3D_2DArray::copySubImage(ContextImpl *contextImpl,
GLint level = static_cast<GLint>(imageLevel); GLint level = static_cast<GLint>(imageLevel);
gl::ImageIndex index = gl::ImageIndex::Make2DArray(level, destOffset.z); gl::ImageIndex index = gl::ImageIndex::Make2DArray(level, destOffset.z);
if (canCreateRenderTargetForImage(index)) if (!canCreateRenderTargetForImage(index))
{ {
gl::Offset destLayerOffset(destOffset.x, destOffset.y, 0); gl::Offset destLayerOffset(destOffset.x, destOffset.y, 0);
ANGLE_TRY(mImageArray[level][destOffset.z]->copyFromFramebuffer(destLayerOffset, sourceArea, ANGLE_TRY(mImageArray[level][destOffset.z]->copyFromFramebuffer(destLayerOffset, sourceArea,
......
...@@ -2797,59 +2797,57 @@ gl::Error TextureStorage11_3D::getRenderTarget(const gl::ImageIndex &index, Rend ...@@ -2797,59 +2797,57 @@ gl::Error TextureStorage11_3D::getRenderTarget(const gl::ImageIndex &index, Rend
*outRT = mLevelRenderTargets[mipLevel]; *outRT = mLevelRenderTargets[mipLevel];
return gl::NoError(); return gl::NoError();
} }
else
{
const int layer = index.layerIndex;
LevelLayerKey key(mipLevel, layer); const int layer = index.layerIndex;
if (mLevelLayerRenderTargets.find(key) == mLevelLayerRenderTargets.end())
{
ID3D11Device *device = mRenderer->getDevice();
HRESULT result;
ID3D11Resource *texture = nullptr; LevelLayerKey key(mipLevel, layer);
ANGLE_TRY(getResource(&texture)); if (mLevelLayerRenderTargets.find(key) == mLevelLayerRenderTargets.end())
{
ID3D11Device *device = mRenderer->getDevice();
HRESULT result;
// TODO, what kind of SRV is expected here? ID3D11Resource *texture = nullptr;
ID3D11ShaderResourceView *srv = nullptr; ANGLE_TRY(getResource(&texture));
ID3D11ShaderResourceView *blitSRV = nullptr;
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; // TODO, what kind of SRV is expected here?
rtvDesc.Format = mFormatInfo.rtvFormat; ID3D11ShaderResourceView *srv = nullptr;
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; ID3D11ShaderResourceView *blitSRV = nullptr;
rtvDesc.Texture3D.MipSlice = mTopLevel + mipLevel;
rtvDesc.Texture3D.FirstWSlice = layer;
rtvDesc.Texture3D.WSize = 1;
ID3D11RenderTargetView *rtv; D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
result = device->CreateRenderTargetView(texture, &rtvDesc, &rtv); 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)); ID3D11RenderTargetView *rtv;
if (FAILED(result)) result = device->CreateRenderTargetView(texture, &rtvDesc, &rtv);
{
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));
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( d3d11::SetDebugName(rtv, "TexStorage3D.LayerRTV");
rtv, texture, srv, blitSRV, mFormatInfo.internalFormat, getFormatSet(),
getLevelWidth(mipLevel), getLevelHeight(mipLevel), 1, 0);
// RenderTarget will take ownership of these resources mLevelLayerRenderTargets[key] = new TextureRenderTarget11(
SafeRelease(rtv); rtv, texture, srv, blitSRV, mFormatInfo.internalFormat, getFormatSet(),
} getLevelWidth(mipLevel), getLevelHeight(mipLevel), 1, 0);
ASSERT(outRT); // RenderTarget will take ownership of these resources
*outRT = mLevelLayerRenderTargets[key]; SafeRelease(rtv);
return gl::NoError();
} }
ASSERT(outRT);
*outRT = mLevelLayerRenderTargets[key];
return gl::NoError();
} }
gl::Error TextureStorage11_3D::getSwizzleTexture(ID3D11Resource **outTexture) gl::Error TextureStorage11_3D::getSwizzleTexture(ID3D11Resource **outTexture)
......
...@@ -3195,7 +3195,7 @@ bool ValidateCopyTexImageParametersBase(ValidationContext *context, ...@@ -3195,7 +3195,7 @@ bool ValidateCopyTexImageParametersBase(ValidationContext *context,
// Detect texture copying feedback loops for WebGL. // Detect texture copying feedback loops for WebGL.
if (context->getExtensions().webglCompatibility) if (context->getExtensions().webglCompatibility)
{ {
if (readFramebuffer->formsCopyingFeedbackLoopWith(texture->id(), level)) if (readFramebuffer->formsCopyingFeedbackLoopWith(texture->id(), level, zoffset))
{ {
context->handleError(Error(GL_INVALID_OPERATION, context->handleError(Error(GL_INVALID_OPERATION,
"Texture copying feedback loop formed between Framebuffer " "Texture copying feedback loop formed between Framebuffer "
......
...@@ -1037,6 +1037,63 @@ TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDepthStencil) ...@@ -1037,6 +1037,63 @@ TEST_P(WebGL2CompatibilityTest, RenderingFeedbackLoopWithDepthStencil)
EXPECT_GL_NO_ERROR(); 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 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against. // tests should be run against.
ANGLE_INSTANTIATE_TEST(WebGLCompatibilityTest, 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