blob: e4cbfb561158c7ffdadf1f3a8878247b6c41c467 [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2018 Google 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 Shader limit tests.
*//*--------------------------------------------------------------------*/
#include "vktShaderRenderLimitTests.hpp"
#include "vktShaderRender.hpp"
#include "tcuImageCompare.hpp"
#include "tcuStringTemplate.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuTestLog.hpp"
#include "vktDrawUtil.hpp"
#include "deMath.h"
using namespace std;
using namespace tcu;
using namespace vk;
using namespace de;
namespace vkt
{
using namespace drawutil;
namespace sr
{
namespace
{
class FragmentInputComponentCaseInstance : public ShaderRenderCaseInstance
{
public:
FragmentInputComponentCaseInstance (Context& context);
TestStatus iterate(void);
virtual void setupDefaultInputs(void);
private:
const Vec4 m_constantColor;
};
FragmentInputComponentCaseInstance::FragmentInputComponentCaseInstance (Context& context)
: ShaderRenderCaseInstance (context)
, m_constantColor (0.1f, 0.05f, 0.2f, 0.0f)
{
}
TestStatus FragmentInputComponentCaseInstance::iterate (void)
{
const UVec2 viewportSize = getViewportSize();
const int width = viewportSize.x();
const int height = viewportSize.y();
const tcu::RGBA threshold (2, 2, 2, 2);
Surface resImage (width, height);
Surface refImage (width, height);
bool compareOk = false;
const deUint16 indices[12] =
{
0, 4, 1,
0, 5, 4,
1, 2, 3,
1, 3, 4
};
setup();
render(6, 4, indices);
copy(resImage.getAccess(), getResultImage().getAccess());
// Reference image
for (int y = 0; y < refImage.getHeight(); y++)
{
for (int x = 0; x < refImage.getWidth(); x++)
refImage.setPixel(x, y, RGBA(0, 255, 0, 255));
}
compareOk = pixelThresholdCompare(m_context.getTestContext().getLog(), "Result", "Image comparison result", refImage, resImage, threshold, COMPARE_LOG_RESULT);
if (compareOk)
return TestStatus::pass("Result image matches reference");
else
return TestStatus::fail("Image mismatch");
}
void FragmentInputComponentCaseInstance::setupDefaultInputs (void)
{
const float vertices[] =
{
-1.0f, -1.0f, 0.0f, 1.0f,
0.0f, -1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 1.0f
};
addAttribute(0u, VK_FORMAT_R32G32B32A32_SFLOAT, deUint16(sizeof(float) * 4), 6, vertices);
}
class FragmentInputComponentCase : public TestCase
{
public:
FragmentInputComponentCase (TestContext& testCtx, const string& name, const string& description, const deUint16 inputComponents);
virtual ~FragmentInputComponentCase(void);
void initPrograms(SourceCollections& dst) const;
TestInstance* createInstance(Context& context) const;
private:
FragmentInputComponentCase (const FragmentInputComponentCase&);
const deUint16 m_inputComponents;
};
FragmentInputComponentCase::FragmentInputComponentCase (TestContext& testCtx, const string& name, const string& description, const deUint16 inputComponents)
: TestCase (testCtx, name, description)
, m_inputComponents (inputComponents)
{
}
FragmentInputComponentCase::~FragmentInputComponentCase (void)
{
}
void FragmentInputComponentCase::initPrograms (SourceCollections& dst) const
{
const tcu::StringTemplate vertexCodeTemplate(
"#version 450\n"
"layout(location = 0) in highp vec4 a_position;\n"
"${VARYING_OUT}"
"void main (void)\n"
"{\n"
" gl_Position = a_position;\n"
"${VARYING_DECL}"
"}\n");
const tcu::StringTemplate fragmentCodeTemplate(
"#version 450\n"
"layout(location = 0) out highp vec4 o_color;\n"
"${VARYING_IN}"
"void main (void)\n"
"{\n"
" int errorCount = 0;\n"
"${VERIFY}"
"\n"
" if (errorCount == 0)\n"
" o_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
" else\n"
" o_color = vec4(1.0, 0.0, 0.0, 1.0);\n"
"}\n");
//
// The number of vertex output/fragment input components is *inclusive* of any built-ins being used,
// since gl_Position is always output by the shader, this actually means that there are n - 4 components
// available as user specified output data.
//
// [14.1.4. Location Assignment, para 11]
//
// "The number of input and output locations available for a shader input or output
// interface are limited, and dependent on the shader stage as described in Shader
// Input and Output Locations. All variables in both the built-in interface block
// and the user-defined variable interface count against these limits."
//
// So, as an example, the '128' component variant of this test will specify 124 user
// declared outputs in addition to gl_Position.
deUint16 maxLocations = (deUint16)deCeilToInt32((float)(m_inputComponents - 4) / 4u);
string varyingType;
map<string, string> vertexParams;
map<string, string> fragmentParams;
for (deUint16 loc = 0; loc < maxLocations; loc++)
{
if (loc == (maxLocations - 1u))
{
switch (m_inputComponents - loc * 4u)
{
case 1:
varyingType = "float";
break;
case 2:
varyingType = "vec2";
break;
case 3:
varyingType = "vec3";
break;
default:
varyingType = "vec4";
}
}
else
varyingType = "vec4";
vertexParams["VARYING_OUT"] += "layout(location = " + de::toString(loc) + ") out highp " + varyingType + " o_color" + de::toString(loc) + ";\n";
vertexParams["VARYING_DECL"] += " o_color" + de::toString(loc) + " = " + varyingType + "(" + de::toString(loc) + ".0);\n";
fragmentParams["VARYING_IN"] += "layout(location = " + de::toString(loc) + ") in highp " + varyingType + " i_color" + de::toString(loc) + ";\n";
fragmentParams["VERIFY"] += " errorCount += (i_color" + de::toString(loc) + " == " + varyingType + "(" + de::toString(loc) + ".0)) ? 0 : 1;\n";
}
dst.glslSources.add("vert") << glu::VertexSource(vertexCodeTemplate.specialize(vertexParams));
dst.glslSources.add("frag") << glu::FragmentSource(fragmentCodeTemplate.specialize(fragmentParams));
}
TestInstance* FragmentInputComponentCase::createInstance (Context& context) const
{
const InstanceInterface& vki = context.getInstanceInterface();
const VkPhysicalDevice physDevice = context.getPhysicalDevice();
const VkPhysicalDeviceLimits limits = getPhysicalDeviceProperties(vki, physDevice).limits;
const deUint16 maxFragmentInputComponents = (deUint16)limits.maxFragmentInputComponents;
const deUint16 maxVertexOutputComponents = (deUint16)limits.maxVertexOutputComponents;
if (m_inputComponents > maxFragmentInputComponents)
{
const std::string notSupportedStr = "Unsupported number of fragment input components (" +
de::toString(m_inputComponents) +
") maxFragmentInputComponents=" + de::toString(maxFragmentInputComponents);
TCU_THROW(NotSupportedError, notSupportedStr.c_str());
}
if (m_inputComponents > maxVertexOutputComponents)
{
const std::string notSupportedStr = "Unsupported number of user specified vertex output components (" +
de::toString(m_inputComponents) +
") maxVertexOutputComponents=" + de::toString(maxVertexOutputComponents);
TCU_THROW(NotSupportedError, notSupportedStr.c_str());
}
return new FragmentInputComponentCaseInstance(context);
}
} // anonymous
TestCaseGroup* createLimitTests (TestContext& testCtx)
{
de::MovePtr<TestCaseGroup> limitGroup (new TestCaseGroup(testCtx, "limits", "Shader device limit tests"));
de::MovePtr<TestCaseGroup> nearGroup (new TestCaseGroup(testCtx, "near_max", "Shaders near maximum values"));
de::MovePtr<TestCaseGroup> inputComponentsGroup (new TestCaseGroup(testCtx, "fragment_input", "Fragment input component variations"));
// Fragment input component case
deUint16 fragmentComponentMaxLimits [] = { 64u, 128u, 256u };
for (deUint16 limitNdx = 0; limitNdx < DE_LENGTH_OF_ARRAY(fragmentComponentMaxLimits); limitNdx++)
{
for (deInt16 cases = 5; cases > 0; cases--)
inputComponentsGroup->addChild(new FragmentInputComponentCase(testCtx, "components_" + de::toString(fragmentComponentMaxLimits[limitNdx] - cases), "Input component count", (deUint16)(fragmentComponentMaxLimits[limitNdx] - cases)));
}
nearGroup->addChild(inputComponentsGroup.release());
limitGroup->addChild(nearGroup.release());
return limitGroup.release();
}
} // sr
} // vkt