text: /*------------------------------------------------------------------------
   * Vulkan Conformance Tests
   * ------------------------
   *
   * Copyright (c) 2015 The Khronos Group Inc.
   * Copyright (c) 2015 Samsung Electronics Co., Ltd.
   * Copyright (c) 2016 The Android Open Source Project
   *
   * 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 Random uniform block layout case.
   *//*--------------------------------------------------------------------*/
  
  #include "vktRandomUniformBlockCase.hpp"
  #include "deRandom.hpp"
  
  namespace vkt
  {
  namespace ubo
  {
  
  namespace
  {
  
  static std::string genName (char first, char last, int ndx)
  {
  	std::string	str			= "";
  	int			alphabetLen	= last - first + 1;
  
  	while (ndx > alphabetLen)
  	{
  		str.insert(str.begin(), (char)(first + ((ndx - 1) % alphabetLen)));
  		ndx = (ndx - 1) / alphabetLen;
  	}
  
  	str.insert(str.begin(), (char)(first + (ndx % (alphabetLen + 1)) - 1));
  
  	return str;
  }
  
  } // anonymous
  
  RandomUniformBlockCase::RandomUniformBlockCase (tcu::TestContext&	testCtx,
  												const std::string&	name,
  												const std::string&	description,
  												BufferMode			bufferMode,
  												deUint32			features,
  												deUint32			seed)
  	: UniformBlockCase						(testCtx, name, description, bufferMode, LOAD_FULL_MATRIX, (features & FEATURE_OUT_OF_ORDER_OFFSETS) != 0u)
  	, m_features							(features)
  	, m_maxVertexBlocks						((features & FEATURE_VERTEX_BLOCKS)		? 4 : 0)
  	, m_maxFragmentBlocks					((features & FEATURE_FRAGMENT_BLOCKS)	? 4 : 0)
  	, m_maxSharedBlocks						((features & FEATURE_SHARED_BLOCKS)		? 4 : 0)
  	, m_maxInstances						((features & FEATURE_INSTANCE_ARRAYS)	? 3 : 0)
  	, m_maxArrayLength						((features & FEATURE_ARRAYS)			? 8 : 0)
  	, m_maxStructDepth						((features & FEATURE_STRUCTS)			? 2 : 0)
  	, m_maxBlockMembers						(5)
  	, m_maxStructMembers					(4)
  	, m_seed								(seed)
  	, m_blockNdx							(1)
  	, m_uniformNdx							(1)
  	, m_structNdx							(1)
  	, m_availableDescriptorUniformBuffers	(12)
  {
  	de::Random rnd(m_seed);
  
  	int numShared		= m_maxSharedBlocks				> 0	? rnd.getInt(1, m_maxSharedBlocks)				: 0;
  	int numVtxBlocks	= m_maxVertexBlocks-numShared	> 0	? rnd.getInt(1, m_maxVertexBlocks - numShared)	: 0;
  	int	numFragBlocks	= m_maxFragmentBlocks-numShared	> 0 ? rnd.getInt(1, m_maxFragmentBlocks - numShared): 0;
  
  	// calculate how many additional descriptors we can use for arrays
  	// this is needed for descriptor_indexing testing as we need to take in to account
  	// maxPerStageDescriptorUniformBuffers limit and we can't query it as we need to
  	// generate shaders before Context is created; minimal value of this limit is 12
  	m_availableDescriptorUniformBuffers -= numVtxBlocks + numFragBlocks;
  
  	for (int ndx = 0; ndx < numShared; ndx++)
  		generateBlock(rnd, DECLARE_VERTEX | DECLARE_FRAGMENT);
  
  	for (int ndx = 0; ndx < numVtxBlocks; ndx++)
  		generateBlock(rnd, DECLARE_VERTEX);
  
  	for (int ndx = 0; ndx < numFragBlocks; ndx++)
  		generateBlock(rnd, DECLARE_FRAGMENT);
  
  	init();
  }
  
  void RandomUniformBlockCase::generateBlock (de::Random& rnd, deUint32 layoutFlags)
  {
  	DE_ASSERT(m_blockNdx <= 'z' - 'a');
  
  	const float		instanceArrayWeight	= 0.3f;
  	UniformBlock&	block				= m_interface.allocBlock(std::string("Block") + (char)('A' + m_blockNdx));
  	int				numInstances		= (m_maxInstances > 0 && rnd.getFloat() < instanceArrayWeight) ? rnd.getInt(0, m_maxInstances) : 0;
  	int				numUniforms			= rnd.getInt(1, m_maxBlockMembers);
  
  	if (m_features & FEATURE_DESCRIPTOR_INDEXING)
  	{
  		// generate arrays only when we are within the limit
  		if (m_availableDescriptorUniformBuffers > 3)
  			numInstances = rnd.getInt(2, 4);
  		else if (m_availableDescriptorUniformBuffers > 1)
  			numInstances = m_availableDescriptorUniformBuffers;
  		else
  			numInstances = 0;
  		m_availableDescriptorUniformBuffers -= numInstances;
  	}
  
  	if (numInstances > 0)
  		block.setArraySize(numInstances);
  
  	if (numInstances > 0 || rnd.getBool())
  		block.setInstanceName(std::string("block") + (char)('A' + m_blockNdx));
  
  	// Layout flag candidates.
  	std::vector<deUint32> layoutFlagCandidates;
  	layoutFlagCandidates.push_back(0);
  
  	if (m_features & FEATURE_STD140_LAYOUT)
  		layoutFlagCandidates.push_back(LAYOUT_STD140);
  
  	if (m_features & FEATURE_STD430_LAYOUT)
  		layoutFlagCandidates.push_back(LAYOUT_STD430);
  
  	if (m_features & FEATURE_SCALAR_LAYOUT)
  		layoutFlagCandidates.push_back(LAYOUT_SCALAR);
  
  	if (m_features & FEATURE_16BIT_STORAGE)
  		layoutFlags |= LAYOUT_16BIT_STORAGE;
  
  	if (m_features & FEATURE_8BIT_STORAGE)
  		layoutFlags |= LAYOUT_8BIT_STORAGE;
  
  	if (m_features & FEATURE_DESCRIPTOR_INDEXING)
  		layoutFlags |= LAYOUT_DESCRIPTOR_INDEXING;
  
  	layoutFlags |= rnd.choose<deUint32>(layoutFlagCandidates.begin(), layoutFlagCandidates.end());
  
  	if (m_features & FEATURE_MATRIX_LAYOUT)
  	{
  		static const deUint32 matrixCandidates[] = { 0, LAYOUT_ROW_MAJOR, LAYOUT_COLUMN_MAJOR };
  		layoutFlags |= rnd.choose<deUint32>(&matrixCandidates[0], &matrixCandidates[DE_LENGTH_OF_ARRAY(matrixCandidates)]);
  	}
  
  	block.setFlags(layoutFlags);
  
  	for (int ndx = 0; ndx < numUniforms; ndx++)
  		generateUniform(rnd, block);
  
  	m_blockNdx += 1;
  }
  
  void RandomUniformBlockCase::generateUniform (de::Random& rnd, UniformBlock& block)
  {
  	const float		unusedVtxWeight		= 0.15f;
  	const float		unusedFragWeight	= 0.15f;
  	bool			unusedOk			= (m_features & FEATURE_UNUSED_UNIFORMS) != 0;
  	deUint32		flags				= 0;
  	std::string		name				= genName('a', 'z', m_uniformNdx);
  	VarType			type				= generateType(rnd, 0, true);
  
  	flags |= (unusedOk && rnd.getFloat() < unusedVtxWeight)		? UNUSED_VERTEX		: 0;
  	flags |= (unusedOk && rnd.getFloat() < unusedFragWeight)	? UNUSED_FRAGMENT	: 0;
  
  	block.addUniform(Uniform(name, type, flags));
  
  	m_uniformNdx += 1;
  }
  
  VarType RandomUniformBlockCase::generateType (de::Random& rnd, int typeDepth, bool arrayOk)
  {
  	const float structWeight	= 0.1f;
  	const float arrayWeight		= 0.1f;
  
  	if (typeDepth < m_maxStructDepth && rnd.getFloat() < structWeight)
  	{
  		const float				unusedVtxWeight		= 0.15f;
  		const float				unusedFragWeight	= 0.15f;
  		bool					unusedOk			= (m_features & FEATURE_UNUSED_MEMBERS) != 0;
  		std::vector<VarType>	memberTypes;
  		int						numMembers = rnd.getInt(1, m_maxStructMembers);
  
  		// Generate members first so nested struct declarations are in correct order.
  		for (int ndx = 0; ndx < numMembers; ndx++)
  			memberTypes.push_back(generateType(rnd, typeDepth+1, true));
  
  		StructType& structType = m_interface.allocStruct(std::string("s") + genName('A', 'Z', m_structNdx));
  		m_structNdx += 1;
  
  		DE_ASSERT(numMembers <= 'Z' - 'A');
  		for (int ndx = 0; ndx < numMembers; ndx++)
  		{
  			deUint32 flags = 0;
  
  			flags |= (unusedOk && rnd.getFloat() < unusedVtxWeight)		? UNUSED_VERTEX		: 0;
  			flags |= (unusedOk && rnd.getFloat() < unusedFragWeight)	? UNUSED_FRAGMENT	: 0;
  
  			structType.addMember(std::string("m") + (char)('A' + ndx), memberTypes[ndx], flags);
  		}
  
  		return VarType(&structType, m_shuffleUniformMembers ? static_cast<deUint32>(LAYOUT_OFFSET) : 0u);
  	}
  	else if (m_maxArrayLength > 0 && arrayOk && rnd.getFloat() < arrayWeight)
  	{
  		const bool	arraysOfArraysOk	= (m_features & FEATURE_ARRAYS_OF_ARRAYS) != 0;
  		const int	arrayLength			= rnd.getInt(1, m_maxArrayLength);
  		VarType		elementType			= generateType(rnd, typeDepth, arraysOfArraysOk);
  		return VarType(elementType, arrayLength);
  	}
  	else
  	{
  		std::vector<glu::DataType> typeCandidates;
  
  		typeCandidates.push_back(glu::TYPE_FLOAT);
  		typeCandidates.push_back(glu::TYPE_INT);
  		typeCandidates.push_back(glu::TYPE_UINT);
  		typeCandidates.push_back(glu::TYPE_BOOL);
  
  		if (m_features & FEATURE_16BIT_STORAGE) {
  			typeCandidates.push_back(glu::TYPE_UINT16);
  			typeCandidates.push_back(glu::TYPE_INT16);
  			typeCandidates.push_back(glu::TYPE_FLOAT16);
  		}
  
  		if (m_features & FEATURE_8BIT_STORAGE) {
  			typeCandidates.push_back(glu::TYPE_UINT8);
  			typeCandidates.push_back(glu::TYPE_INT8);
  		}
  
  		if (m_features & FEATURE_VECTORS)
  		{
  			typeCandidates.push_back(glu::TYPE_FLOAT_VEC2);
  			typeCandidates.push_back(glu::TYPE_FLOAT_VEC3);
  			typeCandidates.push_back(glu::TYPE_FLOAT_VEC4);
  			typeCandidates.push_back(glu::TYPE_INT_VEC2);
  			typeCandidates.push_back(glu::TYPE_INT_VEC3);
  			typeCandidates.push_back(glu::TYPE_INT_VEC4);
  			typeCandidates.push_back(glu::TYPE_UINT_VEC2);
  			typeCandidates.push_back(glu::TYPE_UINT_VEC3);
  			typeCandidates.push_back(glu::TYPE_UINT_VEC4);
  			typeCandidates.push_back(glu::TYPE_BOOL_VEC2);
  			typeCandidates.push_back(glu::TYPE_BOOL_VEC3);
  			typeCandidates.push_back(glu::TYPE_BOOL_VEC4);
  			if (m_features & FEATURE_16BIT_STORAGE)
  			{
  				typeCandidates.push_back(glu::TYPE_FLOAT16_VEC2);
  				typeCandidates.push_back(glu::TYPE_FLOAT16_VEC3);
  				typeCandidates.push_back(glu::TYPE_FLOAT16_VEC4);
  				typeCandidates.push_back(glu::TYPE_INT16_VEC2);
  				typeCandidates.push_back(glu::TYPE_INT16_VEC3);
  				typeCandidates.push_back(glu::TYPE_INT16_VEC4);
  				typeCandidates.push_back(glu::TYPE_UINT16_VEC2);
  				typeCandidates.push_back(glu::TYPE_UINT16_VEC3);
  				typeCandidates.push_back(glu::TYPE_UINT16_VEC4);
  			}
  			if (m_features & FEATURE_8BIT_STORAGE)
  			{
  				typeCandidates.push_back(glu::TYPE_INT8_VEC2);
  				typeCandidates.push_back(glu::TYPE_INT8_VEC3);
  				typeCandidates.push_back(glu::TYPE_INT8_VEC4);
  				typeCandidates.push_back(glu::TYPE_UINT8_VEC2);
  				typeCandidates.push_back(glu::TYPE_UINT8_VEC3);
  				typeCandidates.push_back(glu::TYPE_UINT8_VEC4);
  			}
  		}
  
  		if (m_features & FEATURE_MATRICES)
  		{
  			typeCandidates.push_back(glu::TYPE_FLOAT_MAT2);
  			typeCandidates.push_back(glu::TYPE_FLOAT_MAT2X3);
  			typeCandidates.push_back(glu::TYPE_FLOAT_MAT3X2);
  			typeCandidates.push_back(glu::TYPE_FLOAT_MAT3);
  			typeCandidates.push_back(glu::TYPE_FLOAT_MAT3X4);
  			typeCandidates.push_back(glu::TYPE_FLOAT_MAT4X2);
  			typeCandidates.push_back(glu::TYPE_FLOAT_MAT4X3);
  			typeCandidates.push_back(glu::TYPE_FLOAT_MAT4);
  		}
  
  		glu::DataType	type	= rnd.choose<glu::DataType>(typeCandidates.begin(), typeCandidates.end());
  		deUint32		flags	= (m_shuffleUniformMembers ? static_cast<deUint32>(LAYOUT_OFFSET) : 0u);
  
  		if (glu::dataTypeSupportsPrecisionModifier(type))
  		{
  			// Precision.
  			static const deUint32 precisionCandidates[] = { PRECISION_LOW, PRECISION_MEDIUM, PRECISION_HIGH };
  			flags |= rnd.choose<deUint32>(&precisionCandidates[0], &precisionCandidates[DE_LENGTH_OF_ARRAY(precisionCandidates)]);
  		}
  
  		return VarType(type, flags);
  	}
  }
  
  } // ubo
  } // vkt
  