Vulkan: Mask out Depth/Stencil RTs in feedback loops.

This should enable some cases of limited support for feedback loops
with depth/stencil buffers. For example with Manhattan and the Vulkan
back-end.

Increases the number of RenderPasses in Manhattan slightly. This will
regress performance slightly until we can work out a better solution
that is also conformant with the spec.

Bug: angleproject:4517
Change-Id: I2758e6b4c2a930474c09cdc0950f3b6c34541089
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2106670
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Courtney Goeltzenleuchter <courtneygo@google.com>
This commit is contained in:
Jamie Madill 2020-03-26 17:22:18 -04:00 коммит произвёл Commit Bot
Родитель 68083e8996
Коммит d03b15b2f9
8 изменённых файлов: 159 добавлений и 52 удалений

Просмотреть файл

@ -13,11 +13,11 @@
#include <string>
#include <vector>
#define ANGLE_FEATURE_CONDITION(set, feature, cond) \
do \
{ \
set->feature.enabled = cond; \
set->feature.condition = ANGLE_STRINGIFY(cond); \
#define ANGLE_FEATURE_CONDITION(set, feature, cond) \
do \
{ \
(set)->feature.enabled = cond; \
(set)->feature.condition = ANGLE_STRINGIFY(cond); \
} while (0)
namespace angle

Просмотреть файл

@ -268,6 +268,12 @@ struct FeaturesVk : FeatureSetBase
Feature enablePrecisionQualifiers = {
"enable_precision_qualifiers", FeatureCategory::VulkanFeatures,
"Enable precision qualifiers in shaders", &members, "http://anglebug.com/3078"};
// Support Depth/Stencil rendering feedback loops by masking out the depth/stencil buffer.
// Manhattan uses this feature in a few draw calls.
Feature supportDepthStencilRenderingFeedbackLoops = {
"support_depth_stencil_rendering_feedback_loops", FeatureCategory::VulkanFeatures,
"Suport depth/stencil rendering feedback loops", &members, "http://anglebug.com/4490"};
};
inline FeaturesVk::FeaturesVk() = default;

Просмотреть файл

@ -61,14 +61,9 @@ ANGLE_INLINE void MarkShaderStorageBufferUsage(const Context *context)
// Return true if the draw is a no-op, else return false.
// A no-op draw occurs if the count of vertices is less than the minimum required to
// have a valid primitive for this mode (0 for points, 0-1 for lines, 0-2 for tris).
// We also no-op draws that have rendering feedback loops. This is spec:
// "In this scenario, the framebuffer will be considered framebuffer complete
// but the values of fragments rendered while in this state will be undefined."
// From 3.2 spec: 9.3.1 Rendering Feedback Loops
ANGLE_INLINE bool Context::noopDraw(PrimitiveMode mode, GLsizei count)
{
return count < kMinimumPrimitiveCounts[mode] ||
mState.mDrawFramebuffer->hasRenderingFeedbackLoop();
return count < kMinimumPrimitiveCounts[mode];
}
ANGLE_INLINE angle::Result Context::syncDirtyBits()

Просмотреть файл

@ -699,11 +699,13 @@ bool FramebufferState::updateAttachmentFeedbackLoopAndReturnIfChanged(size_t dir
break;
default:
{
ASSERT(dirtyBit <= Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX);
previous = mDrawBufferFeedbackLoops.test(dirtyBit);
loop = mColorAttachments[dirtyBit].isBoundAsSamplerOrImage();
mDrawBufferFeedbackLoops[dirtyBit] = loop;
break;
}
}
updateHasRenderingFeedbackLoop();

Просмотреть файл

