1 /*------------------------------------------------------------------------ 2 * Vulkan Conformance Tests 3 * ------------------------ 4 * 5 * Copyright (c) 2016 The Khronos Group Inc. 6 * Copyright (c) 2014 The Android Open Source Project 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 Geometry shader instanced rendering tests 23 *//*--------------------------------------------------------------------*/ 24 25 #include "vktGeometryInstancedRenderingTests.hpp" 26 #include "vktTestCase.hpp" 27 #include "vktTestCaseUtil.hpp" 28 #include "vktGeometryTestsUtil.hpp" 29 30 #include "vkPrograms.hpp" 31 #include "vkQueryUtil.hpp" 32 #include "vkMemUtil.hpp" 33 #include "vkRefUtil.hpp" 34 #include "vkTypeUtil.hpp" 35 #include "vkImageUtil.hpp" 36 #include "vkCmdUtil.hpp" 37 #include "vkObjUtil.hpp" 38 39 #include "tcuTextureUtil.hpp" 40 #include "tcuImageCompare.hpp" 41 #include "tcuTestLog.hpp" 42 43 #include "deRandom.hpp" 44 #include "deMath.h" 45 46 namespace vkt 47 { 48 namespace geometry 49 { 50 namespace 51 { 52 using namespace vk; 53 using de::MovePtr; 54 using de::UniquePtr; 55 using tcu::Vec4; 56 using tcu::UVec2; 57 58 struct TestParams 59 { 60 int numDrawInstances; 61 int numInvocations; 62 }; 63 64 VkImageCreateInfo makeImageCreateInfo (const VkFormat format, const VkExtent3D size, const VkImageUsageFlags usage) 65 { 66 const VkImageCreateInfo imageParams = 67 { 68 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; 69 DE_NULL, // const void* pNext; 70 (VkImageCreateFlags)0, // VkImageCreateFlags flags; 71 VK_IMAGE_TYPE_2D, // VkImageType imageType; 72 format, // VkFormat format; 73 size, // VkExtent3D extent; 74 1u, // deUint32 mipLevels; 75 1u, // deUint32 arrayLayers; 76 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; 77 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; 78 usage, // VkImageUsageFlags usage; 79 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; 80 0u, // deUint32 queueFamilyIndexCount; 81 DE_NULL, // const deUint32* pQueueFamilyIndices; 82 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; 83 }; 84 return imageParams; 85 } 86 87 Move<VkPipeline> makeGraphicsPipeline (const DeviceInterface& vk, 88 const VkDevice device, 89 const VkPipelineLayout pipelineLayout, 90 const VkRenderPass renderPass, 91 const VkShaderModule vertexModule, 92 const VkShaderModule geometryModule, 93 const VkShaderModule fragmentModule, 94 const VkExtent2D renderSize) 95 { 96 const std::vector<VkViewport> viewports (1, makeViewport(renderSize)); 97 const std::vector<VkRect2D> scissors (1, makeRect2D(renderSize)); 98 99 const VkVertexInputBindingDescription vertexInputBindingDescription = 100 { 101 0u, // deUint32 binding; 102 sizeof(Vec4), // deUint32 stride; 103 VK_VERTEX_INPUT_RATE_INSTANCE // VkVertexInputRate inputRate; 104 }; 105 106 const VkVertexInputAttributeDescription vertexInputAttributeDescription = 107 { 108 0u, // deUint32 location; 109 0u, // deUint32 binding; 110 VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format; 111 0u // deUint32 offset; 112 }; 113 114 const VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = 115 { 116 VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType; 117 DE_NULL, // const void* pNext; 118 (VkPipelineVertexInputStateCreateFlags)0, // VkPipelineVertexInputStateCreateFlags flags; 119 1u, // deUint32 vertexBindingDescriptionCount; 120 &vertexInputBindingDescription, // const VkVertexInputBindingDescription* pVertexBindingDescriptions; 121 1u, // deUint32 vertexAttributeDescriptionCount; 122 &vertexInputAttributeDescription // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions; 123 }; 124 125 return vk::makeGraphicsPipeline(vk, // const DeviceInterface& vk 126 device, // const VkDevice device 127 pipelineLayout, // const VkPipelineLayout pipelineLayout 128 vertexModule, // const VkShaderModule vertexShaderModule 129 DE_NULL, // const VkShaderModule tessellationControlModule 130 DE_NULL, // const VkShaderModule tessellationEvalModule 131 geometryModule, // const VkShaderModule geometryShaderModule 132 fragmentModule, // const VkShaderModule fragmentShaderModule 133 renderPass, // const VkRenderPass renderPass 134 viewports, // const std::vector<VkViewport>& viewports 135 scissors, // const std::vector<VkRect2D>& scissors 136 VK_PRIMITIVE_TOPOLOGY_POINT_LIST, // const VkPrimitiveTopology topology 137 0u, // const deUint32 subpass 138 0u, // const deUint32 patchControlPoints 139 &vertexInputStateCreateInfo); // const VkPipelineVertexInputStateCreateInfo* vertexInputStateCreateInfo 140 } 141 142 void draw (Context& context, 143 const UVec2& renderSize, 144 const VkFormat colorFormat, 145 const Vec4& clearColor, 146 const VkBuffer colorBuffer, 147 const int numDrawInstances, 148 const std::vector<Vec4>& perInstanceAttribute) 149 { 150 const DeviceInterface& vk = context.getDeviceInterface(); 151 const VkDevice device = context.getDevice(); 152 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); 153 const VkQueue queue = context.getUniversalQueue(); 154 Allocator& allocator = context.getDefaultAllocator(); 155 156 const VkImageSubresourceRange colorSubresourceRange (makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u)); 157 const VkExtent3D colorImageExtent (makeExtent3D(renderSize.x(), renderSize.y(), 1u)); 158 const VkExtent2D renderExtent (makeExtent2D(renderSize.x(), renderSize.y())); 159 160 const Unique<VkImage> colorImage (makeImage (vk, device, makeImageCreateInfo(colorFormat, colorImageExtent, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT))); 161 const UniquePtr<Allocation> colorImageAlloc (bindImage (vk, device, allocator, *colorImage, MemoryRequirement::Any)); 162 const Unique<VkImageView> colorAttachment (makeImageView (vk, device, *colorImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorSubresourceRange)); 163 164 const VkDeviceSize vertexBufferSize = sizeInBytes(perInstanceAttribute); 165 const Unique<VkBuffer> vertexBuffer (makeBuffer(vk, device, makeBufferCreateInfo(vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT))); 166 const UniquePtr<Allocation> vertexBufferAlloc (bindBuffer(vk, device, allocator, *vertexBuffer, MemoryRequirement::HostVisible)); 167 168 const Unique<VkShaderModule> vertexModule (createShaderModule (vk, device, context.getBinaryCollection().get("vert"), 0u)); 169 const Unique<VkShaderModule> geometryModule (createShaderModule (vk, device, context.getBinaryCollection().get("geom"), 0u)); 170 const Unique<VkShaderModule> fragmentModule (createShaderModule (vk, device, context.getBinaryCollection().get("frag"), 0u)); 171 172 const Unique<VkRenderPass> renderPass (vk::makeRenderPass (vk, device, colorFormat)); 173 const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, *colorAttachment, renderSize.x(), renderSize.y(), 1u)); 174 const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout (vk, device)); 175 const Unique<VkPipeline> pipeline (makeGraphicsPipeline (vk, device, *pipelineLayout, *renderPass, *vertexModule, *geometryModule, *fragmentModule, renderExtent)); 176 177 const Unique<VkCommandPool> cmdPool (createCommandPool (vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex)); 178 const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer (vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY)); 179 180 // Initialize vertex data 181 { 182 deMemcpy(vertexBufferAlloc->getHostPtr(), &perInstanceAttribute[0], (size_t)vertexBufferSize); 183 flushMappedMemoryRange(vk, device, vertexBufferAlloc->getMemory(), vertexBufferAlloc->getOffset(), vertexBufferSize); 184 } 185 186 beginCommandBuffer(vk, *cmdBuffer); 187 188 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, makeRect2D(renderExtent), clearColor); 189 190 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); 191 { 192 const VkDeviceSize offset = 0ull; 193 vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &offset); 194 } 195 vk.cmdDraw(*cmdBuffer, 1u, static_cast<deUint32>(numDrawInstances), 0u, 0u); 196 endRenderPass(vk, *cmdBuffer); 197 198 copyImageToBuffer(vk, *cmdBuffer, *colorImage, colorBuffer, tcu::IVec2(renderSize.x(), renderSize.y())); 199 200 endCommandBuffer(vk, *cmdBuffer); 201 submitCommandsAndWait(vk, device, queue, *cmdBuffer); 202 } 203 204 std::vector<Vec4> generatePerInstancePosition (const int numInstances) 205 { 206 de::Random rng(1234); 207 std::vector<Vec4> positions; 208 209 for (int i = 0; i < numInstances; ++i) 210 { 211 const float flipX = rng.getBool() ? 1.0f : -1.0f; 212 const float flipY = rng.getBool() ? 1.0f : -1.0f; 213 const float x = flipX * rng.getFloat(0.1f, 0.9f); // x mustn't be 0.0, because we are using sign() in the shader 214 const float y = flipY * rng.getFloat(0.0f, 0.7f); 215 216 positions.push_back(Vec4(x, y, 0.0f, 1.0f)); 217 } 218 219 return positions; 220 } 221 222 //! Get a rectangle region of an image, using NDC coordinates (i.e. [-1, 1] range). 223 //! Result rect is cropped in either dimension to be inside the bounds of the image. 224 tcu::PixelBufferAccess getSubregion (tcu::PixelBufferAccess image, const float x, const float y, const float size) 225 { 226 const float w = static_cast<float>(image.getWidth()); 227 const float h = static_cast<float>(image.getHeight()); 228 const float x1 = w * (x + 1.0f) * 0.5f; 229 const float y1 = h * (y + 1.0f) * 0.5f; 230 const float sx = w * size * 0.5f; 231 const float sy = h * size * 0.5f; 232 const float x2 = x1 + sx; 233 const float y2 = y1 + sy; 234 235 // Round and clamp only after all of the above. 236 const int ix1 = std::max(deRoundFloatToInt32(x1), 0); 237 const int ix2 = std::min(deRoundFloatToInt32(x2), image.getWidth()); 238 const int iy1 = std::max(deRoundFloatToInt32(y1), 0); 239 const int iy2 = std::min(deRoundFloatToInt32(y2), image.getHeight()); 240 241 return tcu::getSubregion(image, ix1, iy1, ix2 - ix1, iy2 - iy1); 242 } 243 244 //! Must be in sync with the geometry shader code. 245 void generateReferenceImage(tcu::PixelBufferAccess image, const Vec4& clearColor, const std::vector<Vec4>& perInstancePosition, const int numInvocations) 246 { 247 tcu::clear(image, clearColor); 248 249 for (std::vector<Vec4>::const_iterator iterPosition = perInstancePosition.begin(); iterPosition != perInstancePosition.end(); ++iterPosition) 250 for (int invocationNdx = 0; invocationNdx < numInvocations; ++invocationNdx) 251 { 252 const float x = iterPosition->x(); 253 const float y = iterPosition->y(); 254 const float modifier = (numInvocations > 1 ? static_cast<float>(invocationNdx) / static_cast<float>(numInvocations - 1) : 0.0f); 255 const Vec4 color (deFloatAbs(x), deFloatAbs(y), 0.2f + 0.8f * modifier, 1.0f); 256 const float size = 0.05f + 0.03f * modifier; 257 const float dx = (deFloatSign(-x) - x) / static_cast<float>(numInvocations); 258 const float xOffset = static_cast<float>(invocationNdx) * dx; 259 const float yOffset = 0.3f * deFloatSin(12.0f * modifier); 260 261 tcu::PixelBufferAccess rect = getSubregion(image, x + xOffset - size, y + yOffset - size, size + size); 262 tcu::clear(rect, color); 263 } 264 } 265 266 void initPrograms (SourceCollections& programCollection, const TestParams params) 267 { 268 // Vertex shader 269 { 270 std::ostringstream src; 271 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 272 << "\n" 273 << "layout(location = 0) in vec4 in_position;\n" 274 << "\n" 275 << "out gl_PerVertex {\n" 276 << " vec4 gl_Position;\n" 277 << "};\n" 278 << "\n" 279 << "void main(void)\n" 280 << "{\n" 281 << " gl_Position = in_position;\n" 282 << "}\n"; 283 284 programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); 285 } 286 287 // Geometry shader 288 { 289 // The shader must be in sync with reference image rendering routine. 290 291 std::ostringstream src; 292 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 293 << "\n" 294 << "layout(points, invocations = " << params.numInvocations << ") in;\n" 295 << "layout(triangle_strip, max_vertices = 4) out;\n" 296 << "\n" 297 << "layout(location = 0) out vec4 out_color;\n" 298 << "\n" 299 << "in gl_PerVertex {\n" 300 << " vec4 gl_Position;\n" 301 << "} gl_in[];\n" 302 << "\n" 303 << "out gl_PerVertex {\n" 304 << " vec4 gl_Position;\n" 305 << "};\n" 306 << "\n" 307 << "void main(void)\n" 308 << "{\n" 309 << " const vec4 pos = gl_in[0].gl_Position;\n" 310 << " const float modifier = " << (params.numInvocations > 1 ? "float(gl_InvocationID) / float(" + de::toString(params.numInvocations - 1) + ")" : "0.0") << ";\n" 311 << " const vec4 color = vec4(abs(pos.x), abs(pos.y), 0.2 + 0.8 * modifier, 1.0);\n" 312 << " const float size = 0.05 + 0.03 * modifier;\n" 313 << " const float dx = (sign(-pos.x) - pos.x) / float(" << params.numInvocations << ");\n" 314 << " const vec4 offsetPos = pos + vec4(float(gl_InvocationID) * dx,\n" 315 << " 0.3 * sin(12.0 * modifier),\n" 316 << " 0.0,\n" 317 << " 0.0);\n" 318 << "\n" 319 << " gl_Position = offsetPos + vec4(-size, -size, 0.0, 0.0);\n" 320 << " out_color = color;\n" 321 << " EmitVertex();\n" 322 << "\n" 323 << " gl_Position = offsetPos + vec4(-size, size, 0.0, 0.0);\n" 324 << " out_color = color;\n" 325 << " EmitVertex();\n" 326 << "\n" 327 << " gl_Position = offsetPos + vec4( size, -size, 0.0, 0.0);\n" 328 << " out_color = color;\n" 329 << " EmitVertex();\n" 330 << "\n" 331 << " gl_Position = offsetPos + vec4( size, size, 0.0, 0.0);\n" 332 << " out_color = color;\n" 333 << " EmitVertex();\n" 334 << "}\n"; 335 336 programCollection.glslSources.add("geom") << glu::GeometrySource(src.str()); 337 } 338 339 // Fragment shader 340 { 341 std::ostringstream src; 342 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 343 << "\n" 344 << "layout(location = 0) in vec4 in_color;\n" 345 << "layout(location = 0) out vec4 o_color;\n" 346 << "\n" 347 << "void main(void)\n" 348 << "{\n" 349 << " o_color = in_color;\n" 350 << "}\n"; 351 352 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str()); 353 } 354 } 355 356 tcu::TestStatus test (Context& context, const TestParams params) 357 { 358 const DeviceInterface& vk = context.getDeviceInterface(); 359 const InstanceInterface& vki = context.getInstanceInterface(); 360 const VkDevice device = context.getDevice(); 361 const VkPhysicalDevice physDevice = context.getPhysicalDevice(); 362 Allocator& allocator = context.getDefaultAllocator(); 363 364 checkGeometryShaderSupport(vki, physDevice, params.numInvocations); 365 366 const UVec2 renderSize (128u, 128u); 367 const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM; 368 const Vec4 clearColor = Vec4(0.0f, 0.0f, 0.0f, 1.0f); 369 370 const VkDeviceSize colorBufferSize = renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat)); 371 const Unique<VkBuffer> colorBuffer (makeBuffer(vk, device, makeBufferCreateInfo(colorBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT))); 372 const UniquePtr<Allocation> colorBufferAlloc (bindBuffer(vk, device, allocator, *colorBuffer, MemoryRequirement::HostVisible)); 373 374 const std::vector<Vec4> perInstancePosition = generatePerInstancePosition(params.numDrawInstances); 375 376 { 377 context.getTestContext().getLog() 378 << tcu::TestLog::Message << "Rendering " << params.numDrawInstances << " instance(s) of colorful quads." << tcu::TestLog::EndMessage 379 << tcu::TestLog::Message << "Drawing " << params.numInvocations << " quad(s), each drawn by a geometry shader invocation." << tcu::TestLog::EndMessage; 380 } 381 382 zeroBuffer(vk, device, *colorBufferAlloc, colorBufferSize); 383 draw(context, renderSize, colorFormat, clearColor, *colorBuffer, params.numDrawInstances, perInstancePosition); 384 385 // Compare result 386 { 387 invalidateMappedMemoryRange(vk, device, colorBufferAlloc->getMemory(), colorBufferAlloc->getOffset(), colorBufferSize); 388 const tcu::ConstPixelBufferAccess result(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1u, colorBufferAlloc->getHostPtr()); 389 390 tcu::TextureLevel reference(mapVkFormat(colorFormat), renderSize.x(), renderSize.y()); 391 generateReferenceImage(reference.getAccess(), clearColor, perInstancePosition, params.numInvocations); 392 393 if (!tcu::fuzzyCompare(context.getTestContext().getLog(), "Image Compare", "Image Compare", reference.getAccess(), result, 0.01f, tcu::COMPARE_LOG_RESULT)) 394 return tcu::TestStatus::fail("Rendered image is incorrect"); 395 else 396 return tcu::TestStatus::pass("OK"); 397 } 398 } 399 400 } // anonymous 401 402 //! \note CTS requires shaders to be known ahead of time (some platforms use precompiled shaders), so we can't query a limit at runtime and generate 403 //! a shader based on that. This applies to number of GS invocations which can't be injected into the shader. 404 tcu::TestCaseGroup* createInstancedRenderingTests (tcu::TestContext& testCtx) 405 { 406 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "instanced", "Instanced rendering tests.")); 407 408 const int drawInstanceCases[] = 409 { 410 1, 2, 4, 8, 411 }; 412 const int invocationCases[] = 413 { 414 1, 2, 8, 32, // required by the Vulkan spec 415 64, 127, // larger than the minimum, but perhaps some implementations support it, so we'll try 416 }; 417 418 for (const int* pNumDrawInstances = drawInstanceCases; pNumDrawInstances != drawInstanceCases + DE_LENGTH_OF_ARRAY(drawInstanceCases); ++pNumDrawInstances) 419 for (const int* pNumInvocations = invocationCases; pNumInvocations != invocationCases + DE_LENGTH_OF_ARRAY(invocationCases); ++pNumInvocations) 420 { 421 std::ostringstream caseName; 422 caseName << "draw_" << *pNumDrawInstances << "_instances_" << *pNumInvocations << "_geometry_invocations"; 423 424 const TestParams params = 425 { 426 *pNumDrawInstances, 427 *pNumInvocations, 428 }; 429 430 addFunctionCaseWithPrograms(group.get(), caseName.str(), "", initPrograms, test, params); 431 } 432 433 return group.release(); 434 } 435 436 } // geometry 437 } // vkt 438