"VK_LAYER_LUNARG_core_validation", VK_LAYER_API_VERSION, 1, "LunarG Validation Layer",
};
+static const VkExtensionProperties device_extensions[] = {
+ { VK_EXT_VALIDATION_CACHE_EXTENSION_NAME, VK_EXT_VALIDATION_CACHE_SPEC_VERSION },
+};
+
template <class TCreateInfo>
void ValidateLayerOrdering(const TCreateInfo &createInfo) {
bool foundLayer = false;
return result;
}
+// Validation cache:
+// CV is the bottommost implementor of this extension. Don't pass calls down.
+VKAPI_ATTR VkResult VKAPI_CALL CreateValidationCacheEXT(VkDevice device, const VkValidationCacheCreateInfoEXT *pCreateInfo,
+ const VkAllocationCallbacks *pAllocator, VkValidationCacheEXT *pValidationCache) {
+ *pValidationCache = ValidationCache::Create(pCreateInfo);
+ return *pValidationCache ? VK_SUCCESS : VK_ERROR_INITIALIZATION_FAILED;
+}
+
+VKAPI_ATTR void VKAPI_CALL DestroyValidationCacheEXT(VkDevice device, VkValidationCacheEXT validationCache,
+ const VkAllocationCallbacks *pAllocator) {
+ delete (ValidationCache *)validationCache;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL GetValidationCacheDataEXT(VkDevice device, VkValidationCacheEXT validationCache, size_t *pDataSize,
+ void *pData) {
+ size_t inSize = *pDataSize;
+ ((ValidationCache *)validationCache)->Write(pDataSize, pData);
+ return (pData && *pDataSize != inSize) ? VK_INCOMPLETE : VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL MergeValidationCachesEXT(VkDevice device, VkValidationCacheEXT dstCache, uint32_t srcCacheCount,
+ const VkValidationCacheEXT *pSrcCaches) {
+ auto dst = (ValidationCache *)dstCache;
+ auto src = (ValidationCache const * const *)pSrcCaches;
+
+ for (uint32_t i = 0; i < srcCacheCount; i++)
+ dst->Merge(src[i]);
+
+ return VK_SUCCESS;
+}
+
// utility function to set collective state for pipeline
void set_pipeline_state(PIPELINE_STATE *pPipe) {
// If any attachment used by this pipeline has blendEnable, set top-level blendEnable
VKAPI_ATTR VkResult VKAPI_CALL EnumerateDeviceExtensionProperties(VkPhysicalDevice physicalDevice, const char *pLayerName,
uint32_t *pCount, VkExtensionProperties *pProperties) {
- if (pLayerName && !strcmp(pLayerName, global_layer.layerName)) return util_GetExtensionProperties(0, NULL, pCount, pProperties);
+ if (pLayerName && !strcmp(pLayerName, global_layer.layerName)) return util_GetExtensionProperties(1, device_extensions, pCount, pProperties);
assert(physicalDevice);
{"vkGetSemaphoreFdKHR", (void*)GetSemaphoreFdKHR},
{"vkImportFenceFdKHR", (void*)ImportFenceFdKHR},
{"vkGetFenceFdKHR", (void*)GetFenceFdKHR},
+ {"vkCreateValidationCacheEXT", (void*)CreateValidationCacheEXT},
+ {"vkDestroyValidationCacheEXT", (void*)DestroyValidationCacheEXT},
+ {"vkGetValidationCacheDataEXT", (void*)GetValidationCacheDataEXT},
+ {"vkMergeValidationCachesEXT", (void*)MergeValidationCachesEXT},
};
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL GetDeviceProcAddr(VkDevice device, const char *funcName) {
#include "core_validation_types.h"
#include "shader_validation.h"
#include "spirv-tools/libspirv.h"
+#include "xxhash.h"
enum FORMAT_TYPE {
FORMAT_TYPE_FLOAT = 1, // UNORM, SNORM, FLOAT, USCALED, SSCALED, SRGB -- anything we consider float in the shader
return validate_pipeline_shader_stage(dev_data, &pCreateInfo->stage, pipeline, &module, &entrypoint);
}
+uint32_t ValidationCache::MakeShaderHash(VkShaderModuleCreateInfo const *smci) {
+ return XXH32(smci->pCode, smci->codeSize * sizeof(uint32_t), 0);
+}
+
+static ValidationCache *GetValidationCacheInfo(
+ VkShaderModuleCreateInfo const *pCreateInfo) {
+ while ((pCreateInfo = (VkShaderModuleCreateInfo const *)pCreateInfo->pNext) != nullptr) {
+ if (pCreateInfo->sType == VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT)
+ return (ValidationCache *)((VkShaderModuleValidationCacheCreateInfoEXT const *)pCreateInfo)->validationCache;
+ }
+
+ return nullptr;
+}
+
bool PreCallValidateCreateShaderModule(layer_data *dev_data, VkShaderModuleCreateInfo const *pCreateInfo, bool *spirv_valid) {
bool skip = false;
spv_result_t spv_valid = SPV_SUCCESS;
"SPIR-V module not valid: Codesize must be a multiple of 4 but is " PRINTF_SIZE_T_SPECIFIER ". %s",
pCreateInfo->codeSize, validation_error_map[VALIDATION_ERROR_12a00ac0]);
} else {
+ auto cache = GetValidationCacheInfo(pCreateInfo);
+ uint32_t hash = 0;
+ if (cache) {
+ hash = ValidationCache::MakeShaderHash(pCreateInfo);
+ if (cache->Contains(hash))
+ return false;
+ }
+
// Use SPIRV-Tools validator to try and catch any issues with the module itself
spv_context ctx = spvContextCreate(SPV_ENV_VULKAN_1_0);
spv_const_binary_t binary{ pCreateInfo->pCode, pCreateInfo->codeSize / sizeof(uint32_t) };
VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0, __LINE__, SHADER_CHECKER_INCONSISTENT_SPIRV, "SC",
"SPIR-V module not valid: %s", diag && diag->error ? diag->error : "(no error text)");
}
+ } else {
+ if (cache) {
+ cache->Insert(hash);
+ }
}
spvDiagnosticDestroy(diag);
void build_def_index();
};
+// TODO: Wire this up to SPIRV-Tools commit hash
+#define VALIDATION_CACHE_VERSION 1
+
+class ValidationCache {
+ // hashes of shaders that have passed validation before, and can be skipped.
+ // we don't store negative results, as we would have to also store what was
+ // wrong with them; also, we expect they will get fixed, so we're less
+ // likely to see them again.
+ std::unordered_set<uint32_t> good_shader_hashes;
+ ValidationCache() {}
+
+public:
+ static VkValidationCacheEXT Create(VkValidationCacheCreateInfoEXT const *pCreateInfo) {
+ auto cache = new ValidationCache();
+ cache->Load(pCreateInfo);
+ return VkValidationCacheEXT(cache);
+ }
+
+ void Load(VkValidationCacheCreateInfoEXT const *pCreateInfo) {
+ auto size = 8u + VK_UUID_SIZE;
+ if (!pCreateInfo->pInitialData || pCreateInfo->initialDataSize < size)
+ return;
+
+ uint32_t const *data = (uint32_t const *)pCreateInfo->pInitialData;
+ if (data[0] != size)
+ return;
+ if (data[1] != VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT)
+ return;
+ if (data[2] != VALIDATION_CACHE_VERSION || data[3] || data[4] || data[5])
+ return; // different version
+
+ data += 6;
+
+ for (;size < pCreateInfo->initialDataSize;
+ data++, size += sizeof(uint32_t)) {
+ good_shader_hashes.insert(*data);
+ }
+ }
+
+ void Write(size_t *pDataSize, void *pData) {
+ auto headerSize = 8u + VK_UUID_SIZE;
+ if (!pData) {
+ *pDataSize = headerSize + good_shader_hashes.size() * sizeof(uint32_t);
+ return;
+ }
+
+ if (*pDataSize < headerSize) {
+ *pDataSize = 0;
+ return; // Too small for even the header!
+ }
+
+ uint32_t *out = (uint32_t *)pData;
+ size_t actualSize = headerSize;
+
+ // Write the header
+ *out++ = headerSize;
+ *out++ = VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT;
+ *out++ = VALIDATION_CACHE_VERSION;
+ *out++ = 0;
+ *out++ = 0;
+ *out++ = 0;
+
+ for (auto it = good_shader_hashes.begin();
+ it != good_shader_hashes.end() && actualSize < *pDataSize;
+ it++, out++, actualSize += sizeof(uint32_t)) {
+ *out = *it;
+ }
+
+ *pDataSize = actualSize;
+ }
+
+ void Merge(ValidationCache const *other) {
+ for (auto h : other->good_shader_hashes)
+ good_shader_hashes.insert(h);
+ }
+
+ static uint32_t MakeShaderHash(VkShaderModuleCreateInfo const *smci);
+
+ bool Contains(uint32_t hash) {
+ return good_shader_hashes.count(hash) != 0;
+ }
+
+ void Insert(uint32_t hash) {
+ good_shader_hashes.insert(hash);
+ }
+};
+
bool validate_and_capture_pipeline_shader_state(layer_data *dev_data, PIPELINE_STATE *pPipeline);
bool validate_compute_pipeline(layer_data *dev_data, PIPELINE_STATE *pPipeline);
typedef std::pair<unsigned, unsigned> descriptor_slot_t;