@ -114,7 +114,9 @@ FramebufferVk::FramebufferVk(RendererVk *renderer,
: FramebufferImpl(state),
mBackbuffer(backbuffer),
mFramebuffer(nullptr),
mActiveColorComponents(0)
mActiveColorComponents(0),
mSupportDepthStencilFeedbackLoops(
renderer->getFeatures().supportDepthStencilRenderingFeedbackLoops.enabled)
{
mReadPixelBuffer.init(renderer, VK_BUFFER_USAGE_TRANSFER_DST_BIT, kReadPixelsBufferAlignment,
kMinReadPixelsBufferSize, true);
@ -229,6 +231,36 @@ angle::Result FramebufferVk::clearImpl(const gl::Context *context,
// The front-end should ensure we don't attempt to clear stencil if all bits are masked.
ASSERT(!clearStencil || stencilMask != 0);
// Special case for rendering feedback loops: clears are always valid in GL since they don't
// sample from any textures.
if ((clearDepth || clearStencil) && mState.hasDepthStencilFeedbackLoop())
{
// We currently don't handle scissored clears with rendering feedback loops.
ANGLE_VK_CHECK(contextVk, scissoredRenderArea == getCompleteRenderArea(),
VK_ERROR_INCOMPATIBLE_DRIVER);
RenderTargetVk *depthStencilRT = mRenderTargetCache.getDepthStencil(true);
vk::ImageHelper &image = depthStencilRT->getImage();
vk::CommandBuffer *commandBuffer;
ANGLE_TRY(
contextVk->onImageWrite(image.getAspectFlags(), vk::ImageLayout::TransferDst, &image));
ANGLE_TRY(contextVk->endRenderPassAndGetCommandBuffer(&commandBuffer));
VkImageSubresourceRange range;
range.aspectMask = image.getAspectFlags();
range.baseMipLevel = depthStencilRT->getLevelIndex();
range.levelCount = 1;
range.baseArrayLayer = depthStencilRT->getLayerIndex();
range.layerCount = 1;
commandBuffer->clearDepthStencilImage(image.getImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
clearDepthStencilValue, 1, &range);
clearDepth = false;
clearStencil = false;
}
// If there is nothing to clear, return right away (for example, if asked to clear depth, but
// there is no depth attachment).
if (!clearColor && !clearDepth && !clearStencil)
@ -447,7 +479,9 @@ angle::Result FramebufferVk::readPixels(const gl::Context *context,
RenderTargetVk *FramebufferVk::getDepthStencilRenderTarget() const
{
return mRenderTargetCache.getDepthStencil(true);
// If we mask out depth/stencil feedback loops, do not allow the user to access the looped DS
// render target. Passing "false" to getDepthStencil forces a return of "nullptr" for loops.
return mRenderTargetCache.getDepthStencil(!mSupportDepthStencilFeedbackLoops);
}
RenderTargetVk *FramebufferVk::getColorDrawRenderTarget(size_t colorIndex) const
@ -983,7 +1017,7 @@ angle::Result FramebufferVk::invalidateImpl(ContextVk *contextVk,
void FramebufferVk::updateDepthStencilAttachmentSerial(ContextVk *contextVk)
{
RenderTargetVk *depthStencilRT = mRenderTargetCache.getDepthStencil(true);
RenderTargetVk *depthStencilRT = getDepthStencilRenderTarget();
if (depthStencilRT != nullptr)
{
@ -1021,7 +1055,7 @@ angle::Result FramebufferVk::syncState(const gl::Context *context,
case gl::Framebuffer::DIRTY_BIT_DEPTH_BUFFER_CONTENTS:
case gl::Framebuffer::DIRTY_BIT_STENCIL_BUFFER_CONTENTS:
{
RenderTargetVk *depthStencilRT = mRenderTargetCache.getDepthStencil(true);
RenderTargetVk *depthStencilRT = getDepthStencilRenderTarget();
ASSERT(depthStencilRT != nullptr);
ANGLE_TRY(depthStencilRT->flushStagedUpdates(contextVk));
mCurrentFramebufferDesc.update(vk::kFramebufferDescDepthStencilIndex,
@ -1141,7 +1175,7 @@ void FramebufferVk::updateRenderPassDesc()
}
}
RenderTargetVk *depthStencilRenderTarget = mRenderTargetCache.getDepthStencil(true);
RenderTargetVk *depthStencilRenderTarget = getDepthStencilRenderTarget();
if (depthStencilRenderTarget)
{
mRenderPassDesc.packDepthStencilAttachment(
@ -1200,7 +1234,7 @@ angle::Result FramebufferVk::getFramebuffer(ContextVk *contextVk, vk::Framebuffe
attachmentsSize = colorRenderTarget->getExtents();
}
RenderTargetVk *depthStencilRenderTarget = mRenderTargetCache.getDepthStencil(true);
RenderTargetVk *depthStencilRenderTarget = getDepthStencilRenderTarget();
if (depthStencilRenderTarget)
{
const vk::ImageView *imageView = nullptr;
@ -1324,7 +1358,7 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
attachmentClearValues.emplace_back(kUninitializedClearValue);
}
RenderTargetVk *depthStencilRenderTarget = mRenderTargetCache.getDepthStencil(true);
RenderTargetVk *depthStencilRenderTarget = getDepthStencilRenderTarget();
if (depthStencilRenderTarget)
{
ANGLE_TRY(depthStencilRenderTarget->onDepthStencilDraw(contextVk));
@ -1402,7 +1436,7 @@ RenderTargetVk *FramebufferVk::getFirstRenderTarget() const
}
}
return mRenderTargetCache.getDepthStencil(true);
return getDepthStencilRenderTarget();
}
GLint FramebufferVk::getSamples() const

Просмотреть файл

@ -186,6 +186,7 @@ class FramebufferVk : public FramebufferImpl
vk::FramebufferDesc mCurrentFramebufferDesc;
std::unordered_map<vk::FramebufferDesc, vk::FramebufferHelper> mFramebufferCache;
bool mSupportDepthStencilFeedbackLoops;
};
} // namespace rx

Просмотреть файл

@ -767,7 +767,7 @@ angle::Result RendererVk::initialize(DisplayVk *displayVk,
if (ExtensionFound(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, instanceExtensionNames))
{
enabledInstanceExtensions.push_back(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME);
ANGLE_FEATURE_CONDITION((&mFeatures), supportsSwapchainColorspace, true);
ANGLE_FEATURE_CONDITION(&mFeatures, supportsSwapchainColorspace, true);
}
// Verify the required extensions are in the extension names set. Fail if not.
@ -1543,35 +1543,35 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev
{
ASSERT(mLineRasterizationFeatures.sType ==
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT);
ANGLE_FEATURE_CONDITION((&mFeatures), bresenhamLineRasterization, true);
ANGLE_FEATURE_CONDITION(&mFeatures, bresenhamLineRasterization, true);
}
else
{
// Use OpenGL line rasterization rules if extension not available by default.
// TODO(jmadill): Fix Android support. http://anglebug.com/2830
ANGLE_FEATURE_CONDITION((&mFeatures), basicGLLineRasterization, !IsAndroid());
ANGLE_FEATURE_CONDITION(&mFeatures, basicGLLineRasterization, !IsAndroid());
}
if (mProvokingVertexFeatures.provokingVertexLast == VK_TRUE)
{
ASSERT(mProvokingVertexFeatures.sType ==
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROVOKING_VERTEX_FEATURES_EXT);
ANGLE_FEATURE_CONDITION((&mFeatures), provokingVertex, true);
ANGLE_FEATURE_CONDITION(&mFeatures, provokingVertex, true);
}
// TODO(lucferron): Currently disabled on Intel only since many tests are failing and need
// investigation. http://anglebug.com/2728
ANGLE_FEATURE_CONDITION(
(&mFeatures), flipViewportY,
&mFeatures, flipViewportY,
!IsIntel(mPhysicalDeviceProperties.vendorID) &&
(mPhysicalDeviceProperties.apiVersion >= VK_MAKE_VERSION(1, 1, 0)) ||
ExtensionFound(VK_KHR_MAINTENANCE1_EXTENSION_NAME, deviceExtensionNames));
// http://anglebug.com/2838
ANGLE_FEATURE_CONDITION((&mFeatures), extraCopyBufferRegion, IsWindows() && isIntel);
ANGLE_FEATURE_CONDITION(&mFeatures, extraCopyBufferRegion, IsWindows() && isIntel);
// http://anglebug.com/3055
ANGLE_FEATURE_CONDITION((&mFeatures), forceCPUPathForCubeMapCopy, IsWindows() && isIntel);
ANGLE_FEATURE_CONDITION(&mFeatures, forceCPUPathForCubeMapCopy, IsWindows() && isIntel);
// Work around incorrect NVIDIA point size range clamping.
// http://anglebug.com/2970#c10
@ -1584,23 +1584,23 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev
nvidiaVersion =
angle::ParseNvidiaDriverVersion(this->mPhysicalDeviceProperties.driverVersion);
}
ANGLE_FEATURE_CONDITION((&mFeatures), clampPointSize,
ANGLE_FEATURE_CONDITION(&mFeatures, clampPointSize,
isNvidia && nvidiaVersion.major < uint32_t(IsWindows() ? 430 : 421));
// Work around ineffective compute-graphics barriers on Nexus 5X.
// TODO(syoussefi): Figure out which other vendors and driver versions are affected.
// http://anglebug.com/3019
ANGLE_FEATURE_CONDITION((&mFeatures), flushAfterVertexConversion,
ANGLE_FEATURE_CONDITION(&mFeatures, flushAfterVertexConversion,
IsAndroid() && IsNexus5X(mPhysicalDeviceProperties.vendorID,
mPhysicalDeviceProperties.deviceID));
ANGLE_FEATURE_CONDITION(
(&mFeatures), supportsIncrementalPresent,
&mFeatures, supportsIncrementalPresent,
ExtensionFound(VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, deviceExtensionNames));
#if defined(ANGLE_PLATFORM_ANDROID)
ANGLE_FEATURE_CONDITION(
(&mFeatures), supportsAndroidHardwareBuffer,
&mFeatures, supportsAndroidHardwareBuffer,
IsAndroid() &&
ExtensionFound(VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
deviceExtensionNames) &&
@ -1608,73 +1608,74 @@ void RendererVk::initFeatures(DisplayVk *displayVk, const ExtensionNameList &dev
#endif
ANGLE_FEATURE_CONDITION(
(&mFeatures), supportsExternalMemoryFd,
&mFeatures, supportsExternalMemoryFd,
ExtensionFound(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, deviceExtensionNames));
ANGLE_FEATURE_CONDITION(
(&mFeatures), supportsExternalMemoryFuchsia,
&mFeatures, supportsExternalMemoryFuchsia,
ExtensionFound(VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME, deviceExtensionNames));
ANGLE_FEATURE_CONDITION(
(&mFeatures), supportsExternalSemaphoreFd,
&mFeatures, supportsExternalSemaphoreFd,
ExtensionFound(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, deviceExtensionNames));
ANGLE_FEATURE_CONDITION(
(&mFeatures), supportsExternalSemaphoreFuchsia,
&mFeatures, supportsExternalSemaphoreFuchsia,
ExtensionFound(VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME, deviceExtensionNames));
ANGLE_FEATURE_CONDITION(
(&mFeatures), supportsShaderStencilExport,
&mFeatures, supportsShaderStencilExport,
ExtensionFound(VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME, deviceExtensionNames));
ANGLE_FEATURE_CONDITION((&mFeatures), supportsTransformFeedbackExtension,
ANGLE_FEATURE_CONDITION(&mFeatures, supportsTransformFeedbackExtension,
mTransformFeedbackFeatures.transformFeedback == VK_TRUE);
ANGLE_FEATURE_CONDITION((&mFeatures), supportsIndexTypeUint8,
ANGLE_FEATURE_CONDITION(&mFeatures, supportsIndexTypeUint8,
mIndexTypeUint8Features.indexTypeUint8 == VK_TRUE);
ANGLE_FEATURE_CONDITION((&mFeatures), emulateTransformFeedback,
ANGLE_FEATURE_CONDITION(&mFeatures, emulateTransformFeedback,
(mFeatures.supportsTransformFeedbackExtension.enabled == VK_FALSE &&
mPhysicalDeviceFeatures.vertexPipelineStoresAndAtomics == VK_TRUE));
ANGLE_FEATURE_CONDITION((&mFeatures), disableFifoPresentMode, IsLinux() && isIntel);
ANGLE_FEATURE_CONDITION(&mFeatures, disableFifoPresentMode, IsLinux() && isIntel);
ANGLE_FEATURE_CONDITION((&mFeatures), bindEmptyForUnusedDescriptorSets,
ANGLE_FEATURE_CONDITION(&mFeatures, bindEmptyForUnusedDescriptorSets,
IsAndroid() && isQualcomm);
ANGLE_FEATURE_CONDITION((&mFeatures), forceOldRewriteStructSamplers, IsAndroid() && !isSwS);
ANGLE_FEATURE_CONDITION(&mFeatures, forceOldRewriteStructSamplers, IsAndroid() && !isSwS);
ANGLE_FEATURE_CONDITION((&mFeatures), perFrameWindowSizeQuery,
ANGLE_FEATURE_CONDITION(&mFeatures, perFrameWindowSizeQuery,
isIntel || (IsWindows() && isAMD) || IsFuchsia());
// Disabled on AMD/windows due to buggy behavior.
ANGLE_FEATURE_CONDITION((&mFeatures), disallowSeamfulCubeMapEmulation, IsWindows() && isAMD);
ANGLE_FEATURE_CONDITION(&mFeatures, disallowSeamfulCubeMapEmulation, IsWindows() && isAMD);
ANGLE_FEATURE_CONDITION((&mFeatures), padBuffersToMaxVertexAttribStride, isAMD);
ANGLE_FEATURE_CONDITION(&mFeatures, padBuffersToMaxVertexAttribStride, isAMD);
mMaxVertexAttribStride = std::min(static_cast<uint32_t>(gl::limits::kMaxVertexAttribStride),
mPhysicalDeviceProperties.limits.maxVertexInputBindingStride);
ANGLE_FEATURE_CONDITION((&mFeatures), forceD16TexFilter, IsAndroid() && isQualcomm);
ANGLE_FEATURE_CONDITION(&mFeatures, forceD16TexFilter, IsAndroid() && isQualcomm);
ANGLE_FEATURE_CONDITION((&mFeatures), disableFlippingBlitWithCommand,
IsAndroid() && isQualcomm);
ANGLE_FEATURE_CONDITION(&mFeatures, disableFlippingBlitWithCommand, IsAndroid() && isQualcomm);
// Allocation sanitization disabled by default because of a heaveyweight implementation
// that can cause OOM and timeouts.
ANGLE_FEATURE_CONDITION((&mFeatures), allocateNonZeroMemory, false);
ANGLE_FEATURE_CONDITION(&mFeatures, allocateNonZeroMemory, false);
ANGLE_FEATURE_CONDITION(
(&mFeatures), supportsExternalMemoryHost,
&mFeatures, supportsExternalMemoryHost,
ExtensionFound(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, deviceExtensionNames));
// Pre-rotation support is not fully ready to be enabled.
ANGLE_FEATURE_CONDITION((&mFeatures), enablePreRotateSurfaces, false);
ANGLE_FEATURE_CONDITION(&mFeatures, enablePreRotateSurfaces, false);
// Currently disable FramebufferVk cache on Apple: http://anglebug.com/4442
ANGLE_FEATURE_CONDITION((&mFeatures), enableFramebufferVkCache, !IsApple());
ANGLE_FEATURE_CONDITION(&mFeatures, enableFramebufferVkCache, !IsApple());
// Currently disabled by default: http://anglebug.com/3078
ANGLE_FEATURE_CONDITION((&mFeatures), enablePrecisionQualifiers, false);
ANGLE_FEATURE_CONDITION(&mFeatures, enablePrecisionQualifiers, false);
ANGLE_FEATURE_CONDITION(&mFeatures, supportDepthStencilRenderingFeedbackLoops, true);
angle::PlatformMethods *platform = ANGLEPlatformCurrent();
platform->overrideFeaturesVk(platform, &mFeatures);

Просмотреть файл

@ -1252,6 +1252,74 @@ TEST_P(FramebufferTest_ES3, AttachmentStateChange)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Tests that we can support a feedback loop between a depth textures and the depth buffer.
// Does not totally mirror the case used in Manhattan. The Manhattan case seems to handle
// "clear" specially instead of rendering to depth in the same RP.
TEST_P(FramebufferTest_ES3, DepthFeedbackLoopSupported)
{
// Feedback loops not supported on D3D11 and may not ever be.
ANGLE_SKIP_TEST_IF(IsD3D11());
// Also this particular test doesn't work on Android despite similar support in Manhattan.
ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
constexpr GLuint kSize = 2;
glViewport(0, 0, kSize, kSize);
constexpr char kFS[] = R"(precision mediump float;
varying vec2 v_texCoord;
uniform sampler2D depth;
void main()
{
if (abs(texture2D(depth, v_texCoord).x - 0.5) < 0.1)
{
gl_FragColor = vec4(0, 1, 0, 1);
}
else
{
gl_FragColor = vec4(1, 0, 0, 1);
}
})";
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), kFS);
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
GLTexture colorTexture;
glBindTexture(GL_TEXTURE_2D, colorTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);
GLTexture depthTexture;
glBindTexture(GL_TEXTURE_2D, depthTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, kSize, kSize, 0, GL_DEPTH_COMPONENT,
GL_UNSIGNED_INT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Clear depth to 0.5.
glClearDepthf(0.5f);
glClear(GL_DEPTH_BUFFER_BIT);
// Disable the depth mask. Although this does not remove the feedback loop as defined by the
// spec it mimics what gfxbench does in its rendering tests.
glDepthMask(false);
// Verify we can sample the depth texture and get 0.5.
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
ANGLE_INSTANTIATE_TEST_ES2(AddDummyTextureNoRenderTargetTest);
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(FramebufferFormatsTest);
ANGLE_INSTANTIATE_TEST_ES3(FramebufferTest_ES3);