blob: 1cc85d2b6e44e1331f25d2d2f8ef5570634e4761 [file] [log] [blame]
/*-------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2015 Google 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 ShaderLibrary Vulkan implementation
*//*--------------------------------------------------------------------*/
#include "vktShaderLibrary.hpp"
#include "vktTestCase.hpp"
#include "vkPrograms.hpp"
#include "vkRef.hpp"
#include "vkRefUtil.hpp"
#include "vkMemUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkBuilderUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkImageUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkObjUtil.hpp"
#include "gluShaderLibrary.hpp"
#include "gluShaderUtil.hpp"
#include "tcuStringTemplate.hpp"
#include "tcuTexture.hpp"
#include "tcuTestLog.hpp"
#include "tcuVector.hpp"
#include "tcuVectorUtil.hpp"
#include "deStringUtil.hpp"
#include "deArrayUtil.hpp"
#include "deMemory.h"
#include <sstream>
#include <map>
namespace vkt
{
using std::string;
using std::vector;
using std::map;
using std::pair;
using std::ostringstream;
using de::MovePtr;
using de::UniquePtr;
using glu::ShaderType;
using glu::ProgramSources;
using glu::DataType;
using glu::sl::ShaderCaseSpecification;
using glu::sl::ProgramSpecializationParams;
using glu::sl::RequiredExtension;
using glu::sl::Value;
using glu::sl::ValueBlock;
using tcu::TestStatus;
using tcu::StringTemplate;
using tcu::Vec2;
using tcu::ConstPixelBufferAccess;
using tcu::TextureFormat;
using tcu::TestLog;
using vk::SourceCollections;
using vk::Move;
using vk::Unique;
namespace
{
enum
{
REFERENCE_UNIFORM_BINDING = 0,
USER_UNIFORM_BINDING = 1
};
string getShaderName (ShaderType shaderType, size_t progNdx)
{
ostringstream str;
str << glu::getShaderTypeName(shaderType);
if (progNdx > 0)
str << "_" << progNdx;
return str.str();
}
void genUniformBlock (ostringstream& out, const string& blockName, const string& instanceName, int setNdx, int bindingNdx, const vector<Value>& uniforms)
{
out << "layout(";
if (setNdx != 0)
out << "set = " << setNdx << ", ";
out << "binding = " << bindingNdx << ", std140) uniform " << blockName << "\n"
<< "{\n";
for (vector<Value>::const_iterator val = uniforms.begin(); val != uniforms.end(); ++val)
out << "\t" << glu::declare(val->type, val->name, 1) << ";\n";
out << "}";
if (!instanceName.empty())
out << " " << instanceName;
out << ";\n";
}
void declareReferenceBlock (ostringstream& out, const ValueBlock& valueBlock)
{
if (!valueBlock.outputs.empty())
genUniformBlock(out, "Reference", "ref", 0, REFERENCE_UNIFORM_BINDING, valueBlock.outputs);
}
void declareUniforms (ostringstream& out, const ValueBlock& valueBlock)
{
if (!valueBlock.uniforms.empty())
genUniformBlock(out, "Uniforms", "", 0, USER_UNIFORM_BINDING, valueBlock.uniforms);
}
DataType getTransportType (DataType valueType)
{
if (isDataTypeBoolOrBVec(valueType))
return glu::getDataTypeUintVec(getDataTypeScalarSize(valueType));
else
return valueType;
}
int getNumTransportLocations (DataType valueType)
{
return isDataTypeMatrix(valueType) ? getDataTypeMatrixNumColumns(valueType) : 1;
}
// This functions builds a matching vertex shader for a 'both' case, when
// the fragment shader is being tested.
// We need to build attributes and varyings for each 'input'.
string genVertexShader (const ShaderCaseSpecification& spec)
{
ostringstream res;
int curInputLoc = 0;
int curOutputLoc = 0;
res << glu::getGLSLVersionDeclaration(spec.targetVersion) << "\n";
// Declarations (position + attribute/varying for each input).
res << "precision highp float;\n";
res << "precision highp int;\n";
res << "\n";
res << "layout(location = 0) in highp vec4 dEQP_Position;\n";
curInputLoc += 1;
for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++)
{
const Value& val = spec.values.inputs[ndx];
const DataType valueType = val.type.getBasicType();
const DataType transportType = getTransportType(valueType);
const char* const transportTypeStr = getDataTypeName(transportType);
const int numLocs = getNumTransportLocations(valueType);
res << "layout(location = " << curInputLoc << ") in " << transportTypeStr << " a_" << val.name << ";\n";
res << "layout(location = " << curOutputLoc << ") flat out " << transportTypeStr << " " << (transportType != valueType ? "v_" : "") << val.name << ";\n";
curInputLoc += numLocs;
curOutputLoc += numLocs;
}
res << "\n";
// Main function.
// - gl_Position = dEQP_Position;
// - for each input: write attribute directly to varying
res << "void main()\n";
res << "{\n";
res << " gl_Position = dEQP_Position;\n";
for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++)
{
const Value& val = spec.values.inputs[ndx];
const string& name = val.name;
res << " " << (getTransportType(val.type.getBasicType()) != val.type.getBasicType() ? "v_" : "")
<< name << " = a_" << name << ";\n";
}
res << "}\n";
return res.str();
}
void genCompareOp (ostringstream& output, const char* dstVec4Var, const ValueBlock& valueBlock, const char* checkVarName)
{
bool isFirstOutput = true;
for (size_t ndx = 0; ndx < valueBlock.outputs.size(); ndx++)
{
const Value& val = valueBlock.outputs[ndx];
// Check if we're only interested in one variable (then skip if not the right one).
if (checkVarName && val.name != checkVarName)
continue;
// Prefix.
if (isFirstOutput)
{
output << "bool RES = ";
isFirstOutput = false;
}
else
output << "RES = RES && ";
// Generate actual comparison.
if (getDataTypeScalarType(val.type.getBasicType()) == glu::TYPE_FLOAT)
output << "isOk(" << val.name << ", ref." << val.name << ", 0.05);\n";
else
output << "isOk(" << val.name << ", ref." << val.name << ");\n";
}
if (isFirstOutput)
output << dstVec4Var << " = vec4(1.0);\n";
else
output << dstVec4Var << " = vec4(RES, RES, RES, 1.0);\n";
}
string genFragmentShader (const ShaderCaseSpecification& spec)
{
ostringstream shader;
ostringstream setup;
int curInLoc = 0;
shader << glu::getGLSLVersionDeclaration(spec.targetVersion) << "\n";
shader << "precision highp float;\n";
shader << "precision highp int;\n";
shader << "\n";
shader << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
shader << "\n";
genCompareFunctions(shader, spec.values, false);
shader << "\n";
// Declarations (varying, reference for each output).
for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++)
{
const Value& val = spec.values.outputs[ndx];
const DataType valueType = val.type.getBasicType();
const char* const valueTypeStr = getDataTypeName(valueType);
const DataType transportType = getTransportType(valueType);
const char* const transportTypeStr = getDataTypeName(transportType);
const int numLocs = getNumTransportLocations(valueType);
shader << "layout(location = " << curInLoc << ") flat in " << transportTypeStr << " " << (valueType != transportType ? "v_" : "") << val.name << ";\n";
if (valueType != transportType)
setup << " " << valueTypeStr << " " << val.name << " = " << valueTypeStr << "(v_" << val.name << ");\n";
curInLoc += numLocs;
}
declareReferenceBlock(shader, spec.values);
shader << "\n";
shader << "void main()\n";
shader << "{\n";
shader << setup.str();
shader << " ";
genCompareOp(shader, "dEQP_FragColor", spec.values, DE_NULL);
shader << "}\n";
return shader.str();
}
// Specialize a shader for the vertex shader test case.
string specializeVertexShader (const ShaderCaseSpecification& spec, const string& src)
{
ostringstream decl;
ostringstream setup;
ostringstream output;
int curInputLoc = 0;
int curOutputLoc = 0;
// generated from "both" case
DE_ASSERT(spec.caseType == glu::sl::CASETYPE_VERTEX_ONLY);
// Output (write out position).
output << "gl_Position = dEQP_Position;\n";
// Declarations (position + attribute for each input, varying for each output).
decl << "layout(location = 0) in highp vec4 dEQP_Position;\n";
curInputLoc += 1;
for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++)
{
const Value& val = spec.values.inputs[ndx];
const DataType valueType = val.type.getBasicType();
const char* const valueTypeStr = getDataTypeName(valueType);
const DataType transportType = getTransportType(valueType);
const char* const transportTypeStr = getDataTypeName(transportType);
const int numLocs = getNumTransportLocations(valueType);
decl << "layout(location = " << curInputLoc << ") in ";
curInputLoc += numLocs;
if (valueType == transportType)
decl << transportTypeStr << " " << val.name << ";\n";
else
{
decl << transportTypeStr << " a_" << val.name << ";\n";
setup << valueTypeStr << " " << val.name << " = " << valueTypeStr << "(a_" << val.name << ");\n";
}
}
declareUniforms(decl, spec.values);
for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++)
{
const Value& val = spec.values.outputs[ndx];
const DataType valueType = val.type.getBasicType();
const char* const valueTypeStr = getDataTypeName(valueType);
const DataType transportType = getTransportType(valueType);
const char* const transportTypeStr = getDataTypeName(transportType);
const int numLocs = getNumTransportLocations(valueType);
decl << "layout(location = " << curOutputLoc << ") flat out ";
curOutputLoc += numLocs;
if (valueType == transportType)
decl << transportTypeStr << " " << val.name << ";\n";
else
{
decl << transportTypeStr << " v_" << val.name << ";\n";
decl << valueTypeStr << " " << val.name << ";\n";
output << "v_" << val.name << " = " << transportTypeStr << "(" << val.name << ");\n";
}
}
// Shader specialization.
map<string, string> params;
params.insert(pair<string, string>("DECLARATIONS", decl.str()));
params.insert(pair<string, string>("SETUP", setup.str()));
params.insert(pair<string, string>("OUTPUT", output.str()));
params.insert(pair<string, string>("POSITION_FRAG_COLOR", "gl_Position"));
StringTemplate tmpl (src);
const string baseSrc = tmpl.specialize(params);
const string withExt = injectExtensionRequirements(baseSrc, spec.programs[0].requiredExtensions, glu::SHADERTYPE_VERTEX);
return withExt;
}
// Specialize a shader for the fragment shader test case.
string specializeFragmentShader (const ShaderCaseSpecification& spec, const string& src)
{
ostringstream decl;
ostringstream setup;
ostringstream output;
int curInputLoc = 0;
// generated from "both" case
DE_ASSERT(spec.caseType == glu::sl::CASETYPE_FRAGMENT_ONLY);
genCompareFunctions(decl, spec.values, false);
genCompareOp(output, "dEQP_FragColor", spec.values, DE_NULL);
decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++)
{
const Value& val = spec.values.inputs[ndx];
const DataType valueType = val.type.getBasicType();
const char* const valueTypeStr = getDataTypeName(valueType);
const DataType transportType = getTransportType(valueType);
const char* const transportTypeStr = getDataTypeName(transportType);
const int numLocs = getNumTransportLocations(valueType);
decl << "layout(location = " << curInputLoc << ") flat in ";
curInputLoc += numLocs;
if (valueType == transportType)
decl << transportTypeStr << " " << val.name << ";\n";
else
{
decl << transportTypeStr << " v_" << val.name << ";\n";
setup << valueTypeStr << " " << val.name << " = " << valueTypeStr << "(v_" << val.name << ");\n";
}
}
declareUniforms(decl, spec.values);
declareReferenceBlock(decl, spec.values);
for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++)
{
const Value& val = spec.values.outputs[ndx];
const DataType basicType = val.type.getBasicType();
const char* const refTypeStr = getDataTypeName(basicType);
decl << refTypeStr << " " << val.name << ";\n";
}
// Shader specialization.
map<string, string> params;
params.insert(pair<string, string>("DECLARATIONS", decl.str()));
params.insert(pair<string, string>("SETUP", setup.str()));
params.insert(pair<string, string>("OUTPUT", output.str()));
params.insert(pair<string, string>("POSITION_FRAG_COLOR", "dEQP_FragColor"));
StringTemplate tmpl (src);
const string baseSrc = tmpl.specialize(params);
const string withExt = injectExtensionRequirements(baseSrc, spec.programs[0].requiredExtensions, glu::SHADERTYPE_FRAGMENT);
return withExt;
}
map<string, string> generateVertexSpecialization (const ProgramSpecializationParams& specParams)
{
ostringstream decl;
ostringstream setup;
map<string, string> params;
int curInputLoc = 0;
decl << "layout(location = 0) in highp vec4 dEQP_Position;\n";
curInputLoc += 1;
for (size_t ndx = 0; ndx < specParams.caseSpec.values.inputs.size(); ndx++)
{
const Value& val = specParams.caseSpec.values.inputs[ndx];
const DataType valueType = val.type.getBasicType();
const char* const valueTypeStr = getDataTypeName(valueType);
const DataType transportType = getTransportType(valueType);
const char* const transportTypeStr = getDataTypeName(transportType);
const int numLocs = getNumTransportLocations(valueType);
decl << "layout(location = " << curInputLoc << ") in ";
curInputLoc += numLocs;
if (valueType == transportType)
decl << transportTypeStr << " " << val.name << ";\n";
else
{
decl << transportTypeStr << " a_" << val.name << ";\n";
setup << valueTypeStr << " " << val.name << " = " << valueTypeStr << "(a_" << val.name << ");\n";
}
}
declareUniforms(decl, specParams.caseSpec.values);
params.insert(pair<string, string>("VERTEX_DECLARATIONS", decl.str()));
params.insert(pair<string, string>("VERTEX_SETUP", setup.str()));
params.insert(pair<string, string>("VERTEX_OUTPUT", string("gl_Position = dEQP_Position;\n")));
return params;
}
map<string, string> generateFragmentSpecialization (const ProgramSpecializationParams& specParams)
{
ostringstream decl;
ostringstream output;
map<string, string> params;
genCompareFunctions(decl, specParams.caseSpec.values, false);
genCompareOp(output, "dEQP_FragColor", specParams.caseSpec.values, DE_NULL);
decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
for (size_t ndx = 0; ndx < specParams.caseSpec.values.outputs.size(); ndx++)
{
const Value& val = specParams.caseSpec.values.outputs[ndx];
const char* const refTypeStr = getDataTypeName(val.type.getBasicType());
decl << refTypeStr << " " << val.name << ";\n";
}
declareReferenceBlock(decl, specParams.caseSpec.values);
declareUniforms(decl, specParams.caseSpec.values);
params.insert(pair<string, string>("FRAGMENT_DECLARATIONS", decl.str()));
params.insert(pair<string, string>("FRAGMENT_OUTPUT", output.str()));
params.insert(pair<string, string>("FRAG_COLOR", "dEQP_FragColor"));
return params;
}
map<string, string> generateGeometrySpecialization (const ProgramSpecializationParams& specParams)
{
ostringstream decl;
map<string, string> params;
decl << "layout (triangles) in;\n";
decl << "layout (triangle_strip, max_vertices=3) out;\n";
decl << "\n";
declareUniforms(decl, specParams.caseSpec.values);
params.insert(pair<string, string>("GEOMETRY_DECLARATIONS", decl.str()));
return params;
}
map<string, string> generateTessControlSpecialization (const ProgramSpecializationParams& specParams)
{
ostringstream decl;
ostringstream output;
map<string, string> params;
decl << "layout (vertices=3) out;\n";
decl << "\n";
declareUniforms(decl, specParams.caseSpec.values);
output << "gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
"gl_TessLevelInner[0] = 2.0;\n"
"gl_TessLevelInner[1] = 2.0;\n"
"gl_TessLevelOuter[0] = 2.0;\n"
"gl_TessLevelOuter[1] = 2.0;\n"
"gl_TessLevelOuter[2] = 2.0;\n"
"gl_TessLevelOuter[3] = 2.0;";
params.insert(pair<string, string>("TESSELLATION_CONTROL_DECLARATIONS", decl.str()));
params.insert(pair<string, string>("TESSELLATION_CONTROL_OUTPUT", output.str()));
params.insert(pair<string, string>("GL_MAX_PATCH_VERTICES", de::toString(specParams.maxPatchVertices)));
return params;
}
map<string, string> generateTessEvalSpecialization (const ProgramSpecializationParams& specParams)
{
ostringstream decl;
ostringstream output;
map<string, string> params;
decl << "layout (triangles) in;\n";
decl << "\n";
declareUniforms(decl, specParams.caseSpec.values);
output << "gl_Position = gl_TessCoord[0] * gl_in[0].gl_Position + gl_TessCoord[1] * gl_in[1].gl_Position + gl_TessCoord[2] * gl_in[2].gl_Position;\n";
params.insert(pair<string, string>("TESSELLATION_EVALUATION_DECLARATIONS", decl.str()));
params.insert(pair<string, string>("TESSELLATION_EVALUATION_OUTPUT", output.str()));
params.insert(pair<string, string>("GL_MAX_PATCH_VERTICES", de::toString(specParams.maxPatchVertices)));
return params;
}
void specializeShaderSources (ProgramSources& dst,
const ProgramSources& src,
const ProgramSpecializationParams& specParams,
glu::ShaderType shaderType,
map<string, string> (*specializationGenerator) (const ProgramSpecializationParams& specParams))
{
if (!src.sources[shaderType].empty())
{
const map<string, string> tmplParams = specializationGenerator(specParams);
for (size_t ndx = 0; ndx < src.sources[shaderType].size(); ++ndx)
{
const StringTemplate tmpl (src.sources[shaderType][ndx]);
const string baseGLSLCode = tmpl.specialize(tmplParams);
const string sourceWithExts = injectExtensionRequirements(baseGLSLCode, specParams.requiredExtensions, shaderType);
dst << glu::ShaderSource(shaderType, sourceWithExts);
}
}
}
void specializeProgramSources (glu::ProgramSources& dst,
const glu::ProgramSources& src,
const ProgramSpecializationParams& specParams)
{
specializeShaderSources(dst, src, specParams, glu::SHADERTYPE_VERTEX, generateVertexSpecialization);
specializeShaderSources(dst, src, specParams, glu::SHADERTYPE_FRAGMENT, generateFragmentSpecialization);
specializeShaderSources(dst, src, specParams, glu::SHADERTYPE_GEOMETRY, generateGeometrySpecialization);
specializeShaderSources(dst, src, specParams, glu::SHADERTYPE_TESSELLATION_CONTROL, generateTessControlSpecialization);
specializeShaderSources(dst, src, specParams, glu::SHADERTYPE_TESSELLATION_EVALUATION, generateTessEvalSpecialization);
dst << glu::ProgramSeparable(src.separable);
}
struct ValueBufferLayout
{
struct Entry
{
int offset;
int vecStride; //! Applies to matrices only
Entry (void) : offset(0), vecStride(0) {}
Entry (int offset_, int vecStride_) : offset(offset_), vecStride(vecStride_) {}
};
vector<Entry> entries;
int size;
ValueBufferLayout (void) : size(0) {}
};
ValueBufferLayout computeStd140Layout (const vector<Value>& values)
{
ValueBufferLayout layout;
layout.entries.resize(values.size());
for (size_t ndx = 0; ndx < values.size(); ++ndx)
{
const DataType basicType = values[ndx].type.getBasicType();
const bool isMatrix = isDataTypeMatrix(basicType);
const int numVecs = isMatrix ? getDataTypeMatrixNumColumns(basicType) : 1;
const DataType vecType = isMatrix ? glu::getDataTypeFloatVec(getDataTypeMatrixNumRows(basicType)) : basicType;
const int vecSize = getDataTypeScalarSize(vecType);
const int alignment = ((isMatrix || vecSize == 3) ? 4 : vecSize)*int(sizeof(deUint32));
layout.size = deAlign32(layout.size, alignment);
layout.entries[ndx] = ValueBufferLayout::Entry(layout.size, alignment);
layout.size += alignment*(numVecs-1) + vecSize*int(sizeof(deUint32));
}
return layout;
}
ValueBufferLayout computeStd430Layout (const vector<Value>& values)
{
ValueBufferLayout layout;
layout.entries.resize(values.size());
for (size_t ndx = 0; ndx < values.size(); ++ndx)
{
const DataType basicType = values[ndx].type.getBasicType();
const int numVecs = isDataTypeMatrix(basicType) ? getDataTypeMatrixNumColumns(basicType) : 1;
const DataType vecType = isDataTypeMatrix(basicType) ? glu::getDataTypeFloatVec(getDataTypeMatrixNumRows(basicType)) : basicType;
const int vecSize = getDataTypeScalarSize(vecType);
const int alignment = (vecSize == 3 ? 4 : vecSize)*int(sizeof(deUint32));
layout.size = deAlign32(layout.size, alignment);
layout.entries[ndx] = ValueBufferLayout::Entry(layout.size, alignment);
layout.size += alignment*(numVecs-1) + vecSize*int(sizeof(deUint32));
}
return layout;
}
void copyToLayout (void* dst, const ValueBufferLayout::Entry& entryLayout, const Value& value, int arrayNdx)
{
const DataType basicType = value.type.getBasicType();
const int scalarSize = getDataTypeScalarSize(basicType);
const int numVecs = isDataTypeMatrix(basicType) ? getDataTypeMatrixNumColumns(basicType) : 1;
const int numComps = isDataTypeMatrix(basicType) ? getDataTypeMatrixNumRows(basicType) : scalarSize;
DE_ASSERT(size_t((arrayNdx+1)*scalarSize) <= value.elements.size());
if (isDataTypeBoolOrBVec(basicType))
{
for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
{
for (int compNdx = 0; compNdx < numComps; compNdx++)
{
const deUint32 data = value.elements[arrayNdx*scalarSize + vecNdx*numComps + compNdx].bool32 ? ~0u : 0u;
deMemcpy((deUint8*)dst + entryLayout.offset + vecNdx*entryLayout.vecStride + compNdx * sizeof(deUint32),
&data,
sizeof(deUint32));
}
}
}
else
{
for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
deMemcpy((deUint8*)dst + entryLayout.offset + vecNdx*entryLayout.vecStride,
&value.elements[arrayNdx*scalarSize + vecNdx*numComps],
numComps*sizeof(deUint32));
}
}
void copyToLayout (void* dst, const ValueBufferLayout& layout, const vector<Value>& values, int arrayNdx)
{
DE_ASSERT(layout.entries.size() == values.size());
for (size_t ndx = 0; ndx < values.size(); ndx++)
copyToLayout(dst, layout.entries[ndx], values[ndx], arrayNdx);
}
deUint32 getShaderStages (const ShaderCaseSpecification& spec)
{
if (spec.caseType == glu::sl::CASETYPE_COMPLETE)
{
deUint32 stages = 0u;
for (size_t progNdx = 0; progNdx < spec.programs.size(); progNdx++)
{
for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
{
if (!spec.programs[progNdx].sources.sources[shaderType].empty())
stages |= (1u << shaderType);
}
}
return stages;
}
else
return (1u << glu::SHADERTYPE_VERTEX) | (1u << glu::SHADERTYPE_FRAGMENT);
}
class PipelineProgram
{
public:
PipelineProgram (Context& context, const ShaderCaseSpecification& spec);
deUint32 getStages (void) const { return m_stages; }
bool hasShader (glu::ShaderType type) const { return (m_stages & (1u << type)) != 0; }
vk::VkShaderModule getShader (glu::ShaderType type) const { return *m_shaderModules[type]; }
private:
const deUint32 m_stages;
Move<vk::VkShaderModule> m_shaderModules[glu::SHADERTYPE_LAST];
};
PipelineProgram::PipelineProgram (Context& context, const ShaderCaseSpecification& spec)
: m_stages(getShaderStages(spec))
{
// \note Currently only a single source program is supported as framework lacks SPIR-V linking capability
TCU_CHECK_INTERNAL(spec.programs.size() == 1);
for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
{
if ((m_stages & (1u << shaderType)) != 0)
{
m_shaderModules[shaderType] = vk::createShaderModule(context.getDeviceInterface(), context.getDevice(),
context.getBinaryCollection().get(getShaderName((glu::ShaderType)shaderType, 0)), 0u);
}
}
}
Move<vk::VkBuffer> createBuffer (Context& context, vk::VkDeviceSize size, vk::VkBufferUsageFlags usageFlags)
{
const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
const vk::VkBufferCreateInfo params =
{
vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // sType
DE_NULL, // pNext
0u, // flags
size, // size
usageFlags, // usage
vk::VK_SHARING_MODE_EXCLUSIVE, // sharingMode
1u, // queueFamilyCount
&queueFamilyIndex, // pQueueFamilyIndices
};
return vk::createBuffer(context.getDeviceInterface(), context.getDevice(), &params);
}
Move<vk::VkImage> createImage2D (Context& context, deUint32 width, deUint32 height, vk::VkFormat format, vk::VkImageTiling tiling, vk::VkImageUsageFlags usageFlags)
{
const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
const vk::VkImageCreateInfo params =
{
vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // sType
DE_NULL, // pNext
0u, // flags
vk::VK_IMAGE_TYPE_2D, // imageType
format, // format
{ width, height, 1u }, // extent
1u, // mipLevels
1u, // arraySize
vk::VK_SAMPLE_COUNT_1_BIT, // samples
tiling, // tiling
usageFlags, // usage
vk::VK_SHARING_MODE_EXCLUSIVE, // sharingMode
1u, // queueFamilyCount
&queueFamilyIndex, // pQueueFamilyIndices
vk::VK_IMAGE_LAYOUT_UNDEFINED, // initialLayout
};
return vk::createImage(context.getDeviceInterface(), context.getDevice(), &params);
}
Move<vk::VkImageView> createAttachmentView (Context& context, vk::VkImage image, vk::VkFormat format)
{
const vk::VkImageViewCreateInfo params =
{
vk::VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // sType
DE_NULL, // pNext
0u, // flags
image, // image
vk::VK_IMAGE_VIEW_TYPE_2D, // viewType
format, // format
vk::makeComponentMappingRGBA(), // channels
{
vk::VK_IMAGE_ASPECT_COLOR_BIT, // aspectMask
0u, // baseMipLevel
1u, // mipLevels
0u, // baseArrayLayer
1u, // arraySize
}, // subresourceRange
};
return vk::createImageView(context.getDeviceInterface(), context.getDevice(), &params);
}
Move<vk::VkRenderPass> createRenderPass (Context& context, vk::VkFormat colorAttFormat, deUint32 size)
{
vk::VkAttachmentDescription colorAttDesc[4];
vk::VkAttachmentReference colorAttRef[4];
for (deUint32 i = 0; i < size; i++)
{
vk::VkAttachmentDescription desc =
{
0u, // flags
colorAttFormat, // format
vk::VK_SAMPLE_COUNT_1_BIT, // samples
vk::VK_ATTACHMENT_LOAD_OP_CLEAR, // loadOp
vk::VK_ATTACHMENT_STORE_OP_STORE, // storeOp
vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE, // stencilLoadOp
vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencilStoreOp
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // initialLayout
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // finalLayout
};
colorAttDesc[i] = desc;
vk::VkAttachmentReference ref =
{
i, // attachment
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // layout
};
colorAttRef[i] = ref;
}
const vk::VkAttachmentReference dsAttRef =
{
VK_ATTACHMENT_UNUSED, // attachment
vk::VK_IMAGE_LAYOUT_GENERAL, // layout
};
const vk::VkSubpassDescription subpassDesc =
{
(vk::VkSubpassDescriptionFlags)0,
vk::VK_PIPELINE_BIND_POINT_GRAPHICS, // pipelineBindPoint
0u, // inputCount
DE_NULL, // pInputAttachments
size, // colorCount
&colorAttRef[0], // pColorAttachments
DE_NULL, // pResolveAttachments
&dsAttRef, // depthStencilAttachment
0u, // preserveCount
DE_NULL, // pPreserveAttachments
};
const vk::VkRenderPassCreateInfo renderPassParams =
{
vk::VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // sType
DE_NULL, // pNext
(vk::VkRenderPassCreateFlags)0,
size, // attachmentCount
&colorAttDesc[0], // pAttachments
1u, // subpassCount
&subpassDesc, // pSubpasses
0u, // dependencyCount
DE_NULL, // pDependencies
};
return vk::createRenderPass(context.getDeviceInterface(), context.getDevice(), &renderPassParams);
}
vk::VkShaderStageFlags getVkStageFlags (deUint32 stages)
{
vk::VkShaderStageFlags vkStages = 0u;
for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
{
if ((stages & (1u << shaderType)) != 0)
vkStages |= vk::getVkShaderStage((glu::ShaderType)shaderType);
}
return vkStages;
}
Move<vk::VkDescriptorSetLayout> createDescriptorSetLayout (Context& context, deUint32 shaderStages)
{
DE_STATIC_ASSERT(REFERENCE_UNIFORM_BINDING == 0);
DE_STATIC_ASSERT(USER_UNIFORM_BINDING == 1);
return vk::DescriptorSetLayoutBuilder()
.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk::VK_SHADER_STAGE_FRAGMENT_BIT)
.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, getVkStageFlags(shaderStages))
.build(context.getDeviceInterface(), context.getDevice());
}
Move<vk::VkPipelineLayout> createPipelineLayout (Context& context, vk::VkDescriptorSetLayout descriptorSetLayout)
{
const vk::VkPipelineLayoutCreateInfo params =
{
vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // sType
DE_NULL, // pNext
(vk::VkPipelineLayoutCreateFlags)0,
1u, // descriptorSetCount
&descriptorSetLayout, // pSetLayouts
0u, // pushConstantRangeCount
DE_NULL, // pPushConstantRanges
};
return vk::createPipelineLayout(context.getDeviceInterface(), context.getDevice(), &params);
}
vk::VkFormat getVecFormat (DataType scalarType, int scalarSize)
{
switch (scalarType)
{
case glu::TYPE_FLOAT:
{
const vk::VkFormat vecFmts[] =
{
vk::VK_FORMAT_R32_SFLOAT,
vk::VK_FORMAT_R32G32_SFLOAT,
vk::VK_FORMAT_R32G32B32_SFLOAT,
vk::VK_FORMAT_R32G32B32A32_SFLOAT,
};
return de::getSizedArrayElement<4>(vecFmts, scalarSize-1);
}
case glu::TYPE_INT:
{
const vk::VkFormat vecFmts[] =
{
vk::VK_FORMAT_R32_SINT,
vk::VK_FORMAT_R32G32_SINT,
vk::VK_FORMAT_R32G32B32_SINT,
vk::VK_FORMAT_R32G32B32A32_SINT,
};
return de::getSizedArrayElement<4>(vecFmts, scalarSize-1);
}
case glu::TYPE_UINT:
{
const vk::VkFormat vecFmts[] =
{
vk::VK_FORMAT_R32_UINT,
vk::VK_FORMAT_R32G32_UINT,
vk::VK_FORMAT_R32G32B32_UINT,
vk::VK_FORMAT_R32G32B32A32_UINT,
};
return de::getSizedArrayElement<4>(vecFmts, scalarSize-1);
}
case glu::TYPE_BOOL:
{
const vk::VkFormat vecFmts[] =
{
vk::VK_FORMAT_R32_UINT,
vk::VK_FORMAT_R32G32_UINT,
vk::VK_FORMAT_R32G32B32_UINT,
vk::VK_FORMAT_R32G32B32A32_UINT,
};
return de::getSizedArrayElement<4>(vecFmts, scalarSize-1);
}
default:
DE_FATAL("Unknown scalar type");
return vk::VK_FORMAT_R8G8B8A8_UINT;
}
}
vector<vk::VkVertexInputAttributeDescription> getVertexAttributeDescriptions (const vector<Value>& inputValues, const ValueBufferLayout& layout)
{
vector<vk::VkVertexInputAttributeDescription> attribs;
// Position
{
const vk::VkVertexInputAttributeDescription posDesc =
{
0u, // location
0u, // binding
vk::VK_FORMAT_R32G32_SFLOAT, // format
0u, // offset
};
attribs.push_back(posDesc);
}
// Input values
for (size_t inputNdx = 0; inputNdx < inputValues.size(); inputNdx++)
{
const Value& input = inputValues[inputNdx];
const ValueBufferLayout::Entry& layoutEntry = layout.entries[inputNdx];
const DataType basicType = input.type.getBasicType();
const int numVecs = isDataTypeMatrix(basicType)
? getDataTypeMatrixNumColumns(basicType)
: 1;
const int vecSize = isDataTypeMatrix(basicType)
? getDataTypeMatrixNumRows(basicType)
: getDataTypeScalarSize(basicType);
const DataType scalarType = getDataTypeScalarType(basicType);
const vk::VkFormat vecFmt = getVecFormat(scalarType, vecSize);
for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
{
const deUint32 curLoc = (deUint32)attribs.size();
const deUint32 offset = (deUint32)(layoutEntry.offset + layoutEntry.vecStride*vecNdx);
const vk::VkVertexInputAttributeDescription desc =
{
curLoc, // location
1u, // binding
vecFmt, // format
offset, // offset
};
attribs.push_back(desc);
}
}
return attribs;
}
Move<vk::VkPipeline> createPipeline (Context& context,
const vector<Value>& inputValues,
const ValueBufferLayout& inputLayout,
const PipelineProgram& program,
vk::VkRenderPass renderPass,
vk::VkPipelineLayout pipelineLayout,
tcu::UVec2 renderSize,
deUint32 size)
{
const vk::VkShaderModule vertShader = program.hasShader(glu::SHADERTYPE_VERTEX) ? program.getShader(glu::SHADERTYPE_VERTEX) : DE_NULL;
const vk::VkShaderModule tessControlShader = program.hasShader(glu::SHADERTYPE_TESSELLATION_CONTROL) ? program.getShader(glu::SHADERTYPE_TESSELLATION_CONTROL) : DE_NULL;
const vk::VkShaderModule tessEvalShader = program.hasShader(glu::SHADERTYPE_TESSELLATION_EVALUATION) ? program.getShader(glu::SHADERTYPE_TESSELLATION_EVALUATION) : DE_NULL;
const vk::VkShaderModule geomShader = program.hasShader(glu::SHADERTYPE_GEOMETRY) ? program.getShader(glu::SHADERTYPE_GEOMETRY) : DE_NULL;
const vk::VkShaderModule fragShader = program.hasShader(glu::SHADERTYPE_FRAGMENT) ? program.getShader(glu::SHADERTYPE_FRAGMENT) : DE_NULL;
const vector<vk::VkVertexInputAttributeDescription> vertexAttribParams (getVertexAttributeDescriptions(inputValues, inputLayout));
const vector<vk::VkViewport> viewports (1, vk::makeViewport(renderSize));
const vector<vk::VkRect2D> scissors (1, vk::makeRect2D(renderSize));
const vk::VkVertexInputBindingDescription vertexBindings[] =
{
{
0u, // binding
(deUint32)sizeof(tcu::Vec2), // stride
vk::VK_VERTEX_INPUT_RATE_VERTEX, // stepRate
},
{
1u, // binding
0u, // stride
vk::VK_VERTEX_INPUT_RATE_INSTANCE, // stepRate
},
};
const vk::VkPipelineVertexInputStateCreateInfo vertexInputStateParams =
{
vk::VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // sType
DE_NULL, // pNext
(vk::VkPipelineVertexInputStateCreateFlags)0,
(inputValues.empty() ? 1u : 2u), // bindingCount
vertexBindings, // pVertexBindingDescriptions
(deUint32)vertexAttribParams.size(), // attributeCount
&vertexAttribParams[0], // pVertexAttributeDescriptions
};
const vk::VkColorComponentFlags allCompMask = vk::VK_COLOR_COMPONENT_R_BIT
| vk::VK_COLOR_COMPONENT_G_BIT
| vk::VK_COLOR_COMPONENT_B_BIT
| vk::VK_COLOR_COMPONENT_A_BIT;
vk::VkPipelineColorBlendAttachmentState attBlendParams[4];
for (deUint32 i = 0; i < size; i++)
{
vk::VkPipelineColorBlendAttachmentState blend =
{
VK_FALSE, // blendEnable
vk::VK_BLEND_FACTOR_ONE, // srcBlendColor
vk::VK_BLEND_FACTOR_ZERO, // destBlendColor
vk::VK_BLEND_OP_ADD, // blendOpColor
vk::VK_BLEND_FACTOR_ONE, // srcBlendAlpha
vk::VK_BLEND_FACTOR_ZERO, // destBlendAlpha
vk::VK_BLEND_OP_ADD, // blendOpAlpha
allCompMask, // componentWriteMask
};
attBlendParams[i] = blend;
}
const vk::VkPipelineColorBlendStateCreateInfo blendParams =
{
vk::VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // sType
DE_NULL, // pNext
(vk::VkPipelineColorBlendStateCreateFlags)0,
VK_FALSE, // logicOpEnable
vk::VK_LOGIC_OP_COPY, // logicOp
size, // attachmentCount
&attBlendParams[0], // pAttachments
{ 0.0f, 0.0f, 0.0f, 0.0f }, // blendConstants
};
return vk::makeGraphicsPipeline(context.getDeviceInterface(), // const DeviceInterface& vk
context.getDevice(), // const VkDevice device
pipelineLayout, // const VkPipelineLayout pipelineLayout
vertShader, // const VkShaderModule vertexShaderModule
tessControlShader, // const VkShaderModule tessellationControlShaderModule
tessEvalShader, // const VkShaderModule tessellationEvalShaderModule
geomShader, // const VkShaderModule geometryShaderModule
fragShader, // const VkShaderModule fragmentShaderModule
renderPass, // const VkRenderPass renderPass
viewports, // const std::vector<VkViewport>& viewports
scissors, // const std::vector<VkRect2D>& scissors
vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // const VkPrimitiveTopology topology
0u, // const deUint32 subpass
0u, // const deUint32 patchControlPoints
&vertexInputStateParams, // const VkPipelineVertexInputStateCreateInfo* vertexInputStateCreateInfo
DE_NULL, // const VkPipelineRasterizationStateCreateInfo* rasterizationStateCreateInfo
DE_NULL, // const VkPipelineMultisampleStateCreateInfo* multisampleStateCreateInfo
DE_NULL, // const VkPipelineDepthStencilStateCreateInfo* depthStencilStateCreateInfo
&blendParams); // const VkPipelineColorBlendStateCreateInfo* colorBlendStateCreateInfo
}
Move<vk::VkFramebuffer> createFramebuffer (Context& context, vk::VkRenderPass renderPass, Move<vk::VkImageView> colorAttView[4], deUint32 size, int width, int height)
{
vk::VkImageView att[4];
for (deUint32 i = 0; i < size; i++)
{
att[i] = *colorAttView[i];
}
const vk::VkFramebufferCreateInfo framebufferParams =
{
vk::VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // sType
DE_NULL, // pNext
(vk::VkFramebufferCreateFlags)0,
renderPass, // renderPass
size, // attachmentCount
&att[0], // pAttachments
(deUint32)width, // width
(deUint32)height, // height
1u, // layers
};
return vk::createFramebuffer(context.getDeviceInterface(), context.getDevice(), &framebufferParams);
}
Move<vk::VkCommandPool> createCommandPool (Context& context)
{
const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
return vk::createCommandPool(context.getDeviceInterface(), context.getDevice(), (vk::VkCommandPoolCreateFlags)0u, queueFamilyIndex);
}
Move<vk::VkDescriptorPool> createDescriptorPool (Context& context)
{
return vk::DescriptorPoolBuilder()
.addType(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2u)
.build(context.getDeviceInterface(), context.getDevice(), vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
}
Move<vk::VkDescriptorSet> allocateDescriptorSet (Context& context, vk::VkDescriptorPool descriptorPool, vk::VkDescriptorSetLayout setLayout)
{
const vk::VkDescriptorSetAllocateInfo params =
{
vk::VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
DE_NULL,
descriptorPool,
1u,
&setLayout
};
return vk::allocateDescriptorSet(context.getDeviceInterface(), context.getDevice(), &params);
}
Move<vk::VkCommandBuffer> allocateCommandBuffer (Context& context, vk::VkCommandPool cmdPool)
{
return vk::allocateCommandBuffer(context.getDeviceInterface(), context.getDevice(), cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
}
MovePtr<vk::Allocation> allocateAndBindMemory (Context& context, vk::VkBuffer buffer, vk::MemoryRequirement memReqs)
{
const vk::DeviceInterface& vkd = context.getDeviceInterface();
const vk::VkMemoryRequirements bufReqs = vk::getBufferMemoryRequirements(vkd, context.getDevice(), buffer);
MovePtr<vk::Allocation> memory = context.getDefaultAllocator().allocate(bufReqs, memReqs);
vkd.bindBufferMemory(context.getDevice(), buffer, memory->getMemory(), memory->getOffset());
return memory;
}
vk::VkFormat getRenderTargetFormat (DataType dataType)
{
switch (dataType)
{
case glu::TYPE_FLOAT_VEC2:
return vk::VK_FORMAT_R8G8_UNORM;
case glu::TYPE_FLOAT_VEC3:
return vk::VK_FORMAT_R5G6B5_UNORM_PACK16;
case glu::TYPE_FLOAT_VEC4:
return vk::VK_FORMAT_R8G8B8A8_UNORM;
case glu::TYPE_INT_VEC2:
return vk::VK_FORMAT_R8G8_SINT;
case glu::TYPE_INT_VEC4:
return vk::VK_FORMAT_R8G8B8A8_SINT;
default:
return vk::VK_FORMAT_R8G8B8A8_UNORM;
}
}
MovePtr<vk::Allocation> allocateAndBindMemory (Context& context, vk::VkImage image, vk::MemoryRequirement memReqs)
{
const vk::DeviceInterface& vkd = context.getDeviceInterface();
const vk::VkMemoryRequirements imgReqs = vk::getImageMemoryRequirements(vkd, context.getDevice(), image);
MovePtr<vk::Allocation> memory = context.getDefaultAllocator().allocate(imgReqs, memReqs);
vkd.bindImageMemory(context.getDevice(), image, memory->getMemory(), memory->getOffset());
return memory;
}
void writeValuesToMem (Context& context, const vk::Allocation& dst, const ValueBufferLayout& layout, const vector<Value>& values, int arrayNdx)
{
copyToLayout(dst.getHostPtr(), layout, values, arrayNdx);
// \note Buffers are not allocated with coherency / uncached requirement so we need to manually flush CPU write caches
flushAlloc(context.getDeviceInterface(), context.getDevice(), dst);
}
class ShaderCaseInstance : public TestInstance
{
public:
ShaderCaseInstance (Context& context, const ShaderCaseSpecification& spec);
~ShaderCaseInstance (void);
TestStatus iterate (void);
private:
enum
{
RENDER_WIDTH = 64,
RENDER_HEIGHT = 64,
POSITIONS_OFFSET = 0,
POSITIONS_SIZE = (int)sizeof(Vec2)*4,
INDICES_OFFSET = POSITIONS_SIZE,
INDICES_SIZE = (int)sizeof(deUint16)*6,
TOTAL_POS_NDX_SIZE = POSITIONS_SIZE+INDICES_SIZE
};
const ShaderCaseSpecification& m_spec;
const Unique<vk::VkBuffer> m_posNdxBuffer;
const UniquePtr<vk::Allocation> m_posNdxMem;
const ValueBufferLayout m_inputLayout;
const Unique<vk::VkBuffer> m_inputBuffer; // Input values (attributes). Can be NULL if no inputs present
const UniquePtr<vk::Allocation> m_inputMem; // Input memory, can be NULL if no input buffer exists
const ValueBufferLayout m_referenceLayout;
const Unique<vk::VkBuffer> m_referenceBuffer; // Output (reference) values. Can be NULL if no outputs present
const UniquePtr<vk::Allocation> m_referenceMem; // Output (reference) memory, can be NULL if no reference buffer exists
const ValueBufferLayout m_uniformLayout;
const Unique<vk::VkBuffer> m_uniformBuffer; // Uniform values. Can be NULL if no uniforms present
const UniquePtr<vk::Allocation> m_uniformMem; // Uniform memory, can be NULL if no uniform buffer exists
const vk::VkFormat m_rtFormat;
deUint32 m_outputCount;
Move<vk::VkImage> m_rtImage [4];
MovePtr<vk::Allocation> m_rtMem[4];
Move<vk::VkImageView> m_rtView[4];
Move<vk::VkBuffer> m_readImageBuffer[4];
MovePtr<vk::Allocation> m_readImageMem[4];
const Unique<vk::VkRenderPass> m_renderPass;
Move<vk::VkFramebuffer> m_framebuffer;
const PipelineProgram m_program;
const Unique<vk::VkDescriptorSetLayout> m_descriptorSetLayout;
const Unique<vk::VkPipelineLayout> m_pipelineLayout;
const Unique<vk::VkPipeline> m_pipeline;
const Unique<vk::VkDescriptorPool> m_descriptorPool;
const Unique<vk::VkDescriptorSet> m_descriptorSet;
const Unique<vk::VkCommandPool> m_cmdPool;
const Unique<vk::VkCommandBuffer> m_cmdBuffer;
int m_subCaseNdx;
};
ShaderCaseInstance::ShaderCaseInstance (Context& context, const ShaderCaseSpecification& spec)
: TestInstance (context)
, m_spec (spec)
, m_posNdxBuffer (createBuffer(context, (vk::VkDeviceSize)TOTAL_POS_NDX_SIZE, vk::VK_BUFFER_USAGE_INDEX_BUFFER_BIT|vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT))
, m_posNdxMem (allocateAndBindMemory(context, *m_posNdxBuffer, vk::MemoryRequirement::HostVisible))
, m_inputLayout (computeStd430Layout(spec.values.inputs))
, m_inputBuffer (m_inputLayout.size > 0 ? createBuffer(context, (vk::VkDeviceSize)m_inputLayout.size, vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) : Move<vk::VkBuffer>())
, m_inputMem (m_inputLayout.size > 0 ? allocateAndBindMemory(context, *m_inputBuffer, vk::MemoryRequirement::HostVisible) : MovePtr<vk::Allocation>())
, m_referenceLayout (computeStd140Layout(spec.values.outputs))
, m_referenceBuffer (m_referenceLayout.size > 0 ? createBuffer(context, (vk::VkDeviceSize)m_referenceLayout.size, vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) : Move<vk::VkBuffer>())
, m_referenceMem (m_referenceLayout.size > 0 ? allocateAndBindMemory(context, *m_referenceBuffer, vk::MemoryRequirement::HostVisible) : MovePtr<vk::Allocation>())
, m_uniformLayout (computeStd140Layout(spec.values.uniforms))
, m_uniformBuffer (m_uniformLayout.size > 0 ? createBuffer(context, (vk::VkDeviceSize)m_uniformLayout.size, vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) : Move<vk::VkBuffer>())
, m_uniformMem (m_uniformLayout.size > 0 ? allocateAndBindMemory(context, *m_uniformBuffer, vk::MemoryRequirement::HostVisible) : MovePtr<vk::Allocation>())
, m_rtFormat (getRenderTargetFormat(spec.outputFormat))
, m_outputCount (((deUint32)m_spec.values.outputs.size() == 0 || m_spec.outputType == glu::sl::OUTPUT_RESULT) ? 1 : (deUint32)m_spec.values.outputs.size())
, m_rtImage ()
, m_rtMem ()
, m_rtView ()
, m_readImageBuffer ()
, m_readImageMem ()
, m_renderPass (createRenderPass(context, m_rtFormat, m_outputCount))
, m_framebuffer ()
, m_program (context, spec)
, m_descriptorSetLayout (createDescriptorSetLayout(context, m_program.getStages()))
, m_pipelineLayout (createPipelineLayout(context, *m_descriptorSetLayout))
, m_pipeline (createPipeline(context, spec.values.inputs, m_inputLayout, m_program, *m_renderPass, *m_pipelineLayout, tcu::UVec2(RENDER_WIDTH, RENDER_HEIGHT), m_outputCount))
, m_descriptorPool (createDescriptorPool(context))
, m_descriptorSet (allocateDescriptorSet(context, *m_descriptorPool, *m_descriptorSetLayout))
, m_cmdPool (createCommandPool(context))
, m_cmdBuffer (allocateCommandBuffer(context, *m_cmdPool))
, m_subCaseNdx (0)
{
{
// Initialize the resources for each color attachment needed by the shader
for (deUint32 outNdx = 0; outNdx < m_outputCount; outNdx++)
{
m_rtImage[outNdx] = createImage2D(context, RENDER_WIDTH, RENDER_HEIGHT, m_rtFormat, vk::VK_IMAGE_TILING_OPTIMAL, vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT| vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
m_rtMem[outNdx] = allocateAndBindMemory(context, *m_rtImage[outNdx], vk::MemoryRequirement::Any);
m_rtView[outNdx] = createAttachmentView(context, *m_rtImage[outNdx], m_rtFormat);
m_readImageBuffer[outNdx] = createBuffer(context, (vk::VkDeviceSize)(RENDER_WIDTH * RENDER_HEIGHT * tcu::getPixelSize(vk::mapVkFormat(m_rtFormat))), vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT);
m_readImageMem[outNdx] = allocateAndBindMemory(context, *m_readImageBuffer[outNdx], vk::MemoryRequirement::HostVisible);
}
m_framebuffer = createFramebuffer(context, *m_renderPass, m_rtView, m_outputCount, RENDER_WIDTH, RENDER_HEIGHT);
}
const vk::DeviceInterface& vkd = context.getDeviceInterface();
const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
{
const Vec2 s_positions[] =
{
Vec2(-1.0f, -1.0f),
Vec2(-1.0f, +1.0f),
Vec2(+1.0f, -1.0f),
Vec2(+1.0f, +1.0f)
};
const deUint16 s_indices[] =
{
0, 1, 2,
1, 3, 2
};
DE_STATIC_ASSERT(sizeof(s_positions) == POSITIONS_SIZE);
DE_STATIC_ASSERT(sizeof(s_indices) == INDICES_SIZE);
deMemcpy((deUint8*)m_posNdxMem->getHostPtr() + POSITIONS_OFFSET, &s_positions[0], sizeof(s_positions));
deMemcpy((deUint8*)m_posNdxMem->getHostPtr() + INDICES_OFFSET, &s_indices[0], sizeof(s_indices));
flushAlloc(m_context.getDeviceInterface(), context.getDevice(), *m_posNdxMem);
}
if (!m_spec.values.uniforms.empty())
{
const vk::VkDescriptorBufferInfo bufInfo =
{
*m_uniformBuffer,
(vk::VkDeviceSize)0, // offset
(vk::VkDeviceSize)m_uniformLayout.size
};
vk::DescriptorSetUpdateBuilder()
.writeSingle(*m_descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(USER_UNIFORM_BINDING),
vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &bufInfo)
.update(vkd, m_context.getDevice());
}
if (!m_spec.values.outputs.empty())
{
const vk::VkDescriptorBufferInfo bufInfo =
{
*m_referenceBuffer,
(vk::VkDeviceSize)0, // offset
(vk::VkDeviceSize)m_referenceLayout.size
};
vk::DescriptorSetUpdateBuilder()
.writeSingle(*m_descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(REFERENCE_UNIFORM_BINDING),
vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &bufInfo)
.update(vkd, m_context.getDevice());
}
// Record command buffer
beginCommandBuffer(vkd, *m_cmdBuffer, 0u);
{
const vk::VkMemoryBarrier vertFlushBarrier =
{
vk::VK_STRUCTURE_TYPE_MEMORY_BARRIER, // sType
DE_NULL, // pNext
vk::VK_ACCESS_HOST_WRITE_BIT, // srcAccessMask
vk::VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT|vk::VK_ACCESS_UNIFORM_READ_BIT, // dstAccessMask
};
vk::VkImageMemoryBarrier colorAttBarrier [4];
for (deUint32 outNdx = 0; outNdx < m_outputCount; outNdx++)
{
vk::VkImageMemoryBarrier barrier =
{
vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
DE_NULL, // pNext
0u, // srcAccessMask
vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // dstAccessMask
vk::VK_IMAGE_LAYOUT_UNDEFINED, // oldLayout
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // newLayout
queueFamilyIndex, // srcQueueFamilyIndex
queueFamilyIndex, // destQueueFamilyIndex
*m_rtImage[outNdx], // image
{
vk::VK_IMAGE_ASPECT_COLOR_BIT, // aspectMask
0u, // baseMipLevel
1u, // mipLevels
0u, // baseArraySlice
1u, // arraySize
} // subresourceRange
};
colorAttBarrier[outNdx] = barrier;
}
vkd.cmdPipelineBarrier(*m_cmdBuffer, vk::VK_PIPELINE_STAGE_HOST_BIT, vk::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, (vk::VkDependencyFlags)0,
1, &vertFlushBarrier,
0, (const vk::VkBufferMemoryBarrier*)DE_NULL,
m_outputCount, &colorAttBarrier[0]);
}
{
vk::VkClearValue clearValue[4];
for (deUint32 outNdx = 0; outNdx < m_outputCount; outNdx++)
{
vk::VkClearValue value = vk::makeClearValueColorF32(0.125f, 0.25f, 0.75f, 1.0f);
clearValue[outNdx] = value;
}
beginRenderPass(vkd, *m_cmdBuffer, *m_renderPass, *m_framebuffer, vk::makeRect2D(0, 0, RENDER_WIDTH, RENDER_HEIGHT), m_outputCount, clearValue);
}
vkd.cmdBindPipeline(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
if (!m_spec.values.uniforms.empty() || !m_spec.values.outputs.empty())
vkd.cmdBindDescriptorSets(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelineLayout, 0u, 1u, &*m_descriptorSet, 0u, DE_NULL);
{
const vk::VkBuffer buffers[] = { *m_posNdxBuffer, *m_inputBuffer };
const vk::VkDeviceSize offsets[] = { POSITIONS_OFFSET, 0u };
const deUint32 numBuffers = buffers[1] != 0 ? 2u : 1u;
vkd.cmdBindVertexBuffers(*m_cmdBuffer, 0u, numBuffers, buffers, offsets);
}
vkd.cmdBindIndexBuffer (*m_cmdBuffer, *m_posNdxBuffer, (vk::VkDeviceSize)INDICES_OFFSET, vk::VK_INDEX_TYPE_UINT16);
vkd.cmdDrawIndexed (*m_cmdBuffer, 6u, 1u, 0u, 0u, 0u);
endRenderPass (vkd, *m_cmdBuffer);
{
vk::VkImageMemoryBarrier renderFinishBarrier[4];
for (deUint32 outNdx = 0; outNdx < m_outputCount; outNdx++)
{
vk::VkImageMemoryBarrier barrier =
{
vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
DE_NULL, // pNext
vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // srcAccessMask
vk::VK_ACCESS_TRANSFER_READ_BIT, // dstAccessMask
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // oldLayout
vk::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // newLayout
queueFamilyIndex, // srcQueueFamilyIndex
queueFamilyIndex, // destQueueFamilyIndex
*m_rtImage[outNdx], // image
{
vk::VK_IMAGE_ASPECT_COLOR_BIT, // aspectMask
0u, // baseMipLevel
1u, // mipLevels
0u, // baseArraySlice
1u, // arraySize
} // subresourceRange
};
renderFinishBarrier[outNdx] = barrier;
}
vkd.cmdPipelineBarrier(*m_cmdBuffer, vk::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, (vk::VkDependencyFlags)0,
0, (const vk::VkMemoryBarrier*)DE_NULL,
0, (const vk::VkBufferMemoryBarrier*)DE_NULL,
m_outputCount, &renderFinishBarrier[0]);
}
{
for (deUint32 outNdx = 0; outNdx < m_outputCount; outNdx++)
{
const vk::VkBufferImageCopy copyParams =
{
(vk::VkDeviceSize)0u, // bufferOffset
(deUint32)RENDER_WIDTH, // bufferRowLength
(deUint32)RENDER_HEIGHT, // bufferImageHeight
{
vk::VK_IMAGE_ASPECT_COLOR_BIT, // aspect
0u, // mipLevel
0u, // arrayLayer
1u, // arraySize
}, // imageSubresource
{ 0u, 0u, 0u }, // imageOffset
{ RENDER_WIDTH, RENDER_HEIGHT, 1u } // imageExtent
};
vkd.cmdCopyImageToBuffer(*m_cmdBuffer, *m_rtImage[outNdx], vk::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *m_readImageBuffer[outNdx], 1u, &copyParams);
}
}
{
const vk::VkDeviceSize size = (vk::VkDeviceSize)(RENDER_WIDTH * RENDER_HEIGHT * tcu::getPixelSize(vk::mapVkFormat(m_rtFormat)));
vk::VkBufferMemoryBarrier copyFinishBarrier[4];
for (deUint32 outNdx = 0; outNdx < m_outputCount; outNdx++)
{
vk::VkBufferMemoryBarrier barrier =
{
vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // sType
DE_NULL, // pNext
vk::VK_ACCESS_TRANSFER_WRITE_BIT, // srcAccessMask
vk::VK_ACCESS_HOST_READ_BIT, // dstAccessMask
queueFamilyIndex, // srcQueueFamilyIndex
queueFamilyIndex, // destQueueFamilyIndex
*m_readImageBuffer[outNdx], // buffer
0u, // offset
size // size
};
copyFinishBarrier[outNdx] = barrier;
}
vkd.cmdPipelineBarrier(*m_cmdBuffer, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, vk::VK_PIPELINE_STAGE_HOST_BIT, (vk::VkDependencyFlags)0,
0, (const vk::VkMemoryBarrier*)DE_NULL,
m_outputCount, &copyFinishBarrier[0],
0, (const vk::VkImageMemoryBarrier*)DE_NULL);
}
endCommandBuffer(vkd, *m_cmdBuffer);
}
ShaderCaseInstance::~ShaderCaseInstance (void)
{
}
int getNumSubCases (const ValueBlock& values)
{
if (!values.outputs.empty())
return int(values.outputs[0].elements.size() / values.outputs[0].type.getScalarSize());
else
return 1; // Always run at least one iteration even if no output values are specified
}
bool checkResultImage (const ConstPixelBufferAccess& result)
{
const tcu::IVec4 refPix (255, 255, 255, 255);
for (int y = 0; y < result.getHeight(); y++)
{
for (int x = 0; x < result.getWidth(); x++)
{
const tcu::IVec4 resPix = result.getPixelInt(x, y);
if (boolAny(notEqual(resPix, refPix)))
return false;
}
}
return true;
}
bool checkResultImageWithReference (const ConstPixelBufferAccess& result, tcu::IVec4 refPix)
{
for (int y = 0; y < result.getHeight(); y++)
{
for (int x = 0; x < result.getWidth(); x++)
{
const tcu::IVec4 resPix = result.getPixelInt(x, y);
if (boolAny(notEqual(resPix, refPix)))
return false;
}
}
return true;
}
TestStatus ShaderCaseInstance::iterate (void)
{
const vk::DeviceInterface& vkd = m_context.getDeviceInterface();
const vk::VkDevice device = m_context.getDevice();
const vk::VkQueue queue = m_context.getUniversalQueue();
if (!m_spec.values.inputs.empty())
writeValuesToMem(m_context, *m_inputMem, m_inputLayout, m_spec.values.inputs, m_subCaseNdx);
if (!m_spec.values.outputs.empty())
writeValuesToMem(m_context, *m_referenceMem, m_referenceLayout, m_spec.values.outputs, m_subCaseNdx);
if (!m_spec.values.uniforms.empty())
writeValuesToMem(m_context, *m_uniformMem, m_uniformLayout, m_spec.values.uniforms, m_subCaseNdx);
submitCommandsAndWait(vkd, device, queue, m_cmdBuffer.get());
// Result was checked in fragment shader
if (m_spec.outputType == glu::sl::OUTPUT_RESULT)
{
const ConstPixelBufferAccess imgAccess (TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8), RENDER_WIDTH, RENDER_HEIGHT, 1, m_readImageMem[0]->getHostPtr());
invalidateMappedMemoryRange(vkd, device, m_readImageMem[0]->getMemory(), m_readImageMem[0]->getOffset(), (vk::VkDeviceSize)(RENDER_WIDTH*RENDER_HEIGHT*4));
if (!checkResultImage(imgAccess))
{
TestLog& log = m_context.getTestContext().getLog();
log << TestLog::Message << "ERROR: Got non-white pixels on sub-case " << m_subCaseNdx << TestLog::EndMessage
<< TestLog::Image("Result", "Result", imgAccess);
dumpValues(log, m_spec.values, m_subCaseNdx);
return TestStatus::fail(string("Got invalid pixels at sub-case ") + de::toString(m_subCaseNdx));
}
}
// Result was written to color buffer
else
{
for (deUint32 outNdx = 0; outNdx < m_outputCount; outNdx++)
{
const ConstPixelBufferAccess imgAccess (vk::mapVkFormat(m_rtFormat), RENDER_WIDTH, RENDER_HEIGHT, 1, m_readImageMem[outNdx]->getHostPtr());
const DataType dataType = m_spec.values.outputs[outNdx].type.getBasicType();
const int numComponents = getDataTypeScalarSize(dataType);
tcu::IVec4 reference (0, 0, 0, 1);
for (int refNdx = 0; refNdx < numComponents; refNdx++)
{
if (isDataTypeFloatOrVec(dataType))
reference[refNdx] = (int)m_spec.values.outputs[outNdx].elements[m_subCaseNdx * numComponents + refNdx].float32;
else if (isDataTypeIntOrIVec(dataType))
reference[refNdx] = m_spec.values.outputs[outNdx].elements[m_subCaseNdx * numComponents + refNdx].int32;
else
DE_FATAL("Unknown data type");
}
invalidateMappedMemoryRange(vkd, device, m_readImageMem[outNdx]->getMemory(), m_readImageMem[outNdx]->getOffset(), (vk::VkDeviceSize)(RENDER_WIDTH * RENDER_HEIGHT * tcu::getPixelSize(vk::mapVkFormat(m_rtFormat))));
if (!checkResultImageWithReference(imgAccess, reference))
{
TestLog& log = m_context.getTestContext().getLog();
log << TestLog::Message << "ERROR: Got nonmatching pixels on sub-case " << m_subCaseNdx << " output " << outNdx << TestLog::EndMessage
<< TestLog::Image("Result", "Result", imgAccess);
dumpValues(log, m_spec.values, m_subCaseNdx);
return TestStatus::fail(string("Got invalid pixels at sub-case ") + de::toString(m_subCaseNdx));
}
}
}
if (++m_subCaseNdx < getNumSubCases(m_spec.values))
return TestStatus::incomplete();
else
return TestStatus::pass("All sub-cases passed");
}
class ShaderCase : public TestCase
{
public:
ShaderCase (tcu::TestContext& testCtx, const string& name, const string& description, const ShaderCaseSpecification& spec);
void initPrograms (SourceCollections& programCollection) const;
TestInstance* createInstance (Context& context) const;
private:
const ShaderCaseSpecification m_spec;
};
ShaderCase::ShaderCase (tcu::TestContext& testCtx, const string& name, const string& description, const ShaderCaseSpecification& spec)
: TestCase (testCtx, name, description)
, m_spec (spec)
{
}
void ShaderCase::initPrograms (SourceCollections& sourceCollection) const
{
vector<ProgramSources> specializedSources (m_spec.programs.size());
DE_ASSERT(isValid(m_spec));
if (m_spec.expectResult != glu::sl::EXPECT_PASS)
TCU_THROW(InternalError, "Only EXPECT_PASS is supported");
if (m_spec.caseType == glu::sl::CASETYPE_VERTEX_ONLY)
{
DE_ASSERT(m_spec.programs.size() == 1 && m_spec.programs[0].sources.sources[glu::SHADERTYPE_VERTEX].size() == 1);
specializedSources[0] << glu::VertexSource(specializeVertexShader(m_spec, m_spec.programs[0].sources.sources[glu::SHADERTYPE_VERTEX][0]))
<< glu::FragmentSource(genFragmentShader(m_spec));
}
else if (m_spec.caseType == glu::sl::CASETYPE_FRAGMENT_ONLY)
{
DE_ASSERT(m_spec.programs.size() == 1 && m_spec.programs[0].sources.sources[glu::SHADERTYPE_FRAGMENT].size() == 1);
specializedSources[0] << glu::VertexSource(genVertexShader(m_spec))
<< glu::FragmentSource(specializeFragmentShader(m_spec, m_spec.programs[0].sources.sources[glu::SHADERTYPE_FRAGMENT][0]));
}
else
{
DE_ASSERT(m_spec.caseType == glu::sl::CASETYPE_COMPLETE);
const int maxPatchVertices = 4; // \todo [2015-08-05 pyry] Query
for (size_t progNdx = 0; progNdx < m_spec.programs.size(); progNdx++)
{
const ProgramSpecializationParams progSpecParams (m_spec, m_spec.programs[progNdx].requiredExtensions, maxPatchVertices);
specializeProgramSources(specializedSources[progNdx], m_spec.programs[progNdx].sources, progSpecParams);
}
}
for (size_t progNdx = 0; progNdx < specializedSources.size(); progNdx++)
{
for (int shaderType = 0; shaderType < glu::SHADERTYPE_LAST; shaderType++)
{
if (!specializedSources[progNdx].sources[shaderType].empty())
{
vk::GlslSource& curSrc = sourceCollection.glslSources.add(getShaderName((glu::ShaderType)shaderType, progNdx));
curSrc.sources[shaderType] = specializedSources[progNdx].sources[shaderType];
}
}
}
}
TestInstance* ShaderCase::createInstance (Context& context) const
{
return new ShaderCaseInstance(context, m_spec);
}
class ShaderCaseFactory : public glu::sl::ShaderCaseFactory
{
public:
ShaderCaseFactory (tcu::TestContext& testCtx)
: m_testCtx(testCtx)
{
}
tcu::TestCaseGroup* createGroup (const string& name, const string& description, const vector<tcu::TestNode*>& children)
{
return new tcu::TestCaseGroup(m_testCtx, name.c_str(), description.c_str(), children);
}
tcu::TestCase* createCase (const string& name, const string& description, const ShaderCaseSpecification& spec)
{
return new ShaderCase(m_testCtx, name, description, spec);
}
private:
tcu::TestContext& m_testCtx;
};
class ShaderLibraryGroup : public tcu::TestCaseGroup
{
public:
ShaderLibraryGroup (tcu::TestContext& testCtx, const string& name, const string& description, const string& filename)
: tcu::TestCaseGroup (testCtx, name.c_str(), description.c_str())
, m_filename (filename)
{
}
void init (void)
{
ShaderCaseFactory caseFactory (m_testCtx);
const vector<tcu::TestNode*> children = glu::sl::parseFile(m_testCtx.getArchive(), m_filename, &caseFactory);
for (size_t ndx = 0; ndx < children.size(); ndx++)
{
try
{
addChild(children[ndx]);
}
catch (...)
{
for (; ndx < children.size(); ndx++)
delete children[ndx];
throw;
}
}
}
private:
const string m_filename;
};
} // anonymous
MovePtr<tcu::TestCaseGroup> createShaderLibraryGroup (tcu::TestContext& testCtx, const string& name, const string& description, const string& filename)
{
return MovePtr<tcu::TestCaseGroup>(new ShaderLibraryGroup(testCtx, name, description, filename));
}
} // vkt