/*------------------------------------------------------------------------ * 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 vertexParams; map 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()); } // gl_Position counts as an output component as well, so outputComponents = inputComponents + 4 if (m_inputComponents + 4 > maxVertexOutputComponents) { const std::string notSupportedStr = "Unsupported number of user specified vertex output components (" + de::toString(m_inputComponents + 4) + ") maxVertexOutputComponents=" + de::toString(maxVertexOutputComponents); TCU_THROW(NotSupportedError, notSupportedStr.c_str()); } return new FragmentInputComponentCaseInstance(context); } } // anonymous TestCaseGroup* createLimitTests (TestContext& testCtx) { de::MovePtr limitGroup (new TestCaseGroup(testCtx, "limits", "Shader device limit tests")); de::MovePtr nearGroup (new TestCaseGroup(testCtx, "near_max", "Shaders near maximum values")); de::MovePtr 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