| #ifndef _VKTTESSELLATIONUTIL_HPP |
| #define _VKTTESSELLATIONUTIL_HPP |
| /*------------------------------------------------------------------------ |
| * Vulkan Conformance Tests |
| * ------------------------ |
| * |
| * Copyright (c) 2014 The Android Open Source Project |
| * Copyright (c) 2016 The Khronos Group 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 Tessellation Utilities |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "vkDefs.hpp" |
| #include "vkMemUtil.hpp" |
| #include "vkRef.hpp" |
| #include "vkPrograms.hpp" |
| #include "vkRefUtil.hpp" |
| #include "vkQueryUtil.hpp" |
| #include "vkObjUtil.hpp" |
| #include "vktTestCase.hpp" |
| |
| #include "tcuVector.hpp" |
| #include "tcuMaybe.hpp" |
| |
| #include "deStringUtil.hpp" |
| |
| #include <algorithm> // sort |
| #include <iterator> // distance |
| |
| namespace vkt |
| { |
| namespace tessellation |
| { |
| |
| class Buffer |
| { |
| public: |
| Buffer (const vk::DeviceInterface& vk, |
| const vk::VkDevice device, |
| vk::Allocator& allocator, |
| const vk::VkBufferCreateInfo& bufferCreateInfo, |
| const vk::MemoryRequirement memoryRequirement) |
| |
| : m_buffer (createBuffer(vk, device, &bufferCreateInfo)) |
| , m_allocation (allocator.allocate(getBufferMemoryRequirements(vk, device, *m_buffer), memoryRequirement)) |
| { |
| VK_CHECK(vk.bindBufferMemory(device, *m_buffer, m_allocation->getMemory(), m_allocation->getOffset())); |
| } |
| |
| const vk::VkBuffer& get (void) const { return *m_buffer; } |
| const vk::VkBuffer& operator* (void) const { return get(); } |
| vk::Allocation& getAllocation (void) const { return *m_allocation; } |
| |
| private: |
| const vk::Unique<vk::VkBuffer> m_buffer; |
| const de::UniquePtr<vk::Allocation> m_allocation; |
| |
| // "deleted" |
| Buffer (const Buffer&); |
| Buffer& operator= (const Buffer&); |
| }; |
| |
| class Image |
| { |
| public: |
| Image (const vk::DeviceInterface& vk, |
| const vk::VkDevice device, |
| vk::Allocator& allocator, |
| const vk::VkImageCreateInfo& imageCreateInfo, |
| const vk::MemoryRequirement memoryRequirement) |
| |
| : m_image (createImage(vk, device, &imageCreateInfo)) |
| , m_allocation (allocator.allocate(getImageMemoryRequirements(vk, device, *m_image), memoryRequirement)) |
| { |
| VK_CHECK(vk.bindImageMemory(device, *m_image, m_allocation->getMemory(), m_allocation->getOffset())); |
| } |
| |
| const vk::VkImage& get (void) const { return *m_image; } |
| const vk::VkImage& operator* (void) const { return get(); } |
| vk::Allocation& getAllocation (void) const { return *m_allocation; } |
| |
| private: |
| const vk::Unique<vk::VkImage> m_image; |
| const de::UniquePtr<vk::Allocation> m_allocation; |
| |
| // "deleted" |
| Image (const Image&); |
| Image& operator= (const Image&); |
| }; |
| |
| class GraphicsPipelineBuilder |
| { |
| public: |
| GraphicsPipelineBuilder (void) : m_renderSize (0, 0) |
| , m_shaderStageFlags (0u) |
| , m_cullModeFlags (vk::VK_CULL_MODE_NONE) |
| , m_frontFace (vk::VK_FRONT_FACE_COUNTER_CLOCKWISE) |
| , m_patchControlPoints (1u) |
| , m_blendEnable (false) |
| , m_primitiveTopology (vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) |
| , m_tessellationDomainOrigin (tcu::nothing<vk::VkTessellationDomainOrigin>()) {} |
| |
| GraphicsPipelineBuilder& setRenderSize (const tcu::IVec2& size) { m_renderSize = size; return *this; } |
| GraphicsPipelineBuilder& setShader (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkShaderStageFlagBits stage, const vk::ProgramBinary& binary, const vk::VkSpecializationInfo* specInfo); |
| GraphicsPipelineBuilder& setPatchControlPoints (const deUint32 controlPoints) { m_patchControlPoints = controlPoints; return *this; } |
| GraphicsPipelineBuilder& setCullModeFlags (const vk::VkCullModeFlags cullModeFlags) { m_cullModeFlags = cullModeFlags; return *this; } |
| GraphicsPipelineBuilder& setFrontFace (const vk::VkFrontFace frontFace) { m_frontFace = frontFace; return *this; } |
| GraphicsPipelineBuilder& setBlend (const bool enable) { m_blendEnable = enable; return *this; } |
| |
| //! Applies only to pipelines without tessellation shaders. |
| GraphicsPipelineBuilder& setPrimitiveTopology (const vk::VkPrimitiveTopology topology) { m_primitiveTopology = topology; return *this; } |
| |
| GraphicsPipelineBuilder& addVertexBinding (const vk::VkVertexInputBindingDescription vertexBinding) { m_vertexInputBindings.push_back(vertexBinding); return *this; } |
| GraphicsPipelineBuilder& addVertexAttribute (const vk::VkVertexInputAttributeDescription vertexAttribute) { m_vertexInputAttributes.push_back(vertexAttribute); return *this; } |
| |
| //! Basic vertex input configuration (uses biding 0, location 0, etc.) |
| GraphicsPipelineBuilder& setVertexInputSingleAttribute (const vk::VkFormat vertexFormat, const deUint32 stride); |
| |
| //! If tessellation domain origin is set, pipeline requires VK__maintenance2 |
| GraphicsPipelineBuilder& setTessellationDomainOrigin (const vk::VkTessellationDomainOrigin domainOrigin) { return setTessellationDomainOrigin(tcu::just(domainOrigin)); } |
| GraphicsPipelineBuilder& setTessellationDomainOrigin (const tcu::Maybe<vk::VkTessellationDomainOrigin>& domainOrigin) { m_tessellationDomainOrigin = domainOrigin; return *this; } |
| |
| vk::Move<vk::VkPipeline> build (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkPipelineLayout pipelineLayout, const vk::VkRenderPass renderPass); |
| |
| private: |
| tcu::IVec2 m_renderSize; |
| vk::Move<vk::VkShaderModule> m_vertexShaderModule; |
| vk::Move<vk::VkShaderModule> m_fragmentShaderModule; |
| vk::Move<vk::VkShaderModule> m_geometryShaderModule; |
| vk::Move<vk::VkShaderModule> m_tessControlShaderModule; |
| vk::Move<vk::VkShaderModule> m_tessEvaluationShaderModule; |
| std::vector<vk::VkPipelineShaderStageCreateInfo> m_shaderStages; |
| std::vector<vk::VkVertexInputBindingDescription> m_vertexInputBindings; |
| std::vector<vk::VkVertexInputAttributeDescription> m_vertexInputAttributes; |
| vk::VkShaderStageFlags m_shaderStageFlags; |
| vk::VkCullModeFlags m_cullModeFlags; |
| vk::VkFrontFace m_frontFace; |
| deUint32 m_patchControlPoints; |
| bool m_blendEnable; |
| vk::VkPrimitiveTopology m_primitiveTopology; |
| tcu::Maybe<vk::VkTessellationDomainOrigin> m_tessellationDomainOrigin; |
| |
| GraphicsPipelineBuilder (const GraphicsPipelineBuilder&); // "deleted" |
| GraphicsPipelineBuilder& operator= (const GraphicsPipelineBuilder&); |
| }; |
| |
| struct TessLevels |
| { |
| float inner[2]; |
| float outer[4]; |
| }; |
| |
| enum TessPrimitiveType |
| { |
| TESSPRIMITIVETYPE_TRIANGLES = 0, |
| TESSPRIMITIVETYPE_QUADS, |
| TESSPRIMITIVETYPE_ISOLINES, |
| |
| TESSPRIMITIVETYPE_LAST, |
| }; |
| |
| enum SpacingMode |
| { |
| SPACINGMODE_EQUAL = 0, |
| SPACINGMODE_FRACTIONAL_ODD, |
| SPACINGMODE_FRACTIONAL_EVEN, |
| |
| SPACINGMODE_LAST, |
| }; |
| |
| enum Winding |
| { |
| WINDING_CCW = 0, |
| WINDING_CW, |
| |
| WINDING_LAST, |
| }; |
| |
| enum ShaderLanguage |
| { |
| SHADER_LANGUAGE_GLSL = 0, |
| SHADER_LANGUAGE_HLSL = 1, |
| |
| SHADER_LANGUAGE_LAST, |
| }; |
| |
| enum FeatureFlagBits |
| { |
| FEATURE_TESSELLATION_SHADER = 1u << 0, |
| FEATURE_GEOMETRY_SHADER = 1u << 1, |
| FEATURE_SHADER_FLOAT_64 = 1u << 2, |
| FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS = 1u << 3, |
| FEATURE_FRAGMENT_STORES_AND_ATOMICS = 1u << 4, |
| FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE = 1u << 5, |
| }; |
| typedef deUint32 FeatureFlags; |
| |
| vk::VkImageCreateInfo makeImageCreateInfo (const tcu::IVec2& size, const vk::VkFormat format, const vk::VkImageUsageFlags usage, const deUint32 numArrayLayers); |
| vk::Move<vk::VkPipeline> makeComputePipeline (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkPipelineLayout pipelineLayout, const vk::VkShaderModule shaderModule, const vk::VkSpecializationInfo* specInfo); |
| vk::Move<vk::VkRenderPass> makeRenderPassWithoutAttachments (const vk::DeviceInterface& vk, const vk::VkDevice device); |
| vk::VkBufferImageCopy makeBufferImageCopy (const vk::VkExtent3D extent, const vk::VkImageSubresourceLayers subresourceLayers); |
| void beginRenderPassWithRasterizationDisabled (const vk::DeviceInterface& vk, const vk::VkCommandBuffer commandBuffer, const vk::VkRenderPass renderPass, const vk::VkFramebuffer framebuffer); |
| void requireFeatures (const vk::InstanceInterface& vki, const vk::VkPhysicalDevice physDevice, const FeatureFlags flags); |
| float getClampedTessLevel (const SpacingMode mode, const float tessLevel); |
| int getRoundedTessLevel (const SpacingMode mode, const float clampedTessLevel); |
| int getClampedRoundedTessLevel (const SpacingMode mode, const float tessLevel); |
| void getClampedRoundedTriangleTessLevels (const SpacingMode mode, const float* innerSrc, const float* outerSrc, int* innerDst, int* outerDst); |
| void getClampedRoundedQuadTessLevels (const SpacingMode mode, const float* innerSrc, const float* outerSrc, int* innerDst, int* outerDst); |
| void getClampedRoundedIsolineTessLevels (const SpacingMode mode, const float* outerSrc, int* outerDst); |
| int numOuterTessellationLevels (const TessPrimitiveType primitiveType); |
| std::string getTessellationLevelsString (const TessLevels& tessLevels, const TessPrimitiveType primitiveType); |
| std::string getTessellationLevelsString (const float* inner, const float* outer); |
| bool isPatchDiscarded (const TessPrimitiveType primitiveType, const float* outerLevels); |
| std::vector<tcu::Vec3> generateReferenceTriangleTessCoords (const SpacingMode spacingMode, const int inner, const int outer0, const int outer1, const int outer2); |
| std::vector<tcu::Vec3> generateReferenceQuadTessCoords (const SpacingMode spacingMode, const int inner0, const int inner1, const int outer0, const int outer1, const int outer2, const int outer3); |
| std::vector<tcu::Vec3> generateReferenceIsolineTessCoords (const int outer0, const int outer1); |
| int referenceVertexCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float* innerLevels, const float* outerLevels); |
| int referencePrimitiveCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float* innerLevels, const float* outerLevels); |
| int numVerticesPerPrimitive (const TessPrimitiveType primitiveType, const bool usePointMode); |
| |
| static inline const char* getTessPrimitiveTypeShaderName (const TessPrimitiveType type, bool forSpirv = false) |
| { |
| static std::string primitiveName[][2] = |
| { |
| // glsl name spirv name |
| { "triangles", "Triangles"}, |
| { "quads" , "Quads" }, |
| { "isolines" , "Isolines" } |
| }; |
| |
| if (type >= TESSPRIMITIVETYPE_LAST) |
| { |
| DE_FATAL("Unexpected primitive type."); |
| return DE_NULL; |
| } |
| |
| return primitiveName[type][forSpirv].c_str(); |
| } |
| |
| static inline const char* getDomainName (const TessPrimitiveType type) |
| { |
| switch (type) |
| { |
| case TESSPRIMITIVETYPE_TRIANGLES: return "tri"; |
| case TESSPRIMITIVETYPE_QUADS: return "quad"; |
| case TESSPRIMITIVETYPE_ISOLINES: return "isoline"; |
| default: |
| DE_FATAL("Unexpected primitive type."); |
| return DE_NULL; |
| } |
| } |
| |
| static inline const char* getOutputTopologyName (const TessPrimitiveType type, const Winding winding, const bool usePointMode) |
| { |
| if (usePointMode) |
| return "point"; |
| else if (type == TESSPRIMITIVETYPE_TRIANGLES || type == TESSPRIMITIVETYPE_QUADS) |
| return (winding == WINDING_CCW ? "triangle_ccw" : "triangle_cw"); |
| else if (type == TESSPRIMITIVETYPE_ISOLINES) |
| return "line"; |
| |
| DE_FATAL("Unexpected primitive type."); |
| return DE_NULL; |
| } |
| |
| static inline const char* getSpacingModeShaderName (SpacingMode mode, bool forSpirv = false) |
| { |
| static std::string spacingName[][2] = |
| { |
| // glsl name spirv name |
| { "equal_spacing", "SpacingEqual"}, |
| { "fractional_odd_spacing", "SpacingFractionalOdd" }, |
| { "fractional_even_spacing", "SpacingFractionalEven" } |
| }; |
| |
| if (mode >= SPACINGMODE_LAST) |
| { |
| DE_FATAL("Unexpected spacing type."); |
| return DE_NULL; |
| } |
| |
| return spacingName[mode][forSpirv].c_str(); |
| } |
| |
| static inline const char* getPartitioningShaderName (SpacingMode mode) |
| { |
| switch (mode) |
| { |
| case SPACINGMODE_EQUAL: return "integer"; |
| case SPACINGMODE_FRACTIONAL_ODD: return "fractional_odd"; |
| case SPACINGMODE_FRACTIONAL_EVEN: return "fractional_even"; |
| default: |
| DE_FATAL("Unexpected spacing mode."); |
| return DE_NULL; |
| } |
| } |
| |
| static inline const char* getWindingShaderName (const Winding winding) |
| { |
| switch (winding) |
| { |
| case WINDING_CCW: return "ccw"; |
| case WINDING_CW: return "cw"; |
| default: |
| DE_FATAL("Unexpected winding type."); |
| return DE_NULL; |
| } |
| } |
| |
| static inline const char* getShaderLanguageName (const ShaderLanguage language) |
| { |
| switch (language) |
| { |
| case SHADER_LANGUAGE_GLSL: return "glsl"; |
| case SHADER_LANGUAGE_HLSL: return "hlsl"; |
| default: |
| DE_FATAL("Unexpected shader language."); |
| return DE_NULL; |
| } |
| } |
| |
| static inline const char* getGeometryShaderInputPrimitiveTypeShaderName (const TessPrimitiveType type, const bool usePointMode) |
| { |
| if (usePointMode) |
| return "points"; |
| |
| switch (type) |
| { |
| case TESSPRIMITIVETYPE_TRIANGLES: |
| case TESSPRIMITIVETYPE_QUADS: |
| return "triangles"; |
| |
| case TESSPRIMITIVETYPE_ISOLINES: |
| return "lines"; |
| |
| default: |
| DE_FATAL("Unexpected primitive type."); |
| return DE_NULL; |
| } |
| } |
| |
| static inline const char* getGeometryShaderOutputPrimitiveTypeShaderName (const TessPrimitiveType type, const bool usePointMode) |
| { |
| if (usePointMode) |
| return "points"; |
| |
| switch (type) |
| { |
| case TESSPRIMITIVETYPE_TRIANGLES: |
| case TESSPRIMITIVETYPE_QUADS: |
| return "triangle_strip"; |
| |
| case TESSPRIMITIVETYPE_ISOLINES: |
| return "line_strip"; |
| |
| default: |
| DE_FATAL("Unexpected primitive type."); |
| return DE_NULL; |
| } |
| } |
| |
| static inline const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR* getPortability (const Context& context) |
| { |
| if (context.isDeviceFunctionalitySupported("VK_KHR_portability_subset")) |
| return &context.getPortabilitySubsetFeatures(); |
| return DE_NULL; |
| } |
| |
| static inline void checkIsolines (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR& features) |
| { |
| if (!features.tessellationIsolines) |
| TCU_THROW(NotSupportedError, "VK_KHR_portability_subset: Tessellation iso lines are not supported by this implementation"); |
| } |
| |
| static inline void checkPrimitive (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR& features, const TessPrimitiveType primitive) |
| { |
| if (primitive == TESSPRIMITIVETYPE_ISOLINES) |
| checkIsolines(features); |
| } |
| |
| static inline void checkSupportPrimitive (Context& context, const TessPrimitiveType primitive) |
| { |
| if (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR* const features = getPortability(context)) |
| checkPrimitive(*features, primitive); |
| } |
| |
| static inline void checkPointMode (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR& features) |
| { |
| if (!features.tessellationPointMode) |
| TCU_THROW(NotSupportedError, "VK_KHR_portability_subset: Tessellation point mode is not supported by this implementation"); |
| } |
| |
| template<typename T> |
| inline std::size_t sizeInBytes (const std::vector<T>& vec) |
| { |
| return vec.size() * sizeof(vec[0]); |
| } |
| |
| template <typename T> |
| static std::vector<T> sorted (const std::vector<T>& unsorted) |
| { |
| std::vector<T> result = unsorted; |
| std::sort(result.begin(), result.end()); |
| return result; |
| } |
| |
| template <typename T, typename P> |
| static std::vector<T> sorted (const std::vector<T>& unsorted, P pred) |
| { |
| std::vector<T> result = unsorted; |
| std::sort(result.begin(), result.end(), pred); |
| return result; |
| } |
| |
| template <typename IterT> |
| std::string elemsStr (const IterT& begin, const IterT& end, int wrapLengthParam = 0, int numIndentationSpaces = 0) |
| { |
| const int bigInt = ~0u/2; |
| const std::string baseIndentation = std::string(numIndentationSpaces, ' '); |
| const std::string deepIndentation = baseIndentation + std::string(4, ' '); |
| const int wrapLength = wrapLengthParam > 0 ? wrapLengthParam : bigInt; |
| const int length = static_cast<int>(std::distance(begin, end)); |
| std::string result; |
| |
| if (length > wrapLength) |
| result += "(amount: " + de::toString(length) + ") "; |
| result += std::string() + "{" + (length > wrapLength ? "\n"+deepIndentation : " "); |
| |
| { |
| int index = 0; |
| for (IterT it = begin; it != end; ++it) |
| { |
| if (it != begin) |
| result += std::string() + ", " + (index % wrapLength == 0 ? "\n"+deepIndentation : ""); |
| result += de::toString(*it); |
| index++; |
| } |
| |
| result += length > wrapLength ? "\n"+baseIndentation : " "; |
| } |
| |
| result += "}"; |
| return result; |
| } |
| |
| template <typename ContainerT> |
| std::string containerStr (const ContainerT& c, int wrapLengthParam = 0, int numIndentationSpaces = 0) |
| { |
| return elemsStr(c.begin(), c.end(), wrapLengthParam, numIndentationSpaces); |
| } |
| |
| //! Copy 'count' objects of type T from 'memory' into a vector. |
| //! 'offset' is the offset of first object in memory, and 'stride' is the distance between consecutive objects. |
| template<typename T> |
| std::vector<T> readInterleavedData (const int count, const void* memory, const int offset, const int stride) |
| { |
| std::vector<T> results(count); |
| const deUint8* pData = static_cast<const deUint8*>(memory) + offset; |
| |
| for (int i = 0; i < count; ++i) |
| { |
| deMemcpy(&results[i], pData, sizeof(T)); |
| pData += stride; |
| } |
| |
| return results; |
| } |
| |
| template <typename CaseDef, typename = bool> |
| struct PointMode |
| { |
| static void check(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR&, const CaseDef) |
| { |
| } |
| }; |
| |
| template <typename CaseDef> |
| struct PointMode<CaseDef, decltype(CaseDef().usePointMode)> |
| { |
| static void check(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR& features, const CaseDef caseDef) |
| { |
| if (caseDef.usePointMode) |
| checkPointMode(features); |
| } |
| }; |
| |
| template <typename CaseDef> |
| void checkSupportCase (Context& context, const CaseDef caseDef) |
| { |
| if (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR* const features = getPortability(context)) |
| { |
| PointMode<CaseDef>::check(*features, caseDef); |
| checkPrimitive(*features, caseDef.primitiveType); |
| } |
| } |
| |
| } // tessellation |
| } // vkt |
| |
| #endif // _VKTTESSELLATIONUTIL_HPP |