blob: 3fb078184eebe7325598a6ee929a3f5e33794a7c [file] [log] [blame]
/*-------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2017 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 SPIR-V Versions check cases
*//*--------------------------------------------------------------------*/
#include "vkApiVersion.hpp"
#include "vktSpvAsmSpirvVersionTests.hpp"
#include "vktTestCase.hpp"
#include "vktSpvAsmComputeShaderCase.hpp"
#include "vktSpvAsmGraphicsShaderTestUtil.hpp"
namespace vkt
{
namespace SpirVAssembly
{
using namespace vk;
using std::map;
using std::string;
using std::vector;
using tcu::RGBA;
enum Operation
{
OPERATION_COMPUTE = 0,
OPERATION_GRAPHICS_VERTEX,
OPERATION_GRAPHICS_TESSELATION_EVALUATION,
OPERATION_GRAPHICS_TESSELATION_CONTROL,
OPERATION_GRAPHICS_GEOMETRY,
OPERATION_GRAPHICS_FRAGMENT,
OPERATION_LAST
};
Operation& operator++ (Operation& operation)
{
if (operation == OPERATION_LAST)
operation = OPERATION_COMPUTE;
else
operation = static_cast<Operation>(static_cast<deUint32>(operation) + 1);
return operation;
}
struct TestParameters
{
Operation operation;
SpirvVersion spirvVersion;
};
static InstanceContext initGraphicsInstanceContext (const TestParameters& testParameters)
{
static const ShaderElement vertFragPipelineStages[] =
{
ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
};
static const ShaderElement tessPipelineStages[] =
{
ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
ShaderElement("tessc", "main", VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT),
ShaderElement("tesse", "main", VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT),
ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
};
static const ShaderElement geomPipelineStages[] =
{
ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
ShaderElement("geom", "main", VK_SHADER_STAGE_GEOMETRY_BIT),
ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
};
map<string, string> opSimpleTest;
opSimpleTest["testfun"] =
"%test_code = OpFunction %v4f32 None %v4f32_v4f32_function\n"
"%param1 = OpFunctionParameter %v4f32\n"
"%label_testfun = OpLabel\n"
"%a = OpVectorExtractDynamic %f32 %param1 %c_i32_0\n"
"%b = OpFAdd %f32 %a %a\n"
"%c = OpFSub %f32 %b %a\n"
"%ret = OpVectorInsertDynamic %v4f32 %param1 %c %c_i32_0\n"
"OpReturnValue %ret\n"
"OpFunctionEnd\n";
if (testParameters.spirvVersion > SPIRV_VERSION_1_3)
opSimpleTest["GL_entrypoint"] = "%BP_vertexIdInCurrentPatch";
switch (testParameters.operation)
{
case OPERATION_GRAPHICS_VERTEX: return createInstanceContext(vertFragPipelineStages, opSimpleTest);
case OPERATION_GRAPHICS_TESSELATION_EVALUATION: return createInstanceContext(tessPipelineStages, opSimpleTest);
case OPERATION_GRAPHICS_TESSELATION_CONTROL: return createInstanceContext(tessPipelineStages, opSimpleTest);
case OPERATION_GRAPHICS_GEOMETRY: return createInstanceContext(geomPipelineStages, opSimpleTest);
case OPERATION_GRAPHICS_FRAGMENT: return createInstanceContext(vertFragPipelineStages, opSimpleTest);
default: TCU_THROW(InternalError, "Invalid operation specified");
}
}
static void getComputeSourceCode (std::string& computeSourceCode, SpirvVersion spirvVersion)
{
computeSourceCode = "";
if (spirvVersion > SPIRV_VERSION_1_3)
computeSourceCode += string(getComputeAsmShaderPreamble("", "", "", "", "%indata %outdata"));
else
computeSourceCode += string(getComputeAsmShaderPreamble());
computeSourceCode +=
"OpSource GLSL 430\n"
"OpName %main \"main\"\n"
"OpName %id \"gl_GlobalInvocationID\"\n"
"OpDecorate %id BuiltIn GlobalInvocationId\n" +
string(getComputeAsmInputOutputBufferTraits((spirvVersion > SPIRV_VERSION_1_3) ? "Block" : "BufferBlock")) +
string(getComputeAsmCommonTypes((spirvVersion > SPIRV_VERSION_1_3) ? "StorageBuffer" : "Uniform")) +
string(getComputeAsmInputOutputBuffer((spirvVersion > SPIRV_VERSION_1_3) ? "StorageBuffer" : "Uniform")) +
"%id = OpVariable %uvec3ptr Input\n"
"%zero = OpConstant %i32 0\n"
"%main = OpFunction %void None %voidf\n"
"%label = OpLabel\n"
"%idval = OpLoad %uvec3 %id\n"
"%x = OpCompositeExtract %u32 %idval 0\n"
" OpNop\n" // Inside a function body
"%inloc = OpAccessChain %f32ptr %indata %zero %x\n"
"%inval = OpLoad %f32 %inloc\n"
"%neg = OpFNegate %f32 %inval\n"
"%outloc = OpAccessChain %f32ptr %outdata %zero %x\n"
" OpStore %outloc %neg\n"
" OpReturn\n"
" OpFunctionEnd\n";
}
static ComputeShaderSpec getComputeShaderSpec (const TestParameters& testParameters)
{
ComputeShaderSpec spec;
const deUint32 seed = (static_cast<deUint32>(testParameters.operation)<<16) ^ static_cast<deUint32>(testParameters.spirvVersion);
de::Random rnd (seed);
const int numElements = 100;
vector<float> positiveFloats (numElements, 0);
vector<float> negativeFloats (numElements, 0);
for (size_t ndx = 0; ndx < numElements; ++ndx)
{
positiveFloats[ndx] = rnd.getFloat(1.0f, 100.0f);
negativeFloats[ndx] = -positiveFloats[ndx];
}
// Shader source code can be retrieved to complete definition of ComputeShaderSpec, though it is not required at this stage
// getComputeSourceCode (spec.assembly);
spec.inputs.push_back(BufferSp(new Float32Buffer(positiveFloats)));
spec.outputs.push_back(BufferSp(new Float32Buffer(negativeFloats)));
spec.numWorkGroups = tcu::IVec3(numElements, 1, 1);
return spec;
}
static bool isSpirVersionsAsRequested (const BinaryCollection& binaryCollection, SpirvVersion requestedSpirvVersion)
{
bool result = true;
DE_ASSERT(!binaryCollection.empty());
for (vk::BinaryCollection::Iterator binaryIt = binaryCollection.begin(); binaryIt != binaryCollection.end(); ++binaryIt)
{
SpirvVersion binarySpirvVersion = extractSpirvVersion (binaryIt.getProgram());
if (binarySpirvVersion != requestedSpirvVersion)
result = false;
}
return result;
}
class SpvAsmGraphicsSpirvVersionsInstance : public TestInstance
{
public:
SpvAsmGraphicsSpirvVersionsInstance (Context& ctx, const TestParameters& testParameters);
tcu::TestStatus iterate (void);
private:
TestParameters m_testParameters;
};
SpvAsmGraphicsSpirvVersionsInstance::SpvAsmGraphicsSpirvVersionsInstance (Context& ctx, const TestParameters& testParameters)
: TestInstance (ctx)
, m_testParameters (testParameters)
{
}
tcu::TestStatus SpvAsmGraphicsSpirvVersionsInstance::iterate (void)
{
InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
if (!isSpirVersionsAsRequested(m_context.getBinaryCollection(), m_testParameters.spirvVersion))
return tcu::TestStatus::fail("Binary SPIR-V version is different from requested");
return runAndVerifyDefaultPipeline(m_context, instanceContext);
}
class SpvAsmComputeSpirvVersionsInstance : public ComputeShaderSpec, public SpvAsmComputeShaderInstance
{
public:
SpvAsmComputeSpirvVersionsInstance (Context& ctx, const TestParameters& testParameters);
tcu::TestStatus iterate (void);
private:
TestParameters m_testParameters;
};
SpvAsmComputeSpirvVersionsInstance::SpvAsmComputeSpirvVersionsInstance (Context& ctx, const TestParameters& testParameters)
: ComputeShaderSpec(getComputeShaderSpec(testParameters))
, SpvAsmComputeShaderInstance(ctx, *this)
, m_testParameters(testParameters)
{
if (m_testParameters.operation != OPERATION_COMPUTE)
TCU_THROW(InternalError, "Invalid operation specified");
}
tcu::TestStatus SpvAsmComputeSpirvVersionsInstance::iterate (void)
{
if (!isSpirVersionsAsRequested(m_context.getBinaryCollection(), m_testParameters.spirvVersion))
return tcu::TestStatus::fail("Binary SPIR-V version is different from requested");
return SpvAsmComputeShaderInstance::iterate();
}
class SpvAsmSpirvVersionsCase : public TestCase
{
public:
SpvAsmSpirvVersionsCase (tcu::TestContext& testCtx, const char* name, const char* description, const TestParameters& testParameters);
void initPrograms (vk::SourceCollections& programCollection) const;
TestInstance* createInstance (Context& context) const;
private:
const TestParameters m_testParameters;
};
SpvAsmSpirvVersionsCase::SpvAsmSpirvVersionsCase (tcu::TestContext& testCtx, const char* name, const char* description, const TestParameters& testParameters)
: TestCase (testCtx, name, description)
, m_testParameters (testParameters)
{
}
void validateVulkanVersion (const deUint32 usedVulkanVersion, const SpirvVersion testedSpirvVersion)
{
const SpirvVersion usedSpirvVersionForAsm = getMaxSpirvVersionForAsm(usedVulkanVersion);
if (testedSpirvVersion > usedSpirvVersionForAsm)
TCU_THROW(NotSupportedError, "Specified SPIR-V version is not supported by the device/instance");
}
void SpvAsmSpirvVersionsCase::initPrograms (SourceCollections& programCollection) const
{
const SpirVAsmBuildOptions spirVAsmBuildOptions (programCollection.usedVulkanVersion, m_testParameters.spirvVersion);
validateVulkanVersion(programCollection.usedVulkanVersion, m_testParameters.spirvVersion);
switch (m_testParameters.operation)
{
case OPERATION_COMPUTE:
{
std::string comp;
getComputeSourceCode(comp, m_testParameters.spirvVersion);
programCollection.spirvAsmSources.add("compute", &spirVAsmBuildOptions) << comp;
break;
}
case OPERATION_GRAPHICS_VERTEX:
{
InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
addShaderCodeCustomVertex(programCollection, instanceContext, &spirVAsmBuildOptions);
break;
}
case OPERATION_GRAPHICS_TESSELATION_EVALUATION:
{
InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
addShaderCodeCustomTessEval(programCollection, instanceContext, &spirVAsmBuildOptions);
break;
}
case OPERATION_GRAPHICS_TESSELATION_CONTROL:
{
InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
addShaderCodeCustomTessControl(programCollection, instanceContext, &spirVAsmBuildOptions);
break;
}
case OPERATION_GRAPHICS_GEOMETRY:
{
InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
addShaderCodeCustomGeometry(programCollection, instanceContext, &spirVAsmBuildOptions);
break;
}
case OPERATION_GRAPHICS_FRAGMENT:
{
InstanceContext instanceContext = initGraphicsInstanceContext(m_testParameters);
addShaderCodeCustomFragment(programCollection, instanceContext, &spirVAsmBuildOptions);
break;
}
default:
TCU_THROW(InternalError, "Invalid operation specified");
}
}
TestInstance* SpvAsmSpirvVersionsCase::createInstance (Context& context) const
{
validateVulkanVersion(context.getUsedApiVersion(), m_testParameters.spirvVersion);
switch (m_testParameters.operation)
{
case OPERATION_COMPUTE:
return new SpvAsmComputeSpirvVersionsInstance(context, m_testParameters);
case OPERATION_GRAPHICS_VERTEX:
case OPERATION_GRAPHICS_TESSELATION_EVALUATION:
case OPERATION_GRAPHICS_TESSELATION_CONTROL:
case OPERATION_GRAPHICS_GEOMETRY:
case OPERATION_GRAPHICS_FRAGMENT:
return new SpvAsmGraphicsSpirvVersionsInstance(context, m_testParameters);
default:
TCU_THROW(InternalError, "Invalid operation specified");
}
}
tcu::TestCaseGroup* createSpivVersionCheckTests (tcu::TestContext& testCtx, const bool compute)
{
const char* operationNames[OPERATION_LAST] =
{
"compute",
"vertex",
"tesselation_evaluation",
"tesselation_control",
"geometry",
"fragment",
};
de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "spirv_version", "Test SPIR-V version is supported"));
for (SpirvVersion spirvVersion = SPIRV_VERSION_1_0; spirvVersion < SPIRV_VERSION_LAST; ++spirvVersion)
{
std::string spirvVersionName = getSpirvVersionName(spirvVersion);
std::replace(spirvVersionName.begin(), spirvVersionName.end(), '.', '_');
for (Operation operation = OPERATION_COMPUTE; operation < OPERATION_LAST; ++operation)
{
if ((compute && operation == OPERATION_COMPUTE) || (!compute && operation != OPERATION_COMPUTE))
{
const std::string testName = spirvVersionName + "_" + operationNames[static_cast<deUint32>(operation)];
const TestParameters testParameters =
{
operation,
spirvVersion
};
group->addChild(new SpvAsmSpirvVersionsCase(testCtx, testName.c_str(), "", testParameters));
}
}
}
return group.release();
}
} // SpirVAssembly
} // vkt