blob: 08b5510c473a807d3746c09310553382a017248a [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2014-2016 The Khronos Group Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/ /*!
* \file
* \brief
*/ /*-------------------------------------------------------------------*/
/**
*/ /*!
* \file es31cTextureStorageMultisampleFunctionalTests.cpp
* \brief Implements conformance tests that verify behavior of
* multisample texture functionality (ES3.1 only).
*/ /*-------------------------------------------------------------------*/
#include "es31cTextureStorageMultisampleFunctionalTests.hpp"
#include "gluContextInfo.hpp"
#include "gluDefs.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuTestLog.hpp"
#include <algorithm>
#include <string.h>
#include <string>
#include <vector>
namespace glcts
{
/** Constructor.
*
* @param context CTS context handle.
**/
MultisampleTextureFunctionalTestsBlittingTest::MultisampleTextureFunctionalTestsBlittingTest(Context& context)
: TestCase(context, "multisampled_fbo_to_singlesampled_fbo_blit",
"Verifies that blitting from a multi-sampled framebuffer object "
"to a single-sampled framebuffer object does not generate an error.")
, dst_fbo_id(0)
, dst_to_color_id(0)
, dst_to_depth_stencil_id(0)
, src_fbo_id(0)
, src_to_color_id(0)
, src_to_depth_stencil_id(0)
{
/* Left blank on purpose */
}
/** Deinitializes ES objects created during test execution */
void MultisampleTextureFunctionalTestsBlittingTest::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (dst_fbo_id != 0)
{
gl.deleteFramebuffers(1, &dst_fbo_id);
dst_fbo_id = 0;
}
if (src_fbo_id != 0)
{
gl.deleteFramebuffers(1, &src_fbo_id);
src_fbo_id = 0;
}
if (dst_to_color_id != 0)
{
gl.deleteTextures(1, &dst_to_color_id);
dst_to_color_id = 0;
}
if (dst_to_depth_stencil_id != 0)
{
gl.deleteTextures(1, &dst_to_depth_stencil_id);
dst_to_depth_stencil_id = 0;
}
if (src_to_color_id != 0)
{
gl.deleteTextures(1, &src_to_color_id);
src_to_color_id = 0;
}
if (src_to_depth_stencil_id != 0)
{
gl.deleteTextures(1, &src_to_depth_stencil_id);
src_to_depth_stencil_id = 0;
}
/* Call base class' deinit() */
TestCase::deinit();
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing.
*/
tcu::TestNode::IterateResult MultisampleTextureFunctionalTestsBlittingTest::iterate()
{
bool are_2d_ms_array_tos_supported =
m_context.getContextInfo().isExtensionSupported("GL_OES_texture_storage_multisample_2d_array");
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Set up texture objects */
gl.genTextures(1, &dst_to_color_id);
gl.genTextures(1, &dst_to_depth_stencil_id);
gl.genTextures(1, &src_to_color_id);
gl.genTextures(1, &src_to_depth_stencil_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up texture objects");
if (are_2d_ms_array_tos_supported)
{
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES, src_to_color_id);
gl.texStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES, 2, /* samples */
GL_RGBA8, 4, /* width */
4, /* height */
4, /* depth */
GL_TRUE); /* fixedsamplelocations */
GLU_EXPECT_NO_ERROR(gl.getError(),
"gltexStorage3DMultisample() call failed for texture object src_to_color_id");
}
else
{
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, src_to_color_id);
gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 2, /* samples */
GL_RGBA8, 4, /* width */
4, /* height */
GL_TRUE); /* fixedsamplelocations */
GLU_EXPECT_NO_ERROR(gl.getError(),
"gltexStorage3DMultisample() call failed for texture object src_to_color_id");
}
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, src_to_depth_stencil_id);
gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 2, /* samples */
GL_DEPTH32F_STENCIL8, 4, /* width */
4, /* height */
GL_TRUE); /* fixedsamplelocations */
GLU_EXPECT_NO_ERROR(gl.getError(),
"glTexStorage2DMultisample() call failed for texture object src_to_depth_stencil_id");
gl.bindTexture(GL_TEXTURE_2D, dst_to_color_id);
gl.texImage2D(GL_TEXTURE_2D, 0, /* level */
GL_RGBA8, 4, /* width */
4, /* height */
0, /* border */
GL_RGBA, GL_UNSIGNED_BYTE, NULL); /* data */
GLU_EXPECT_NO_ERROR(gl.getError(), "glTexImage2D() call failed for texture object dst_to_color_id");
gl.bindTexture(GL_TEXTURE_2D_ARRAY, dst_to_depth_stencil_id);
gl.texImage3D(GL_TEXTURE_2D_ARRAY, 0, /* level */
GL_DEPTH32F_STENCIL8, 4, /* width */
4, /* height */
4, /* depth */
0, /* border */
GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, NULL); /* data */
GLU_EXPECT_NO_ERROR(gl.getError(), "glTexImage3D9) call failed for texture object dst_to_depth_stencil_id");
/* Set up framebuffer objects */
gl.genFramebuffers(1, &dst_fbo_id);
gl.genFramebuffers(1, &src_fbo_id);
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_fbo_id);
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, src_fbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up framebuffer objects");
/* Set up source FBO attachments. */
glw::GLenum fbo_completeness_status = 0;
if (are_2d_ms_array_tos_supported)
{
gl.framebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, src_to_color_id, 0, /* level */
0); /* layer */
}
else
{
gl.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, src_to_color_id,
0); /* level */
}
gl.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE,
src_to_depth_stencil_id, 0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up source framebuffer's attachments");
fbo_completeness_status = gl.checkFramebufferStatus(GL_READ_FRAMEBUFFER);
if (fbo_completeness_status != GL_FRAMEBUFFER_COMPLETE)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Source FBO completeness status is: " << fbo_completeness_status
<< ", expected: GL_FRAMEBUFFER_COMPLETE" << tcu::TestLog::EndMessage;
TCU_FAIL("Source FBO is considered incomplete which is invalid");
}
/* Set up draw FBO attachments */
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_to_color_id, 0); /* level */
gl.framebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, dst_to_depth_stencil_id, 0, /* level */
0); /* layer */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up draw framebuffer's attachments");
fbo_completeness_status = gl.checkFramebufferStatus(GL_DRAW_FRAMEBUFFER);
if (fbo_completeness_status != GL_FRAMEBUFFER_COMPLETE)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Draw FBO completeness status is: " << fbo_completeness_status
<< ", expected: GL_FRAMEBUFFER_COMPLETE" << tcu::TestLog::EndMessage;
TCU_FAIL("Draw FBO is considered incomplete which is invalid");
}
/* Perform the blitting operation */
gl.blitFramebuffer(0, /* srcX0 */
0, /* srcY0 */
4, /* srcX1 */
4, /* srcY1 */
0, /* dstX0 */
0, /* dstY0 */
4, /* dstX1 */
4, /* dstY1 */
GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
GL_NEAREST); /* An INVALID_OPERATION error is generated if ...., and filter is not NEAREST. */
/* Make sure no error was generated */
GLU_EXPECT_NO_ERROR(gl.getError(), "glBlitFramebuffer() call failed.");
/* All done */
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
/** Constructor.
*
* @param context CTS context handle.
**/
MultisampleTextureFunctionalTestsBlittingMultisampledDepthAttachmentTest::
MultisampleTextureFunctionalTestsBlittingMultisampledDepthAttachmentTest(Context& context)
: TestCase(context, "blitting_multisampled_depth_attachment",
"Verifies that blitting a multi-sampled depth attachment to "
"a single-sampled depth attachment gives valid results")
, fbo_dst_id(0)
, fbo_src_id(0)
, fs_depth_preview_id(0)
, fs_id(0)
, po_depth_preview_id(0)
, po_id(0)
, to_dst_preview_id(0)
, to_dst_id(0)
, to_src_id(0)
, vao_id(0)
, vs_id(0)
{
/* Left blank on purpose */
}
/** Deinitializes ES objects created during test execution */
void MultisampleTextureFunctionalTestsBlittingMultisampledDepthAttachmentTest::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (fbo_dst_id != 0)
{
gl.deleteFramebuffers(1, &fbo_dst_id);
fbo_dst_id = 0;
}
if (fbo_src_id != 0)
{
gl.deleteFramebuffers(1, &fbo_src_id);
fbo_src_id = 0;
}
if (to_dst_preview_id != 0)
{
gl.deleteTextures(1, &to_dst_preview_id);
to_dst_preview_id = 0;
}
if (to_dst_id != 0)
{
gl.deleteTextures(1, &to_dst_id);
to_dst_id = 0;
}
if (to_src_id != 0)
{
gl.deleteTextures(1, &to_src_id);
to_src_id = 0;
}
if (fs_id != 0)
{
gl.deleteShader(fs_id);
fs_id = 0;
}
if (fs_depth_preview_id != 0)
{
gl.deleteShader(fs_depth_preview_id);
fs_depth_preview_id = 0;
}
if (po_depth_preview_id != 0)
{
gl.deleteProgram(po_depth_preview_id);
po_depth_preview_id = 0;
}
if (po_id != 0)
{
gl.deleteProgram(po_id);
po_id = 0;
}
if (vao_id != 0)
{
gl.deleteVertexArrays(1, &vao_id);
vao_id = 0;
}
if (vs_id != 0)
{
gl.deleteShader(vs_id);
vs_id = 0;
}
/* Call base class' deinit() */
TestCase::deinit();
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing.
*/
tcu::TestNode::IterateResult MultisampleTextureFunctionalTestsBlittingMultisampledDepthAttachmentTest::iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Generate all objects this test will use */
fs_depth_preview_id = gl.createShader(GL_FRAGMENT_SHADER);
fs_id = gl.createShader(GL_FRAGMENT_SHADER);
vs_id = gl.createShader(GL_VERTEX_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() failed");
po_depth_preview_id = gl.createProgram();
po_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed");
gl.genTextures(1, &to_src_id);
gl.genTextures(1, &to_dst_preview_id);
gl.genTextures(1, &to_dst_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() failed");
gl.genFramebuffers(1, &fbo_src_id);
gl.genFramebuffers(1, &fbo_dst_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() failed");
gl.genVertexArrays(1, &vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays() failed");
/* Bind FBOs to relevant FB targets */
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_dst_id);
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, fbo_src_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() call(s) failed");
/* Configure body of vs_id vertex shader */
const glw::GLchar* vs_body = "#version 310 es\n"
"\n"
"precision highp float;\n"
"\n"
"out vec2 uv;\n"
"\n"
"void main()\n"
"{\n"
" switch(gl_VertexID)\n"
" {\n"
" case 0: gl_Position = vec4(-1, -1, 0, 1); uv = vec2(0, 0); break;\n"
" case 1: gl_Position = vec4(-1, 1, 0, 1); uv = vec2(0, 1); break;\n"
" case 2: gl_Position = vec4( 1, 1, 0, 1); uv = vec2(1, 1); break;\n"
" case 3: gl_Position = vec4( 1, -1, 0, 1); uv = vec2(1, 0); break;\n"
" }\n"
"}\n";
gl.shaderSource(vs_id, 1 /* count */, &vs_body, NULL);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for vertex shader of vs_id id");
/* Configure body of fs_id fragment shader */
const glw::GLchar* fs_body = "#version 310 es\n"
"\n"
"precision highp float;\n"
"\n"
"in vec2 uv;\n"
"\n"
"void main()\n"
"{\n"
" gl_FragDepth = uv.x * uv.y;\n"
"}\n";
gl.shaderSource(fs_id, 1 /* count */, &fs_body, NULL);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for fragment shader of fs_id id");
/* Configure body of fs_depth_preview_id fragment shader */
const glw::GLchar* fs_depth_preview_body = "#version 310 es\n"
"\n"
"precision highp int;\n"
"precision highp float;\n"
"\n"
"in vec2 uv;\n"
"out uint result;\n"
"uniform sampler2D tex;\n"
"\n"
"void main()\n"
"{\n"
" result = floatBitsToUint(textureLod(tex, uv, 0.0).x);\n"
"}\n";
gl.shaderSource(fs_depth_preview_id, 1 /* count */, &fs_depth_preview_body, NULL);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() failed for fragment shader of fs_depth_preview_id id");
/* Compile all the shaders */
const glw::GLuint shader_ids[] = { fs_id, fs_depth_preview_id, vs_id };
const unsigned int n_shader_ids = sizeof(shader_ids) / sizeof(shader_ids[0]);
for (unsigned int n_shader_id = 0; n_shader_id < n_shader_ids; ++n_shader_id)
{
glw::GLint compile_status = GL_FALSE;
gl.compileShader(shader_ids[n_shader_id]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() failed");
gl.getShaderiv(shader_ids[n_shader_id], GL_COMPILE_STATUS, &compile_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() failed");
if (compile_status != GL_TRUE)
{
TCU_FAIL("Shader compilation failure");
}
} /* for (all shader ids) */
/* Configure and link both program objects */
const glw::GLuint program_ids[] = { po_depth_preview_id, po_id };
const unsigned int n_program_ids = sizeof(program_ids) / sizeof(program_ids[0]);
gl.attachShader(po_depth_preview_id, fs_depth_preview_id);
gl.attachShader(po_depth_preview_id, vs_id);
gl.attachShader(po_id, fs_id);
gl.attachShader(po_id, vs_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() failed");
for (unsigned int n_program_id = 0; n_program_id < n_program_ids; ++n_program_id)
{
glw::GLint link_status = GL_FALSE;
gl.linkProgram(program_ids[n_program_id]);
GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() failed");
gl.getProgramiv(program_ids[n_program_id], GL_LINK_STATUS, &link_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() failed");
if (link_status != GL_TRUE)
{
TCU_FAIL("Program linking failure");
}
} /* for (all program ids) */
/* Retrieve maximum amount of samples that can be used for GL_DEPTH_COMPONENT32F internalformat */
glw::GLint n_depth_component32f_max_samples = 0;
gl.getInternalformativ(GL_TEXTURE_2D_MULTISAMPLE, GL_DEPTH_COMPONENT32F, GL_SAMPLES, 1, /* bufSize */
&n_depth_component32f_max_samples);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetInternalformativ() failed");
/* Configure texture storage for to_src_id */
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, to_src_id);
gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, n_depth_component32f_max_samples, GL_DEPTH_COMPONENT32F,
64, /* width */
64, /* height */
GL_FALSE); /* fixedsamplelocations */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not configure texture storage for texture object to_src_id");
gl.texParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
/* When <target> is TEXTURE_2D_MULTISAMPLE or
TEXTURE_2D_MULTISAMPLE_ARRAY_XXX, certain texture parameters may not be
specified. In this case, an INVALID_ENUM error is generated if the
parameter is any sampler state value from table 6.10. */
glw::GLenum error_code = gl.getError();
if (error_code != GL_INVALID_ENUM)
{
m_testCtx.getLog() << tcu::TestLog::Message << "glTexParameteri() call generated an error " << error_code
<< " instead of the expected error code " << GL_INVALID_ENUM << tcu::TestLog::EndMessage;
TCU_FAIL("glTexParameterf() with target GL_TEXTURE_2D_MULTISAMPLE and pname=GL_TEXTURE_MIN_FILTER did not "
"generate GL_INVALID_ENUM.");
}
/* Configure texture storage for to_dst_id */
gl.bindTexture(GL_TEXTURE_2D, to_dst_id);
gl.texStorage2D(GL_TEXTURE_2D, 1, /* levels */
GL_DEPTH_COMPONENT32F, 64, /* width */
64); /* height */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not configure texture storage for texture object to_dst_id");
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set nearest minification filter for texture object to_dst_id");
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set nearest magnification filter for texture object to_dst_id");
/* Configure draw framebuffer */
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, to_src_id,
0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not configure draw framebuffer for depth data rendering");
/* Render the test geometry.
*
* NOTE: Original test spec rendered to fbo_dst_id which would have been invalid,
* since fbo_dst_id is the assumed destination for the blitting operation.
* Actual FBO management code has been rewritten to improve code clarity.
*
* NOTE: Original test spec didn't enable the depth tests, which are necessary
* for the depth buffer to be updated.
**/
gl.viewport(0, 0, 64 /* rt width */, 64 /* rt height */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport() failed");
gl.enable(GL_DEPTH_TEST);
GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_DEPTH_TEST) failed");
gl.depthFunc(GL_ALWAYS);
GLU_EXPECT_NO_ERROR(gl.getError(), "gldepthFunc(GL_ALWAYS) failed");
gl.useProgram(po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() for program object po_id failed");
gl.bindVertexArray(vao_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() failed");
gl.drawArrays(GL_TRIANGLE_FAN, 0 /* first */, 4 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed");
/* Configure FBOs for blitting */
gl.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, to_src_id,
0); /* level */
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, to_dst_id, 0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not configure FBOs for blitting");
/* Blit source FBO to destination FBO */
gl.blitFramebuffer(0, /* srcX0 */
0, /* srcY0 */
64, /* srcX1 */
64, /* srcY1 */
0, /* dstX0 */
0, /* dstY0 */
64, /* dstX1 */
64, /* dstY1 */
GL_DEPTH_BUFFER_BIT, GL_NEAREST);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBlitFramebuffer() failed");
/* Single-sampled depth data is now stored in dst_to_id in GL_DEPTH_COMPONENT32F which may
* not work too well with glReadPixels().
*
* We will now convert it to GL_R32UI using the other program */
gl.useProgram(po_depth_preview_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed for program object po_depth_preview_id");
/* Configure texture storage for the destination texture object */
gl.bindTexture(GL_TEXTURE_2D, to_dst_preview_id);
gl.texStorage2D(GL_TEXTURE_2D, 1, /* levels */
GL_R32UI, 64, /* width */
64); /* height */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up texcture storage for texture object to_dst_preview_id");
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GLU_EXPECT_NO_ERROR(gl.getError(), "glTexParameteri() failed for texture object to_dst_preview_id");
/* Update draw framebuffer configuration */
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, /* texture */
0); /* level */
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, to_dst_preview_id, 0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() failed for depth preview pass");
/* Render a full-screen quad */
gl.bindTexture(GL_TEXTURE_2D, to_dst_id);
gl.drawArrays(GL_TRIANGLE_FAN, 0 /* first */, 4 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed for depth preview pass");
/* to_dst_id now contains the result data.
*
* Before we do a glReadPixels() call, we need to re-configure the read FBO */
gl.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, /* texture */
0); /* level */
gl.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, to_dst_preview_id, 0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() failed for depth data download pass");
/* Read the data */
unsigned int buffer[64 /* width */ * 64 /* height */ * 4 /* components */];
gl.readPixels(0, /* x */
0, /* y */
64, /* width */
64, /* height */
GL_RGBA_INTEGER, GL_UNSIGNED_INT, buffer);
GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() failed for depth data download pass");
/* Verify the rendered data */
const float epsilon = 1.0f / 64.0f + (float)1e-5;
for (int y = 0; y < 64 /* height */; ++y)
{
/* NOTE: data_y is used to take the bottom-top orientation of the data downloaded
* by glReadPixels() into account.
*/
const int data_y = (64 /* height */ - y - 1 /* counting starts from 0 */);
const int pixel_size = 4; /* components */
const int row_width = 64 /* width */ * pixel_size;
float* row_ptr = (float*)buffer + row_width * data_y;
for (int x = 0; x < 64 /* width */; ++x)
{
float* data_ptr = row_ptr + x * pixel_size;
float depth = data_ptr[0];
float expected_value_max = float(x) * float(data_y) / (64.0f * 64.0f) + epsilon;
float expected_value_min = float(x) * float(data_y) / (64.0f * 64.0f) - epsilon;
if (!(depth >= expected_value_min && depth <= expected_value_max))
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid data rendered at (" << x << ", " << y << "): "
<< "Expected value from " << expected_value_min << " to " << expected_value_max
<< ", "
<< "rendered: " << depth << tcu::TestLog::EndMessage;
TCU_FAIL("Invalid data rendered");
}
} /* for (all x argument values) */
} /* for (all y argument values) */
/* All done */
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
/** Constructor.
*
* @param context CTS context handle.
**/
MultisampleTextureFunctionalTestsBlittingMultisampledIntegerAttachmentTest::
MultisampleTextureFunctionalTestsBlittingMultisampledIntegerAttachmentTest(Context& context)
: TestCase(context, "blitting_multisampled_integer_attachment",
"Verifies that blitting a multi-sampled integer attachment "
"to a single-sampled integer attachment gives valid results")
, dst_to_id(0)
, fbo_draw_id(0)
, fbo_read_id(0)
, fs_id(0)
, po_id(0)
, src_to_id(0)
, vs_id(0)
{
/* Left blank on purpose */
}
/** Deinitializes ES objects created during test execution */
void MultisampleTextureFunctionalTestsBlittingMultisampledIntegerAttachmentTest::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (fbo_draw_id != 0)
{
gl.deleteFramebuffers(1, &fbo_draw_id);
fbo_draw_id = 0;
}
if (fbo_read_id != 0)
{
gl.deleteFramebuffers(1, &fbo_read_id);
fbo_read_id = 0;
}
if (dst_to_id != 0)
{
gl.deleteTextures(1, &dst_to_id);
dst_to_id = 0;
}
if (src_to_id != 0)
{
gl.deleteTextures(1, &src_to_id);
src_to_id = 0;
}
if (fs_id != 0)
{
gl.deleteShader(fs_id);
fs_id = 0;
}
if (po_id != 0)
{
gl.deleteProgram(po_id);
po_id = 0;
}
if (vs_id != 0)
{
gl.deleteShader(vs_id);
vs_id = 0;
}
/* Call base class' deinit() */
TestCase::deinit();
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing.
*/
tcu::TestNode::IterateResult MultisampleTextureFunctionalTestsBlittingMultisampledIntegerAttachmentTest::iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Make sure GL_MAX_INTEGER_SAMPLES is at least 2 */
glw::GLint gl_max_integer_samples_value = 0;
gl.getIntegerv(GL_MAX_INTEGER_SAMPLES, &gl_max_integer_samples_value);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not query GL_MAX_INTEGER_SAMPLES pname value");
if (gl_max_integer_samples_value < 2)
{
throw tcu::NotSupportedError("GL_MAX_INTEGER_SAMPLES is lower than 2");
}
/* Retrieve maximum amount of samples for GL_R16UI internalformat */
glw::GLint r16ui_max_samples = 0;
gl.getInternalformativ(GL_TEXTURE_2D_MULTISAMPLE, GL_R16UI, GL_SAMPLES, 1 /* bufSize */, &r16ui_max_samples);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetInternalformativ() call failed.");
/* Set up texture objects */
gl.genTextures(1, &dst_to_id);
gl.genTextures(1, &src_to_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up texture objects");
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, src_to_id);
gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, r16ui_max_samples, GL_R16UI, 16, /* width */
16, /* height */
GL_FALSE); /* fixedsamplelocations */
GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2DMultisample() call failed for texture object src_to_id");
gl.bindTexture(GL_TEXTURE_2D, dst_to_id);
gl.texStorage2D(GL_TEXTURE_2D, 1, /* levels */
GL_R16UI, 16, /* width */
16); /* height */
GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() call failed for texture object dst_to_id");
/* Create a program object */
po_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed");
/* Create two shader objects */
fs_id = gl.createShader(GL_FRAGMENT_SHADER);
vs_id = gl.createShader(GL_VERTEX_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "Either of the glCreateShader() calls failed");
/* Set up framebuffer objects */
gl.genFramebuffers(1, &fbo_draw_id);
gl.genFramebuffers(1, &fbo_read_id);
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_draw_id);
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, fbo_read_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up framebuffer objects");
/* Set up draw FBO's color attachment. */
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, src_to_id,
0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() call failed");
/* Set up shader objects.
*
* NOTE: Original test spec specified an invalid case 3 for the vertex shader.
* NOTE: Original test case used a float output value. This would not be valid,
* as the color buffer's attachment uses GL_R16UI format.
*/
glw::GLint compile_status = GL_FALSE;
static const char* fs_body = "#version 310 es\n"
"\n"
"precision highp float;\n"
"\n"
"uniform uint n_sample;\n"
"uniform uint n_max_samples;\n"
"uniform highp uvec2 x1y1;\n"
"out uint result;\n"
"\n"
"void main()\n"
"{\n"
" uint row_index = x1y1.y;\n"
" uint column_index = x1y1.x;\n"
"\n"
" if (n_sample < n_max_samples / 2u)\n"
" {\n"
" result = row_index * 16u + column_index;\n"
" }\n"
" else\n"
" {\n"
" result = row_index * 16u + column_index + 1u;\n"
" }\n"
"}\n";
static const char* vs_body = "#version 310 es\n"
"\n"
"uniform uvec2 x1y1;\n"
"\n"
"void main()\n"
"{\n"
" switch (gl_VertexID)\n"
" {\n"
" case 0: gl_Position = vec4( (float(x1y1.x) + 0.0 - 8.0)/8.0, (float(x1y1.y) "
"+ 0.0 - 8.0)/8.0, 0.0, 1.0); break;\n"
" case 1: gl_Position = vec4( (float(x1y1.x) + 1.0 - 8.0)/8.0, (float(x1y1.y) "
"+ 0.0 - 8.0)/8.0, 0.0, 1.0); break;\n"
" case 2: gl_Position = vec4( (float(x1y1.x) + 1.0 - 8.0)/8.0, (float(x1y1.y) "
"+ 1.0 - 8.0)/8.0, 0.0, 1.0); break;\n"
" case 3: gl_Position = vec4( (float(x1y1.x) + 0.0 - 8.0)/8.0, (float(x1y1.y) "
"+ 1.0 - 8.0)/8.0, 0.0, 1.0); break;\n"
" }\n"
"}\n";
gl.attachShader(po_id, fs_id);
gl.attachShader(po_id, vs_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call(s) failed.");
gl.shaderSource(fs_id, 1 /* count */, &fs_body, NULL);
gl.shaderSource(vs_id, 1 /* count */, &vs_body, NULL);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call(s) failed.");
gl.compileShader(fs_id);
gl.compileShader(vs_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call(s) failed.");
gl.getShaderiv(fs_id, GL_COMPILE_STATUS, &compile_status);
if (compile_status != GL_TRUE)
{
TCU_FAIL("Could not compile fragment shader");
}
gl.getShaderiv(vs_id, GL_COMPILE_STATUS, &compile_status);
if (compile_status != GL_TRUE)
{
/* Retrieve and dump compilation fail reason */
char infoLog[1024];
glw::GLsizei length = 0;
gl.getShaderInfoLog(vs_id, 1024, &length, infoLog);
m_testCtx.getLog() << tcu::TestLog::Message << "Vertex shader compilation failed, infoLog: " << infoLog
<< tcu::TestLog::EndMessage;
TCU_FAIL("Could not compile vertex shader");
}
/* Link the program object */
glw::GLint link_status = GL_FALSE;
gl.linkProgram(po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() failed");
gl.getProgramiv(po_id, GL_LINK_STATUS, &link_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() failed");
if (link_status != GL_TRUE)
{
TCU_FAIL("Could not link program object");
}
/* Enable GL_SAMPLE_MASK mode */
gl.enable(GL_SAMPLE_MASK);
GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_SAMPLE_MASK) call generated an unexpected error");
/* Prepare for rendering */
glw::GLint n_max_samples_uniform_location = gl.getUniformLocation(po_id, "n_max_samples");
glw::GLint n_sample_uniform_location = gl.getUniformLocation(po_id, "n_sample");
glw::GLint x1y1_uniform_location = gl.getUniformLocation(po_id, "x1y1");
if (n_max_samples_uniform_location == -1)
{
TCU_FAIL("n_max_samples is not considered an active uniform.");
}
if (n_sample_uniform_location == -1)
{
TCU_FAIL("n_sample is not considered an active uniform.");
}
if (x1y1_uniform_location == -1)
{
TCU_FAIL("x1y1 is not considered an active uniform.");
}
gl.viewport(0 /* x */, 0 /* y */, 16 /* width */, 16 /* height */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport() call failed");
gl.useProgram(po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed");
gl.uniform1ui(n_max_samples_uniform_location, r16ui_max_samples);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1ui() call failed");
/* Clear color buffer before drawing to it, not strictly neccesary
* but helps debugging */
glw::GLuint clear_color_src[] = { 11, 22, 33, 44 };
gl.clearBufferuiv(GL_COLOR, 0, clear_color_src);
GLU_EXPECT_NO_ERROR(gl.getError(), "glClearBufferiv() call failed");
/* Render */
for (int x = 0; x < 16 /* width */; ++x)
{
for (int y = 0; y < 16 /* height */; ++y)
{
for (int n_sample = 0; n_sample < r16ui_max_samples; ++n_sample)
{
gl.uniform1ui(n_sample_uniform_location, n_sample);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1ui() call failed");
gl.uniform2ui(x1y1_uniform_location, x, y);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform2f() call failed");
gl.sampleMaski(n_sample / 32, 1 << (n_sample % 32));
GLU_EXPECT_NO_ERROR(gl.getError(), "glSampleMaski() call failed.");
gl.drawArrays(GL_TRIANGLE_FAN, 0 /* first */, 4 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed.");
} /* for (all n_sample values) */
} /* for (all y values) */
} /* for (all x values) */
/* Now, configure the framebuffer binding points for the blitting operation */
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_to_id, 0); /* level */
gl.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, src_to_id,
0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up framebuffer objects for the blitting operation");
/* Clear color buffer before drawing to it, not strictly neccesary
* but helps debugging */
glw::GLuint clear_color_dst[] = { 55, 66, 77, 88 };
gl.clearBufferuiv(GL_COLOR, 0, clear_color_dst);
GLU_EXPECT_NO_ERROR(gl.getError(), "glClearBufferiv() call failed");
/* Blit the data.
*
* NOTE: Original test spec specified GL_LINEAR filter which would not have worked
* because the read buffer contains integer data.
**/
gl.blitFramebuffer(0, /* srcX0 */
0, /* srcY0 */
16, /* srcX1 */
16, /* srcY1 */
0, /* dstX0 */
0, /* dstY0 */
16, /* dstX1 */
16, /* dstY1 */
GL_COLOR_BUFFER_BIT, GL_NEAREST);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBlitFramebuffer() call failed.");
/* Configure the read framebuffer for upcoming glReadPixels() call */
gl.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_to_id, 0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not configure the read framebuffer");
/* Allocate a buffer to hold the result data and then download the result texture's
* contents */
unsigned int* buffer = new unsigned int[16 /* width */ * 16 /* height */ * 4 /* components */];
gl.readPixels(0, /* x */
0, /* y */
16, /* width */
16, /* height */
GL_RGBA_INTEGER, GL_UNSIGNED_INT, buffer);
GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() call failed");
/* Verify the downloaded contents */
int has_test_fail = 0;
int pixel_size = sizeof(unsigned int) * 4 /* components */;
int row_width = pixel_size * 16 /* width */;
for (unsigned int y = 0; y < 16 /* height */; ++y)
{
/* NOTE: Vertical flipping should not be needed, but we cannot confirm this at the moment.
* Should it be the case, please change data_y to 15 - y */
/* TODO: Remove NOTE above when verified on actual ES3.1 implementation */
unsigned int data_y = y;
const unsigned int* row_ptr = (unsigned int*)((char*)buffer + data_y * row_width);
for (unsigned int x = 0; x < 16 /* width */; ++x)
{
const unsigned int* data_ptr = (unsigned int*)((char*)row_ptr + x * pixel_size);
const unsigned int r = data_ptr[0];
const unsigned int g = data_ptr[1];
const unsigned int b = data_ptr[2];
const unsigned int a = data_ptr[3];
bool is_r_valid = (r == (data_y * 16 + x)) || (r == (data_y * 16 + x + 1));
bool is_g_valid = (g == 0);
bool is_b_valid = (b == 0);
bool is_a_valid = (a == 1);
if (!is_r_valid || !is_g_valid || !is_b_valid || !is_a_valid)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid texture data at (" << x << ", " << y << "):"
<< " Expected (" << (data_y * 16 + x) << ", 0, 0, 1)"
<< " or (" << (data_y * 16 + x + 1) << ", 0, 0, 1)"
<< ", found (" << r << ", " << g << ", " << b << ", " << a << ")."
<< tcu::TestLog::EndMessage;
has_test_fail++;
}
} /* for (all x values) */
} /* for (all y values) */
if (has_test_fail)
{
TCU_FAIL("Invalid texture data");
}
/* All done */
if (buffer != NULL)
{
delete[] buffer;
buffer = NULL;
}
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
/** Constructor.
*
* @param context CTS context handle.
**/
MultisampleTextureFunctionalTestsBlittingToMultisampledFBOIsForbiddenTest::
MultisampleTextureFunctionalTestsBlittingToMultisampledFBOIsForbiddenTest(Context& context)
: TestCase(context, "blitting_to_multisampled_fbo_is_forbidden",
"Verifies that blitting to a multisampled framebuffer "
"object results in a GL_INVALID_OPERATION error.")
, dst_to_id(0)
, fbo_draw_id(0)
, fbo_read_id(0)
, src_to_id(0)
{
/* Left blank on purpose */
}
/** Deinitializes ES objects created during test execution */
void MultisampleTextureFunctionalTestsBlittingToMultisampledFBOIsForbiddenTest::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (fbo_draw_id != 0)
{
gl.deleteFramebuffers(1, &fbo_draw_id);
fbo_draw_id = 0;
}
if (fbo_read_id != 0)
{
gl.deleteFramebuffers(1, &fbo_read_id);
fbo_read_id = 0;
}
if (dst_to_id != 0)
{
gl.deleteTextures(1, &dst_to_id);
dst_to_id = 0;
}
if (src_to_id != 0)
{
gl.deleteTextures(1, &src_to_id);
src_to_id = 0;
}
/* Call base class' deinit() */
TestCase::deinit();
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing.
*/
tcu::TestNode::IterateResult MultisampleTextureFunctionalTestsBlittingToMultisampledFBOIsForbiddenTest::iterate()
{
const std::vector<std::string>& exts = m_context.getContextInfo().getExtensions();
const bool has_NV_framebuffer_blit = std::find(exts.begin(), exts.end(), "GL_NV_framebuffer_blit") != exts.end();
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Set up texture objects */
gl.genTextures(1, &dst_to_id);
gl.genTextures(1, &src_to_id);
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, dst_to_id);
gl.bindTexture(GL_TEXTURE_2D, src_to_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up texture objects");
gl.texStorage2D(GL_TEXTURE_2D, 2, /* levels */
GL_RGB10_A2, 64, /* width */
64); /* height */
GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() call failed for GL_TEXTURE_2D texture target");
gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 2, /* samples */
GL_RGB10_A2, 64, /* width */
64, /* height */
GL_FALSE); /* fixedsamplelocations */
GLU_EXPECT_NO_ERROR(gl.getError(),
"gl.texStorage2DMultisample() call failed for GL_TEXTURE_2D_MULTISAMPLE texture target");
/* Set up framebuffer objects */
gl.genFramebuffers(1, &fbo_draw_id);
gl.genFramebuffers(1, &fbo_read_id);
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_draw_id);
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, fbo_read_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up framebuffer objects");
/* Set up FBO attachments.
*
* NOTE: The draw/read FBO configuration in original test spec was the other
* way around which was wrong.
*/
gl.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, src_to_id, 0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up read framebuffer's attachments");
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
dst_to_id, /* texture */
0); /* layer */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up draw framebuffer's attachments");
/* Try to perform blitting operations. */
const glw::GLenum filters[] = { GL_NEAREST, GL_LINEAR };
const unsigned int n_filters = sizeof(filters) / sizeof(filters[0]);
for (unsigned int n_filter = 0; n_filter < n_filters; ++n_filter)
{
glw::GLenum filter = filters[n_filter];
// This blit would be supported by NV_framebuffer_blit if sizes match.
// Alter the size of destination if extension is present to make it invalid.
int dstY1 = has_NV_framebuffer_blit ? 63 : 64;
gl.blitFramebuffer(0, /* srcX0 */
0, /* srcY0 */
64, /* srcX1 */
64, /* srcY1 */
0, /* dstX0 */
0, /* dstY0 */
64, /* dstX1 */
dstY1, /* dstY1 */
GL_COLOR_BUFFER_BIT, filter);
/* Verify GL_INVALID_OPERATION was returned */
glw::GLenum error_code = gl.getError();
if (error_code != GL_INVALID_OPERATION)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid glBlitFramebuffer() call for filter argument ["
<< filter << "]"
"should have generated a GL_INVALID_OPERATION error. Instead, "
"["
<< error_code << "] error was reported." << tcu::TestLog::EndMessage;
TCU_FAIL("GL_INVALID_OPERATION was not returned by invalid glBlitFramebuffer() call.");
}
} /* for (all valid filter argument values) */
/* All done */
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
/** Constructor.
*
* @param context Rendering context handle.
**/
MultisampleTextureFunctionalTestsSampleMaskingForNonIntegerColorRenderableTexturesTest::
MultisampleTextureFunctionalTestsSampleMaskingForNonIntegerColorRenderableTexturesTest(Context& context)
: TestCase(context, "verify_sample_masking_for_non_integer_color_renderable_internalformats",
"Verifies sample masking mechanism for non-integer color-renderable "
"internalformats used for 2D multisample textures")
, bo_id(0)
, fbo_id(0)
, fs_draw_id(0)
, po_draw_id(0)
, po_verify_id(0)
, tfo_id(0)
, to_2d_multisample_id(0)
, vs_draw_id(0)
, vs_verify_id(0)
{
/* Left blank on purpose */
}
/** Deinitializes ES objects created during test execution */
void MultisampleTextureFunctionalTestsSampleMaskingForNonIntegerColorRenderableTexturesTest::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Unset used program */
gl.useProgram(0);
/* Unbind transform feedback object bound to GL_TRANSFORM_FEEDBACK target */
gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
/* Unbind buffer object bound to GL_TRANSFORM_FEEDBACK_BUFFER target */
gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
/* Unbind framebuffer object bound to GL_DRAW_FRAMEBUFFER target */
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
/* Unbind texture object bound to GL_TEXTURE_2D_MULTISAMPLE texture target */
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
/* Delete a 2D multisample texture object of id to_2d_multisample_id */
if (to_2d_multisample_id != 0)
{
gl.deleteTextures(1, &to_2d_multisample_id);
to_2d_multisample_id = 0;
}
/* Delete a buffer object of id bo_id */
if (bo_id != 0)
{
gl.deleteBuffers(1, &bo_id);
bo_id = 0;
}
/* Delete a framebuffer object of id fbo_id */
if (fbo_id != 0)
{
gl.deleteFramebuffers(1, &fbo_id);
fbo_id = 0;
}
/* Delete a transform feedback object of id tfo_id */
if (tfo_id != 0)
{
gl.deleteTransformFeedbacks(1, &tfo_id);
tfo_id = 0;
}
/* Delete fs_draw_id shader */
if (fs_draw_id != 0)
{
gl.deleteShader(fs_draw_id);
fs_draw_id = 0;
}
/* Delete vs_verify_id shader */
if (vs_verify_id != 0)
{
gl.deleteShader(vs_verify_id);
vs_verify_id = 0;
}
/* Delete vs_draw_id shader */
if (vs_draw_id != 0)
{
gl.deleteShader(vs_draw_id);
vs_draw_id = 0;
}
/* Delete program objects po_verify_id */
if (po_verify_id != 0)
{
gl.deleteProgram(po_verify_id);
po_verify_id = 0;
}
/* Delete program objects po_draw_id */
if (po_draw_id != 0)
{
gl.deleteProgram(po_draw_id);
po_draw_id = 0;
}
/* Call base class' deinit() */
TestCase::deinit();
}
/** Initializes test-specific ES objects */
void MultisampleTextureFunctionalTestsSampleMaskingForNonIntegerColorRenderableTexturesTest::initInternals()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Generate a 2D multisample texture object of id to_2d_multisample_id */
gl.genTextures(1, &to_2d_multisample_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() call failed");
if (to_2d_multisample_id == 0)
{
TCU_FAIL("Texture object has not been generated properly");
}
/* Generate a buffer object of id bo_id */
gl.genBuffers(1, &bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed");
if (bo_id == 0)
{
TCU_FAIL("Buffer object has not been generated properly");
}
/* Generate a framebuffer object of id fbo_id */
gl.genFramebuffers(1, &fbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() call failed");
if (fbo_id == 0)
{
TCU_FAIL("Framebuffer object has not been generated properly");
}
/* Generate a transform feedback object of id tfo_id */
gl.genTransformFeedbacks(1, &tfo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTransformFeedbacks() call failed");
if (tfo_id == 0)
{
TCU_FAIL("Transform feedback object has not been generated properly");
}
/* Create a vertex shader vs_draw_id */
vs_draw_id = gl.createShader(GL_VERTEX_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed for vs_draw_id");
/* Create a vertex shader vs_verify_id */
vs_verify_id = gl.createShader(GL_VERTEX_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed for vs_verify_id");
/* Create a fragment shader fs_draw_id */
fs_draw_id = gl.createShader(GL_FRAGMENT_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed for fs_draw_id");
/* Create program objects po_draw_id */
po_draw_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed for po_draw_id");
/* Create program objects po_verify_id */
po_verify_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed for po_verify_id");
}
/** Executes test iteration.
*
* @return Always STOP.
*/
tcu::TestNode::IterateResult MultisampleTextureFunctionalTestsSampleMaskingForNonIntegerColorRenderableTexturesTest::
iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
initInternals();
/* Constants */
const glw::GLfloat epsilon = 1e-5f;
const glw::GLfloat expected_value = 1.0f;
const glw::GLchar* fs_draw_body = "#version 310 es\n"
"\n"
"precision highp float;\n"
"\n"
"out vec4 out_color;\n"
"\n"
"void main()\n"
"{\n"
" out_color = vec4(1, 1, 1, 1);\n"
"}\n";
const glw::GLchar* vs_draw_body = "#version 310 es\n"
"\n"
"precision highp float;\n"
"\n"
"void main()\n"
"{\n"
" switch (gl_VertexID)\n"
" {\n"
" case 0: gl_Position = vec4(-1, 1, 0, 1); break;\n"
" case 1: gl_Position = vec4( 1, 1, 0, 1); break;\n"
" case 2: gl_Position = vec4( 1, -1, 0, 1); break;\n"
" case 3: gl_Position = vec4(-1, -1, 0, 1); break;\n"
" }\n"
"}\n";
const glw::GLchar* vs_verify_body = "#version 310 es\n"
"\n"
"precision highp float;\n"
"\n"
"uniform uint n_bit_on;\n"
"uniform uint n_bits;\n"
"uniform highp sampler2DMS sampler;\n"
"\n"
"out float result;\n"
"\n"
"void main()\n"
"{\n"
" vec4 one = vec4(1);\n"
" vec4 zero = vec4(0.0, 0.0, 0.0, 1.0);\n"
"\n"
" result = 1.0;\n"
"\n"
" for (uint n_current_bit = 0u; n_current_bit < n_bits; n_current_bit++)\n"
" {\n"
" vec4 value = texelFetch(sampler, ivec2(0), int(n_current_bit));\n"
"\n"
" if (n_bit_on == n_current_bit)\n"
" {\n"
" if (any(notEqual(value, one)))\n"
" {\n"
" result = 0.1 + float(n_current_bit)/1000.0;\n"
" break;\n"
" }\n"
" }\n"
" else\n"
" {\n"
" if (any(notEqual(value, zero)))\n"
" {\n"
" result = 0.2 + float(n_current_bit)/1000.0;\n"
" break;\n"
" }\n"
" }\n"
" }\n"
"}\n";
/* Configure the vertex shader vs_draw_id */
compileShader(vs_draw_id, vs_draw_body);
/* Configure the vertex shader vs_verify_id */
compileShader(vs_verify_id, vs_verify_body);
/* Configure the fragment shader fs_draw_id */
compileShader(fs_draw_id, fs_draw_body);
/* Attach the shaders vs_draw_id and fs_draw_id to program object po_draw_id */
gl.attachShader(po_draw_id, vs_draw_id);
gl.attachShader(po_draw_id, fs_draw_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not configure po_draw_id program object");
/* Attach the shaders vs_verify_id and fs_draw_id to program object po_verify_id */
gl.attachShader(po_verify_id, vs_verify_id);
gl.attachShader(po_verify_id, fs_draw_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not configure po_verify_id program object");
/* Configure vs_verify_id for transform feedback - our varying of choice is result and we're happy to use either of the TF modes since we'll be outputting a single float anyway. */
const glw::GLchar* vs_verify_varying_name = "result";
gl.transformFeedbackVaryings(po_verify_id, 1, &vs_verify_varying_name, GL_SEPARATE_ATTRIBS);
/* Link the program objects po_draw_id */
linkProgram(po_draw_id);
/* Link the program objects po_verify_id */
linkProgram(po_verify_id);
/* Retrieve uniform locations */
glw::GLuint n_bits_location = gl.getUniformLocation(po_verify_id, "n_bits");
glw::GLuint n_bit_on_location = gl.getUniformLocation(po_verify_id, "n_bit_on");
/* Bind the to_2d_multisample_id texture object to GL_TEXTURE_2D_MULTISAMPLE texture target */
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, to_2d_multisample_id);
/* Bind the fbo_id framebuffer object to GL_DRAW_FRAMEBUFFER target */
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up a framebuffer object");
/* Bind the bo_id buffer object to GL_TRANSFORM_FEEDBACK_BUFFER generic target */
gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, bo_id);
/* Bind the tfo_id transform feedback object go GL_TRANSFORM_FEEDBACK target */
gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfo_id);
/* Bind the bo_id buffer object to zeroth binding point of GL_TRANSFORM_FEEDBACK_BUFFER */
gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, bo_id);
/* Initialize buffer object's storage to hold a total of 4 bytes (sizeof(float) );*/
gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(glw::GLfloat), NULL, GL_STATIC_DRAW);
/* Attach the 2D multisample texture object to the framebuffer object's zeroth color attachment */
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, to_2d_multisample_id,
0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up zeroth color attachment");
/* Enable GL_SAMPLE_MASK mode */
gl.enable(GL_SAMPLE_MASK);
GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_SAMPLE_MASK) call generated an unexpected error");
/* Color-renderable internalformats to test, note that GL_R8 will not work since sampling from
* such format will never return {1,1,1,1} or {0,0,0,1} which current shader uses for sample validation */
const glw::GLenum internalformat_list[] = { GL_RGBA8, GL_RGB565, GL_SRGB8_ALPHA8 };
const int internalformat_list_count = sizeof(internalformat_list) / sizeof(internalformat_list[0]);
/* Get GL_MAX_SAMPLES value */
glw::GLint gl_max_samples_value = 0;
gl.getIntegerv(GL_MAX_SAMPLES, &gl_max_samples_value);
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to retrieve GL_MAX_SAMPLES value");
if (gl_max_samples_value > 32)
{
m_testCtx.getLog() << tcu::TestLog::Message << "The test case checks only first 32 samples out of "
<< gl_max_samples_value << " reported by GL_MAX_SAMPLES." << tcu::TestLog::EndMessage;
}
/* Work with no more than 32 bits of mask's first word */
const glw::GLint mask_bits_to_check = de::min(gl_max_samples_value, 32);
/* Keep the results but continue running all cases */
bool test_fail = false;
/* Iterate through all internal formats test case should check */
for (int internalformat_index = 0; internalformat_index < internalformat_list_count; internalformat_index++)
{
/* Configure the texture object storage */
gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, mask_bits_to_check, /* samples */
internalformat_list[internalformat_index], 1, /* width */
1, /* height */
GL_TRUE); /* fixedsamplelocations */
/* Make sure no errors were reported. The framebuffer should be considered complete at this moment. */
GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2DMultisample() call failed");
/* Following code does not affect test method. Just checks if FBO is complete
To catch errors earlier */
glw::GLenum fbo_completeness_status = gl.checkFramebufferStatus(GL_DRAW_FRAMEBUFFER);
if (fbo_completeness_status != GL_FRAMEBUFFER_COMPLETE)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "Draw FBO completeness status is: " << fbo_completeness_status
<< ", expected: GL_FRAMEBUFFER_COMPLETE" << tcu::TestLog::EndMessage;
TCU_FAIL("Draw FBO is considered incomplete which is invalid");
}
/* For all values of n_bit from range <0, GL_MAX_SAMPLES pname value) */
for (int n_bit = 0; n_bit < mask_bits_to_check; n_bit++)
{
/* We need to clear render buffer, otherwise masked samples will have undefined values */
glw::GLfloat clear_color_src[] = { 0.0f, 0.0f, 0.0f, 1.0f };
gl.clearBufferfv(GL_COLOR, 0, clear_color_src);
GLU_EXPECT_NO_ERROR(gl.getError(), "clearBufferfv() call failed");
/* Use program object po_draw_id */
gl.useProgram(po_draw_id);
/* Configure sample mask to only render n_bit-th sample using glSampleMaski() function */
gl.sampleMaski(0, 1 << n_bit);
/* Draw a triangle fan of 4 vertices. This fills to_2d_id with multisampled data.
* However, due to active GL_SAMPLE_MASK and the way we configured it, only one sample should have been rendered.
* The only way we can check if this is the case is by using a special shader,
* because we have no way of downloading multisampled data to process space in ES3.0+.
*/
gl.drawArrays(GL_TRIANGLE_FAN, 0, /* first */
4 /* count */);
/* Unbind fbo_id before sourcing from the texture attached to it */
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
/* Enable GL_RASTERIZER_DISCARD mode */
gl.enable(GL_RASTERIZER_DISCARD);
{
/* Use program object po_verify_id */
gl.useProgram(po_verify_id);
/* Specify input arguments for vertex shader */
gl.uniform1ui(n_bits_location, mask_bits_to_check);
gl.uniform1ui(n_bit_on_location, n_bit);
/* Bind to_2d_multisample_id to GL_TEXTURE_2D_MULTISAMPLE texture target. Current texture unit is GL_TEXTURE0 */
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, to_2d_multisample_id);
/* Begin transform feedback (primitiveMode: GL_POINTS) */
gl.beginTransformFeedback(GL_POINTS);
{
/* Draw a single point. This will fill our transform feedback buffer with result */
gl.drawArrays(GL_POINTS, 0, 1);
}
/* End transform feedback */
gl.endTransformFeedback();
}
/* Disable GL_RASTERIZER_DISCARD mode */
gl.disable(GL_RASTERIZER_DISCARD);
/* Make sure no errors were generated */
GLU_EXPECT_NO_ERROR(gl.getError(), "Transform feedback failed");
/* Rebind the fbo_id framebuffer object to GL_DRAW_FRAMEBUFFER target */
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_id);
/* Map buffer object bo_id's contents to user-space */
const glw::GLfloat* mapped_bo =
(glw::GLfloat*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 4, GL_MAP_READ_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() call failed");
/* Copy result from buffer */
glw::GLfloat result = *mapped_bo;
/* Unmap the buffer object */
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed");
/* Verify the value stored by verification program is not 0. If it is 0, the test has failed. */
if (de::abs(result - expected_value) > epsilon)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Unexpected value stored by verification program: ["
<< result << "],"
<< "Format index: " << internalformat_index << ", "
<< "Bit to check: [" << n_bit << "." << tcu::TestLog::EndMessage;
/* Notice test failure */
test_fail = true;
}
}
/* Delete the 2D multisample texture object with glDeleteTextures() call and re-bind the object to a GL_TEXTURE_2D_MULTISAMPLE texture target with a glBindTexture() call */
gl.deleteTextures(1, &to_2d_multisample_id);
to_2d_multisample_id = 0;
/* Recreate to_2d_multisample_id texture object. */
/* Generate a 2D multisample texture object of id to_2d_multisample_id */
gl.genTextures(1, &to_2d_multisample_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() call failed");
if (to_2d_multisample_id == 0)
{
TCU_FAIL("Texture object has not been generated properly");
}
/* Bind the to_2d_id texture object to GL_TEXTURE_2D_MULTISAMPLE texture target */
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, to_2d_multisample_id);
/* Attach the 2D multisample texture object to the framebuffer object's zeroth color attachment */
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE,
to_2d_multisample_id, 0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up zeroth color attachment");
}
if (test_fail)
{
TCU_FAIL("Value stored by verification program is not 1.0. Test has failed.");
}
/* Test case passed */
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
/** Links a program object. Shaders should be attached to program id before call.
* If an error reported throws an exception.
*
* @param id Program id
*/
void MultisampleTextureFunctionalTestsSampleMaskingForNonIntegerColorRenderableTexturesTest::linkProgram(glw::GLuint id)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Link the test program object */
glw::GLint link_status = GL_FALSE;
gl.linkProgram(id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed");
gl.getProgramiv(id, GL_LINK_STATUS, &link_status);
if (link_status != GL_TRUE)
{
TCU_FAIL("Program linking failed");
}
}
/** Compiles the shader. Should the shader not compile, a TestError exception will be thrown.
*
* @param id Generated shader id
* @param source NULL-terminated shader source code string
*/
void MultisampleTextureFunctionalTestsSampleMaskingForNonIntegerColorRenderableTexturesTest::compileShader(
glw::GLuint id, const glw::GLchar* source)
{
glw::GLint compile_status = GL_FALSE;
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.shaderSource(id, 1, &source, NULL);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed");
gl.compileShader(id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed");
gl.getShaderiv(id, GL_COMPILE_STATUS, &compile_status);
if (compile_status != GL_TRUE)
{
/* Retrieve adn dump compliation fail reason */
char infoLog[1024];
glw::GLsizei length = 0;
gl.getShaderInfoLog(id, 1024, &length, infoLog);
m_testCtx.getLog() << tcu::TestLog::Message << "Shader compilation failed, shader id=" << id
<< ", infoLog: " << infoLog << tcu::TestLog::EndMessage;
TCU_FAIL("Shader compilation failed");
}
}
/** Constructor.
*
* @param context Rendering context handle.
**/
MultisampleTextureFunctionalTestsSampleMaskingTexturesTest::MultisampleTextureFunctionalTestsSampleMaskingTexturesTest(
Context& context)
: TestCase(context, "verify_sample_masking_textures",
"Verifies sample masking mechanism for non-integer, integer/unsigned, "
"integer/signed color-renderable internalformats and "
"depth-renderable internalformats. All internalformats "
"are used for 2D multisample textures.")
, bo_id(0)
, fbo_id(0)
, fs_draw_id(0)
, po_draw_id(0)
, po_verify_id(0)
, tfo_id(0)
, to_2d_multisample_id(0)
, vs_draw_id(0)
, vs_verify_id(0)
{
/* Left blank on purpose */
}
/** Deinitializes ES objects created during test execution */
void MultisampleTextureFunctionalTestsSampleMaskingTexturesTest::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Unset used program */
gl.useProgram(0);
/* Unbind transform feedback object bound to GL_TRANSFORM_FEEDBACK target */
gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
/* Unbind buffer object bound to GL_TRANSFORM_FEEDBACK_BUFFER target */
gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
/* Unbind framebuffer object bound to GL_DRAW_FRAMEBUFFER target */
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
/* Unbind texture object bound to GL_TEXTURE_2D_MULTISAMPLE texture target */
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
/* Delete a 2D multisample texture object of id to_2d_multisample_id */
if (to_2d_multisample_id != 0)
{
gl.deleteTextures(1, &to_2d_multisample_id);
to_2d_multisample_id = 0;
}
/* Delete a buffer object of id bo_id */
if (bo_id != 0)
{
gl.deleteBuffers(1, &bo_id);
bo_id = 0;
}
/* Delete a framebuffer object of id fbo_id */
if (fbo_id != 0)
{
gl.deleteFramebuffers(1, &fbo_id);
fbo_id = 0;
}
/* Delete a transform feedback object of id tfo_id */
if (tfo_id != 0)
{
gl.deleteTransformFeedbacks(1, &tfo_id);
tfo_id = 0;
}
/* Delete fs_draw_id shader */
if (fs_draw_id != 0)
{
gl.deleteShader(fs_draw_id);
fs_draw_id = 0;
}
/* Delete vs_verify_id shader */
if (vs_verify_id != 0)
{
gl.deleteShader(vs_verify_id);
vs_verify_id = 0;
}
/* Delete vs_draw_id shader */
if (vs_draw_id != 0)
{
gl.deleteShader(vs_draw_id);
vs_draw_id = 0;
}
/* Delete program objects po_verify_id */
if (po_verify_id != 0)
{
gl.deleteProgram(po_verify_id);
po_verify_id = 0;
}
/* Delete program objects po_draw_id */
if (po_draw_id != 0)
{
gl.deleteProgram(po_draw_id);
po_draw_id = 0;
}
/* Call base class' deinit() */
TestCase::deinit();
}
/** Initializes test-specific ES objects */
void MultisampleTextureFunctionalTestsSampleMaskingTexturesTest::initInternals()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Generate a 2D multisample texture object of id to_2d_multisample_id */
gl.genTextures(1, &to_2d_multisample_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() call failed");
if (to_2d_multisample_id == 0)
{
TCU_FAIL("Texture object has not been generated properly");
}
/* Generate a buffer object of id bo_id */
gl.genBuffers(1, &bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed");
if (bo_id == 0)
{
TCU_FAIL("Buffer object has not been generated properly");
}
/* Generate a framebuffer object of id fbo_id */
gl.genFramebuffers(1, &fbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() call failed");
if (fbo_id == 0)
{
TCU_FAIL("Framebuffer object has not been generated properly");
}
/* Generate a transform feedback object of id tfo_id */
gl.genTransformFeedbacks(1, &tfo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTransformFeedbacks() call failed");
if (tfo_id == 0)
{
TCU_FAIL("Transform feedback object has not been generated properly");
}
/* Create a vertex shader vs_draw_id */
vs_draw_id = gl.createShader(GL_VERTEX_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed for vs_draw_id");
/* Create a vertex shader vs_verify_id */
vs_verify_id = gl.createShader(GL_VERTEX_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed for vs_verify_id");
/* Create a fragment shader fs_draw_id */
fs_draw_id = gl.createShader(GL_FRAGMENT_SHADER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed for fs_draw_id");
/* Create program objects po_draw_id */
po_draw_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed for po_draw_id");
/* Create program objects po_verify_id */
po_verify_id = gl.createProgram();
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed for po_verify_id");
}
/** Executes test iteration.
*
* @return Always STOP.
*/
tcu::TestNode::IterateResult MultisampleTextureFunctionalTestsSampleMaskingTexturesTest::iterate()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
initInternals();
/* Constants */
const glw::GLfloat epsilon = 1e-5f;
const glw::GLchar* fs_draw_body = "#version 310 es\n"
"\n"
"out vec4 out_color;\n"
"\n"
"void main()\n"
"{\n"
" out_color = vec4(1, 1, 1, 1);\n"
"}\n";
const glw::GLchar* vs_draw_body = "#version 310 es\n"
"\n"
"void main()\n"
"{\n"
" switch (gl_VertexID)\n"
" {\n"
" case 0: gl_Position = vec4(-1, 1, 0, 1); break;\n"
" case 1: gl_Position = vec4( 1, 1, 0, 1); break;\n"
" case 2: gl_Position = vec4( 1, -1, 0, 1); break;\n"
" case 3: gl_Position = vec4(-1, -1, 0, 1); break;\n"
" }\n"
"}\n";
const glw::GLchar* vs_verify_body = "#version 310 es\n"
"\n"
"precision highp float;\n"
"\n"
"uniform uint n_bit_on;\n"
"uniform uint n_bits;\n"
"uniform highp sampler2DMS sampler;\n"
"\n"
"out float result;\n"
"\n"
"void main()\n"
"{\n"
" vec4 one = vec4(1);\n"
" vec4 zero = vec4(0);\n"
"\n"
" result = 1.0;\n"
"\n"
" for (uint n_current_bit = 0u; n_current_bit < n_bits; n_current_bit++)\n"
" {\n"
" vec4 value = texelFetch(sampler, ivec2(0), int(n_current_bit));\n"
"\n"
" if (n_bit_on == n_current_bit)\n"
" {\n"
" if (any(notEqual(value, one)))\n"
" {\n"
" result = 0.0;\n"
" }\n"
" }\n"
" else\n"
" {\n"
" if (any(notEqual(value, zero)))\n"
" {\n"
" result = 0.0;\n"
" }\n"
" }\n"
" }\n"
"}\n";
/* Configure the vertex shader vs_draw_id */
compileShader(vs_draw_id, vs_draw_body);
/* Configure the vertex shader vs_verify_id */
compileShader(vs_verify_id, vs_verify_body);
/* Configure the fragment shader fs_draw_id */
compileShader(fs_draw_id, fs_draw_body);
/* Attach the shaders vs_draw_id and fs_draw_id to program object po_draw_id */
gl.attachShader(po_draw_id, vs_draw_id);
gl.attachShader(po_draw_id, fs_draw_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not configure po_draw_id program object");
/* Attach the shaders vs_verify_id and fs_draw_id to program object po_verify_id */
gl.attachShader(po_verify_id, vs_verify_id);
gl.attachShader(po_verify_id, fs_draw_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not configure po_verify_id program object");
/* Configure vs_verify_id for transform feedback - our varying of choice is result and we're happy to use either of the TF modes since we'll be outputting a single float anyway. */
const glw::GLchar* vs_verify_varying_name = "result";
gl.transformFeedbackVaryings(po_verify_id, 1, &vs_verify_varying_name, GL_SEPARATE_ATTRIBS);
/* Link the program objects po_draw_id */
linkProgram(po_draw_id);
/* Link the program objects po_verify_id */
linkProgram(po_verify_id);
/* Retrieve uniform locations */
glw::GLuint n_bits_location = gl.getUniformLocation(po_verify_id, "n_bits");
glw::GLuint n_bit_on_location = gl.getUniformLocation(po_verify_id, "n_bit_on");
/* Bind the to_2d_multisample_id texture object to GL_TEXTURE_2D_MULTISAMPLE texture target */
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, to_2d_multisample_id);
/* Bind the fbo_id framebuffer object to GL_DRAW_FRAMEBUFFER target */
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up a framebuffer object");
/* Bind the bo_id buffer object to GL_TRANSFORM_FEEDBACK_BUFFER generic target */
gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, bo_id);
/* Bind the bo_id buffer object to zeroth binding point of GL_TRANSFORM_FEEDBACK_BUFFER */
gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, bo_id);
/* Bind the tfo_id transform feedback object go GL_TRANSFORM_FEEDBACK target */
gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfo_id);
/* Initialize buffer object's storage to hold a total of 4 bytes (sizeof(float) );*/
gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(glw::GLfloat), NULL, GL_STATIC_DRAW);
/* Attach the 2D multisample texture object to the framebuffer object's zeroth color attachment */
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, to_2d_multisample_id,
0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up zeroth color attachment");
/* Enable GL_SAMPLE_MASK mode */
gl.enable(GL_SAMPLE_MASK);
GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_SAMPLE_MASK) call generated an unexpected error");
/* Iterate through color-normalized-, color-unsigned-integer-, color-signed-integer- and depth-renderable internalformats */
const glw::GLenum normalized_color_internalformats[] = { GL_R8, GL_RGB565, GL_SRGB8_ALPHA8 };
const glw::GLenum unsigned_color_internalformats[] = { GL_RGBA32UI, GL_RG16UI };
const glw::GLenum signed_color_internalformats[] = { GL_RGBA32I, GL_RG16I };
const glw::GLenum depth_internalformats[] = { GL_DEPTH_COMPONENT32F };
const unsigned int n_normalized_color_internalformats =
sizeof(normalized_color_internalformats) / sizeof(normalized_color_internalformats[0]);
const unsigned int n_unsigned_color_internalformats =
sizeof(unsigned_color_internalformats) / sizeof(unsigned_color_internalformats[0]);
const unsigned int n_signed_color_internalformats =
sizeof(signed_color_internalformats) / sizeof(signed_color_internalformats[0]);
const unsigned int n_depth_internalformats = sizeof(depth_internalformats) / sizeof(depth_internalformats[0]);
for (unsigned int n_iteration = 0; n_iteration < 4 /* normalized/unsigned/signed/depth */; ++n_iteration)
{
glw::GLenum attachment = 0;
const glw::GLenum* internalformats = NULL;
unsigned int n_internalformats = 0;
switch (n_iteration)
{
case 0:
{
attachment = GL_COLOR_ATTACHMENT0;
internalformats = normalized_color_internalformats;
n_internalformats = n_normalized_color_internalformats;
break;
}
case 1:
{
attachment = GL_COLOR_ATTACHMENT0;
internalformats = unsigned_color_internalformats;
n_internalformats = n_unsigned_color_internalformats;
break;
}
case 2:
{
attachment = GL_COLOR_ATTACHMENT0;
internalformats = signed_color_internalformats;
n_internalformats = n_signed_color_internalformats;
break;
}
case 3:
{
attachment = GL_DEPTH_ATTACHMENT;
internalformats = depth_internalformats;
n_internalformats = n_depth_internalformats;
break;
}
default:
{
TCU_FAIL("Unrecognized iteration index");
}
} /* switch (n_iteration) */
/* Iterate through internalformats */
for (unsigned int n_internalformat = 0; n_internalformat < n_internalformats; ++n_internalformat)
{
glw::GLenum internalformat = internalformats[n_internalformat];
glw::GLint internalformat_max_samples = 0;
/* Retrieve internalformat-specific GL_MAX_SAMPLES value */
gl.getInternalformativ(GL_TEXTURE_2D_MULTISAMPLE, /* target */
internalformat, GL_SAMPLES, 1, /* bufSize */
&internalformat_max_samples);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetInternalformativ() failed");
if (internalformat_max_samples > 32)
{
m_testCtx.getLog() << tcu::TestLog::Message << "The test case checks only first 32 samples out of "
<< internalformat_max_samples
<< " reported by GL_SAMPLES reported by getInternalformativ() "
<< "for internalformat " << internalformat << "." << tcu::TestLog::EndMessage;
}
/* Work with no more than 32 bits of mask's first word */
const glw::GLint mask_bits_to_check = de::min(internalformat_max_samples, 32);
/* Recreate to_2d_multisample_id texture object. */
if (to_2d_multisample_id == 0)
{
/* Generate a 2D multisample texture object of id to_2d_multisample_id */
gl.genTextures(1, &to_2d_multisample_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() call failed");
if (to_2d_multisample_id == 0)
{
TCU_FAIL("Texture object has not been generated properly");
}
/* Bind the to_2d_id texture object to GL_TEXTURE_2D_MULTISAMPLE texture target */
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, to_2d_multisample_id);
/* Attach the 2D multisample texture object to the framebuffer object's attachment */
gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, GL_TEXTURE_2D_MULTISAMPLE,
to_2d_multisample_id, 0); /* level */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up attachment");
}
/* Configure the texture object storage */
gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, internalformat_max_samples, /* samples */
internalformat, 1, /* width */
1, /* height */
GL_TRUE); /* fixedsamplelocations */
/* Make sure no errors were reported. The framebuffer should be considered complete at this moment. */
GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2DMultisample() call failed");
/* For all values of n_bit from range <0, GL_MAX_SAMPLES pname value) */
for (int n_bit = 0; n_bit < mask_bits_to_check; n_bit++)
{
/* Use program object po_draw_id */
gl.useProgram(po_draw_id);
/* Configure sample mask to only render n_bit-th sample using glSampleMaski() function */
gl.sampleMaski(0, 1 << n_bit);
/* Draw a triangle fan of 4 vertices. This fills to_2d_id with multisampled data.
* However, due to active GL_SAMPLE_MASK and the way we configured it, only one sample should have been rendered.
* The only way we can check if this is the case is by using a special shader,
* because we have no way of downloading multisampled data to process space in ES3.0+.
*/
gl.drawArrays(GL_TRIANGLE_FAN, 0, /* first */
4 /* count */);
/* Enable GL_RASTERIZER_DISCARD mode */
gl.enable(GL_RASTERIZER_DISCARD);
{
/* Use program object po_verify_id */
gl.useProgram(po_verify_id);
/* Specify input arguments for vertex shader */
gl.uniform1i(n_bits_location, mask_bits_to_check);
gl.uniform1i(n_bit_on_location, n_bit);
/* Bind to_2d_multisample_id to GL_TEXTURE_2D_MULTISAMPLE texture target. Current texture unit is GL_TEXTURE0 */
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, to_2d_multisample_id);
/* Begin transform feedback (primitiveMode: GL_POINTS) */
gl.beginTransformFeedback(GL_POINTS);
{
/* Draw a single point. This will fill our transform feedback buffer with result */
gl.drawArrays(GL_POINTS, 0, 1);
}
/* End transform feedback */
gl.endTransformFeedback();
}
/* Disable GL_RASTERIZER_DISCARD mode */
gl.disable(GL_RASTERIZER_DISCARD);
/* Make sure no errors were generated */
GLU_EXPECT_NO_ERROR(gl.getError(), "Transform feedback failed");
/* Map buffer object bo_id's contents to user-space */
const glw::GLfloat* mapped_bo = (glw::GLfloat*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY);
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer() call failed");
/* Copy result from buffer */
glw::GLfloat result = *mapped_bo;
/* Unmap the buffer object */
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed");
/* Verify the value stored by verification program is not 0. If it is 0, the test has failed. */
if (de::abs(result) < epsilon)
{
TCU_FAIL("Value stored by verification program is zero. Test has failed.");
}
}
/* Delete the 2D multisample texture object with glDeleteTextures() call and re-bind the object to a GL_TEXTURE_2D_MULTISAMPLE texture target with a glBindTexture() call */
gl.deleteTextures(1, &to_2d_multisample_id);
to_2d_multisample_id = 0;
} /* for (all renderable internalformats) */
} /* for (all iterations) */
/* Test case passed */
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
/** Links a program object. Shaders should be attached to program id before call.
* If an error reported throws an exception.
*
* @param id Program id
*/
void MultisampleTextureFunctionalTestsSampleMaskingTexturesTest::linkProgram(glw::GLuint id)
{
/* Link the test program object */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
glw::GLint link_status = GL_FALSE;
gl.linkProgram(id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed");
gl.getProgramiv(id, GL_LINK_STATUS, &link_status);
if (link_status != GL_TRUE)
{
TCU_FAIL("Program linking failed");
}
}
/** Compiles the shader. Should the shader not compile, a TestError exception will be thrown.
*
* @param id Generated shader id
* @param source NULL-terminated shader source code string
*/
void MultisampleTextureFunctionalTestsSampleMaskingTexturesTest::compileShader(glw::GLuint id,
const glw::GLchar* source)
{
glw::GLint compile_status = GL_FALSE;
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.shaderSource(id, 1, &source, NULL);
GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed");
gl.compileShader(id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed");
gl.getShaderiv(id, GL_COMPILE_STATUS, &compile_status);
if (compile_status != GL_TRUE)
{
TCU_FAIL("Shader compilation failed");
}
}
/** Constructor.
*
* @param context CTS context handle.
**/
MultisampleTextureFunctionalTestsTextureSizeFragmentShadersTest::
MultisampleTextureFunctionalTestsTextureSizeFragmentShadersTest(Context& context)
: TestCase(context, "texture_size_in_fragment_shaders",
"Verifies textureSize() works for multisample textures when used in fragment shaders")
, fbo_id(0)
, fs_id(0)
, po_id(0)
, to_2d_multisample_id(0)
, to_2d_multisample_array_id(0)
, vs_id(0)
{
/* Left blank on purpose */
}
/** Deinitializes ES objects created during test execution */
void MultisampleTextureFunctionalTestsTextureSizeFragmentShadersTest::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (fbo_id != 0)
{
gl.deleteFramebuffers(1, &fbo_id);
fbo_id = 0;
}
if (fs_id != 0)
{
gl.deleteShader(fs_id);
fs_id = 0;
}
if (po_id != 0)
{
gl.deleteProgram(po_id);
po_id = 0;
}
if (to_2d_multisample_id != 0)
{
gl.deleteTextures(1, &to_2d_multisample_id);
to_2d_multisample_id = 0;
}
if (to_2d_multisample_array_id != 0)
{
gl.deleteTextures(1, &to_2d_multisample_array_id);
to_2d_multisample_array_id = 0;
}
if (vs_id != 0)
{
gl.deleteShader(vs_id);
vs_id = 0;
}
/* Call base class' deinit() */
TestCase::deinit();
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing.
*/
tcu::TestNode::IterateResult MultisampleTextureFunctionalTestsTextureSizeFragmentShadersTest::iterate()
{
bool are_2d_array_ms_tos_supported =
m_context.getContextInfo().isExtensionSupported("GL_OES_texture_storage_multisample_2d_array");
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Set up texture objects */
if (are_2d_array_ms_tos_supported)
{
gl.genTextures(1, &to_2d_multisample_array_id);
}
gl.genTextures(1, &to_2d_multisample_id);
if (are_2d_array_ms_tos_supported)
{
gl.activeTexture(GL_TEXTURE0);
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES, to_2d_multisample_array_id);
gl.texStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES, 2, /* samples */
GL_RGBA8, 16, /* width */
32, /* height */
8, /* depth */
GL_TRUE); /* fixedsamplelocations */
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up 2D multisample array texture storage");
}
gl.activeTexture(GL_TEXTURE1);
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, to_2d_multisample_id);
gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 2, /* samples */
GL_RGBA8, 16, /* width */
32, /* height */
GL_TRUE);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up 2D multisample texture storage");
/* Set up a fragment shader */
static const char* fs_body =
"#version 310 es\n"
"\n"
"#ifdef GL_OES_texture_storage_multisample_2d_array\n"
" #extension GL_OES_texture_storage_multisample_2d_array : enable\n"
"#endif\n"
"\n"
"precision highp float;\n"
"\n"
"uniform highp sampler2DMS sampler_2d;\n"
"\n"
"#ifdef GL_OES_texture_storage_multisample_2d_array\n"
" uniform highp sampler2DMSArray sampler_2d_array;\n"
"#endif"
"\n"
"out vec4 result;\n"
"\n"
"void main()\n"
"{\n"
" #ifdef GL_OES_texture_storage_multisample_2d_array\n"
" ivec3 sampler_2d_array_size = textureSize(sampler_2d_array);\n"
" #else\n"
" ivec3 sampler_2d_array_size = ivec3(16, 32, 8);\n"
" #endif\n"
"\n"
" ivec2 sampler_2d_size = textureSize(sampler_2d);\n"
"\n"
" if (sampler_2d_size.x == 16 && sampler_2d_size.y == 32 &&\n"
" sampler_2d_array_size.x == 16 && sampler_2d_array_size.y == 32 && sampler_2d_array_size.z == 8)\n"
" {\n"
" result = vec4(0, 1, 0, 0);\n"
" }\n"
" else\n"
" {\n"
" result = vec4(1, 0, 0, 0);\n"
" }\n"
"}\n";
glw::GLint compile_status = GL_FALSE;
fs_id = gl.createShader(GL_FRAGMENT_SHADER);
gl.shaderSource(fs_id, 1 /* count */, &fs_body, NULL);
gl.compileShader(fs_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up a fragment shader");
gl.getShaderiv(fs_id, GL_COMPILE_STATUS, &compile_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not query fragment shader's compile status");
if (compile_status == GL_FALSE)
{
TCU_FAIL("Could not compile fragment shader");
}
/* Set up a vertex shader for 2D multisample texture case */
static const char* vs_body = "#version 310 es\n"
"\n"
"precision highp float;\n"
"\n"
"void main()\n"
"{\n"
" switch (gl_VertexID)\n"
" {\n"
" case 0: gl_Position = vec4(-1, -1, 0, 1); break;\n"
" case 1: gl_Position = vec4(-1, 1, 0, 1); break;\n"
" case 2: gl_Position = vec4( 1, 1, 0, 1); break;\n"
" case 3: gl_Position = vec4( 1, -1, 0, 1); break;\n"
" }\n"
"}\n";
vs_id = gl.createShader(GL_VERTEX_SHADER);
gl.shaderSource(vs_id, 1 /* count */, &vs_body, NULL);
gl.compileShader(vs_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up a vertex shader");
gl.getShaderiv(vs_id, GL_COMPILE_STATUS, &compile_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not query vertex shader's compile status");
if (compile_status == GL_FALSE)
{
TCU_FAIL("Could not compile vertex shader");
}
/* Set up a program object */
po_id = gl.createProgram();
gl.attachShader(po_id, fs_id);
gl.attachShader(po_id, vs_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up a program object");
/* Link the program object */
glw::GLint link_status = GL_FALSE;
gl.linkProgram(po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed.");
gl.getProgramiv(po_id, GL_LINK_STATUS, &link_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed.");
if (link_status != GL_TRUE)
{
TCU_FAIL("Program object linking failed");
}
/* Set up uniforms */
glw::GLint sampler_2d_array_location = gl.getUniformLocation(po_id, "sampler_2d_array");
glw::GLint sampler_2d_location = gl.getUniformLocation(po_id, "sampler_2d");
if ((sampler_2d_array_location == -1 && are_2d_array_ms_tos_supported) || sampler_2d_location == -1)
{
TCU_FAIL("At least one of the required uniforms is not considered active");
}
gl.useProgram(po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
gl.uniform1i(sampler_2d_array_location, 0);
gl.uniform1i(sampler_2d_location, 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1i() call(s) failed.");
/* Render a full-screen quad */
gl.drawArrays(GL_TRIANGLE_FAN, 0 /* first */, 4 /* count */);
GLU_EXPECT_NO_ERROR(gl.getError(), "Rendering failed");
/* Verify the results */
const tcu::RenderTarget& rt = m_context.getRenderContext().getRenderTarget();
unsigned char* buffer = NULL;
unsigned char* data_ptr = NULL;
int rt_bits_alpha = 0;
int rt_bits_blue = 0;
int rt_bits_green = 0;
int rt_bits_red = 0;
int rt_height = rt.getHeight();
int rt_width = rt.getWidth();
const int row_width = 4 /* RGBA */ * rt_width;
gl.getIntegerv(GL_ALPHA_BITS, &rt_bits_alpha);
gl.getIntegerv(GL_BLUE_BITS, &rt_bits_blue);
gl.getIntegerv(GL_GREEN_BITS, &rt_bits_green);
gl.getIntegerv(GL_RED_BITS, &rt_bits_red);
buffer = new unsigned char[rt_height * rt_width * 4];
gl.readPixels(0, 0, rt_width, rt_height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() call failed");
data_ptr = buffer + row_width * (rt_height / 2) + rt_width / 2 * 4;
if (((data_ptr[0] != 0) && (rt_bits_red != 0)) || ((data_ptr[1] != 255) && (rt_bits_green != 0)) ||
((data_ptr[2] != 0) && (rt_bits_blue != 0)) || ((data_ptr[3] != 0) && (rt_bits_alpha != 0)))
{
m_testCtx.getLog() << tcu::TestLog::Message << "Invalid data rendered: expected:(0, 255, 0, 0) rendered:"
<< "(" << data_ptr[0] << ", " << data_ptr[1] << ", " << data_ptr[2] << ", " << data_ptr[3]
<< ")" << tcu::TestLog::EndMessage;
delete[] buffer;
buffer = NULL;
TCU_FAIL("Invalid data rendered");
}
if (buffer != NULL)
{
delete[] buffer;
buffer = NULL;
}
/* All done */
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
/** Constructor.
*
* @param context CTS context handle.
**/
MultisampleTextureFunctionalTestsTextureSizeVertexShadersTest::
MultisampleTextureFunctionalTestsTextureSizeVertexShadersTest(Context& context)
: TestCase(context, "texture_size_in_vertex_shaders",
"Verifies textureSize() works for multisample textures when used in vertex shaders")
, bo_id(0)
, fbo_id(0)
, fs_id(0)
, po_id(0)
, tfo_id(0)
, to_2d_multisample_id(0)
, to_2d_multisample_array_id(0)
, vs_2d_array_id(0)
, vs_2d_id(0)
{
/* Left blank on purpose */
}
/** Deinitializes ES objects created during test execution */
void MultisampleTextureFunctionalTestsTextureSizeVertexShadersTest::deinit()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (bo_id != 0)
{
gl.deleteBuffers(1, &bo_id);
bo_id = 0;
}
if (fbo_id != 0)
{
gl.deleteFramebuffers(1, &fbo_id);
fbo_id = 0;
}
if (fs_id != 0)
{
gl.deleteShader(fs_id);
fs_id = 0;
}
if (po_id != 0)
{
gl.deleteProgram(po_id);
po_id = 0;
}
if (tfo_id != 0)
{
gl.deleteTransformFeedbacks(1, &tfo_id);
tfo_id = 0;
}
if (to_2d_multisample_id != 0)
{
gl.deleteTextures(1, &to_2d_multisample_id);
to_2d_multisample_id = 0;
}
if (to_2d_multisample_array_id != 0)
{
gl.deleteTextures(1, &to_2d_multisample_array_id);
to_2d_multisample_array_id = 0;
}
if (vs_2d_id != 0)
{
gl.deleteShader(vs_2d_id);
vs_2d_id = 0;
}
if (vs_2d_array_id != 0)
{
gl.deleteShader(vs_2d_array_id);
vs_2d_array_id = 0;
}
/* Call base class' deinit() */
TestCase::deinit();
}
/** Executes test iteration.
*
* @return Returns STOP when test has finished executing.
*/
tcu::TestNode::IterateResult MultisampleTextureFunctionalTestsTextureSizeVertexShadersTest::iterate()
{
bool are_multisample_2d_array_tos_supported =
m_context.getContextInfo().isExtensionSupported("GL_OES_texture_storage_multisample_2d_array");
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Set up a TFO */
gl.genTransformFeedbacks(1, &tfo_id);
gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, tfo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up a transform feedback object");
/* Set up a buffer object */
gl.genBuffers(1, &bo_id);
gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, bo_id);
gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(float), NULL, GL_STATIC_READ);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up a buffer object");
/* Set up texture objects */
if (are_multisample_2d_array_tos_supported)
{
gl.genTextures(1, &to_2d_multisample_array_id);
}
gl.genTextures(1, &to_2d_multisample_id);
/* NOTE: Since we're binding the textures to zero texture unit,
* we don't need to do glUniform1i() calls to configure
* the texture samplers in the vertex shaders later on.
*/
if (are_multisample_2d_array_tos_supported)
{
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES, to_2d_multisample_array_id);
}
gl.bindTexture(GL_TEXTURE_2D_MULTISAMPLE, to_2d_multisample_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up texture objects");
gl.texStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 2, /* samples */
GL_RGBA8, 16, /* width */
32, /* height */
GL_TRUE);
GLU_EXPECT_NO_ERROR(gl.getError(),
"glTexStorage2DMultisample() call failed for GL_TEXTURE_2D_MULTISAMPLE texture target");
if (are_multisample_2d_array_tos_supported)
{
gl.texStorage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES, 2, /* samples */
GL_RGBA8, 16, /* width */
32, /* height */
8, /* depth */
GL_TRUE); /* fixedsamplelocations */
GLU_EXPECT_NO_ERROR(
gl.getError(),
"gltexStorage3DMultisample() call failed for GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES texture target");
}
/* Set up a fragment shader */
glw::GLint compile_status = GL_FALSE;
static const char* fs_body = "#version 310 es\n"
"\n"
"void main()\n"
"{\n"
"}\n";
fs_id = gl.createShader(GL_FRAGMENT_SHADER);
gl.shaderSource(fs_id, 1 /* count */, &fs_body, NULL);
gl.compileShader(fs_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up a fragment shader");
gl.getShaderiv(fs_id, GL_COMPILE_STATUS, &compile_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not query fragment shader's compile status");
if (compile_status == GL_FALSE)
{
TCU_FAIL("Could not compile fragment shader");
}
/* Set up a vertex shader for 2D multisample texture case */
static const char* vs_2d_body = "#version 310 es\n"
"\n"
"precision highp float;\n"
"\n"
"uniform highp sampler2DMS sampler;\n"
"out float is_size_correct;\n"
"\n"
"void main()\n"
"{\n"
" ivec2 size = textureSize(sampler);\n"
"\n"
" if (size.x == 16 && size.y == 32)\n"
" {\n"
" is_size_correct = 1.0f;\n"
" }\n"
" else\n"
" {\n"
" is_size_correct = 0.0f;\n"
" }\n"
"}\n";
vs_2d_id = gl.createShader(GL_VERTEX_SHADER);
gl.shaderSource(vs_2d_id, 1 /* count */, &vs_2d_body, NULL);
gl.compileShader(vs_2d_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up a vertex shader for 2D multisample texture case");
gl.getShaderiv(vs_2d_id, GL_COMPILE_STATUS, &compile_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not query 2D multisample texture vertex shader's compile status");
if (compile_status == GL_FALSE)
{
TCU_FAIL("Could not compile vertex shader for 2D multisample texture");
}
/* Set up a vertex shader for 2D multisample array texture case */
if (are_multisample_2d_array_tos_supported)
{
static const char* vs_2d_array_body = "#version 310 es\n"
"\n"
"#extension GL_OES_texture_storage_multisample_2d_array : enable\n"
"precision highp float;\n"
"\n"
"uniform highp sampler2DMSArray sampler;\n"
"out float is_size_correct;\n"
"\n"
"void main()\n"
"{\n"
" ivec3 size = textureSize(sampler);\n"
"\n"
" if (size.x == 16 && size.y == 32 && size.z == 8)\n"
" {\n"
" is_size_correct = 1.0f;\n"
" }\n"
" else\n"
" {\n"
" is_size_correct = 0.0f;\n"
" }\n"
"}\n";
vs_2d_array_id = gl.createShader(GL_VERTEX_SHADER);
gl.shaderSource(vs_2d_array_id, 1 /* count */, &vs_2d_array_body, NULL);
gl.compileShader(vs_2d_array_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up a vertex shader for 2D multisample array texture case");
gl.getShaderiv(vs_2d_array_id, GL_COMPILE_STATUS, &compile_status);
GLU_EXPECT_NO_ERROR(gl.getError(),
"Could not query 2D multisample array texture vertex shader's compile status");
if (compile_status == GL_FALSE)
{
TCU_FAIL("Could not compile vertex shader for 2D multisample array texture");
}
}
/* Execute two iterations:
*
* a) Create a program object using fs and vs_2d shaders;
* b) Create a program object using fs and vs_2d_array shaders.
*
* Case b) should only be executed if 2D Array MS textures are
* supported.
*/
for (int n_iteration = 0; n_iteration < 2 /* iterations */; ++n_iteration)
{
if (n_iteration == 1 && !are_multisample_2d_array_tos_supported)
{
/* Skip the iteration */
continue;
}
if (po_id != 0)
{
gl.deleteProgram(po_id);
po_id = 0;
}
po_id = gl.createProgram();
/* Attach iteration-specific shaders */
switch (n_iteration)
{
case 0:
{
gl.attachShader(po_id, fs_id);
gl.attachShader(po_id, vs_2d_id);
break;
}
case 1:
{
gl.attachShader(po_id, fs_id);
gl.attachShader(po_id, vs_2d_array_id);
break;
}
default:
TCU_FAIL("Unrecognized iteration index");
}
GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call(s) failed.");
/* Configure the program object for XFB */
const char* varying_name = "is_size_correct";
gl.transformFeedbackVaryings(po_id, 1 /* count */, &varying_name, GL_INTERLEAVED_ATTRIBS);
GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() call failed.");
/* Link the program object */
glw::GLint link_status = GL_FALSE;
gl.linkProgram(po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed.");
gl.getProgramiv(po_id, GL_LINK_STATUS, &link_status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed.");
if (link_status != GL_TRUE)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Linking failed for program object in iteration "
<< n_iteration << tcu::TestLog::EndMessage;
TCU_FAIL("Program object linking failed");
}
/* Render a point using the program */
gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, bo_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase() call failed");
gl.useProgram(po_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
gl.beginTransformFeedback(GL_POINTS);
{
gl.drawArrays(GL_POINTS, 0 /* first */, 1 /* count */);
}
gl.endTransformFeedback();
GLU_EXPECT_NO_ERROR(gl.getError(), "Rendering failed");
/* Read the captured data. Reset the contents of the BO before the buffer
* object is unmapped.
*/
void* data_ptr = NULL;
const float epsilon = (float)1e-5;
float result = 0.0f;
data_ptr = gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* offset */, sizeof(float) /* size */,
GL_MAP_READ_BIT | GL_MAP_WRITE_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() call failed");
result = *((const float*)data_ptr);
memset(data_ptr, 0, sizeof(float));
gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed");
if (de::abs(result - 1.0f) > epsilon)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Retrieved value: " << result << ", expected: 1.0"
<< tcu::TestLog::EndMessage;
TCU_FAIL("Invalid value reported.");
}
}
/* All done */
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
} /* glcts namespace */