Add dEQP-VK.pipeline.creation_cache_control.*
authorGreg Grebe <Gregory.Grebe@amd.com>
Tue, 10 Mar 2020 17:29:46 +0000 (13:29 -0400)
committerAlexander Galazin <Alexander.Galazin@arm.com>
Thu, 26 Nov 2020 19:43:53 +0000 (14:43 -0500)
Add new test groups:

dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.*
dEQP-VK.pipeline.creation_cache_control.compute_pipelines.*

Add new tests:

*.single_pipeline_no_compile
*.batch_pipelines_early_return
*.duplicate_single_recreate_explicit_caching
*.duplicate_single_recreate_no_caching
*.duplicate_single_recreate_derivative
*.duplicate_batch_pipelines_explicit_cache
*.duplicate_batch_pipelines_no_cache
*.duplicate_batch_pipelines_derivative_index

VK_EXT_pipeline_creation_cache_control adds the ability for
vkCreate*Pipeline calls to fail rather compile when requested by the
application. These tests use both single and batch pipeline creation
with the most common methods of caching: explicit caching, no cache/
implicit caching, and pipeline derivatives.

This ensures that drivers are responsive to the needs of an application
using this extension by not only checking the output but the time taken
to execute the vkCreate*Pipeline call.

The tests explicitly fail in the following cases:

    -   Any result other than SUCCESS or PIPELINE_COMPILE_REQUIRED.

    -   An invalid pipeline handle returned when created without
        FAIL_ON_PIPELINE_COMPILE_REQUIRED.

    -   A valid pipeline is returned sequentially following a failed
        pipeline with the flag EARLY_RETURN_ON_FAILURE set during batch
        pipeline creation.

Failures due to timing will only be QUALITY warnings.

The shaders used contain random constant values baked into the GLSL in
an attempt to force the need to compile pipelines through fuzzing. Any
unexpectedly successful pipeline creations (such as due to accidental
cache hits) will raise a COMPATIBILITY warning.

Affects no existing tests.

Components: Framework, Vulkan
Vulkan issue: 2013

Change-Id: I90dec48293a5c8ece66fc871a13c6d6f0e3002ff
(cherry picked from commit 26cb99a1eb50332a93bc329a4d34a5d9239a5667)

13 files changed:
AndroidGen.mk
android/cts/master/vk-master-2020-03-01.txt
android/cts/master/vk-master.txt
external/vulkancts/framework/vulkan/vkMandatoryFeatures.inl
external/vulkancts/modules/vulkan/pipeline/CMakeLists.txt
external/vulkancts/modules/vulkan/pipeline/vktPipelineCreationCacheControlTests.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/pipeline/vktPipelineCreationCacheControlTests.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/pipeline/vktPipelineTests.cpp
external/vulkancts/modules/vulkan/util/CMakeLists.txt
external/vulkancts/modules/vulkan/util/vktConstexprVectorUtil.hpp [new file with mode: 0644]
external/vulkancts/mustpass/master/vk-default.txt
external/vulkancts/scripts/src/mandatory_features.txt
framework/common/tcuStringTemplate.hpp

index 98d33e3..cee1e3d 100644 (file)
@@ -199,6 +199,7 @@ LOCAL_SRC_FILES := \
        external/vulkancts/modules/vulkan/pipeline/vktPipelineBlendTests.cpp \
        external/vulkancts/modules/vulkan/pipeline/vktPipelineCacheTests.cpp \
        external/vulkancts/modules/vulkan/pipeline/vktPipelineClearUtil.cpp \
+       external/vulkancts/modules/vulkan/pipeline/vktPipelineCreationCacheControlTests.cpp \
        external/vulkancts/modules/vulkan/pipeline/vktPipelineCreationFeedbackTests.cpp \
        external/vulkancts/modules/vulkan/pipeline/vktPipelineDepthRangeUnrestrictedTests.cpp \
        external/vulkancts/modules/vulkan/pipeline/vktPipelineDepthTests.cpp \
index 9ad4b7f..0e30398 100644 (file)
@@ -148533,6 +148533,22 @@ dEQP-VK.pipeline.extended_dynamic_state.two_draws_static.stencil_state_face_both
 dEQP-VK.pipeline.extended_dynamic_state.two_draws_static.stencil_state_face_both_dual_xt_dec_wrap_clear_0_ref_0_depthfail
 dEQP-VK.pipeline.extended_dynamic_state.two_draws_static.stencil_state_face_both_dual_xt_dec_wrap_clear_0_ref_1_pass
 dEQP-VK.pipeline.extended_dynamic_state.two_draws_static.stencil_state_face_both_dual_xt_dec_wrap_clear_0_ref_1_depthfail
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.single_pipeline_no_compile
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.batch_pipelines_early_return
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_single_recreate_explicit_caching
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_single_recreate_no_caching
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_single_recreate_derivative
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_batch_pipelines_explicit_cache
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_batch_pipelines_no_cache
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_batch_pipelines_derivative_index
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.single_pipeline_no_compile
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.batch_pipelines_early_return
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_single_recreate_explicit_caching
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_single_recreate_no_caching
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_single_recreate_derivative
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_batch_pipelines_explicit_cache
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_batch_pipelines_no_cache
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_batch_pipelines_derivative_index
 dEQP-VK.binding_model.descriptor_update.samplerless.sampled_img_sampler_zero
 dEQP-VK.binding_model.descriptor_update.samplerless.sampled_img_sampler_one
 dEQP-VK.binding_model.descriptor_update.samplerless.sampled_img_sampler_destroyed
index ccf56ef..1d6a2dc 100644 (file)
@@ -325546,6 +325546,22 @@ dEQP-VK.pipeline.extended_dynamic_state.two_draws_static.stencil_state_face_both
 dEQP-VK.pipeline.extended_dynamic_state.two_draws_static.stencil_state_face_both_dual_xt_dec_wrap_clear_0_ref_0_depthfail
 dEQP-VK.pipeline.extended_dynamic_state.two_draws_static.stencil_state_face_both_dual_xt_dec_wrap_clear_0_ref_1_pass
 dEQP-VK.pipeline.extended_dynamic_state.two_draws_static.stencil_state_face_both_dual_xt_dec_wrap_clear_0_ref_1_depthfail
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.single_pipeline_no_compile
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.batch_pipelines_early_return
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_single_recreate_explicit_caching
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_single_recreate_no_caching
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_single_recreate_derivative
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_batch_pipelines_explicit_cache
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_batch_pipelines_no_cache
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_batch_pipelines_derivative_index
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.single_pipeline_no_compile
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.batch_pipelines_early_return
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_single_recreate_explicit_caching
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_single_recreate_no_caching
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_single_recreate_derivative
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_batch_pipelines_explicit_cache
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_batch_pipelines_no_cache
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_batch_pipelines_derivative_index
 dEQP-VK.binding_model.shader_access.primary_cmd_buf.sampler_mutable.no_access.single_descriptor.1d
 dEQP-VK.binding_model.shader_access.primary_cmd_buf.sampler_mutable.no_access.single_descriptor.1d_base_mip
 dEQP-VK.binding_model.shader_access.primary_cmd_buf.sampler_mutable.no_access.single_descriptor.1d_base_slice
index 7a71a92..0c76ee5 100644 (file)
@@ -176,6 +176,16 @@ bool checkMandatoryFeatures(const vkt::Context& context)
                nextPtr  = &physicalDevicePerformanceQueryFeaturesKHR.pNext;
        }
 
+       vk::VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT physicalDevicePipelineCreationCacheControlFeaturesEXT;
+       deMemset(&physicalDevicePipelineCreationCacheControlFeaturesEXT, 0, sizeof(physicalDevicePipelineCreationCacheControlFeaturesEXT));
+
+       if ( isExtensionSupported(deviceExtensions, RequiredExtension("VK_EXT_pipeline_creation_cache_control")) )
+       {
+               physicalDevicePipelineCreationCacheControlFeaturesEXT.sType = getStructureType<VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT>();
+               *nextPtr = &physicalDevicePipelineCreationCacheControlFeaturesEXT;
+               nextPtr  = &physicalDevicePipelineCreationCacheControlFeaturesEXT.pNext;
+       }
+
        vk::VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR physicalDevicePipelineExecutablePropertiesFeaturesKHR;
        deMemset(&physicalDevicePipelineExecutablePropertiesFeaturesKHR, 0, sizeof(physicalDevicePipelineExecutablePropertiesFeaturesKHR));
 
@@ -723,6 +733,15 @@ bool checkMandatoryFeatures(const vkt::Context& context)
                }
        }
 
