blob: 2ef858d5a7544166f140f9962f9059cfa318fc96 [file] [log] [blame] [edit]
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// MultiviewMultisampledRenderToTextureTest:
// Tests of OVR_multiview_multisampled_render_to_texture extension
#include <tuple>
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
using namespace angle;
namespace
{
constexpr int kWindowSize = 6;
class MultiviewMSRTTTest : public ANGLETest<>
{
protected:
MultiviewMSRTTTest()
{
setWindowWidth(kWindowSize);
setWindowHeight(kWindowSize);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
void testSetUp() override
{
if (getClientMajorVersion() >= 3)
{
glGetIntegerv(GL_MAX_SAMPLES, &mMaxIntegerSamples);
glGetIntegerv(GL_MAX_VIEWS_OVR, &mMaxViewsOVR);
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &mMaxArrayTextureLayers);
}
}
GLint mMaxIntegerSamples = 0;
GLint mMaxViewsOVR = 0;
GLint mMaxArrayTextureLayers = 0;
};
class MultiviewMSRTTES3Test : public MultiviewMSRTTTest
{};
// Test that INVALID_FRAMEBUFFER_OPERATION is generated by glReadPixels() command if the number
// of views in the current read framebuffer is greater than 1
TEST_P(MultiviewMSRTTES3Test, ReadPixelsFromMultiviewFramebufferShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numLayers = 2;
constexpr GLsizei multisampleRenderToTextureSampleSize = 4;
std::vector<GLubyte> textureData;
textureData.resize(kWindowSize * kWindowSize * numLayers * 4, 0u);
GLTexture texture;
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numLayers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, textureData.data());
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0,
multisampleRenderToTextureSampleSize, 0, 2);
ASSERT_GL_NO_ERROR();
std::vector<GLColor> pixels(kWindowSize * kWindowSize * numLayers);
glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
}
// Test that INVALID_FRAMEBUFFER_OPERATION is generated by glCopyTexImage2D() command if the number
// of views in the current read framebuffer is greater than 1
TEST_P(MultiviewMSRTTES3Test, CopyTexImageFromMultiviewFramebufferShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numLayers = 2;
constexpr GLsizei multisampleRenderToTextureSampleSize = 4;
std::vector<GLubyte> textureData;
textureData.resize(kWindowSize * kWindowSize * numLayers * 4, 0u);
GLTexture texture;
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numLayers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, textureData.data());
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0,
multisampleRenderToTextureSampleSize, 0, 2);
ASSERT_GL_NO_ERROR();
GLTexture copyImageTargetTexture;
glBindTexture(GL_TEXTURE_2D, copyImageTargetTexture);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, kWindowSize, kWindowSize, 0);
EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
}
// Test that INVALID_FRAMEBUFFER_OPERATION is generated by glCopyTexSubImage() command if the number
// of views in the current read framebuffer is greater than 1
TEST_P(MultiviewMSRTTES3Test, CopyTexSubImageFromMultiviewFramebufferShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numLayers = 2;
constexpr GLsizei multisampleRenderToTextureSampleSize = 4;
std::vector<GLubyte> textureData;
textureData.resize(kWindowSize * kWindowSize * numLayers * 4, 0u);
GLTexture texture;
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numLayers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, textureData.data());
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0,
multisampleRenderToTextureSampleSize, 0, 2);
ASSERT_GL_NO_ERROR();
GLTexture copySubImageTargetTexture;
glBindTexture(GL_TEXTURE_2D, copySubImageTargetTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kWindowSize, kWindowSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, kWindowSize, kWindowSize);
EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
}
// Test that INVALID_FRAMEBUFFER_OPERATION is generated by glCopyTexSubImage3D() command if the
// number of views in the current read framebuffer is greater than 1
TEST_P(MultiviewMSRTTES3Test, CopyTexSubImage3DFromMultiviewFramebufferShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numLayers = 2;
constexpr GLsizei multisampleRenderToTextureSampleSize = 4;
std::vector<GLubyte> textureData;
textureData.resize(kWindowSize * kWindowSize * numLayers * 4, 0u);
GLTexture texture;
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numLayers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, textureData.data());
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0,
multisampleRenderToTextureSampleSize, 0, 2);
ASSERT_GL_NO_ERROR();
GLTexture copySubImage3DTargetTexture;
glBindTexture(GL_TEXTURE_2D_ARRAY, copySubImage3DTargetTexture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numLayers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glCopyTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, 0, 0, kWindowSize, kWindowSize);
EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
}
// Test that INVALID_VALUE is generated by FramebufferTextureMultisampleMultiviewOVR if numViews is
// less than 1
TEST_P(MultiviewMSRTTES3Test, NumViewsLessThanOneShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numLayers = 2;
constexpr GLsizei multisampleRenderToTextureSampleSize = 4;
std::vector<GLubyte> textureData;
textureData.resize(kWindowSize * kWindowSize * numLayers * 4, 0u);
GLTexture texture;
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numLayers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, textureData.data());
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0,
multisampleRenderToTextureSampleSize, 0, 0);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
}
// Test that INVALID_VALUE is generated by FramebufferTextureMultisampleMultiviewOVR if numViews is
// more than MAX_VIEWS_OVR
TEST_P(MultiviewMSRTTES3Test, NumViewsGreaterThanMaxShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numLayers = 2;
constexpr GLsizei multisampleRenderToTextureSampleSize = 4;
std::vector<GLubyte> textureData;
textureData.resize(kWindowSize * kWindowSize * numLayers * 4, 0u);
GLTexture texture;
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numLayers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, textureData.data());
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0,
multisampleRenderToTextureSampleSize, 0,
mMaxViewsOVR + 1);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
}
// Test that INVALID_VALUE is generated by FramebufferTextureMultisampleMultiviewOVR if
// (baseViewIndex + numViews) exceeds GL_MAX_ARRAY_TEXTURE_LAYERS
TEST_P(MultiviewMSRTTES3Test, BaseViewIndexPlusNumViewsExceedsMaxArrayTextureLayersShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numLayers = 2;
constexpr GLsizei multisampleRenderToTextureSampleSize = 4;
std::vector<GLubyte> textureData;
textureData.resize(kWindowSize * kWindowSize * numLayers * 4, 0u);
GLTexture texture;
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numLayers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, textureData.data());
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0,
multisampleRenderToTextureSampleSize,
mMaxArrayTextureLayers, 1);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
}
// Test that INVALID_VALUE is generated by FramebufferTextureMultisampleMultiviewOVR if samples is
// greater than MAX_SAMPLES
TEST_P(MultiviewMSRTTES3Test, SamplesGreaterThanMaxSamplesShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numLayers = 2;
std::vector<GLubyte> textureData;
textureData.resize(kWindowSize * kWindowSize * numLayers * 4, 0u);
GLTexture texture;
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numLayers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, textureData.data());
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0,
mMaxIntegerSamples + 1, 0, 2);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
}
// Test that INVALID_OPERATION is generated if a rendering command is issued and the number
// of views in the current draw framebuffer is not equal to the number of views declared in
// the currently bound program.
TEST_P(MultiviewMSRTTES3Test, ProgramViewMismatchFramebufferViewShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
// Create a program with 2 views
const std::string vsSource = R"(#version 300 es
#extension GL_OVR_multiview : require
layout(num_views = 2) in;
in vec4 a_position;
void main()
{
gl_Position = a_position;
}
)";
const std::string fsSource = R"(#version 300 es
precision mediump float;
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
)";
ANGLE_GL_PROGRAM(program, vsSource.c_str(), fsSource.c_str());
glUseProgram(program);
// Create a framebuffer with 1 view
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numLayers = 1; // 1 view in framebuffer
constexpr GLsizei multisampleRenderToTextureSampleSize = 4;
std::vector<GLubyte> textureData;
textureData.resize(kWindowSize * kWindowSize * numLayers * 4, 0u);
GLTexture texture;
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numLayers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, textureData.data());
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0,
multisampleRenderToTextureSampleSize, 0, 1);
ASSERT_GL_NO_ERROR();
// Draw with program (2 views) and framebuffer (1 view) mismatch
drawQuad(program, "a_position", 0.5f, 0.5f, true);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// Test that INVALID_OPERATION is generated if the target type of <texture> specified in
// FramebufferTextureMultisampleMultiviewOVR is not TEXTURE_2D_ARRAY
TEST_P(MultiviewMSRTTES3Test, InvalidTextureTargetShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numLayers = 2;
constexpr GLsizei multisampleRenderToTextureSampleSize = 4;
std::vector<GLubyte> textureData;
textureData.resize(kWindowSize * kWindowSize * numLayers * 4, 0u);
GLTexture texture;
glBindTexture(GL_TEXTURE_2D, texture); // Bind to GL_TEXTURE_2D instead of GL_TEXTURE_2D_ARRAY
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kWindowSize, kWindowSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
textureData.data());
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0,
multisampleRenderToTextureSampleSize, 0, 2);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// Test that calling glFramebufferTextureMultisampleMultiviewOVR() on both GL_COLOR_ATTACHMENT0
// and GL_DEPTH_ATTACHMENT works, and that drawing to it works.
TEST_P(MultiviewMSRTTES3Test, ColorAndDepthAttachment)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
const std::string vsSource = R"(#version 300 es
#extension GL_OVR_multiview : require
layout(num_views = 2) in;
in vec4 a_position;
void main()
{
gl_Position = a_position;
}
)";
const std::string fsSource = R"(#version 300 es
#extension GL_OVR_multiview : require
precision mediump float;
out vec4 FragColor;
void main()
{
if (gl_ViewID_OVR == 0u)
{
FragColor = vec4(0.0, 1.0, 0.0, 1.0);
gl_FragDepth = 0.25;
}
else
{
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
gl_FragDepth = 0.75;
}
}
)";
ANGLE_GL_PROGRAM(program, vsSource.c_str(), fsSource.c_str());
glUseProgram(program);
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numViews = 2;
constexpr int baseViewIndex = 0;
constexpr GLsizei multisampleRenderToTextureSampleSize = 4;
// Color attachment
GLTexture colorTexture;
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numViews, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture,
0, multisampleRenderToTextureSampleSize,
baseViewIndex, numViews);
ASSERT_GL_NO_ERROR();
// Depth attachment
GLTexture depthTexture;
glBindTexture(GL_TEXTURE_2D_ARRAY, depthTexture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT32F, kWindowSize, kWindowSize, numViews,
0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture,
0, multisampleRenderToTextureSampleSize,
baseViewIndex, numViews);
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glEnable(GL_DEPTH_TEST);
glClearDepthf(1.0f);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
drawQuad(program, "a_position", 0.5f, 0.5f, true);
ASSERT_GL_NO_ERROR();
// Verification
GLFramebuffer verifyFbo;
glBindFramebuffer(GL_FRAMEBUFFER, verifyFbo);
// Verify view 0
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture, 0,
baseViewIndex + 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize / 2, GLColor::green);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0,
baseViewIndex + 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
float depth0 = 0.0f;
glReadPixels(0, 0, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth0);
EXPECT_NEAR(1.0f, depth0, 1e-6);
glReadPixels(kWindowSize / 2, kWindowSize / 2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth0);
EXPECT_NEAR(0.25, depth0, 1e-6);
// Verify view 1
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture, 0,
baseViewIndex + 1);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize / 2, GLColor::red);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthTexture, 0,
baseViewIndex + 1);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
float depth1 = 0.0f;
glReadPixels(0, 0, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth1);
EXPECT_NEAR(1.0f, depth1, 1e-6);
glReadPixels(kWindowSize / 2, kWindowSize / 2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth1);
EXPECT_NEAR(0.75, depth1, 1e-6);
ASSERT_GL_NO_ERROR();
}
// Test that an INVALID_OPERATION error is generated if samples is greater than the maximum number
// of samples supported for target and its internalformat.
TEST_P(MultiviewMSRTTES3Test, SamplesGreaterThanMaxSamplesForTextureFormatShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
GLint maxSamplesForFormat = 0;
glGetInternalformativ(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_RGBA8, GL_SAMPLES, 1,
&maxSamplesForFormat);
INFO() << "maxSamplesForFormat: " << maxSamplesForFormat;
ASSERT_GL_NO_ERROR();
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numLayers = 2;
std::vector<GLubyte> textureData;
textureData.resize(kWindowSize * kWindowSize * numLayers * 4, 0u);
GLTexture texture;
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numLayers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, textureData.data());
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0,
maxSamplesForFormat + 1, 0, 2);
if (maxSamplesForFormat < mMaxIntegerSamples)
{
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
else
{
// GL_INVALID_VALUE error code is generated if samples > mMaxIntegerSamples
EXPECT_GL_ERROR(GL_INVALID_VALUE);
}
}
// Test that passing samples 0 to glFramebufferTextureMultisampleMultiviewOVR() works and
// produces the same result as glFramebufferTextureMultiviewOVR.
TEST_P(MultiviewMSRTTES3Test, SamplesZeroWorks)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
const std::string vsSource = R"(#version 300 es
#extension GL_OVR_multiview : require
layout(num_views = 2) in;
in vec4 a_position;
void main()
{
gl_Position = a_position;
}
)";
const std::string fsSource = R"(#version 300 es
#extension GL_OVR_multiview : require
precision mediump float;
out vec4 FragColor;
void main()
{
if (gl_ViewID_OVR == 0u)
{
FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
else
{
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
}
)";
ANGLE_GL_PROGRAM(program, vsSource.c_str(), fsSource.c_str());
glUseProgram(program);
constexpr int numViews = 2;
constexpr int baseViewIndex = 0;
// FBO 0: Use glFramebufferTextureMultisampleMultiviewOVR with 0 samples
GLFramebuffer fbo0;
glBindFramebuffer(GL_FRAMEBUFFER, fbo0);
GLTexture colorTexture0;
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture0);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numViews, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture0,
0, 0, baseViewIndex, numViews);
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(program, "a_position", 0.5f, 0.5f, true);
ASSERT_GL_NO_ERROR();
// FBO 1: Use glFramebufferTextureMultiviewOVR
GLFramebuffer fbo1;
glBindFramebuffer(GL_FRAMEBUFFER, fbo1);
GLTexture colorTexture1;
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture1);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numViews, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture1, 0,
baseViewIndex, numViews);
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(program, "a_position", 0.5f, 0.5f, true);
ASSERT_GL_NO_ERROR();
// Verification: Compare colorTexture0 and colorTexture1
GLFramebuffer verifyFbo;
glBindFramebuffer(GL_FRAMEBUFFER, verifyFbo);
std::vector<GLColor> pixels0(kWindowSize * kWindowSize);
std::vector<GLColor> pixels1(kWindowSize * kWindowSize);
for (int i = 0; i < numViews; ++i)
{
// Read from colorTexture0
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture0, 0,
baseViewIndex + i);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glReadPixels(0, 0, kWindowSize, kWindowSize, GL_RGBA, GL_UNSIGNED_BYTE, pixels0.data());
ASSERT_GL_NO_ERROR();
// Read from colorTexture1
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture1, 0,
baseViewIndex + i);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glReadPixels(0, 0, kWindowSize, kWindowSize, GL_RGBA, GL_UNSIGNED_BYTE, pixels1.data());
ASSERT_GL_NO_ERROR();
// Compare
EXPECT_EQ(pixels0, pixels1);
}
ASSERT_GL_NO_ERROR();
}
// Test that after calling glFramebufferTextureMultisampleMultiviewOVR, the resulting value for
// TEXTURE_SAMPLES_EXT is guaranteed to be greater than or equal to samples and no more than the
// next larger sample count supported by the implementation.
TEST_P(MultiviewMSRTTES3Test, FrameBufferTextureSamplesExtValue)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numViews = 2;
constexpr int baseViewIndex = 0;
constexpr GLsizei requestedSamples = 2; // Request 2 samples
GLTexture colorTexture;
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numViews, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture,
0, requestedSamples, baseViewIndex, numViews);
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Query TEXTURE_SAMPLES_EXT from the framebuffer attachment
GLint actualSamples = 0;
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT,
&actualSamples);
ASSERT_GL_NO_ERROR();
// Get supported sample counts for GL_RGBA8
GLint numSampleCounts = 0;
glGetInternalformativ(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, GL_RGBA8, GL_NUM_SAMPLE_COUNTS, 1,
&numSampleCounts);
ASSERT_GL_NO_ERROR();
std::vector<GLint> supportedSampleCounts(numSampleCounts);
glGetInternalformativ(GL_RENDERBUFFER, GL_RGBA8, GL_SAMPLES, numSampleCounts,
supportedSampleCounts.data());
ASSERT_GL_NO_ERROR();
// Sort supported sample counts in ascending order
std::sort(supportedSampleCounts.begin(), supportedSampleCounts.end());
// Find the smallest supported sample count that is >= requestedSamples
GLint expectedMaxSamples = 0;
if (requestedSamples == 0)
{
expectedMaxSamples = 0;
}
else
{
auto it = std::lower_bound(supportedSampleCounts.begin(), supportedSampleCounts.end(),
requestedSamples);
ASSERT(it != supportedSampleCounts.end());
expectedMaxSamples = *it;
}
// Verify the conditions
EXPECT_GE(actualSamples, requestedSamples);
EXPECT_LE(actualSamples, expectedMaxSamples);
}
// Test that glReadPixels() does not return an error if the GL_SAMPLE_BUFFERS of read framebuffer is
// 1, when the read framebuffer attachment is attached with
// glFramebufferTextureMultisampleMultiviewOVR()
TEST_P(MultiviewMSRTTES3Test, ReadPixelsFromSingleViewFramebufferShouldSucceed)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numLayers = 1;
constexpr GLsizei multisampleRenderToTextureSampleSize = 4;
std::vector<GLubyte> textureData;
textureData.resize(kWindowSize * kWindowSize * numLayers * 4, 0u);
GLTexture texture;
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numLayers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, textureData.data());
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0,
multisampleRenderToTextureSampleSize, 0, 1);
ASSERT_GL_NO_ERROR();
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
GLint sampleBuffers = 0;
glGetIntegerv(GL_SAMPLE_BUFFERS, &sampleBuffers);
if (sampleBuffers == 1)
{
std::vector<GLColor> pixels(kWindowSize * kWindowSize * numLayers);
glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
EXPECT_GL_NO_ERROR();
}
}
// Test that framebuffer succeed with draw operations if the attachments are created with
// glFramebufferTextureMultisampleMultiviewOVR() and have the same render-to-texture sample counts
TEST_P(MultiviewMSRTTES3Test, FBOAttachmentsWithSameSampleCountShouldSucceed)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
constexpr GLsizei multisampleRenderToTextureSampleSize4 = 4;
ANGLE_SKIP_TEST_IF(mMaxIntegerSamples <= multisampleRenderToTextureSampleSize4);
constexpr int numViews = 2;
constexpr int baseViewIndex = 0;
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// Attach msrtt-multiview (4 samples) texture to COLOR_ATTACHMENT0
GLTexture colorTexture0;
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture0);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numViews, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture0,
0, multisampleRenderToTextureSampleSize4,
baseViewIndex, numViews);
ASSERT_GL_NO_ERROR();
// Attach msrtt-multiview (4 samples) texture to COLOR_ATTACHMENT1
GLTexture colorTexture1;
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture1);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numViews, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, colorTexture1,
0, multisampleRenderToTextureSampleSize4,
baseViewIndex, numViews);
const std::string vsSource = R"(#version 300 es
#extension GL_OVR_multiview : require
layout(num_views = 2) in;
in vec4 a_position;
void main()
{
gl_Position = a_position;
}
)";
const std::string fsSource = R"(#version 300 es
#extension GL_OVR_multiview : require
precision mediump float;
layout(location = 0)out vec4 FragColor0;
layout(location = 1)out vec4 FragColor1;
void main()
{
if (gl_ViewID_OVR == 0u) {
FragColor0 = vec4(1.0, 0.0, 0.0, 1.0); // Red for view 0 GL_COLOR_ATTACHMENT0
FragColor1 = vec4(1.0, 1.0, 0.0, 1.0); // Yellow for view 0 GL_COLOR_ATTACHMENT1
} else {
FragColor0 = vec4(0.0, 1.0, 0.0, 1.0); // Green for view 1 GL_COLOR_ATTACHMENT0
FragColor1 = vec4(0.0, 0.0, 1.0, 1.0); // Blue for view 1 GL_COLOR_ATTACHMENT1
}
}
)";
ANGLE_GL_PROGRAM(program, vsSource.c_str(), fsSource.c_str());
glUseProgram(program);
GLenum buffers[2] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
glDrawBuffers(2, buffers);
glClearColor(1.0, 1.0, 1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
// Draw a quad
drawQuad(program, "a_position", 0.5f, 0.5f, true);
ASSERT_GL_NO_ERROR();
// Verify draw result
GLFramebuffer verifyFbo;
glBindFramebuffer(GL_FRAMEBUFFER, verifyFbo);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture0, 0,
baseViewIndex);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize / 2, GLColor::red);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture0, 0,
baseViewIndex + 1);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize / 2, GLColor::green);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture1, 0,
baseViewIndex);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize / 2, GLColor::yellow);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture1, 0,
baseViewIndex + 1);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize / 2, GLColor::blue);
// Now attach with a different render-to-texture sample count
// Attach msrtt-multiview (mMaxIntegerSamples samples) texture to COLOR_ATTACHMENT0
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
GLTexture colorTexture3;
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture3);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numViews, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture3,
0, mMaxIntegerSamples, baseViewIndex, numViews);
ASSERT_GL_NO_ERROR();
// Attach msrtt-multiview (mMaxIntegerSamples samples) texture to COLOR_ATTACHMENT1
GLTexture colorTexture4;
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture4);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numViews, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, colorTexture4,
0, mMaxIntegerSamples, baseViewIndex, numViews);
// Draw a quad
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(program, "a_position", 0.5f, 0.5f, true);
ASSERT_GL_NO_ERROR();
// Verify draw result
glBindFramebuffer(GL_FRAMEBUFFER, verifyFbo);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture3, 0,
baseViewIndex);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize / 2, GLColor::red);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture3, 0,
baseViewIndex + 1);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize / 2, GLColor::green);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture4, 0,
baseViewIndex);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize / 2, GLColor::yellow);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture4, 0,
baseViewIndex + 1);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize / 2, GLColor::blue);
}
// Test that framebuffer fails completeness check if the attachments created with
// glFramebufferTextureMultisampleMultiviewOVR() have different render-to-texture sample counts
TEST_P(MultiviewMSRTTES3Test, FBOAttachmentsWithDifferentSampleCountShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
constexpr GLsizei multisampleRenderToTextureSampleSize4 = 4;
ANGLE_SKIP_TEST_IF(mMaxIntegerSamples <= multisampleRenderToTextureSampleSize4);
constexpr int numViews = 2;
constexpr int baseViewIndex = 0;
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// Attach msrtt-multiview (4 samples) texture to COLOR_ATTACHMENT0
GLTexture colorTexture0;
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture0);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numViews, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture0,
0, multisampleRenderToTextureSampleSize4,
baseViewIndex, numViews);
ASSERT_GL_NO_ERROR();
// Attach msrtt-multiview (2 samples) texture to COLOR_ATTACHMENT1
GLTexture colorTexture1;
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture1);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numViews, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, colorTexture1,
0, mMaxIntegerSamples, baseViewIndex, numViews);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
// Test that framebuffer fails completeness check if the attachments created with
// glFramebufferTextureMultisampleMultiviewOVR() have different numViews
TEST_P(MultiviewMSRTTES3Test, FBOAttachmentsWithDifferentNumViewsShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
constexpr int multisampleRenderToTextureNumViews1 = 1;
ANGLE_SKIP_TEST_IF(mMaxViewsOVR <= multisampleRenderToTextureNumViews1);
constexpr GLsizei samples = 2;
constexpr int baseViewIndex = 0;
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// Attach a texture with numViews as 1 to COLOR_ATTACHMENT0
GLTexture colorTexture0;
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture0);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize,
multisampleRenderToTextureNumViews1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture0,
0, samples, baseViewIndex,
multisampleRenderToTextureNumViews1);
ASSERT_GL_NO_ERROR();
// Attach a texture with numViews as max to COLOR_ATTACHMENT1
GLTexture colorTexture1;
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture1);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, mMaxViewsOVR, 0,
GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, colorTexture1,
0, samples, baseViewIndex, mMaxViewsOVR);
EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR,
glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
// Test that if the layout qualifier num_views is declared more than once in the same shader,
// all those declarations must set num_views to the same value; otherwise a compile-time error
// results.
TEST_P(MultiviewMSRTTES3Test, ConflictingNumViewsDeclarationsShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
const std::string vsSource = R"(#version 300 es
#extension GL_OVR_multiview : require
layout(num_views = 2) in;
layout(num_views = 3) in; // Conflicting declaration
in vec4 a_position;
void main()
{
gl_Position = a_position;
}
)";
const std::string fsSource = R"(#version 300 es
precision mediump float;
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
)";
// Expect program compilation/linking to fail due to conflicting num_views
GLuint program = CompileProgram(vsSource.c_str(), fsSource.c_str());
EXPECT_EQ(0u, program);
}
// Test that it is a compile-time error to declare num_views to be less than or equal to zero.
TEST_P(MultiviewMSRTTES3Test, NumViewsLessThanOrEqualToZeroShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
// Test with num_views = 0
const std::string vsSourceZero = R"(#version 300 es
#extension GL_OVR_multiview : require
layout(num_views = 0) in;
in vec4 a_position;
void main()
{
gl_Position = a_position;
}
)";
GLuint vs1 = CompileShader(GL_VERTEX_SHADER, vsSourceZero.c_str());
ASSERT(vs1 == 0);
// Test with num_views = -1
const std::string vsSourceNegative = R"(#version 300 es
#extension GL_OVR_multiview : require
layout(num_views = -1) in;
in vec4 a_position;
void main()
{
gl_Position = a_position;
}
)";
GLuint vs2 = CompileShader(GL_VERTEX_SHADER, vsSourceNegative.c_str());
ASSERT(vs2 == 0);
glDeleteShader(vs1);
glDeleteShader(vs2);
}
// Test that it is a compile-time error to declare num_views to be greater than MAX_VIEWS_OVR.
TEST_P(MultiviewMSRTTES3Test, NumViewsGreaterThanMaxViewsOVRShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
// Use mMaxViewsOVR + 1 for the num_views declaration
std::string vsSource = "#version 300 es\n";
vsSource += " #extension GL_OVR_multiview : require\n";
vsSource += " layout(num_views = " + std::to_string(mMaxViewsOVR + 1) + ") in;\n";
vsSource += " in vec4 a_position;\n";
vsSource += " void main()\n";
vsSource += " {\n";
vsSource += " gl_Position = a_position;\n";
vsSource += " }\n";
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource.c_str());
ASSERT(vs == 0);
glDeleteShader(vs);
}
// Test that when attaching 2D array texture to FBO with
// glFramebufferTextureMultisampleMultiviewOVR(), glClear() will apply clear color to all views.
TEST_P(MultiviewMSRTTES3Test, ClearAppliesToAllViewsInMultiviewFramebuffer)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numViews = 2;
constexpr int baseViewIndex = 0;
constexpr GLsizei multisampleRenderToTextureSampleSize = 4;
GLTexture colorTexture;
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numViews, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture,
0, multisampleRenderToTextureSampleSize,
baseViewIndex, numViews);
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Set a clear color (e.g., red)
GLColor clearColor(255, 0, 0, 255);
glClearColor(clearColor.R / 255.0f, clearColor.G / 255.0f, clearColor.B / 255.0f,
clearColor.A / 255.0f);
glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
// Verify that all views are cleared to the specified color
GLFramebuffer verifyFbo;
glBindFramebuffer(GL_FRAMEBUFFER, verifyFbo);
for (int i = 0; i < numViews; ++i)
{
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture, 0,
baseViewIndex + i);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize / 2, clearColor);
}
ASSERT_GL_NO_ERROR();
}
// Test that INVALID_OPERATION is generated for a draw operation when the draw framebuffer is
// multiview and transform feedback is active.
TEST_P(MultiviewMSRTTES3Test, MultiviewDrawFramebufferWithTransformFeedbackShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
// Create a multiview draw framebuffer
GLFramebuffer drawFbo;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, drawFbo);
constexpr int numViews = 2;
constexpr int baseViewIndex = 0;
constexpr GLsizei multisampleRenderToTextureSampleSize = 4;
GLTexture drawColorTexture;
glBindTexture(GL_TEXTURE_2D_ARRAY, drawColorTexture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numViews, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(
GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, drawColorTexture, 0,
multisampleRenderToTextureSampleSize, baseViewIndex, numViews);
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER);
// Create a program with transform feedback varyings
const std::string vsSource = R"(#version 300 es
#extension GL_OVR_multiview : require
layout(num_views = 2) in;
in vec4 a_position;
out vec4 xfb_position;
void main()
{
gl_Position = a_position;
xfb_position = a_position;
}
)";
const std::string fsSource = R"(#version 300 es
precision mediump float;
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
)";
std::vector<std::string> varyings = {"xfb_position"};
ANGLE_GL_PROGRAM_TRANSFORM_FEEDBACK(program, vsSource.c_str(), fsSource.c_str(), varyings,
GL_INTERLEAVED_ATTRIBS);
glUseProgram(program);
// Create transform feedback buffer
GLBuffer transformFeedbackBuffer;
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, transformFeedbackBuffer);
glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(GLfloat) * 4 * 3, nullptr, GL_STREAM_READ);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, transformFeedbackBuffer);
// Begin transform feedback
glBeginTransformFeedback(GL_TRIANGLES);
ASSERT_GL_NO_ERROR();
// Attempt a draw operation with transform feedback active and multiview framebuffer
drawQuad(program, "a_position", 0.5f, 0.5f, true);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
glEndTransformFeedback();
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
}
// Test that instanced drawing commands work with multiview.
TEST_P(MultiviewMSRTTES3Test, InstancedDrawingWithMultiview)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
const std::string vsSource = R"(#version 300 es
#extension GL_OVR_multiview : require
layout(num_views = 2) in;
in vec4 a_position;
void main()
{
gl_Position = a_position;
if (gl_InstanceID == 1) {
gl_Position.y = gl_Position.y * 0.5 + 0.5;
} else {
gl_Position.y = gl_Position.y * 0.5 - 0.5;
}
}
)";
const std::string fsSource = R"(#version 300 es
#extension GL_OVR_multiview : require
precision mediump float;
out vec4 FragColor;
void main()
{
if (gl_ViewID_OVR == 0u)
{
FragColor = vec4(0.0, 1.0, 0.0, 1.0); // Green for view 0
}
else
{
FragColor = vec4(1.0, 0.0, 0.0, 1.0); // Red for view 1
}
}
)";
ANGLE_GL_PROGRAM(program, vsSource.c_str(), fsSource.c_str());
glUseProgram(program);
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numViews = 2;
constexpr int baseViewIndex = 0;
constexpr GLsizei multisampleRenderToTextureSampleSize = 4;
GLTexture colorTexture;
glBindTexture(GL_TEXTURE_2D_ARRAY, colorTexture);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numViews, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture,
0, multisampleRenderToTextureSampleSize,
baseViewIndex, numViews);
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Draw 2 instance
drawQuadInstanced(program, "a_position", 0.5f, 0.5f, true, 2);
ASSERT_GL_NO_ERROR();
// Verification
GLFramebuffer verifyFbo;
glBindFramebuffer(GL_FRAMEBUFFER, verifyFbo);
// Verify view 0 (should be green)
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture, 0,
baseViewIndex + 0);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// verify first instance
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize / 4, GLColor::green);
// verify second instance
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize * 3 / 4, GLColor::green);
// Verify view 1 (should be red)
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, colorTexture, 0,
baseViewIndex + 1);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// verify first instance
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize / 4, GLColor::red);
// verify second instance
EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize * 3 / 4, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// Test that rendering to a mipmap level of a 2D array texture works.
TEST_P(MultiviewMSRTTES3Test, RenderToMipmapLevel1)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
const std::string vsSource = R"(#version 300 es
#extension GL_OVR_multiview : require
layout(num_views = 2) in;
in vec4 a_position;
void main()
{
gl_Position = a_position;
}
)";
const std::string fsSource = R"(#version 300 es
#extension GL_OVR_multiview : require
precision mediump float;
out vec4 FragColor;
void main()
{
if (gl_ViewID_OVR == 0u) {
FragColor = vec4(0.0, 1.0, 0.0, 1.0); // Green for view 0
} else {
FragColor = vec4(1.0, 1.0, 0.0, 1.0); // Yellow for view 1
}
}
)";
ANGLE_GL_PROGRAM(program, vsSource.c_str(), fsSource.c_str());
glUseProgram(program);
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numViews = 2;
constexpr int baseViewIndex = 0;
constexpr GLsizei samples = 4;
constexpr int mipmapLevels = 2;
GLTexture texture;
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
glTexStorage3D(GL_TEXTURE_2D_ARRAY, mipmapLevels, GL_RGBA8, kWindowSize, kWindowSize, 2);
// Initialize mipmap level 0 with red
std::vector<GLColor> level0Data(kWindowSize * kWindowSize * numViews, GLColor::red);
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, kWindowSize, kWindowSize, numViews, GL_RGBA,
GL_UNSIGNED_BYTE, level0Data.data());
// Initialize mipmap level 1 with blue
constexpr int mipLevel1Size = kWindowSize >> 1;
std::vector<GLColor> level1Data(mipLevel1Size * mipLevel1Size * numViews, GLColor::blue);
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 1, 0, 0, 0, mipLevel1Size, mipLevel1Size, numViews,
GL_RGBA, GL_UNSIGNED_BYTE, level1Data.data());
// Attachm mipLevel 1 of the texture array to the FBO
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 1,
samples, baseViewIndex, numViews);
ASSERT_GL_NO_ERROR();
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
// Draw a quad
glViewport(0, 0, mipLevel1Size, mipLevel1Size);
drawQuad(program, "a_position", 0.5f, 0.5f, true);
ASSERT_GL_NO_ERROR();
// Verification
GLFramebuffer verifyFbo;
glBindFramebuffer(GL_FRAMEBUFFER, verifyFbo);
// Verify mipmap level 0 is unchanged (should be red)
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0, baseViewIndex);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(mipLevel1Size / 2, mipLevel1Size / 2, GLColor::red);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 0, baseViewIndex + 1);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(mipLevel1Size / 2, mipLevel1Size / 2, GLColor::red);
// Verify mipmap level 1 is changed (outer edge is blue, inner quad on layer 0 is green, inner
// quad on layer 1 is yellow)
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 1, baseViewIndex);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(mipLevel1Size / 2, mipLevel1Size / 2, GLColor::green);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture, 1, baseViewIndex + 1);
ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(mipLevel1Size / 2, mipLevel1Size / 2, GLColor::yellow);
ASSERT_GL_NO_ERROR();
}
class MultiviewMSRTTES31Test : public MultiviewMSRTTTest
{};
// Test that it is invalid to attach a 2d array texture to the framebuffer with
// glFramebufferTextureMultisampleMultiviewOVR and then attach a multisampled texture that is
// created with glTexStorage2DMultisample() to the same framebuffer.
TEST_P(MultiviewMSRTTES31Test,
FramebufferTextureMultisampleMultiviewOVRInvalidAttachmentsShouldFail)
{
ANGLE_SKIP_TEST_IF(
!EnsureGLExtensionEnabled("GL_OVR_multiview_multisampled_render_to_texture"));
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
constexpr int numLayers = 2;
constexpr GLsizei multisampleRenderToTextureSampleSize = 4;
// Attach a 2D array texture with glFramebufferTextureMultisampleMultiviewOVR
GLTexture textureArray;
glBindTexture(GL_TEXTURE_2D_ARRAY, textureArray);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kWindowSize, kWindowSize, numLayers, 0, GL_RGBA,
GL_UNSIGNED_BYTE, nullptr);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureArray,
0, multisampleRenderToTextureSampleSize, 0,
numLayers);
ASSERT_GL_NO_ERROR();
// Attempt to attach a multisample texture created with glTexStorage2DMultisample to the same
// Framebuffer should fail
GLTexture textureMultisample;
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textureMultisample);
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, multisampleRenderToTextureSampleSize,
GL_RGBA8, kWindowSize, kWindowSize, GL_TRUE);
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1,
textureMultisample, 0,
multisampleRenderToTextureSampleSize, 0, numLayers);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MultiviewMSRTTES3Test);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MultiviewMSRTTES31Test);
ANGLE_INSTANTIATE_TEST_ES3(MultiviewMSRTTES3Test);
ANGLE_INSTANTIATE_TEST_ES31(MultiviewMSRTTES31Test);
} // namespace