1/*------------------------------------------------------------------------ 2 * Vulkan Conformance Tests 3 * ------------------------ 4 * 5 * Copyright (c) 2014 The Android Open Source Project 6 * Copyright (c) 2016 The Khronos Group Inc. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * 20 *//*! 21 * \file 22 * \brief Tessellation Winding Tests 23 *//*--------------------------------------------------------------------*/ 24 25#include "vktTessellationWindingTests.hpp" 26#include "vktTestCaseUtil.hpp" 27#include "vktTessellationUtil.hpp" 28 29#include "tcuTestLog.hpp" 30#include "tcuRGBA.hpp" 31 32#include "vkDefs.hpp" 33#include "vkQueryUtil.hpp" 34#include "vkBuilderUtil.hpp" 35#include "vkImageUtil.hpp" 36#include "vkTypeUtil.hpp" 37#include "vkStrUtil.hpp" 38 39#include "deUniquePtr.hpp" 40 41namespace vkt 42{ 43namespace tessellation 44{ 45 46using namespace vk; 47 48namespace 49{ 50 51std::string getCaseName (const TessPrimitiveType primitiveType, const Winding winding) 52{ 53 std::ostringstream str; 54 str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getWindingShaderName(winding); 55 return str.str(); 56} 57 58inline VkFrontFace mapFrontFace (const Winding winding) 59{ 60 switch (winding) 61 { 62 case WINDING_CCW: return VK_FRONT_FACE_COUNTER_CLOCKWISE; 63 case WINDING_CW: return VK_FRONT_FACE_CLOCKWISE; 64 default: 65 DE_ASSERT(false); 66 return VK_FRONT_FACE_LAST; 67 } 68} 69 70//! Returns true when the image passes the verification. 71bool verifyResultImage (tcu::TestLog& log, 72 const tcu::ConstPixelBufferAccess image, 73 const TessPrimitiveType primitiveType, 74 const Winding winding, 75 const Winding frontFaceWinding) 76{ 77 const int totalNumPixels = image.getWidth()*image.getHeight(); 78 const int badPixelTolerance = (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 5*de::max(image.getWidth(), image.getHeight()) : 0); 79 80 const tcu::Vec4 white = tcu::RGBA::white().toVec(); 81 const tcu::Vec4 red = tcu::RGBA::red().toVec(); 82 83 int numWhitePixels = 0; 84 int numRedPixels = 0; 85 for (int y = 0; y < image.getHeight(); y++) 86 for (int x = 0; x < image.getWidth(); x++) 87 { 88 numWhitePixels += image.getPixel(x, y) == white ? 1 : 0; 89 numRedPixels += image.getPixel(x, y) == red ? 1 : 0; 90 } 91 92 DE_ASSERT(numWhitePixels + numRedPixels <= totalNumPixels); 93 94 log << tcu::TestLog::Message << "Note: got " << numWhitePixels << " white and " << numRedPixels << " red pixels" << tcu::TestLog::EndMessage; 95 96 const int otherPixels = totalNumPixels - numWhitePixels - numRedPixels; 97 if (otherPixels > badPixelTolerance) 98 { 99 log << tcu::TestLog::Message 100 << "Failure: Got " << otherPixels << " other than white or red pixels (maximum tolerance " << badPixelTolerance << ")" 101 << tcu::TestLog::EndMessage; 102 return false; 103 } 104 105 if (frontFaceWinding == winding) 106 { 107 if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 108 { 109 if (de::abs(numWhitePixels - totalNumPixels/2) > badPixelTolerance) 110 { 111 log << tcu::TestLog::Message << "Failure: wrong number of white pixels; expected approximately " << totalNumPixels/2 << tcu::TestLog::EndMessage; 112 return false; 113 } 114 } 115 else if (primitiveType == TESSPRIMITIVETYPE_QUADS) 116 { 117 if (numWhitePixels != totalNumPixels) 118 { 119 log << tcu::TestLog::Message << "Failure: expected only white pixels (full-viewport quad)" << tcu::TestLog::EndMessage; 120 return false; 121 } 122 } 123 else 124 DE_ASSERT(false); 125 } 126 else 127 { 128 if (numWhitePixels != 0) 129 { 130 log << tcu::TestLog::Message << "Failure: expected only red pixels (everything culled)" << tcu::TestLog::EndMessage; 131 return false; 132 } 133 } 134 135 return true; 136} 137 138class WindingTest : public TestCase 139{ 140public: 141 WindingTest (tcu::TestContext& testCtx, 142 const TessPrimitiveType primitiveType, 143 const Winding winding); 144 145 void initPrograms (SourceCollections& programCollection) const; 146 TestInstance* createInstance (Context& context) const; 147 148private: 149 const TessPrimitiveType m_primitiveType; 150 const Winding m_winding; 151}; 152 153WindingTest::WindingTest (tcu::TestContext& testCtx, 154 const TessPrimitiveType primitiveType, 155 const Winding winding) 156 : TestCase (testCtx, getCaseName(primitiveType, winding), "") 157 , m_primitiveType (primitiveType) 158 , m_winding (winding) 159{ 160} 161 162void WindingTest::initPrograms (SourceCollections& programCollection) const 163{ 164 // Vertex shader - no inputs 165 { 166 std::ostringstream src; 167 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 168 << "\n" 169 << "void main (void)\n" 170 << "{\n" 171 << "}\n"; 172 173 programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); 174 } 175 176 // Tessellation control shader 177 { 178 std::ostringstream src; 179 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 180 << "#extension GL_EXT_tessellation_shader : require\n" 181 << "\n" 182 << "layout(vertices = 1) out;\n" 183 << "\n" 184 << "void main (void)\n" 185 << "{\n" 186 << " gl_TessLevelInner[0] = 5.0;\n" 187 << " gl_TessLevelInner[1] = 5.0;\n" 188 << "\n" 189 << " gl_TessLevelOuter[0] = 5.0;\n" 190 << " gl_TessLevelOuter[1] = 5.0;\n" 191 << " gl_TessLevelOuter[2] = 5.0;\n" 192 << " gl_TessLevelOuter[3] = 5.0;\n" 193 << "}\n"; 194 195 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str()); 196 } 197 198 // Tessellation evaluation shader 199 { 200 std::ostringstream src; 201 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 202 << "#extension GL_EXT_tessellation_shader : require\n" 203 << "\n" 204 << "layout(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ", " 205 << getWindingShaderName(m_winding) << ") in;\n" 206 << "\n" 207 << "void main (void)\n" 208 << "{\n" 209 << " gl_Position = vec4(gl_TessCoord.xy*2.0 - 1.0, 0.0, 1.0);\n" 210 << "}\n"; 211 212 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str()); 213 } 214 215 // Fragment shader 216 { 217 std::ostringstream src; 218 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 219 << "\n" 220 << "layout(location = 0) out mediump vec4 o_color;\n" 221 << "\n" 222 << "void main (void)\n" 223 << "{\n" 224 << " o_color = vec4(1.0);\n" 225 << "}\n"; 226 227 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str()); 228 } 229} 230 231class WindingTestInstance : public TestInstance 232{ 233public: 234 WindingTestInstance (Context& context, 235 const TessPrimitiveType primitiveType, 236 const Winding winding); 237 238 tcu::TestStatus iterate (void); 239 240private: 241 const TessPrimitiveType m_primitiveType; 242 const Winding m_winding; 243}; 244 245WindingTestInstance::WindingTestInstance (Context& context, 246 const TessPrimitiveType primitiveType, 247 const Winding winding) 248 : TestInstance (context) 249 , m_primitiveType (primitiveType) 250 , m_winding (winding) 251{ 252} 253 254tcu::TestStatus WindingTestInstance::iterate (void) 255{ 256 const DeviceInterface& vk = m_context.getDeviceInterface(); 257 const VkDevice device = m_context.getDevice(); 258 const VkQueue queue = m_context.getUniversalQueue(); 259 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); 260 Allocator& allocator = m_context.getDefaultAllocator(); 261 262 // Color attachment 263 264 const tcu::IVec2 renderSize = tcu::IVec2(64, 64); 265 const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM; 266 const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u); 267 const Image colorAttachmentImage (vk, device, allocator, 268 makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u), 269 MemoryRequirement::Any); 270 271 // Color output buffer: image will be copied here for verification 272 273 const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat)); 274 const Buffer colorBuffer (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible); 275 276 // Pipeline 277 278 const Unique<VkImageView> colorAttachmentView(makeImageView (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange)); 279 const Unique<VkRenderPass> renderPass (makeRenderPass (vk, device, colorFormat)); 280 const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u)); 281 const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayoutWithoutDescriptors(vk, device)); 282 283 const VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT; 284 285 // Front face is static state, so we have to create two pipelines. 286 287 const Unique<VkPipeline> pipelineCounterClockwise(GraphicsPipelineBuilder() 288 .setRenderSize (renderSize) 289 .setCullModeFlags(cullMode) 290 .setFrontFace (VK_FRONT_FACE_COUNTER_CLOCKWISE) 291 .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL) 292 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL) 293 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL) 294 .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL) 295 .build (vk, device, *pipelineLayout, *renderPass)); 296 297 const Unique<VkPipeline> pipelineClockwise(GraphicsPipelineBuilder() 298 .setRenderSize (renderSize) 299 .setCullModeFlags(cullMode) 300 .setFrontFace (VK_FRONT_FACE_CLOCKWISE) 301 .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL) 302 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL) 303 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL) 304 .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL) 305 .build (vk, device, *pipelineLayout, *renderPass)); 306 307 const struct // not static 308 { 309 Winding frontFaceWinding; 310 VkPipeline pipeline; 311 } testCases[] = 312 { 313 { WINDING_CCW, *pipelineCounterClockwise }, 314 { WINDING_CW, *pipelineClockwise }, 315 }; 316 317 tcu::TestLog& log = m_context.getTestContext().getLog(); 318 log << tcu::TestLog::Message << "Pipeline uses " << getCullModeFlagsStr(cullMode) << tcu::TestLog::EndMessage; 319 320 bool success = true; 321 322 // Draw commands 323 324 const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex)); 325 const Unique<VkCommandBuffer> cmdBuffer(makeCommandBuffer(vk, device, *cmdPool)); 326 327 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(testCases); ++caseNdx) 328 { 329 const Winding frontFaceWinding = testCases[caseNdx].frontFaceWinding; 330 331 log << tcu::TestLog::Message << "Setting " << getFrontFaceName(mapFrontFace(frontFaceWinding)) << tcu::TestLog::EndMessage; 332 333 // Reset the command buffer and begin. 334 beginCommandBuffer(vk, *cmdBuffer); 335 336 // Change color attachment image layout 337 { 338 // State is slightly different on the first iteration. 339 const VkImageLayout currentLayout = (caseNdx == 0 ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); 340 const VkAccessFlags srcFlags = (caseNdx == 0 ? (VkAccessFlags)0 : (VkAccessFlags)VK_ACCESS_TRANSFER_READ_BIT); 341 342 const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier( 343 srcFlags, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 344 currentLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 345 *colorAttachmentImage, colorImageSubresourceRange); 346 347 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u, 348 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier); 349 } 350 351 // Begin render pass 352 { 353 const VkRect2D renderArea = { 354 makeOffset2D(0, 0), 355 makeExtent2D(renderSize.x(), renderSize.y()), 356 }; 357 const tcu::Vec4 clearColor = tcu::RGBA::red().toVec(); 358 359 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor); 360 } 361 362 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, testCases[caseNdx].pipeline); 363 364 // Process a single abstract vertex. 365 vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u); 366 endRenderPass(vk, *cmdBuffer); 367 368 // Copy render result to a host-visible buffer 369 { 370 const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier( 371 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, 372 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 373 *colorAttachmentImage, colorImageSubresourceRange); 374 375 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 376 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier); 377 } 378 { 379 const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 1), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u)); 380 vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, ©Region); 381 } 382 { 383 const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier( 384 VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes); 385 386 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 387 0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL); 388 } 389 390 endCommandBuffer(vk, *cmdBuffer); 391 submitCommandsAndWait(vk, device, queue, *cmdBuffer); 392 393 { 394 // Log rendered image 395 const Allocation& colorBufferAlloc = colorBuffer.getAllocation(); 396 invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes); 397 398 const tcu::ConstPixelBufferAccess imagePixelAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr()); 399 log << tcu::TestLog::Image("color0", "Rendered image", imagePixelAccess); 400 401 // Verify case result 402 success = success && verifyResultImage(log, imagePixelAccess, m_primitiveType, m_winding, frontFaceWinding); 403 } 404 } // for windingNdx 405 406 return (success ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure")); 407} 408 409TestInstance* WindingTest::createInstance (Context& context) const 410{ 411 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER); 412 413 return new WindingTestInstance(context, m_primitiveType, m_winding); 414} 415 416} // anonymous 417 418//! These tests correspond to dEQP-GLES31.functional.tessellation.winding.* 419tcu::TestCaseGroup* createWindingTests (tcu::TestContext& testCtx) 420{ 421 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "winding", "Test the cw and ccw input layout qualifiers")); 422 423 static const TessPrimitiveType primitivesNoIsolines[] = 424 { 425 TESSPRIMITIVETYPE_TRIANGLES, 426 TESSPRIMITIVETYPE_QUADS, 427 }; 428 429 for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitivesNoIsolines); ++primitiveTypeNdx) 430 for (int windingNdx = 0; windingNdx < WINDING_LAST; ++windingNdx) 431 group->addChild(new WindingTest(testCtx, primitivesNoIsolines[primitiveTypeNdx], (Winding)windingNdx)); 432 433 return group.release(); 434} 435 436} // tessellation 437} // vkt 438