+       if ( isExtensionSupported(deviceExtensions, RequiredExtension("VK_EXT_pipeline_creation_cache_control")) )
+       {
+               if ( physicalDevicePipelineCreationCacheControlFeaturesEXT.pipelineCreationCacheControl == VK_FALSE )
+               {
+                       log << tcu::TestLog::Message << "Mandatory feature pipelineCreationCacheControl not supported" << tcu::TestLog::EndMessage;
+                       result = false;
+               }
+       }
+
        if ( context.contextSupports(vk::ApiVersion(1, 2, 0)) )
        {
                if ( physicalDeviceVulkan12Features.separateDepthStencilLayouts == VK_FALSE )
index 2df5782..c239120 100644 (file)
@@ -1,9 +1,4 @@
 
-include_directories(
-       ..
-       ../amber
-       )
-
 set(DEQP_VK_PIPELINE_SRCS
        vktPipelineBlendTests.cpp
        vktPipelineBlendTests.hpp
@@ -101,6 +96,8 @@ set(DEQP_VK_PIPELINE_SRCS
        vktPipelineBlendOperationAdvancedTests.hpp
        vktPipelineExtendedDynamicStateTests.cpp
        vktPipelineExtendedDynamicStateTests.hpp
+       vktPipelineCreationCacheControlTests.cpp
+       vktPipelineCreationCacheControlTests.hpp
        )
 
 set(DEQP_VK_PIPELINE_LIBS
@@ -111,8 +108,13 @@ set(DEQP_VK_PIPELINE_LIBS
        )
 
 PCH(DEQP_VK_PIPELINE_SRCS ../pch.cpp)
-include_directories("../../../../amber/src/include")
-include_directories("../amber")
 
 add_library(deqp-vk-pipeline STATIC ${DEQP_VK_PIPELINE_SRCS})
 target_link_libraries(deqp-vk-pipeline ${DEQP_VK_PIPELINE_LIBS})
+target_include_directories(deqp-vk-pipeline
+       PRIVATE
+               ..
+               ../../../../amber/src/include
+               ../amber
+               ../util
+)
\ No newline at end of file
diff --git a/external/vulkancts/modules/vulkan/pipeline/vktPipelineCreationCacheControlTests.cpp b/external/vulkancts/modules/vulkan/pipeline/vktPipelineCreationCacheControlTests.cpp
new file mode 100644 (file)
index 0000000..5289088
--- /dev/null
@@ -0,0 +1,1412 @@
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2020 The Khronos Group Inc.
+ * Copyright (c) 2020 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+/*!
+ * \file
+ * \brief Pipeline Cache Tests
+ */
+/*--------------------------------------------------------------------*/
+
+#include "vktPipelineCreationCacheControlTests.hpp"
+
+#include "deRandom.hpp"
+#include "deUniquePtr.hpp"
+#include "tcuStringTemplate.hpp"
+#include "vkDeviceUtil.hpp"
+#include "vkRefUtil.hpp"
+#include "vktConstexprVectorUtil.hpp"
+#include "vktTestCase.hpp"
+#include "vktTestCaseUtil.hpp"
+
+#include <chrono>
+#include <random>
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace pipeline
+{
+namespace
+{
+using namespace vk;
+
+using tcu::StringTemplate;
+using tcu::TestCaseGroup;
+using tcu::TestContext;
+using tcu::TestStatus;
+
+using ::std::array;
+using ::std::string;
+using ::std::vector;
+
+/*--------------------------------------------------------------------*//*!
+ * Elements common to all test types
+ *//*--------------------------------------------------------------------*/
+namespace test_common
+{
+static constexpr auto VK_NULL_HANDLE = DE_NULL;
+
+using ::std::chrono::high_resolution_clock;
+using ::std::chrono::microseconds;
+
+using duration                  = high_resolution_clock::duration;
+using UniquePipeline    = Move<VkPipeline>;
+using UniqueShaderModule = Move<VkShaderModule>;
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Paired Vulkan API result with elapsed duration
+ *//*--------------------------------------------------------------------*/
+struct TimedResult
+{
+       VkResult result;
+       duration elapsed;
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Validation function type output from vkCreate*Pipelines()
+ *
+ * \param result - VkResult returned from API call
+ * \param pipeliens - vector of pipelines created
+ * \param elapsed - high_resolution_clock::duration of time elapsed in API
+ * \param reason - output string to give the reason for failure
+ *
+ * \return QP_TEST_RESULT_PASS on success QP_TEST_RESULT_FAIL otherwise
+ *//*--------------------------------------------------------------------*/
+using Validator = qpTestResult (*)(VkResult, const vector<UniquePipeline>&, duration, string&);
+
+static constexpr size_t VALIDATOR_ARRAY_MAX = 4;
+using ValidatorArray                                           = ConstexprVector<Validator, VALIDATOR_ARRAY_MAX>;
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Run a loop of validation tests and return the result
+ *//*--------------------------------------------------------------------*/
+template <typename pipelines_t, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL>
+TestStatus validateResults(VkResult                             result,
+                                                  const pipelines_t&    pipelines,
+                                                  duration                              elapsed,
+                                                  const ValidatorArray& validators)
+{
+       using de::contains;
+       static constexpr VkResult ALLOWED_RESULTS[] = {VK_SUCCESS, VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT};
+
+       string reason;
+
+       if (contains(DE_ARRAY_BEGIN(ALLOWED_RESULTS), DE_ARRAY_END(ALLOWED_RESULTS), result) == DE_FALSE)
+       {
+               static const StringTemplate ERROR_MSG = {"Pipeline creation returned an error result: ${0}"};
+               TCU_THROW(InternalError, ERROR_MSG.format(result).c_str());
+       }
+
+       for (const auto& validator : validators)
+       {
+               const auto qpResult = validator(result, pipelines, elapsed, reason);
+               if (qpResult != QP_TEST_RESULT_PASS)
+               {
+                       return {qpResult, reason};
+               }
+       }
+
+       return TestStatus::pass("Test passed.");
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Generate an error if result does not match VK_RESULT
+ *//*--------------------------------------------------------------------*/
+template <VkResult VK_RESULT, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL>
+qpTestResult checkResult(VkResult result, const vector<UniquePipeline>&, duration, string& reason)
+{
+       if (VK_RESULT != result)
+       {
+               static const StringTemplate ERROR_MSG = {"Got ${0}, Expected ${1}"};
+               reason                                                            = ERROR_MSG.format(result, VK_RESULT);
+               return FAIL_RESULT;
+       }
+
+       return QP_TEST_RESULT_PASS;
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Generate an error if pipeline[INDEX] is not valid
+ *//*--------------------------------------------------------------------*/
+template <size_t INDEX, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL>
+qpTestResult checkPipelineMustBeValid(VkResult, const vector<UniquePipeline>& pipelines, duration, string& reason)
+{
+       if (pipelines.size() <= INDEX)
+       {
+               static const StringTemplate ERROR_MSG = {"Index ${0} is not in created pipelines (pipelines.size(): ${1})"};
+               TCU_THROW(TestError, ERROR_MSG.format(INDEX, pipelines.size()));
+       }
+
+       if (*pipelines[INDEX] == VK_NULL_HANDLE)
+       {
+               static const StringTemplate ERROR_MSG = {"pipelines[${0}] is not a valid VkPipeline object"};
+               reason                                                            = ERROR_MSG.format(INDEX);
+               return FAIL_RESULT;
+       }
+
+       return QP_TEST_RESULT_PASS;
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Generate an error if pipeline[INDEX] is not VK_NULL_HANDLE
+ *//*--------------------------------------------------------------------*/
+template <size_t INDEX, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL>
+qpTestResult checkPipelineMustBeNull(VkResult, const vector<UniquePipeline>& pipelines, duration, string& reason)
+{
+       if (pipelines.size() <= INDEX)
+       {
+               static const StringTemplate ERROR_MSG = {"Index ${0} is not in created pipelines (pipelines.size(): ${1})"};
+               TCU_THROW(TestError, ERROR_MSG.format(INDEX, pipelines.size()));
+       }
+
+       if (*pipelines[INDEX] != VK_NULL_HANDLE)
+       {
+               static const StringTemplate ERROR_MSG = {"pipelines[${0}] is not VK_NULL_HANDLE"};
+               reason                                                            = ERROR_MSG.format(INDEX);
+               return FAIL_RESULT;
+       }
+
+       return QP_TEST_RESULT_PASS;
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Generate an error if any pipeline is valid after an early-return failure
+ *//*--------------------------------------------------------------------*/
+template <size_t INDEX, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL>
+qpTestResult checkPipelineNullAfterIndex(VkResult, const vector<UniquePipeline>& pipelines, duration, string& reason)
+{
+       if (pipelines.size() <= INDEX)
+       {
+               static const StringTemplate ERROR_MSG = {"Index ${0} is not in created pipelines (pipelines.size(): ${1})"};
+               TCU_THROW(TestError, ERROR_MSG.format(INDEX, pipelines.size()));
+       }
+
+       if (pipelines.size() - 1 == INDEX)
+       {
+               static const StringTemplate ERROR_MSG = {"Index ${0} is the last pipeline, likely a malformed test case"};
+               TCU_THROW(TestError, ERROR_MSG.format(INDEX));
+       }
+
+       // Only have to iterate through if the requested index is null
+       if (*pipelines[INDEX] == VK_NULL_HANDLE)
+       {
+               for (size_t i = INDEX + 1; i < pipelines.size(); ++i)
+               {
+                       if (*pipelines[i] != VK_NULL_HANDLE)
+                       {
+                               static const StringTemplate ERROR_MSG = {
+                                       "pipelines[${0}] is not VK_NULL_HANDLE after a explicit early return index"};
+                               reason = ERROR_MSG.format(i);
+                               return FAIL_RESULT;
+                       }
+               }
+       }
+
+       return QP_TEST_RESULT_PASS;
+}
+
+/*--------------------------------------------------------------------*//*!
+ * Time limit constants
+ *//*--------------------------------------------------------------------*/
+enum ElapsedTime
+{
+       ELAPSED_TIME_INFINITE  = microseconds{-1}.count(),
+       ELAPSED_TIME_IMMEDIATE = microseconds{500}.count(),
+       ELAPSED_TIME_FAST          = microseconds{1000}.count()
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Generate an error if elapsed time exceeds MAX_TIME
+ *//*--------------------------------------------------------------------*/
+template <ElapsedTime MAX_TIME, qpTestResult FAIL_RESULT = QP_TEST_RESULT_FAIL>
+qpTestResult checkElapsedTime(VkResult, const vector<UniquePipeline>&, duration elapsed, string& reason)
+{
+#if defined(DE_DEBUG)
+       DE_UNREF(elapsed);
+       DE_UNREF(reason);
+
+       // In debug mode timing is not likely to be accurate
+       return QP_TEST_RESULT_PASS;
+#else
+
+       using ::std::chrono::duration_cast;
+
+       static constexpr microseconds ALLOWED_TIME = microseconds{MAX_TIME};
+
+       if (elapsed > ALLOWED_TIME)
+       {
+               static const StringTemplate ERROR_MSG = {"pipeline creation took longer than ${0}us (actual time: ${1}us)"};
+               reason = ERROR_MSG.format(ALLOWED_TIME.count(), duration_cast<microseconds>(elapsed).count());
+               return FAIL_RESULT;
+       }
+
+       return QP_TEST_RESULT_PASS;
+#endif
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test case parameters
+ *//*--------------------------------------------------------------------*/
+struct TestParams
+{
+       enum CacheType
+       {
+               NO_CACHE = 0,
+               EXPLICIT_CACHE,
+               DERIVATIVE_HANDLE,
+               DERIVATIVE_INDEX
+       };
+
+       struct Iteration
+       {
+               static constexpr size_t MAX_VARIANTS = 4;
+               using Variant                                            = VkPipelineCreateFlags;
+               using VariantArray                                       = ConstexprVector<Variant, MAX_VARIANTS>;
+
+               static constexpr Variant NORMAL           = 0;
+               static constexpr Variant NO_COMPILE       = VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT;
+               static constexpr Variant EARLY_RETURN = NO_COMPILE | VK_PIPELINE_CREATE_EARLY_RETURN_ON_FAILURE_BIT_EXT;
+
+               static constexpr VariantArray SINGLE_NORMAL                                             = VariantArray{NORMAL};
+               static constexpr VariantArray SINGLE_NOCOMPILE                                  = VariantArray{NO_COMPILE};
+               static constexpr VariantArray BATCH_NOCOMPILE_COMPILE_NOCOMPILE = VariantArray{NO_COMPILE, NORMAL, NO_COMPILE};
+               static constexpr VariantArray BATCH_RETURN_COMPILE_NOCOMPILE = VariantArray{EARLY_RETURN, NORMAL, NO_COMPILE};
+
+               inline constexpr Iteration() : variants{}, validators{} {};
+               inline constexpr Iteration(const VariantArray& v, const ValidatorArray& f) : variants{v}, validators{f} {};
+
+               VariantArray   variants;
+               ValidatorArray validators;
+       };
+
+       static constexpr size_t MAX_ITERATIONS = 4;
+       using IterationArray                               = ConstexprVector<Iteration, MAX_ITERATIONS>;
+
+       const char*        name;
+       const char*        description;
+       CacheType          cacheType;
+       IterationArray iterations;
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Verify extension and feature support
+ *//*--------------------------------------------------------------------*/
+void checkSupport(Context& context, const TestParams&)
+{
+       static constexpr char EXT_NAME[] = "VK_EXT_pipeline_creation_cache_control";
+       if (!context.requireDeviceFunctionality(EXT_NAME))
+       {
+               TCU_THROW(NotSupportedError, "Extension 'VK_EXT_pipeline_creation_cache_control' is not supported");
+       }
+
+       const auto features = context.getPipelineCreationCacheControlFeaturesEXT();
+       if (features.pipelineCreationCacheControl == DE_FALSE)
+       {
+               TCU_THROW(NotSupportedError, "Feature 'pipelineCreationCacheControl' is not enabled");
+       }
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Generate a random floating point number as a string
+ *//*--------------------------------------------------------------------*/
+float randomFloat()
+{
+#if !defined(DE_DEBUG)
+       static de::Random state = {::std::random_device{}()};
+#else
+       static de::Random state = {0xDEADBEEF};
+#endif
+
+       return state.getFloat();
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Get a string of VkResults from a vector
+ *//*--------------------------------------------------------------------*/
+string getResultsString(const vector<VkResult>& results)
+{
+       using ::std::ostringstream;
+
+       ostringstream output;
+
+       output << "results[" << results.size() << "]={ ";
+
+       if (!results.empty())
+       {
+               output << results[0];
+       }
+
+       for (size_t i = 1; i < results.size(); ++i)
+       {
+               output << ", " << results[i];
+       }
+
+       output << " }";
+
+       return output.str();
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Cast a pointer to an the expected SPIRV type
+ *//*--------------------------------------------------------------------*/
+template <typename _t>
+inline const deUint32* shader_cast(const _t* ptr)
+{
+       return reinterpret_cast<const deUint32*>(ptr);
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Capture a container of Vulkan handles into Move<> types
+ *//*--------------------------------------------------------------------*/
+template <typename input_container_t,
+                 typename handle_t      = typename input_container_t::value_type,
+                 typename move_t        = Move<handle_t>,
+                 typename deleter_t = Deleter<handle_t>,
+                 typename output_t      = vector<move_t>>
+output_t wrapHandles(const DeviceInterface&              vk,
+                                        VkDevice                                         device,
+                                        const input_container_t&         input,
+                                        const VkAllocationCallbacks* allocator = DE_NULL)
+{
+       using ::std::begin;
+       using ::std::end;
+       using ::std::transform;
+
+       auto output = output_t{};
+       output.resize(input.size());
+
+       struct Predicate
+       {
+               deleter_t deleter;
+               move_t    operator()(handle_t v)
+               {
+                       return (v != VK_NULL_HANDLE) ? move_t{check(v), deleter} : move_t{};
+               }
+       };
+
+       const auto wrapHandle = Predicate{deleter_t{vk, device, allocator}};
+
+       transform(begin(input), end(input), begin(output), wrapHandle);
+
+       return output;
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief create vkPipelineCache for test params
+ *//*--------------------------------------------------------------------*/
+Move<VkPipelineCache> createPipelineCache(const DeviceInterface& vk, VkDevice device, const TestParams& params)
+{
+       if (params.cacheType != TestParams::EXPLICIT_CACHE)
+       {
+               return {};
+       }
+
+       static constexpr auto cacheInfo = VkPipelineCacheCreateInfo{
+               VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, //sType
+               DE_NULL,                                                                          //pNext
+               VkPipelineCacheCreateFlags{},                             //flags
+               deUintptr{0},                                                             //initialDataSize
+               DE_NULL                                                                           //pInitialData
+       };
+
+       return createPipelineCache(vk, device, &cacheInfo);
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief create VkPipelineLayout with descriptor sets from test parameters
+ *//*--------------------------------------------------------------------*/
+Move<VkPipelineLayout> createPipelineLayout(const DeviceInterface&                              vk,
+                                                                                       VkDevice                                                         device,
+                                                                                       const vector<VkDescriptorSetLayout>& setLayouts,
+                                                                                       const TestParams&)
+{
+       const auto layoutCreateInfo = VkPipelineLayoutCreateInfo{
+               VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // sType
+               DE_NULL,                                                                           // pNext
+               VkPipelineLayoutCreateFlags{},                             // flags
+               static_cast<deUint32>(setLayouts.size()),          // setLayoutCount
+               setLayouts.data(),                                                         // pSetLayouts
+               deUint32{0u},                                                              // pushConstantRangeCount
+               DE_NULL,                                                                           // pPushConstantRanges
+       };
+
+       return createPipelineLayout(vk, device, &layoutCreateInfo);
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief create basic VkPipelineLayout from test parameters
+ *//*--------------------------------------------------------------------*/
+Move<VkPipelineLayout> createPipelineLayout(const DeviceInterface& vk, VkDevice device, const TestParams&)
+{
+       static constexpr auto layoutCreateInfo = VkPipelineLayoutCreateInfo{
+               VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // sType
+               DE_NULL,                                                                           // pNext
+               VkPipelineLayoutCreateFlags{},                             // flags
+               deUint32{0u},                                                              // setLayoutCount
+               DE_NULL,                                                                           // pSetLayouts
+               deUint32{0u},                                                              // pushConstantRangeCount
+               DE_NULL,                                                                           // pPushConstantRanges
+       };
+
+       return createPipelineLayout(vk, device, &layoutCreateInfo);
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Create array of shader modules
+ *//*--------------------------------------------------------------------*/
+vector<UniqueShaderModule> createShaderModules(const DeviceInterface&    vk,
+                                                                                          VkDevice                                       device,
+                                                                                          const BinaryCollection&        collection,
+                                                                                          const vector<const char*>& names)
+{
+       auto output = vector<UniqueShaderModule>{};
+       output.reserve(names.size());
+
+       for (const auto& name : names)
+       {
+               const auto& binary         = collection.get(name);
+               const auto      createInfo = VkShaderModuleCreateInfo{
+                        VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, // sType
+                        DE_NULL,                                                                         // pNext
+                        VkShaderModuleCreateFlags{},                             // flags
+                        binary.getSize(),                                                        // codeSize
+                        shader_cast(binary.getBinary())                          // pCode
+                };
+
+               output.push_back(createShaderModule(vk, device, &createInfo));
+       }
+
+       return output;
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Create array of shader binding stages
+ *//*--------------------------------------------------------------------*/
+vector<VkPipelineShaderStageCreateInfo> createShaderStages(const vector<Move<VkShaderModule>>& modules,
+                                                                                                                  const vector<VkShaderStageFlagBits>& stages)
+{
+       DE_ASSERT(modules.size() == stages.size());
+
+       auto output = vector<VkPipelineShaderStageCreateInfo>{};
+       output.reserve(modules.size());
+
+       int i = 0;
+
+       for (const auto& module : modules)
+       {
+               const auto stageInfo = VkPipelineShaderStageCreateInfo{
+                       VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // sType
+                       DE_NULL,                                                                                         // pNext
+                       VkPipelineShaderStageCreateFlags{},                                      // flags
+                       stages[i++],                                                                             // stage
+                       *module,                                                                                         // module
+                       "main",                                                                                          // pName
+                       DE_NULL                                                                                          // pSpecializationInfo
+               };
+
+               output.push_back(stageInfo);
+       }
+
+       return output;
+}
+
+} // namespace test_common
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Graphics pipeline specific testing
+ *//*--------------------------------------------------------------------*/
+namespace graphics_tests
+{
+using namespace test_common;
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Common graphics pipeline create info initialization
+ *//*--------------------------------------------------------------------*/
+VkGraphicsPipelineCreateInfo getPipelineCreateInfoCommon()
+{
+       static constexpr auto VERTEX_BINDING = VkVertexInputBindingDescription{
+               deUint32{0u},                           // binding
+               sizeof(float[4]),                       // stride
+               VK_VERTEX_INPUT_RATE_VERTEX // inputRate
+       };
+
+       static constexpr auto VERTEX_ATTRIBUTE = VkVertexInputAttributeDescription{
+               deUint32{0u},                              // location
+               deUint32{0u},                              // binding
+               VK_FORMAT_R32G32B32A32_SFLOAT, // format
+               deUint32{0u}                               // offset
+       };
+
+       static constexpr auto VERTEX_INPUT_STATE = VkPipelineVertexInputStateCreateInfo{
+               VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // sType
+               DE_NULL,                                                                                                   // pNext
+               VkPipelineVertexInputStateCreateFlags{},                                   // flags
+               deUint32{1u},                                                                                      // vertexBindingDescriptionCount
+               &VERTEX_BINDING,                                                                                   // pVertexBindingDescriptions
+               deUint32{1u},                                                                                      // vertexAttributeDescriptionCount
+               &VERTEX_ATTRIBUTE                                                                                  // pVertexAttributeDescriptions
+       };
+
+       static constexpr auto IA_STATE = VkPipelineInputAssemblyStateCreateInfo{
+               VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // sType
+               DE_NULL,                                                                                                         // pNext
+               VkPipelineInputAssemblyStateCreateFlags{},                                       // flags
+               VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,                                             // topology
+               VK_TRUE                                                                                                          // primitiveRestartEnable
+       };
+
+       static constexpr auto TESSALATION_STATE = VkPipelineTessellationStateCreateInfo{
+               VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO, // sType
+               DE_NULL,                                                                                                                                 // pNext
+               VkPipelineTessellationStateCreateFlags{},                                                                // flags
+               deUint32{0u}                                                                                                                     // patchControlPoints
+       };
+
+       static constexpr auto VIEWPORT = VkViewport{
+               0.f, // x
+               0.f, // y
+               1.f, // width
+               1.f, // height
+               0.f, // minDepth
+               1.f      // maxDept
+       };
+
+       static constexpr auto SCISSOR_RECT = VkRect2D{
+               {0, 0},    // offset
+               {256, 256} // extent
+       };
+
+       static constexpr auto VIEWPORT_STATE = VkPipelineViewportStateCreateInfo{
+               VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // sType
+               DE_NULL,                                                                                           // pNext
+               VkPipelineViewportStateCreateFlags{},                              // flags
+               deUint32{1u},                                                                              // viewportCount
+               &VIEWPORT,                                                                                         // pViewports
+               deUint32{1u},                                                                              // scissorCount
+               &SCISSOR_RECT                                                                              // pScissors
+       };
+
+       static constexpr auto RASTERIZATION_STATE = VkPipelineRasterizationStateCreateInfo{
+               VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // sType
+               DE_NULL,                                                                                                        // pNext
+               VkPipelineRasterizationStateCreateFlags{},                                      // flags
+               VK_TRUE,                                                                                                        // depthClampEnable
+               VK_TRUE,                                                                                                        // rasterizerDiscardEnable
+               VK_POLYGON_MODE_FILL,                                                                           // polygonMode
+               VK_CULL_MODE_NONE,                                                                                      // cullMode
+               VK_FRONT_FACE_CLOCKWISE,                                                                        // frontFace
+               VK_FALSE,                                                                                                       // depthBiasEnable
+               0.f,                                                                                                            // depthBiasConstantFactor
+               0.f,                                                                                                            // depthBiasClamp
+               0.f,                                                                                                            // depthBiasSlopeFactor
+               1.f                                                                                                                     // lineWidth
+       };
+
+       static constexpr auto SAMPLE_MASK = VkSampleMask{};
+
+       static constexpr auto MULTISAMPLE_STATE = VkPipelineMultisampleStateCreateInfo{
+               VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // sType
+               DE_NULL,                                                                                                  // pNext
+               VkPipelineMultisampleStateCreateFlags{},                                  // flags
+               VK_SAMPLE_COUNT_1_BIT,                                                                    // rasterizationSamples
+               VK_FALSE,                                                                                                 // sampleShadingEnable
+               0.f,                                                                                                      // minSampleShading
+               &SAMPLE_MASK,                                                                                     // pSampleMask
+               VK_FALSE,                                                                                                 // alphaToCoverageEnable
+               VK_FALSE                                                                                                  // alphaToOneEnable
+       };
+
+       static constexpr auto STENCIL_OP_STATE = VkStencilOpState{
+               VK_STENCIL_OP_ZERO,       // failOp
+               VK_STENCIL_OP_ZERO,       // passOp
+               VK_STENCIL_OP_ZERO,       // depthFailOp
+               VK_COMPARE_OP_ALWAYS, // compareOp
+               deUint32{0u},             // compareMask
+               deUint32{0u},             // writeMask
+               deUint32{0u}              // reference
+       };
+
+       static constexpr auto DEPTH_STENCIL_STATE = VkPipelineDepthStencilStateCreateInfo{
+               VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // sType
+               DE_NULL,                                                                                                        // pNext
+               VkPipelineDepthStencilStateCreateFlags{},                                       // flags
+               VK_FALSE,                                                                                                       // depthTestEnable
+               VK_FALSE,                                                                                                       // depthWriteEnable
+               VK_COMPARE_OP_ALWAYS,                                                                           // depthCompareOp
+               VK_FALSE,                                                                                                       // depthBoundsTestEnable
+               VK_FALSE,                                                                                                       // stencilTestEnable
+               STENCIL_OP_STATE,                                                                                       // front
+               STENCIL_OP_STATE,                                                                                       // back
+               0.f,                                                                                                            // minDepthBounds
+               1.f                                                                                                                     // maxDepthBounds
+       };
+
+       static constexpr auto COLOR_FLAGS_ALL = VkColorComponentFlags{VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
+                                                                                                                                 VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT};
+
+       static constexpr auto COLOR_BLEND_ATTACH_STATE = VkPipelineColorBlendAttachmentState{
+               VK_FALSE,                         // blendEnable
+               VK_BLEND_FACTOR_ONE,  // srcColorBlendFactor
+               VK_BLEND_FACTOR_ZERO, // dstColorBlendFactor
+               VK_BLEND_OP_ADD,          // colorBlendOp
+               VK_BLEND_FACTOR_ONE,  // srcAlphaBlendFactor
+               VK_BLEND_FACTOR_ZERO, // dstAlphaBlendFactor
+               VK_BLEND_OP_ADD,          // alphaBlendOp
+               COLOR_FLAGS_ALL           // colorWriteMask
+       };
+
+       static constexpr auto COLOR_BLEND_STATE = VkPipelineColorBlendStateCreateInfo{
+               VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // sType
+               DE_NULL,                                                                                                  // pNext
+               VkPipelineColorBlendStateCreateFlags{},                                   // flags
+               VK_FALSE,                                                                                                 // logicOpEnable
+               VK_LOGIC_OP_SET,                                                                                  // logicOp
+               deUint32{1u},                                                                                     // attachmentCount
+               &COLOR_BLEND_ATTACH_STATE,                                                                // pAttachments
+               {0.f, 0.f, 0.f, 0.f}                                                                      // blendConstants[4]
+       };
+
+       static constexpr auto DYNAMIC_STATE = VkPipelineDynamicStateCreateInfo{
+               VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // sType;
+               DE_NULL,                                                                                          // pNext;
+               VkPipelineDynamicStateCreateFlags{},                              // flags;
+               deUint32{0u},                                                                             // dynamicStateCount;
+               DE_NULL                                                                                           // pDynamicStates;
+       };
+
+       return VkGraphicsPipelineCreateInfo{
+               VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, // sType
+               DE_NULL,                                                                                 // pNext
+               VkPipelineCreateFlags{},                                                 // flags
+               deUint32{0u},                                                                    // stageCount
+               DE_NULL,                                                                                 // pStages
+               &VERTEX_INPUT_STATE,                                                     // pVertexInputState
+               &IA_STATE,                                                                               // pInputAssemblyState
+               &TESSALATION_STATE,                                                              // pTessellationState
+               &VIEWPORT_STATE,                                                                 // pViewportState
+               &RASTERIZATION_STATE,                                                    // pRasterizationState
+               &MULTISAMPLE_STATE,                                                              // pMultisampleState
+               &DEPTH_STENCIL_STATE,                                                    // pDepthStencilState
+               &COLOR_BLEND_STATE,                                                              // pColorBlendState
+               &DYNAMIC_STATE,                                                                  // pDynamicState
+               VK_NULL_HANDLE,                                                                  // layout
+               VK_NULL_HANDLE,                                                                  // renderPass
+               deUint32{0u},                                                                    // subpass
+               VK_NULL_HANDLE,                                                                  // basePipelineHandle
+               deInt32{-1}                                                                              // basePipelineIndex
+       };
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief create VkGraphicsPipelineCreateInfo structs from test iteration
+ *//*--------------------------------------------------------------------*/
+vector<VkGraphicsPipelineCreateInfo> createPipelineCreateInfos(const TestParams::Iteration&               iteration,
+                                                                                                                          const VkGraphicsPipelineCreateInfo& base,
+                                                                                                                          VkPipeline                                              basePipeline,
+                                                                                                                          const TestParams&                               testParameter)
+{
+       auto output = vector<VkGraphicsPipelineCreateInfo>{};
+       output.reserve(iteration.variants.size());
+
+       deInt32 count                     = 0;
+       deInt32 basePipelineIndex = -1;
+
+       for (VkPipelineCreateFlags flags : iteration.variants)
+       {
+               const auto curIndex       = count++;
+               auto       createInfo = base;
+
+               if (testParameter.cacheType == TestParams::DERIVATIVE_INDEX)
+               {
+                       if (flags & VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT)
+                       {
+                               if (basePipelineIndex != -1)
+                               {
+                                       flags |= VK_PIPELINE_CREATE_DERIVATIVE_BIT;
+                               }
+                       }
+                       else
+                       {
+                               flags |= VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT;
+
+                               if (basePipelineIndex == -1)
+                               {
+                                       basePipelineIndex = curIndex;
+                               }
+                       }
+               }
+
+               createInfo.flags                          = flags;
+               createInfo.basePipelineHandle = basePipeline;
+               createInfo.basePipelineIndex  = basePipelineIndex;
+
+               output.push_back(createInfo);
+       }
+
+       return output;
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief create VkRenderPass object for Graphics test
+ *//*--------------------------------------------------------------------*/
+Move<VkRenderPass> createRenderPass(const DeviceInterface& vk, VkDevice device, const TestParams&)
+{
+       static constexpr auto COLOR_FORMAT = VK_FORMAT_R8G8B8_UNORM;
+
+       static constexpr auto COLOR_ATTACHMENT_REF = VkAttachmentReference{
+               deUint32{0u},                                                    // attachment
+               VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // layout
+       };
+
+       static constexpr auto SUBPASS = VkSubpassDescription{
+               VkSubpassDescriptionFlags{},     // flags
+               VK_PIPELINE_BIND_POINT_GRAPHICS, // pipelineBindPoint
+               deUint32{0u},                                    // inputAttachmentCount
+               DE_NULL,                                                 // pInputAttachments
+               deUint32{1u},                                    // colorAttachmentCount
+               &COLOR_ATTACHMENT_REF,                   // pColorAttachments
+               DE_NULL,                                                 // pResolveAttachments
+               DE_NULL,                                                 // pDepthStencilAttachment
+               deUint32{0u},                                    // preserveAttachmentCount
+               DE_NULL                                                  // pPreserveAttachments
+       };
+
+       static constexpr auto COLOR_ATTACHMENT = VkAttachmentDescription{
+               VkAttachmentDescriptionFlags{},   // flags
+               COLOR_FORMAT,                                     // format
+               VK_SAMPLE_COUNT_1_BIT,                    // samples
+               VK_ATTACHMENT_LOAD_OP_CLEAR,      // loadOp
+               VK_ATTACHMENT_STORE_OP_STORE,     // storeOp
+               VK_ATTACHMENT_LOAD_OP_DONT_CARE,  // stencilLoadOp
+               VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencilStoreOp
+               VK_IMAGE_LAYOUT_UNDEFINED,                // initialLayout
+               VK_IMAGE_LAYOUT_PRESENT_SRC_KHR   // finalLayout
+       };
+
+       static constexpr auto RENDER_PASS_CREATE_INFO = VkRenderPassCreateInfo{
+               VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // sType
+               DE_NULL,                                                                   // pNext
+               VkRenderPassCreateFlags{},                                 // flags
+               deUint32{1u},                                                      // attachmentCount
+               &COLOR_ATTACHMENT,                                                 // pAttachments
+               deUint32{1u},                                                      // subpassCount
+               &SUBPASS,                                                                  // pSubpasses
+               deUint32{0u},                                                      // dependencyCount
+               DE_NULL                                                                    // pDependencies
+       };
+
+       return createRenderPass(vk, device, &RENDER_PASS_CREATE_INFO);
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Initialize shader programs
+ *//*--------------------------------------------------------------------*/
+void initPrograms(SourceCollections& dst, const TestParams&)
+{
+       using ::glu::FragmentSource;
+       using ::glu::VertexSource;
+
+       // Vertex Shader
+       static const StringTemplate VS_TEXT = {"#version 310 es\n"
+                                                                                  "layout(location = 0) in vec4 position;\n"
+                                                                                  "layout(location = 0) out vec3 vertColor;\n"
+                                                                                  "void main (void)\n"
+                                                                                  "{\n"
+                                                                                  "  gl_Position = position;\n"
+                                                                                  "  vertColor = vec3(${0}, ${1}, ${2});\n"
+                                                                                  "}\n"};
+
+       // Fragment Shader
+       static const StringTemplate FS_TEXT = {"#version 310 es\n"
+                                                                                  "precision highp float;\n"
+                                                                                  "layout(location = 0) in vec3 vertColor;\n"
+                                                                                  "layout(location = 0) out vec4 outColor;\n"
+                                                                                  "void main (void)\n"
+                                                                                  "{\n"
+                                                                                  "  const vec3 fragColor = vec3(${0}, ${1}, ${2});\n"
+                                                                                  "  outColor = vec4((fragColor + vertColor) * 0.5, 1.0);\n"
+                                                                                  "}\n"};
+
+       dst.glslSources.add("vertex") << VertexSource{VS_TEXT.format(randomFloat(), randomFloat(), randomFloat())};
+       dst.glslSources.add("fragment") << FragmentSource{FS_TEXT.format(randomFloat(), randomFloat(), randomFloat())};
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief return both result and elapsed time from pipeline creation
+ *//*--------------------------------------------------------------------*/
+template <typename create_infos_t, typename pipelines_t>
+TimedResult timePipelineCreation(const DeviceInterface&                  vk,
+                                                                const VkDevice                           device,
+                                                                const VkPipelineCache            cache,
+                                                                const create_infos_t&            createInfos,
+                                                                pipelines_t&                             pipelines,
+                                                                const VkAllocationCallbacks* pAllocator = DE_NULL)
+{
+       DE_ASSERT(createInfos.size() <= pipelines.size());
+
+       const auto timeStart = high_resolution_clock::now();
+       const auto result        = vk.createGraphicsPipelines(
+                  device, cache, static_cast<deUint32>(createInfos.size()), createInfos.data(), pAllocator, pipelines.data());
+       const auto elapsed = high_resolution_clock::now() - timeStart;
+       return {result, elapsed};
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test instance function
+ *//*--------------------------------------------------------------------*/
+TestStatus testInstance(Context& context, const TestParams& testParameter)
+{
+       const auto& vk                    = context.getDeviceInterface();
+       const auto      device            = context.getDevice();
+       const auto      pipelineCache = createPipelineCache(vk, device, testParameter);
+       const auto      layout            = createPipelineLayout(vk, device, testParameter);
+       const auto      renderPass        = createRenderPass(vk, device, testParameter);
+       const auto      modules           = createShaderModules(vk, device, context.getBinaryCollection(), {"vertex", "fragment"});
+       const auto      shaderStages  = createShaderStages(modules, {VK_SHADER_STAGE_VERTEX_BIT, VK_SHADER_STAGE_FRAGMENT_BIT});
+
+       // Placeholder for base pipeline if using cacheType == DERIVATIVE_HANDLE
+       auto basePipeline = UniquePipeline{};
+
+       auto baseCreateInfo               = getPipelineCreateInfoCommon();
+       baseCreateInfo.layout     = layout.get();
+       baseCreateInfo.renderPass = renderPass.get();
+       baseCreateInfo.stageCount = static_cast<deUint32>(shaderStages.size());
+       baseCreateInfo.pStages    = shaderStages.data();
+
+       auto results = vector<VkResult>{};
+       results.reserve(testParameter.iterations.size());
+
+       for (const auto& i : testParameter.iterations)
+       {
+               const auto createInfos = createPipelineCreateInfos(i, baseCreateInfo, basePipeline.get(), testParameter);
+               auto       created         = vector<VkPipeline>{};
+               created.resize(createInfos.size());
+
+               const auto timedResult = timePipelineCreation(vk, device, pipelineCache.get(), createInfos, created);
+               auto       pipelines   = wrapHandles(vk, device, created);
+
+               const auto status = validateResults(timedResult.result, pipelines, timedResult.elapsed, i.validators);
+               if (status.getCode() != QP_TEST_RESULT_PASS)
+               {
+                       return status;
+               }
+
+               if ((testParameter.cacheType == TestParams::DERIVATIVE_HANDLE) && (*basePipeline == VK_NULL_HANDLE))
+               {
+                       for (auto& pipeline : pipelines)
+                       {
+                               if (*pipeline != VK_NULL_HANDLE)
+                               {
+                                       basePipeline = pipeline;
+                                       break;
+                               }
+                       }
+               }
+
+               results.push_back(timedResult.result);
+       }
+
+       static const StringTemplate PASS_MSG = {"Test Passed. ${0}"};
+       return TestStatus::pass(PASS_MSG.format(getResultsString(results)));
+}
+
+} // namespace graphics_tests
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Compute pipeline specific testing
+ *//*--------------------------------------------------------------------*/
+namespace compute_tests
+{
+using namespace test_common;
+
+/*--------------------------------------------------------------------*//*!
+ * \brief create VkComputePipelineCreateInfo structs from test iteration
+ *//*--------------------------------------------------------------------*/
+vector<VkComputePipelineCreateInfo> createPipelineCreateInfos(const TestParams::Iteration&              iteration,
+                                                                                                                         const VkComputePipelineCreateInfo& base,
+                                                                                                                         VkPipeline                                             basePipeline,
+                                                                                                                         const TestParams&                                      testParameter)
+{
+       auto output = vector<VkComputePipelineCreateInfo>{};
+       output.reserve(iteration.variants.size());
+
+       deInt32 count                     = 0;
+       deInt32 basePipelineIndex = -1;
+
+       for (VkPipelineCreateFlags flags : iteration.variants)
+       {
+               const auto curIndex       = count++;
+               auto       createInfo = base;
+
+               if (testParameter.cacheType == TestParams::DERIVATIVE_INDEX)
+               {
+                       if (flags & VK_PIPELINE_CREATE_FAIL_ON_PIPELINE_COMPILE_REQUIRED_BIT_EXT)
+                       {
+                               if (basePipelineIndex != -1)
+                               {
+                                       flags |= VK_PIPELINE_CREATE_DERIVATIVE_BIT;
+                               }
+                       }
+                       else
+                       {
+                               flags |= VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT;
+
+                               if (basePipelineIndex == -1)
+                               {
+                                       basePipelineIndex = curIndex;
+                               }
+                       }
+               }
+
+               createInfo.flags                          = flags;
+               createInfo.basePipelineHandle = basePipeline;
+               createInfo.basePipelineIndex  = basePipelineIndex;
+
+               output.push_back(createInfo);
+       }
+
+       return output;
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief create compute descriptor set layout
+ *//*--------------------------------------------------------------------*/
+Move<VkDescriptorSetLayout> createDescriptorSetLayout(const DeviceInterface& vk, VkDevice device, const TestParams&)
+{
+       static constexpr auto DESCRIPTOR_SET_LAYOUT_BINDING = VkDescriptorSetLayoutBinding{
+               deUint32{0u},                                      // binding
+               VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // descriptorType
+               deUint32{1u},                                      // descriptorCount
+               VK_SHADER_STAGE_COMPUTE_BIT,       // stageFlags
+               DE_NULL                                                    // pImmutableSamplers
+       };
+
+       static constexpr auto DESCRIPTOR_SET_LAYOUT_CREATE_INFO = VkDescriptorSetLayoutCreateInfo{
+               VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, // sType
+               DE_NULL,                                                                                         // pNext
+               VkDescriptorSetLayoutCreateFlags{},                                      // flags
+               deUint32{1u},                                                                            // bindingCount
+               &DESCRIPTOR_SET_LAYOUT_BINDING                                           // pBindings
+       };
+
+       return createDescriptorSetLayout(vk, device, &DESCRIPTOR_SET_LAYOUT_CREATE_INFO);
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Initialize shader programs
+ *//*--------------------------------------------------------------------*/
+void initPrograms(SourceCollections& dst, const TestParams&)
+{
+       using ::glu::ComputeSource;
+
+       static const StringTemplate CS_TEXT = {"#version 450\n"
+                                                                                  "precision highp float;\n"
+                                                                                  "layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in;\n"
+                                                                                  "layout (std140, binding = 0) buffer buf { vec3 data[]; };\n"
+                                                                                  "void main (void)\n"
+                                                                                  "{\n"
+                                                                                  "  data[gl_GlobalInvocationID.x] = vec3(${0}, ${1}, ${2});\n"
+                                                                                  "}\n"};
+
+       dst.glslSources.add("compute")
+               << ComputeSource{CS_TEXT.format(randomFloat(), randomFloat(), randomFloat())};
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief return both result and elapsed time from pipeline creation
+ *//*--------------------------------------------------------------------*/
+template <typename create_infos_t, typename pipelines_t>
+TimedResult timePipelineCreation(const DeviceInterface&                  vk,
+                                                                const VkDevice                           device,
+                                                                const VkPipelineCache            cache,
+                                                                const create_infos_t&            createInfos,
+                                                                pipelines_t&                             pipelines,
+                                                                const VkAllocationCallbacks* pAllocator = DE_NULL)
+{
+       DE_ASSERT(createInfos.size() <= pipelines.size());
+
+       const auto timeStart = high_resolution_clock::now();
+       const auto result        = vk.createComputePipelines(
+                  device, cache, static_cast<deUint32>(createInfos.size()), createInfos.data(), pAllocator, pipelines.data());
+       const auto elapsed = high_resolution_clock::now() - timeStart;
+       return {result, elapsed};
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test instance function
+ *//*--------------------------------------------------------------------*/
+TestStatus testInstance(Context& context, const TestParams& testParameter)
+{
+       const auto& vk                                  = context.getDeviceInterface();
+       const auto      device                          = context.getDevice();
+       const auto      pipelineCache           = createPipelineCache(vk, device, testParameter);
+       const auto      descriptorSetLayout = createDescriptorSetLayout(vk, device, testParameter);
+       const auto      pipelineLayout          = createPipelineLayout(vk, device, {descriptorSetLayout.get()}, testParameter);
+       const auto      modules                         = createShaderModules(vk, device, context.getBinaryCollection(), {"compute"});
+       const auto      shaderStages            = createShaderStages(modules, {VK_SHADER_STAGE_COMPUTE_BIT});
+
+       // Placeholder for base pipeline if using cacheType == DERIVATIVE_HANDLE
+       auto basePipeline = UniquePipeline{};
+
+       const auto baseCreateInfo = VkComputePipelineCreateInfo{
+               VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // sType
+               DE_NULL,                                                                                // pNext
+               VkPipelineCreateFlags{},                                                // flags
+               shaderStages[0],                                                                // stage
+               pipelineLayout.get(),                                                   // layout
+               VK_NULL_HANDLE,                                                                 // basePipelineHandle
+               deInt32{-1}                                                                             // basePipelineIndex
+       };
+
+       auto results = vector<VkResult>{};
+       results.reserve(testParameter.iterations.size());
+
+       for (const auto& i : testParameter.iterations)
+       {
+               const auto createInfos = createPipelineCreateInfos(i, baseCreateInfo, basePipeline.get(), testParameter);
+               auto       created         = vector<VkPipeline>{};
+               created.resize(createInfos.size());
+
+               const auto timedResult = timePipelineCreation(vk, device, pipelineCache.get(), createInfos, created);
+               auto       pipelines   = wrapHandles(vk, device, created);
+
+               const auto status = validateResults(timedResult.result, pipelines, timedResult.elapsed, i.validators);
+               if (status.getCode() != QP_TEST_RESULT_PASS)
+               {
+                       return status;
+               }
+
+               if ((testParameter.cacheType == TestParams::DERIVATIVE_HANDLE) && (*basePipeline == VK_NULL_HANDLE))
+               {
+                       for (auto& pipeline : pipelines)
+                       {
+                               if (*pipeline != VK_NULL_HANDLE)
+                               {
+                                       basePipeline = pipeline;
+                                       break;
+                               }
+                       }
+               }
+
+               results.push_back(timedResult.result);
+       }
+
+       static const StringTemplate PASS_MSG = {"Test Passed. ${0}"};
+       return TestStatus::pass(PASS_MSG.format(getResultsString(results)));
+}
+
+} // namespace compute_tests
+
+using namespace test_common;
+
+// Disable formatting on this next block for readability
+// clang-format off
+/*--------------------------------------------------------------------*//*!
+ * \brief Duplicate single pipeline recreation with explicit caching
+ *//*--------------------------------------------------------------------*/
+static constexpr TestParams DUPLICATE_SINGLE_RECREATE_EXPLICIT_CACHING =
+{
+       "duplicate_single_recreate_explicit_caching",
+       "Duplicate single pipeline recreation with explicit caching",
+       TestParams::EXPLICIT_CACHE,
+       TestParams::IterationArray
+       {
+               TestParams::Iteration{
+                       // Iteration [0]: Force compilation of pipeline
+                       TestParams::Iteration::SINGLE_NORMAL,
+                       ValidatorArray{
+                               // Fail if result is not VK_SUCCESS
+                               checkResult<VK_SUCCESS>,
+                               // Fail if pipeline is not valid
+                               checkPipelineMustBeValid<0>
+                       }
+               },
+               TestParams::Iteration{
+                       // Iteration [1]: Request compilation of same pipeline without compile
+                       TestParams::Iteration::SINGLE_NOCOMPILE,
+                       ValidatorArray{
+                               // Warn if result is not VK_SUCCESS
+                               checkResult<VK_SUCCESS, QP_TEST_RESULT_COMPATIBILITY_WARNING>,
+                               // Warn if pipeline is not valid
+                               checkPipelineMustBeValid<0, QP_TEST_RESULT_COMPATIBILITY_WARNING>,
+                               // Warn if pipeline took too long
+                               checkElapsedTime<ELAPSED_TIME_FAST, QP_TEST_RESULT_QUALITY_WARNING>
+                       }
+               }
+       }
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Duplicate single pipeline recreation with no explicit cache
+ *//*--------------------------------------------------------------------*/
+static constexpr TestParams DUPLICATE_SINGLE_RECREATE_NO_CACHING =
+{
+       "duplicate_single_recreate_no_caching",
+       "Duplicate single pipeline recreation with no explicit cache",
+       TestParams::NO_CACHE,
+       TestParams::IterationArray{
+               TestParams::Iteration{
+                       // Iteration [0]: Force compilation of pipeline
+                       TestParams::Iteration::SINGLE_NORMAL,
+                       ValidatorArray{
+                               // Fail if result is not VK_SUCCESS
+                               checkResult<VK_SUCCESS>,
+                               // Fail if pipeline is not valid
+                               checkPipelineMustBeValid<0>
+                       }
+               },
+               TestParams::Iteration{
+                       // Iteration [1]: Request compilation of same pipeline without compile
+                       TestParams::Iteration::SINGLE_NOCOMPILE,
+                       ValidatorArray{
+                               // Warn if pipeline took too long
+                               checkElapsedTime<ELAPSED_TIME_FAST, QP_TEST_RESULT_QUALITY_WARNING>
+                       }
+               }
+       }
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Duplicate single pipeline recreation using derivative pipelines
+ *//*--------------------------------------------------------------------*/
+static constexpr TestParams DUPLICATE_SINGLE_RECREATE_DERIVATIVE =
+{
+       "duplicate_single_recreate_derivative",
+       "Duplicate single pipeline recreation using derivative pipelines",
+       TestParams::DERIVATIVE_HANDLE,
+       TestParams::IterationArray{
+               TestParams::Iteration{
+                       // Iteration [0]: Force compilation of pipeline
+                       TestParams::Iteration::SINGLE_NORMAL,
+                       ValidatorArray{
+                               // Fail if result is not VK_SUCCESS
+                               checkResult<VK_SUCCESS>,
+                               // Fail if pipeline is not valid
+                               checkPipelineMustBeValid<0>
+                               }
+                       },
+               TestParams::Iteration{
+                       // Iteration [1]: Request compilation of same pipeline without compile
+                       TestParams::Iteration::SINGLE_NOCOMPILE,
+                       ValidatorArray{
+                               // Warn if pipeline took too long
+                               checkElapsedTime<ELAPSED_TIME_FAST, QP_TEST_RESULT_QUALITY_WARNING>
+                       }
+               }
+       }
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Single creation of never before seen pipeline without compile
+ *//*--------------------------------------------------------------------*/
+static constexpr TestParams SINGLE_PIPELINE_NO_COMPILE =
+{
+       "single_pipeline_no_compile",
+       "Single creation of never before seen pipeline without compile",
+       TestParams::NO_CACHE,
+       TestParams::IterationArray{
+               TestParams::Iteration{
+                       TestParams::Iteration::SINGLE_NOCOMPILE,
+                       ValidatorArray{
+                               // Warn if pipeline took too long
+                               checkElapsedTime<ELAPSED_TIME_IMMEDIATE, QP_TEST_RESULT_QUALITY_WARNING>
+                       }
+               }
+       }
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Batch creation of duplicate pipelines with explicit caching
+ *//*--------------------------------------------------------------------*/
+static constexpr TestParams DUPLICATE_BATCH_PIPELINES_EXPLICIT_CACHE =
+{
+       "duplicate_batch_pipelines_explicit_cache",
+       "Batch creation of duplicate pipelines with explicit caching",
+       TestParams::EXPLICIT_CACHE,
+       TestParams::IterationArray{
+               TestParams::Iteration{
+                       TestParams::Iteration::BATCH_NOCOMPILE_COMPILE_NOCOMPILE,
+                       ValidatorArray{
+                               // Fail if pipeline[1] is not valid
+                               checkPipelineMustBeValid<1>,
+                               // Warn if result is not VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT
+                               checkResult<VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT, QP_TEST_RESULT_COMPATIBILITY_WARNING>,
+                               // Warn if pipelines[0] is not VK_NULL_HANDLE
+                               checkPipelineMustBeNull<0, QP_TEST_RESULT_COMPATIBILITY_WARNING>,
+                               // Warn if pipelines[2] is not valid
+                               checkPipelineMustBeValid<2, QP_TEST_RESULT_COMPATIBILITY_WARNING>
+                       }
+               }
+       }
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Batch creation of duplicate pipelines with no caching
+ *//*--------------------------------------------------------------------*/
+static constexpr TestParams DUPLICATE_BATCH_PIPELINES_NO_CACHE =
+{
+       "duplicate_batch_pipelines_no_cache",
+       "Batch creation of duplicate pipelines with no caching",
+       TestParams::EXPLICIT_CACHE,
+       TestParams::IterationArray{
+               TestParams::Iteration{
+                       TestParams::Iteration::BATCH_NOCOMPILE_COMPILE_NOCOMPILE,
+                       ValidatorArray{
+                               // Fail if pipeline[1] is not valid
+                               checkPipelineMustBeValid<1>,
+                               // Warn if result is not VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT
+                               checkResult<VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT, QP_TEST_RESULT_COMPATIBILITY_WARNING>,
+                               // Warn if pipelines[0] is not VK_NULL_HANDLE
+                               checkPipelineMustBeNull<0, QP_TEST_RESULT_COMPATIBILITY_WARNING>
+                       }
+               }
+       }
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Batch creation of duplicate pipelines with derivative pipeline index
+ *//*--------------------------------------------------------------------*/
+static constexpr TestParams DUPLICATE_BATCH_PIPELINES_DERIVATIVE_INDEX =
+{
+       "duplicate_batch_pipelines_derivative_index",
+       "Batch creation of duplicate pipelines with derivative pipeline index",
+       TestParams::DERIVATIVE_INDEX,
+       TestParams::IterationArray{
+               TestParams::Iteration{
+                       TestParams::Iteration::BATCH_NOCOMPILE_COMPILE_NOCOMPILE,
+                       ValidatorArray{
+                               // Fail if pipeline[1] is not valid
+                               checkPipelineMustBeValid<1>,
+                               // Warn if result is not VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT
+                               checkResult<VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT, QP_TEST_RESULT_COMPATIBILITY_WARNING>,
+                               // Warn if pipelines[0] is not VK_NULL_HANDLE
+                               checkPipelineMustBeNull<0, QP_TEST_RESULT_COMPATIBILITY_WARNING>
+                       }
+               }
+       }
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Batch creation of pipelines with early return
+ *//*--------------------------------------------------------------------*/
+static constexpr TestParams BATCH_PIPELINES_EARLY_RETURN =
+{
+       "batch_pipelines_early_return",
+       "Batch creation of pipelines with early return",
+       TestParams::NO_CACHE,
+       TestParams::IterationArray{
+               TestParams::Iteration{
+                       TestParams::Iteration::BATCH_RETURN_COMPILE_NOCOMPILE,
+                       ValidatorArray{
+                               // fail if a valid pipeline follows the early-return failure
+                               checkPipelineNullAfterIndex<0>,
+                               // Warn if return was not immediate
+                               checkElapsedTime<ELAPSED_TIME_IMMEDIATE, QP_TEST_RESULT_QUALITY_WARNING>,
+                               // Warn if pipelines[0] is not VK_NULL_HANDLE
+                               checkPipelineMustBeNull<0, QP_TEST_RESULT_COMPATIBILITY_WARNING>,
+                               // Warn if result is not VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT
+                               checkResult<VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT, QP_TEST_RESULT_COMPATIBILITY_WARNING>
+                       }
+               }
+       }
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Full array of test cases
+ *//*--------------------------------------------------------------------*/
+static constexpr TestParams TEST_CASES[] =
+{
+       SINGLE_PIPELINE_NO_COMPILE,
+       BATCH_PIPELINES_EARLY_RETURN,
+       DUPLICATE_SINGLE_RECREATE_EXPLICIT_CACHING,
+       DUPLICATE_SINGLE_RECREATE_NO_CACHING,
+       DUPLICATE_SINGLE_RECREATE_DERIVATIVE,
+       DUPLICATE_BATCH_PIPELINES_EXPLICIT_CACHE,
+       DUPLICATE_BATCH_PIPELINES_NO_CACHE,
+       DUPLICATE_BATCH_PIPELINES_DERIVATIVE_INDEX
+};
+// clang-format on
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Variadic version of de::newMovePtr
+ *//*--------------------------------------------------------------------*/
+template <typename T, typename... args_t>
+inline de::MovePtr<T> newMovePtr(args_t&&... args)
+{
+       return de::MovePtr<T>(new T(::std::forward<args_t>(args)...));
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Make test group consisting of graphics pipeline tests
+ *//*--------------------------------------------------------------------*/
+void addGraphicsPipelineTests(TestCaseGroup& group)
+{
+       using namespace graphics_tests;
+
+       auto tests = newMovePtr<TestCaseGroup>(
+               group.getTestContext(), "graphics_pipelines", "Test pipeline creation cache control with graphics pipelines");
+
+       for (const auto& params : TEST_CASES)
+       {
+               addFunctionCaseWithPrograms<const TestParams&>(
+                       tests.get(), params.name, params.description, checkSupport, initPrograms, testInstance, params);
+       }
+
+       group.addChild(tests.release());
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Make test group consisting of compute pipeline tests
+ *//*--------------------------------------------------------------------*/
+void addComputePipelineTests(TestCaseGroup& group)
+{
+       using namespace compute_tests;
+
+       auto tests = newMovePtr<TestCaseGroup>(
+               group.getTestContext(), "compute_pipelines", "Test pipeline creation cache control with compute pipelines");
+
+       for (const auto& params : TEST_CASES)
+       {
+               addFunctionCaseWithPrograms<const TestParams&>(
+                       tests.get(), params.name, params.description, checkSupport, initPrograms, testInstance, params);
+       }
+
+       group.addChild(tests.release());
+}
+
+} // namespace
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Make pipeline creation cache control test group
+ *//*--------------------------------------------------------------------*/
+TestCaseGroup* createCacheControlTests(TestContext& testCtx)
+{
+       auto tests = newMovePtr<TestCaseGroup>(testCtx, "creation_cache_control", "pipeline creation cache control tests");
+
+       addGraphicsPipelineTests(*tests);
+       addComputePipelineTests(*tests);
+
+       return tests.release();
+}
+
+} // namespace pipeline
+
+} // namespace vkt
diff --git a/external/vulkancts/modules/vulkan/pipeline/vktPipelineCreationCacheControlTests.hpp b/external/vulkancts/modules/vulkan/pipeline/vktPipelineCreationCacheControlTests.hpp
new file mode 100644 (file)
index 0000000..c10707a
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef _VKTPIPELINECREATIONCACHECONTROLTESTS_HPP
+#define _VKTPIPELINECREATIONCACHECONTROLTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2020 The Khronos Group Inc.
+ * Copyright (c) 2020, Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Pipeline Creation Cache Control Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTestCase.hpp"
+
+namespace vkt
+{
+namespace pipeline
+{
+
+tcu::TestCaseGroup* createCacheControlTests (tcu::TestContext& testCtx);
+
+} // pipeline
+} // vkt
+
+#endif // _VKTPIPELINECREATIONCACHECONTROLTESTS_HPP
index e00f23f..c5b4f77 100644 (file)
@@ -53,6 +53,7 @@
 #include "vktPipelineMaxVaryingsTests.hpp"
 #include "vktPipelineBlendOperationAdvancedTests.hpp"
 #include "vktPipelineExtendedDynamicStateTests.hpp"
+#include "vktPipelineCreationCacheControlTests.hpp"
 #include "vktTestGroupUtil.hpp"
 
 namespace vkt
@@ -97,6 +98,7 @@ void createChildren (tcu::TestCaseGroup* pipelineTests)
        pipelineTests->addChild(createMaxVaryingsTests                          (testCtx));
        pipelineTests->addChild(createBlendOperationAdvancedTests       (testCtx));
        pipelineTests->addChild(createExtendedDynamicStateTests         (testCtx));
+       pipelineTests->addChild(createCacheControlTests                         (testCtx));
 }
 
 } // anonymous
index 1c288d9..64e6bcc 100644 (file)
@@ -7,6 +7,7 @@ set(DEQP_VK_UTIL_SRCS
        vktDrawUtil.hpp
        vktExternalMemoryUtil.cpp
        vktExternalMemoryUtil.hpp
+       vktConstexprVectorUtil.hpp
 )
 
 set(DEQP_VK_UTIL_LIBS
diff --git a/external/vulkancts/modules/vulkan/util/vktConstexprVectorUtil.hpp b/external/vulkancts/modules/vulkan/util/vktConstexprVectorUtil.hpp
new file mode 100644 (file)
index 0000000..792d211
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef _VKTCONSTEXPRVECTORUTIL_HPP
+#define _VKTCONSTEXPRVECTORUTIL_HPP
+/*------------------------------------------------------------------------
+ * Vulkan CTS Framework
+ * ------------------------
+ *
+ * Copyright (c) 2020 The Khronos Group Inc.
+ * Copyright (c) 2020 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+/*!
+ * \file
+ * \brief Compile time friendly dynamic sized array with maximum capacity
+ */
+/*--------------------------------------------------------------------*/
+
+#include <cstddef>
+#include <array>
+
+namespace vkt
+{
+/*--------------------------------------------------------------------*//*!
+ * \brief Constexpr compatable vector with checked maximum capacity
+ *
+ * \note Unlike std::array, size() and max_size() are different values
+ *       This makes behaviour more similar to that of std::vector.
+ *//*--------------------------------------------------------------------*/
+template<typename _t, size_t CAPACITY>
+class ConstexprVector
+{
+public:
+       using value_type = _t;
+       using size_type = ::std::size_t;
+       using difference_type = ::std::ptrdiff_t;
+       using const_reference = const value_type&;
+       using const_pointer = const value_type*;
+       using const_iterator = const value_type*;
+
+       inline constexpr ConstexprVector() noexcept : values{}, count{0} {};
+
+       /*--------------------------------------------------------------------*//*!
+        * MSVC v140 chokes on this if it is a raw variadic template list.
+        * By providing a single argument lead for type deduction it seems to fix
+        * things. Marking constructor as explicit since this effectively becomes
+        * a single argument constructor.
+        *//*--------------------------------------------------------------------*/
+       template<typename _arg_t, typename... _args_t>
+       inline constexpr explicit ConstexprVector(const _arg_t& arg1, const _args_t&... args) noexcept :
+               values{arg1, args...},
+               count{sizeof...(_args_t) + 1}
+       {
+               static_assert((sizeof...(_args_t) + 1) <= CAPACITY, "Not enough capacity to store values");
+       }
+
+       inline constexpr const_reference at(size_type pos) const noexcept { return values[pos]; }
+       inline constexpr const_reference operator[](size_type pos) const noexcept { return values[pos]; }
+       inline constexpr const_reference front() const noexcept { return values[0]; }
+       inline constexpr const_reference back() const noexcept { return values[count - 1];      }
+       inline constexpr const_pointer data() const noexcept { return values; }
+       inline constexpr const_iterator begin() const noexcept { return &values[0]; }
+       inline constexpr const_iterator cbegin() const noexcept { return &values[0]; }
+       inline constexpr const_iterator end() const noexcept { return &values[count]; }
+       inline constexpr const_iterator cend() const noexcept { return &values[count]; }
+       inline constexpr bool empty() const noexcept { return count == 0; }
+       inline constexpr size_type size() const noexcept { return count; }
+       inline constexpr size_type max_size() const noexcept { return CAPACITY; }
+
+private:
+       value_type values[CAPACITY];
+       size_type count;
+};
+
+} // namespace vkt
+
+#endif // _VKTCONSTEXPRVECTORUTIL_HPP
index 7264f49..861660a 100644 (file)
@@ -325556,6 +325556,22 @@ dEQP-VK.pipeline.extended_dynamic_state.two_draws_static.stencil_state_face_both
 dEQP-VK.pipeline.extended_dynamic_state.two_draws_static.stencil_state_face_both_dual_xt_dec_wrap_clear_0_ref_0_depthfail
 dEQP-VK.pipeline.extended_dynamic_state.two_draws_static.stencil_state_face_both_dual_xt_dec_wrap_clear_0_ref_1_pass
 dEQP-VK.pipeline.extended_dynamic_state.two_draws_static.stencil_state_face_both_dual_xt_dec_wrap_clear_0_ref_1_depthfail
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.single_pipeline_no_compile
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.batch_pipelines_early_return
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_single_recreate_explicit_caching
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_single_recreate_no_caching
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_single_recreate_derivative
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_batch_pipelines_explicit_cache
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_batch_pipelines_no_cache
+dEQP-VK.pipeline.creation_cache_control.graphics_pipelines.duplicate_batch_pipelines_derivative_index
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.single_pipeline_no_compile
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.batch_pipelines_early_return
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_single_recreate_explicit_caching
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_single_recreate_no_caching
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_single_recreate_derivative
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_batch_pipelines_explicit_cache
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_batch_pipelines_no_cache
+dEQP-VK.pipeline.creation_cache_control.compute_pipelines.duplicate_batch_pipelines_derivative_index
 dEQP-VK.binding_model.shader_access.primary_cmd_buf.sampler_mutable.no_access.single_descriptor.1d
 dEQP-VK.binding_model.shader_access.primary_cmd_buf.sampler_mutable.no_access.single_descriptor.1d_base_mip
 dEQP-VK.binding_model.shader_access.primary_cmd_buf.sampler_mutable.no_access.single_descriptor.1d_base_slice
index 49a0181..34a3933 100644 (file)
@@ -41,6 +41,7 @@ VkPhysicalDeviceImagelessFramebufferFeaturesKHR                       FEATURES ( imagelessFramebuff
 VkPhysicalDeviceVulkan12Features                                                       FEATURES ( imagelessFramebuffer )                                                               REQUIREMENTS ( "ApiVersion(1, 2, 0)" )
 VkPhysicalDeviceVulkan12Features                                                       FEATURES ( uniformBufferStandardLayout )                                                REQUIREMENTS ( "ApiVersion(1, 2, 0)" )
 VkPhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR         FEATURES ( separateDepthStencilLayouts )                                                REQUIREMENTS ( VK_KHR_separate_depth_stencil_layouts )
+VkPhysicalDevicePipelineCreationCacheControlFeaturesEXT                FEATURES ( pipelineCreationCacheControl )                                               REQUIREMENTS ( VK_EXT_pipeline_creation_cache_control )
 VkPhysicalDeviceVulkan12Features                                                       FEATURES ( separateDepthStencilLayouts )                                                REQUIREMENTS ( "ApiVersion(1, 2, 0)" )
 VkPhysicalDeviceHostQueryResetFeaturesEXT                                      FEATURES ( hostQueryReset )                                                                             REQUIREMENTS ( VK_EXT_host_query_reset )
 VkPhysicalDeviceVulkan12Features                                                       FEATURES ( hostQueryReset )                                                                             REQUIREMENTS ( "ApiVersion(1, 2, 0)" )
index 31d6a30..8896f47 100644 (file)
@@ -5,6 +5,8 @@
  * ----------------------------------------
  *
  * Copyright 2014 The Android Open Source Project
+ * Copyright (c) 2020 The Khronos Group Inc.
+ * Copyright (c) 2020 Advanced Micro Devices, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,6 +25,8 @@
  * \brief String template class.
  *//*--------------------------------------------------------------------*/
 
+#include <deStringUtil.hpp>
+
 #include <map>
 #include <string>
 
@@ -40,6 +44,9 @@ public:
 
        std::string                     specialize                      (const std::map<std::string, std::string>& params) const;
 
+       template <typename... args_t>
+       std::string                     format                          (args_t&&... args) const;
+
 private:
                                                StringTemplate          (const StringTemplate&);                // not allowed!
        StringTemplate&         operator=                       (const StringTemplate&);                // not allowed!
@@ -47,6 +54,43 @@ private:
        std::string                     m_template;
 } DE_WARN_UNUSED_TYPE;
 
+/*--------------------------------------------------------------------*//*!
+ * Utility to unpack consecutive arguments into a parameter map
+ *//*--------------------------------------------------------------------*/
+namespace detail
+{
+static constexpr const char* TOKENS[] = {
+       "0",  "1",  "2",  "3",  "4",  "5",  "6",  "7",  "8",  "9",  "10",
+       "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21",
+       "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32",
+       "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43",
+       "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54",
+       "55", "56", "57", "58", "59", "60", "61", "62", "63"};
+
+template <size_t ARG_NUM, typename unpacked_t>
+inline void unpackArgs(unpacked_t&) {}
+
+template <size_t ARG_NUM, typename unpacked_t, typename arg_t, typename... args_t>
+inline void unpackArgs(unpacked_t& unpacked, arg_t&& cur, args_t&&... args)
+{
+       static_assert(ARG_NUM < DE_LENGTH_OF_ARRAY(TOKENS),
+                                 "ARG_NUM must be less than DE_LENGTH_OF_ARRAY(TOKENS)");
+       unpacked[TOKENS[ARG_NUM]] = de::toString(cur);
+       unpackArgs<ARG_NUM + 1>(unpacked, ::std::forward<args_t>(args)...);
+}
+} // detail
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Implementation of specialize() using a variable argument list
+ *//*--------------------------------------------------------------------*/
+template <typename... args_t>
+std::string StringTemplate::format(args_t&&... args) const
+{
+       std::map<std::string, std::string> unpacked = {};
+       detail::unpackArgs<0>(unpacked, ::std::forward<args_t>(args)...);
+       return specialize(unpacked);
+}
+
 } // tcu
 
 #endif // _TCUSTRINGTEMPLATE_HPP