1/*------------------------------------------------------------------------ 2* Vulkan Conformance Tests 3* ------------------------ 4* 5* Copyright (c) 2018 Google Inc. 6* 7* Licensed under the Apache License, Version 2.0 (the "License"); 8* you may not use this file except in compliance with the License. 9* You may obtain a copy of the License at 10* 11* http://www.apache.org/licenses/LICENSE-2.0 12* 13* Unless required by applicable law or agreed to in writing, software 14* distributed under the License is distributed on an "AS IS" BASIS, 15* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16* See the License for the specific language governing permissions and 17* limitations under the License. 18* 19*//*! 20* \file 21* \brief Shader limit tests. 22*//*--------------------------------------------------------------------*/ 23 24#include "vktShaderRenderLimitTests.hpp" 25#include "vktShaderRender.hpp" 26#include "tcuImageCompare.hpp" 27#include "tcuStringTemplate.hpp" 28#include "tcuTextureUtil.hpp" 29#include "tcuTestLog.hpp" 30#include "vktDrawUtil.hpp" 31#include "deMath.h" 32 33using namespace std; 34using namespace tcu; 35using namespace vk; 36using namespace de; 37 38namespace vkt 39{ 40using namespace drawutil; 41 42namespace sr 43{ 44 45namespace 46{ 47 48class FragmentInputComponentCaseInstance : public ShaderRenderCaseInstance 49{ 50public: 51 FragmentInputComponentCaseInstance (Context& context); 52 53 TestStatus iterate(void); 54 virtual void setupDefaultInputs(void); 55 56private: 57 const Vec4 m_constantColor; 58}; 59 60FragmentInputComponentCaseInstance::FragmentInputComponentCaseInstance (Context& context) 61 : ShaderRenderCaseInstance (context) 62 , m_constantColor (0.1f, 0.05f, 0.2f, 0.0f) 63{ 64} 65 66TestStatus FragmentInputComponentCaseInstance::iterate (void) 67{ 68 const UVec2 viewportSize = getViewportSize(); 69 const int width = viewportSize.x(); 70 const int height = viewportSize.y(); 71 const tcu::RGBA threshold (2, 2, 2, 2); 72 Surface resImage (width, height); 73 Surface refImage (width, height); 74 bool compareOk = false; 75 76 const deUint16 indices[12] = 77 { 78 0, 4, 1, 79 0, 5, 4, 80 1, 2, 3, 81 1, 3, 4 82 }; 83 84 setup(); 85 render(6, 4, indices); 86 copy(resImage.getAccess(), getResultImage().getAccess()); 87 88 // Reference image 89 for (int y = 0; y < refImage.getHeight(); y++) 90 { 91 for (int x = 0; x < refImage.getWidth(); x++) 92 refImage.setPixel(x, y, RGBA(0, 255, 0, 255)); 93 } 94 95 compareOk = pixelThresholdCompare(m_context.getTestContext().getLog(), "Result", "Image comparison result", refImage, resImage, threshold, COMPARE_LOG_RESULT); 96 97 if (compareOk) 98 return TestStatus::pass("Result image matches reference"); 99 else 100 return TestStatus::fail("Image mismatch"); 101} 102 103void FragmentInputComponentCaseInstance::setupDefaultInputs (void) 104{ 105 const float vertices[] = 106 { 107 -1.0f, -1.0f, 0.0f, 1.0f, 108 0.0f, -1.0f, 0.0f, 1.0f, 109 1.0f, -1.0f, 0.0f, 1.0f, 110 1.0f, 1.0f, 0.0f, 1.0f, 111 0.0f, 1.0f, 0.0f, 1.0f, 112 -1.0f, 1.0f, 0.0f, 1.0f 113 }; 114 115 addAttribute(0u, VK_FORMAT_R32G32B32A32_SFLOAT, deUint16(sizeof(float) * 4), 6, vertices); 116} 117 118class FragmentInputComponentCase : public TestCase 119{ 120public: 121 FragmentInputComponentCase (TestContext& testCtx, const string& name, const deUint16 inputComponents); 122 virtual ~FragmentInputComponentCase(void); 123 124 void initPrograms(SourceCollections& dst) const; 125 TestInstance* createInstance(Context& context) const; 126 127private: 128 FragmentInputComponentCase (const FragmentInputComponentCase&); 129 const deUint16 m_inputComponents; 130}; 131 132FragmentInputComponentCase::FragmentInputComponentCase (TestContext& testCtx, const string& name, const deUint16 inputComponents) 133 : TestCase (testCtx, name) 134 , m_inputComponents (inputComponents) 135{ 136} 137 138FragmentInputComponentCase::~FragmentInputComponentCase (void) 139{ 140} 141 142void FragmentInputComponentCase::initPrograms (SourceCollections& dst) const 143{ 144 const tcu::StringTemplate vertexCodeTemplate( 145 "#version 450\n" 146 "layout(location = 0) in highp vec4 a_position;\n" 147 "${VARYING_OUT}" 148 "void main (void)\n" 149 "{\n" 150 " gl_Position = a_position;\n" 151 "${VARYING_DECL}" 152 "}\n"); 153 154 const tcu::StringTemplate fragmentCodeTemplate( 155 "#version 450\n" 156 "layout(location = 0) out highp vec4 o_color;\n" 157 "${VARYING_IN}" 158 "void main (void)\n" 159 "{\n" 160 " int errorCount = 0;\n" 161 "${VERIFY}" 162 "\n" 163 " if (errorCount == 0)\n" 164 " o_color = vec4(0.0, 1.0, 0.0, 1.0);\n" 165 " else\n" 166 " o_color = vec4(1.0, 0.0, 0.0, 1.0);\n" 167 "}\n"); 168 169 // 170 // The number of vertex output/fragment input components is *inclusive* of any built-ins being used, 171 // since gl_Position is always output by the shader, this actually means that there are n - 4 components 172 // available as user specified output data. 173 // 174 // [14.1.4. Location Assignment, para 11] 175 // 176 // "The number of input and output locations available for a shader input or output 177 // interface are limited, and dependent on the shader stage as described in Shader 178 // Input and Output Locations. All variables in both the built-in interface block 179 // and the user-defined variable interface count against these limits." 180 // 181 // So, as an example, the '128' component variant of this test will specify 124 user 182 // declared outputs in addition to gl_Position. 183 184 deUint16 maxLocations = (deUint16)deCeilToInt32((float)(m_inputComponents - 4) / 4u); 185 string varyingType; 186 map<string, string> vertexParams; 187 map<string, string> fragmentParams; 188 189 for (deUint16 loc = 0; loc < maxLocations; loc++) 190 { 191 if (loc == (maxLocations - 1u)) 192 { 193 switch (m_inputComponents - loc * 4u) 194 { 195 case 1: 196 varyingType = "float"; 197 break; 198 case 2: 199 varyingType = "vec2"; 200 break; 201 case 3: 202 varyingType = "vec3"; 203 break; 204 default: 205 varyingType = "vec4"; 206 } 207 } 208 else 209 varyingType = "vec4"; 210 211 vertexParams["VARYING_OUT"] += "layout(location = " + de::toString(loc) + ") out highp " + varyingType + " o_color" + de::toString(loc) + ";\n"; 212 vertexParams["VARYING_DECL"] += " o_color" + de::toString(loc) + " = " + varyingType + "(" + de::toString(loc) + ".0);\n"; 213 fragmentParams["VARYING_IN"] += "layout(location = " + de::toString(loc) + ") in highp " + varyingType + " i_color" + de::toString(loc) + ";\n"; 214 fragmentParams["VERIFY"] += " errorCount += (i_color" + de::toString(loc) + " == " + varyingType + "(" + de::toString(loc) + ".0)) ? 0 : 1;\n"; 215 } 216 217 dst.glslSources.add("vert") << glu::VertexSource(vertexCodeTemplate.specialize(vertexParams)); 218 dst.glslSources.add("frag") << glu::FragmentSource(fragmentCodeTemplate.specialize(fragmentParams)); 219} 220 221TestInstance* FragmentInputComponentCase::createInstance (Context& context) const 222{ 223 const InstanceInterface& vki = context.getInstanceInterface(); 224 const VkPhysicalDevice physDevice = context.getPhysicalDevice(); 225 const VkPhysicalDeviceLimits limits = getPhysicalDeviceProperties(vki, physDevice).limits; 226 const deUint16 maxFragmentInputComponents = (deUint16)limits.maxFragmentInputComponents; 227 const deUint16 maxVertexOutputComponents = (deUint16)limits.maxVertexOutputComponents; 228 229 if (m_inputComponents > maxFragmentInputComponents) 230 { 231 const std::string notSupportedStr = "Unsupported number of fragment input components (" + 232 de::toString(m_inputComponents) + 233 ") maxFragmentInputComponents=" + de::toString(maxFragmentInputComponents); 234 TCU_THROW(NotSupportedError, notSupportedStr.c_str()); 235 } 236 237 // gl_Position counts as an output component as well, so outputComponents = inputComponents + 4 238 if (m_inputComponents + 4 > maxVertexOutputComponents) 239 { 240 const std::string notSupportedStr = "Unsupported number of user specified vertex output components (" + 241 de::toString(m_inputComponents + 4) + 242 ") maxVertexOutputComponents=" + de::toString(maxVertexOutputComponents); 243 TCU_THROW(NotSupportedError, notSupportedStr.c_str()); 244 } 245 246 return new FragmentInputComponentCaseInstance(context); 247} 248} // anonymous 249 250TestCaseGroup* createLimitTests (TestContext& testCtx) 251{ 252 de::MovePtr<TestCaseGroup> limitGroup (new TestCaseGroup(testCtx, "limits", "Shader device limit tests")); 253 de::MovePtr<TestCaseGroup> nearGroup (new TestCaseGroup(testCtx, "near_max", "Shaders near maximum values")); 254 255 de::MovePtr<TestCaseGroup> inputComponentsGroup (new TestCaseGroup(testCtx, "fragment_input", "Fragment input component variations")); 256 257 // Fragment input component case 258 deUint16 fragmentComponentMaxLimits [] = { 64u, 128u, 256u }; 259 260 for (deUint16 limitNdx = 0; limitNdx < DE_LENGTH_OF_ARRAY(fragmentComponentMaxLimits); limitNdx++) 261 { 262 for (deInt16 cases = 5; cases > 0; cases--) 263 inputComponentsGroup->addChild(new FragmentInputComponentCase(testCtx, "components_" + de::toString(fragmentComponentMaxLimits[limitNdx] - cases), (deUint16)(fragmentComponentMaxLimits[limitNdx] - cases))); 264 } 265 266 nearGroup->addChild(inputComponentsGroup.release()); 267 limitGroup->addChild(nearGroup.release()); 268 return limitGroup.release(); 269} 270 271} // sr 272} // vkt 273