#ifndef _VKTSHADERRENDER_HPP #define _VKTSHADERRENDER_HPP /*------------------------------------------------------------------------ * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2015 The Khronos Group Inc. * Copyright (c) 2015 Samsung Electronics Co., Ltd. * * 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 Vulkan ShaderRenderCase *//*--------------------------------------------------------------------*/ #include "tcuTexture.hpp" #include "tcuSurface.hpp" #include "tcuMaybe.hpp" #include "deMemory.h" #include "deSharedPtr.hpp" #include "deUniquePtr.hpp" #include "vkDefs.hpp" #include "vkRefUtil.hpp" #include "vkPrograms.hpp" #include "vkRef.hpp" #include "vkMemUtil.hpp" #include "vkBuilderUtil.hpp" #include "vkTypeUtil.hpp" #include "vkPlatform.hpp" #include "vktTestCaseUtil.hpp" namespace vkt { namespace sr { class LineStream { public: LineStream (int indent = 0) { m_indent = indent; } ~LineStream (void) {} const char* str (void) const { m_string = m_stream.str(); return m_string.c_str(); } LineStream& operator<< (const char* line) { for (int i = 0; i < m_indent; i++) { m_stream << "\t"; } m_stream << line << "\n"; return *this; } private: int m_indent; std::ostringstream m_stream; mutable std::string m_string; }; class QuadGrid; class ShaderRenderCaseInstance; class TextureBinding { public: enum Type { TYPE_NONE = 0, TYPE_1D, TYPE_2D, TYPE_3D, TYPE_CUBE_MAP, TYPE_1D_ARRAY, TYPE_2D_ARRAY, TYPE_CUBE_ARRAY, TYPE_LAST }; enum Init { INIT_UPLOAD_DATA, INIT_CLEAR, INIT_LAST }; struct MinMaxLod { float minLod; float maxLod; MinMaxLod (float min, float max) : minLod(min), maxLod(max) {} }; struct Parameters { deUint32 baseMipLevel; vk::VkComponentMapping componentMapping; vk::VkSampleCountFlagBits samples; Init initialization; tcu::Maybe minMaxLod; Parameters (deUint32 baseMipLevel_ = 0, vk::VkComponentMapping componentMapping_ = vk::makeComponentMappingRGBA(), vk::VkSampleCountFlagBits samples_ = vk::VK_SAMPLE_COUNT_1_BIT, Init initialization_ = INIT_UPLOAD_DATA, const tcu::Maybe& minMaxLod_ = tcu::Nothing) : baseMipLevel (baseMipLevel_) , componentMapping (componentMapping_) , samples (samples_) , initialization (initialization_) , minMaxLod (minMaxLod_) { } }; TextureBinding (const tcu::Archive& archive, const char* filename, const Type type, const tcu::Sampler& sampler); TextureBinding (const tcu::Texture1D* tex1D, const tcu::Sampler& sampler); TextureBinding (const tcu::Texture2D* tex2D, const tcu::Sampler& sampler); TextureBinding (const tcu::Texture3D* tex3D, const tcu::Sampler& sampler); TextureBinding (const tcu::TextureCube* texCube, const tcu::Sampler& sampler); TextureBinding (const tcu::Texture1DArray* tex1DArray, const tcu::Sampler& sampler); TextureBinding (const tcu::Texture2DArray* tex2DArray, const tcu::Sampler& sampler); TextureBinding (const tcu::TextureCubeArray* texCubeArray, const tcu::Sampler& sampler); ~TextureBinding (void); Type getType (void) const { return m_type; } const tcu::Sampler& getSampler (void) const { return m_sampler; } const tcu::Texture1D& get1D (void) const { DE_ASSERT(getType() == TYPE_1D && m_binding.tex1D != NULL); return *m_binding.tex1D; } const tcu::Texture2D& get2D (void) const { DE_ASSERT(getType() == TYPE_2D && m_binding.tex2D != NULL); return *m_binding.tex2D; } const tcu::Texture3D& get3D (void) const { DE_ASSERT(getType() == TYPE_3D && m_binding.tex3D != NULL); return *m_binding.tex3D; } const tcu::TextureCube& getCube (void) const { DE_ASSERT(getType() == TYPE_CUBE_MAP && m_binding.texCube != NULL); return *m_binding.texCube; } const tcu::Texture1DArray& get1DArray (void) const { DE_ASSERT(getType() == TYPE_1D_ARRAY && m_binding.tex1DArray != NULL); return *m_binding.tex1DArray; } const tcu::Texture2DArray& get2DArray (void) const { DE_ASSERT(getType() == TYPE_2D_ARRAY && m_binding.tex2DArray != NULL); return *m_binding.tex2DArray; } const tcu::TextureCubeArray& getCubeArray (void) const { DE_ASSERT(getType() == TYPE_CUBE_ARRAY && m_binding.texCubeArray != NULL); return *m_binding.texCubeArray; } void setParameters (const Parameters& params) { m_params = params; } const Parameters& getParameters (void) const { return m_params; } private: TextureBinding (const TextureBinding&); // not allowed! TextureBinding& operator= (const TextureBinding&); // not allowed! static de::MovePtr loadTexture2D (const tcu::Archive& archive, const char* filename); Type m_type; tcu::Sampler m_sampler; Parameters m_params; union { const tcu::Texture1D* tex1D; const tcu::Texture2D* tex2D; const tcu::Texture3D* tex3D; const tcu::TextureCube* texCube; const tcu::Texture1DArray* tex1DArray; const tcu::Texture2DArray* tex2DArray; const tcu::TextureCubeArray* texCubeArray; } m_binding; }; typedef de::SharedPtr TextureBindingSp; // ShaderEvalContext. class ShaderEvalContext { public: // Limits. enum { MAX_USER_ATTRIBS = 4, MAX_TEXTURES = 4 }; struct ShaderSampler { tcu::Sampler sampler; const tcu::Texture1D* tex1D; const tcu::Texture2D* tex2D; const tcu::Texture3D* tex3D; const tcu::TextureCube* texCube; const tcu::Texture1DArray* tex1DArray; const tcu::Texture2DArray* tex2DArray; const tcu::TextureCubeArray* texCubeArray; inline ShaderSampler (void) : tex1D (DE_NULL) , tex2D (DE_NULL) , tex3D (DE_NULL) , texCube (DE_NULL) , tex1DArray (DE_NULL) , tex2DArray (DE_NULL) , texCubeArray (DE_NULL) { } }; ShaderEvalContext (const QuadGrid& quadGrid); ~ShaderEvalContext (void); void reset (float sx, float sy); // Inputs. tcu::Vec4 coords; tcu::Vec4 unitCoords; tcu::Vec4 constCoords; tcu::Vec4 in[MAX_USER_ATTRIBS]; ShaderSampler textures[MAX_TEXTURES]; // Output. tcu::Vec4 color; bool isDiscarded; // Functions. inline void discard (void) { isDiscarded = true; } tcu::Vec4 texture2D (int unitNdx, const tcu::Vec2& coords); private: const QuadGrid& m_quadGrid; }; typedef void (*ShaderEvalFunc) (ShaderEvalContext& c); inline void evalCoordsPassthroughX (ShaderEvalContext& c) { c.color.x() = c.coords.x(); } inline void evalCoordsPassthroughXY (ShaderEvalContext& c) { c.color.xy() = c.coords.swizzle(0,1); } inline void evalCoordsPassthroughXYZ (ShaderEvalContext& c) { c.color.xyz() = c.coords.swizzle(0,1,2); } inline void evalCoordsPassthrough (ShaderEvalContext& c) { c.color = c.coords; } inline void evalCoordsSwizzleWZYX (ShaderEvalContext& c) { c.color = c.coords.swizzle(3,2,1,0); } // ShaderEvaluator // Either inherit a class with overridden evaluate() or just pass in an evalFunc. class ShaderEvaluator { public: ShaderEvaluator (void); ShaderEvaluator (const ShaderEvalFunc evalFunc); virtual ~ShaderEvaluator (void); virtual void evaluate (ShaderEvalContext& ctx) const; private: ShaderEvaluator (const ShaderEvaluator&); // not allowed! ShaderEvaluator& operator= (const ShaderEvaluator&); // not allowed! const ShaderEvalFunc m_evalFunc; }; // UniformSetup typedef void (*UniformSetupFunc) (ShaderRenderCaseInstance& instance, const tcu::Vec4& constCoords); class UniformSetup { public: UniformSetup (void); UniformSetup (const UniformSetupFunc setup); virtual ~UniformSetup (void); virtual void setup (ShaderRenderCaseInstance& instance, const tcu::Vec4& constCoords) const; private: UniformSetup (const UniformSetup&); // not allowed! UniformSetup& operator= (const UniformSetup&); // not allowed! const UniformSetupFunc m_setupFunc; }; typedef void (*AttributeSetupFunc) (ShaderRenderCaseInstance& instance, deUint32 numVertices); class ShaderRenderCase : public vkt::TestCase { public: ShaderRenderCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const bool isVertexCase, const ShaderEvalFunc evalFunc, const UniformSetup* uniformSetup, const AttributeSetupFunc attribFunc); ShaderRenderCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const bool isVertexCase, const ShaderEvaluator* evaluator, const UniformSetup* uniformSetup, const AttributeSetupFunc attribFunc); virtual ~ShaderRenderCase (void); virtual void initPrograms (vk::SourceCollections& programCollection) const; virtual TestInstance* createInstance (Context& context) const; protected: std::string m_vertShaderSource; std::string m_fragShaderSource; const bool m_isVertexCase; const de::UniquePtr m_evaluator; const de::UniquePtr m_uniformSetup; const AttributeSetupFunc m_attribFunc; }; enum BaseUniformType { // Bool UB_FALSE, UB_TRUE, // BVec4 UB4_FALSE, UB4_TRUE, // Integers UI_ZERO, UI_ONE, UI_TWO, UI_THREE, UI_FOUR, UI_FIVE, UI_SIX, UI_SEVEN, UI_EIGHT, UI_ONEHUNDREDONE, // IVec2 UI2_MINUS_ONE, UI2_ZERO, UI2_ONE, UI2_TWO, UI2_THREE, UI2_FOUR, UI2_FIVE, // IVec3 UI3_MINUS_ONE, UI3_ZERO, UI3_ONE, UI3_TWO, UI3_THREE, UI3_FOUR, UI3_FIVE, // IVec4 UI4_MINUS_ONE, UI4_ZERO, UI4_ONE, UI4_TWO, UI4_THREE, UI4_FOUR, UI4_FIVE, // Float UF_ZERO, UF_ONE, UF_TWO, UF_THREE, UF_FOUR, UF_FIVE, UF_SIX, UF_SEVEN, UF_EIGHT, UF_HALF, UF_THIRD, UF_FOURTH, UF_FIFTH, UF_SIXTH, UF_SEVENTH, UF_EIGHTH, // Vec2 UV2_MINUS_ONE, UV2_ZERO, UV2_ONE, UV2_TWO, UV2_THREE, UV2_HALF, // Vec3 UV3_MINUS_ONE, UV3_ZERO, UV3_ONE, UV3_TWO, UV3_THREE, UV3_HALF, // Vec4 UV4_MINUS_ONE, UV4_ZERO, UV4_ONE, UV4_TWO, UV4_THREE, UV4_HALF, UV4_BLACK, UV4_GRAY, UV4_WHITE, // Last U_LAST }; enum BaseAttributeType { // User attributes A_IN0, A_IN1, A_IN2, A_IN3, // Matrices MAT2, MAT2x3, MAT2x4, MAT3x2, MAT3, MAT3x4, MAT4x2, MAT4x3, MAT4 }; // ShaderRenderCaseInstance. class ShaderRenderCaseInstance : public vkt::TestInstance { public: enum ImageBackingMode { IMAGE_BACKING_MODE_REGULAR = 0, IMAGE_BACKING_MODE_SPARSE, }; // Default wertex and fragment grid sizes are used by a large collection of tests // to generate input sets. Some tests might change their behavior if the // default grid size values are altered, so care should be taken to confirm that // any changes to default values do not produce regressions. // If a particular tests needs to use a different grid size value, rather than // modifying the default grid size values for all tests, it is recommended that // the test specifies the required grid size using the gridSize parameter in the // ShaderRenderCaseInstance constuctor instead. enum { GRID_SIZE_DEFAULTS = 0, GRID_SIZE_DEFAULT_VERTEX = 90, GRID_SIZE_DEFAULT_FRAGMENT = 4, }; ShaderRenderCaseInstance (Context& context); ShaderRenderCaseInstance (Context& context, const bool isVertexCase, const ShaderEvaluator& evaluator, const UniformSetup& uniformSetup, const AttributeSetupFunc attribFunc, const ImageBackingMode imageBackingMode = IMAGE_BACKING_MODE_REGULAR, const deUint32 gridSize = static_cast(GRID_SIZE_DEFAULTS), const bool fuzzyCompare = true); virtual ~ShaderRenderCaseInstance (void); virtual tcu::TestStatus iterate (void); void addAttribute (deUint32 bindingLocation, vk::VkFormat format, deUint32 sizePerElement, deUint32 count, const void* data); void useAttribute (deUint32 bindingLocation, BaseAttributeType type); template void addUniform (deUint32 bindingLocation, vk::VkDescriptorType descriptorType, const T& data); void addUniform (deUint32 bindingLocation, vk::VkDescriptorType descriptorType, size_t dataSize, const void* data); void useUniform (deUint32 bindingLocation, BaseUniformType type); void useSampler (deUint32 bindingLocation, deUint32 textureId); static const tcu::Vec4 getDefaultConstCoords (void) { return tcu::Vec4(0.125f, 0.25f, 0.5f, 1.0f); } void setPushConstantRanges (const deUint32 rangeCount, const vk::VkPushConstantRange* const pcRanges); virtual void updatePushConstants (vk::VkCommandBuffer commandBuffer, vk::VkPipelineLayout pipelineLayout); protected: ShaderRenderCaseInstance (Context& context, const bool isVertexCase, const ShaderEvaluator* evaluator, const UniformSetup* uniformSetup, const AttributeSetupFunc attribFunc, const ImageBackingMode imageBackingMode = IMAGE_BACKING_MODE_REGULAR, const deUint32 gridSize = static_cast(GRID_SIZE_DEFAULTS)); virtual void setup (void); virtual void setupUniforms (const tcu::Vec4& constCoords); virtual void setupDefaultInputs (void); void render (deUint32 numVertices, deUint32 numTriangles, const deUint16* indices, const tcu::Vec4& constCoords = getDefaultConstCoords()); void render (deUint32 numVertices, deUint32 numIndices, const deUint16* indices, vk::VkPrimitiveTopology topology, const tcu::Vec4& constCoords = getDefaultConstCoords()); const tcu::TextureLevel& getResultImage (void) const { return m_resultImage; } const tcu::UVec2 getViewportSize (void) const; void setSampleCount (vk::VkSampleCountFlagBits sampleCount); bool isMultiSampling (void) const; ImageBackingMode m_imageBackingMode; deUint32 m_quadGridSize; protected: vk::Allocator& m_memAlloc; const tcu::Vec4 m_clearColor; const bool m_isVertexCase; std::vector m_userAttribTransforms; std::vector m_textures; std::string m_vertexShaderName; std::string m_fragmentShaderName; tcu::UVec2 m_renderSize; vk::VkFormat m_colorFormat; de::SharedPtr > m_externalCommandPool; private: typedef std::vector TextureLayerData; typedef std::vector TextureData; void uploadImage (const tcu::TextureFormat& texFormat, const TextureData& textureData, const tcu::Sampler& refSampler, deUint32 mipLevels, deUint32 arrayLayers, vk::VkImage destImage); void clearImage (const tcu::Sampler& refSampler, deUint32 mipLevels, deUint32 arrayLayers, vk::VkImage destImage); void checkSparseSupport (const vk::VkImageCreateInfo& imageInfo) const; #ifndef CTS_USES_VULKANSC void uploadSparseImage (const tcu::TextureFormat& texFormat, const TextureData& textureData, const tcu::Sampler& refSampler, const deUint32 mipLevels, const deUint32 arrayLayers, const vk::VkImage sparseImage, const vk::VkImageCreateInfo& imageCreateInfo, const tcu::UVec3 texSize); #endif // CTS_USES_VULKANSC void createSamplerUniform (deUint32 bindingLocation, TextureBinding::Type textureType, TextureBinding::Init textureInit, const tcu::TextureFormat& texFormat, const tcu::UVec3 texSize, const TextureData& textureData, const tcu::Sampler& refSampler, deUint32 mipLevels, deUint32 arrayLayers, TextureBinding::Parameters textureParams); void setupUniformData (deUint32 bindingLocation, size_t size, const void* dataPtr); void computeVertexReference (tcu::Surface& result, const QuadGrid& quadGrid); void computeFragmentReference (tcu::Surface& result, const QuadGrid& quadGrid); bool compareImages (const tcu::Surface& resImage, const tcu::Surface& refImage, float errorThreshold); private: const ShaderEvaluator* m_evaluator; const UniformSetup* m_uniformSetup; const AttributeSetupFunc m_attribFunc; de::MovePtr m_quadGrid; tcu::TextureLevel m_resultImage; struct EnabledBaseAttribute { deUint32 location; BaseAttributeType type; }; std::vector m_enabledBaseAttributes; de::MovePtr m_descriptorSetLayoutBuilder; de::MovePtr m_descriptorPoolBuilder; de::MovePtr m_descriptorSetUpdateBuilder; typedef de::SharedPtr > VkBufferSp; typedef de::SharedPtr > VkImageSp; typedef de::SharedPtr > VkImageViewSp; typedef de::SharedPtr > VkSamplerSp; typedef de::SharedPtr AllocationSp; class UniformInfo { public: UniformInfo (void) {} virtual ~UniformInfo (void) {} vk::VkDescriptorType type; deUint32 location; }; class BufferUniform : public UniformInfo { public: BufferUniform (void) {} virtual ~BufferUniform (void) {} VkBufferSp buffer; AllocationSp alloc; vk::VkDescriptorBufferInfo descriptor; }; class SamplerUniform : public UniformInfo { public: SamplerUniform (void) {} virtual ~SamplerUniform (void) {} VkImageSp image; VkImageViewSp imageView; VkSamplerSp sampler; AllocationSp alloc; vk::VkDescriptorImageInfo descriptor; }; typedef de::SharedPtr > UniformInfoSp; std::vector m_uniformInfos; std::vector< de::SharedPtr > m_allocations; std::vector m_vertexBindingDescription; std::vector m_vertexAttributeDescription; std::vector m_vertexBuffers; std::vector m_vertexBufferAllocs; vk::VkSampleCountFlagBits m_sampleCount; std::vector m_pushConstantRanges; bool m_fuzzyCompare; protected: vk::VkDevice getDevice (void) const; deUint32 getUniversalQueueFamilyIndex (void) const; deUint32 getSparseQueueFamilyIndex (void) const; const vk::DeviceInterface& getDeviceInterface (void) const; vk::VkQueue getUniversalQueue (void) const; vk::VkQueue getSparseQueue (void) const; vk::VkPhysicalDevice getPhysicalDevice (void) const; const vk::InstanceInterface& getInstanceInterface (void) const; vk::Allocator& getAllocator (void) const; }; template void ShaderRenderCaseInstance::addUniform (deUint32 bindingLocation, vk::VkDescriptorType descriptorType, const T& data) { addUniform(bindingLocation, descriptorType, sizeof(T), &data); } vk::VkImageViewType textureTypeToImageViewType (TextureBinding::Type type); vk::VkImageType viewTypeToImageType (vk::VkImageViewType type); vk::VkImageUsageFlags textureUsageFlags (void); vk::VkImageCreateFlags textureCreateFlags (vk::VkImageViewType viewType, ShaderRenderCaseInstance::ImageBackingMode backingMode); } // sr } // vkt #endif // _VKTSHADERRENDER_HPP