Commit 6722009e by Jamie Madill Committed by Commit Bot

Vulkan: Handle dirty RTs with state messages.

Prior to this CL we were handling dirty state change notifications by flushing the RT Images just prior to use or just after they were changed. This could lead to a few redundant checks in several places. It also meant we needed an owner pointer from the RT to the parent Image. This pointer would be null for Surfaces and Renderbuffers. This cleans up the image flushing logic to be handled by dirty bit notifications. When an app updates an attached Texture with TexSubImage or related calls it will send a notification to the Framebuffer. The Framebuffer then sets a dirty contents bit that is handled in the implementation. In Vulkan this means flushing the dirty bits. Requires adding a flag to the FramebufferImpl class to determine if we need to syncState before we checkStatus. Adding the option allows us to only call syncState for the GL back-end. Not calling syncState allows the robust resource init operation to happen *before* we syncState. Which in turn allows FramebuffeVk to initialize the VkImages in one go. Added new regression tests for Texture updates. This might not cover all cases. I found it was very hard to trigger some of the resource update staging in TextureVk. Bug: angleproject:3427 Change-Id: Idfa177436ba7fcb9d398f2b67922e085f778f82a Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1601552 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org>
parent 3f3a05d4
......@@ -467,7 +467,9 @@ void Context::initialize()
mClearDirtyBits.set(State::DIRTY_BIT_STENCIL_WRITEMASK_FRONT);
mClearDirtyBits.set(State::DIRTY_BIT_STENCIL_WRITEMASK_BACK);
mClearDirtyBits.set(State::DIRTY_BIT_DRAW_FRAMEBUFFER_BINDING);
mClearDirtyObjects.set(State::DIRTY_OBJECT_DRAW_FRAMEBUFFER);
// We sync the draw Framebuffer manually in prepareForClear to allow the clear calls to do
// more custom handling for robust resource init.
mBlitDirtyBits.set(State::DIRTY_BIT_SCISSOR_TEST_ENABLED);
mBlitDirtyBits.set(State::DIRTY_BIT_SCISSOR);
......@@ -3498,17 +3500,21 @@ bool Context::noopDrawInstanced(PrimitiveMode mode, GLsizei count, GLsizei insta
angle::Result Context::prepareForClear(GLbitfield mask)
{
ANGLE_TRY(syncDirtyObjects(mClearDirtyObjects));
// Sync the draw framebuffer manually after the clear attachments.
ASSERT(mClearDirtyObjects.none());
ANGLE_TRY(mState.getDrawFramebuffer()->ensureClearAttachmentsInitialized(this, mask));
ANGLE_TRY(mState.syncDirtyObject(this, GL_DRAW_FRAMEBUFFER));
ANGLE_TRY(syncDirtyBits(mClearDirtyBits));
return angle::Result::Continue;
}
angle::Result Context::prepareForClearBuffer(GLenum buffer, GLint drawbuffer)
{
ANGLE_TRY(syncDirtyObjects(mClearDirtyObjects));
// Sync the draw framebuffer manually after the clear attachments.
ASSERT(mClearDirtyObjects.none());
ANGLE_TRY(mState.getDrawFramebuffer()->ensureClearBufferAttachmentsInitialized(this, buffer,
drawbuffer));
ANGLE_TRY(mState.syncDirtyObject(this, GL_DRAW_FRAMEBUFFER));
ANGLE_TRY(syncDirtyBits(mClearDirtyBits));
return angle::Result::Continue;
}
......@@ -8190,8 +8196,11 @@ void Context::onSubjectStateChange(const Context *context,
default:
if (index < kTextureMaxSubjectIndex)
{
mState.onActiveTextureStateChange(this, index);
mStateCache.onActiveTextureChange(this);
if (message != angle::SubjectMessage::ContentsChanged)
{
mState.onActiveTextureStateChange(this, index);
mStateCache.onActiveTextureChange(this);
}
}
else if (index < kUniformBufferMaxSubjectIndex)
{
......
......@@ -968,11 +968,16 @@ GLenum Framebuffer::checkStatusImpl(const Context *context)
if (mCachedStatus.value() == GL_FRAMEBUFFER_COMPLETE)
{
angle::Result err = syncState(context);
if (err != angle::Result::Continue)
// We can skip syncState on several back-ends.
if (mImpl->shouldSyncStateBeforeCheckStatus())
{
return 0;
angle::Result err = syncState(context);
if (err != angle::Result::Continue)
{
return 0;
}
}
if (!mImpl->checkStatus(context))
{
mCachedStatus = GL_FRAMEBUFFER_UNSUPPORTED;
......@@ -1848,6 +1853,14 @@ void Framebuffer::onSubjectStateChange(const Context *context,
{
if (message != angle::SubjectMessage::SubjectChanged)
{
// This can be triggered by SubImage calls for Textures.
if (message == angle::SubjectMessage::ContentsChanged)
{
mDirtyBits.set(DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 + index);
onStateChange(context, angle::SubjectMessage::DirtyBitsFlagged);
return;
}
// This can be triggered by the GL back-end TextureGL class.
ASSERT(message == angle::SubjectMessage::DirtyBitsFlagged);
return;
......
......@@ -314,6 +314,11 @@ class Framebuffer final : public angle::ObserverInterface,
DIRTY_BIT_COLOR_ATTACHMENT_0 + IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS,
DIRTY_BIT_DEPTH_ATTACHMENT = DIRTY_BIT_COLOR_ATTACHMENT_MAX,
DIRTY_BIT_STENCIL_ATTACHMENT,
DIRTY_BIT_COLOR_BUFFER_CONTENTS_0,
DIRTY_BIT_COLOR_BUFFER_CONTENTS_MAX =
DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 + IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS,
DIRTY_BIT_DEPTH_BUFFER_CONTENTS,
DIRTY_BIT_STENCIL_BUFFER_CONTENTS,
DIRTY_BIT_DRAW_BUFFERS,
DIRTY_BIT_READ_BUFFER,
DIRTY_BIT_DEFAULT_WIDTH,
......
......@@ -1058,6 +1058,8 @@ angle::Result Texture::setSubImage(Context *context,
ANGLE_TRY(handleMipmapGenerationHint(context, level));
onStateChange(context, angle::SubjectMessage::ContentsChanged);
return angle::Result::Continue;
}
......@@ -1103,8 +1105,12 @@ angle::Result Texture::setCompressedSubImage(const Context *context,
ImageIndex index = ImageIndex::MakeFromTarget(target, level);
return mTexture->setCompressedSubImage(context, index, area, format, unpackState, imageSize,
pixels);
ANGLE_TRY(mTexture->setCompressedSubImage(context, index, area, format, unpackState, imageSize,
pixels));
onStateChange(context, angle::SubjectMessage::ContentsChanged);
return angle::Result::Continue;
}
angle::Result Texture::copyImage(Context *context,
......@@ -1158,6 +1164,8 @@ angle::Result Texture::copySubImage(Context *context,
ANGLE_TRY(mTexture->copySubImage(context, index, destOffset, sourceArea, source));
ANGLE_TRY(handleMipmapGenerationHint(context, index.getLevelIndex()));
onStateChange(context, angle::SubjectMessage::ContentsChanged);
return angle::Result::Continue;
}
......@@ -1222,8 +1230,13 @@ angle::Result Texture::copySubTexture(const Context *context,
ImageIndex index = ImageIndex::MakeFromTarget(target, level);
return mTexture->copySubTexture(context, index, destOffset, sourceLevel, sourceBox, unpackFlipY,
unpackPremultiplyAlpha, unpackUnmultiplyAlpha, source);
ANGLE_TRY(mTexture->copySubTexture(context, index, destOffset, sourceLevel, sourceBox,
unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha,
source));
onStateChange(context, angle::SubjectMessage::ContentsChanged);
return angle::Result::Continue;
}
angle::Result Texture::copyCompressedTexture(Context *context, const Texture *source)
......
......@@ -86,11 +86,23 @@ class FramebufferImpl : angle::NonCopyable
size_t index,
GLfloat *xy) const = 0;
// Special configuration option for checkStatus(). Some back-ends don't require a syncState
// before calling checkStatus. In practice the GL back-end is the only config that needs
// syncState because it depends on the behaviour of the driver. Allowing the Vulkan and
// D3D back-ends to skip syncState lets us do more work in the syncState call.
virtual bool shouldSyncStateBeforeCheckStatus() const;
const gl::FramebufferState &getState() const { return mState; }
protected:
const gl::FramebufferState &mState;
};
inline bool FramebufferImpl::shouldSyncStateBeforeCheckStatus() const
{
return false;
}
} // namespace rx
#endif // LIBANGLE_RENDERER_FRAMEBUFFERIMPL_H_
......@@ -86,11 +86,13 @@ angle::Result RenderTargetCache<RenderTargetT>::update(const gl::Context *contex
break;
default:
{
ASSERT(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0 &&
dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX);
size_t colorIndex =
static_cast<size_t>(dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
ANGLE_TRY(updateColorRenderTarget(context, state, colorIndex));
static_assert(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0, "FB dirty bits");
if (dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX)
{
size_t colorIndex = static_cast<size_t>(
dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
ANGLE_TRY(updateColorRenderTarget(context, state, colorIndex));
}
break;
}
}
......
......@@ -602,6 +602,11 @@ angle::Result FramebufferGL::getSamplePosition(const gl::Context *context,
return angle::Result::Continue;
}
bool FramebufferGL::shouldSyncStateBeforeCheckStatus() const
{
return true;
}
bool FramebufferGL::checkStatus(const gl::Context *context) const
{
const FunctionsGL *functions = GetFunctionsGL(context);
......@@ -691,16 +696,19 @@ angle::Result FramebufferGL::syncState(const gl::Context *context,
break;
default:
{
ASSERT(Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0 &&
dirtyBit < Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX);
size_t index =
static_cast<size_t>(dirtyBit - Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
const FramebufferAttachment *newAttachment = mState.getColorAttachment(index);
BindFramebufferAttachment(
functions, static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + index), newAttachment);
if (newAttachment)
static_assert(Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0, "FB dirty bits");
if (dirtyBit < Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX)
{
attachment = newAttachment;
size_t index =
static_cast<size_t>(dirtyBit - Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
const FramebufferAttachment *newAttachment = mState.getColorAttachment(index);
BindFramebufferAttachment(functions,
static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + index),
newAttachment);
if (newAttachment)
{
attachment = newAttachment;
}
}
break;
}
......
......@@ -78,6 +78,9 @@ class FramebufferGL : public FramebufferImpl
size_t index,
GLfloat *xy) const override;
// The GL back-end requires a full sync state before we call checkStatus.
bool shouldSyncStateBeforeCheckStatus() const override;
bool checkStatus(const gl::Context *context) const override;
angle::Result syncState(const gl::Context *context,
......
......@@ -810,6 +810,35 @@ bool FramebufferVk::checkStatus(const gl::Context *context) const
return true;
}
angle::Result FramebufferVk::updateColorAttachment(const gl::Context *context, size_t colorIndexGL)
{
ContextVk *contextVk = vk::GetImpl(context);
ANGLE_TRY(mRenderTargetCache.updateColorRenderTarget(context, mState, colorIndexGL));
// Update cached masks for masked clears.
RenderTargetVk *renderTarget = mRenderTargetCache.getColors()[colorIndexGL];
if (renderTarget)
{
const angle::Format &emulatedFormat = renderTarget->getImageFormat().imageFormat();
updateActiveColorMasks(colorIndexGL, emulatedFormat.redBits > 0,
emulatedFormat.greenBits > 0, emulatedFormat.blueBits > 0,
emulatedFormat.alphaBits > 0);
const angle::Format &sourceFormat = renderTarget->getImageFormat().angleFormat();
mEmulatedAlphaAttachmentMask.set(
colorIndexGL, sourceFormat.alphaBits == 0 && emulatedFormat.alphaBits > 0);
contextVk->updateColorMask(context->getState().getBlendState());
}
else
{
updateActiveColorMasks(colorIndexGL, false, false, false, false);
}
return angle::Result::Continue;
}
angle::Result FramebufferVk::syncState(const gl::Context *context,
const gl::Framebuffer::DirtyBits &dirtyBits)
{
......@@ -825,6 +854,10 @@ angle::Result FramebufferVk::syncState(const gl::Context *context,
case gl::Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT:
ANGLE_TRY(mRenderTargetCache.updateDepthStencilRenderTarget(context, mState));
break;
case gl::Framebuffer::DIRTY_BIT_DEPTH_BUFFER_CONTENTS:
case gl::Framebuffer::DIRTY_BIT_STENCIL_BUFFER_CONTENTS:
ANGLE_TRY(mRenderTargetCache.getDepthStencil()->flushStagedUpdates(contextVk));
break;
case gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS:
case gl::Framebuffer::DIRTY_BIT_READ_BUFFER:
case gl::Framebuffer::DIRTY_BIT_DEFAULT_WIDTH:
......@@ -834,33 +867,21 @@ angle::Result FramebufferVk::syncState(const gl::Context *context,
break;
default:
{
ASSERT(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0 &&
dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX);
size_t colorIndexGL =
static_cast<size_t>(dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
ANGLE_TRY(
mRenderTargetCache.updateColorRenderTarget(context, mState, colorIndexGL));
// Update cached masks for masked clears.
RenderTargetVk *renderTarget = mRenderTargetCache.getColors()[colorIndexGL];
if (renderTarget)
static_assert(gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0, "FB dirty bits");
if (dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX)
{
const angle::Format &emulatedFormat =
renderTarget->getImageFormat().imageFormat();
updateActiveColorMasks(
colorIndexGL, emulatedFormat.redBits > 0, emulatedFormat.greenBits > 0,
emulatedFormat.blueBits > 0, emulatedFormat.alphaBits > 0);
const angle::Format &sourceFormat =
renderTarget->getImageFormat().angleFormat();
mEmulatedAlphaAttachmentMask.set(
colorIndexGL, sourceFormat.alphaBits == 0 && emulatedFormat.alphaBits > 0);
contextVk->updateColorMask(context->getState().getBlendState());
size_t colorIndexGL = static_cast<size_t>(
dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
ANGLE_TRY(updateColorAttachment(context, colorIndexGL));
}
else
{
updateActiveColorMasks(colorIndexGL, false, false, false, false);
ASSERT(dirtyBit >= gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 &&
dirtyBit < gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_MAX);
size_t colorIndexGL = static_cast<size_t>(
dirtyBit - gl::Framebuffer::DIRTY_BIT_COLOR_BUFFER_CONTENTS_0);
ANGLE_TRY(mRenderTargetCache.getColors()[colorIndexGL]->flushStagedUpdates(
contextVk));
}
break;
}
......@@ -1171,13 +1192,10 @@ angle::Result FramebufferVk::readPixelsImpl(ContextVk *contextVk,
RendererVk *renderer = contextVk->getRenderer();
ANGLE_TRY(renderTarget->ensureImageInitialized(contextVk));
vk::CommandBuffer *commandBuffer = nullptr;
ANGLE_TRY(mFramebuffer.recordCommands(contextVk, &commandBuffer));
// Note that although we're reading from the image, we need to update the layout below.
vk::ImageHelper *srcImage =
renderTarget->getImageForRead(&mFramebuffer, vk::ImageLayout::TransferSrc, commandBuffer);
......
......@@ -186,6 +186,7 @@ class FramebufferVk : public FramebufferImpl
uint8_t clearStencilValue);
void updateActiveColorMasks(size_t colorIndex, bool r, bool g, bool b, bool a);
void updateRenderPassDesc();
angle::Result updateColorAttachment(const gl::Context *context, size_t colorIndex);
WindowSurfaceVk *mBackbuffer;
......
......@@ -10,6 +10,7 @@
#include "libANGLE/renderer/vulkan/RenderTargetVk.h"
#include "libANGLE/renderer/vulkan/CommandGraph.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/TextureVk.h"
#include "libANGLE/renderer/vulkan/vk_format_utils.h"
#include "libANGLE/renderer/vulkan/vk_helpers.h"
......@@ -17,7 +18,7 @@
namespace rx
{
RenderTargetVk::RenderTargetVk()
: mImage(nullptr), mImageView(nullptr), mLevelIndex(0), mLayerIndex(0), mOwner(nullptr)
: mImage(nullptr), mImageView(nullptr), mLevelIndex(0), mLayerIndex(0)
{}
RenderTargetVk::~RenderTargetVk() {}
......@@ -26,21 +27,18 @@ RenderTargetVk::RenderTargetVk(RenderTargetVk &&other)
: mImage(other.mImage),
mImageView(other.mImageView),
mLevelIndex(other.mLevelIndex),
mLayerIndex(other.mLayerIndex),
mOwner(other.mOwner)
mLayerIndex(other.mLayerIndex)
{}
void RenderTargetVk::init(vk::ImageHelper *image,
vk::ImageView *imageView,
size_t levelIndex,
size_t layerIndex,
TextureVk *owner)
size_t layerIndex)
{
mImage = image;
mImageView = imageView;
mLevelIndex = levelIndex;
mLayerIndex = layerIndex;
mOwner = owner;
}
void RenderTargetVk::reset()
......@@ -49,7 +47,6 @@ void RenderTargetVk::reset()
mImageView = nullptr;
mLevelIndex = 0;
mLayerIndex = 0;
mOwner = nullptr;
}
angle::Result RenderTargetVk::onColorDraw(ContextVk *contextVk,
......@@ -59,8 +56,6 @@ angle::Result RenderTargetVk::onColorDraw(ContextVk *contextVk,
ASSERT(commandBuffer->valid());
ASSERT(!mImage->getFormat().imageFormat().hasDepthOrStencilBits());
ANGLE_TRY(ensureImageInitialized(contextVk));
// TODO(jmadill): Use automatic layout transition. http://anglebug.com/2361
mImage->changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, vk::ImageLayout::ColorAttachment,
commandBuffer);
......@@ -82,8 +77,6 @@ angle::Result RenderTargetVk::onDepthStencilDraw(ContextVk *contextVk,
const angle::Format &format = mImage->getFormat().imageFormat();
VkImageAspectFlags aspectFlags = vk::GetDepthStencilAspectFlags(format);
ANGLE_TRY(ensureImageInitialized(contextVk));
mImage->changeLayout(aspectFlags, vk::ImageLayout::DepthStencilAttachment, commandBuffer);
// Set up dependencies between the RT resource and the Framebuffer.
......@@ -132,7 +125,6 @@ void RenderTargetVk::updateSwapchainImage(vk::ImageHelper *image, vk::ImageView
ASSERT(image && image->valid() && imageView && imageView->valid());
mImage = image;
mImageView = imageView;
mOwner = nullptr;
}
vk::ImageHelper *RenderTargetVk::getImageForRead(vk::CommandGraphResource *readingResource,
......@@ -170,16 +162,16 @@ vk::ImageHelper *RenderTargetVk::getImageForWrite(vk::CommandGraphResource *writ
return mImage;
}
angle::Result RenderTargetVk::ensureImageInitialized(ContextVk *contextVk)
angle::Result RenderTargetVk::flushStagedUpdates(ContextVk *contextVk)
{
if (mOwner)
{
// If the render target source is a texture, make sure the image is initialized and its
// staged updates flushed.
return mOwner->ensureImageInitialized(contextVk);
}
return angle::Result::Continue;
ASSERT(mImage->valid());
if (!mImage->hasStagedUpdates())
return angle::Result::Continue;
vk::CommandBuffer *commandBuffer;
ANGLE_TRY(mImage->recordCommands(contextVk, &commandBuffer));
return mImage->flushStagedUpdates(contextVk, mLevelIndex, mLevelIndex + 1, mLayerIndex,
mLayerIndex + 1, commandBuffer);
}
} // namespace rx
......@@ -46,8 +46,7 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
void init(vk::ImageHelper *image,
vk::ImageView *imageView,
size_t levelIndex,
size_t layerIndex,
TextureVk *owner);
size_t layerIndex);
void reset();
// Note: RenderTargets should be called in order, with the depth/stencil onRender last.
......@@ -79,7 +78,7 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
// RenderTargetVk pointer.
void updateSwapchainImage(vk::ImageHelper *image, vk::ImageView *imageView);
angle::Result ensureImageInitialized(ContextVk *contextVk);
angle::Result flushStagedUpdates(ContextVk *contextVk);
private:
vk::ImageHelper *mImage;
......@@ -88,10 +87,6 @@ class RenderTargetVk final : public FramebufferAttachmentRenderTarget
vk::ImageView *mImageView;
size_t mLevelIndex;
size_t mLayerIndex;
// If owned by the texture, this will be non-nullptr, and is used to ensure texture changes
// are flushed.
TextureVk *mOwner;
};
} // namespace rx
......
......@@ -85,10 +85,9 @@ angle::Result RenderbufferVk::setStorage(const gl::Context *context,
&mImageView, 0, 1));
// Clear the renderbuffer if it has emulated channels.
ANGLE_TRY(mImage->clearIfEmulatedFormat(vk::GetImpl(context), gl::ImageIndex::Make2D(0),
vkFormat));
mImage->clearIfEmulatedFormat(vk::GetImpl(context), gl::ImageIndex::Make2D(0), vkFormat);
mRenderTarget.init(mImage, &mImageView, 0, 0, nullptr);
mRenderTarget.init(mImage, &mImageView, 0, 0);
}
return angle::Result::Continue;
......@@ -135,8 +134,7 @@ angle::Result RenderbufferVk::setStorageEGLImageTarget(const gl::Context *contex
gl::SwizzleState(), &mImageView, imageVk->getImageLevel(),
1, imageVk->getImageLayer(), 1));
mRenderTarget.init(mImage, &mImageView, imageVk->getImageLevel(), imageVk->getImageLayer(),
nullptr);
mRenderTarget.init(mImage, &mImageView, imageVk->getImageLevel(), imageVk->getImageLayer());
return angle::Result::Continue;
}
......@@ -147,6 +145,7 @@ angle::Result RenderbufferVk::getAttachmentRenderTarget(const gl::Context *conte
FramebufferAttachmentRenderTarget **rtOut)
{
ASSERT(mImage && mImage->valid());
ANGLE_TRY(mRenderTarget.flushStagedUpdates(vk::GetImpl(context)));
*rtOut = &mRenderTarget;
return angle::Result::Continue;
}
......
......@@ -87,11 +87,34 @@ constexpr VkImageUsageFlags kSurfaceVKDepthStencilImageUsageFlags =
} // namespace
OffscreenSurfaceVk::AttachmentImage::AttachmentImage()
SurfaceVk::SurfaceVk(const egl::SurfaceState &surfaceState) : SurfaceImpl(surfaceState) {}
SurfaceVk::~SurfaceVk() = default;
angle::Result SurfaceVk::getAttachmentRenderTarget(const gl::Context *context,
GLenum binding,
const gl::ImageIndex &imageIndex,
FramebufferAttachmentRenderTarget **rtOut)
{
renderTarget.init(&image, &imageView, 0, 0, nullptr);
ContextVk *contextVk = vk::GetImpl(context);
if (binding == GL_BACK)
{
ANGLE_TRY(mColorRenderTarget.flushStagedUpdates(contextVk));
*rtOut = &mColorRenderTarget;
}
else
{
ASSERT(binding == GL_DEPTH || binding == GL_STENCIL || binding == GL_DEPTH_STENCIL);
ANGLE_TRY(mDepthStencilRenderTarget.flushStagedUpdates(contextVk));
*rtOut = &mDepthStencilRenderTarget;
}
return angle::Result::Continue;
}
OffscreenSurfaceVk::AttachmentImage::AttachmentImage() {}
OffscreenSurfaceVk::AttachmentImage::~AttachmentImage() = default;
angle::Result OffscreenSurfaceVk::AttachmentImage::initialize(DisplayVk *displayVk,
......@@ -120,7 +143,7 @@ angle::Result OffscreenSurfaceVk::AttachmentImage::initialize(DisplayVk *display
&imageView, 0, 1));
// Clear the image if it has emulated channels.
ANGLE_TRY(image.clearIfEmulatedFormat(displayVk, gl::ImageIndex::Make2D(0), vkFormat));
image.clearIfEmulatedFormat(displayVk, gl::ImageIndex::Make2D(0), vkFormat);
return angle::Result::Continue;
}
......@@ -138,8 +161,12 @@ void OffscreenSurfaceVk::AttachmentImage::destroy(const egl::Display *display)
OffscreenSurfaceVk::OffscreenSurfaceVk(const egl::SurfaceState &surfaceState,
EGLint width,
EGLint height)
: SurfaceImpl(surfaceState), mWidth(width), mHeight(height)
{}
: SurfaceVk(surfaceState), mWidth(width), mHeight(height)
{
mColorRenderTarget.init(&mColorAttachment.image, &mColorAttachment.imageView, 0, 0);
mDepthStencilRenderTarget.init(&mDepthStencilAttachment.image,
&mDepthStencilAttachment.imageView, 0, 0);
}
OffscreenSurfaceVk::~OffscreenSurfaceVk() {}
......@@ -250,25 +277,6 @@ EGLint OffscreenSurfaceVk::getSwapBehavior() const
return EGL_BUFFER_DESTROYED;
}
angle::Result OffscreenSurfaceVk::getAttachmentRenderTarget(
const gl::Context *context,
GLenum binding,
const gl::ImageIndex &imageIndex,
FramebufferAttachmentRenderTarget **rtOut)
{
if (binding == GL_BACK)
{
*rtOut = &mColorAttachment.renderTarget;
}
else
{
ASSERT(binding == GL_DEPTH || binding == GL_STENCIL || binding == GL_DEPTH_STENCIL);
*rtOut = &mDepthStencilAttachment.renderTarget;
}
return angle::Result::Continue;
}
angle::Result OffscreenSurfaceVk::initializeContents(const gl::Context *context,
const gl::ImageIndex &imageIndex)
{
......@@ -351,7 +359,7 @@ WindowSurfaceVk::WindowSurfaceVk(const egl::SurfaceState &surfaceState,
EGLNativeWindowType window,
EGLint width,
EGLint height)
: SurfaceImpl(surfaceState),
: SurfaceVk(surfaceState),
mNativeWindowType(window),
mSurface(VK_NULL_HANDLE),
mInstance(VK_NULL_HANDLE),
......@@ -364,10 +372,10 @@ WindowSurfaceVk::WindowSurfaceVk(const egl::SurfaceState &surfaceState,
mCurrentSwapchainImageIndex(0),
mCurrentSwapHistoryIndex(0)
{
mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageView, 0, 0, nullptr);
// Initialize the color render target with the multisampled targets. If not multisampled, the
// render target will be updated to refer to a swapchain image on every acquire.
mColorRenderTarget.init(&mColorImageMS, &mColorImageViewMS, 0, 0, nullptr);
mColorRenderTarget.init(&mColorImageMS, &mColorImageViewMS, 0, 0);
mDepthStencilRenderTarget.init(&mDepthStencilImage, &mDepthStencilImageView, 0, 0);
}
WindowSurfaceVk::~WindowSurfaceVk()
......@@ -604,8 +612,7 @@ angle::Result WindowSurfaceVk::recreateSwapchain(DisplayVk *displayVk,
&mColorImageViewMS, 0, 1));
// Clear the image if it has emulated channels.
ANGLE_TRY(
mColorImageMS.clearIfEmulatedFormat(displayVk, gl::ImageIndex::Make2D(0), format));
mColorImageMS.clearIfEmulatedFormat(displayVk, gl::ImageIndex::Make2D(0), format);
}
mSwapchainImages.resize(imageCount);
......@@ -627,8 +634,7 @@ angle::Result WindowSurfaceVk::recreateSwapchain(DisplayVk *displayVk,
// Clear the image if it has emulated channels. If a multisampled image exists, this
// image will be unused until a pre-present resolve, at which point it will be fully
// initialized and wouldn't need a clear.
ANGLE_TRY(
member.image.clearIfEmulatedFormat(displayVk, gl::ImageIndex::Make2D(0), format));
member.image.clearIfEmulatedFormat(displayVk, gl::ImageIndex::Make2D(0), format);
}
}
......@@ -652,8 +658,7 @@ angle::Result WindowSurfaceVk::recreateSwapchain(DisplayVk *displayVk,
// We will need to pass depth/stencil image views to the RenderTargetVk in the future.
// Clear the image if it has emulated channels.
ANGLE_TRY(mDepthStencilImage.clearIfEmulatedFormat(displayVk, gl::ImageIndex::Make2D(0),
dsFormat));
mDepthStencilImage.clearIfEmulatedFormat(displayVk, gl::ImageIndex::Make2D(0), dsFormat);
}
return angle::Result::Continue;
......@@ -880,7 +885,7 @@ angle::Result WindowSurfaceVk::present(DisplayVk *displayVk,
// Update the swap history for this presentation
swap.sharedFence = renderer->getLastSubmittedFence();
swap.semaphores = std::move(mFlushSemaphoreChain);
swap.semaphores = std::move(mFlushSemaphoreChain);
++mCurrentSwapHistoryIndex;
mCurrentSwapHistoryIndex =
mCurrentSwapHistoryIndex == mSwapHistory.size() ? 0 : mCurrentSwapHistoryIndex;
......@@ -1053,24 +1058,6 @@ EGLint WindowSurfaceVk::getSwapBehavior() const
return EGL_BUFFER_DESTROYED;
}
angle::Result WindowSurfaceVk::getAttachmentRenderTarget(const gl::Context *context,
GLenum binding,
const gl::ImageIndex &imageIndex,
FramebufferAttachmentRenderTarget **rtOut)
{
if (binding == GL_BACK)
{
*rtOut = &mColorRenderTarget;
}
else
{
ASSERT(binding == GL_DEPTH || binding == GL_STENCIL || binding == GL_DEPTH_STENCIL);
*rtOut = &mDepthStencilRenderTarget;
}
return angle::Result::Continue;
}
angle::Result WindowSurfaceVk::getCurrentFramebuffer(vk::Context *context,
const vk::RenderPass &compatibleRenderPass,
vk::Framebuffer **framebufferOut)
......
......@@ -20,7 +20,23 @@ namespace rx
{
class RendererVk;
class OffscreenSurfaceVk : public SurfaceImpl
class SurfaceVk : public SurfaceImpl
{
public:
angle::Result getAttachmentRenderTarget(const gl::Context *context,
GLenum binding,
const gl::ImageIndex &imageIndex,
FramebufferAttachmentRenderTarget **rtOut) override;
protected:
SurfaceVk(const egl::SurfaceState &surfaceState);
~SurfaceVk() override;
RenderTargetVk mColorRenderTarget;
RenderTargetVk mDepthStencilRenderTarget;
};
class OffscreenSurfaceVk : public SurfaceVk
{
public:
OffscreenSurfaceVk(const egl::SurfaceState &surfaceState, EGLint width, EGLint height);
......@@ -52,11 +68,6 @@ class OffscreenSurfaceVk : public SurfaceImpl
EGLint isPostSubBufferSupported() const override;
EGLint getSwapBehavior() const override;
angle::Result getAttachmentRenderTarget(const gl::Context *context,
GLenum binding,
const gl::ImageIndex &imageIndex,
FramebufferAttachmentRenderTarget **rtOut) override;
angle::Result initializeContents(const gl::Context *context,
const gl::ImageIndex &imageIndex) override;
......@@ -77,7 +88,6 @@ class OffscreenSurfaceVk : public SurfaceImpl
vk::ImageHelper image;
vk::ImageView imageView;
RenderTargetVk renderTarget;
};
angle::Result initializeImpl(DisplayVk *displayVk);
......@@ -89,7 +99,7 @@ class OffscreenSurfaceVk : public SurfaceImpl
AttachmentImage mDepthStencilAttachment;
};
class WindowSurfaceVk : public SurfaceImpl
class WindowSurfaceVk : public SurfaceVk
{
public:
WindowSurfaceVk(const egl::SurfaceState &surfaceState,
......@@ -125,11 +135,6 @@ class WindowSurfaceVk : public SurfaceImpl
EGLint isPostSubBufferSupported() const override;
EGLint getSwapBehavior() const override;
angle::Result getAttachmentRenderTarget(const gl::Context *context,
GLenum binding,
const gl::ImageIndex &imageIndex,
FramebufferAttachmentRenderTarget **rtOut) override;
angle::Result initializeContents(const gl::Context *context,
const gl::ImageIndex &imageIndex) override;
......@@ -147,7 +152,7 @@ class WindowSurfaceVk : public SurfaceImpl
VkInstance mInstance;
private:
virtual angle::Result createSurfaceVk(vk::Context *context, gl::Extents *extentsOut) = 0;
virtual angle::Result createSurfaceVk(vk::Context *context, gl::Extents *extentsOut) = 0;
virtual angle::Result getCurrentWindowSize(vk::Context *context, gl::Extents *extentsOut) = 0;
angle::Result initializeImpl(DisplayVk *displayVk);
......@@ -178,9 +183,6 @@ class WindowSurfaceVk : public SurfaceImpl
VkSurfaceTransformFlagBitsKHR mPreTransform;
VkCompositeAlphaFlagBitsKHR mCompositeAlpha;
RenderTargetVk mColorRenderTarget;
RenderTargetVk mDepthStencilRenderTarget;
uint32_t mCurrentSwapchainImageIndex;
struct SwapchainImage : angle::NonCopyable
......
......@@ -403,7 +403,6 @@ angle::Result TextureVk::copySubImageImpl(const gl::Context *context,
destOffset.y + clippedSourceArea.y - sourceArea.y, 0);
RenderTargetVk *colorReadRT = framebufferVk->getColorReadRenderTarget();
ANGLE_TRY(colorReadRT->ensureImageInitialized(contextVk));
const vk::Format &srcFormat = colorReadRT->getImageFormat();
const vk::Format &destFormat = renderer->getFormat(internalFormat.sizedInternalFormat);
......@@ -930,7 +929,7 @@ void TextureVk::setImageHelper(RendererVk *renderer,
mImage->initStagingBuffer(renderer, format);
mRenderTarget.init(mImage, &mDrawBaseLevelImageView, getNativeImageLevel(0),
getNativeImageLayer(0), this);
getNativeImageLayer(0));
// Force re-creation of cube map render targets next time they are needed
mCubeMapRenderTargets.clear();
......@@ -1215,7 +1214,7 @@ angle::Result TextureVk::initCubeMapRenderTargets(ContextVk *contextVk)
vk::ImageView *imageView;
ANGLE_TRY(getLayerLevelDrawImageView(contextVk, cubeMapFaceIndex, 0, &imageView));
mCubeMapRenderTargets[cubeMapFaceIndex].init(mImage, imageView, getNativeImageLevel(0),
getNativeImageLayer(cubeMapFaceIndex), this);
getNativeImageLayer(cubeMapFaceIndex));
}
return angle::Result::Continue;
}
......@@ -1285,6 +1284,8 @@ angle::Result TextureVk::initializeContents(const gl::Context *context,
mImage->stageSubresourceRobustClear(imageIndex, format.angleFormat());
// Note that we cannot ensure the image is initialized because we might be calling subImage
// on a non-complete cube map.
return angle::Result::Continue;
}
......
......@@ -2150,17 +2150,14 @@ void ImageHelper::stageSubresourceEmulatedClear(const gl::ImageIndex &index,
stageSubresourceClear(index, format, kEmulatedInitColorValue, kWebGLInitDepthStencilValue);
}
angle::Result ImageHelper::clearIfEmulatedFormat(Context *context,
const gl::ImageIndex &index,
const Format &format)
void ImageHelper::clearIfEmulatedFormat(Context *context,
const gl::ImageIndex &index,
const Format &format)
{
if (format.hasEmulatedImageChannels())
{
stageSubresourceEmulatedClear(index, format.angleFormat());
ANGLE_TRY(flushAllStagedUpdates(context));
}
return angle::Result::Continue;
}
void ImageHelper::stageSubresourceClear(const gl::ImageIndex &index,
......
......@@ -690,9 +690,7 @@ class ImageHelper final : public CommandGraphResource
// If the image has emulated channels, we clear them once so as not to leave garbage on those
// channels.
angle::Result clearIfEmulatedFormat(Context *context,
const gl::ImageIndex &index,
const Format &format);
void clearIfEmulatedFormat(Context *context, const gl::ImageIndex &index, const Format &format);
// This will use the underlying dynamic buffer to allocate some memory to be used as a src or
// dst.
......
......@@ -1267,6 +1267,9 @@ class SimpleStateChangeTest : public ANGLETest
void simpleDrawWithBuffer(GLBuffer *buffer);
void simpleDrawWithColor(const GLColor &color);
using UpdateFunc = std::function<void(GLenum, GLTexture *, GLint, GLint, const GLColor &)>;
void updateTextureBoundToFramebufferHelper(UpdateFunc updateFunc);
};
class SimpleStateChangeTestES3 : public SimpleStateChangeTest
......@@ -1789,6 +1792,95 @@ TEST_P(SimpleStateChangeTest, UpdateTextureInUse)
ASSERT_GL_NO_ERROR();
}
void SimpleStateChangeTest::updateTextureBoundToFramebufferHelper(UpdateFunc updateFunc)
{
std::vector<GLColor> red(4, GLColor::red);
std::vector<GLColor> green(4, GLColor::green);
GLTexture renderTarget;
glBindTexture(GL_TEXTURE_2D, renderTarget);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, red.data());
GLFramebuffer fbo;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTarget,
0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER);
glViewport(0, 0, 2, 2);
ASSERT_GL_NO_ERROR();
GLTexture tex;
glBindTexture(GL_TEXTURE_2D, tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, red.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Draw once to flush dirty state bits.
draw2DTexturedQuad(0.5f, 1.0f, true);
ASSERT_GL_NO_ERROR();
// Update the (0, 1) pixel to be blue
updateFunc(GL_TEXTURE_2D, &renderTarget, 0, 1, GLColor::blue);
// Draw green to the right half of the Framebuffer.
glBindTexture(GL_TEXTURE_2D, tex);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, green.data());
glViewport(1, 0, 1, 2);
draw2DTexturedQuad(0.5f, 1.0f, true);
// Update the (1, 1) pixel to be yellow
updateFunc(GL_TEXTURE_2D, &renderTarget, 1, 1, GLColor::yellow);
ASSERT_GL_NO_ERROR();
// Verify we have a quad with the right colors in the FBO.
std::vector<GLColor> expected = {
{GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow}};
std::vector<GLColor> actual(4);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glReadPixels(0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, actual.data());
EXPECT_EQ(expected, actual);
}
// Tests that TexSubImage updates are flushed before rendering.
TEST_P(SimpleStateChangeTest, TexSubImageOnTextureBoundToFrambuffer)
{
auto updateFunc = [](GLenum textureBinding, GLTexture *tex, GLint x, GLint y,
const GLColor &color) {
glBindTexture(textureBinding, *tex);
glTexSubImage2D(textureBinding, 0, x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, color.data());
};
updateTextureBoundToFramebufferHelper(updateFunc);
}
// Tests that CopyTexSubImage updates are flushed before rendering.
TEST_P(SimpleStateChangeTest, CopyTexSubImageOnTextureBoundToFrambuffer)
{
GLTexture copySource;
glBindTexture(GL_TEXTURE_2D, copySource);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
GLFramebuffer copyFBO;
glBindFramebuffer(GL_READ_FRAMEBUFFER, copyFBO);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, copySource, 0);
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_READ_FRAMEBUFFER);
auto updateFunc = [&copySource](GLenum textureBinding, GLTexture *tex, GLint x, GLint y,
const GLColor &color) {
glBindTexture(GL_TEXTURE_2D, copySource);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, color.data());
glBindTexture(textureBinding, *tex);
glCopyTexSubImage2D(textureBinding, 0, x, y, 0, 0, 1, 1);
};
updateTextureBoundToFramebufferHelper(updateFunc);
}
// Tests deleting a Framebuffer that is in use.
TEST_P(SimpleStateChangeTest, DeleteFramebufferInUse)
{
......
......@@ -67,6 +67,11 @@ struct SystemInfo;
#define EXPECT_EGLENUM_EQ(expected, actual) \
EXPECT_EQ(static_cast<EGLenum>(expected), static_cast<EGLenum>(actual))
#define ASSERT_GL_FRAMEBUFFER_COMPLETE(framebuffer) \
ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(framebuffer))
#define EXPECT_GL_FRAMEBUFFER_COMPLETE(framebuffer) \
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(framebuffer))
namespace angle
{
struct GLColorRGB
......@@ -75,6 +80,9 @@ struct GLColorRGB
GLColorRGB(GLubyte r, GLubyte g, GLubyte b);
GLColorRGB(const angle::Vector3 &floatColor);
const GLubyte *data() const { return &R; }
GLubyte *data() { return &R; }
GLubyte R, G, B;
static const GLColorRGB black;
......
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