blob: df78d0ddaf2e5547b148a3c35c608e5e8d93cd5e [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2017 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 glcMultipleContextsTests.cpp
* \brief
*/ /*-------------------------------------------------------------------*/
#include "glcMultipleContextsTests.hpp"
#include "deSharedPtr.hpp"
#include "gl4cShaderSubroutineTests.hpp"
#include "gluContextInfo.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuMatrix.hpp"
#include <cmath>
#include <cstring>
#include <deMath.h>
using namespace glw;
using namespace gl4cts::ShaderSubroutine;
namespace glcts
{
/**
* * Create multiple contexts and verify that subroutine uniforms values
* are preserved for each program stage when switching rendering context.
*
* OpenGL 4.1 or ARB_separate_shader_objects support required
* * Same as above, but use pipelines instead of monolithic program.
**/
class UniformPreservationTest : public tcu::TestCase
{
public:
/* Public methods */
UniformPreservationTest(tcu::TestContext& testCtx, glu::ApiType apiType);
virtual void deinit();
virtual tcu::TestNode::IterateResult iterate();
private:
/* Private types */
struct subroutineUniformSet
{
bool operator!=(const subroutineUniformSet& arg) const;
void set(glw::GLuint bit_field, const subroutineUniformSet subroutine_indices[2]);
glw::GLuint m_vertex_shader_stage;
glw::GLuint m_tesselation_control_shader_stage;
glw::GLuint m_tesselation_evaluation_shader_stage;
glw::GLuint m_geometry_shader_stage;
glw::GLuint m_fragment_shader_stage;
};
/* Private methods */
void captureCurrentSubroutineSet(subroutineUniformSet& set);
void getShaders(const glw::GLchar*& out_vertex_shader_code, const glw::GLchar*& out_tesselation_control_shader_code,
const glw::GLchar*& out_tesselation_evaluation_shader_code,
const glw::GLchar*& out_geometry_shader_code, const glw::GLchar*& out_fragment_shader_code);
void initSharedContexts();
void prepareProgram(Utils::program** programs, bool is_separable);
void prepareProgramPipeline(glw::GLuint& pipeline_id, Utils::program** programs);
bool testCase(const glw::GLuint bit_field[5]);
bool testProgram(Utils::program** programs, bool is_separable, const glw::GLuint test_cases[][5],
glw::GLuint n_test_cases);
void updateCurrentSubroutineSet(const subroutineUniformSet& set);
/* Private fields */
static const glw::GLuint m_n_shared_contexts;
static const glw::GLuint m_fragment_stage_index;
static const glw::GLuint m_geometry_stage_index;
static const glw::GLuint m_tesselation_control_stage_index;
static const glw::GLuint m_tesselation_evaluation_stage_index;
static const glw::GLuint m_vertex_stage_index;
glu::ApiType m_api_type;
de::SharedPtr<deqp::Context> m_base_context;
glu::RenderContext* m_shared_contexts[4];
glw::GLuint m_program_pipelines[5];
subroutineUniformSet m_subroutine_indices[2];
subroutineUniformSet m_subroutine_uniform_locations;
};
/* Constants used by FunctionalTest20_21 */
const GLuint UniformPreservationTest::m_n_shared_contexts = 4;
const GLuint UniformPreservationTest::m_fragment_stage_index = 0;
const GLuint UniformPreservationTest::m_geometry_stage_index = 1;
const GLuint UniformPreservationTest::m_tesselation_control_stage_index = 2;
const GLuint UniformPreservationTest::m_tesselation_evaluation_stage_index = 3;
const GLuint UniformPreservationTest::m_vertex_stage_index = 4;
/** Set subroutine indices, indices are taken from one of two sets according to provided <bit_field>
*
* @param bit_field Selects source of of index for each stage
* @param subroutine_indices Array of two indices sets
**/
void UniformPreservationTest::subroutineUniformSet::set(GLuint bit_field,
const subroutineUniformSet subroutine_indices[2])
{
GLuint vertex_stage = ((bit_field & (0x01 << 0)) >> 0);
GLuint tesselation_control_stage = ((bit_field & (0x01 << 1)) >> 1);
GLuint tesselation_evaluation_stage = ((bit_field & (0x01 << 2)) >> 2);
GLuint geometry_stage = ((bit_field & (0x01 << 3)) >> 3);
GLuint fragment_stage = ((bit_field & (0x01 << 4)) >> 4);
m_vertex_shader_stage = subroutine_indices[vertex_stage].m_vertex_shader_stage;
m_tesselation_control_shader_stage =
subroutine_indices[tesselation_control_stage].m_tesselation_control_shader_stage;
m_tesselation_evaluation_shader_stage =
subroutine_indices[tesselation_evaluation_stage].m_tesselation_evaluation_shader_stage;
m_geometry_shader_stage = subroutine_indices[geometry_stage].m_geometry_shader_stage;
m_fragment_shader_stage = subroutine_indices[fragment_stage].m_fragment_shader_stage;
}
/** Negated comparison of two sets
*
* @param arg Instance that will be compared to this
*
* @return false when both objects are equal, true otherwise
**/
bool UniformPreservationTest::subroutineUniformSet::operator!=(const subroutineUniformSet& arg) const
{
if ((arg.m_vertex_shader_stage != m_vertex_shader_stage) ||
(arg.m_tesselation_control_shader_stage != m_tesselation_control_shader_stage) ||
(arg.m_tesselation_evaluation_shader_stage != m_tesselation_evaluation_shader_stage) ||
(arg.m_geometry_shader_stage != m_geometry_shader_stage) ||
(arg.m_fragment_shader_stage != m_fragment_shader_stage))
{
return true;
}
return false;
}
/** Constructor.
*
* @param context Rendering context.
*
**/
UniformPreservationTest::UniformPreservationTest(tcu::TestContext& testCtx, glu::ApiType apiType)
: tcu::TestCase(testCtx, "uniform_preservation",
"Verifies that shader uniforms are preserved when rendering context is switched.")
, m_api_type(apiType)
{
for (GLuint i = 0; i < m_n_shared_contexts + 1; ++i)
{
m_program_pipelines[i] = 0;
}
for (GLuint i = 0; i < m_n_shared_contexts; ++i)
{
m_shared_contexts[i] = 0;
}
}
/** Deinitializes all GL objects that may have been created during
* test execution.
**/
void UniformPreservationTest::deinit()
{
/* GL entry points */
const glw::Functions& gl = m_base_context->getRenderContext().getFunctions();
for (GLuint i = 0; i < m_n_shared_contexts + 1; ++i)
{
if (0 != m_program_pipelines[i])
{
gl.deleteProgramPipelines(1, &m_program_pipelines[i]);
m_program_pipelines[i] = 0;
}
}
for (GLuint i = 0; i < m_n_shared_contexts; ++i)
{
if (0 != m_shared_contexts[i])
{
delete m_shared_contexts[i];
m_shared_contexts[i] = 0;
}
}
}
/** Executes test iteration.
*
* @return Returns STOP
*/
tcu::TestNode::IterateResult UniformPreservationTest::iterate()
{
/* Test cases, values stored here are used as bit fields */
static const GLuint test_cases[][m_n_shared_contexts + 1] = {
{ 0, 1, 2, 3, 4 }, { 1, 2, 3, 4, 0 }, { 2, 3, 4, 0, 1 }, { 3, 4, 0, 1, 2 },
{ 4, 0, 1, 2, 3 }, { 27, 28, 29, 30, 31 }, { 28, 29, 30, 31, 27 }, { 29, 30, 31, 27, 28 },
{ 30, 31, 27, 28, 29 }, { 31, 27, 28, 29, 30 },
};
static const GLuint n_test_cases = sizeof(test_cases) / sizeof(test_cases[0]);
glu::ContextType context_type(m_api_type);
m_base_context = de::SharedPtr<deqp::Context>(new deqp::Context(m_testCtx, context_type));
/* Do not execute the test if GL_ARB_shader_subroutine is not supported */
if (!m_base_context->getContextInfo().isExtensionSupported("GL_ARB_shader_subroutine"))
{
throw tcu::NotSupportedError("GL_ARB_shader_subroutine is not supported.");
}
/* Prepare contexts */
initSharedContexts();
/* Test result */
bool result = true;
/* Program pointers */
Utils::program* program_pointers[5];
/* Test monolithic program */
{
/* Prepare program */
Utils::program program(*m_base_context.get());
program_pointers[m_fragment_stage_index] = &program;
prepareProgram(program_pointers, false);
/* Execute test */
if (false == testProgram(program_pointers, false, test_cases, n_test_cases))
{
m_testCtx.getLog() << tcu::TestLog::Message << "Last error message was caused by monolithic program."
<< tcu::TestLog::EndMessage;
result = false;
}
}
/* Test separable programs */
if (true == m_base_context->getContextInfo().isExtensionSupported("GL_ARB_separate_shader_objects"))
{
/* Prepare programs */
Utils::program vertex_program(*m_base_context.get());
Utils::program tesselation_control_program(*m_base_context.get());
Utils::program tesselation_evaluation_program(*m_base_context.get());
Utils::program geometry_program(*m_base_context.get());
Utils::program fragment_program(*m_base_context.get());
program_pointers[m_fragment_stage_index] = &fragment_program;
program_pointers[m_geometry_stage_index] = &geometry_program;
program_pointers[m_tesselation_control_stage_index] = &tesselation_control_program;
program_pointers[m_tesselation_evaluation_stage_index] = &tesselation_evaluation_program;
program_pointers[m_vertex_stage_index] = &vertex_program;
prepareProgram(program_pointers, true);
/* Execute test */
if (false == testProgram(program_pointers, true, test_cases, n_test_cases))
{
m_testCtx.getLog() << tcu::TestLog::Message << "Last error message was caused by separable program."
<< tcu::TestLog::EndMessage;
result = false;
}
}
/* All done */
if (true == result)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
return tcu::TestNode::STOP;
}
/** Query state of subroutine uniforms of current program/pipeline
*
* @param set Storage for results
**/
void UniformPreservationTest::captureCurrentSubroutineSet(subroutineUniformSet& set)
{
/* GL entry points */
const glw::Functions& gl = m_base_context->getRenderContext().getFunctions();
/* Fragment */
gl.getUniformSubroutineuiv(GL_FRAGMENT_SHADER, m_subroutine_uniform_locations.m_fragment_shader_stage,
&set.m_fragment_shader_stage);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetUniformSubroutineuiv");
/* Geometry */
gl.getUniformSubroutineuiv(GL_GEOMETRY_SHADER, m_subroutine_uniform_locations.m_geometry_shader_stage,
&set.m_geometry_shader_stage);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetUniformSubroutineuiv");
/* Tess ctrl */
gl.getUniformSubroutineuiv(GL_TESS_CONTROL_SHADER,
m_subroutine_uniform_locations.m_tesselation_control_shader_stage,
&set.m_tesselation_control_shader_stage);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetUniformSubroutineuiv");
/* Tess eval */
gl.getUniformSubroutineuiv(GL_TESS_EVALUATION_SHADER,
m_subroutine_uniform_locations.m_tesselation_evaluation_shader_stage,
&set.m_tesselation_evaluation_shader_stage);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetUniformSubroutineuiv");
/* Vertex */
gl.getUniformSubroutineuiv(GL_VERTEX_SHADER, m_subroutine_uniform_locations.m_vertex_shader_stage,
&set.m_vertex_shader_stage);
GLU_EXPECT_NO_ERROR(gl.getError(), "GetUniformSubroutineuiv");
}
/** Get shaders' source code
*
* @param out_vertex_shader_code Vertex source code
* @param out_tesselation_control_shader_code Tess ctrl source code
* @param out_tesselation_evaluation_shader_code Tess eval source code
* @param out_geometry_shader_code Geometry source code
* @param out_fragment_shader_code Fragment source code
**/
void UniformPreservationTest::getShaders(const glw::GLchar*& out_vertex_shader_code,
const glw::GLchar*& out_tesselation_control_shader_code,
const glw::GLchar*& out_tesselation_evaluation_shader_code,
const glw::GLchar*& out_geometry_shader_code,
const glw::GLchar*& out_fragment_shader_code)
{
static const GLchar* vertex_shader_code = "#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"// Subroutine type\n"
"subroutine vec4 routine_type(in vec4 left, in vec4 right);\n"
"\n"
"// Subroutine definition\n"
"subroutine(routine_type) vec4 add(in vec4 left, in vec4 right)\n"
"{\n"
" return left + right;\n"
"}\n"
"\n"
"subroutine(routine_type) vec4 multiply(in vec4 left, in vec4 right)\n"
"{\n"
" return left * right;\n"
"}\n"
"\n"
"// Sub routine uniform\n"
"subroutine uniform routine_type routine;\n"
"\n"
"// Input data\n"
"uniform vec4 uni_vs_left;\n"
"uniform vec4 uni_vs_right;\n"
"\n"
"// Output\n"
"out vec4 vs_tcs_result;\n"
"\n"
"void main()\n"
"{\n"
" vs_tcs_result = routine(uni_vs_left, uni_vs_right);\n"
"}\n"
"\n";
static const GLchar* tesselation_control_shader_code =
"#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"layout(vertices = 1) out;\n"
"\n"
"// Subroutine type\n"
"subroutine vec4 routine_type(in vec4 left, in vec4 right);\n"
"\n"
"// Subroutine definition\n"
"subroutine(routine_type) vec4 add(in vec4 left, in vec4 right)\n"
"{\n"
" return left + right;\n"
"}\n"
"\n"
"subroutine(routine_type) vec4 multiply(in vec4 left, in vec4 right)\n"
"{\n"
" return left * right;\n"
"}\n"
"\n"
"// Sub routine uniform\n"
"subroutine uniform routine_type routine;\n"
"\n"
"// Input data\n"
"uniform vec4 uni_tcs_left;\n"
"uniform vec4 uni_tcs_right;\n"
"\n"
"in vec4 vs_tcs_result[];\n"
"\n"
"// Output\n"
"out vec4 tcs_tes_result[];\n"
"\n"
"void main()\n"
"{\n"
" gl_TessLevelOuter[0] = 1.0;\n"
" gl_TessLevelOuter[1] = 1.0;\n"
" gl_TessLevelOuter[2] = 1.0;\n"
" gl_TessLevelOuter[3] = 1.0;\n"
" gl_TessLevelInner[0] = 1.0;\n"
" gl_TessLevelInner[1] = 1.0;\n"
"\n"
" tcs_tes_result[gl_InvocationID] = routine(uni_tcs_left, uni_tcs_right) + vs_tcs_result[gl_InvocationID];\n"
"}\n"
"\n";
static const GLchar* tesselation_evaluation_shader_code =
"#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"layout(isolines, point_mode) in;\n"
"\n"
"// Subroutine type\n"
"subroutine vec4 routine_type(in vec4 left, in vec4 right);\n"
"\n"
"// Subroutine definition\n"
"subroutine(routine_type) vec4 add(in vec4 left, in vec4 right)\n"
"{\n"
" return left + right;\n"
"}\n"
"\n"
"subroutine(routine_type) vec4 multiply(in vec4 left, in vec4 right)\n"
"{\n"
" return left * right;\n"
"}\n"
"\n"
"// Sub routine uniform\n"
"subroutine uniform routine_type routine;\n"
"\n"
"// Input data\n"
"uniform vec4 uni_tes_left;\n"
"uniform vec4 uni_tes_right;\n"
"\n"
"in vec4 tcs_tes_result[];\n"
"\n"
"// Output\n"
"out vec4 tes_gs_result;\n"
"\n"
"void main()\n"
"{\n"
" tes_gs_result = routine(uni_tes_left, uni_tes_right) + tcs_tes_result[0];\n"
"}\n"
"\n";
static const GLchar* geometry_shader_code =
"#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"layout(points) in;\n"
"layout(points, max_vertices = 1) out;\n"
"\n"
"// Subroutine type\n"
"subroutine vec4 routine_type(in vec4 left, in vec4 right);\n"
"\n"
"// Subroutine definition\n"
"subroutine(routine_type) vec4 add(in vec4 left, in vec4 right)\n"
"{\n"
" return left + right;\n"
"}\n"
"\n"
"subroutine(routine_type) vec4 multiply(in vec4 left, in vec4 right)\n"
"{\n"
" return left * right;\n"
"}\n"
"\n"
"// Sub routine uniform\n"
"subroutine uniform routine_type routine;\n"
"\n"
"// Input data\n"
"uniform vec4 uni_gs_left;\n"
"uniform vec4 uni_gs_right;\n"
"\n"
"in vec4 tes_gs_result[];\n"
"\n"
"// Output\n"
"out vec4 gs_fs_result;\n"
"\n"
"void main()\n"
"{\n"
" gs_fs_result = routine(uni_gs_left, uni_gs_right) + tes_gs_result[0];\n"
"}\n"
"\n";
static const GLchar* fragmenty_shader_code =
"#version 400 core\n"
"#extension GL_ARB_shader_subroutine : require\n"
"\n"
"precision highp float;\n"
"\n"
"// Subroutine type\n"
"subroutine vec4 routine_type(in vec4 left, in vec4 right);\n"
"\n"
"// Subroutine definition\n"
"subroutine(routine_type) vec4 add(in vec4 left, in vec4 right)\n"
"{\n"
" return left + right;\n"
"}\n"
"\n"
"subroutine(routine_type) vec4 multiply(in vec4 left, in vec4 right)\n"
"{\n"
" return left * right;\n"
"}\n"
"\n"
"// Sub routine uniform\n"
"subroutine uniform routine_type routine;\n"
"\n"
"// Input data\n"
"uniform vec4 uni_fs_left;\n"
"uniform vec4 uni_fs_right;\n"
"\n"
"in vec4 gs_fs_result;\n"
"\n"
"// Output\n"
"out vec4 fs_out_result;\n"
"\n"
"void main()\n"
"{\n"
" fs_out_result = routine(uni_fs_left, uni_fs_right) + gs_fs_result;\n"
"}\n"
"\n";
out_vertex_shader_code = vertex_shader_code;
out_tesselation_control_shader_code = tesselation_control_shader_code;
out_tesselation_evaluation_shader_code = tesselation_evaluation_shader_code;
out_geometry_shader_code = geometry_shader_code;
out_fragment_shader_code = fragmenty_shader_code;
}
/** Create <m_n_shared_contexts> shared contexts
*
**/
void UniformPreservationTest::initSharedContexts()
{
glu::ContextType context_type(m_api_type);
glu::RenderConfig render_config(context_type);
const tcu::CommandLine& command_line(m_testCtx.getCommandLine());
glu::RenderContext* shared_context = &(m_base_context->getRenderContext());
glu::parseRenderConfig(&render_config, command_line);
#if (DE_OS == DE_OS_ANDROID)
// Android can only have one Window created at a time
// Note that this surface type is not supported on all platforms
render_config.surfaceType = glu::RenderConfig::SURFACETYPE_OFFSCREEN_GENERIC;
#endif
for (GLuint i = 0; i < m_n_shared_contexts; ++i)
{
m_shared_contexts[i] =
glu::createRenderContext(m_testCtx.getPlatform(), command_line, render_config, shared_context);
}
m_base_context->getRenderContext().makeCurrent();
}
/** Prepare program(s)
*
* @param programs An array of 5 programs' pointers. If monolithic program is prepared that only index m_fragment_stage_index should be initialized, otherwise all 5
* @param is_separable Select if monolithic or separable programs should be prepared
**/
void UniformPreservationTest::prepareProgram(Utils::program** programs, bool is_separable)
{
/* Get shader sources */
const GLchar* vertex_shader_code;
const GLchar* tesselation_control_shader_code;
const GLchar* tesselation_evaluation_shader_code;
const GLchar* geometry_shader_code;
const GLchar* fragmenty_shader_code;
getShaders(vertex_shader_code, tesselation_control_shader_code, tesselation_evaluation_shader_code,
geometry_shader_code, fragmenty_shader_code);
/* Subroutines and uniform names */
static const GLchar* subroutine_names[] = { "add", "multiply" };
static const GLuint n_subroutines = sizeof(subroutine_names) / sizeof(subroutine_names[0]);
static const GLchar* subroutine_uniform_name = "routine";
/* Build program */
if (false == is_separable)
{
programs[0]->build(0 /* compute shader source */, fragmenty_shader_code, geometry_shader_code,
tesselation_control_shader_code, tesselation_evaluation_shader_code, vertex_shader_code,
0 /* varying_names */, 0 /* n_varying_names */);
programs[m_geometry_stage_index] = programs[m_fragment_stage_index];
programs[m_tesselation_control_stage_index] = programs[m_fragment_stage_index];
programs[m_tesselation_evaluation_stage_index] = programs[m_fragment_stage_index];
programs[m_vertex_stage_index] = programs[m_fragment_stage_index];
}
else
{
programs[m_fragment_stage_index]->build(0, fragmenty_shader_code, 0, 0, 0, 0, 0, 0, true);
programs[m_geometry_stage_index]->build(0, 0, geometry_shader_code, 0, 0, 0, 0, 0, true);
programs[m_tesselation_control_stage_index]->build(0, 0, 0, tesselation_control_shader_code, 0, 0, 0, 0, true);
programs[m_tesselation_evaluation_stage_index]->build(0, 0, 0, 0, tesselation_evaluation_shader_code, 0, 0, 0,
true);
programs[m_vertex_stage_index]->build(0, 0, 0, 0, 0, vertex_shader_code, 0, 0, true);
}
/* Get subroutine indices */
for (GLuint i = 0; i < n_subroutines; ++i)
{
m_subroutine_indices[i].m_fragment_shader_stage =
programs[m_fragment_stage_index]->getSubroutineIndex(subroutine_names[i], GL_FRAGMENT_SHADER);
m_subroutine_indices[i].m_geometry_shader_stage =
programs[m_geometry_stage_index]->getSubroutineIndex(subroutine_names[i], GL_GEOMETRY_SHADER);
m_subroutine_indices[i].m_tesselation_control_shader_stage =
programs[m_tesselation_control_stage_index]->getSubroutineIndex(subroutine_names[i],
GL_TESS_CONTROL_SHADER);
m_subroutine_indices[i].m_tesselation_evaluation_shader_stage =
programs[m_tesselation_evaluation_stage_index]->getSubroutineIndex(subroutine_names[i],
GL_TESS_EVALUATION_SHADER);
m_subroutine_indices[i].m_vertex_shader_stage =
programs[m_vertex_stage_index]->getSubroutineIndex(subroutine_names[i], GL_VERTEX_SHADER);
}
/* Get subroutine uniform locations */
m_subroutine_uniform_locations.m_fragment_shader_stage =
programs[m_fragment_stage_index]->getSubroutineUniformLocation(subroutine_uniform_name, GL_FRAGMENT_SHADER);
m_subroutine_uniform_locations.m_geometry_shader_stage =
programs[m_geometry_stage_index]->getSubroutineUniformLocation(subroutine_uniform_name, GL_GEOMETRY_SHADER);
m_subroutine_uniform_locations.m_tesselation_control_shader_stage =
programs[m_tesselation_control_stage_index]->getSubroutineUniformLocation(subroutine_uniform_name,
GL_TESS_CONTROL_SHADER);
m_subroutine_uniform_locations.m_tesselation_evaluation_shader_stage =
programs[m_tesselation_evaluation_stage_index]->getSubroutineUniformLocation(subroutine_uniform_name,
GL_TESS_EVALUATION_SHADER);
m_subroutine_uniform_locations.m_vertex_shader_stage =
programs[m_vertex_stage_index]->getSubroutineUniformLocation(subroutine_uniform_name, GL_VERTEX_SHADER);
}
/** Generate program pipeline for current context and attach separable programs
*
* @param out_pipeline_id Id of generated pipeline
* @param programs Collection of separable programs
**/
void UniformPreservationTest::prepareProgramPipeline(glw::GLuint& out_pipeline_id, Utils::program** programs)
{
/* GL entry points */
const glw::Functions& gl = m_base_context->getRenderContext().getFunctions();
/* Generate */
gl.genProgramPipelines(1, &out_pipeline_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "GenProgramPipelines");
/* Bind */
gl.bindProgramPipeline(out_pipeline_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindProgramPipeline");
/* Set up programs */
gl.useProgramStages(out_pipeline_id, GL_FRAGMENT_SHADER_BIT, programs[m_fragment_stage_index]->m_program_object_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgramStages");
gl.useProgramStages(out_pipeline_id, GL_GEOMETRY_SHADER_BIT, programs[m_geometry_stage_index]->m_program_object_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgramStages");
gl.useProgramStages(out_pipeline_id, GL_TESS_CONTROL_SHADER_BIT,
programs[m_tesselation_control_stage_index]->m_program_object_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgramStages");
gl.useProgramStages(out_pipeline_id, GL_TESS_EVALUATION_SHADER_BIT,
programs[m_tesselation_evaluation_stage_index]->m_program_object_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgramStages");
gl.useProgramStages(out_pipeline_id, GL_VERTEX_SHADER_BIT, programs[m_vertex_stage_index]->m_program_object_id);
GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgramStages");
}
/** Test specific case
*
* @param bit_field An array of 5 bit fields used to set up subroutine uniforms, one element per context
*
* @return True if test pass, false otherwise
**/
bool UniformPreservationTest::testCase(const glw::GLuint bit_field[5])
{
/* Storage for subroutine indices */
subroutineUniformSet captured_subroutine_indices[m_n_shared_contexts + 1];
subroutineUniformSet subroutine_indices[m_n_shared_contexts + 1];
/* Prepare subroutine_indices with bit fields */
for (GLuint i = 0; i < m_n_shared_contexts + 1; ++i)
{
subroutine_indices[i].set(bit_field[i], m_subroutine_indices);
};
/* Update subroutine uniforms, each context gets different set */
for (GLuint i = 0; i < m_n_shared_contexts; ++i)
{
m_shared_contexts[i]->makeCurrent();
updateCurrentSubroutineSet(subroutine_indices[i]);
}
m_base_context->getRenderContext().makeCurrent();
updateCurrentSubroutineSet(subroutine_indices[m_n_shared_contexts]);
/* Capture subroutine uniforms */
for (GLuint i = 0; i < m_n_shared_contexts; ++i)
{
m_shared_contexts[i]->makeCurrent();
captureCurrentSubroutineSet(captured_subroutine_indices[i]);
}
m_base_context->getRenderContext().makeCurrent();
captureCurrentSubroutineSet(captured_subroutine_indices[m_n_shared_contexts]);
/* Verify that captured uniforms match expected values */
for (GLuint i = 0; i < m_n_shared_contexts + 1; ++i)
{
if (subroutine_indices[i] != captured_subroutine_indices[i])
{
m_testCtx.getLog() << tcu::TestLog::Message << "Error."
<< " Context: " << i << " VS, expected: " << subroutine_indices[i].m_vertex_shader_stage
<< " captured: " << captured_subroutine_indices[i].m_vertex_shader_stage
<< " TCS, expected: " << subroutine_indices[i].m_tesselation_control_shader_stage
<< " captured: " << captured_subroutine_indices[i].m_tesselation_control_shader_stage
<< " TES, expected: " << subroutine_indices[i].m_tesselation_evaluation_shader_stage
<< " captured: " << captured_subroutine_indices[i].m_tesselation_evaluation_shader_stage
<< " GS, expected: " << subroutine_indices[i].m_geometry_shader_stage
<< " captured: " << captured_subroutine_indices[i].m_geometry_shader_stage
<< " FS, expected: " << subroutine_indices[i].m_fragment_shader_stage
<< " captured: " << captured_subroutine_indices[i].m_fragment_shader_stage
<< tcu::TestLog::EndMessage;
return false;
}
}
return true;
}
/** Test a program or pipeline
*
* @param programs An array of 5 programs\ pointers, as in preparePrograms
* @param is_separable Selects if monolithic or separable programs should be used
* @param test_cases Collection of test cases
* @param n_test_cases Number of test cases
*
* @return True if all cases pass, false otherwise
**/
bool UniformPreservationTest::testProgram(Utils::program** programs, bool is_separable,
const glw::GLuint test_cases[][5], glw::GLuint n_test_cases)
{
/* Set program/pipeline as current for all contexts */
if (false == is_separable)
{
programs[0]->use();
for (GLuint i = 0; i < m_n_shared_contexts; ++i)
{
m_shared_contexts[i]->makeCurrent();
programs[0]->use();
}
}
else
{
/* GL entry points */
const glw::Functions& gl = m_base_context->getRenderContext().getFunctions();
/* Make sure that program pipeline will be used */
gl.useProgram(0);
GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgram");
prepareProgramPipeline(m_program_pipelines[m_n_shared_contexts], programs);
for (GLuint i = 0; i < m_n_shared_contexts; ++i)
{
m_shared_contexts[i]->makeCurrent();
/* Make sure that program pipeline will be used */
gl.useProgram(0);
GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgram");
prepareProgramPipeline(m_program_pipelines[i], programs);
}
}
/* Execute test */
bool result = true;
for (GLuint i = 0; i < n_test_cases; ++i)
{
if (false == testCase(test_cases[i]))
{
result = false;
break;
}
}
return result;
}
/** Set up subroutine uniforms for current program or pipeline
*
* @param set Set of subroutine indices
**/
void UniformPreservationTest::updateCurrentSubroutineSet(const subroutineUniformSet& set)
{
/* GL entry points */
const glw::Functions& gl = m_base_context->getRenderContext().getFunctions();
/* Fragment */
gl.uniformSubroutinesuiv(GL_FRAGMENT_SHADER, 1 /* count */, &set.m_fragment_shader_stage);
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformSubroutinesuiv");
/* Geometry */
gl.uniformSubroutinesuiv(GL_GEOMETRY_SHADER, 1 /* count */, &set.m_geometry_shader_stage);
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformSubroutinesuiv");
/* Tess ctrl */
gl.uniformSubroutinesuiv(GL_TESS_CONTROL_SHADER, 1 /* count */, &set.m_tesselation_control_shader_stage);
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformSubroutinesuiv");
/* Tess eval */
gl.uniformSubroutinesuiv(GL_TESS_EVALUATION_SHADER, 1 /* count */, &set.m_tesselation_evaluation_shader_stage);
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformSubroutinesuiv");
/* Vertex */
gl.uniformSubroutinesuiv(GL_VERTEX_SHADER, 1 /* count */, &set.m_vertex_shader_stage);
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformSubroutinesuiv");
}
/** Constructor.
*
* @param context Rendering context.
**/
MultipleContextsTests::MultipleContextsTests(tcu::TestContext& testCtx, glu::ApiType apiType)
: tcu::TestCaseGroup(testCtx, "multiple_contexts", "Verifies \"shader_subroutine\" functionality")
, m_apiType(apiType)
{
/* Left blank on purpose */
}
/** Initializes a texture_storage_multisample test group.
*
**/
void MultipleContextsTests::init(void)
{
addChild(new UniformPreservationTest(m_testCtx, m_apiType));
}
} /* glcts namespace */