/*------------------------------------------------------------------------ * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2019 The Khronos Group Inc. * Copyright (c) 2019 Intel Corporation. * * 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 VK_KHR_pipeline_executable_properties * * These tests creates compute and graphics pipelines with a variety of * stages both with and without a pipeline cache and exercise the new * queries provided by VK_KHR_pipeline_executable_properties. * * For each query type, it asserts that the query works and doesn't crash * and returns consistent results: * * - The tests assert that the same set of pipeline executables is * reported regardless of whether or not a pipeline cache is used. * * - For each pipeline executable, the tests assert that the same set of * statistics is returned regardless of whether or not a pipeline cache * is used. * * - For each pipeline executable, the tests assert that the same set of * statistics is returned regardless of whether or not * CAPTURE_INTERNAL_REPRESENTATIONS_BIT is set. * * - For each pipeline executable, the tests assert that the same set of * internal representations is returned regardless of whether or not a * pipeline cache is used. * * - For each string returned (statistic names, etc.) the tests assert * that the string is NULL terminated. * * - For each statistic, the tests compare the results of the two * compilations and report any differences. (Statistics differing * between two compilations is not considered a failure.) * * - For each binary internal representation, the tests attempt to assert * that the amount of data returned by the implementation matches the * amount the implementation claims. (It's impossible to exactly do * this but the tests give it a good try.) * * All of the returned data is recorded in the output file. * *//*--------------------------------------------------------------------*/ #include "vktPipelineExecutablePropertiesTests.hpp" #include "vktPipelineVertexUtil.hpp" #include "vktTestCase.hpp" #include "vktTestCaseUtil.hpp" #include "vkMemUtil.hpp" #include "vkBuilderUtil.hpp" #include "vkRefUtil.hpp" #include "vkTypeUtil.hpp" #include "vkObjUtil.hpp" #include "tcuTestLog.hpp" #include #include namespace vkt { namespace pipeline { using namespace vk; namespace { enum { VK_MAX_SHADER_STAGES = 6, }; enum { PIPELINE_CACHE_NDX_INITIAL = 0, PIPELINE_CACHE_NDX_CACHED = 1, PIPELINE_CACHE_NDX_COUNT, }; // helper functions std::string getShaderFlagStr(const VkShaderStageFlags shader, bool isDescription) { std::ostringstream desc; if (shader & VK_SHADER_STAGE_COMPUTE_BIT) { desc << ((isDescription) ? "compute stage" : "compute_stage"); } else { desc << ((isDescription) ? "vertex stage" : "vertex_stage"); if (shader & VK_SHADER_STAGE_GEOMETRY_BIT) desc << ((isDescription) ? " geometry stage" : "_geometry_stage"); if (shader & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) desc << ((isDescription) ? " tessellation control stage" : "_tessellation_control_stage"); if (shader & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) desc << ((isDescription) ? " tessellation evaluation stage" : "_tessellation_evaluation_stage"); desc << ((isDescription) ? " fragment stage" : "_fragment_stage"); } return desc.str(); } std::string getShaderFlagsStr (const VkShaderStageFlags flags) { std::ostringstream stream; bool empty = true; for (deUint32 b = 0; b < 8 * sizeof(flags); b++) { if (flags & (1u << b)) { if (empty) { empty = false; } else { stream << ", "; } stream << getShaderFlagStr((VkShaderStageFlagBits)(1u << b), true); } } if (empty) { stream << "none"; } return stream.str(); } // helper classes class ExecutablePropertiesTestParam { public: ExecutablePropertiesTestParam (PipelineConstructionType pipelineConstructionType, const VkShaderStageFlags shaders, deBool testStatistics, deBool testInternalRepresentations); virtual ~ExecutablePropertiesTestParam (void) = default; virtual const std::string generateTestName (void) const; virtual const std::string generateTestDescription (void) const; PipelineConstructionType getPipelineConstructionType (void) const { return m_pipelineConstructionType; } VkShaderStageFlags getShaderFlags (void) const { return m_shaders; } deBool getTestStatistics (void) const { return m_testStatistics; } deBool getTestInternalRepresentations (void) const { return m_testInternalRepresentations; } protected: PipelineConstructionType m_pipelineConstructionType; VkShaderStageFlags m_shaders; bool m_testStatistics; bool m_testInternalRepresentations; }; ExecutablePropertiesTestParam::ExecutablePropertiesTestParam (PipelineConstructionType pipelineConstructionType, const VkShaderStageFlags shaders, deBool testStatistics, deBool testInternalRepresentations) : m_pipelineConstructionType (pipelineConstructionType) , m_shaders (shaders) , m_testStatistics (testStatistics) , m_testInternalRepresentations (testInternalRepresentations) { } const std::string ExecutablePropertiesTestParam::generateTestName (void) const { std::string result(getShaderFlagStr(m_shaders, false)); if (m_testStatistics) result += "_statistics"; if (m_testInternalRepresentations) result += "_internal_representations"; return result; } const std::string ExecutablePropertiesTestParam::generateTestDescription (void) const { std::string result; if (m_testStatistics) { result += "Get pipeline executable statistics"; if (m_testInternalRepresentations) { result += " and internal representations"; } } else if (m_testInternalRepresentations) { result += "Get pipeline executable internal representations"; } else { result += "Get pipeline executable properties"; } result += " with " + getShaderFlagStr(m_shaders, true); return result; } template vkt::TestCase* newTestCase (tcu::TestContext& testContext, const ExecutablePropertiesTestParam* testParam) { return new Test(testContext, testParam->generateTestName().c_str(), testParam->generateTestDescription().c_str(), testParam); } // Test Classes class ExecutablePropertiesTest : public vkt::TestCase { public: ExecutablePropertiesTest(tcu::TestContext& testContext, const std::string& name, const std::string& description, const ExecutablePropertiesTestParam* param) : vkt::TestCase (testContext, name, description) , m_param (*param) { } virtual ~ExecutablePropertiesTest (void) { } protected: const ExecutablePropertiesTestParam m_param; }; class ExecutablePropertiesTestInstance : public vkt::TestInstance { public: ExecutablePropertiesTestInstance (Context& context, const ExecutablePropertiesTestParam* param); virtual ~ExecutablePropertiesTestInstance (void); virtual tcu::TestStatus iterate (void); protected: virtual tcu::TestStatus verifyStatistics (deUint32 binaryNdx); virtual tcu::TestStatus verifyInternalRepresentations (deUint32 binaryNdx); virtual tcu::TestStatus verifyTestResult (void); protected: const ExecutablePropertiesTestParam* m_param; Move m_cache; deBool m_extensions; Move m_pipeline[PIPELINE_CACHE_NDX_COUNT]; virtual VkPipeline getPipeline(deUint32 ndx) { return m_pipeline[ndx].get(); } }; ExecutablePropertiesTestInstance::ExecutablePropertiesTestInstance (Context& context, const ExecutablePropertiesTestParam* param) : TestInstance (context) , m_param (param) , m_extensions (m_context.requireDeviceFunctionality("VK_KHR_pipeline_executable_properties")) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice vkDevice = m_context.getDevice(); const VkPipelineCacheCreateInfo pipelineCacheCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkPipelineCacheCreateFlags flags; 0u, // deUintptr initialDataSize; DE_NULL, // const void* pInitialData; }; m_cache = createPipelineCache(vk, vkDevice, &pipelineCacheCreateInfo); } ExecutablePropertiesTestInstance::~ExecutablePropertiesTestInstance (void) { } tcu::TestStatus ExecutablePropertiesTestInstance::iterate (void) { return verifyTestResult(); } bool checkString(const char *string, size_t size) { size_t i = 0; for (; i < size; i++) { if (string[i] == 0) { break; } } // The string needs to be non-empty and null terminated if (i == 0 || i >= size) { return false; } return true; } tcu::TestStatus ExecutablePropertiesTestInstance::verifyStatistics (deUint32 executableNdx) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice vkDevice = m_context.getDevice(); tcu::TestLog &log = m_context.getTestContext().getLog(); std::vector statistics[PIPELINE_CACHE_NDX_COUNT]; for (deUint32 ndx = 0; ndx < PIPELINE_CACHE_NDX_COUNT; ndx++) { const VkPipelineExecutableInfoKHR pipelineExecutableInfo = { VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INFO_KHR, // VkStructureType sType; DE_NULL, // const void* pNext; getPipeline(ndx), // VkPipeline pipeline; executableNdx, // uint32_t executableIndex; }; deUint32 statisticCount = 0; VK_CHECK(vk.getPipelineExecutableStatisticsKHR(vkDevice, &pipelineExecutableInfo, &statisticCount, DE_NULL)); if (statisticCount == 0) { continue; } statistics[ndx].resize(statisticCount); for (deUint32 statNdx = 0; statNdx < statisticCount; statNdx++) { deMemset(&statistics[ndx][statNdx], 0, sizeof(statistics[ndx][statNdx])); statistics[ndx][statNdx].sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_STATISTIC_KHR; statistics[ndx][statNdx].pNext = DE_NULL; } VK_CHECK(vk.getPipelineExecutableStatisticsKHR(vkDevice, &pipelineExecutableInfo, &statisticCount, &statistics[ndx][0])); for (deUint32 statNdx = 0; statNdx < statisticCount; statNdx++) { if (!checkString(statistics[ndx][statNdx].name, DE_LENGTH_OF_ARRAY(statistics[ndx][statNdx].name))) { return tcu::TestStatus::fail("Invalid statistic name string"); } for (deUint32 otherNdx = 0; otherNdx < statNdx; otherNdx++) { if (deMemCmp(statistics[ndx][statNdx].name, statistics[ndx][otherNdx].name, DE_LENGTH_OF_ARRAY(statistics[ndx][statNdx].name)) == 0) { return tcu::TestStatus::fail("Statistic name string not unique within the executable"); } } if (!checkString(statistics[ndx][statNdx].description, DE_LENGTH_OF_ARRAY(statistics[ndx][statNdx].description))) { return tcu::TestStatus::fail("Invalid statistic description string"); } if (statistics[ndx][statNdx].format == VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_BOOL32_KHR) { if (statistics[ndx][statNdx].value.b32 != VK_TRUE && statistics[ndx][statNdx].value.b32 != VK_FALSE) { return tcu::TestStatus::fail("Boolean statistic is neither VK_TRUE nor VK_FALSE"); } } } } if (statistics[0].size() != statistics[1].size()) { return tcu::TestStatus::fail("Identical pipelines have different numbers of statistics"); } if (statistics[0].size() == 0) { return tcu::TestStatus::pass("No statistics reported"); } // Both compiles had better have specified the same infos for (deUint32 statNdx0 = 0; statNdx0 < statistics[0].size(); statNdx0++) { deUint32 statNdx1 = 0; for (; statNdx1 < statistics[1].size(); statNdx1++) { if (deMemCmp(statistics[0][statNdx0].name, statistics[1][statNdx1].name, DE_LENGTH_OF_ARRAY(statistics[0][statNdx0].name)) == 0) { break; } } if (statNdx1 >= statistics[1].size()) { return tcu::TestStatus::fail("Identical pipelines have different statistics"); } if (deMemCmp(statistics[0][statNdx0].description, statistics[1][statNdx1].description, DE_LENGTH_OF_ARRAY(statistics[0][statNdx0].description)) != 0) { return tcu::TestStatus::fail("Invalid binary description string"); } if (statistics[0][statNdx0].format != statistics[1][statNdx1].format) { return tcu::TestStatus::fail("Identical pipelines have statistics with different formats"); } switch (statistics[0][statNdx0].format) { case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_BOOL32_KHR: { bool match = statistics[0][statNdx0].value.b32 == statistics[1][statNdx1].value.b32; log << tcu::TestLog::Message << statistics[0][statNdx0].name << ": " << (statistics[0][statNdx0].value.b32 ? "VK_TRUE" : "VK_FALSE") << (match ? "" : " (non-deterministic)") << " (" << statistics[0][statNdx0].description << ")" << tcu::TestLog::EndMessage; break; } case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_INT64_KHR: { bool match = statistics[0][statNdx0].value.i64 == statistics[1][statNdx1].value.i64; log << tcu::TestLog::Message << statistics[0][statNdx0].name << ": " << statistics[0][statNdx0].value.i64 << (match ? "" : " (non-deterministic)") << " (" << statistics[0][statNdx0].description << ")" << tcu::TestLog::EndMessage; break; } case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR: { bool match = statistics[0][statNdx0].value.u64 == statistics[1][statNdx1].value.u64; log << tcu::TestLog::Message << statistics[0][statNdx0].name << ": " << statistics[0][statNdx0].value.u64 << (match ? "" : " (non-deterministic)") << " (" << statistics[0][statNdx0].description << ")" << tcu::TestLog::EndMessage; break; } case VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_FLOAT64_KHR: { bool match = statistics[0][statNdx0].value.f64 == statistics[1][statNdx1].value.f64; log << tcu::TestLog::Message << statistics[0][statNdx0].name << ": " << statistics[0][statNdx0].value.f64 << (match ? "" : " (non-deterministic)") << " (" << statistics[0][statNdx0].description << ")" << tcu::TestLog::EndMessage; break; } default: return tcu::TestStatus::fail("Invalid statistic format"); } } return tcu::TestStatus::pass("Pass"); } tcu::TestStatus ExecutablePropertiesTestInstance::verifyInternalRepresentations (deUint32 executableNdx) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice vkDevice = m_context.getDevice(); tcu::TestLog &log = m_context.getTestContext().getLog(); // We only care about internal representations on the second pipeline. // We still compile twice to ensure that we still get the right thing // even if the pipeline is hot in the cache. const VkPipelineExecutableInfoKHR pipelineExecutableInfo = { VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INFO_KHR, // VkStructureType sType; DE_NULL, // const void* pNext; getPipeline(1), // VkPipeline pipeline; executableNdx, // uint32_t executableIndex; }; std::vector irs; std::vector> irDatas; deUint32 irCount = 0; VK_CHECK(vk.getPipelineExecutableInternalRepresentationsKHR(vkDevice, &pipelineExecutableInfo, &irCount, DE_NULL)); if (irCount == 0) { return tcu::TestStatus::pass("No internal representations reported"); } irs.resize(irCount); irDatas.resize(irCount); for (deUint32 irNdx = 0; irNdx < irCount; irNdx++) { deMemset(&irs[irNdx], 0, sizeof(irs[irNdx])); irs[irNdx].sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INTERNAL_REPRESENTATION_KHR; irs[irNdx].pNext = DE_NULL; } VK_CHECK(vk.getPipelineExecutableInternalRepresentationsKHR(vkDevice, &pipelineExecutableInfo, &irCount, &irs[0])); for (deUint32 irNdx = 0; irNdx < irCount; irNdx++) { if (!checkString(irs[irNdx].name, DE_LENGTH_OF_ARRAY(irs[irNdx].name))) { return tcu::TestStatus::fail("Invalid internal representation name string"); } for (deUint32 otherNdx = 0; otherNdx < irNdx; otherNdx++) { if (deMemCmp(irs[irNdx].name, irs[otherNdx].name, DE_LENGTH_OF_ARRAY(irs[irNdx].name)) == 0) { return tcu::TestStatus::fail("Internal representation name string not unique within the executable"); } } if (!checkString(irs[irNdx].description, DE_LENGTH_OF_ARRAY(irs[irNdx].description))) { return tcu::TestStatus::fail("Invalid binary description string"); } if (irs[irNdx].dataSize == 0) { return tcu::TestStatus::fail("Internal representation has no data"); } irDatas[irNdx].resize(irs[irNdx].dataSize); irs[irNdx].pData = &irDatas[irNdx][0]; if (irs[irNdx].isText) { // For binary data the size is important. We check that the // implementation fills the whole buffer by filling it with // garbage first and then looking for that same garbage later. for (size_t i = 0; i < irs[irNdx].dataSize; i++) { irDatas[irNdx][i] = (deUint8)(37 * (17 + i)); } } } VK_CHECK(vk.getPipelineExecutableInternalRepresentationsKHR(vkDevice, &pipelineExecutableInfo, &irCount, &irs[0])); for (deUint32 irNdx = 0; irNdx < irCount; irNdx++) { if (irs[irNdx].isText) { if (!checkString((char *)irs[irNdx].pData, irs[irNdx].dataSize)) { return tcu::TestStatus::fail("Textual internal representation isn't a valid string"); } log << tcu::TestLog::Section(irs[irNdx].name, irs[irNdx].description) << tcu::LogKernelSource((char *)irs[irNdx].pData) << tcu::TestLog::EndSection; } else { size_t maxMatchingChunkSize = 0; size_t matchingChunkSize = 0; for (size_t i = 0; i < irs[irNdx].dataSize; i++) { if (irDatas[irNdx][i] == (deUint8)(37 * (17 + i))) { matchingChunkSize++; if (matchingChunkSize > maxMatchingChunkSize) { maxMatchingChunkSize = matchingChunkSize; } } else { matchingChunkSize = 0; } } // 64 bytes of our random data still being in the buffer probably // isn't a coincidence if (matchingChunkSize == irs[irNdx].dataSize || matchingChunkSize >= 64) { return tcu::TestStatus::fail("Implementation didn't fill the whole internal representation data buffer"); } log << tcu::TestLog::Section(irs[irNdx].name, irs[irNdx].description) << tcu::TestLog::Message << "Received " << irs[irNdx].dataSize << "B of binary data" << tcu::TestLog::EndMessage << tcu::TestLog::EndSection; } } return tcu::TestStatus::pass("Pass"); } tcu::TestStatus ExecutablePropertiesTestInstance::verifyTestResult (void) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice vkDevice = m_context.getDevice(); tcu::TestLog &log = m_context.getTestContext().getLog(); std::vector props[PIPELINE_CACHE_NDX_COUNT]; for (deUint32 ndx = 0; ndx < PIPELINE_CACHE_NDX_COUNT; ndx++) { const VkPipelineInfoKHR pipelineInfo = { VK_STRUCTURE_TYPE_PIPELINE_INFO_KHR, // VkStructureType sType; DE_NULL, // const void* pNext; getPipeline(ndx), // VkPipeline pipeline; }; deUint32 executableCount = 0; VK_CHECK(vk.getPipelineExecutablePropertiesKHR(vkDevice, &pipelineInfo, &executableCount, DE_NULL)); if (executableCount == 0) { continue; } props[ndx].resize(executableCount); for (deUint32 execNdx = 0; execNdx < executableCount; execNdx++) { deMemset(&props[ndx][execNdx], 0, sizeof(props[ndx][execNdx])); props[ndx][execNdx].sType = VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_PROPERTIES_KHR; props[ndx][execNdx].pNext = DE_NULL; } VK_CHECK(vk.getPipelineExecutablePropertiesKHR(vkDevice, &pipelineInfo, &executableCount, &props[ndx][0])); for (deUint32 execNdx = 0; execNdx < executableCount; execNdx++) { if (!checkString(props[ndx][execNdx].name, DE_LENGTH_OF_ARRAY(props[ndx][execNdx].name))) { return tcu::TestStatus::fail("Invalid binary name string"); } for (deUint32 otherNdx = 0; otherNdx < execNdx; otherNdx++) { if (deMemCmp(props[ndx][execNdx].name, props[ndx][otherNdx].name, DE_LENGTH_OF_ARRAY(props[ndx][execNdx].name)) == 0) { return tcu::TestStatus::fail("Binary name string not unique within the pipeline"); } } if (!checkString(props[ndx][execNdx].description, DE_LENGTH_OF_ARRAY(props[ndx][execNdx].description))) { return tcu::TestStatus::fail("Invalid binary description string"); } // Check that the binary only contains stages actually used to // compile the pipeline VkShaderStageFlags stages = props[ndx][execNdx].stages; stages &= ~m_param->getShaderFlags(); if (stages != 0) { return tcu::TestStatus::fail("Binary uses unprovided stage"); } } } if (props[0].size() != props[1].size()) { return tcu::TestStatus::fail("Identical pipelines have different numbers of props"); } if (props[0].size() == 0) { return tcu::TestStatus::pass("No executables reported"); } // Both compiles had better have specified the same infos for (deUint32 execNdx0 = 0; execNdx0 < props[0].size(); execNdx0++) { deUint32 execNdx1 = 0; for (; execNdx1 < props[1].size(); execNdx1++) { if (deMemCmp(props[0][execNdx0].name, props[1][execNdx1].name, DE_LENGTH_OF_ARRAY(props[0][execNdx0].name)) == 0) { break; } } if (execNdx1 >= props[1].size()) { return tcu::TestStatus::fail("Identical pipelines have different sets of executables"); } if (deMemCmp(props[0][execNdx0].description, props[1][execNdx1].description, DE_LENGTH_OF_ARRAY(props[0][execNdx0].description)) != 0) { return tcu::TestStatus::fail("Same binary has different descriptions"); } if (props[0][execNdx0].stages != props[1][execNdx1].stages) { return tcu::TestStatus::fail("Same binary has different stages"); } if (props[0][execNdx0].subgroupSize != props[1][execNdx1].subgroupSize) { return tcu::TestStatus::fail("Same binary has different subgroup sizes"); } } log << tcu::TestLog::Section("Binaries", "Binaries reported for this pipeline"); log << tcu::TestLog::Message << "Pipeline reported " << props[0].size() << " props" << tcu::TestLog::EndMessage; tcu::TestStatus status = tcu::TestStatus::pass("Pass"); for (deUint32 execNdx = 0; execNdx < props[0].size(); execNdx++) { log << tcu::TestLog::Section(props[0][execNdx].name, props[0][execNdx].description); log << tcu::TestLog::Message << "Name: " << props[0][execNdx].name << tcu::TestLog::EndMessage; log << tcu::TestLog::Message << "Description: " << props[0][execNdx].description << tcu::TestLog::EndMessage; log << tcu::TestLog::Message << "Stages: " << getShaderFlagsStr(props[0][execNdx].stages) << tcu::TestLog::EndMessage; log << tcu::TestLog::Message << "Subgroup Size: " << props[0][execNdx].subgroupSize << tcu::TestLog::EndMessage; if (m_param->getTestStatistics()) { status = verifyStatistics(execNdx); if (status.getCode() != QP_TEST_RESULT_PASS) { log << tcu::TestLog::EndSection; break; } } if (m_param->getTestInternalRepresentations()) { status = verifyInternalRepresentations(execNdx); if (status.getCode() != QP_TEST_RESULT_PASS) { log << tcu::TestLog::EndSection; break; } } log << tcu::TestLog::EndSection; } log << tcu::TestLog::EndSection; return status; } class GraphicsExecutablePropertiesTest : public ExecutablePropertiesTest { public: GraphicsExecutablePropertiesTest (tcu::TestContext& testContext, const std::string& name, const std::string& description, const ExecutablePropertiesTestParam* param) : ExecutablePropertiesTest (testContext, name, description, param) { } virtual ~GraphicsExecutablePropertiesTest (void) { } virtual void initPrograms (SourceCollections& programCollection) const; virtual TestInstance* createInstance (Context& context) const; void checkSupport (Context& context) const; }; class GraphicsExecutablePropertiesTestInstance : public ExecutablePropertiesTestInstance { public: GraphicsExecutablePropertiesTestInstance (Context& context, const ExecutablePropertiesTestParam* param); virtual ~GraphicsExecutablePropertiesTestInstance (void); protected: void preparePipelineWrapper (GraphicsPipelineWrapper& gpw, VkShaderModule vertShaderModule, VkShaderModule tescShaderModule, VkShaderModule teseShaderModule, VkShaderModule geomShaderModule, VkShaderModule fragShaderModule); VkPipeline getPipeline (deUint32 ndx) override { return m_pipelineWrapper[ndx].getPipeline(); }; protected: const tcu::UVec2 m_renderSize; const VkFormat m_colorFormat; const VkFormat m_depthFormat; Move m_pipelineLayout; GraphicsPipelineWrapper m_pipelineWrapper[PIPELINE_CACHE_NDX_COUNT]; Move m_renderPass; }; void GraphicsExecutablePropertiesTest::initPrograms (SourceCollections& programCollection) const { programCollection.glslSources.add("color_vert") << glu::VertexSource( "#version 310 es\n" "layout(location = 0) in vec4 position;\n" "layout(location = 1) in vec4 color;\n" "layout(location = 0) out highp vec4 vtxColor;\n" "void main (void)\n" "{\n" " gl_Position = position;\n" " vtxColor = color;\n" "}\n"); programCollection.glslSources.add("color_frag") << glu::FragmentSource( "#version 310 es\n" "layout(location = 0) in highp vec4 vtxColor;\n" "layout(location = 0) out highp vec4 fragColor;\n" "void main (void)\n" "{\n" " fragColor = vtxColor;\n" "}\n"); if (m_param.getShaderFlags() & VK_SHADER_STAGE_GEOMETRY_BIT) { programCollection.glslSources.add("dummy_geo") << glu::GeometrySource( "#version 450 \n" "layout(triangles) in;\n" "layout(triangle_strip, max_vertices = 3) out;\n" "layout(location = 0) in highp vec4 in_vtxColor[];\n" "layout(location = 0) out highp vec4 vtxColor;\n" "out gl_PerVertex { vec4 gl_Position; float gl_PointSize; };\n" "in gl_PerVertex { vec4 gl_Position; float gl_PointSize; } gl_in[];\n" "void main (void)\n" "{\n" " for(int ndx=0; ndx<3; ndx++)\n" " {\n" " gl_Position = gl_in[ndx].gl_Position;\n" " gl_PointSize = gl_in[ndx].gl_PointSize;\n" " vtxColor = in_vtxColor[ndx];\n" " EmitVertex();\n" " }\n" " EndPrimitive();\n" "}\n"); } if (m_param.getShaderFlags() & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) { programCollection.glslSources.add("basic_tcs") << glu::TessellationControlSource( "#version 450 \n" "layout(vertices = 3) out;\n" "layout(location = 0) in highp vec4 color[];\n" "layout(location = 0) out highp vec4 vtxColor[];\n" "out gl_PerVertex { vec4 gl_Position; float gl_PointSize; } gl_out[3];\n" "in gl_PerVertex { vec4 gl_Position; float gl_PointSize; } gl_in[gl_MaxPatchVertices];\n" "void main()\n" "{\n" " gl_TessLevelOuter[0] = 4.0;\n" " gl_TessLevelOuter[1] = 4.0;\n" " gl_TessLevelOuter[2] = 4.0;\n" " gl_TessLevelInner[0] = 4.0;\n" " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" " gl_out[gl_InvocationID].gl_PointSize = gl_in[gl_InvocationID].gl_PointSize;\n" " vtxColor[gl_InvocationID] = color[gl_InvocationID];\n" "}\n"); programCollection.glslSources.add("basic_tes") << glu::TessellationEvaluationSource( "#version 450 \n" "layout(triangles, fractional_even_spacing, ccw) in;\n" "layout(location = 0) in highp vec4 colors[];\n" "layout(location = 0) out highp vec4 vtxColor;\n" "out gl_PerVertex { vec4 gl_Position; float gl_PointSize; };\n" "in gl_PerVertex { vec4 gl_Position; float gl_PointSize; } gl_in[gl_MaxPatchVertices];\n" "void main() \n" "{\n" " float u = gl_TessCoord.x;\n" " float v = gl_TessCoord.y;\n" " float w = gl_TessCoord.z;\n" " vec4 pos = vec4(0);\n" " vec4 color = vec4(0);\n" " pos.xyz += u * gl_in[0].gl_Position.xyz;\n" " color.xyz += u * colors[0].xyz;\n" " pos.xyz += v * gl_in[1].gl_Position.xyz;\n" " color.xyz += v * colors[1].xyz;\n" " pos.xyz += w * gl_in[2].gl_Position.xyz;\n" " color.xyz += w * colors[2].xyz;\n" " pos.w = 1.0;\n" " color.w = 1.0;\n" " gl_Position = pos;\n" " gl_PointSize = gl_in[0].gl_PointSize;" " vtxColor = color;\n" "}\n"); } } TestInstance* GraphicsExecutablePropertiesTest::createInstance (Context& context) const { return new GraphicsExecutablePropertiesTestInstance(context, &m_param); } void GraphicsExecutablePropertiesTest::checkSupport(Context& context) const { VkShaderStageFlags shaderFlags = m_param.getShaderFlags(); VkPhysicalDeviceFeatures features = context.getDeviceFeatures(); if ((shaderFlags & VK_SHADER_STAGE_GEOMETRY_BIT) && (features.geometryShader == VK_FALSE)) TCU_THROW(NotSupportedError, "Geometry Shader Not Supported"); if ((shaderFlags & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) || (shaderFlags & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)) { if (features.tessellationShader == VK_FALSE) TCU_THROW(NotSupportedError, "Tessellation Not Supported"); } checkPipelineLibraryRequirements(context.getInstanceInterface(), context.getPhysicalDevice(), m_param.getPipelineConstructionType()); } GraphicsExecutablePropertiesTestInstance::GraphicsExecutablePropertiesTestInstance (Context& context, const ExecutablePropertiesTestParam* param) : ExecutablePropertiesTestInstance (context, param) , m_renderSize (32u, 32u) , m_colorFormat (VK_FORMAT_R8G8B8A8_UNORM) , m_depthFormat (VK_FORMAT_D16_UNORM) , m_pipelineWrapper { { m_context.getDeviceInterface(), m_context.getDevice(), param->getPipelineConstructionType(), (param->getTestStatistics() ? deUint32(VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR) : 0u) }, { m_context.getDeviceInterface(), m_context.getDevice(), param->getPipelineConstructionType(), (param->getTestStatistics() ? deUint32(VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR) : 0u) | // Only check gather internal representations on the second pipeline. // This way, it's more obvious if they failed to capture due to the pipeline being cached. (param->getTestInternalRepresentations() ? deUint32(VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR) : 0u) }, } { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice vkDevice = m_context.getDevice(); // Create pipeline layout { const VkPipelineLayoutCreateInfo pipelineLayoutParams = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkPipelineLayoutCreateFlags flags; 0u, // deUint32 setLayoutCount; DE_NULL, // const VkDescriptorSetLayout* pSetLayouts; 0u, // deUint32 pushConstantRangeCount; DE_NULL // const VkPushConstantRange* pPushConstantRanges; }; m_pipelineLayout = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams); } // Create render pass m_renderPass = makeRenderPass(vk, vkDevice, m_colorFormat, m_depthFormat); // Bind shader stages Move vertShaderModule = createShaderModule(vk, vkDevice, context.getBinaryCollection().get("color_vert"), 0); Move fragShaderModule = createShaderModule(vk, vkDevice, context.getBinaryCollection().get("color_frag"), 0); Move tescShaderModule; Move teseShaderModule; Move geomShaderModule; VkShaderStageFlags shaderFlags = m_param->getShaderFlags(); if (shaderFlags & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) tescShaderModule = createShaderModule(vk, vkDevice, context.getBinaryCollection().get("basic_tcs"), 0); if (shaderFlags & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) teseShaderModule = createShaderModule(vk, vkDevice, context.getBinaryCollection().get("basic_tes"), 0); if (shaderFlags & VK_SHADER_STAGE_GEOMETRY_BIT) geomShaderModule = createShaderModule(vk, vkDevice, context.getBinaryCollection().get("dummy_geo"), 0); for (deUint32 ndx = 0; ndx < PIPELINE_CACHE_NDX_COUNT; ndx++) preparePipelineWrapper(m_pipelineWrapper[ndx], *vertShaderModule, *tescShaderModule, *teseShaderModule, *geomShaderModule, *fragShaderModule); } GraphicsExecutablePropertiesTestInstance::~GraphicsExecutablePropertiesTestInstance (void) { } void GraphicsExecutablePropertiesTestInstance::preparePipelineWrapper(GraphicsPipelineWrapper& gpw, VkShaderModule vertShaderModule, VkShaderModule tescShaderModule, VkShaderModule teseShaderModule, VkShaderModule geomShaderModule, VkShaderModule fragShaderModule) { // Create pipeline const VkVertexInputBindingDescription vertexInputBindingDescription = { 0u, // deUint32 binding; sizeof(Vertex4RGBA), // deUint32 strideInBytes; VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate; }; const VkVertexInputAttributeDescription vertexInputAttributeDescriptions[2] = { { 0u, // deUint32 location; 0u, // deUint32 binding; VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format; 0u // deUint32 offsetInBytes; }, { 1u, // deUint32 location; 0u, // deUint32 binding; VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format; DE_OFFSET_OF(Vertex4RGBA, color), // deUint32 offsetInBytes; } }; const VkPipelineVertexInputStateCreateInfo vertexInputStateParams { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkPipelineVertexInputStateCreateFlags flags; 1u, // deUint32 vertexBindingDescriptionCount; &vertexInputBindingDescription, // const VkVertexInputBindingDescription* pVertexBindingDescriptions; 2u, // deUint32 vertexAttributeDescriptionCount; vertexInputAttributeDescriptions, // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions; }; const std::vector viewport{ makeViewport(m_renderSize) }; const std::vector scissor{ makeRect2D(m_renderSize) }; const VkPipelineColorBlendAttachmentState colorBlendAttachmentState { VK_FALSE, // VkBool32 blendEnable; VK_BLEND_FACTOR_ONE, // VkBlendFactor srcColorBlendFactor; VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstColorBlendFactor; VK_BLEND_OP_ADD, // VkBlendOp colorBlendOp; VK_BLEND_FACTOR_ONE, // VkBlendFactor srcAlphaBlendFactor; VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstAlphaBlendFactor; VK_BLEND_OP_ADD, // VkBlendOp alphaBlendOp; VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT // VkColorComponentFlags colorWriteMask; }; const VkPipelineColorBlendStateCreateInfo colorBlendStateParams { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkPipelineColorBlendStateCreateFlags flags; VK_FALSE, // VkBool32 logicOpEnable; VK_LOGIC_OP_COPY, // VkLogicOp logicOp; 1u, // deUint32 attachmentCount; &colorBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments; { 0.0f, 0.0f, 0.0f, 0.0f }, // float blendConst[4]; }; VkPipelineDepthStencilStateCreateInfo depthStencilStateParams { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkPipelineDepthStencilStateCreateFlags flags; VK_TRUE, // VkBool32 depthTestEnable; VK_TRUE, // VkBool32 depthWriteEnable; VK_COMPARE_OP_LESS_OR_EQUAL, // VkCompareOp depthCompareOp; VK_FALSE, // VkBool32 depthBoundsTestEnable; VK_FALSE, // VkBool32 stencilTestEnable; // VkStencilOpState front; { VK_STENCIL_OP_KEEP, // VkStencilOp failOp; VK_STENCIL_OP_KEEP, // VkStencilOp passOp; VK_STENCIL_OP_KEEP, // VkStencilOp depthFailOp; VK_COMPARE_OP_NEVER, // VkCompareOp compareOp; 0u, // deUint32 compareMask; 0u, // deUint32 writeMask; 0u, // deUint32 reference; }, // VkStencilOpState back; { VK_STENCIL_OP_KEEP, // VkStencilOp failOp; VK_STENCIL_OP_KEEP, // VkStencilOp passOp; VK_STENCIL_OP_KEEP, // VkStencilOp depthFailOp; VK_COMPARE_OP_NEVER, // VkCompareOp compareOp; 0u, // deUint32 compareMask; 0u, // deUint32 writeMask; 0u, // deUint32 reference; }, 0.0f, // float minDepthBounds; 1.0f, // float maxDepthBounds; }; gpw.setDefaultTopology((tescShaderModule == DE_NULL) ? VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST : VK_PRIMITIVE_TOPOLOGY_PATCH_LIST) .setDefaultRasterizationState() .setDefaultMultisampleState() .setupVertexInputState(&vertexInputStateParams) .setupPreRasterizationShaderState(viewport, scissor, *m_pipelineLayout, *m_renderPass, 0u, vertShaderModule, DE_NULL, tescShaderModule, teseShaderModule, geomShaderModule) .setupFragmentShaderState(*m_pipelineLayout, *m_renderPass, 0u, fragShaderModule, &depthStencilStateParams) .setupFragmentOutputState(*m_renderPass, 0u, &colorBlendStateParams) .setMonolithicPipelineLayout(*m_pipelineLayout) .buildPipeline(*m_cache); } class ComputeExecutablePropertiesTest : public ExecutablePropertiesTest { public: ComputeExecutablePropertiesTest (tcu::TestContext& testContext, const std::string& name, const std::string& description, const ExecutablePropertiesTestParam* param) : ExecutablePropertiesTest (testContext, name, description, param) { } virtual ~ComputeExecutablePropertiesTest (void) { } virtual void initPrograms (SourceCollections& programCollection) const; virtual TestInstance* createInstance (Context& context) const; }; class ComputeExecutablePropertiesTestInstance : public ExecutablePropertiesTestInstance { public: ComputeExecutablePropertiesTestInstance (Context& context, const ExecutablePropertiesTestParam* param); virtual ~ComputeExecutablePropertiesTestInstance (void); protected: void buildDescriptorSets (deUint32 ndx); void buildShader (deUint32 ndx); void buildPipeline (deUint32 ndx); protected: Move m_inputBuf; de::MovePtr m_inputBufferAlloc; Move m_computeShaderModule[PIPELINE_CACHE_NDX_COUNT]; Move m_outputBuf[PIPELINE_CACHE_NDX_COUNT]; de::MovePtr m_outputBufferAlloc[PIPELINE_CACHE_NDX_COUNT]; Move m_descriptorPool[PIPELINE_CACHE_NDX_COUNT]; Move m_descriptorSetLayout[PIPELINE_CACHE_NDX_COUNT]; Move m_descriptorSet[PIPELINE_CACHE_NDX_COUNT]; Move m_pipelineLayout[PIPELINE_CACHE_NDX_COUNT]; }; void ComputeExecutablePropertiesTest::initPrograms (SourceCollections& programCollection) const { programCollection.glslSources.add("basic_compute") << glu::ComputeSource( "#version 310 es\n" "layout(local_size_x = 1) in;\n" "layout(std430) buffer;\n" "layout(binding = 0) readonly buffer Input0\n" "{\n" " vec4 elements[];\n" "} input_data0;\n" "layout(binding = 1) writeonly buffer Output\n" "{\n" " vec4 elements[];\n" "} output_data;\n" "void main()\n" "{\n" " uint ident = gl_GlobalInvocationID.x;\n" " output_data.elements[ident] = input_data0.elements[ident] * input_data0.elements[ident];\n" "}"); } TestInstance* ComputeExecutablePropertiesTest::createInstance (Context& context) const { return new ComputeExecutablePropertiesTestInstance(context, &m_param); } void ComputeExecutablePropertiesTestInstance::buildDescriptorSets (deUint32 ndx) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice vkDevice = m_context.getDevice(); // Create descriptor set layout DescriptorSetLayoutBuilder descLayoutBuilder; for (deUint32 bindingNdx = 0u; bindingNdx < 2u; bindingNdx++) descLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT); m_descriptorSetLayout[ndx] = descLayoutBuilder.build(vk, vkDevice); } void ComputeExecutablePropertiesTestInstance::buildShader (deUint32 ndx) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice vkDevice = m_context.getDevice(); // Create compute shader VkShaderModuleCreateInfo shaderModuleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkShaderModuleCreateFlags flags; m_context.getBinaryCollection().get("basic_compute").getSize(), // deUintptr codeSize; (deUint32*)m_context.getBinaryCollection().get("basic_compute").getBinary(), // const deUint32* pCode; }; m_computeShaderModule[ndx] = createShaderModule(vk, vkDevice, &shaderModuleCreateInfo); } void ComputeExecutablePropertiesTestInstance::buildPipeline (deUint32 ndx) { const DeviceInterface& vk = m_context.getDeviceInterface(); const VkDevice vkDevice = m_context.getDevice(); // Create compute pipeline layout const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkPipelineLayoutCreateFlags flags; 1u, // deUint32 setLayoutCount; &m_descriptorSetLayout[ndx].get(), // const VkDescriptorSetLayout* pSetLayouts; 0u, // deUint32 pushConstantRangeCount; DE_NULL, // const VkPushConstantRange* pPushConstantRanges; }; m_pipelineLayout[ndx] = createPipelineLayout(vk, vkDevice, &pipelineLayoutCreateInfo); const VkPipelineShaderStageCreateInfo stageCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // VkPipelineShaderStageCreateFlags flags; VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagBits stage; *m_computeShaderModule[ndx], // VkShaderModule module; "main", // const char* pName; DE_NULL, // const VkSpecializationInfo* pSpecializationInfo; }; VkPipelineCreateFlags flags = 0; if (m_param->getTestStatistics()) { flags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; } // Only check gather internal representations on the second // pipeline. This way, it's more obvious if they failed to capture // due to the pipeline being cached. if (ndx == PIPELINE_CACHE_NDX_CACHED && m_param->getTestInternalRepresentations()) { flags |= VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR; } const VkComputePipelineCreateInfo pipelineCreateInfo = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; flags, // VkPipelineCreateFlags flags; stageCreateInfo, // VkPipelineShaderStageCreateInfo stage; *m_pipelineLayout[ndx], // VkPipelineLayout layout; (VkPipeline)0, // VkPipeline basePipelineHandle; 0u, // deInt32 basePipelineIndex; }; m_pipeline[ndx] = createComputePipeline(vk, vkDevice, *m_cache, &pipelineCreateInfo, DE_NULL); } ComputeExecutablePropertiesTestInstance::ComputeExecutablePropertiesTestInstance (Context& context, const ExecutablePropertiesTestParam* param) : ExecutablePropertiesTestInstance (context, param) { for (deUint32 ndx = 0; ndx < PIPELINE_CACHE_NDX_COUNT; ndx++) { buildDescriptorSets(ndx); buildShader(ndx); buildPipeline(ndx); } } ComputeExecutablePropertiesTestInstance::~ComputeExecutablePropertiesTestInstance (void) { } } // anonymous tcu::TestCaseGroup* createExecutablePropertiesTests (tcu::TestContext& testCtx, PipelineConstructionType pipelineConstructionType) { de::MovePtr binaryInfoTests (new tcu::TestCaseGroup(testCtx, "executable_properties", "pipeline binary statistics tests")); // Graphics Pipeline Tests { de::MovePtr graphicsTests (new tcu::TestCaseGroup(testCtx, "graphics", "Test pipeline binary info with graphics pipeline.")); const VkShaderStageFlags vertFragStages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; const VkShaderStageFlags vertGeomFragStages = vertFragStages | VK_SHADER_STAGE_GEOMETRY_BIT; const VkShaderStageFlags vertTessFragStages = vertFragStages | VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; const ExecutablePropertiesTestParam testParams[] { { pipelineConstructionType, vertFragStages, DE_FALSE, DE_FALSE }, { pipelineConstructionType, vertGeomFragStages, DE_FALSE, DE_FALSE }, { pipelineConstructionType, vertTessFragStages, DE_FALSE, DE_FALSE }, { pipelineConstructionType, vertFragStages, DE_TRUE, DE_FALSE }, { pipelineConstructionType, vertGeomFragStages, DE_TRUE, DE_FALSE }, { pipelineConstructionType, vertTessFragStages, DE_TRUE, DE_FALSE }, { pipelineConstructionType, vertFragStages, DE_FALSE, DE_TRUE }, { pipelineConstructionType, vertGeomFragStages, DE_FALSE, DE_TRUE }, { pipelineConstructionType, vertTessFragStages, DE_FALSE, DE_TRUE }, { pipelineConstructionType, vertFragStages, DE_TRUE, DE_TRUE }, { pipelineConstructionType, vertGeomFragStages, DE_TRUE, DE_TRUE }, { pipelineConstructionType, vertTessFragStages, DE_TRUE, DE_TRUE }, }; for (deUint32 i = 0; i < DE_LENGTH_OF_ARRAY(testParams); i++) graphicsTests->addChild(newTestCase(testCtx, &testParams[i])); binaryInfoTests->addChild(graphicsTests.release()); } // Compute Pipeline Tests - don't repeat those tests for graphics pipeline library if (pipelineConstructionType == PIPELINE_CONSTRUCTION_TYPE_MONOLITHIC) { de::MovePtr computeTests (new tcu::TestCaseGroup(testCtx, "compute", "Test pipeline binary info with compute pipeline.")); const ExecutablePropertiesTestParam testParams[] { { pipelineConstructionType, VK_SHADER_STAGE_COMPUTE_BIT, DE_FALSE, DE_FALSE }, { pipelineConstructionType, VK_SHADER_STAGE_COMPUTE_BIT, DE_TRUE, DE_FALSE }, { pipelineConstructionType, VK_SHADER_STAGE_COMPUTE_BIT, DE_FALSE, DE_TRUE }, { pipelineConstructionType, VK_SHADER_STAGE_COMPUTE_BIT, DE_TRUE, DE_TRUE }, }; for (deUint32 i = 0; i < DE_LENGTH_OF_ARRAY(testParams); i++) computeTests->addChild(newTestCase(testCtx, &testParams[i])); binaryInfoTests->addChild(computeTests.release()); } return binaryInfoTests.release(); } } // pipeline } // vkt