Commit 2c8f0845 by Olli Etuaho Committed by Commit Bot

Add ANGLE_multiview_multisample

We add a novel multiview multisampling extension that includes the requirement to explicitly resolve the multisampled framebuffer. The explicit resolve is much more straightforward to implement on top of OpenGL and D3D11 than implicit resolve found in the native extension OVR_multiview_multisampled_render_to_texture. It also has predictable performance characteristics. The extension allows multiview drawing to 2D multisample texture arrays and is now enabled on both the GL backend and the D3D11 backend. The implementation is fairly simple, as it involves just small changes in validation to allow multisampled framebuffer attachments. The multiview rendering logic is exactly the same regardless of whether multisampling is enabled. For the most part the same tests are used to test both multisampled and non-multisampled rendering. The tests will use a different framebuffer setup depending on the test param. They resolve the multisampled framebuffer to a non-multisampled framebuffer prior to any readbacks from the framebuffer. Some of the tests are adjusted so that they have the correct sub-pixel positioning of multisampled quads, so there won't be any pixels that would be just partially covered. The tests don't have any tolerance for partially covered pixels - if we find any platforms where the tests run into a sub-pixel positioning corner case, tolerance may need to be added later. BUG=angleproject:2775 TEST=angle_end2end_tests Change-Id: I590d7f300a92ea5439f2720d9db14a7976db2e1d Reviewed-on: https://chromium-review.googlesource.com/1221214 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 5c72f28d
Name
ANGLE_multiview_multisample
Name Strings
GL_ANGLE_multiview_multisample
Contributors
Olli Etuaho, NVIDIA Corporation
Contact
Olli Etuaho (oetuaho 'at' nvidia.com)
Status
Incomplete
Version
Last Modified Date: September 12, 2018
Author Revision: 1
Number
OpenGL ES Extension XX
Dependencies
OpenGL ES 3.0 and the extensions ANGLE_texture_multisample, ANGLE_multiview
and OES_texture_storage_multisample_2d_array are required.
With OpenGL ES 3.1, only the extensions ANGLE_multiview and
OES_texture_storage_multisample_2d_array are required.
Overview
This extension enhances the functionality introduced in ANGLE_multiview by
enabling rendering into multisample texture arrays in addition to regular
non-multisampled texture arrays.
IP Status
No known IP claims.
New Tokens
None
New Procedures and Functions
None
Additions to Chapter 4 of the OpenGL ES 3.0 Specification
(Per-Fragment Operations and the Framebuffer)
Modify section 4.4.2 (Attaching Images to Framebuffer Objects), p. 202
Add the following bullet point:
"* Layers of a two-dimensional multisample array texture which can be used
for multi-view rendering."
Modify the error list of FramebufferTextureMultiviewLayeredANGLE:
Change
"An INVALID_OPERATION error is generated if texture is not zero,
and does not name an existing texture object of type TEXTURE_2D_ARRAY."
to
"An INVALID_OPERATION error is generated if texture is not zero,
and does not name an existing texture object of type TEXTURE_2D_ARRAY or
TEXTURE_2D_MULTISAMPLE_ARRAY_OES."
Modify section 4.4.4.1 (Framebuffer Attachment Completeness), p. 213
Change the bullet point about num_views and base_view_index:
"If the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE is TEXTURE and
the value of FRAMEBUFFER_ATTACHMENT_OBJECT_NAME names a two-dimensional
array texture or a two-dimensional multisample array texture, then the sum
of FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_ANGLE and
FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_ANGLE must be less than the
number of layers in the texture."
Additions to the AGL/EGL/GLX/WGL Specifications
None
Errors
None
Issues
......@@ -250,7 +250,8 @@ Extensions::Extensions()
explicitContextGles1(false),
explicitContext(false),
parallelShaderCompile(false),
textureStorageMultisample2DArray(false)
textureStorageMultisample2DArray(false),
multiviewMultisample(false)
{
}
......@@ -856,6 +857,7 @@ const ExtensionInfoMap &GetExtensionInfoMap()
map["GL_ANGLE_explicit_context"] = enableableExtension(&Extensions::explicitContext);
map["GL_KHR_parallel_shader_compile"] = enableableExtension(&Extensions::parallelShaderCompile);
map["GL_OES_texture_storage_multisample_2d_array"] = enableableExtension(&Extensions::textureStorageMultisample2DArray);
map["GL_ANGLE_multiview_multisample"] = enableableExtension(&Extensions::multiviewMultisample);
// GLES1 extensinos
map["GL_OES_point_size_array"] = enableableExtension(&Extensions::pointSizeArray);
map["GL_OES_texture_cube_map"] = enableableExtension(&Extensions::textureCubeMap);
......
......@@ -429,6 +429,9 @@ struct Extensions
// GL_OES_texture_storage_multisample_2d_array
bool textureStorageMultisample2DArray;
// GL_ANGLE_multiview_multisample
bool multiviewMultisample;
};
struct ExtensionInfo
......
......@@ -3775,7 +3775,17 @@ void Context::framebufferTextureMultiviewLayered(GLenum target,
{
Texture *textureObj = getTexture(texture);
ImageIndex index = ImageIndex::Make2DArrayRange(level, baseViewIndex, numViews);
ImageIndex index;
if (textureObj->getType() == TextureType::_2DArray)
{
index = ImageIndex::Make2DArrayRange(level, baseViewIndex, numViews);
}
else
{
ASSERT(textureObj->getType() == TextureType::_2DMultisampleArray);
ASSERT(level == 0);
index = ImageIndex::Make2DMultisampleArrayRange(baseViewIndex, numViews);
}
framebuffer->setAttachmentMultiviewLayered(this, GL_TEXTURE, attachment, index, textureObj,
numViews, baseViewIndex);
}
......
......@@ -190,6 +190,11 @@ ImageIndex ImageIndex::Make2DMultisampleArray(GLint layerIndex)
return ImageIndex(TextureType::_2DMultisampleArray, 0, layerIndex, 1);
}
ImageIndex ImageIndex::Make2DMultisampleArrayRange(GLint layerIndex, GLint numLayers)
{
return ImageIndex(TextureType::_2DMultisampleArray, 0, layerIndex, numLayers);
}
bool ImageIndex::operator<(const ImageIndex &b) const
{
return std::tie(mType, mLevelIndex, mLayerIndex, mLayerCount) <
......
......@@ -56,6 +56,7 @@ class ImageIndex
GLint layerCount = 1);
static ImageIndex Make2DMultisample();
static ImageIndex Make2DMultisampleArray(GLint layerIndex = kEntireLevel);
static ImageIndex Make2DMultisampleArrayRange(GLint layerIndex, GLint layerCount);
static constexpr GLint kEntireLevel = static_cast<GLint>(-1);
......
......@@ -1563,6 +1563,8 @@ void GenerateCaps(ID3D11Device *device,
extensions->copyTexture = true;
extensions->copyCompressedTexture = true;
extensions->textureStorageMultisample2DArray = true;
extensions->multiviewMultisample =
(extensions->multiview && extensions->textureStorageMultisample2DArray);
// D3D11 Feature Level 10_0+ uses SV_IsFrontFace in HLSL to emulate gl_FrontFacing.
// D3D11 Feature Level 9_3 doesn't support SV_IsFrontFace, and has no equivalent, so can't support gl_FrontFacing.
......
......@@ -988,6 +988,9 @@ void GenerateCaps(const FunctionsGL *functions,
extensions->textureStorageMultisample2DArray =
functions->isAtLeastGL(gl::Version(4, 2)) || functions->isAtLeastGLES(gl::Version(3, 2));
extensions->multiviewMultisample =
extensions->textureStorageMultisample2DArray && extensions->multiview;
// NV_path_rendering
// We also need interface query which is available in
// >= 4.3 core or ARB_interface_query or >= GLES 3.1
......
......@@ -2837,7 +2837,6 @@ bool ValidateFramebufferTextureMultiviewLayeredANGLE(Context *context,
GLint baseViewIndex,
GLsizei numViews)
{
if (!ValidateFramebufferTextureMultiviewBaseANGLE(context, target, attachment, texture, level,
numViews))
{
......@@ -2858,7 +2857,18 @@ bool ValidateFramebufferTextureMultiviewLayeredANGLE(Context *context,
switch (tex->getType())
{
case TextureType::_2DArray:
case TextureType::_2DMultisampleArray:
{
if (tex->getType() == TextureType::_2DMultisampleArray)
{
if (!context->getExtensions().multiviewMultisample)
{
context->handleError(InvalidOperation()
<< "Texture's target must be GL_TEXTURE_2D_ARRAY.");
return false;
}
}
const Caps &caps = context->getCaps();
if (static_cast<GLuint>(baseViewIndex + numViews) > caps.maxArrayTextureLayers)
{
......@@ -2867,8 +2877,9 @@ bool ValidateFramebufferTextureMultiviewLayeredANGLE(Context *context,
"GL_MAX_ARRAY_TEXTURE_LAYERS.");
return false;
}
break;
}
break;
default:
context->handleError(InvalidOperation()
<< "Texture's target must be GL_TEXTURE_2D_ARRAY.");
......
......@@ -90,7 +90,7 @@ class FramebufferMultiviewSideBySideClearTest : public FramebufferMultiviewTest
glGenTextures(1, &mDepthTex);
}
CreateMultiviewBackingTextures(GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE, 2, 2, 2,
CreateMultiviewBackingTextures(GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE, 0, 2, 2, 2,
mColorTex, mDepthTex, mDepthStencilTex);
glGenFramebuffers(1, &mMultiviewFBO);
......@@ -232,7 +232,7 @@ class FramebufferMultiviewLayeredClearTest : public FramebufferMultiviewTest
glGenTextures(1, &mDepthTex);
}
CreateMultiviewBackingTextures(GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE, width, height,
CreateMultiviewBackingTextures(GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE, 0, width, height,
numLayers, mColorTex, mDepthTex, mDepthStencilTex);
glGenFramebuffers(1, &mMultiviewFBO);
......@@ -1420,6 +1420,28 @@ TEST_P(FramebufferMultiviewLayeredClearTest, ColorBufferClearAllLayersAttached)
EXPECT_EQ(GLColor::green, getLayerColor(1, GL_COLOR_ATTACHMENT0));
}
// Test that attaching a multisampled texture array is not possible if all the required extensions
// are not enabled.
TEST_P(FramebufferMultiviewTest, NegativeMultisampledFramebufferTest)
{
ANGLE_SKIP_TEST_IF(!requestMultiviewExtension());
ANGLE_SKIP_TEST_IF(!ensureExtensionEnabled("GL_OES_texture_storage_multisample_2d_array"));
// We don't enable ANGLE_multiview_multisample
GLTexture multisampleTexture;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES, multisampleTexture);
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTextureMultiviewLayeredANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
multisampleTexture, 0, 0, 2);
// From the extension spec: "An INVALID_OPERATION error is generated if texture is not zero, and
// does not name an existing texture object of type TEXTURE_2D_ARRAY."
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
ANGLE_INSTANTIATE_TEST(FramebufferMultiviewTest, VertexShaderOpenGL(3, 0), GeomShaderD3D11(3, 0));
ANGLE_INSTANTIATE_TEST(FramebufferMultiviewSideBySideClearTest,
VertexShaderOpenGL(3, 0),
......
......@@ -9,6 +9,7 @@
#include "test_utils/MultiviewTest.h"
#include "platform/WorkaroundsD3D.h"
#include "test_utils/gl_raii.h"
namespace angle
{
......@@ -41,6 +42,7 @@ GLuint CreateSimplePassthroughProgram(int numViews)
}
void CreateMultiviewBackingTextures(GLenum multiviewLayout,
int samples,
int viewWidth,
int height,
int numLayers,
......@@ -52,6 +54,15 @@ void CreateMultiviewBackingTextures(GLenum multiviewLayout,
std::vector<GLubyte> textureData;
textureData.resize(viewWidth * height * numLayers * 4, 0u);
// Multisampling is only supported for layered framebuffers.
ASSERT((samples == 0) || multiviewLayout == GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE);
// We can't upload data to multisample textures, so we clear them using a temporary framebuffer
// instead. The current framebuffer binding is stored so we can restore it once we're done with
// using the temporary framebuffers.
GLint restoreDrawFramebuffer;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &restoreDrawFramebuffer);
// Create color and depth textures.
switch (multiviewLayout)
{
......@@ -83,37 +94,98 @@ void CreateMultiviewBackingTextures(GLenum multiviewLayout,
break;
}
case GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE:
{
GLenum texTarget =
(samples > 0) ? GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES : GL_TEXTURE_2D_ARRAY;
for (auto colorTexture : colorTextures)
{
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, viewWidth, height, numLayers, 0,
GL_RGBA, GL_UNSIGNED_BYTE, textureData.data());
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(texTarget, colorTexture);
if (samples > 0)
{
glTexStorage3DMultisampleOES(texTarget, samples, GL_RGBA8, viewWidth, height,
numLayers, false);
GLFramebuffer tempFbo;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, tempFbo);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
for (int layerIndex = 0; layerIndex < numLayers; ++layerIndex)
{
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
colorTexture, 0, layerIndex);
glClear(GL_COLOR_BUFFER_BIT);
}
}
else
{
glTexImage3D(texTarget, 0, GL_RGBA8, viewWidth, height, numLayers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, textureData.data());
glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
}
if (depthTexture != 0)
{
glBindTexture(GL_TEXTURE_2D_ARRAY, depthTexture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT32F, viewWidth, height,
numLayers, 0, GL_DEPTH_COMPONENT, GL_FLOAT, textureData.data());
glBindTexture(texTarget, depthTexture);
if (samples > 0)
{
glTexStorage3DMultisampleOES(texTarget, samples, GL_DEPTH_COMPONENT32F,
viewWidth, height, numLayers, false);
GLFramebuffer tempFbo;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, tempFbo);
glClearDepthf(0.0f);
for (int layerIndex = 0; layerIndex < numLayers; ++layerIndex)
{
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
depthTexture, 0, layerIndex);
glClear(GL_DEPTH_BUFFER_BIT);
}
}
else
{
glTexImage3D(texTarget, 0, GL_DEPTH_COMPONENT32F, viewWidth, height, numLayers,
0, GL_DEPTH_COMPONENT, GL_FLOAT, textureData.data());
}
}
if (depthStencilTexture != 0)
{
glBindTexture(GL_TEXTURE_2D_ARRAY, depthStencilTexture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH24_STENCIL8, viewWidth, height,
numLayers, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8,
textureData.data());
glBindTexture(texTarget, depthStencilTexture);
if (samples > 0)
{
glTexStorage3DMultisampleOES(texTarget, samples, GL_DEPTH24_STENCIL8, viewWidth,
height, numLayers, false);
GLFramebuffer tempFbo;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, tempFbo);
glClearDepthf(0.0f);
glClearStencil(0);
for (int layerIndex = 0; layerIndex < numLayers; ++layerIndex)
{
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
depthTexture, 0, layerIndex);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
}
else
{
glTexImage3D(texTarget, 0, GL_DEPTH24_STENCIL8, viewWidth, height, numLayers, 0,
GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, textureData.data());
}
}
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
glBindTexture(texTarget, 0);
break;
}
default:
UNREACHABLE();
}
ASSERT_GL_NO_ERROR();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, restoreDrawFramebuffer);
}
void CreateMultiviewBackingTextures(GLenum multiviewLayout,
int samples,
int viewWidth,
int height,
int numLayers,
......@@ -123,8 +195,8 @@ void CreateMultiviewBackingTextures(GLenum multiviewLayout,
{
ASSERT(colorTexture != 0u);
std::vector<GLuint> colorTextures(1, colorTexture);
CreateMultiviewBackingTextures(multiviewLayout, viewWidth, height, numLayers, colorTextures,
depthTexture, depthStencilTexture);
CreateMultiviewBackingTextures(multiviewLayout, samples, viewWidth, height, numLayers,
colorTextures, depthTexture, depthStencilTexture);
}
void AttachMultiviewTextures(GLenum target,
......
......@@ -23,7 +23,10 @@ GLuint CreateSimplePassthroughProgram(int numViews);
// GL_FRAMEBUFFER_MULTIVIEW_SIDE_BY_SIDE_ANGLE, then 2D textures are created. If multiviewLayout is
// GL_FRAMEBUFFER_MULTIVIEW_LAYERED_ANGLE, then 2D texture arrays are created. Texture ids should be
// created beforehand. If depthTexture or stencilTexture is 0, it will not be initialized.
// If samples is 0, then non-multisampled textures are created. Otherwise multisampled textures are
// created with the requested sample count.
void CreateMultiviewBackingTextures(GLenum multiviewLayout,
int samples,
int viewWidth,
int height,
int numLayers,
......@@ -31,6 +34,7 @@ void CreateMultiviewBackingTextures(GLenum multiviewLayout,
GLuint depthTexture,
GLuint depthStencilTexture);
void CreateMultiviewBackingTextures(GLenum multiviewLayout,
int samples,
int viewWidth,
int height,
int numLayers,
......@@ -99,7 +103,7 @@ class MultiviewTestBase : public ANGLETestBase
void MultiviewTestBaseTearDown() { ANGLETestBase::ANGLETestTearDown(); }
// Requests the ANGLE_multiview extension and returns true if the operation succeeds.
bool requestMultiviewExtension()
bool requestMultiviewExtension(bool requireMultiviewMultisample)
{
if (extensionRequestable("GL_ANGLE_multiview"))
{
......@@ -111,9 +115,30 @@ class MultiviewTestBase : public ANGLETestBase
std::cout << "Test skipped due to missing GL_ANGLE_multiview." << std::endl;
return false;
}
if (requireMultiviewMultisample)
{
if (extensionRequestable("GL_OES_texture_storage_multisample_2d_array"))
{
glRequestExtensionANGLE("GL_OES_texture_storage_multisample_2d_array");
}
if (extensionRequestable("GL_ANGLE_multiview_multisample"))
{
glRequestExtensionANGLE("GL_ANGLE_multiview_multisample");
}
if (!extensionEnabled("GL_ANGLE_multiview_multisample"))
{
std::cout << "Test skipped due to missing GL_ANGLE_multiview_multisample."
<< std::endl;
return false;
}
}
return true;
}
bool requestMultiviewExtension() { return requestMultiviewExtension(false); }
PFNGLREQUESTEXTENSIONANGLEPROC glRequestExtensionANGLE = nullptr;
};
......
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