| /*------------------------------------------------------------------------ |
| * Vulkan Conformance Tests |
| * ------------------------ |
| * |
| * Copyright (c) 2022 The Khronos Group Inc. |
| * Copyright (c) 2022 Valve 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_EXT_shader_module_identifier tests |
| *//*--------------------------------------------------------------------*/ |
| #include "vktPipelineShaderModuleIdentifierTests.hpp" |
| #include "vktTestCase.hpp" |
| #include "vktTestCaseUtil.hpp" |
| #include "vktCustomInstancesDevices.hpp" |
| |
| #include "vkQueryUtil.hpp" |
| #include "vkMemUtil.hpp" |
| #include "vkBuilderUtil.hpp" |
| #include "vkBufferWithMemory.hpp" |
| #include "vkImageWithMemory.hpp" |
| #include "vkObjUtil.hpp" |
| #include "vkTypeUtil.hpp" |
| #include "vkRayTracingUtil.hpp" |
| #include "vkCmdUtil.hpp" |
| #include "vkImageUtil.hpp" |
| #include "vkPipelineConstructionUtil.hpp" |
| #include "vkBarrierUtil.hpp" |
| |
| #include "tcuMaybe.hpp" |
| #include "tcuCommandLine.hpp" |
| #include "tcuFormatUtil.hpp" |
| #include "tcuImageCompare.hpp" |
| #include "tcuTestLog.hpp" |
| |
| #include "deUniquePtr.hpp" |
| #include "deRandom.hpp" |
| |
| #include <vector> |
| #include <utility> |
| #include <string> |
| #include <sstream> |
| #include <algorithm> |
| #include <iterator> |
| #include <memory> |
| #include <set> |
| #include <limits> |
| |
| namespace vkt |
| { |
| namespace pipeline |
| { |
| |
| namespace |
| { |
| |
| using GroupPtr = de::MovePtr<tcu::TestCaseGroup>; |
| using StringVec = std::vector<std::string>; |
| using namespace vk; |
| |
| using ShaderModuleId = std::vector<uint8_t>; |
| using ShaderModuleIdPtr = std::unique_ptr<ShaderModuleId>; |
| using ShaderStageIdPtr = std::unique_ptr<VkPipelineShaderStageModuleIdentifierCreateInfoEXT>; |
| |
| // Helper function to create a shader module identifier from a VkShaderModuleIdentifierEXT structure. |
| ShaderModuleId makeShaderModuleId (const VkShaderModuleIdentifierEXT& idExt) |
| { |
| if (idExt.identifierSize == 0u || idExt.identifierSize > VK_MAX_SHADER_MODULE_IDENTIFIER_SIZE_EXT) |
| TCU_FAIL("Invalid identifierSize returned"); |
| |
| ShaderModuleId identifier(idExt.identifier, idExt.identifier + idExt.identifierSize); |
| return identifier; |
| } |
| |
| // Helper function to obtain the shader module identifier for a VkShaderModule as a return value. |
| ShaderModuleId getShaderModuleIdentifier (const DeviceInterface& vkd, const VkDevice device, const VkShaderModule module) |
| { |
| VkShaderModuleIdentifierEXT idExt = initVulkanStructure(); |
| vkd.getShaderModuleIdentifierEXT(device, module, &idExt); |
| return makeShaderModuleId(idExt); |
| } |
| |
| // Helper function to obtain the shader module identifier from a VkShaderModuleCreateInfo structure as a return value. |
| ShaderModuleId getShaderModuleIdentifier (const DeviceInterface& vkd, const VkDevice device, const VkShaderModuleCreateInfo& createInfo) |
| { |
| VkShaderModuleIdentifierEXT idExt = initVulkanStructure(); |
| vkd.getShaderModuleCreateInfoIdentifierEXT(device, &createInfo, &idExt); |
| return makeShaderModuleId(idExt); |
| } |
| |
| // Helper function to create a VkShaderModuleCreateInfo structure. |
| VkShaderModuleCreateInfo makeShaderModuleCreateInfo (size_t codeSize, const uint32_t* pCode, const VkShaderModuleCreateFlags createFlags = 0u, const void* pNext = nullptr) |
| { |
| const VkShaderModuleCreateInfo moduleCreateInfo = |
| { |
| VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, // VkStructureType sType; |
| pNext, // const void* pNext; |
| createFlags, // VkShaderModuleCreateFlags flags; |
| codeSize, // size_t codeSize; |
| pCode, // const uint32_t* pCode; |
| }; |
| return moduleCreateInfo; |
| } |
| |
| // On the actual pipeline in use, will we use module IDs or other data? |
| enum class UseModuleCase |
| { |
| ID = 0, |
| ZERO_LEN_ID, |
| ZERO_LEN_ID_NULL_PTR, |
| ZERO_LEN_ID_GARBAGE_PTR, |
| ALL_ZEROS, |
| ALL_ONES, |
| PSEUDORANDOM_ID, |
| }; |
| |
| bool isZeroLen (UseModuleCase usage) |
| { |
| bool zeroLen = false; |
| |
| switch (usage) |
| { |
| case UseModuleCase::ZERO_LEN_ID: |
| case UseModuleCase::ZERO_LEN_ID_NULL_PTR: |
| case UseModuleCase::ZERO_LEN_ID_GARBAGE_PTR: |
| zeroLen = true; |
| break; |
| default: |
| break; |
| } |
| |
| return zeroLen; |
| } |
| |
| bool expectCacheMiss (UseModuleCase usage) |
| { |
| bool miss = false; |
| |
| switch (usage) |
| { |
| case UseModuleCase::ALL_ZEROS: |
| case UseModuleCase::ALL_ONES: |
| case UseModuleCase::PSEUDORANDOM_ID: |
| miss = true; |
| break; |
| default: |
| break; |
| } |
| |
| return miss; |
| } |
| |
| // Modify a shader module id according to the type of use. |
| void maybeMangleShaderModuleId (ShaderModuleId& moduleId, UseModuleCase moduleUse, de::Random& rnd) |
| { |
| if (moduleUse == UseModuleCase::ALL_ZEROS || moduleUse == UseModuleCase::ALL_ONES) |
| { |
| deMemset(moduleId.data(), ((moduleUse == UseModuleCase::ALL_ZEROS) ? 0 : 0xFF), de::dataSize(moduleId)); |
| } |
| else if (moduleUse == UseModuleCase::PSEUDORANDOM_ID) |
| { |
| for (auto& byte : moduleId) |
| byte = rnd.getUint8(); |
| } |
| } |
| |
| // Helper function to create a VkPipelineShaderStageModuleIdentifierCreateInfoEXT structure. |
| ShaderStageIdPtr makeShaderStageModuleIdentifierCreateInfo (const ShaderModuleId& moduleId, UseModuleCase moduleUse, de::Random* rnd = nullptr) |
| { |
| ShaderStageIdPtr createInfo(new VkPipelineShaderStageModuleIdentifierCreateInfoEXT(initVulkanStructure())); |
| |
| createInfo->identifierSize = (isZeroLen(moduleUse) ? 0u : de::sizeU32(moduleId)); |
| |
| switch (moduleUse) |
| { |
| case UseModuleCase::ID: |
| case UseModuleCase::ZERO_LEN_ID: |
| case UseModuleCase::ALL_ZEROS: // For this one and below, the module id will have been modified outside. |
| case UseModuleCase::ALL_ONES: |
| case UseModuleCase::PSEUDORANDOM_ID: |
| createInfo->pIdentifier = de::dataOrNull(moduleId); |
| break; |
| case UseModuleCase::ZERO_LEN_ID_NULL_PTR: |
| break; // Already null as part of initVulkanStructure(). |
| case UseModuleCase::ZERO_LEN_ID_GARBAGE_PTR: |
| { |
| DE_ASSERT(rnd); |
| |
| // Fill pointer with random bytes. |
| auto pIdentifierPtr = reinterpret_cast<uint8_t*>(&(createInfo->pIdentifier)); |
| |
| for (size_t i = 0; i < sizeof(createInfo->pIdentifier); ++i) |
| pIdentifierPtr[i] = rnd->getUint8(); |
| } |
| break; |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| |
| return createInfo; |
| } |
| |
| VkShaderModule retUsedModule (VkShaderModule module, UseModuleCase moduleUse) |
| { |
| return (isZeroLen(moduleUse) ? module : DE_NULL); |
| } |
| |
| enum class PipelineType |
| { |
| COMPUTE = 0, |
| GRAPHICS, |
| RAY_TRACING, |
| }; |
| |
| enum class GraphicsShaderType |
| { |
| VERTEX = 0, |
| TESS_CONTROL, |
| TESS_EVAL, |
| GEOMETRY, |
| FRAG, |
| }; |
| |
| enum class RayTracingShaderType |
| { |
| RAY_GEN = 0, |
| CLOSEST_HIT, |
| ANY_HIT, |
| INTERSECTION, |
| MISS, |
| CALLABLE, |
| }; |
| |
| using GraphicsShaderVec = std::vector<GraphicsShaderType>; |
| using RTShaderVec = std::vector<RayTracingShaderType>; |
| |
| std::ostream& operator<<(std::ostream& out, GraphicsShaderType type) |
| { |
| switch (type) |
| { |
| case GraphicsShaderType::VERTEX: out << "vert"; break; |
| case GraphicsShaderType::TESS_CONTROL: out << "tesc"; break; |
| case GraphicsShaderType::TESS_EVAL: out << "tese"; break; |
| case GraphicsShaderType::GEOMETRY: out << "geom"; break; |
| case GraphicsShaderType::FRAG: out << "frag"; break; |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| return out; |
| } |
| |
| std::ostream& operator<<(std::ostream& out, RayTracingShaderType type) |
| { |
| switch (type) |
| { |
| case RayTracingShaderType::RAY_GEN: out << "rgen"; break; |
| case RayTracingShaderType::CLOSEST_HIT: out << "chit"; break; |
| case RayTracingShaderType::ANY_HIT: out << "ahit"; break; |
| case RayTracingShaderType::INTERSECTION: out << "isec"; break; |
| case RayTracingShaderType::MISS: out << "miss"; break; |
| case RayTracingShaderType::CALLABLE: out << "call"; break; |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| return out; |
| } |
| |
| template <class T> |
| std::string toString(const std::vector<T>& vec) |
| { |
| std::ostringstream out; |
| for (size_t i = 0; i < vec.size(); ++i) |
| out << ((i == 0) ? "" : "_") << vec.at(i); |
| return out.str(); |
| } |
| |
| // Pipeline executable properties helpers. |
| struct PipelineExecutableStat |
| { |
| PipelineExecutableStat (std::string name_, std::string description_, VkPipelineExecutableStatisticFormatKHR format_, VkPipelineExecutableStatisticValueKHR value_) |
| : name (std::move(name_)) |
| , description (std::move(description_)) |
| , format (format_) |
| , value (value_) |
| {} |
| |
| const std::string name; |
| const std::string description; |
| const VkPipelineExecutableStatisticFormatKHR format; |
| const VkPipelineExecutableStatisticValueKHR value; |
| }; |
| |
| struct PipelineExecutableInternalRepresentation |
| { |
| PipelineExecutableInternalRepresentation (std::string name_, std::string description_, bool isText_, const std::vector<uint8_t>& data) |
| : name (std::move(name_)) |
| , description (std::move(description_)) |
| , m_isText (isText_) |
| , m_text () |
| , m_bytes () |
| { |
| if (m_isText) |
| m_text = std::string(reinterpret_cast<const char*>(data.data())); |
| else |
| m_bytes = data; |
| } |
| |
| const std::string name; |
| const std::string description; |
| |
| bool isText (void) const { return m_isText; } |
| const std::string& getText (void) const { DE_ASSERT(isText()); return m_text; } |
| const std::vector<uint8_t>& getBytes (void) const { DE_ASSERT(!isText()); return m_bytes; } |
| |
| protected: |
| const bool m_isText; |
| std::string m_text; |
| std::vector<uint8_t> m_bytes; |
| }; |
| |
| struct PipelineExecutableProperty |
| { |
| PipelineExecutableProperty (VkShaderStageFlags stageFlags_, std::string name_, std::string description_, uint32_t subgroupSize_) |
| : stageFlags (stageFlags_) |
| , name (std::move(name_)) |
| , description (std::move(description_)) |
| , subgroupSize (subgroupSize_) |
| , m_stats () |
| , m_irs () |
| {} |
| |
| const VkShaderStageFlags stageFlags; |
| const std::string name; |
| const std::string description; |
| const uint32_t subgroupSize; |
| |
| void addStat (const PipelineExecutableStat& stat) { m_stats.emplace_back(stat); } |
| void addIR (const PipelineExecutableInternalRepresentation& ir) { m_irs.emplace_back(ir); } |
| |
| const std::vector<PipelineExecutableStat>& getStats (void) const { return m_stats; } |
| const std::vector<PipelineExecutableInternalRepresentation>& getIRs (void) const { return m_irs; } |
| |
| protected: |
| std::vector<PipelineExecutableStat> m_stats; |
| std::vector<PipelineExecutableInternalRepresentation> m_irs; |
| }; |
| |
| // This will NOT compare stats and IRs, only flags, name, description and subgroup sizes. |
| bool operator== (const PipelineExecutableProperty& a, const PipelineExecutableProperty& b) |
| { |
| return (a.stageFlags == b.stageFlags && a.name == b.name && a.description == b.description && a.subgroupSize == b.subgroupSize); |
| } |
| |
| // For sorting if used as a map key or in a set. Based on the property name. |
| bool operator< (const PipelineExecutableProperty& a, const PipelineExecutableProperty& b) |
| { |
| return (a.name < b.name); |
| } |
| |
| std::ostream& operator<< (std::ostream& out, const PipelineExecutableProperty& prop) |
| { |
| out << "PipelineExecutableProperty(" |
| << "stageFlags=\"" << prop.stageFlags << "\", " |
| << "name=\"" << prop.name << "\", " |
| << "description=\"" << prop.description << "\", " |
| << "subgroupSize=\"" << prop.subgroupSize << "\")"; |
| return out; |
| } |
| |
| // What to capture from a pipeline. |
| enum class CapturedPropertiesBits |
| { |
| NONE = 0, |
| STATS = 1, |
| IRS = 2, |
| }; |
| using CapturedPropertiesFlags = uint32_t; |
| |
| VkPipelineCreateFlags getPipelineCreateFlags (CapturedPropertiesFlags capturedProperties) |
| { |
| VkPipelineCreateFlags createFlags = 0u; |
| |
| if (capturedProperties & static_cast<int>(CapturedPropertiesBits::STATS)) |
| createFlags |= VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR; |
| |
| if (capturedProperties & static_cast<int>(CapturedPropertiesBits::IRS)) |
| createFlags |= VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR; |
| |
| return createFlags; |
| } |
| |
| VkPipelineInfoKHR makePipelineInfo (VkPipeline pipeline) |
| { |
| VkPipelineInfoKHR pipelineInfo = initVulkanStructure(); |
| pipelineInfo.pipeline = pipeline; |
| return pipelineInfo; |
| } |
| |
| VkPipelineExecutableInfoKHR makePipelineExecutableInfo (VkPipeline pipeline, size_t executableIndex) |
| { |
| VkPipelineExecutableInfoKHR info = initVulkanStructure(); |
| info.pipeline = pipeline; |
| info.executableIndex = static_cast<uint32_t>(executableIndex); |
| return info; |
| } |
| |
| using PipelineExecutablePropertyVec = std::vector<PipelineExecutableProperty>; |
| |
| std::ostream& operator<< (std::ostream& out, const PipelineExecutablePropertyVec& vec) |
| { |
| bool first = true; |
| out << "["; |
| for (const auto& prop : vec) |
| { |
| out << (first ? "" : ", ") << prop; |
| first = false; |
| } |
| out << "]"; |
| return out; |
| } |
| |
| PipelineExecutablePropertyVec getPipelineExecutableProperties (const DeviceInterface& vkd, VkDevice device, VkPipeline pipeline, CapturedPropertiesFlags captureFlags) |
| { |
| PipelineExecutablePropertyVec properties; |
| const auto pipelineInfo = makePipelineInfo(pipeline); |
| uint32_t executableCount = 0u; |
| |
| std::vector<VkPipelineExecutablePropertiesKHR> propertiesKHR; |
| VK_CHECK(vkd.getPipelineExecutablePropertiesKHR(device, &pipelineInfo, &executableCount, nullptr)); |
| |
| // No properties? |
| if (executableCount == 0u) |
| return properties; |
| |
| propertiesKHR.resize(executableCount, initVulkanStructure()); |
| VK_CHECK(vkd.getPipelineExecutablePropertiesKHR(device, &pipelineInfo, &executableCount, propertiesKHR.data())); |
| |
| // Make a property with every result structure. |
| properties.reserve(propertiesKHR.size()); |
| for (const auto& prop : propertiesKHR) |
| properties.emplace_back(prop.stages, prop.name, prop.description, prop.subgroupSize); |
| |
| // Query stats if requested. |
| if (captureFlags & static_cast<int>(CapturedPropertiesBits::STATS)) |
| { |
| for (size_t exeIdx = 0; exeIdx < properties.size(); ++exeIdx) |
| { |
| uint32_t statCount = 0u; |
| std::vector<VkPipelineExecutableStatisticKHR> statsKHR; |
| const auto executableInfo = makePipelineExecutableInfo(pipeline, exeIdx); |
| |
| VK_CHECK(vkd.getPipelineExecutableStatisticsKHR(device, &executableInfo, &statCount, nullptr)); |
| |
| if (statCount == 0u) |
| continue; |
| |
| statsKHR.resize(statCount, initVulkanStructure()); |
| VK_CHECK(vkd.getPipelineExecutableStatisticsKHR(device, &executableInfo, &statCount, statsKHR.data())); |
| |
| for (const auto& stat : statsKHR) |
| properties[exeIdx].addStat(PipelineExecutableStat(stat.name, stat.description, stat.format, stat.value)); |
| } |
| } |
| |
| // Query IRs if requested. |
| if (captureFlags & static_cast<int>(CapturedPropertiesBits::IRS)) |
| { |
| for (size_t exeIdx = 0; exeIdx < properties.size(); ++exeIdx) |
| { |
| uint32_t irsCount = 0u; |
| std::vector<VkPipelineExecutableInternalRepresentationKHR> irsKHR; |
| std::vector<std::vector<uint8_t>> irsData; |
| const auto executableInfo = makePipelineExecutableInfo(pipeline, exeIdx); |
| |
| // Get count. |
| VK_CHECK(vkd.getPipelineExecutableInternalRepresentationsKHR(device, &executableInfo, &irsCount, nullptr)); |
| |
| if (irsCount == 0u) |
| continue; |
| |
| // Get data sizes. |
| irsData.resize(irsCount); |
| irsKHR.resize(irsCount, initVulkanStructure()); |
| VK_CHECK(vkd.getPipelineExecutableInternalRepresentationsKHR(device, &executableInfo, &irsCount, irsKHR.data())); |
| |
| // Get data. |
| for (size_t irIdx = 0; irIdx < irsKHR.size(); ++irIdx) |
| { |
| auto& dataBuffer = irsData[irIdx]; |
| auto& irKHR = irsKHR[irIdx]; |
| |
| dataBuffer.resize(irKHR.dataSize); |
| irKHR.pData = dataBuffer.data(); |
| } |
| VK_CHECK(vkd.getPipelineExecutableInternalRepresentationsKHR(device, &executableInfo, &irsCount, irsKHR.data())); |
| |
| // Append IRs to property. |
| for (size_t irIdx = 0; irIdx < irsKHR.size(); ++irIdx) |
| { |
| const auto& ir = irsKHR[irIdx]; |
| properties[exeIdx].addIR(PipelineExecutableInternalRepresentation(ir.name, ir.description, ir.isText, irsData[irIdx])); |
| } |
| } |
| } |
| |
| return properties; |
| } |
| |
| struct BaseParams |
| { |
| const PipelineType pipelineType; |
| GraphicsShaderVec graphicsShaders; |
| RTShaderVec rtShaders; |
| const uint8_t pipelineCount; |
| const tcu::Maybe<uint8_t> pipelineToRun; |
| const bool useSpecializationConstants; |
| const bool useCache; |
| |
| BaseParams (PipelineType pipelineType_, |
| GraphicsShaderVec graphicsShaders_, |
| RTShaderVec rtShaders_, |
| uint8_t pipelineCount_, |
| const tcu::Maybe<uint8_t>& pipelineToRun_, |
| bool useSCs_, |
| bool useCache_) |
| : pipelineType (pipelineType_) |
| , graphicsShaders (std::move(graphicsShaders_)) |
| , rtShaders (std::move(rtShaders_)) |
| , pipelineCount (pipelineCount_) |
| , pipelineToRun (pipelineToRun_) |
| , useSpecializationConstants (useSCs_) |
| , useCache (useCache_) |
| { |
| if (pipelineType != PipelineType::GRAPHICS) |
| DE_ASSERT(graphicsShaders.empty()); |
| else if (pipelineType != PipelineType::RAY_TRACING) |
| DE_ASSERT(rtShaders.empty()); |
| |
| if (static_cast<bool>(pipelineToRun)) |
| DE_ASSERT(pipelineToRun.get() < pipelineCount); |
| |
| // We'll use one descriptor set per pipeline, so we only want a few pipelines. |
| DE_ASSERT(static_cast<uint32_t>(pipelineCount) <= 4u); |
| } |
| |
| // Make the class polymorphic, needed below. |
| virtual ~BaseParams () {} |
| |
| size_t stageCountPerPipeline (void) const |
| { |
| size_t stageCount = 0; |
| |
| switch (pipelineType) |
| { |
| case PipelineType::COMPUTE: stageCount = 1u; break; |
| case PipelineType::GRAPHICS: stageCount = graphicsShaders.size(); break; |
| case PipelineType::RAY_TRACING: stageCount = rtShaders.size(); break; |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| |
| return stageCount; |
| } |
| |
| protected: |
| bool hasGraphicsStage (GraphicsShaderType stage) const |
| { |
| if (pipelineType != PipelineType::GRAPHICS) |
| return false; |
| return de::contains(begin(graphicsShaders), end(graphicsShaders), stage); |
| } |
| |
| bool hasRTStage (RayTracingShaderType stage) const |
| { |
| if (pipelineType != PipelineType::RAY_TRACING) |
| return false; |
| return de::contains(begin(rtShaders), end(rtShaders), stage); |
| } |
| |
| public: |
| bool hasGeom (void) const |
| { |
| return hasGraphicsStage(GraphicsShaderType::GEOMETRY); |
| } |
| |
| bool hasTess (void) const |
| { |
| return (hasGraphicsStage(GraphicsShaderType::TESS_CONTROL) || hasGraphicsStage(GraphicsShaderType::TESS_EVAL)); |
| } |
| |
| bool hasVertexPipelineStage (void) const |
| { |
| return (hasGraphicsStage(GraphicsShaderType::VERTEX) || hasTess() || hasGeom()); |
| } |
| |
| bool hasFrag (void) const |
| { |
| return hasGraphicsStage(GraphicsShaderType::FRAG); |
| } |
| |
| bool hasRayTracing (void) const |
| { |
| return (pipelineType == PipelineType::RAY_TRACING); |
| } |
| |
| bool hasHit (void) const |
| { |
| return (hasRTStage(RayTracingShaderType::ANY_HIT) || hasRTStage(RayTracingShaderType::CLOSEST_HIT) || hasRTStage(RayTracingShaderType::INTERSECTION)); |
| } |
| |
| bool hasISec (void) const |
| { |
| return hasRTStage(RayTracingShaderType::INTERSECTION); |
| } |
| |
| bool hasMiss (void) const |
| { |
| return hasRTStage(RayTracingShaderType::MISS); |
| } |
| |
| VkPipelineStageFlags getPipelineStageFlags (void) const |
| { |
| if (pipelineType == PipelineType::COMPUTE) |
| return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; |
| |
| if (pipelineType == PipelineType::RAY_TRACING) |
| return VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR; |
| |
| if (pipelineType == PipelineType::GRAPHICS) |
| { |
| VkPipelineStageFlags stageFlags = 0u; |
| |
| for (const auto& stage : graphicsShaders) |
| { |
| switch (stage) |
| { |
| case GraphicsShaderType::VERTEX: stageFlags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT; break; |
| case GraphicsShaderType::TESS_CONTROL: stageFlags |= VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT; break; |
| case GraphicsShaderType::TESS_EVAL: stageFlags |= VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT; break; |
| case GraphicsShaderType::GEOMETRY: stageFlags |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT; break; |
| case GraphicsShaderType::FRAG: stageFlags |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; break; |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| } |
| |
| return stageFlags; |
| } |
| |
| DE_ASSERT(false); |
| return 0u; |
| } |
| |
| VkShaderStageFlags getShaderStageFlags (void) const |
| { |
| if (pipelineType == PipelineType::COMPUTE) |
| return VK_SHADER_STAGE_COMPUTE_BIT; |
| |
| if (pipelineType == PipelineType::RAY_TRACING) |
| { |
| VkShaderStageFlags stageFlags = 0u; |
| |
| for (const auto& stage : rtShaders) |
| { |
| switch (stage) |
| { |
| case RayTracingShaderType::RAY_GEN: stageFlags |= VK_SHADER_STAGE_RAYGEN_BIT_KHR; break; |
| case RayTracingShaderType::CLOSEST_HIT: stageFlags |= VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR; break; |
| case RayTracingShaderType::ANY_HIT: stageFlags |= VK_SHADER_STAGE_ANY_HIT_BIT_KHR; break; |
| case RayTracingShaderType::INTERSECTION: stageFlags |= VK_SHADER_STAGE_INTERSECTION_BIT_KHR; break; |
| case RayTracingShaderType::MISS: stageFlags |= VK_SHADER_STAGE_MISS_BIT_KHR; break; |
| case RayTracingShaderType::CALLABLE: stageFlags |= VK_SHADER_STAGE_CALLABLE_BIT_KHR; break; |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| } |
| |
| return stageFlags; |
| } |
| |
| if (pipelineType == PipelineType::GRAPHICS) |
| { |
| VkShaderStageFlags stageFlags = 0u; |
| |
| for (const auto& stage : graphicsShaders) |
| { |
| switch (stage) |
| { |
| case GraphicsShaderType::VERTEX: stageFlags |= VK_SHADER_STAGE_VERTEX_BIT; break; |
| case GraphicsShaderType::TESS_CONTROL: stageFlags |= VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; break; |
| case GraphicsShaderType::TESS_EVAL: stageFlags |= VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; break; |
| case GraphicsShaderType::GEOMETRY: stageFlags |= VK_SHADER_STAGE_GEOMETRY_BIT; break; |
| case GraphicsShaderType::FRAG: stageFlags |= VK_SHADER_STAGE_FRAGMENT_BIT; break; |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| } |
| |
| return stageFlags; |
| } |
| |
| DE_ASSERT(false); |
| return 0u; |
| } |
| }; |
| |
| using BaseParamsPtr = std::unique_ptr<BaseParams>; |
| |
| void checkShaderModuleIdentifierSupport (Context& context) |
| { |
| context.requireDeviceFunctionality("VK_EXT_shader_module_identifier"); |
| } |
| |
| void getTwoShaderIdentifierProperties (Context& context, |
| VkPhysicalDeviceShaderModuleIdentifierPropertiesEXT* properties1, |
| VkPhysicalDeviceShaderModuleIdentifierPropertiesEXT* properties2) |
| { |
| *properties1 = initVulkanStructure(); |
| *properties2 = initVulkanStructure(); |
| |
| const auto& vki = context.getInstanceInterface(); |
| const auto physicalDevice = context.getPhysicalDevice(); |
| VkPhysicalDeviceProperties2 main = initVulkanStructure(properties1); |
| |
| vki.getPhysicalDeviceProperties2(physicalDevice, &main); |
| main.pNext = properties2; |
| vki.getPhysicalDeviceProperties2(physicalDevice, &main); |
| } |
| |
| tcu::TestStatus constantAlgorithmUUIDCase (Context& context) |
| { |
| VkPhysicalDeviceShaderModuleIdentifierPropertiesEXT properties1, properties2; |
| getTwoShaderIdentifierProperties(context, &properties1, &properties2); |
| |
| const auto uuidSize = static_cast<size_t>(VK_UUID_SIZE); |
| |
| if (deMemCmp(properties1.shaderModuleIdentifierAlgorithmUUID, properties2.shaderModuleIdentifierAlgorithmUUID, uuidSize) != 0) |
| return tcu::TestStatus::fail("shaderModuleIdentifierAlgorithmUUID not constant accross calls"); |
| |
| uint8_t nullUUID[uuidSize]; |
| deMemset(nullUUID, 0, uuidSize); |
| |
| if (deMemCmp(properties1.shaderModuleIdentifierAlgorithmUUID, nullUUID, uuidSize) == 0) |
| return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "shaderModuleIdentifierAlgorithmUUID is all zeros"); |
| |
| return tcu::TestStatus::pass("Pass"); |
| } |
| |
| std::vector<uint32_t> generateShaderConstants (PipelineType pipelineType, uint8_t pipelineCount, size_t stageCount) |
| { |
| std::vector<uint32_t> shaderConstants; |
| |
| for (uint8_t pipelineIdx = 0; pipelineIdx < pipelineCount; ++pipelineIdx) |
| for (size_t stageIdx = 0; stageIdx < stageCount; ++stageIdx) |
| shaderConstants.push_back(0xEB000000u |
| | ((static_cast<uint32_t>(pipelineType) & 0xFFu) << 16) |
| | ((static_cast<uint32_t>(pipelineIdx) & 0xFFu) << 8) |
| | ((static_cast<uint32_t>(stageIdx) & 0xFFu) ) |
| ); |
| |
| return shaderConstants; |
| } |
| |
| size_t getShaderIdx (uint8_t pipelineIdx, size_t stageIdx, size_t stageCount) |
| { |
| const auto pIdx = static_cast<size_t>(pipelineIdx); |
| return (pIdx * stageCount + stageIdx); |
| } |
| |
| void generateSources (SourceCollections& programCollection, const BaseParams* params_) |
| { |
| const auto& params = *params_; |
| const auto stageCount = params.stageCountPerPipeline(); |
| const auto constantValues = generateShaderConstants(params.pipelineType, params.pipelineCount, stageCount); |
| |
| StringVec constantDecls; // Per pipeline and stage. |
| StringVec pipelineAdds; // Per pipeline. |
| StringVec stageStores; // Per stage. |
| |
| std::string ssboDecl; // Universal. |
| std::string uboDecls; // Universal. |
| std::string outValueDecl = " uint outValue = stageConstant;\n"; // Universal. |
| |
| // Each stage in each pipeline will have one specific constant value. |
| { |
| for (uint8_t pipelineIdx = 0; pipelineIdx < params.pipelineCount; ++pipelineIdx) |
| for (size_t stageIdx = 0; stageIdx < stageCount; ++stageIdx) |
| { |
| constantDecls.push_back(params.useSpecializationConstants |
| ? "layout (constant_id=0) const uint stageConstant = 0u;\n" |
| : "const uint stageConstant = " + std::to_string(constantValues.at(getShaderIdx(pipelineIdx, stageIdx, stageCount))) + "u;\n"); |
| } |
| } |
| |
| // Each pipeline will have slightly different code by adding more values to the constant in each shader. |
| // The values will come from UBOs and, in practice, will contain zeros. |
| { |
| pipelineAdds.reserve(params.pipelineCount); |
| |
| for (uint8_t pipelineIdx = 0; pipelineIdx < params.pipelineCount; ++pipelineIdx) |
| { |
| std::string additions; |
| const auto addCount = static_cast<size_t>(pipelineIdx + 1); |
| |
| for (size_t addIdx = 0; addIdx < addCount; ++addIdx) |
| { |
| const auto uboId = addIdx + 1; |
| additions += " outValue += ubo_" + std::to_string(uboId) + ".value;\n"; |
| } |
| |
| pipelineAdds.push_back(additions); |
| } |
| } |
| |
| // Each stage will write the output value to an SSBO position. |
| { |
| stageStores.reserve(stageCount); |
| |
| for (size_t stageIdx = 0; stageIdx < stageCount; ++stageIdx) |
| { |
| const auto stageStore = " ssbo.values[" + std::to_string(stageIdx) + "] = outValue;\n"; |
| stageStores.push_back(stageStore); |
| } |
| } |
| |
| // The SSBO declaration is constant. |
| ssboDecl = "layout (set=0, binding=0, std430) buffer SSBOBlock { uint values[]; } ssbo;\n"; |
| |
| // The UBO declarations are constant. We need one UBO per pipeline, but all pipelines declare them all. |
| { |
| for (uint8_t pipelineIdx = 0; pipelineIdx < params.pipelineCount; ++pipelineIdx) |
| { |
| const auto uboId = pipelineIdx + 1; |
| const auto idStr = std::to_string(uboId); |
| uboDecls += "layout (set=0, binding=" + idStr + ") uniform UBOBlock" + idStr + " { uint value; } ubo_" + idStr + ";\n"; |
| } |
| } |
| |
| if (params.pipelineType == PipelineType::COMPUTE) |
| { |
| const std::string localSize = (params.useSpecializationConstants |
| ? "layout (local_size_x_id=1, local_size_y_id=2, local_size_z_id=3) in;\n" |
| : "layout (local_size_x=1, local_size_y=1, local_size_z=1) in;\n"); |
| |
| for (uint8_t pipelineIdx = 0; pipelineIdx < params.pipelineCount; ++pipelineIdx) |
| { |
| const auto plIdxSz = static_cast<size_t>(pipelineIdx); |
| const std::string shaderName = "comp_" + std::to_string(plIdxSz); |
| const auto shaderIdx = getShaderIdx(pipelineIdx, 0, stageCount); |
| |
| std::ostringstream comp; |
| comp |
| << "#version 450\n" |
| << localSize |
| << ssboDecl |
| << uboDecls |
| << constantDecls.at(shaderIdx) |
| << "void main (void) {\n" |
| << outValueDecl |
| << pipelineAdds.at(plIdxSz) |
| << " if (gl_LocalInvocationIndex == 0u) {\n" |
| << stageStores.at(0) |
| << " }\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add(shaderName) << glu::ComputeSource(comp.str()); |
| } |
| } |
| else if (params.pipelineType == PipelineType::GRAPHICS) |
| { |
| bool hasVertex = false; |
| bool hasTessControl = false; |
| bool hasTessEval = false; |
| bool hasGeom = false; |
| bool hasFrag = false; |
| |
| // Assign a unique index to each active shader type. |
| size_t vertShaderIdx = 0u; |
| size_t tescShaderIdx = 0u; |
| size_t teseShaderIdx = 0u; |
| size_t geomShaderIdx = 0u; |
| size_t fragShaderIdx = 0u; |
| size_t curShaderIdx = 0u; |
| |
| const std::set<GraphicsShaderType> uniqueStages (begin(params.graphicsShaders), end(params.graphicsShaders)); |
| |
| for (const auto& stage : uniqueStages) |
| { |
| switch (stage) |
| { |
| case GraphicsShaderType::VERTEX: hasVertex = true; vertShaderIdx = curShaderIdx++; break; |
| case GraphicsShaderType::TESS_CONTROL: hasTessControl = true; tescShaderIdx = curShaderIdx++; break; |
| case GraphicsShaderType::TESS_EVAL: hasTessEval = true; teseShaderIdx = curShaderIdx++; break; |
| case GraphicsShaderType::GEOMETRY: hasGeom = true; geomShaderIdx = curShaderIdx++; break; |
| case GraphicsShaderType::FRAG: hasFrag = true; fragShaderIdx = curShaderIdx++; break; |
| default: DE_ASSERT(false); break; |
| } |
| } |
| |
| const bool hasTess = (hasTessControl || hasTessEval); |
| |
| for (uint8_t pipelineIdx = 0; pipelineIdx < params.pipelineCount; ++pipelineIdx) |
| { |
| const auto plIdxSz = static_cast<size_t>(pipelineIdx); |
| |
| if (hasVertex) |
| { |
| const std::string shaderName = "vert_" + std::to_string(plIdxSz); |
| const auto shaderIdx = getShaderIdx(pipelineIdx, vertShaderIdx, stageCount); |
| |
| std::ostringstream vert; |
| vert |
| << "#version 450\n" |
| << "out gl_PerVertex\n" |
| << "{\n" |
| << " vec4 gl_Position;\n" |
| << (hasTess ? "" : " float gl_PointSize;\n") |
| << "};\n" |
| ; |
| |
| if (hasTess) |
| { |
| vert |
| << "vec2 vertexPositions[3] = vec2[](\n" |
| << " vec2( 0.0, -0.5),\n" |
| << " vec2( 0.5, 0.5),\n" |
| << " vec2(-0.5, 0.5)\n" |
| << ");\n" |
| ; |
| } |
| |
| vert |
| << ssboDecl |
| << uboDecls |
| << constantDecls.at(shaderIdx) |
| << "void main (void) {\n" |
| << outValueDecl |
| << pipelineAdds.at(plIdxSz) |
| << stageStores.at(vertShaderIdx) |
| ; |
| |
| if (hasTess) |
| { |
| vert << " gl_Position = vec4(vertexPositions[gl_VertexIndex], 0.0, 1.0);\n"; |
| } |
| else |
| { |
| vert |
| << " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n" |
| << " gl_PointSize = 1.0;\n" |
| ; |
| } |
| |
| vert << "}\n"; |
| |
| programCollection.glslSources.add(shaderName) << glu::VertexSource(vert.str()); |
| } |
| |
| if (hasFrag) |
| { |
| const std::string shaderName = "frag_" + std::to_string(plIdxSz); |
| const auto shaderIdx = getShaderIdx(pipelineIdx, fragShaderIdx, stageCount); |
| |
| std::ostringstream frag; |
| frag |
| << "#version 450\n" |
| << "layout (location=0) out vec4 outColor;\n" |
| << ssboDecl |
| << uboDecls |
| << constantDecls.at(shaderIdx) |
| << "void main (void) {\n" |
| << outValueDecl |
| << pipelineAdds.at(plIdxSz) |
| << stageStores.at(fragShaderIdx) |
| << " outColor = vec4(0.0, 0.0, 1.0, 1.0);\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add(shaderName) << glu::FragmentSource(frag.str()); |
| } |
| |
| if (hasTessControl) |
| { |
| const std::string shaderName = "tesc_" + std::to_string(plIdxSz); |
| const auto shaderIdx = getShaderIdx(pipelineIdx, tescShaderIdx, stageCount); |
| |
| std::ostringstream tesc; |
| tesc |
| << "#version 450\n" |
| << "layout (vertices=3) out;\n" |
| << "in gl_PerVertex\n" |
| << "{\n" |
| << " vec4 gl_Position;\n" |
| << "} gl_in[gl_MaxPatchVertices];\n" |
| << "out gl_PerVertex\n" |
| << "{\n" |
| << " vec4 gl_Position;\n" |
| << "} gl_out[];\n" |
| << ssboDecl |
| << uboDecls |
| << constantDecls.at(shaderIdx) |
| << "void main (void) {\n" |
| << outValueDecl |
| << pipelineAdds.at(plIdxSz) |
| << stageStores.at(tescShaderIdx) |
| << " gl_TessLevelInner[0] = 1.0;\n" |
| << " gl_TessLevelInner[1] = 1.0;\n" |
| << " gl_TessLevelOuter[0] = 1.0;\n" |
| << " gl_TessLevelOuter[1] = 1.0;\n" |
| << " gl_TessLevelOuter[2] = 1.0;\n" |
| << " gl_TessLevelOuter[3] = 1.0;\n" |
| << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add(shaderName) << glu::TessellationControlSource(tesc.str()); |
| } |
| |
| if (hasTessEval) |
| { |
| const std::string shaderName = "tese_" + std::to_string(plIdxSz); |
| const auto shaderIdx = getShaderIdx(pipelineIdx, teseShaderIdx, stageCount); |
| |
| std::ostringstream tese; |
| tese |
| << "#version 450\n" |
| << "layout (triangles, fractional_odd_spacing, cw) in;\n" |
| << "in gl_PerVertex\n" |
| << "{\n" |
| << " vec4 gl_Position;\n" |
| << "} gl_in[gl_MaxPatchVertices];\n" |
| << "out gl_PerVertex\n" |
| << "{\n" |
| << " vec4 gl_Position;\n" |
| << "};\n" |
| << ssboDecl |
| << uboDecls |
| << constantDecls.at(shaderIdx) |
| << "void main (void) {\n" |
| << outValueDecl |
| << pipelineAdds.at(plIdxSz) |
| << stageStores.at(teseShaderIdx) |
| << " gl_Position = (gl_TessCoord.x * gl_in[0].gl_Position) +\n" |
| << " (gl_TessCoord.y * gl_in[1].gl_Position) +\n" |
| << " (gl_TessCoord.z * gl_in[2].gl_Position);\n" |
| << "}\n" |
| ; |
| programCollection.glslSources.add(shaderName) << glu::TessellationEvaluationSource(tese.str()); |
| } |
| |
| if (hasGeom) |
| { |
| const std::string shaderName = "geom_" + std::to_string(plIdxSz); |
| const auto shaderIdx = getShaderIdx(pipelineIdx, geomShaderIdx, stageCount); |
| const auto inputPrim = (hasTess ? "triangles" : "points"); |
| const auto outputPrim = (hasTess ? "triangle_strip" : "points"); |
| const auto vertexCount = (hasTess ? 3u : 1u); |
| |
| std::ostringstream geom; |
| geom |
| << "#version 450\n" |
| << "layout (" << inputPrim << ") in;\n" |
| << "layout (" << outputPrim << ", max_vertices=" << vertexCount << ") out;\n" |
| << "in gl_PerVertex\n" |
| << "{\n" |
| << " vec4 gl_Position;\n" |
| << (hasTess ? "" : " float gl_PointSize;\n") |
| << "} gl_in[" << vertexCount << "];\n" |
| << "out gl_PerVertex\n" |
| << "{\n" |
| << " vec4 gl_Position;\n" |
| << (hasTess ? "" : " float gl_PointSize;\n") |
| << "};\n" |
| << ssboDecl |
| << uboDecls |
| << constantDecls.at(shaderIdx) |
| << "void main (void) {\n" |
| << outValueDecl |
| << pipelineAdds.at(plIdxSz) |
| << stageStores.at(geomShaderIdx) |
| ; |
| |
| for (uint32_t i = 0; i < vertexCount; ++i) |
| { |
| geom |
| << " gl_Position = gl_in[" << i << "].gl_Position;\n" |
| << (hasTess ? "" : " gl_PointSize = gl_in[" + std::to_string(i) + "].gl_PointSize;\n") |
| << " EmitVertex();\n" |
| ; |
| } |
| |
| geom << "}\n"; |
| |
| programCollection.glslSources.add(shaderName) << glu::GeometrySource(geom.str()); |
| } |
| } |
| } |
| else if (params.pipelineType == PipelineType::RAY_TRACING) |
| { |
| bool hasRayGen = false; |
| bool hasAnyHit = false; |
| bool hasClosestHit = false; |
| bool hasIntersection = false; |
| bool hasMiss = false; |
| bool hasCallable = false; |
| |
| // Assign a unique index to each active shader type. |
| size_t rgenShaderIdx = 0u; |
| size_t ahitShaderIdx = 0u; |
| size_t chitShaderIdx = 0u; |
| size_t isecShaderIdx = 0u; |
| size_t missShaderIdx = 0u; |
| size_t callShaderIdx = 0u; |
| size_t curShaderIdx = 0u; |
| |
| const std::set<RayTracingShaderType> uniqueStages (begin(params.rtShaders), end(params.rtShaders)); |
| |
| for (const auto& stage : uniqueStages) |
| { |
| switch (stage) |
| { |
| case RayTracingShaderType::RAY_GEN: hasRayGen = true; rgenShaderIdx = curShaderIdx++; break; |
| case RayTracingShaderType::ANY_HIT: hasAnyHit = true; ahitShaderIdx = curShaderIdx++; break; |
| case RayTracingShaderType::CLOSEST_HIT: hasClosestHit = true; chitShaderIdx = curShaderIdx++; break; |
| case RayTracingShaderType::INTERSECTION: hasIntersection = true; isecShaderIdx = curShaderIdx++; break; |
| case RayTracingShaderType::MISS: hasMiss = true; missShaderIdx = curShaderIdx++; break; |
| case RayTracingShaderType::CALLABLE: hasCallable = true; callShaderIdx = curShaderIdx++; break; |
| default: DE_ASSERT(false); break; |
| } |
| } |
| |
| const vk::ShaderBuildOptions buildOptions (programCollection.usedVulkanVersion, vk::SPIRV_VERSION_1_4, 0u, true/* allow SPIR-V 1.4 */); |
| const bool needsRayTraced = (hasAnyHit || hasClosestHit || hasIntersection || hasMiss); |
| |
| for (uint8_t pipelineIdx = 0; pipelineIdx < params.pipelineCount; ++pipelineIdx) |
| { |
| const auto plIdxSz = static_cast<size_t>(pipelineIdx); |
| |
| if (hasRayGen) |
| { |
| const std::string shaderName = "rgen_" + std::to_string(plIdxSz); |
| const auto shaderIdx = getShaderIdx(pipelineIdx, rgenShaderIdx, stageCount); |
| |
| std::ostringstream rgen; |
| rgen |
| << "#version 460\n" |
| << "#extension GL_EXT_ray_tracing : require\n" |
| << (needsRayTraced ? "layout (location=0) rayPayloadEXT vec3 hitValue;\n" : "") |
| << (hasCallable ? "layout (location=0) callableDataEXT float unused;\n" : "") |
| // Ray tracing pipelines will use a separate set for the acceleration structure. |
| << "layout (set=1, binding=0) uniform accelerationStructureEXT topLevelAS;\n" |
| << ssboDecl |
| << uboDecls |
| << constantDecls.at(shaderIdx) |
| << "void main (void) {\n" |
| << outValueDecl |
| << pipelineAdds.at(plIdxSz) |
| << " if (gl_LaunchIDEXT.x == 0u) {\n" |
| << stageStores.at(rgenShaderIdx) |
| << " }\n" |
| << " uint rayFlags = 0;\n" |
| << " uint cullMask = 0xFF;\n" |
| << " float tmin = 0.0;\n" |
| << " float tmax = 10.0;\n" |
| // Rays will be traced towards +Z and geometry should be in the [0, 1] range in both X and Y, possibly at Z=5. |
| // If a hit and a miss shader are used, a second ray will be traced starting at X=1.5, which should result in a miss. |
| << " vec3 origin = vec3(float(gl_LaunchIDEXT.x) + 0.5f, 0.5, 0.0);\n" |
| << " vec3 direct = vec3(0.0, 0.0, 1.0);\n" |
| << (needsRayTraced ? " traceRayEXT(topLevelAS, rayFlags, cullMask, 0, 0, 0, origin, tmin, direct, tmax, 0);\n" : "") |
| << (hasCallable ? " executeCallableEXT(0, 0);\n" : "") |
| << "}\n" |
| ; |
| programCollection.glslSources.add(shaderName) << glu::RaygenSource(rgen.str()) << buildOptions; |
| } |
| |
| if (hasAnyHit) |
| { |
| const std::string shaderName = "ahit_" + std::to_string(plIdxSz); |
| const auto shaderIdx = getShaderIdx(pipelineIdx, ahitShaderIdx, stageCount); |
| |
| // VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR should be used. |
| std::stringstream ahit; |
| ahit |
| << "#version 460\n" |
| << "#extension GL_EXT_ray_tracing : require\n" |
| << "layout (location=0) rayPayloadInEXT vec3 hitValue;\n" |
| << "hitAttributeEXT vec3 attribs;\n" |
| << ssboDecl |
| << uboDecls |
| << constantDecls.at(shaderIdx) |
| << "void main()\n" |
| << "{\n" |
| << outValueDecl |
| << pipelineAdds.at(plIdxSz) |
| << stageStores.at(ahitShaderIdx) |
| << "}\n" |
| ; |
| |
| programCollection.glslSources.add(shaderName) << glu::AnyHitSource(ahit.str()) << buildOptions; |
| } |
| |
| if (hasClosestHit) |
| { |
| const std::string shaderName = "chit_" + std::to_string(plIdxSz); |
| const auto shaderIdx = getShaderIdx(pipelineIdx, chitShaderIdx, stageCount); |
| |
| std::stringstream chit; |
| chit |
| << "#version 460\n" |
| << "#extension GL_EXT_ray_tracing : require\n" |
| << "layout (location=0) rayPayloadInEXT vec3 hitValue;\n" |
| << "hitAttributeEXT vec3 attribs;\n" |
| << ssboDecl |
| << uboDecls |
| << constantDecls.at(shaderIdx) |
| << "void main()\n" |
| << "{\n" |
| << outValueDecl |
| << pipelineAdds.at(plIdxSz) |
| << stageStores.at(chitShaderIdx) |
| << "}\n" |
| ; |
| |
| programCollection.glslSources.add(shaderName) << glu::ClosestHitSource(chit.str()) << buildOptions; |
| } |
| |
| if (hasIntersection) |
| { |
| const std::string shaderName = "isec_" + std::to_string(plIdxSz); |
| const auto shaderIdx = getShaderIdx(pipelineIdx, isecShaderIdx, stageCount); |
| |
| std::stringstream isec; |
| isec |
| << "#version 460\n" |
| << "#extension GL_EXT_ray_tracing : require\n" |
| << "hitAttributeEXT vec3 hitAttribute;\n" |
| << ssboDecl |
| << uboDecls |
| << constantDecls.at(shaderIdx) |
| << "void main()\n" |
| << "{\n" |
| << outValueDecl |
| << pipelineAdds.at(plIdxSz) |
| << stageStores.at(isecShaderIdx) |
| << " hitAttribute = vec3(0.0, 0.0, 0.0);\n" |
| << " reportIntersectionEXT(5.0, 0);\n" |
| << "}\n" |
| ; |
| |
| programCollection.glslSources.add(shaderName) << glu::IntersectionSource(isec.str()) << buildOptions; |
| } |
| |
| if (hasMiss) |
| { |
| const std::string shaderName = "miss_" + std::to_string(plIdxSz); |
| const auto shaderIdx = getShaderIdx(pipelineIdx, missShaderIdx, stageCount); |
| |
| std::stringstream miss; |
| miss |
| << "#version 460\n" |
| << "#extension GL_EXT_ray_tracing : require\n" |
| << "layout (location=0) rayPayloadInEXT vec3 hitValue;\n" |
| << ssboDecl |
| << uboDecls |
| << constantDecls.at(shaderIdx) |
| << "void main()\n" |
| << "{\n" |
| << outValueDecl |
| << pipelineAdds.at(plIdxSz) |
| << stageStores.at(missShaderIdx) |
| << "}\n" |
| ; |
| |
| programCollection.glslSources.add(shaderName) << glu::MissSource(miss.str()) << buildOptions; |
| } |
| |
| if (hasCallable) |
| { |
| const std::string shaderName = "call_" + std::to_string(plIdxSz); |
| const auto shaderIdx = getShaderIdx(pipelineIdx, callShaderIdx, stageCount); |
| |
| std::stringstream call; |
| call |
| << "#version 460\n" |
| << "#extension GL_EXT_ray_tracing : require\n" |
| << "layout (location=0) callableDataInEXT float unused;\n" |
| << ssboDecl |
| << uboDecls |
| << constantDecls.at(shaderIdx) |
| << "void main()\n" |
| << "{\n" |
| << outValueDecl |
| << pipelineAdds.at(plIdxSz) |
| << stageStores.at(callShaderIdx) |
| << "}\n" |
| ; |
| |
| programCollection.glslSources.add(shaderName) << glu::CallableSource(call.str()) << buildOptions; |
| } |
| } |
| } |
| else |
| DE_ASSERT(false); |
| } |
| |
| // Virtual base class that uses the functions above to generate sources and check for support. |
| class SourcesAndSupportFromParamsBase : public vkt::TestCase |
| { |
| public: |
| SourcesAndSupportFromParamsBase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, BaseParamsPtr&& params) |
| : vkt::TestCase(testCtx, name, description) |
| , m_params(std::move(params)) |
| {} |
| virtual ~SourcesAndSupportFromParamsBase (void) {} |
| void initPrograms (vk::SourceCollections& programCollection) const override; |
| void checkSupport (Context& context) const override; |
| |
| protected: |
| const BaseParamsPtr m_params; |
| }; |
| |
| void SourcesAndSupportFromParamsBase::initPrograms (vk::SourceCollections &programCollection) const |
| { |
| generateSources(programCollection, m_params.get()); |
| } |
| |
| void SourcesAndSupportFromParamsBase::checkSupport (Context &context) const |
| { |
| checkShaderModuleIdentifierSupport(context); |
| |
| if (m_params->hasVertexPipelineStage()) |
| context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS); |
| |
| if (m_params->hasFrag()) |
| context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_FRAGMENT_STORES_AND_ATOMICS); |
| |
| if (m_params->hasTess()) |
| context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_TESSELLATION_SHADER); |
| |
| if (m_params->hasGeom()) |
| context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER); |
| |
| if (m_params->hasRayTracing()) |
| { |
| context.requireDeviceFunctionality("VK_KHR_acceleration_structure"); |
| context.requireDeviceFunctionality("VK_KHR_ray_tracing_pipeline"); |
| } |
| } |
| |
| // Check shader module identifiers are constant across different API calls. |
| class ConstantModuleIdentifiersInstance : public vkt::TestInstance |
| { |
| public: |
| enum class APICall { MODULE = 0, CREATE_INFO, BOTH }; |
| |
| struct Params : public BaseParams |
| { |
| APICall apiCall; |
| bool differentDevices; |
| |
| Params (PipelineType pipelineType_, |
| GraphicsShaderVec graphicsShaders_, |
| RTShaderVec rtShaders_, |
| uint8_t pipelineCount_, |
| const tcu::Maybe<uint8_t>& pipelineToRun_, |
| bool useSCs_, |
| bool useCache_, |
| APICall apiCall_, |
| bool differentDevices_) |
| : BaseParams (pipelineType_, graphicsShaders_, rtShaders_, pipelineCount_, pipelineToRun_, useSCs_, useCache_) |
| , apiCall (apiCall_) |
| , differentDevices (differentDevices_) |
| {} |
| |
| virtual ~Params () {} |
| |
| bool needsVkModule (void) const |
| { |
| return (apiCall != APICall::CREATE_INFO); |
| } |
| }; |
| |
| using ParamsPtr = std::unique_ptr<Params>; |
| |
| ConstantModuleIdentifiersInstance (Context& context, const Params* params) |
| : vkt::TestInstance(context) |
| , m_params(params) |
| {} |
| virtual ~ConstantModuleIdentifiersInstance (void) {} |
| tcu::TestStatus runTest (const DeviceInterface& vkd1, const VkDevice device1, |
| const DeviceInterface& vkd2, const VkDevice device2); |
| tcu::TestStatus iterate (void) override; |
| |
| protected: |
| const Params* m_params; |
| }; |
| |
| tcu::TestStatus ConstantModuleIdentifiersInstance::runTest (const DeviceInterface& vkd1, const VkDevice device1, |
| const DeviceInterface& vkd2, const VkDevice device2) |
| { |
| const auto& binaries = m_context.getBinaryCollection(); |
| DE_ASSERT(!binaries.empty()); |
| |
| std::set<ShaderModuleId> uniqueIds; |
| bool pass = true; |
| size_t binaryCount = 0u; |
| |
| for (const auto& binary : binaries) |
| { |
| ++binaryCount; |
| binary.setUsed(); |
| |
| const auto binSize = binary.getSize(); |
| const auto binData = reinterpret_cast<const uint32_t*>(binary.getBinary()); |
| const auto shaderModule1 = (m_params->needsVkModule() ? createShaderModule(vkd1, device1, binary) : Move<VkShaderModule>()); |
| const auto shaderModule2 = (m_params->needsVkModule() ? createShaderModule(vkd2, device2, binary) : Move<VkShaderModule>()); |
| |
| // The first one will be a VkShaderModule if needed. |
| const auto id1 = (m_params->needsVkModule() |
| ? getShaderModuleIdentifier(vkd1, device1, shaderModule1.get()) |
| : getShaderModuleIdentifier(vkd1, device1, makeShaderModuleCreateInfo(binSize, binData))); |
| |
| // The second one will be a VkShaderModule only when comparing shader modules. |
| const auto id2 = ((m_params->apiCall == APICall::MODULE) |
| ? getShaderModuleIdentifier(vkd2, device2, shaderModule2.get()) |
| : getShaderModuleIdentifier(vkd2, device2, makeShaderModuleCreateInfo(binSize, binData))); |
| |
| if (id1 != id2) |
| pass = false; |
| |
| uniqueIds.insert(id1); |
| } |
| |
| if (!pass) |
| return tcu::TestStatus::fail("The same shader module returned different identifiers"); |
| |
| if (uniqueIds.size() != binaryCount) |
| return tcu::TestStatus::fail("Different modules share the same identifier"); |
| |
| return tcu::TestStatus::pass("Pass"); |
| } |
| |
| // Helper to create a new device supporting shader module identifiers. |
| struct DeviceHelper |
| { |
| Move<VkDevice> device; |
| std::unique_ptr<DeviceDriver> vkd; |
| deUint32 queueFamilyIndex; |
| VkQueue queue; |
| std::unique_ptr<SimpleAllocator> allocator; |
| |
| // Forbid copy and assignment. |
| DeviceHelper (const DeviceHelper&) = delete; |
| DeviceHelper& operator= (const DeviceHelper& other) = delete; |
| |
| DeviceHelper (Context& context, bool enableRayTracing = false) |
| { |
| const auto& vkp = context.getPlatformInterface(); |
| const auto& vki = context.getInstanceInterface(); |
| const auto instance = context.getInstance(); |
| const auto physicalDevice = context.getPhysicalDevice(); |
| |
| queueFamilyIndex = context.getUniversalQueueFamilyIndex(); |
| |
| // Get device features (these have to be checked in the test case). |
| VkPhysicalDeviceShaderModuleIdentifierFeaturesEXT shaderIdFeatures = initVulkanStructure(); |
| VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT cacheControlFeatures = initVulkanStructure(&shaderIdFeatures); |
| |
| VkPhysicalDeviceDescriptorIndexingFeaturesEXT descriptorIdxFeatures = initVulkanStructure(&cacheControlFeatures); |
| VkPhysicalDeviceBufferDeviceAddressFeaturesKHR deviceAddressFeatures = initVulkanStructure(&descriptorIdxFeatures); |
| |
| VkPhysicalDeviceFeatures2 deviceFeatures = initVulkanStructure(enableRayTracing |
| ? reinterpret_cast<void*>(&deviceAddressFeatures) |
| : reinterpret_cast<void*>(&cacheControlFeatures)); |
| |
| vki.getPhysicalDeviceFeatures2(physicalDevice, &deviceFeatures); |
| |
| // Make sure robust buffer access is disabled as in the default device. |
| deviceFeatures.features.robustBufferAccess = VK_FALSE; |
| |
| const auto queuePriority = 1.0f; |
| const VkDeviceQueueCreateInfo queueInfo |
| { |
| VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // VkStructureType sType; |
| nullptr, // const void* pNext; |
| 0u, // VkDeviceQueueCreateFlags flags; |
| queueFamilyIndex, // deUint32 queueFamilyIndex; |
| 1u, // deUint32 queueCount; |
| &queuePriority, // const float* pQueuePriorities; |
| }; |
| |
| // Required extensions. Note: many of these require VK_KHR_get_physical_device_properties2, which is an instance extension. |
| std::vector<const char*> requiredExtensions |
| { |
| "VK_EXT_pipeline_creation_cache_control", |
| "VK_EXT_shader_module_identifier", |
| }; |
| |
| if (enableRayTracing) |
| { |
| requiredExtensions.push_back("VK_KHR_maintenance3"); |
| requiredExtensions.push_back("VK_EXT_descriptor_indexing"); |
| requiredExtensions.push_back("VK_KHR_buffer_device_address"); |
| requiredExtensions.push_back("VK_KHR_deferred_host_operations"); |
| requiredExtensions.push_back("VK_KHR_acceleration_structure"); |
| requiredExtensions.push_back("VK_KHR_shader_float_controls"); |
| requiredExtensions.push_back("VK_KHR_spirv_1_4"); |
| requiredExtensions.push_back("VK_KHR_ray_tracing_pipeline"); |
| } |
| |
| const VkDeviceCreateInfo createInfo |
| { |
| VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // VkStructureType sType; |
| deviceFeatures.pNext, // const void* pNext; |
| 0u, // VkDeviceCreateFlags flags; |
| 1u, // deUint32 queueCreateInfoCount; |
| &queueInfo, // const VkDeviceQueueCreateInfo* pQueueCreateInfos; |
| 0u, // deUint32 enabledLayerCount; |
| nullptr, // const char* const* ppEnabledLayerNames; |
| de::sizeU32(requiredExtensions), // deUint32 enabledExtensionCount; |
| de::dataOrNull(requiredExtensions), // const char* const* ppEnabledExtensionNames; |
| &deviceFeatures.features, // const VkPhysicalDeviceFeatures* pEnabledFeatures; |
| }; |
| |
| // Create custom device and related objects |
| device = createCustomDevice(context.getTestContext().getCommandLine().isValidationEnabled(), vkp, instance, vki, physicalDevice, &createInfo); |
| vkd.reset(new DeviceDriver(vkp, instance, device.get())); |
| queue = getDeviceQueue(*vkd, *device, queueFamilyIndex, 0u); |
| allocator.reset(new SimpleAllocator(*vkd, device.get(), getPhysicalDeviceMemoryProperties(vki, physicalDevice))); |
| } |
| }; |
| |
| tcu::TestStatus ConstantModuleIdentifiersInstance::iterate (void) |
| { |
| // The second device may be the one from the context or a new device for the cases that require different devices. |
| const auto& vkd = m_context.getDeviceInterface(); |
| const auto device = m_context.getDevice(); |
| const std::unique_ptr<DeviceHelper> helper (m_params->differentDevices ? new DeviceHelper(m_context) : nullptr); |
| |
| const auto& di1 = vkd; |
| const auto dev1 = device; |
| const auto& di2 = (m_params->differentDevices ? *helper->vkd : vkd); |
| const auto dev2 = (m_params->differentDevices ? helper->device.get() : device); |
| |
| return runTest(di1, dev1, di2, dev2); |
| } |
| |
| class ConstantModuleIdentifiersCase : public SourcesAndSupportFromParamsBase |
| { |
| public: |
| using Params = ConstantModuleIdentifiersInstance::Params; |
| using ParamsPtr = ConstantModuleIdentifiersInstance::ParamsPtr; |
| |
| ConstantModuleIdentifiersCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, ParamsPtr&& params) |
| : SourcesAndSupportFromParamsBase(testCtx, name, description, BaseParamsPtr(static_cast<BaseParams*>(params.release()))) |
| {} |
| virtual ~ConstantModuleIdentifiersCase (void) {} |
| TestInstance* createInstance (Context& context) const override; |
| }; |
| |
| TestInstance* ConstantModuleIdentifiersCase::createInstance (Context &context) const |
| { |
| const auto paramsPtr = dynamic_cast<Params*>(m_params.get()); |
| DE_ASSERT(paramsPtr); |
| |
| return new ConstantModuleIdentifiersInstance(context, paramsPtr); |
| } |
| |
| // Tests that create one or more pipelines using several shaders, obtain the shader ids from one of the pipelines and use them to |
| // attempt creation of a new pipeline to be used normally. |
| class CreateAndUseIdsInstance : public vkt::TestInstance |
| { |
| public: |
| using RndGenPtr = std::shared_ptr<de::Random>; |
| |
| struct Params : public BaseParams |
| { |
| PipelineConstructionType constructionType; |
| bool useRTLibraries; // Use ray tracing libraries? For monolithic builds only. |
| UseModuleCase moduleUseCase; |
| CapturedPropertiesFlags capturedProperties; // For UseModuleCase::ID only. |
| RndGenPtr rnd; |
| |
| Params (PipelineType pipelineType_, |
| GraphicsShaderVec graphicsShaders_, |
| RTShaderVec rtShaders_, |
| uint8_t pipelineCount_, |
| const tcu::Maybe<uint8_t>& pipelineToRun_, |
| bool useSCs_, |
| bool useCache_, |
| PipelineConstructionType constructionType_, |
| bool useRTLibraries_, |
| UseModuleCase moduleUseCase_, |
| CapturedPropertiesFlags capturedProperties_) |
| : BaseParams (pipelineType_, graphicsShaders_, rtShaders_, pipelineCount_, pipelineToRun_, useSCs_, useCache_) |
| , constructionType (constructionType_) |
| , useRTLibraries (useRTLibraries_) |
| , moduleUseCase (moduleUseCase_) |
| , capturedProperties(capturedProperties_) |
| , rnd () |
| { |
| DE_ASSERT(!useRTLibraries || hasRayTracing()); |
| DE_ASSERT(!useRTLibraries || constructionType == PIPELINE_CONSTRUCTION_TYPE_MONOLITHIC); |
| DE_ASSERT(capturedProperties == 0u || moduleUseCase == UseModuleCase::ID); |
| |
| // We will only be capturing properties if using one pipeline that will be run later. |
| DE_ASSERT(capturedProperties == 0u || (pipelineCount == uint8_t{1} && static_cast<bool>(pipelineToRun))); |
| } |
| |
| virtual ~Params () {} |
| |
| // Convenience helper method. |
| de::Random& getRndGen (void) const |
| { |
| return *rnd; |
| } |
| |
| // Copy parameters resetting the random number generator with a new seed. |
| BaseParamsPtr copy (uint32_t newSeed) |
| { |
| std::unique_ptr<Params> clone (new Params(*this)); |
| clone->rnd.reset(new de::Random(newSeed)); |
| return BaseParamsPtr(clone.release()); |
| } |
| }; |
| |
| using ParamsPtr = std::unique_ptr<Params>; |
| |
| CreateAndUseIdsInstance (Context& context, const Params* params) |
| : vkt::TestInstance (context) |
| , m_params (params) |
| {} |
| virtual ~CreateAndUseIdsInstance (void) {} |
| |
| tcu::TestStatus iterate (void) override; |
| |
| protected: |
| const Params* m_params; |
| }; |
| |
| class CreateAndUseIdsCase : public SourcesAndSupportFromParamsBase |
| { |
| public: |
| CreateAndUseIdsCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, BaseParamsPtr&& params) |
| : SourcesAndSupportFromParamsBase (testCtx, name, description, std::move(params)) |
| , m_createAndUseIdsParams (dynamic_cast<const CreateAndUseIdsInstance::Params*>(m_params.get())) |
| { |
| DE_ASSERT(m_createAndUseIdsParams); |
| } |
| virtual ~CreateAndUseIdsCase (void) {} |
| void checkSupport (Context& context) const override; |
| TestInstance* createInstance (Context& context) const override; |
| |
| protected: |
| const CreateAndUseIdsInstance::Params* m_createAndUseIdsParams; |
| }; |
| |
| void CreateAndUseIdsCase::checkSupport (Context &context) const |
| { |
| SourcesAndSupportFromParamsBase::checkSupport(context); |
| |
| checkPipelineLibraryRequirements(context.getInstanceInterface(), context.getPhysicalDevice(), m_createAndUseIdsParams->constructionType); |
| |
| if (m_createAndUseIdsParams->useRTLibraries) |
| context.requireDeviceFunctionality("VK_KHR_pipeline_library"); |
| |
| if (m_createAndUseIdsParams->capturedProperties != 0u) |
| context.requireDeviceFunctionality("VK_KHR_pipeline_executable_properties"); |
| |
| if ((m_params->pipelineType == PipelineType::COMPUTE || m_params->hasRayTracing()) && static_cast<bool>(m_params->pipelineToRun)) { |
| const auto features = context.getPipelineCreationCacheControlFeatures(); |
| if (features.pipelineCreationCacheControl == DE_FALSE) |
| TCU_THROW(NotSupportedError, "Feature 'pipelineCreationCacheControl' is not enabled"); |
| } |
| } |
| |
| TestInstance* CreateAndUseIdsCase::createInstance (Context &context) const |
| { |
| return new CreateAndUseIdsInstance(context, m_createAndUseIdsParams); |
| } |
| |
| using SpecInfoPtr = std::unique_ptr<VkSpecializationInfo>; |
| using SCMapEntryVec = std::vector<VkSpecializationMapEntry>; |
| |
| SpecInfoPtr maybeMakeSpecializationInfo (bool makeIt, const VkSpecializationMapEntry* entry, std::vector<uint32_t>::const_iterator& iter) |
| { |
| if (!makeIt) |
| return nullptr; |
| |
| DE_ASSERT(entry); |
| SpecInfoPtr info (new VkSpecializationInfo); |
| |
| info->mapEntryCount = 1u; |
| info->pMapEntries = entry; |
| info->dataSize = sizeof(uint32_t); |
| info->pData = &(*(iter++)); |
| |
| return info; |
| } |
| |
| VkPipelineRasterizationStateCreateInfo makeRasterizationState (bool rasterizationDisabled) |
| { |
| VkPipelineRasterizationStateCreateInfo state = initVulkanStructure(); |
| state.rasterizerDiscardEnable = (rasterizationDisabled ? VK_TRUE : VK_FALSE); |
| state.lineWidth = 1.0f; |
| return state; |
| } |
| |
| class PipelineStageInfo |
| { |
| protected: |
| VkShaderModule m_module; |
| ShaderModuleId m_moduleId; |
| ShaderStageIdPtr m_moduleIdCreateInfo; |
| SpecInfoPtr m_specInfo; |
| |
| public: |
| PipelineStageInfo () |
| : m_module (DE_NULL) |
| , m_moduleId () |
| , m_moduleIdCreateInfo () |
| , m_specInfo () |
| {} |
| |
| void setModule (const DeviceInterface &vkd, const VkDevice device, const VkShaderModule module, UseModuleCase moduleUse, de::Random& rnd) |
| { |
| m_module = module; |
| |
| m_moduleId = getShaderModuleIdentifier(vkd, device, module); |
| maybeMangleShaderModuleId(m_moduleId, moduleUse, rnd); |
| |
| m_moduleIdCreateInfo = makeShaderStageModuleIdentifierCreateInfo(m_moduleId, moduleUse, &rnd); |
| } |
| |
| void setSpecInfo (SpecInfoPtr&& specInfo) |
| { |
| m_specInfo = std::move(specInfo); |
| } |
| |
| VkShaderModule getModule (void) const |
| { |
| return m_module; |
| } |
| |
| VkShaderModule getUsedModule (UseModuleCase moduleUse) |
| { |
| return retUsedModule(m_module, moduleUse); |
| } |
| |
| const VkPipelineShaderStageModuleIdentifierCreateInfoEXT* getModuleIdCreateInfo (void) const |
| { |
| return m_moduleIdCreateInfo.get(); |
| } |
| |
| const VkSpecializationInfo* getSpecInfo (void) const |
| { |
| return m_specInfo.get(); |
| } |
| |
| // Forbid copy and assignment. This would break the relationship between moduleId and moduleIdCreateInfo. |
| PipelineStageInfo (const PipelineStageInfo&) = delete; |
| PipelineStageInfo& operator=(const PipelineStageInfo&) = delete; |
| }; |
| |
| std::vector<uint32_t> makeComputeSpecConstants (uint32_t stageConstant) |
| { |
| return std::vector<uint32_t>{stageConstant, 1u, 1u, 1u}; |
| } |
| |
| SCMapEntryVec makeComputeSpecMapEntries (void) |
| { |
| const auto kNumEntries = 4u; // Matches the vector above. |
| const auto entrySizeSz = sizeof(uint32_t); |
| const auto entrySize = static_cast<uint32_t>(entrySizeSz); |
| SCMapEntryVec entries; |
| |
| entries.reserve(kNumEntries); |
| for (uint32_t i = 0u; i < kNumEntries; ++i) |
| { |
| const VkSpecializationMapEntry entry = |
| { |
| i, // uint32_t constantID; |
| (entrySize * i), // uint32_t offset; |
| entrySizeSz, // size_t size; |
| }; |
| entries.push_back(entry); |
| } |
| |
| return entries; |
| } |
| |
| SpecInfoPtr makeComputeSpecInfo (const SCMapEntryVec& scEntries, const std::vector<uint32_t>& scData) |
| { |
| SpecInfoPtr scInfo (new VkSpecializationInfo); |
| |
| scInfo->mapEntryCount = de::sizeU32(scEntries); |
| scInfo->pMapEntries = de::dataOrNull(scEntries); |
| scInfo->dataSize = de::dataSize(scData); |
| scInfo->pData = de::dataOrNull(scData); |
| |
| return scInfo; |
| } |
| |
| tcu::TestStatus CreateAndUseIdsInstance::iterate (void) |
| { |
| const auto& vkd = m_context.getDeviceInterface(); |
| const auto device = m_context.getDevice(); |
| auto& alloc = m_context.getDefaultAllocator(); |
| const auto queue = m_context.getUniversalQueue(); |
| const auto queueIndex = m_context.getUniversalQueueFamilyIndex(); |
| const auto& vki = m_context.getInstanceInterface(); |
| const auto physicalDevice = m_context.getPhysicalDevice(); |
| |
| const auto pipelineStages = m_params->getPipelineStageFlags(); |
| const auto shaderStages = m_params->getShaderStageFlags(); |
| const auto captureFlags = getPipelineCreateFlags(m_params->capturedProperties); |
| const bool needsCapture = (captureFlags != 0u); |
| const auto isGraphics = (m_params->pipelineType == PipelineType::GRAPHICS); |
| const auto isCompute = (m_params->pipelineType == PipelineType::COMPUTE); |
| const auto fbFormat = VK_FORMAT_R8G8B8A8_UNORM; |
| const auto tcuFbFormat = mapVkFormat(fbFormat); |
| const auto pixelSize = tcu::getPixelSize(tcuFbFormat); |
| const auto fbExtent = makeExtent3D(1u, 1u, 1u); |
| const tcu::IVec3 iExtent (static_cast<int>(fbExtent.width), static_cast<int>(fbExtent.height), static_cast<int>(fbExtent.depth)); |
| const auto isRT = m_params->hasRayTracing(); |
| const auto hasHit = m_params->hasHit(); |
| const auto hasHitAndMiss = hasHit && m_params->hasMiss(); |
| const auto stagesCount = m_params->stageCountPerPipeline(); |
| const auto pipelineCount32 = static_cast<uint32_t>(m_params->pipelineCount); |
| const auto hasTess = m_params->hasTess(); |
| const auto topology = (hasTess ? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST : VK_PRIMITIVE_TOPOLOGY_POINT_LIST); |
| const auto patchCPs = (hasTess ? 3u : 0u); |
| const auto useSCs = m_params->useSpecializationConstants; |
| const auto shaderConstants = generateShaderConstants(m_params->pipelineType, m_params->pipelineCount, stagesCount); |
| const auto runOnePipeline = static_cast<bool>(m_params->pipelineToRun); |
| const bool reqCacheMiss = expectCacheMiss(m_params->moduleUseCase); |
| const bool qualityWarn = (m_params->useCache && !needsCapture); |
| const tcu::Vec4 clearColor (0.0f, 0.0f, 0.0f, 0.0f); |
| const tcu::Vec4 blueColor (0.0f, 0.0f, 1.0f, 1.0f); // Must match fragment shader above. |
| |
| // Used when capturing pipeline executable properties. |
| PipelineExecutablePropertyVec classicExeProps; |
| PipelineExecutablePropertyVec identifierExeProps; |
| |
| // Command pool and buffer. |
| const auto cmdPool = makeCommandPool(vkd, device, queueIndex); |
| const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY); |
| const auto cmdBuffer = cmdBufferPtr.get(); |
| |
| // Begin command buffer. We may need it below for RT. |
| beginCommandBuffer(vkd, cmdBuffer); |
| |
| // Descriptor set layouts. Typically 1 but ray tracing tests use a separate set for the acceleration structure. |
| std::vector<VkDescriptorSetLayout> setLayouts; |
| |
| DescriptorSetLayoutBuilder setLayoutBuilder; |
| setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, shaderStages); |
| for (uint8_t i = 0; i < m_params->pipelineCount; ++i) |
| setLayoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, shaderStages); |
| const auto mainSetLayout = setLayoutBuilder.build(vkd, device); |
| setLayouts.push_back(mainSetLayout.get()); |
| |
| const auto auxSetLayout = (isRT |
| ? DescriptorSetLayoutBuilder().addSingleBinding(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, shaderStages).build(vkd, device) |
| : Move<VkDescriptorSetLayout>()); |
| if (isRT) |
| setLayouts.push_back(auxSetLayout.get()); |
| |
| // Pipeline layout. |
| const auto pipelineLayout = makePipelineLayout(vkd, device, de::sizeU32(setLayouts), de::dataOrNull(setLayouts)); |
| |
| // Descriptor pool. |
| DescriptorPoolBuilder poolBuilder; |
| poolBuilder.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER); |
| poolBuilder.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, pipelineCount32); |
| if (isRT) |
| poolBuilder.addType(VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR); |
| const auto descriptorPool = poolBuilder.build(vkd, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, de::sizeU32(setLayouts)); |
| |
| // Descriptor buffers. |
| const auto storageBufferSize = static_cast<VkDeviceSize>(sizeof(uint32_t) * stagesCount); |
| const auto storageBufferInfo = makeBufferCreateInfo(storageBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT); |
| BufferWithMemory storageBuffer (vkd, device, alloc, storageBufferInfo, MemoryRequirement::HostVisible); |
| auto& storageBufferAlloc = storageBuffer.getAllocation(); |
| void* storageBufferData = storageBufferAlloc.getHostPtr(); |
| |
| // For the uniform buffers we'll use a single allocation. |
| const auto deviceProperties = getPhysicalDeviceProperties(vki, physicalDevice); |
| const auto minBlock = de::roundUp(static_cast<VkDeviceSize>(sizeof(uint32_t)), deviceProperties.limits.minUniformBufferOffsetAlignment); |
| const auto uniformBufferSize = minBlock * pipelineCount32; |
| const auto uniformBufferInfo = makeBufferCreateInfo(uniformBufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT); |
| BufferWithMemory uniformBuffer (vkd, device, alloc, uniformBufferInfo, MemoryRequirement::HostVisible); |
| auto& uniformBufferAlloc = uniformBuffer.getAllocation(); |
| void* uniformBufferData = uniformBufferAlloc.getHostPtr(); |
| |
| deMemset(storageBufferData, 0, static_cast<size_t>(storageBufferSize)); |
| deMemset(uniformBufferData, 0, static_cast<size_t>(uniformBufferSize)); |
| flushAlloc(vkd, device, storageBufferAlloc); |
| flushAlloc(vkd, device, uniformBufferAlloc); |
| |
| // Acceleration structures if needed. |
| using TLASPtr = de::MovePtr<TopLevelAccelerationStructure>; |
| using BLASPtr = de::SharedPtr<BottomLevelAccelerationStructure>; |
| |
| TLASPtr tlas; |
| BLASPtr blas; |
| |
| if (isRT) |
| { |
| tlas = makeTopLevelAccelerationStructure(); |
| blas = BLASPtr(makeBottomLevelAccelerationStructure().release()); |
| |
| // If we don't want hits we move the geometry way off in the X axis. |
| // If we want hits and misses we launch 2 rays (see raygen shader). |
| const float xOffset = (hasHit ? 0.0f : 100.0f); |
| |
| if (m_params->hasISec()) |
| { |
| // AABB around (0.5, 0.5, 5). |
| const std::vector<tcu::Vec3> geometry |
| { |
| tcu::Vec3(0.0f + xOffset, 0.0f, 4.0f), |
| tcu::Vec3(1.0f + xOffset, 1.0f, 6.0f), |
| }; |
| |
| blas->addGeometry(geometry, false/*isTriangles*/, VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR); |
| } |
| else |
| { |
| // Triangle surrounding (0.5, 0.5, 5). |
| const std::vector<tcu::Vec3> geometry |
| { |
| tcu::Vec3(0.25f + xOffset, 0.25f, 5.0f), |
| tcu::Vec3(0.75f + xOffset, 0.25f, 5.0f), |
| tcu::Vec3(0.5f + xOffset, 0.75f, 5.0f), |
| }; |
| |
| blas->addGeometry(geometry, true/*isTriangles*/, VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_KHR); |
| } |
| blas->createAndBuild(vkd, device, cmdBuffer, alloc); |
| tlas->setInstanceCount(1u); |
| tlas->addInstance(blas, identityMatrix3x4, 0u, 0xFFu, 0u, VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR); |
| |
| tlas->createAndBuild(vkd, device, cmdBuffer, alloc); |
| } |
| |
| // Graphics pipeline data if needed. |
| std::unique_ptr<ImageWithMemory> colorAtt; |
| VkImageSubresourceRange colorSRR; |
| VkImageSubresourceLayers colorSRL; |
| Move<VkImageView> colorAttView; |
| Move<VkRenderPass> renderPass; |
| Move<VkFramebuffer> framebuffer; |
| std::unique_ptr<BufferWithMemory> verifBuffer; |
| std::vector<VkViewport> viewports; |
| std::vector<VkRect2D> scissors; |
| |
| // This is constant for all shader stages. |
| const VkSpecializationMapEntry scMapEntry = |
| { |
| 0u, // uint32_t constantID; |
| 0u, // uint32_t offset; |
| sizeof(uint32_t), // size_t size; |
| }; |
| |
| if (isGraphics) |
| { |
| const VkImageCreateInfo colorAttCreateInfo = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; |
| nullptr, // const void* pNext; |
| 0u, // VkImageCreateFlags flags; |
| VK_IMAGE_TYPE_2D, // VkImageType imageType; |
| fbFormat, // VkFormat format; |
| fbExtent, // VkExtent3D extent; |
| 1u, // uint32_t mipLevels; |
| 1u, // uint32_t arrayLayers; |
| VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; |
| VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; |
| (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT), // VkImageUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 0u, // uint32_t queueFamilyIndexCount; |
| nullptr, // const uint32_t* pQueueFamilyIndices; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; |
| }; |
| |
| colorAtt .reset(new ImageWithMemory(vkd, device, alloc, colorAttCreateInfo, MemoryRequirement::Any)); |
| colorSRR = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u); |
| colorSRL = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u); |
| colorAttView = makeImageView(vkd, device, colorAtt->get(), VK_IMAGE_VIEW_TYPE_2D, fbFormat, colorSRR); |
| renderPass = makeRenderPass(vkd, device, fbFormat); |
| framebuffer = makeFramebuffer(vkd, device, renderPass.get(), colorAttView.get(), fbExtent.width, fbExtent.height); |
| |
| DE_ASSERT(fbExtent.width == 1u && fbExtent.height == 1u && fbExtent.depth == 1u); |
| const auto verifBufferSize = static_cast<VkDeviceSize>(pixelSize); |
| const auto verifBufferInfo = makeBufferCreateInfo(verifBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| verifBuffer.reset(new BufferWithMemory(vkd, device, alloc, verifBufferInfo, MemoryRequirement::HostVisible)); |
| |
| viewports.push_back(makeViewport(fbExtent)); |
| scissors.push_back(makeRect2D(fbExtent)); |
| } |
| |
| // Descriptor sets. |
| const auto mainDescriptorSet = makeDescriptorSet(vkd, device, descriptorPool.get(), mainSetLayout.get()); |
| const auto auxDescriptorSet = (isRT ? makeDescriptorSet(vkd, device, descriptorPool.get(), auxSetLayout.get()) : Move<VkDescriptorSet>()); |
| |
| std::vector<VkDescriptorSet> rawDescriptorSets; |
| rawDescriptorSets.push_back(mainDescriptorSet.get()); |
| if (isRT) |
| rawDescriptorSets.push_back(auxDescriptorSet.get()); |
| |
| // Update descriptor sets. |
| DescriptorSetUpdateBuilder updateBuilder; |
| { |
| const auto storageDescInfo = makeDescriptorBufferInfo(storageBuffer.get(), 0ull, storageBufferSize); |
| updateBuilder.writeSingle(mainDescriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &storageDescInfo); |
| } |
| for (uint32_t uboIdx = 0u; uboIdx < pipelineCount32; ++uboIdx) |
| { |
| const auto uboDescInfo = makeDescriptorBufferInfo(uniformBuffer.get(), minBlock * uboIdx, minBlock); |
| updateBuilder.writeSingle(mainDescriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(uboIdx + 1u), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &uboDescInfo); |
| } |
| if (isRT) |
| { |
| const VkWriteDescriptorSetAccelerationStructureKHR accelDescInfo = |
| { |
| VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR, |
| nullptr, |
| 1u, |
| tlas.get()->getPtr(), |
| }; |
| |
| updateBuilder.writeSingle(auxDescriptorSet.get(), DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, &accelDescInfo); |
| } |
| updateBuilder.update(vkd, device); |
| |
| // Make pipelines. |
| using ModuleVec = std::vector<Move<VkShaderModule>>; |
| using PipelinePtrVec = std::vector<Move<VkPipeline>>; |
| using PipelineVec = std::vector<VkPipeline>; |
| using WrapperVec = std::vector<std::unique_ptr<GraphicsPipelineWrapper>>; |
| using BufferPtr = de::MovePtr<BufferWithMemory>; |
| |
| ModuleVec vertModules; |
| ModuleVec tescModules; |
| ModuleVec teseModules; |
| ModuleVec geomModules; |
| ModuleVec fragModules; |
| |
| ModuleVec compModules; |
| |
| ModuleVec rgenModules; |
| ModuleVec ahitModules; |
| ModuleVec chitModules; |
| ModuleVec isecModules; |
| ModuleVec missModules; |
| ModuleVec callModules; |
| |
| BufferPtr rgenSBT; |
| BufferPtr xhitSBT; |
| BufferPtr missSBT; |
| BufferPtr callSBT; |
| |
| VkStridedDeviceAddressRegionKHR rgenRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0ull, 0ull); |
| VkStridedDeviceAddressRegionKHR xhitRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0ull, 0ull); |
| VkStridedDeviceAddressRegionKHR missRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0ull, 0ull); |
| VkStridedDeviceAddressRegionKHR callRegion = makeStridedDeviceAddressRegionKHR(DE_NULL, 0ull, 0ull); |
| |
| WrapperVec pipelineWrappers; // For graphics pipelines. |
| PipelinePtrVec pipelinePtrs; // For other pipelines. |
| PipelineVec pipelines; |
| Move<VkPipelineCache> pipelineCache; |
| |
| if (m_params->useCache) |
| { |
| const VkPipelineCacheCreateInfo cacheCreateInfo = initVulkanStructure(); |
| pipelineCache = createPipelineCache(vkd, device, &cacheCreateInfo); |
| } |
| |
| const auto& binaries = m_context.getBinaryCollection(); |
| |
| if (isGraphics) |
| { |
| const VkPipelineVertexInputStateCreateInfo vertexInputState = initVulkanStructure(); |
| const VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType sType; |
| nullptr, // const void* pNext; |
| 0u, // VkPipelineInputAssemblyStateCreateFlags flags; |
| topology, // VkPrimitiveTopology topology; |
| VK_FALSE, // VkBool32 primitiveRestartEnable; |
| }; |
| const VkPipelineDepthStencilStateCreateInfo depthStencilState = initVulkanStructure(); |
| VkPipelineMultisampleStateCreateInfo multisampleState = initVulkanStructure(); |
| multisampleState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; |
| VkPipelineColorBlendAttachmentState colorBlendAttachmentState; |
| deMemset(&colorBlendAttachmentState, 0, sizeof(colorBlendAttachmentState)); |
| colorBlendAttachmentState.colorWriteMask = (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT); |
| const VkPipelineColorBlendStateCreateInfo colorBlendState = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType |
| nullptr, // const void* pNext |
| 0u, // VkPipelineColorBlendStateCreateFlags flags |
| VK_FALSE, // VkBool32 logicOpEnable |
| VK_LOGIC_OP_CLEAR, // VkLogicOp logicOp |
| 1u, // deUint32 attachmentCount |
| &colorBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments |
| { 0.0f, 0.0f, 0.0f, 0.0f } // float blendConstants[4] |
| }; |
| |
| auto shaderConstIt = shaderConstants.begin(); |
| |
| // In case we have to run a pipeline. |
| PipelineStageInfo vertToRun; |
| PipelineStageInfo tescToRun; |
| PipelineStageInfo teseToRun; |
| PipelineStageInfo geomToRun; |
| PipelineStageInfo fragToRun; |
| |
| for (uint32_t i = 0; i < pipelineCount32; ++i) |
| { |
| const auto runThis = (runOnePipeline && static_cast<uint32_t>(m_params->pipelineToRun.get()) == i); |
| const auto suffix = "_" + std::to_string(i); |
| const auto vertName = "vert" + suffix; |
| const auto tescName = "tesc" + suffix; |
| const auto teseName = "tese" + suffix; |
| const auto geomName = "geom" + suffix; |
| const auto fragName = "frag" + suffix; |
| |
| pipelineWrappers.emplace_back(new GraphicsPipelineWrapper(vkd, device, m_params->constructionType, captureFlags)); |
| auto& wrapper = *pipelineWrappers.back(); |
| |
| VkShaderModule vertModule = DE_NULL; |
| VkShaderModule tescModule = DE_NULL; |
| VkShaderModule teseModule = DE_NULL; |
| VkShaderModule geomModule = DE_NULL; |
| VkShaderModule fragModule = DE_NULL; |
| |
| SpecInfoPtr vertSpecInfo; |
| SpecInfoPtr tescSpecInfo; |
| SpecInfoPtr teseSpecInfo; |
| SpecInfoPtr geomSpecInfo; |
| SpecInfoPtr fragSpecInfo; |
| |
| vertModules .push_back(createShaderModule(vkd, device, binaries.get(vertName))); |
| vertModule = vertModules.back().get(); |
| vertSpecInfo = maybeMakeSpecializationInfo(useSCs, &scMapEntry, shaderConstIt); |
| |
| if (binaries.contains(tescName)) |
| { |
| tescModules .push_back(createShaderModule(vkd, device, binaries.get(tescName))); |
| tescModule = tescModules.back().get(); |
| tescSpecInfo = maybeMakeSpecializationInfo(useSCs, &scMapEntry, shaderConstIt); |
| } |
| |
| if (binaries.contains(teseName)) |
| { |
| teseModules .push_back(createShaderModule(vkd, device, binaries.get(teseName))); |
| teseModule = teseModules.back().get(); |
| teseSpecInfo = maybeMakeSpecializationInfo(useSCs, &scMapEntry, shaderConstIt); |
| } |
| |
| if (binaries.contains(geomName)) |
| { |
| geomModules .push_back(createShaderModule(vkd, device, binaries.get(geomName))); |
| geomModule = geomModules.back().get(); |
| geomSpecInfo = maybeMakeSpecializationInfo(useSCs, &scMapEntry, shaderConstIt); |
| } |
| |
| if (binaries.contains(fragName)) |
| { |
| fragModules .push_back(createShaderModule(vkd, device, binaries.get(fragName))); |
| fragModule = fragModules.back().get(); |
| fragSpecInfo = maybeMakeSpecializationInfo(useSCs, &scMapEntry, shaderConstIt); |
| } |
| |
| const auto rasterizationState = makeRasterizationState(fragModule == DE_NULL); |
| |
| wrapper .setDefaultPatchControlPoints(patchCPs) |
| .setupVertexInputStete(&vertexInputState, &inputAssemblyState, pipelineCache.get()) |
| .setupPreRasterizationShaderState2( |
| viewports, |
| scissors, |
| pipelineLayout.get(), |
| renderPass.get(), |
| 0u, |
| vertModule, |
| &rasterizationState, |
| tescModule, |
| teseModule, |
| geomModule, |
| vertSpecInfo.get(), |
| tescSpecInfo.get(), |
| teseSpecInfo.get(), |
| geomSpecInfo.get(), |
| nullptr, |
| PipelineRenderingCreateInfoWrapper(), |
| pipelineCache.get()) |
| .setupFragmentShaderState( |
| pipelineLayout.get(), |
| renderPass.get(), |
| 0u, |
| fragModule, |
| &depthStencilState, |
| &multisampleState, |
| fragSpecInfo.get(), |
| pipelineCache.get()) |
| .setupFragmentOutputState(*renderPass, 0u, &colorBlendState, &multisampleState, pipelineCache.get()) |
| .setMonolithicPipelineLayout(pipelineLayout.get()) |
| .buildPipeline(pipelineCache.get()); |
| |
| pipelines.push_back(wrapper.getPipeline()); |
| |
| // Capture properties if needed. |
| if (needsCapture) |
| classicExeProps = getPipelineExecutableProperties(vkd, device, pipelines.back(), m_params->capturedProperties); |
| |
| if (runThis) |
| { |
| vertToRun.setModule(vkd, device, vertModule, m_params->moduleUseCase, m_params->getRndGen()); |
| vertToRun.setSpecInfo(std::move(vertSpecInfo)); |
| |
| if (tescModule != DE_NULL) |
| { |
| tescToRun.setModule(vkd, device, tescModule, m_params->moduleUseCase, m_params->getRndGen()); |
| tescToRun.setSpecInfo(std::move(tescSpecInfo)); |
| } |
| |
| if (teseModule != DE_NULL) |
| { |
| teseToRun.setModule(vkd, device, teseModule, m_params->moduleUseCase, m_params->getRndGen()); |
| teseToRun.setSpecInfo(std::move(teseSpecInfo)); |
| } |
| |
| if (geomModule != DE_NULL) |
| { |
| geomToRun.setModule(vkd, device, geomModule, m_params->moduleUseCase, m_params->getRndGen()); |
| geomToRun.setSpecInfo(std::move(geomSpecInfo)); |
| } |
| |
| if (fragModule != DE_NULL) |
| { |
| fragToRun.setModule(vkd, device, fragModule, m_params->moduleUseCase, m_params->getRndGen()); |
| fragToRun.setSpecInfo(std::move(fragSpecInfo)); |
| } |
| } |
| } |
| |
| if (runOnePipeline) |
| { |
| // Append the pipeline to run at the end of the vector. |
| pipelineWrappers.emplace_back(new GraphicsPipelineWrapper(vkd, device, m_params->constructionType, captureFlags)); |
| auto& wrapper = *pipelineWrappers.back(); |
| |
| const auto fragModule = fragToRun.getModule(); |
| const auto rasterizationState = makeRasterizationState(fragModule == DE_NULL); |
| |
| try |
| { |
| wrapper .setDefaultPatchControlPoints(patchCPs) |
| .setupVertexInputStete(&vertexInputState, &inputAssemblyState, pipelineCache.get()) |
| .setupPreRasterizationShaderState3( |
| viewports, |
| scissors, |
| pipelineLayout.get(), |
| renderPass.get(), |
| 0u, |
| vertToRun.getUsedModule(m_params->moduleUseCase), |
| PipelineShaderStageModuleIdentifierCreateInfoWrapper(vertToRun.getModuleIdCreateInfo()), |
| &rasterizationState, |
| tescToRun.getUsedModule(m_params->moduleUseCase), |
| PipelineShaderStageModuleIdentifierCreateInfoWrapper(tescToRun.getModuleIdCreateInfo()), |
| teseToRun.getUsedModule(m_params->moduleUseCase), |
| PipelineShaderStageModuleIdentifierCreateInfoWrapper(teseToRun.getModuleIdCreateInfo()), |
| geomToRun.getUsedModule(m_params->moduleUseCase), |
| PipelineShaderStageModuleIdentifierCreateInfoWrapper(geomToRun.getModuleIdCreateInfo()), |
| vertToRun.getSpecInfo(), |
| tescToRun.getSpecInfo(), |
| teseToRun.getSpecInfo(), |
| geomToRun.getSpecInfo(), |
| nullptr, |
| PipelineRenderingCreateInfoWrapper(), |
| pipelineCache.get()) |
| .setupFragmentShaderState2( |
| pipelineLayout.get(), |
| renderPass.get(), |
| 0u, |
| fragToRun.getUsedModule(m_params->moduleUseCase), |
| fragToRun.getModuleIdCreateInfo(), |
| &depthStencilState, |
| &multisampleState, |
| fragToRun.getSpecInfo(), |
| pipelineCache.get()) |
| .setupFragmentOutputState(*renderPass, 0u, &colorBlendState, &multisampleState, pipelineCache.get()) |
| .setMonolithicPipelineLayout(pipelineLayout.get()) |
| .buildPipeline(pipelineCache.get()); |
| |
| if (reqCacheMiss) |
| TCU_FAIL("Cache miss expected"); |
| } |
| catch (const PipelineCompileRequiredError& err) |
| { |
| if (reqCacheMiss) |
| return tcu::TestStatus::pass("Pass"); |
| |
| if (qualityWarn) |
| return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "VK_PIPELINE_COMPILE_REQUIRED despite passing a pipeline cache"); |
| return tcu::TestStatus::pass("VK_PIPELINE_COMPILE_REQUIRED"); // ;_; |
| } |
| |
| pipelines.push_back(wrapper.getPipeline()); |
| |
| if (needsCapture) |
| identifierExeProps = getPipelineExecutableProperties(vkd, device, pipelines.back(), m_params->capturedProperties); |
| } |
| } |
| else if (isCompute) |
| { |
| const auto invalidPipelineIdx = std::numeric_limits<uint32_t>::max(); |
| auto idxToRun = invalidPipelineIdx; |
| |
| for (uint32_t i = 0; i < pipelineCount32; ++i) |
| { |
| const auto runThis = (runOnePipeline && static_cast<uint32_t>(m_params->pipelineToRun.get()) == i); |
| const auto suffix = "_" + std::to_string(i); |
| const auto compName = "comp" + suffix; |
| |
| const auto scData = (useSCs ? makeComputeSpecConstants(shaderConstants.at(i)) : std::vector<uint32_t>()); |
| const auto scEntries = (useSCs ? makeComputeSpecMapEntries() : std::vector<VkSpecializationMapEntry>()); |
| const auto scInfo = (useSCs ? makeComputeSpecInfo(scEntries, scData) : nullptr); |
| |
| compModules.push_back(createShaderModule(vkd, device, binaries.get(compName))); |
| pipelinePtrs.push_back(makeComputePipeline(vkd, device, pipelineLayout.get(), captureFlags, compModules.back().get(), 0u, scInfo.get(), pipelineCache.get())); |
| pipelines.push_back(pipelinePtrs.back().get()); |
| |
| if (runThis) |
| idxToRun = i; |
| |
| if (needsCapture) |
| classicExeProps = getPipelineExecutableProperties(vkd, device, pipelines.back(), m_params->capturedProperties); |
| } |
| |
| if (idxToRun != invalidPipelineIdx) |
| { |
| const auto compModule = compModules.at(idxToRun).get(); |
| auto moduleId = getShaderModuleIdentifier(vkd, device, compModule); |
| |
| maybeMangleShaderModuleId(moduleId, m_params->moduleUseCase, m_params->getRndGen()); |
| |
| const auto modInfo = makeShaderStageModuleIdentifierCreateInfo(moduleId, m_params->moduleUseCase, &(m_params->getRndGen())); |
| const auto scData = (useSCs ? makeComputeSpecConstants(shaderConstants.at(idxToRun)) : std::vector<uint32_t>()); |
| const auto scEntries = (useSCs ? makeComputeSpecMapEntries() : std::vector<VkSpecializationMapEntry>()); |
| const auto scInfo = (useSCs ? makeComputeSpecInfo(scEntries, scData) : nullptr); |
| |
| // Append the pipeline to run at the end of the vector. |
| { |
| const auto pipelineFlags = (VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT | captureFlags); |
| |
| const VkPipelineShaderStageCreateInfo pipelineShaderStageParams = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType; |
| modInfo.get(), // const void* pNext; |
| 0u, // VkPipelineShaderStageCreateFlags flags; |
| VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagBits stage; |
| retUsedModule(compModule, m_params->moduleUseCase), // VkShaderModule module; |
| "main", // const char* pName; |
| scInfo.get(), // const VkSpecializationInfo* pSpecializationInfo; |
| }; |
| |
| const VkComputePipelineCreateInfo pipelineCreateInfo = |
| { |
| VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // VkStructureType sType; |
| nullptr, // const void* pNext; |
| pipelineFlags, // VkPipelineCreateFlags flags; |
| pipelineShaderStageParams, // VkPipelineShaderStageCreateInfo stage; |
| pipelineLayout.get(), // VkPipelineLayout layout; |
| DE_NULL, // VkPipeline basePipelineHandle; |
| 0, // deInt32 basePipelineIndex; |
| }; |
| |
| VkPipeline pipeline; |
| VkResult creationResult = vkd.createComputePipelines(device, pipelineCache.get(), 1u, &pipelineCreateInfo, nullptr, &pipeline); |
| |
| if (creationResult == VK_PIPELINE_COMPILE_REQUIRED) |
| { |
| if (reqCacheMiss) |
| return tcu::TestStatus::pass("Pass"); |
| |
| if (qualityWarn) |
| return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "VK_PIPELINE_COMPILE_REQUIRED despite passing a pipeline cache"); |
| return tcu::TestStatus::pass("VK_PIPELINE_COMPILE_REQUIRED"); // ;_; |
| } |
| VK_CHECK(creationResult); |
| |
| if (reqCacheMiss) |
| TCU_FAIL("Cache miss expected"); |
| |
| Move<VkPipeline> pipelinePtr(check<VkPipeline>(pipeline), Deleter<VkPipeline>(vkd, device, nullptr)); |
| pipelinePtrs.emplace_back(pipelinePtr); |
| pipelines.push_back(pipeline); |
| |
| if (needsCapture) |
| identifierExeProps = getPipelineExecutableProperties(vkd, device, pipelines.back(), m_params->capturedProperties); |
| } |
| } |
| } |
| else if (isRT) |
| { |
| // Get some ray tracing properties and constants. |
| const auto rayTracingPropertiesKHR = makeRayTracingProperties(vki, physicalDevice); |
| const auto shaderGroupHandleSize = rayTracingPropertiesKHR->getShaderGroupHandleSize(); |
| const auto shaderGroupBaseAlignment = rayTracingPropertiesKHR->getShaderGroupBaseAlignment(); |
| const auto vec3Size = static_cast<uint32_t>(sizeof(tcu::Vec3)); |
| |
| // Empty pipeline vector, needed in a couple places. |
| const std::vector<VkPipeline> emptyPipelinesVec; |
| |
| auto shaderConstIt = shaderConstants.begin(); |
| |
| // In case we have to run a pipeline. |
| PipelineStageInfo rgenToRun; |
| PipelineStageInfo chitToRun; |
| PipelineStageInfo ahitToRun; |
| PipelineStageInfo isecToRun; |
| PipelineStageInfo missToRun; |
| PipelineStageInfo callToRun; |
| |
| for (uint32_t i = 0; i < pipelineCount32; ++i) |
| { |
| const auto runThis = (runOnePipeline && static_cast<uint32_t>(m_params->pipelineToRun.get()) == i); |
| const auto suffix = "_" + std::to_string(i); |
| const auto rgenName = "rgen" + suffix; |
| const auto chitName = "chit" + suffix; |
| const auto ahitName = "ahit" + suffix; |
| const auto isecName = "isec" + suffix; |
| const auto missName = "miss" + suffix; |
| const auto callName = "call" + suffix; |
| |
| VkShaderModule rgenModule = DE_NULL; |
| VkShaderModule chitModule = DE_NULL; |
| VkShaderModule ahitModule = DE_NULL; |
| VkShaderModule isecModule = DE_NULL; |
| VkShaderModule missModule = DE_NULL; |
| VkShaderModule callModule = DE_NULL; |
| |
| SpecInfoPtr rgenSpecInfo; |
| SpecInfoPtr chitSpecInfo; |
| SpecInfoPtr ahitSpecInfo; |
| SpecInfoPtr isecSpecInfo; |
| SpecInfoPtr missSpecInfo; |
| SpecInfoPtr callSpecInfo; |
| |
| uint32_t groupCount = 1u; |
| const uint32_t rgenGroup = 0u; |
| tcu::Maybe<uint32_t> xhitGroup; |
| tcu::Maybe<uint32_t> missGroup; |
| tcu::Maybe<uint32_t> callGroup; |
| |
| rgenModules .push_back(createShaderModule(vkd, device, binaries.get(rgenName))); |
| rgenModule = rgenModules.back().get(); |
| rgenSpecInfo = maybeMakeSpecializationInfo(useSCs, &scMapEntry, shaderConstIt); |
| |
| if (binaries.contains(chitName)) |
| { |
| chitModules .push_back(createShaderModule(vkd, device, binaries.get(chitName))); |
| chitModule = chitModules.back().get(); |
| chitSpecInfo = maybeMakeSpecializationInfo(useSCs, &scMapEntry, shaderConstIt); |
| xhitGroup = (static_cast<bool>(xhitGroup) ? xhitGroup : tcu::just(groupCount++)); |
| } |
| |
| if (binaries.contains(ahitName)) |
| { |
| ahitModules .push_back(createShaderModule(vkd, device, binaries.get(ahitName))); |
| ahitModule = ahitModules.back().get(); |
| ahitSpecInfo = maybeMakeSpecializationInfo(useSCs, &scMapEntry, shaderConstIt); |
| xhitGroup = (static_cast<bool>(xhitGroup) ? xhitGroup : tcu::just(groupCount++)); |
| } |
| |
| if (binaries.contains(isecName)) |
| { |
| isecModules .push_back(createShaderModule(vkd, device, binaries.get(isecName))); |
| isecModule = isecModules.back().get(); |
| isecSpecInfo = maybeMakeSpecializationInfo(useSCs, &scMapEntry, shaderConstIt); |
| xhitGroup = (static_cast<bool>(xhitGroup) ? xhitGroup : tcu::just(groupCount++)); |
| } |
| |
| if (binaries.contains(missName)) |
| { |
| missModules .push_back(createShaderModule(vkd, device, binaries.get(missName))); |
| missModule = missModules.back().get(); |
| missSpecInfo = maybeMakeSpecializationInfo(useSCs, &scMapEntry, shaderConstIt); |
| missGroup = tcu::just(groupCount++); |
| } |
| |
| if (binaries.contains(callName)) |
| { |
| callModules .push_back(createShaderModule(vkd, device, binaries.get(callName))); |
| callModule = callModules.back().get(); |
| callSpecInfo = maybeMakeSpecializationInfo(useSCs, &scMapEntry, shaderConstIt); |
| callGroup = tcu::just(groupCount++); |
| } |
| |
| { |
| const auto rayTracingPipeline = de::newMovePtr<RayTracingPipeline>(); |
| |
| // These have to match the shaders. |
| rayTracingPipeline->setMaxPayloadSize(vec3Size); |
| rayTracingPipeline->setMaxAttributeSize(vec3Size); |
| |
| // Make it a library if we are using libraries. |
| rayTracingPipeline->setCreateFlags(captureFlags | (m_params->useRTLibraries ? VK_PIPELINE_CREATE_LIBRARY_BIT_KHR : 0)); |
| |
| rayTracingPipeline->addShader(VK_SHADER_STAGE_RAYGEN_BIT_KHR, rgenModule, rgenGroup, rgenSpecInfo.get()); |
| |
| if (chitModule != DE_NULL) |
| rayTracingPipeline->addShader(VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, chitModule, xhitGroup.get(), chitSpecInfo.get()); |
| |
| if (ahitModule != DE_NULL) |
| rayTracingPipeline->addShader(VK_SHADER_STAGE_ANY_HIT_BIT_KHR, ahitModule, xhitGroup.get(), ahitSpecInfo.get()); |
| |
| if (isecModule != DE_NULL) |
| rayTracingPipeline->addShader(VK_SHADER_STAGE_INTERSECTION_BIT_KHR, isecModule, xhitGroup.get(), isecSpecInfo.get()); |
| |
| if (missModule != DE_NULL) |
| rayTracingPipeline->addShader(VK_SHADER_STAGE_MISS_BIT_KHR, missModule, missGroup.get(), missSpecInfo.get()); |
| |
| if (callModule != DE_NULL) |
| rayTracingPipeline->addShader(VK_SHADER_STAGE_CALLABLE_BIT_KHR, callModule, callGroup.get(), callSpecInfo.get()); |
| |
| pipelinePtrs.emplace_back(rayTracingPipeline->createPipeline(vkd, device, pipelineLayout.get(), emptyPipelinesVec, pipelineCache.get())); |
| pipelines.push_back(pipelinePtrs.back().get()); |
| |
| // We may need to link the pipeline just like we'll do with shader module identifiers below. |
| if (m_params->useRTLibraries) |
| { |
| const auto linkedPipeline = de::newMovePtr<RayTracingPipeline>(); |
| |
| linkedPipeline->setMaxPayloadSize(vec3Size); |
| linkedPipeline->setMaxAttributeSize(vec3Size); |
| linkedPipeline->setCreateFlags(captureFlags); |
| |
| const std::vector<VkPipeline> rawPipelines(1u, pipelines.back()); |
| pipelinePtrs.emplace_back(linkedPipeline->createPipeline(vkd, device, pipelineLayout.get(), rawPipelines, pipelineCache.get())); |
| pipelines.push_back(pipelinePtrs.back().get()); |
| } |
| |
| if (needsCapture) |
| classicExeProps = getPipelineExecutableProperties(vkd, device, pipelines.back(), m_params->capturedProperties); |
| } |
| |
| if (runThis) |
| { |
| rgenToRun.setModule(vkd, device, rgenModule, m_params->moduleUseCase, m_params->getRndGen()); |
| rgenToRun.setSpecInfo(std::move(rgenSpecInfo)); |
| |
| if (chitModule != DE_NULL) |
| { |
| chitToRun.setModule(vkd, device, chitModule, m_params->moduleUseCase, m_params->getRndGen()); |
| chitToRun.setSpecInfo(std::move(chitSpecInfo)); |
| } |
| |
| if (ahitModule != DE_NULL) |
| { |
| ahitToRun.setModule(vkd, device, ahitModule, m_params->moduleUseCase, m_params->getRndGen()); |
| ahitToRun.setSpecInfo(std::move(ahitSpecInfo)); |
| } |
| |
| if (isecModule != DE_NULL) |
| { |
| isecToRun.setModule(vkd, device, isecModule, m_params->moduleUseCase, m_params->getRndGen()); |
| isecToRun.setSpecInfo(std::move(isecSpecInfo)); |
| } |
| |
| if (missModule != DE_NULL) |
| { |
| missToRun.setModule(vkd, device, missModule, m_params->moduleUseCase, m_params->getRndGen()); |
| missToRun.setSpecInfo(std::move(missSpecInfo)); |
| } |
| |
| if (callModule != DE_NULL) |
| { |
| callToRun.setModule(vkd, device, callModule, m_params->moduleUseCase, m_params->getRndGen()); |
| callToRun.setSpecInfo(std::move(callSpecInfo)); |
| } |
| } |
| } |
| |
| if (runOnePipeline) |
| { |
| uint32_t groupCount = 1u; |
| const uint32_t rgenGroup = 0u; |
| tcu::Maybe<uint32_t> xhitGroup; |
| tcu::Maybe<uint32_t> missGroup; |
| tcu::Maybe<uint32_t> callGroup; |
| |
| const auto rgenModule = rgenToRun.getModule(); DE_UNREF(rgenModule); |
| const auto chitModule = chitToRun.getModule(); |
| const auto ahitModule = ahitToRun.getModule(); |
| const auto isecModule = isecToRun.getModule(); |
| const auto missModule = missToRun.getModule(); |
| const auto callModule = callToRun.getModule(); |
| |
| if (chitModule != DE_NULL) |
| xhitGroup = (xhitGroup ? xhitGroup : tcu::just(groupCount++)); |
| if (ahitModule != DE_NULL) |
| xhitGroup = (xhitGroup ? xhitGroup : tcu::just(groupCount++)); |
| if (isecModule != DE_NULL) |
| xhitGroup = (xhitGroup ? xhitGroup : tcu::just(groupCount++)); |
| |
| if (missModule != DE_NULL) |
| missGroup = tcu::just(groupCount++); |
| |
| if (callModule != DE_NULL) |
| callGroup = tcu::just(groupCount++); |
| |
| const auto shaderOwningPipelinePtr = makeVkSharedPtr(de::newMovePtr<RayTracingPipeline>()); |
| const auto shaderOwningPipeline = shaderOwningPipelinePtr->get(); |
| |
| de::SharedPtr<de::MovePtr<RayTracingPipeline>> auxiliaryPipelinePtr; |
| RayTracingPipeline* auxiliaryPipeline = nullptr; |
| |
| if (m_params->useRTLibraries) |
| { |
| // The shader-owning pipeline will be a library and auxiliaryPipeline will be the bound pipeline helper. |
| auxiliaryPipelinePtr = makeVkSharedPtr(de::newMovePtr<RayTracingPipeline>()); |
| auxiliaryPipeline = auxiliaryPipelinePtr->get(); |
| } |
| |
| // The bound pipeline is the shader-owning pipeline if not using libraries, or the auxiliary pipeline otherwise. |
| RayTracingPipeline* boundPipeline = (m_params->useRTLibraries ? auxiliaryPipeline : shaderOwningPipeline); |
| |
| shaderOwningPipeline->setMaxPayloadSize(vec3Size); |
| shaderOwningPipeline->setMaxAttributeSize(vec3Size); |
| { |
| VkPipelineCreateFlags creationFlags = (VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT | captureFlags); |
| if (m_params->useRTLibraries) |
| creationFlags |= VK_PIPELINE_CREATE_LIBRARY_BIT_KHR; |
| shaderOwningPipeline->setCreateFlags(creationFlags); |
| } |
| |
| shaderOwningPipeline->addShader( |
| VK_SHADER_STAGE_RAYGEN_BIT_KHR, |
| rgenToRun.getUsedModule(m_params->moduleUseCase), |
| rgenGroup, |
| rgenToRun.getSpecInfo(), 0, |
| rgenToRun.getModuleIdCreateInfo()); |
| |
| if (chitModule != DE_NULL) |
| { |
| shaderOwningPipeline->addShader( |
| VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, |
| chitToRun.getUsedModule(m_params->moduleUseCase), |
| xhitGroup.get(), |
| chitToRun.getSpecInfo(), 0, |
| chitToRun.getModuleIdCreateInfo()); |
| } |
| |
| if (ahitModule != DE_NULL) |
| { |
| shaderOwningPipeline->addShader( |
| VK_SHADER_STAGE_ANY_HIT_BIT_KHR, |
| ahitToRun.getUsedModule(m_params->moduleUseCase), |
| xhitGroup.get(), |
| ahitToRun.getSpecInfo(), 0, |
| ahitToRun.getModuleIdCreateInfo()); |
| } |
| |
| if (isecModule != DE_NULL) |
| { |
| shaderOwningPipeline->addShader( |
| VK_SHADER_STAGE_INTERSECTION_BIT_KHR, |
| isecToRun.getUsedModule(m_params->moduleUseCase), |
| xhitGroup.get(), |
| isecToRun.getSpecInfo(), 0, |
| isecToRun.getModuleIdCreateInfo()); |
| } |
| |
| if (missModule != DE_NULL) |
| { |
| shaderOwningPipeline->addShader( |
| VK_SHADER_STAGE_MISS_BIT_KHR, |
| missToRun.getUsedModule(m_params->moduleUseCase), |
| missGroup.get(), |
| missToRun.getSpecInfo(), 0, |
| missToRun.getModuleIdCreateInfo()); |
| } |
| |
| if (callModule != DE_NULL) |
| { |
| shaderOwningPipeline->addShader( |
| VK_SHADER_STAGE_CALLABLE_BIT_KHR, |
| callToRun.getUsedModule(m_params->moduleUseCase), |
| callGroup.get(), |
| callToRun.getSpecInfo(), 0, |
| callToRun.getModuleIdCreateInfo()); |
| } |
| |
| // Append the pipeline, SBTs and regions to use at the end of their vectors. |
| try |
| { |
| pipelinePtrs.emplace_back(shaderOwningPipeline->createPipeline(vkd, device, pipelineLayout.get(), emptyPipelinesVec, pipelineCache.get())); |
| pipelines.push_back(pipelinePtrs.back().get()); |
| } |
| catch (const RayTracingPipeline::CompileRequiredError& err) |
| { |
| if (reqCacheMiss) |
| return tcu::TestStatus::pass("Pass"); |
| |
| if (qualityWarn) |
| return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "VK_PIPELINE_COMPILE_REQUIRED despite passing a pipeline cache"); |
| return tcu::TestStatus::pass("VK_PIPELINE_COMPILE_REQUIRED"); // ;_; |
| } |
| |
| if (m_params->useRTLibraries) |
| { |
| // Create a new pipeline using the library created above, and use it as the active pipeline. |
| auxiliaryPipeline->setMaxPayloadSize(vec3Size); |
| auxiliaryPipeline->setMaxAttributeSize(vec3Size); |
| auxiliaryPipeline->setCreateFlags(VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT | captureFlags); |
| |
| try |
| { |
| const std::vector<VkPipeline> rawPipelines(1u, pipelines.back()); |
| pipelinePtrs.emplace_back(auxiliaryPipeline->createPipeline(vkd, device, pipelineLayout.get(), rawPipelines, pipelineCache.get())); |
| pipelines.push_back(pipelinePtrs.back().get()); |
| |
| if (reqCacheMiss) |
| TCU_FAIL("Cache miss expected"); |
| } |
| catch (const RayTracingPipeline::CompileRequiredError& err) |
| { |
| if (reqCacheMiss) |
| return tcu::TestStatus::pass("Pass"); |
| |
| if (qualityWarn) |
| return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "VK_PIPELINE_COMPILE_REQUIRED on library use despite passing a pipeline cache"); |
| return tcu::TestStatus::pass("VK_PIPELINE_COMPILE_REQUIRED on library use"); // ;_; |
| } |
| } |
| else if (reqCacheMiss) |
| TCU_FAIL("Cache miss expected"); |
| |
| if (needsCapture) |
| identifierExeProps = getPipelineExecutableProperties(vkd, device, pipelines.back(), m_params->capturedProperties); |
| |
| const auto pipeline = pipelines.back(); |
| |
| rgenSBT = boundPipeline->createShaderBindingTable(vkd, device, pipeline, alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, rgenGroup, 1u); |
| rgenRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, rgenSBT->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize); |
| |
| if (xhitGroup) |
| { |
| xhitSBT = boundPipeline->createShaderBindingTable(vkd, device, pipeline, alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, xhitGroup.get(), 1u); |
| xhitRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, xhitSBT->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize); |
| } |
| |
| if (missGroup) |
| { |
| missSBT = boundPipeline->createShaderBindingTable(vkd, device, pipeline, alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, missGroup.get(), 1u); |
| missRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, missSBT->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize); |
| } |
| |
| if (callGroup) |
| { |
| callSBT = boundPipeline->createShaderBindingTable(vkd, device, pipeline, alloc, shaderGroupHandleSize, shaderGroupBaseAlignment, callGroup.get(), 1u); |
| callRegion = makeStridedDeviceAddressRegionKHR(getBufferDeviceAddress(vkd, device, callSBT->get(), 0), shaderGroupHandleSize, shaderGroupHandleSize); |
| } |
| } |
| } |
| else |
| { |
| DE_ASSERT(false); |
| } |
| |
| // Early exit if we don't need to run any pipeline. |
| if (!runOnePipeline) |
| return tcu::TestStatus::pass("Pass (not using any pipeline)"); |
| |
| // Compare executable properties if captured. |
| if (needsCapture) |
| { |
| using PipelineExecutablePropertySet = std::set<PipelineExecutableProperty>; |
| |
| const PipelineExecutablePropertySet classicProps (begin(classicExeProps), end(classicExeProps)); |
| const PipelineExecutablePropertySet identifierProps (begin(identifierExeProps), end(identifierExeProps)); |
| |
| if (classicProps != identifierProps) |
| { |
| auto& log = m_context.getTestContext().getLog(); |
| |
| log << tcu::TestLog::Message << "Properties without identifiers: " << classicExeProps << tcu::TestLog::EndMessage; |
| log << tcu::TestLog::Message << "Properties with identifiers: " << identifierExeProps << tcu::TestLog::EndMessage; |
| |
| TCU_FAIL("Pipeline executable properties differ (check log for details)"); |
| } |
| } |
| |
| if (isGraphics) |
| { |
| const auto bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
| const auto vertexCount = (m_params->hasTess() ? 3u : 1u); |
| |
| beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0u), clearColor); |
| vkd.cmdBindDescriptorSets(cmdBuffer, bindPoint, pipelineLayout.get(), 0u, de::sizeU32(rawDescriptorSets), de::dataOrNull(rawDescriptorSets), 0u, nullptr); |
| vkd.cmdBindPipeline(cmdBuffer, bindPoint, pipelines.back()); |
| vkd.cmdDraw(cmdBuffer, vertexCount, 1u, 0u, 0u); |
| endRenderPass(vkd, cmdBuffer); |
| |
| const auto copyRegion = makeBufferImageCopy(fbExtent, colorSRL); |
| const auto preHostBarrier = makeMemoryBarrier((VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_WRITE_BIT), VK_ACCESS_HOST_READ_BIT); |
| const auto postRenderBarrier = makeImageMemoryBarrier( |
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, |
| colorAtt->get(), colorSRR); |
| |
| // Copy color attachment to verification buffer. |
| cmdPipelineImageMemoryBarrier(vkd, cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, &postRenderBarrier); |
| vkd.cmdCopyImageToBuffer(cmdBuffer, colorAtt->get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, verifBuffer->get(), 1u, ©Region); |
| |
| // Synchronize SSBO and verification buffer reads from the host. |
| cmdPipelineMemoryBarrier(vkd, cmdBuffer, (VK_PIPELINE_STAGE_TRANSFER_BIT | pipelineStages), VK_PIPELINE_STAGE_HOST_BIT, &preHostBarrier); |
| } |
| else if (isCompute) |
| { |
| const auto bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE; |
| const auto preHostBarrier = makeMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT); |
| |
| vkd.cmdBindDescriptorSets(cmdBuffer, bindPoint, pipelineLayout.get(), 0u, de::sizeU32(rawDescriptorSets), de::dataOrNull(rawDescriptorSets), 0u, nullptr); |
| vkd.cmdBindPipeline(cmdBuffer, bindPoint, pipelines.back()); |
| vkd.cmdDispatch(cmdBuffer, 1u, 1u, 1u); |
| cmdPipelineMemoryBarrier(vkd, cmdBuffer, pipelineStages, VK_PIPELINE_STAGE_HOST_BIT, &preHostBarrier); |
| } |
| else if (isRT) |
| { |
| const auto bindPoint = VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR; |
| const auto preHostBarrier = makeMemoryBarrier(VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT); |
| const auto rayCount = (hasHitAndMiss ? 2u : 1u); |
| |
| vkd.cmdBindDescriptorSets(cmdBuffer, bindPoint, pipelineLayout.get(), 0u, de::sizeU32(rawDescriptorSets), de::dataOrNull(rawDescriptorSets), 0u, nullptr); |
| vkd.cmdBindPipeline(cmdBuffer, bindPoint, pipelines.back()); |
| vkd.cmdTraceRaysKHR(cmdBuffer, &rgenRegion, &missRegion, &xhitRegion, &callRegion, rayCount, 1u, 1u); |
| cmdPipelineMemoryBarrier(vkd, cmdBuffer, pipelineStages, VK_PIPELINE_STAGE_HOST_BIT, &preHostBarrier); |
| } |
| else |
| { |
| DE_ASSERT(false); |
| } |
| |
| // Finish and submit command buffer. |
| endCommandBuffer(vkd, cmdBuffer); |
| submitCommandsAndWait(vkd, device, queue, cmdBuffer); |
| |
| // Verify framebuffer if used. |
| if (isGraphics) |
| { |
| auto& verifBufferAlloc = verifBuffer->getAllocation(); |
| void* verifBufferData = verifBufferAlloc.getHostPtr(); |
| |
| invalidateAlloc(vkd, device, verifBufferAlloc); |
| |
| tcu::ConstPixelBufferAccess resultAccess (tcuFbFormat, iExtent, verifBufferData); |
| const tcu::Vec4 expectedColor = (m_params->hasFrag() ? blueColor : clearColor); |
| const auto resultColor = resultAccess.getPixel(0, 0); |
| |
| if (resultColor != expectedColor) |
| { |
| std::ostringstream msg; |
| msg << "Unexpected color found in Framebuffer: expected " << expectedColor << " but found " << resultColor; |
| TCU_FAIL(msg.str()); |
| } |
| } |
| |
| // Verify SSBO data. |
| { |
| invalidateAlloc(vkd, device, storageBufferAlloc); |
| std::vector<uint32_t> outputData(stagesCount, 0u); |
| deMemcpy(outputData.data(), storageBufferData, de::dataSize(outputData)); |
| |
| for (size_t stageIdx = 0u; stageIdx < stagesCount; ++stageIdx) |
| { |
| const auto& expected = shaderConstants.at(getShaderIdx(m_params->pipelineToRun.get(), stageIdx, stagesCount)); |
| const auto& result = outputData.at(stageIdx); |
| |
| if (expected != result) |
| { |
| std::ostringstream msg; |
| msg << "Unexpected data found for stage " << stageIdx << std::hex << ": expected 0x" << expected << " but found 0x" << result; |
| TCU_FAIL(msg.str()); |
| } |
| } |
| } |
| |
| return tcu::TestStatus::pass("Pass"); |
| } |
| |
| enum class Winding |
| { |
| CW = 0, |
| CCW, |
| }; |
| |
| enum class Partitioning |
| { |
| INTEGER = 0, |
| FRACTIONAL_ODD, |
| }; |
| |
| std::ostream& operator<<(std::ostream& out, Winding w) |
| { |
| return (out << ((w == Winding::CW) ? "triangle_cw" : "triangle_ccw")); |
| } |
| |
| std::ostream& operator<<(std::ostream& out, Partitioning p) |
| { |
| return (out << ((p == Partitioning::INTEGER) ? "integer" : "fractional_odd")); |
| } |
| |
| class HLSLTessellationInstance : public vkt::TestInstance |
| { |
| public: |
| HLSLTessellationInstance (Context& context, PipelineConstructionType constructionType) |
| : vkt::TestInstance (context) |
| , m_constructionType (constructionType) |
| {} |
| virtual ~HLSLTessellationInstance (void) {} |
| |
| tcu::TestStatus iterate (void) override; |
| |
| protected: |
| const PipelineConstructionType m_constructionType; |
| }; |
| |
| class HLSLTessellationCase : public vkt::TestCase |
| { |
| public: |
| HLSLTessellationCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, PipelineConstructionType constructionType) |
| : vkt::TestCase (testCtx, name, description) |
| , m_constructionType (constructionType) |
| {} |
| virtual ~HLSLTessellationCase (void) {} |
| |
| void checkSupport (Context& context) const override; |
| void initPrograms (vk::SourceCollections& programCollection) const override; |
| TestInstance* createInstance (Context& context) const override { return new HLSLTessellationInstance(context, m_constructionType); } |
| |
| static std::vector<tcu::Vec4> getOutputColors (void); |
| |
| protected: |
| const PipelineConstructionType m_constructionType; |
| }; |
| |
| std::vector<tcu::Vec4> HLSLTessellationCase::getOutputColors (void) |
| { |
| std::vector<tcu::Vec4> outColors |
| { |
| tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f), |
| tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f), |
| tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f), |
| tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f), |
| }; |
| |
| return outColors; |
| } |
| |
| void HLSLTessellationCase::checkSupport (Context &context) const |
| { |
| const auto& vki = context.getInstanceInterface(); |
| const auto physicalDevice = context.getPhysicalDevice(); |
| |
| checkPipelineLibraryRequirements(vki, physicalDevice, m_constructionType); |
| context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_TESSELLATION_SHADER); |
| checkShaderModuleIdentifierSupport(context); |
| } |
| |
| void HLSLTessellationCase::initPrograms (vk::SourceCollections &programCollection) const |
| { |
| // Vertex shader. |
| { |
| // Full-screen triangle. |
| std::ostringstream vert; |
| vert |
| << "#version 450\n" |
| << "out gl_PerVertex\n" |
| << "{\n" |
| << " vec4 gl_Position;\n" |
| << "};\n" |
| << "vec2 vertexPositions[3] = vec2[](\n" |
| << " vec2(-1.0, -1.0),\n" |
| << " vec2( 3.0, -1.0),\n" |
| << " vec2(-1.0, 3.0)\n" |
| << ");\n" |
| << "void main (void) {\n" |
| << " gl_Position = vec4(vertexPositions[gl_VertexIndex], 0.0, 1.0);\n" |
| << "}\n" |
| ; |
| |
| programCollection.glslSources.add("vert") << glu::VertexSource(vert.str()); |
| } |
| |
| // Fragment shader, which outputs the color from the previous stages. |
| { |
| std::ostringstream frag; |
| frag |
| << "#version 450\n" |
| << "layout (location=0) in vec4 inColor;\n" |
| << "layout (location=0) out vec4 outColor;\n" |
| << "void main (void) {\n" |
| << " outColor = inColor;\n" |
| << "}\n" |
| ; |
| |
| programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str()); |
| } |
| |
| // Tessellation evaluation shader (AKA domain shader) in HLSL, common for every pipeline. |
| // Contrary to GLSL, HLSL allows us to omit execution modes in the "tese" shader and specify them on the "tesc" shader. |
| { |
| std::ostringstream tese; |
| tese |
| << "struct HullShaderOutput\n" |
| << "{\n" |
| << " float4 Position : SV_Position;\n" |
| << " [[vk::location(0)]] float4 Color : COLOR0;\n" |
| << "};\n" |
| << "\n" |
| << "struct HullShaderConstantOutput\n" |
| << "{\n" |
| << " float TessLevelOuter[4] : SV_TessFactor;\n" |
| << " float TessLevelInner[2] : SV_InsideTessFactor;\n" |
| << "};\n" |
| << "\n" |
| << "struct DomainShaderOutput\n" |
| << "{\n" |
| << " float4 Position : SV_Position;\n" |
| << " [[vk::location(0)]] float4 Color : COLOR0;\n" |
| << "};\n" |
| << "\n" |
| << "DomainShaderOutput main (HullShaderConstantOutput input, float3 TessCoord : SV_DomainLocation, const OutputPatch<HullShaderOutput, 3> patch)\n" |
| << "{\n" |
| << " DomainShaderOutput output = (DomainShaderOutput)0;\n" |
| << "\n" |
| << " output.Position = (TessCoord.x * patch[0].Position) +\n" |
| << " (TessCoord.y * patch[1].Position) +\n" |
| << " (TessCoord.z * patch[2].Position);\n" |
| << "\n" |
| << " output.Color = (TessCoord.x * patch[0].Color) +\n" |
| << " (TessCoord.y * patch[1].Color) +\n" |
| << " (TessCoord.z * patch[2].Color);\n" |
| << "\n" |
| << " return output;\n" |
| << "}\n" |
| ; |
| |
| programCollection.hlslSources.add("tese") << glu::TessellationEvaluationSource(tese.str()); |
| } |
| |
| // Tessellation control shaders. Create 4 combinations with different execution modes. Each combination will also assign a different color to the vertices. |
| // We will later run each pipeline to draw a pixel in a framebuffer (using viewports and scissors) to end up with 4 distinct colors. |
| { |
| const auto outColors = getOutputColors(); |
| size_t colorIdx = 0; |
| |
| const Winding windings[] = { Winding::CW, Winding::CCW }; |
| const Partitioning partitionings[] = { Partitioning::INTEGER, Partitioning::FRACTIONAL_ODD }; |
| |
| for (const auto& winding : windings) |
| for (const auto& partitioning : partitionings) |
| { |
| std::ostringstream tesc; |
| tesc |
| << "struct VertexShaderOutput\n" |
| << "{\n" |
| << " float4 Position : SV_Position;\n" |
| << "};\n" |
| << "\n" |
| << "struct HullShaderOutput\n" |
| << "{\n" |
| << " float4 Position : SV_Position;\n" |
| << " [[vk::location(0)]] float4 Color : COLOR0;\n" |
| << "};\n" |
| << "\n" |
| << "struct HullShaderConstantOutput\n" |
| << "{\n" |
| << " float TessLevelOuter[4] : SV_TessFactor;\n" |
| << " float TessLevelInner[2] : SV_InsideTessFactor;\n" |
| << "};\n" |
| << "\n" |
| << "[domain(\"tri\")]\n" |
| << "[partitioning(\"" << partitioning << "\")]\n" |
| << "[outputtopology(\"" << winding << "\")]\n" |
| << "[outputcontrolpoints(3)]\n" |
| << "[patchconstantfunc(\"PCF\")]\n" |
| << "HullShaderOutput main (InputPatch<VertexShaderOutput, 3> patch, uint InvocationID : SV_OutputControlPointID)\n" |
| << "{\n" |
| << " HullShaderOutput output = (HullShaderOutput)0;\n" |
| << " output.Position = patch[InvocationID].Position;\n" |
| << " output.Color = float4" << outColors.at(colorIdx) << ";\n" |
| << " return output;\n" |
| << "}\n" |
| << "\n" |
| << "HullShaderConstantOutput PCF (InputPatch<VertexShaderOutput, 3> patch, uint InvocationID : SV_PrimitiveID)\n" |
| << "{\n" |
| << " HullShaderConstantOutput output = (HullShaderConstantOutput)0;\n" |
| << "\n" |
| << " output.TessLevelOuter[0] = 1;\n" |
| << " output.TessLevelOuter[1] = 1;\n" |
| << " output.TessLevelOuter[2] = 1;\n" |
| << " output.TessLevelOuter[3] = 1;\n" |
| << "\n" |
| << " output.TessLevelInner[0] = 1;\n" |
| << " output.TessLevelInner[1] = 1;\n" |
| << "\n" |
| << " return output;\n" |
| << "}\n" |
| ; |
| |
| const auto idxStr = std::to_string(colorIdx); |
| programCollection.hlslSources.add("tesc" + idxStr) << glu::TessellationControlSource(tesc.str()); |
| |
| ++colorIdx; |
| } |
| } |
| } |
| |
| tcu::TestStatus HLSLTessellationInstance::iterate (void) |
| { |
| const auto& vkd = m_context.getDeviceInterface(); |
| const auto device = m_context.getDevice(); |
| auto& alloc = m_context.getDefaultAllocator(); |
| const auto queue = m_context.getUniversalQueue(); |
| const auto queueIndex = m_context.getUniversalQueueFamilyIndex(); |
| |
| const auto fbFormat = VK_FORMAT_R8G8B8A8_UNORM; |
| const auto fbExtent = makeExtent3D(2u, 2u, 1u); |
| const tcu::IVec3 iExtent (static_cast<int>(fbExtent.width), static_cast<int>(fbExtent.height), static_cast<int>(fbExtent.depth)); |
| const auto tcuFbFormat = mapVkFormat(fbFormat); |
| const auto pixelSize = tcu::getPixelSize(tcuFbFormat); |
| const auto topology = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST; |
| const auto patchCPs = 3u; |
| const tcu::Vec4 clearColor (0.0f, 0.0f, 0.0f, 1.0f); |
| const auto bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
| |
| const std::vector<VkViewport> rpViewports (1u, makeViewport(fbExtent)); |
| const std::vector<VkRect2D> rpScissors (1u, makeRect2D(fbExtent)); |
| |
| // Color attachment. |
| const VkImageCreateInfo colorAttCreateInfo = |
| { |
| VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType; |
| nullptr, // const void* pNext; |
| 0u, // VkImageCreateFlags flags; |
| VK_IMAGE_TYPE_2D, // VkImageType imageType; |
| fbFormat, // VkFormat format; |
| fbExtent, // VkExtent3D extent; |
| 1u, // uint32_t mipLevels; |
| 1u, // uint32_t arrayLayers; |
| VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples; |
| VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling; |
| (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT), // VkImageUsageFlags usage; |
| VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode; |
| 0u, // uint32_t queueFamilyIndexCount; |
| nullptr, // const uint32_t* pQueueFamilyIndices; |
| VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout; |
| }; |
| |
| ImageWithMemory colorAtt (vkd, device, alloc, colorAttCreateInfo, MemoryRequirement::Any); |
| const auto colorSRR = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u); |
| const auto colorSRL = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u); |
| const auto colorAttView = makeImageView(vkd, device, colorAtt.get(), VK_IMAGE_VIEW_TYPE_2D, fbFormat, colorSRR); |
| const auto renderPass = makeRenderPass(vkd, device, fbFormat); |
| const auto framebuffer = makeFramebuffer(vkd, device, renderPass.get(), colorAttView.get(), fbExtent.width, fbExtent.height); |
| |
| // Verification buffer. |
| DE_ASSERT(fbExtent.depth == 1u); |
| const auto verifBufferSize = static_cast<VkDeviceSize>(pixelSize) * fbExtent.width * fbExtent.height; |
| const auto verifBufferInfo = makeBufferCreateInfo(verifBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT); |
| BufferWithMemory verifBuffer (vkd, device, alloc, verifBufferInfo, MemoryRequirement::HostVisible); |
| |
| // Create shader modules, obtain IDs and verify all of them differ. |
| const auto& binaries = m_context.getBinaryCollection(); |
| const auto vertModule = createShaderModule(vkd, device, binaries.get("vert")); |
| const auto fragModule = createShaderModule(vkd, device, binaries.get("frag")); |
| const auto teseModule = createShaderModule(vkd, device, binaries.get("tese")); |
| |
| std::vector<Move<VkShaderModule>> tescModules; |
| { |
| size_t tescIdx = 0; |
| |
| for (;;) |
| { |
| const auto shaderName = "tesc" + std::to_string(tescIdx); |
| if (!binaries.contains(shaderName)) |
| break; |
| tescModules.emplace_back(createShaderModule(vkd, device, binaries.get(shaderName))); |
| |
| ++tescIdx; |
| } |
| } |
| |
| const auto vertId = getShaderModuleIdentifier(vkd, device, vertModule.get()); |
| const auto fragId = getShaderModuleIdentifier(vkd, device, fragModule.get()); |
| const auto teseId = getShaderModuleIdentifier(vkd, device, teseModule.get()); |
| std::vector<ShaderModuleId> tescIds; |
| for (const auto& mod : tescModules) |
| tescIds.emplace_back(getShaderModuleIdentifier(vkd, device, mod.get())); |
| |
| // Verify all of them are unique. |
| { |
| std::vector<ShaderModuleId> allIds; |
| allIds.emplace_back(vertId); |
| allIds.emplace_back(fragId); |
| allIds.emplace_back(teseId); |
| for (const auto& id : tescIds) |
| allIds.emplace_back(id); |
| |
| std::set<ShaderModuleId> uniqueIds (begin(allIds), end(allIds)); |
| |
| if (allIds.size() != uniqueIds.size()) |
| TCU_FAIL("Not every module has a unique ID"); |
| } |
| |
| // Constant structures used when creating pipelines. |
| const VkPipelineVertexInputStateCreateInfo vertexInputState = initVulkanStructure(); |
| const VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType sType; |
| nullptr, // const void* pNext; |
| 0u, // VkPipelineInputAssemblyStateCreateFlags flags; |
| topology, // VkPrimitiveTopology topology; |
| VK_FALSE, // VkBool32 primitiveRestartEnable; |
| }; |
| const VkPipelineDepthStencilStateCreateInfo depthStencilState = initVulkanStructure(); |
| VkPipelineMultisampleStateCreateInfo multisampleState = initVulkanStructure(); |
| multisampleState.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; |
| VkPipelineColorBlendAttachmentState colorBlendAttachmentState; |
| deMemset(&colorBlendAttachmentState, 0, sizeof(colorBlendAttachmentState)); |
| colorBlendAttachmentState.colorWriteMask = (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT); |
| const VkPipelineColorBlendStateCreateInfo colorBlendState = |
| { |
| VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType |
| nullptr, // const void* pNext |
| 0u, // VkPipelineColorBlendStateCreateFlags flags |
| VK_FALSE, // VkBool32 logicOpEnable |
| VK_LOGIC_OP_CLEAR, // VkLogicOp logicOp |
| 1u, // deUint32 attachmentCount |
| &colorBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments |
| { 0.0f, 0.0f, 0.0f, 0.0f } // float blendConstants[4] |
| }; |
| const auto rasterizationState = makeRasterizationState(false/*rasterizationDisabled*/); |
| |
| // Pipeline cache. |
| const VkPipelineCacheCreateInfo cacheCreateInfo = initVulkanStructure(); |
| const auto pipelineCache = createPipelineCache(vkd, device, &cacheCreateInfo); |
| |
| // Empty pipeline layout. |
| const auto pipelineLayout = makePipelineLayout(vkd, device); |
| |
| using GraphicsPipelineWrapperPtr = std::unique_ptr<GraphicsPipelineWrapper>; |
| |
| // Create temporary pipelines with them to prime the cache. |
| { |
| for (const auto& tescModule : tescModules) |
| { |
| GraphicsPipelineWrapperPtr wrapper (new GraphicsPipelineWrapper(vkd, device, m_constructionType)); |
| |
| try |
| { |
| wrapper->setDefaultPatchControlPoints(patchCPs) |
| .setupVertexInputStete(&vertexInputState, &inputAssemblyState, pipelineCache.get()) |
| .setupPreRasterizationShaderState2( |
| rpViewports, |
| rpScissors, |
| pipelineLayout.get(), |
| renderPass.get(), |
| 0u, |
| vertModule.get(), |
| &rasterizationState, |
| tescModule.get(), |
| teseModule.get(), |
| DE_NULL, |
| nullptr, |
| nullptr, |
| nullptr, |
| nullptr, |
| nullptr, |
| PipelineRenderingCreateInfoWrapper(), |
| pipelineCache.get()) |
| .setupFragmentShaderState( |
| pipelineLayout.get(), |
| renderPass.get(), |
| 0u, |
| fragModule.get(), |
| &depthStencilState, |
| &multisampleState, |
| nullptr, |
| pipelineCache.get()) |
| .setupFragmentOutputState( |
| *renderPass, |
| 0u, |
| &colorBlendState, |
| &multisampleState, |
| pipelineCache.get()) |
| .setMonolithicPipelineLayout(pipelineLayout.get()) |
| .buildPipeline(pipelineCache.get()); |
| } |
| catch (const PipelineCompileRequiredError& err) |
| { |
| TCU_FAIL("PipelineCompileRequiredError received while priming pipeline cache"); |
| } |
| } |
| } |
| |
| // Create pipelines using shader module ids. These will actually be run. Note the changing viewports and scissors. |
| std::vector<GraphicsPipelineWrapperPtr> pipelineWrappers; |
| std::vector<VkViewport> viewports; |
| std::vector<VkRect2D> scissors; |
| |
| const auto vertIdInfo = makeShaderStageModuleIdentifierCreateInfo(vertId, UseModuleCase::ID); |
| const auto fragIdInfo = makeShaderStageModuleIdentifierCreateInfo(fragId, UseModuleCase::ID); |
| const auto teseIdInfo = makeShaderStageModuleIdentifierCreateInfo(teseId, UseModuleCase::ID); |
| std::vector<ShaderStageIdPtr> tescIdInfos; |
| for (const auto& tescId : tescIds) |
| tescIdInfos.emplace_back(makeShaderStageModuleIdentifierCreateInfo(tescId, UseModuleCase::ID)); |
| |
| for (size_t tescIdx = 0; tescIdx < tescModules.size(); ++tescIdx) |
| { |
| const auto row = tescIdx / fbExtent.width; |
| const auto col = tescIdx % fbExtent.width; |
| |
| viewports.emplace_back(makeViewport(static_cast<float>(col), static_cast<float>(row), 1.0f, 1.0f, 0.0f, 1.0f)); |
| scissors.emplace_back(makeRect2D(static_cast<int32_t>(col), static_cast<int32_t>(row), 1u, 1u)); |
| pipelineWrappers.emplace_back(new GraphicsPipelineWrapper(vkd, device, m_constructionType)); |
| |
| const auto& wrapper = pipelineWrappers.back(); |
| |
| try |
| { |
| wrapper->setDefaultPatchControlPoints(patchCPs) |
| .setupVertexInputStete(&vertexInputState, &inputAssemblyState, pipelineCache.get()) |
| .setupPreRasterizationShaderState3( |
| std::vector<VkViewport>(1u, viewports.back()), |
| std::vector<VkRect2D>(1u, scissors.back()), |
| pipelineLayout.get(), |
| renderPass.get(), |
| 0u, |
| DE_NULL, |
| PipelineShaderStageModuleIdentifierCreateInfoWrapper(vertIdInfo.get()), |
| &rasterizationState, |
| DE_NULL, |
| PipelineShaderStageModuleIdentifierCreateInfoWrapper(tescIdInfos.at(tescIdx).get()), |
| DE_NULL, |
| PipelineShaderStageModuleIdentifierCreateInfoWrapper(teseIdInfo.get()), |
| DE_NULL, |
| PipelineShaderStageModuleIdentifierCreateInfoWrapper(), |
| nullptr, |
| nullptr, |
| nullptr, |
| nullptr, |
| nullptr, |
| PipelineRenderingCreateInfoWrapper(), |
| pipelineCache.get()) |
| .setupFragmentShaderState2( |
| pipelineLayout.get(), |
| renderPass.get(), |
| 0u, |
| DE_NULL, |
| PipelineShaderStageModuleIdentifierCreateInfoWrapper(fragIdInfo.get()), |
| &depthStencilState, |
| &multisampleState, |
| nullptr, |
| pipelineCache.get()) |
| .setupFragmentOutputState( |
| *renderPass, |
| 0u, |
| &colorBlendState, |
| &multisampleState, |
| pipelineCache.get()) |
| .setMonolithicPipelineLayout(pipelineLayout.get()) |
| .buildPipeline(pipelineCache.get()); |
| } |
| catch (const PipelineCompileRequiredError& err) |
| { |
| return tcu::TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "PipelineCompileRequiredError received despite using pipeline cache"); |
| } |
| } |
| |
| // Use pipelines in a render pass. |
| const auto cmdPool = makeCommandPool(vkd, device, queueIndex); |
| const auto cmdBufferPtr = allocateCommandBuffer(vkd, device, cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_PRIMARY); |
| const auto cmdBuffer = cmdBufferPtr.get(); |
| |
| beginCommandBuffer(vkd, cmdBuffer); |
| beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), rpScissors.at(0u), clearColor); |
| for (const auto& wrapper : pipelineWrappers) |
| { |
| vkd.cmdBindPipeline(cmdBuffer, bindPoint, wrapper->getPipeline()); |
| vkd.cmdDraw(cmdBuffer, 3u, 1u, 0u, 0u); |
| } |
| endRenderPass(vkd, cmdBuffer); |
| |
| // Transfer color attachment to verification buffer. |
| const auto copyRegion = makeBufferImageCopy(fbExtent, colorSRL); |
| const auto preHostBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT); |
| const auto postRenderBarrier = makeImageMemoryBarrier( |
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, |
| VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, |
| colorAtt.get(), colorSRR); |
| |
| cmdPipelineImageMemoryBarrier(vkd, cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, &postRenderBarrier); |
| vkd.cmdCopyImageToBuffer(cmdBuffer, colorAtt.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, verifBuffer.get(), 1u, ©Region); |
| cmdPipelineMemoryBarrier(vkd, cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, &preHostBarrier); |
| |
| endCommandBuffer(vkd, cmdBuffer); |
| submitCommandsAndWait(vkd, device, queue, cmdBuffer); |
| |
| // Verify result. |
| { |
| auto& log = m_context.getTestContext().getLog(); |
| const auto outColors = HLSLTessellationCase::getOutputColors(); |
| auto& verifBufferAlloc = verifBuffer.getAllocation(); |
| void* verifBufferData = verifBufferAlloc.getHostPtr(); |
| |
| invalidateAlloc(vkd, device, verifBufferAlloc); |
| |
| tcu::ConstPixelBufferAccess resultAccess (tcuFbFormat, iExtent, verifBufferData); |
| tcu::TextureLevel referenceLevel (tcuFbFormat, iExtent.x(), iExtent.y()); |
| const auto referenceAccess = referenceLevel.getAccess(); |
| const tcu::Vec4 threshold (0.0f, 0.0f, 0.0f, 0.0f); |
| |
| for (int x = 0; x < iExtent.x(); ++x) |
| for (int y = 0; y < iExtent.y(); ++y) |
| referenceAccess.setPixel(outColors.at(y*iExtent.x() + x), x, y); |
| |
| tcu::floatThresholdCompare(log, "Result", "", referenceAccess, resultAccess, threshold, tcu::COMPARE_LOG_EVERYTHING); |
| } |
| |
| return tcu::TestStatus::pass("Pass"); |
| } |
| |
| } // anonymous namespace |
| |
| tcu::TestCaseGroup* createShaderModuleIdentifierTests (tcu::TestContext& testCtx, vk::PipelineConstructionType constructionType) |
| { |
| // No pipelines are actually constructed in some of these variants, so adding them to a single group is fine. |
| GroupPtr mainGroup (new tcu::TestCaseGroup(testCtx, "shader_module_identifier", "Tests for VK_EXT_shader_module_identifier")); |
| |
| if (constructionType == PIPELINE_CONSTRUCTION_TYPE_MONOLITHIC) |
| { |
| // Property tests. |
| GroupPtr propertiesGroup (new tcu::TestCaseGroup(testCtx, "properties", "Test shader module identifier extension properties")); |
| |
| addFunctionCase(propertiesGroup.get(), "constant_algorithm_uuid", "", checkShaderModuleIdentifierSupport, constantAlgorithmUUIDCase); |
| |
| mainGroup->addChild(propertiesGroup.release()); |
| } |
| |
| const struct |
| { |
| PipelineType pipelineType; |
| bool useRTLibraries; |
| const char* name; |
| } pipelineTypeCases[] = |
| { |
| { PipelineType::COMPUTE, false, "compute" }, |
| { PipelineType::GRAPHICS, false, "graphics" }, |
| { PipelineType::RAY_TRACING, false, "ray_tracing" }, |
| { PipelineType::RAY_TRACING, true, "ray_tracing_libs" }, |
| }; |
| |
| const uint8_t pipelineCountCases[] = { uint8_t{1}, uint8_t{4} }; |
| |
| const std::vector<GraphicsShaderVec> graphicsShadersCases |
| { |
| { GraphicsShaderType::VERTEX }, |
| { GraphicsShaderType::VERTEX, GraphicsShaderType::FRAG }, |
| { GraphicsShaderType::VERTEX, GraphicsShaderType::TESS_CONTROL, GraphicsShaderType::TESS_EVAL, GraphicsShaderType::FRAG }, |
| { GraphicsShaderType::VERTEX, GraphicsShaderType::GEOMETRY, GraphicsShaderType::FRAG }, |
| { GraphicsShaderType::VERTEX, GraphicsShaderType::TESS_CONTROL, GraphicsShaderType::TESS_EVAL, GraphicsShaderType::GEOMETRY, GraphicsShaderType::FRAG }, |
| }; |
| |
| const std::vector<RTShaderVec> rtShadersCases |
| { |
| { RayTracingShaderType::RAY_GEN, RayTracingShaderType::MISS }, |
| { RayTracingShaderType::RAY_GEN, RayTracingShaderType::CLOSEST_HIT, RayTracingShaderType::MISS }, |
| { RayTracingShaderType::RAY_GEN, RayTracingShaderType::ANY_HIT, RayTracingShaderType::CLOSEST_HIT, RayTracingShaderType::MISS }, |
| { RayTracingShaderType::RAY_GEN, RayTracingShaderType::INTERSECTION, RayTracingShaderType::ANY_HIT, RayTracingShaderType::CLOSEST_HIT, RayTracingShaderType::MISS }, |
| { RayTracingShaderType::RAY_GEN, RayTracingShaderType::CALLABLE }, |
| }; |
| |
| const struct |
| { |
| bool useSCs; |
| const char* name; |
| } useSCCases[] = |
| { |
| { false, "no_spec_constants" }, |
| { true, "use_spec_constants" }, |
| }; |
| |
| // Tests checking the identifiers are constant. |
| if (constructionType == PIPELINE_CONSTRUCTION_TYPE_MONOLITHIC) |
| { |
| // Constant and unique module identifier tests. |
| GroupPtr constantIdsGroup (new tcu::TestCaseGroup(testCtx, "constant_identifiers", "Test shader modules have constant and unique identifiers")); |
| |
| const struct |
| { |
| ConstantModuleIdentifiersInstance::APICall apiCall; |
| const char* name; |
| } apiCallCases[] = |
| { |
| { ConstantModuleIdentifiersInstance::APICall::MODULE, "module_id" }, |
| { ConstantModuleIdentifiersInstance::APICall::CREATE_INFO, "create_info_id" }, |
| { ConstantModuleIdentifiersInstance::APICall::BOTH, "both_ids" }, |
| }; |
| |
| const struct |
| { |
| bool differentDevice; |
| const char* name; |
| } differentDeviceCases[] = |
| { |
| { false, "same_device" }, |
| { true, "different_devices" }, |
| }; |
| |
| for (const auto& pipelineTypeCase : pipelineTypeCases) |
| { |
| // Skip this case for constant module identifiers. |
| if (pipelineTypeCase.useRTLibraries) |
| continue; |
| |
| GroupPtr pipelineTypeGroup (new tcu::TestCaseGroup(testCtx, pipelineTypeCase.name, "")); |
| |
| for (const auto& pipelineCountCase : pipelineCountCases) |
| { |
| const auto countGroupName = std::to_string(static_cast<int>(pipelineCountCase)) + "_variants"; |
| |
| GroupPtr pipelineCountGroup (new tcu::TestCaseGroup(testCtx, countGroupName.c_str(), "")); |
| |
| for (const auto& useSCCase : useSCCases) |
| { |
| GroupPtr useSCGroup (new tcu::TestCaseGroup(testCtx, useSCCase.name, "")); |
| |
| for (const auto& apiCallCase : apiCallCases) |
| { |
| GroupPtr apiCallGroup (new tcu::TestCaseGroup(testCtx, apiCallCase.name, "")); |
| |
| for (const auto& differentDeviceCase : differentDeviceCases) |
| { |
| GroupPtr differentDeviceGroup (new tcu::TestCaseGroup(testCtx, differentDeviceCase.name, "")); |
| |
| using Params = ConstantModuleIdentifiersInstance::Params; |
| |
| Params commonParams( |
| pipelineTypeCase.pipelineType, |
| {}, {}, pipelineCountCase, tcu::Nothing, |
| useSCCase.useSCs, false, apiCallCase.apiCall, differentDeviceCase.differentDevice); |
| |
| if (pipelineTypeCase.pipelineType == PipelineType::GRAPHICS) |
| { |
| for (const auto& graphicsShadersCase : graphicsShadersCases) |
| { |
| std::unique_ptr<Params> params (new Params(commonParams)); |
| params->graphicsShaders = graphicsShadersCase; |
| differentDeviceGroup->addChild(new ConstantModuleIdentifiersCase(testCtx, toString(graphicsShadersCase), "", std::move(params))); |
| } |
| } |
| else if (pipelineTypeCase.pipelineType == PipelineType::RAY_TRACING) |
| { |
| for (const auto& rtShadersCase : rtShadersCases) |
| { |
| std::unique_ptr<Params> params (new Params(commonParams)); |
| params->rtShaders = rtShadersCase; |
| differentDeviceGroup->addChild(new ConstantModuleIdentifiersCase(testCtx, toString(rtShadersCase), "", std::move(params))); |
| } |
| } |
| else // Compute |
| { |
| std::unique_ptr<Params> params (new Params(commonParams)); |
| differentDeviceGroup->addChild(new ConstantModuleIdentifiersCase(testCtx, "comp", "", std::move(params))); |
| } |
| |
| apiCallGroup->addChild(differentDeviceGroup.release()); |
| } |
| |
| useSCGroup->addChild(apiCallGroup.release()); |
| } |
| |
| pipelineCountGroup->addChild(useSCGroup.release()); |
| } |
| |
| pipelineTypeGroup->addChild(pipelineCountGroup.release()); |
| } |
| |
| constantIdsGroup->addChild(pipelineTypeGroup.release()); |
| } |
| |
| mainGroup->addChild(constantIdsGroup.release()); |
| } |
| |
| // Tests creating pipelines using the module id extension structures. |
| { |
| const struct |
| { |
| bool useVkPipelineCache; |
| const char* name; |
| } pipelineCacheCases[] = |
| { |
| { false, "no_pipeline_cache" }, |
| { true, "use_pipeline_cache" }, |
| }; |
| |
| const struct |
| { |
| UseModuleCase moduleUse; |
| const char* name; |
| } moduleUsageCases[] = |
| { |
| { UseModuleCase::ID, "use_id" }, |
| { UseModuleCase::ZERO_LEN_ID, "zero_len_id" }, |
| { UseModuleCase::ZERO_LEN_ID_NULL_PTR, "zero_len_id_null_ptr" }, |
| { UseModuleCase::ZERO_LEN_ID_GARBAGE_PTR, "zero_len_id_garbage_ptr" }, |
| { UseModuleCase::ALL_ZEROS, "all_zeros_id" }, |
| { UseModuleCase::ALL_ONES, "all_ones_id" }, |
| { UseModuleCase::PSEUDORANDOM_ID, "pseudorandom_id" }, |
| }; |
| |
| const struct |
| { |
| CapturedPropertiesBits capturedProperties; |
| const char* name; |
| } capturingCases[] = |
| { |
| { CapturedPropertiesBits::NONE, "no_exec_properties" }, |
| { CapturedPropertiesBits::STATS, "capture_stats" }, |
| { CapturedPropertiesBits::IRS, "capture_irs" }, |
| }; |
| |
| uint32_t rndSeed = 1651848014u; |
| |
| // Tests using pipelines created using shader identifiers. |
| GroupPtr pipelineFromIdsGroup (new tcu::TestCaseGroup(testCtx, "pipeline_from_id", "Test creating and using pipelines from shader module identifiers")); |
| |
| for (const auto& pipelineTypeCase : pipelineTypeCases) |
| { |
| if (pipelineTypeCase.pipelineType != PipelineType::GRAPHICS && constructionType != PipelineConstructionType::PIPELINE_CONSTRUCTION_TYPE_MONOLITHIC) |
| continue; |
| |
| GroupPtr pipelineTypeGroup (new tcu::TestCaseGroup(testCtx, pipelineTypeCase.name, "")); |
| |
| for (const auto& pipelineCountCase : pipelineCountCases) |
| { |
| const auto countGroupName = std::to_string(static_cast<int>(pipelineCountCase)) + "_variants"; |
| |
| GroupPtr pipelineCountGroup (new tcu::TestCaseGroup(testCtx, countGroupName.c_str(), "")); |
| |
| for (const auto& useSCCase : useSCCases) |
| { |
| GroupPtr useSCGroup (new tcu::TestCaseGroup(testCtx, useSCCase.name, "")); |
| |
| for (const auto& pipelineCacheCase : pipelineCacheCases) |
| { |
| GroupPtr pipelineCacheGroup (new tcu::TestCaseGroup(testCtx, pipelineCacheCase.name, "")); |
| |
| for (const auto& moduleUsageCase : moduleUsageCases) |
| { |
| GroupPtr moduleUsageGroup (new tcu::TestCaseGroup(testCtx, moduleUsageCase.name, "")); |
| |
| for (const auto& capturingCase : capturingCases) |
| { |
| // We are only going to attempt to capture properties in a specific subset of the tests. |
| if (capturingCase.capturedProperties != CapturedPropertiesBits::NONE && |
| (pipelineCountCase > 1u || moduleUsageCase.moduleUse != UseModuleCase::ID)) |
| continue; |
| |
| GroupPtr captureGroup (new tcu::TestCaseGroup(testCtx, capturingCase.name, "")); |
| |
| DE_ASSERT(pipelineCountCase > 0u); |
| const uint8_t pipelineToRun = (pipelineCountCase == 1u ? uint8_t{0} : static_cast<uint8_t>(pipelineCountCase - 2u)); |
| |
| CreateAndUseIdsInstance::Params baseParams( |
| pipelineTypeCase.pipelineType, |
| {}, {}, pipelineCountCase, tcu::just(pipelineToRun), |
| useSCCase.useSCs, pipelineCacheCase.useVkPipelineCache, |
| constructionType, pipelineTypeCase.useRTLibraries, |
| moduleUsageCase.moduleUse, |
| static_cast<CapturedPropertiesFlags>(capturingCase.capturedProperties)); |
| |
| if (pipelineTypeCase.pipelineType == PipelineType::GRAPHICS) |
| { |
| for (const auto& graphicsShadersCase : graphicsShadersCases) |
| { |
| BaseParamsPtr params = baseParams.copy(rndSeed++); |
| params->graphicsShaders = graphicsShadersCase; |
| captureGroup->addChild(new CreateAndUseIdsCase(testCtx, toString(graphicsShadersCase), "", std::move(params))); |
| } |
| } |
| else if (pipelineTypeCase.pipelineType == PipelineType::RAY_TRACING) |
| { |
| for (const auto& rtShadersCase : rtShadersCases) |
| { |
| BaseParamsPtr params = baseParams.copy(rndSeed++); |
| params->rtShaders = rtShadersCase; |
| captureGroup->addChild(new CreateAndUseIdsCase(testCtx, toString(rtShadersCase), "", std::move(params))); |
| } |
| } |
| else // Compute |
| { |
| BaseParamsPtr params = baseParams.copy(rndSeed++); |
| captureGroup->addChild(new CreateAndUseIdsCase(testCtx, "comp", "", std::move(params))); |
| } |
| |
| moduleUsageGroup->addChild(captureGroup.release()); |
| } |
| |
| pipelineCacheGroup->addChild(moduleUsageGroup.release()); |
| } |
| |
| useSCGroup->addChild(pipelineCacheGroup.release()); |
| } |
| |
| pipelineCountGroup->addChild(useSCGroup.release()); |
| } |
| |
| pipelineTypeGroup->addChild(pipelineCountGroup.release()); |
| } |
| |
| pipelineFromIdsGroup->addChild(pipelineTypeGroup.release()); |
| } |
| |
| mainGroup->addChild(pipelineFromIdsGroup.release()); |
| } |
| |
| // HLSL tessellation test. |
| { |
| GroupPtr hlslTessGroup (new tcu::TestCaseGroup(testCtx, "hlsl_tessellation", "Tests checking HLSL tessellation shaders with module identifiers")); |
| hlslTessGroup->addChild(new HLSLTessellationCase(testCtx, "test", "", constructionType)); |
| mainGroup->addChild(hlslTessGroup.release()); |
| } |
| |
| return mainGroup.release(); |
| } |
| |
| } // pipeline |
| } // vkt |