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 Geometry Interaction - Point Size 23*//*--------------------------------------------------------------------*/ 24 25#include "vktTessellationGeometryPassthroughTests.hpp" 26#include "vktTestCaseUtil.hpp" 27#include "vktTessellationUtil.hpp" 28 29#include "tcuTestLog.hpp" 30 31#include "vkDefs.hpp" 32#include "vkQueryUtil.hpp" 33#include "vkBuilderUtil.hpp" 34#include "vkTypeUtil.hpp" 35#include "vkImageUtil.hpp" 36 37#include "deUniquePtr.hpp" 38 39#include <string> 40#include <vector> 41 42namespace vkt 43{ 44namespace tessellation 45{ 46 47using namespace vk; 48 49namespace 50{ 51 52enum Constants 53{ 54 RENDER_SIZE = 32, 55}; 56 57enum FlagBits 58{ 59 FLAG_VERTEX_SET = 1u << 0, // !< set gl_PointSize in vertex shader 60 FLAG_TESSELLATION_EVALUATION_SET = 1u << 1, // !< set gl_PointSize in tessellation evaluation shader 61 FLAG_TESSELLATION_ADD = 1u << 2, // !< read and add to gl_PointSize in tessellation shader pair 62 FLAG_GEOMETRY_SET = 1u << 3, // !< set gl_PointSize in geometry shader 63 FLAG_GEOMETRY_ADD = 1u << 4, // !< read and add to gl_PointSize in geometry shader 64}; 65typedef deUint32 Flags; 66 67void checkPointSizeRequirements (const InstanceInterface& vki, const VkPhysicalDevice physDevice, const int maxPointSize) 68{ 69 const VkPhysicalDeviceProperties properties = getPhysicalDeviceProperties(vki, physDevice); 70 if (maxPointSize > static_cast<int>(properties.limits.pointSizeRange[1])) 71 throw tcu::NotSupportedError("Test requires point size " + de::toString(maxPointSize)); 72 // Point size granularity must be 1.0 at most, so no need to check it for this test. 73} 74 75int getExpectedPointSize (const Flags flags) 76{ 77 int addition = 0; 78 79 // geometry 80 if (flags & FLAG_GEOMETRY_SET) 81 return 6; 82 else if (flags & FLAG_GEOMETRY_ADD) 83 addition += 2; 84 85 // tessellation 86 if (flags & FLAG_TESSELLATION_EVALUATION_SET) 87 return 4 + addition; 88 else if (flags & FLAG_TESSELLATION_ADD) 89 addition += 2; 90 91 // vertex 92 if (flags & FLAG_VERTEX_SET) 93 return 2 + addition; 94 95 // undefined 96 DE_ASSERT(false); 97 return -1; 98} 99 100inline bool isTessellationStage (const Flags flags) 101{ 102 return (flags & (FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD)) != 0; 103} 104 105inline bool isGeometryStage (const Flags flags) 106{ 107 return (flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD)) != 0; 108} 109 110bool verifyImage (tcu::TestLog& log, const tcu::ConstPixelBufferAccess image, const int expectedSize) 111{ 112 log << tcu::TestLog::Message << "Verifying rendered point size. Expecting " << expectedSize << " pixels." << tcu::TestLog::EndMessage; 113 114 bool resultAreaFound = false; 115 tcu::IVec4 resultArea; 116 const tcu::Vec4 black(0.0, 0.0, 0.0, 1.0); 117 118 // Find rasterization output area 119 120 for (int y = 0; y < image.getHeight(); ++y) 121 for (int x = 0; x < image.getWidth(); ++x) 122 if (image.getPixel(x, y) != black) 123 { 124 if (!resultAreaFound) 125 { 126 // first fragment 127 resultArea = tcu::IVec4(x, y, x + 1, y + 1); 128 resultAreaFound = true; 129 } 130 else 131 { 132 // union area 133 resultArea.x() = de::min(resultArea.x(), x); 134 resultArea.y() = de::min(resultArea.y(), y); 135 resultArea.z() = de::max(resultArea.z(), x+1); 136 resultArea.w() = de::max(resultArea.w(), y+1); 137 } 138 } 139 140 if (!resultAreaFound) 141 { 142 log << tcu::TestLog::Message << "Verification failed, could not find any point fragments." << tcu::TestLog::EndMessage; 143 return false; 144 } 145 146 const tcu::IVec2 pointSize = resultArea.swizzle(2,3) - resultArea.swizzle(0, 1); 147 148 if (pointSize.x() != pointSize.y()) 149 { 150 log << tcu::TestLog::Message << "ERROR! Rasterized point is not a square. Point size was " << pointSize << tcu::TestLog::EndMessage; 151 return false; 152 } 153 154 if (pointSize.x() != expectedSize) 155 { 156 log << tcu::TestLog::Message << "ERROR! Point size invalid, expected " << expectedSize << ", got " << pointSize.x() << tcu::TestLog::EndMessage; 157 return false; 158 } 159 160 return true; 161} 162 163void initPrograms (vk::SourceCollections& programCollection, const Flags flags) 164{ 165 // Vertex shader 166 { 167 std::ostringstream src; 168 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 169 << "\n" 170 << "void main (void)\n" 171 << "{\n" 172 << " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"; 173 174 if (flags & FLAG_VERTEX_SET) 175 src << " gl_PointSize = 2.0;\n"; 176 177 src << "}\n"; 178 179 programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); 180 } 181 182 // Fragment shader 183 { 184 std::ostringstream src; 185 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 186 << "layout(location = 0) out mediump vec4 fragColor;\n" 187 << "\n" 188 << "void main (void)\n" 189 << "{\n" 190 << " fragColor = vec4(1.0);\n" 191 << "}\n"; 192 193 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str()); 194 } 195 196 if (isTessellationStage(flags)) 197 { 198 // Tessellation control 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 << "#extension GL_EXT_tessellation_point_size : require\n" 204 << "layout(vertices = 1) out;\n" 205 << "\n" 206 << "void main (void)\n" 207 << "{\n" 208 << " gl_TessLevelOuter[0] = 3.0;\n" 209 << " gl_TessLevelOuter[1] = 3.0;\n" 210 << " gl_TessLevelOuter[2] = 3.0;\n" 211 << " gl_TessLevelInner[0] = 3.0;\n" 212 << "\n" 213 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"; 214 215 if (flags & FLAG_TESSELLATION_ADD) 216 src << " // pass as is to eval\n" 217 << " gl_out[gl_InvocationID].gl_PointSize = gl_in[gl_InvocationID].gl_PointSize;\n"; 218 219 src << "}\n"; 220 221 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str()); 222 } 223 224 // Tessellation evaluation shader 225 { 226 std::ostringstream src; 227 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 228 << "#extension GL_EXT_tessellation_shader : require\n" 229 << "#extension GL_EXT_tessellation_point_size : require\n" 230 << "layout(triangles, point_mode) in;\n" 231 << "\n" 232 << "void main (void)\n" 233 << "{\n" 234 << " // hide all but one vertex\n" 235 << " if (gl_TessCoord.x < 0.99)\n" 236 << " gl_Position = vec4(-2.0, 0.0, 0.0, 1.0);\n" 237 << " else\n" 238 << " gl_Position = gl_in[0].gl_Position;\n"; 239 240 if (flags & FLAG_TESSELLATION_ADD) 241 src << "\n" 242 << " // add to point size\n" 243 << " gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n"; 244 else if (flags & FLAG_TESSELLATION_EVALUATION_SET) 245 src << "\n" 246 << " // set point size\n" 247 << " gl_PointSize = 4.0;\n"; 248 249 src << "}\n"; 250 251 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str()); 252 } 253 } 254 255 if (isGeometryStage(flags)) 256 { 257 // Geometry shader 258 std::ostringstream src; 259 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 260 << "#extension GL_EXT_geometry_shader : require\n" 261 << "#extension GL_EXT_geometry_point_size : require\n" 262 << "layout(points) in;\n" 263 << "layout(points, max_vertices = 1) out;\n" 264 << "\n" 265 << "void main (void)\n" 266 << "{\n" 267 << " gl_Position = gl_in[0].gl_Position;\n"; 268 269 if (flags & FLAG_GEOMETRY_SET) 270 src << " gl_PointSize = 6.0;\n"; 271 else if (flags & FLAG_GEOMETRY_ADD) 272 src << " gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n"; 273 274 src << "\n" 275 << " EmitVertex();\n" 276 << "}\n"; 277 278 programCollection.glslSources.add("geom") << glu::GeometrySource(src.str()); 279 } 280} 281 282tcu::TestStatus test (Context& context, const Flags flags) 283{ 284 const int expectedPointSize = getExpectedPointSize(flags); 285 { 286 const InstanceInterface& vki = context.getInstanceInterface(); 287 const VkPhysicalDevice physDevice = context.getPhysicalDevice(); 288 289 requireFeatures (vki, physDevice, FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER | FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE); 290 checkPointSizeRequirements(vki, physDevice, expectedPointSize); 291 } 292 { 293 tcu::TestLog& log = context.getTestContext().getLog(); 294 295 if (flags & FLAG_VERTEX_SET) 296 log << tcu::TestLog::Message << "Setting point size in vertex shader to 2.0." << tcu::TestLog::EndMessage; 297 if (flags & FLAG_TESSELLATION_EVALUATION_SET) 298 log << tcu::TestLog::Message << "Setting point size in tessellation evaluation shader to 4.0." << tcu::TestLog::EndMessage; 299 if (flags & FLAG_TESSELLATION_ADD) 300 log << tcu::TestLog::Message << "Reading point size in tessellation control shader and adding 2.0 to it in evaluation." << tcu::TestLog::EndMessage; 301 if (flags & FLAG_GEOMETRY_SET) 302 log << tcu::TestLog::Message << "Setting point size in geometry shader to 6.0." << tcu::TestLog::EndMessage; 303 if (flags & FLAG_GEOMETRY_ADD) 304 log << tcu::TestLog::Message << "Reading point size in geometry shader and adding 2.0." << tcu::TestLog::EndMessage; 305 } 306 307 const DeviceInterface& vk = context.getDeviceInterface(); 308 const VkDevice device = context.getDevice(); 309 const VkQueue queue = context.getUniversalQueue(); 310 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); 311 Allocator& allocator = context.getDefaultAllocator(); 312 313 // Color attachment 314 315 const tcu::IVec2 renderSize = tcu::IVec2(RENDER_SIZE, RENDER_SIZE); 316 const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM; 317 const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u); 318 const Image colorAttachmentImage (vk, device, allocator, 319 makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u), 320 MemoryRequirement::Any); 321 322 // Color output buffer 323 324 const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat)); 325 const Buffer colorBuffer (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible); 326 327 // Pipeline 328 329 const Unique<VkImageView> colorAttachmentView(makeImageView (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange)); 330 const Unique<VkRenderPass> renderPass (makeRenderPass (vk, device, colorFormat)); 331 const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u)); 332 const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayoutWithoutDescriptors(vk, device)); 333 const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex)); 334 const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer (vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY)); 335 336 GraphicsPipelineBuilder pipelineBuilder; 337 338 pipelineBuilder 339 .setPrimitiveTopology (VK_PRIMITIVE_TOPOLOGY_POINT_LIST) 340 .setRenderSize (renderSize) 341 .setPatchControlPoints (1) 342 .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"), DE_NULL) 343 .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"), DE_NULL); 344 345 if (isTessellationStage(flags)) 346 pipelineBuilder 347 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, context.getBinaryCollection().get("tesc"), DE_NULL) 348 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL); 349 350 if (isGeometryStage(flags)) 351 pipelineBuilder 352 .setShader (vk, device, VK_SHADER_STAGE_GEOMETRY_BIT, context.getBinaryCollection().get("geom"), DE_NULL); 353 354 const Unique<VkPipeline> pipeline(pipelineBuilder.build(vk, device, *pipelineLayout, *renderPass)); 355 356 // Draw commands 357 358 beginCommandBuffer(vk, *cmdBuffer); 359 360 { 361 const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier( 362 (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 363 VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 364 *colorAttachmentImage, colorImageSubresourceRange); 365 366 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u, 367 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier); 368 } 369 370 // Begin render pass 371 { 372 const VkRect2D renderArea = { 373 makeOffset2D(0, 0), 374 makeExtent2D(renderSize.x(), renderSize.y()), 375 }; 376 const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f); 377 378 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor); 379 } 380 381 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); 382 383 vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u); 384 endRenderPass(vk, *cmdBuffer); 385 386 // Copy render result to a host-visible buffer 387 { 388 const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier( 389 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, 390 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 391 *colorAttachmentImage, colorImageSubresourceRange); 392 393 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 394 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier); 395 } 396 { 397 const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 1), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u)); 398 vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, ©Region); 399 } 400 { 401 const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier( 402 VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes); 403 404 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 405 0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL); 406 } 407 408 endCommandBuffer(vk, *cmdBuffer); 409 submitCommandsAndWait(vk, device, queue, *cmdBuffer); 410 411 // Verify results 412 { 413 const Allocation& alloc = colorBuffer.getAllocation(); 414 invalidateMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), colorBufferSizeBytes); 415 tcu::ConstPixelBufferAccess image(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, alloc.getHostPtr()); 416 417 tcu::TestLog& log = context.getTestContext().getLog(); 418 log << tcu::LogImage("color0", "", image); 419 420 if (verifyImage(log, image, expectedPointSize)) 421 return tcu::TestStatus::pass("OK"); 422 else 423 return tcu::TestStatus::fail("Didn't render expected point"); 424 } 425} 426 427std::string getTestCaseName (const Flags flags) 428{ 429 std::ostringstream buf; 430 431 // join per-bit descriptions into a single string with '_' separator 432 if (flags & FLAG_VERTEX_SET) buf << "vertex_set"; 433 if (flags & FLAG_TESSELLATION_EVALUATION_SET) buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET-1)) ? ("_") : ("")) << "evaluation_set"; 434 if (flags & FLAG_TESSELLATION_ADD) buf << ((flags & (FLAG_TESSELLATION_ADD-1)) ? ("_") : ("")) << "control_pass_eval_add"; 435 if (flags & FLAG_GEOMETRY_SET) buf << ((flags & (FLAG_GEOMETRY_SET-1)) ? ("_") : ("")) << "geometry_set"; 436 if (flags & FLAG_GEOMETRY_ADD) buf << ((flags & (FLAG_GEOMETRY_ADD-1)) ? ("_") : ("")) << "geometry_add"; 437 438 return buf.str(); 439} 440 441std::string getTestCaseDescription (const Flags flags) 442{ 443 std::ostringstream buf; 444 445 // join per-bit descriptions into a single string with ", " separator 446 if (flags & FLAG_VERTEX_SET) buf << "set point size in vertex shader"; 447 if (flags & FLAG_TESSELLATION_EVALUATION_SET) buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET-1)) ? (", ") : ("")) << "set point size in tessellation evaluation shader"; 448 if (flags & FLAG_TESSELLATION_ADD) buf << ((flags & (FLAG_TESSELLATION_ADD-1)) ? (", ") : ("")) << "add to point size in tessellation shader"; 449 if (flags & FLAG_GEOMETRY_SET) buf << ((flags & (FLAG_GEOMETRY_SET-1)) ? (", ") : ("")) << "set point size in geometry shader"; 450 if (flags & FLAG_GEOMETRY_ADD) buf << ((flags & (FLAG_GEOMETRY_ADD-1)) ? (", ") : ("")) << "add to point size in geometry shader"; 451 452 return buf.str(); 453} 454 455} // anonymous 456 457//! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.point_size.* 458//! with the exception of the default 1.0 point size cases (not valid in Vulkan). 459tcu::TestCaseGroup* createGeometryPointSizeTests (tcu::TestContext& testCtx) 460{ 461 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "point_size", "Test point size")); 462 463 static const Flags caseFlags[] = 464 { 465 FLAG_VERTEX_SET, 466 FLAG_TESSELLATION_EVALUATION_SET, 467 FLAG_GEOMETRY_SET, 468 FLAG_VERTEX_SET | FLAG_TESSELLATION_EVALUATION_SET, 469 FLAG_VERTEX_SET | FLAG_GEOMETRY_SET, 470 FLAG_VERTEX_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_GEOMETRY_SET, 471 FLAG_VERTEX_SET | FLAG_TESSELLATION_ADD | FLAG_GEOMETRY_ADD, 472 }; 473 474 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseFlags); ++ndx) 475 { 476 const std::string name = getTestCaseName (caseFlags[ndx]); 477 const std::string desc = getTestCaseDescription(caseFlags[ndx]); 478 479 addFunctionCaseWithPrograms(group.get(), name, desc, initPrograms, test, caseFlags[ndx]); 480 } 481 482 return group.release(); 483} 484 485} // tessellation 486} // vkt 487