anv: fix bug when using component qualifier in FS outputs
[mesa.git] / src / intel / vulkan / anv_pipeline_cache.c
index abca9fe3313ec46ce801894a1ecb2946d4171db9..82551e9f81f293ecadbec7d2a52ca6dfc10a75eb 100644 (file)
  * IN THE SOFTWARE.
  */
 
-#include "util/mesa-sha1.h"
+#include "compiler/blob.h"
+#include "util/hash_table.h"
 #include "util/debug.h"
 #include "anv_private.h"
 
-struct shader_bin_key {
-   uint32_t size;
-   uint8_t data[0];
-};
-
-static size_t
-anv_shader_bin_size(uint32_t prog_data_size, uint32_t key_size,
-                    uint32_t surface_count, uint32_t sampler_count)
-{
-   const uint32_t binding_data_size =
-      (surface_count + sampler_count) * sizeof(struct anv_pipeline_binding);
-
-   return align_u32(sizeof(struct anv_shader_bin), 8) +
-          align_u32(prog_data_size, 8) +
-          align_u32(sizeof(uint32_t) + key_size, 8) +
-          align_u32(binding_data_size, 8);
-}
-
-static inline const struct shader_bin_key *
-anv_shader_bin_get_key(const struct anv_shader_bin *shader)
-{
-   const void *data = shader;
-   data += align_u32(sizeof(struct anv_shader_bin), 8);
-   data += align_u32(shader->prog_data_size, 8);
-   return data;
-}
-
 struct anv_shader_bin *
 anv_shader_bin_create(struct anv_device *device,
                       const void *key_data, uint32_t key_size,
                       const void *kernel_data, uint32_t kernel_size,
-                      const void *prog_data, uint32_t prog_data_size,
+                      const struct brw_stage_prog_data *prog_data_in,
+                      uint32_t prog_data_size, const void *prog_data_param_in,
                       const struct anv_pipeline_bind_map *bind_map)
 {
-   const size_t size =
-      anv_shader_bin_size(prog_data_size, key_size,
-                          bind_map->surface_count, bind_map->sampler_count);
-
-   struct anv_shader_bin *shader =
-      anv_alloc(&device->alloc, size, 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
-   if (!shader)
+   struct anv_shader_bin *shader;
+   struct anv_shader_bin_key *key;
+   struct brw_stage_prog_data *prog_data;
+   uint32_t *prog_data_param;
+   struct anv_pipeline_binding *surface_to_descriptor, *sampler_to_descriptor;
+
+   ANV_MULTIALLOC(ma);
+   anv_multialloc_add(&ma, &shader, 1);
+   anv_multialloc_add_size(&ma, &key, sizeof(*key) + key_size);
+   anv_multialloc_add_size(&ma, &prog_data, prog_data_size);
+   anv_multialloc_add(&ma, &prog_data_param, prog_data_in->nr_params);
+   anv_multialloc_add(&ma, &surface_to_descriptor,
+                           bind_map->surface_count);
+   anv_multialloc_add(&ma, &sampler_to_descriptor,
+                           bind_map->sampler_count);
+
+   if (!anv_multialloc_alloc(&ma, &device->alloc,
+                             VK_SYSTEM_ALLOCATION_SCOPE_DEVICE))
       return NULL;
 
    shader->ref_cnt = 1;
 
+   key->size = key_size;
+   memcpy(key->data, key_data, key_size);
+   shader->key = key;
+
    shader->kernel =
       anv_state_pool_alloc(&device->instruction_state_pool, kernel_size, 64);
    memcpy(shader->kernel.map, kernel_data, kernel_size);
    shader->kernel_size = kernel_size;
-   shader->bind_map = *bind_map;
-   shader->prog_data_size = prog_data_size;
-
-   /* Now we fill out the floating data at the end */
-   void *data = shader;
-   data += align_u32(sizeof(struct anv_shader_bin), 8);
-
-   memcpy(data, prog_data, prog_data_size);
-   data += align_u32(prog_data_size, 8);
 
-   struct shader_bin_key *key = data;
-   key->size = key_size;
-   memcpy(key->data, key_data, key_size);
-   data += align_u32(sizeof(*key) + key_size, 8);
-
-   shader->bind_map.surface_to_descriptor = data;
-   memcpy(data, bind_map->surface_to_descriptor,
-          bind_map->surface_count * sizeof(struct anv_pipeline_binding));
-   data += bind_map->surface_count * sizeof(struct anv_pipeline_binding);
+   memcpy(prog_data, prog_data_in, prog_data_size);
+   memcpy(prog_data_param, prog_data_param_in,
+          prog_data->nr_params * sizeof(*prog_data_param));
+   prog_data->param = prog_data_param;
+   shader->prog_data = prog_data;
+   shader->prog_data_size = prog_data_size;
 
-   shader->bind_map.sampler_to_descriptor = data;
-   memcpy(data, bind_map->sampler_to_descriptor,
-          bind_map->sampler_count * sizeof(struct anv_pipeline_binding));
+   shader->bind_map = *bind_map;
+   typed_memcpy(surface_to_descriptor, bind_map->surface_to_descriptor,
+                bind_map->surface_count);
+   shader->bind_map.surface_to_descriptor = surface_to_descriptor;
+   typed_memcpy(sampler_to_descriptor, bind_map->sampler_to_descriptor,
+                bind_map->sampler_count);
+   shader->bind_map.sampler_to_descriptor = sampler_to_descriptor;
 
    return shader;
 }
@@ -107,32 +89,77 @@ anv_shader_bin_destroy(struct anv_device *device,
 {
    assert(shader->ref_cnt == 0);
    anv_state_pool_free(&device->instruction_state_pool, shader->kernel);
-   anv_free(&device->alloc, shader);
+   vk_free(&device->alloc, shader);
 }
 
-static size_t
-anv_shader_bin_data_size(const struct anv_shader_bin *shader)
+static bool
+anv_shader_bin_write_to_blob(const struct anv_shader_bin *shader,
+                             struct blob *blob)
 {
-   return anv_shader_bin_size(shader->prog_data_size,
-                              anv_shader_bin_get_key(shader)->size,
-                              shader->bind_map.surface_count,
-                              shader->bind_map.sampler_count) +
-          align_u32(shader->kernel_size, 8);
+   bool ok;
+
+   ok = blob_write_uint32(blob, shader->key->size);
+   ok = blob_write_bytes(blob, shader->key->data, shader->key->size);
+
+   ok = blob_write_uint32(blob, shader->kernel_size);
+   ok = blob_write_bytes(blob, shader->kernel.map, shader->kernel_size);
+
+   ok = blob_write_uint32(blob, shader->prog_data_size);
+   ok = blob_write_bytes(blob, shader->prog_data, shader->prog_data_size);
+   ok = blob_write_bytes(blob, shader->prog_data->param,
+                               shader->prog_data->nr_params *
+                               sizeof(*shader->prog_data->param));
+
+   ok = blob_write_uint32(blob, shader->bind_map.surface_count);
+   ok = blob_write_uint32(blob, shader->bind_map.sampler_count);
+   ok = blob_write_uint32(blob, shader->bind_map.image_count);
+   ok = blob_write_bytes(blob, shader->bind_map.surface_to_descriptor,
+                               shader->bind_map.surface_count *
+                               sizeof(*shader->bind_map.surface_to_descriptor));
+   ok = blob_write_bytes(blob, shader->bind_map.sampler_to_descriptor,
+                               shader->bind_map.sampler_count *
+                               sizeof(*shader->bind_map.sampler_to_descriptor));
+
+   return ok;
 }
 
-static void
-anv_shader_bin_write_data(const struct anv_shader_bin *shader, void *data)
+static struct anv_shader_bin *
+anv_shader_bin_create_from_blob(struct anv_device *device,
+                                struct blob_reader *blob)
 {
-   size_t struct_size =
-      anv_shader_bin_size(shader->prog_data_size,
-                          anv_shader_bin_get_key(shader)->size,
-                          shader->bind_map.surface_count,
-                          shader->bind_map.sampler_count);
+   uint32_t key_size = blob_read_uint32(blob);
+   const void *key_data = blob_read_bytes(blob, key_size);
+
+   uint32_t kernel_size = blob_read_uint32(blob);
+   const void *kernel_data = blob_read_bytes(blob, kernel_size);
 
-   memcpy(data, shader, struct_size);
-   data += struct_size;
+   uint32_t prog_data_size = blob_read_uint32(blob);
+   const struct brw_stage_prog_data *prog_data =
+      blob_read_bytes(blob, prog_data_size);
+   if (blob->overrun)
+      return NULL;
+   const void *prog_data_param =
+      blob_read_bytes(blob, prog_data->nr_params * sizeof(*prog_data->param));
+
+   struct anv_pipeline_bind_map bind_map;
+   bind_map.surface_count = blob_read_uint32(blob);
+   bind_map.sampler_count = blob_read_uint32(blob);
+   bind_map.image_count = blob_read_uint32(blob);
+   bind_map.surface_to_descriptor = (void *)
+      blob_read_bytes(blob, bind_map.surface_count *
+                            sizeof(*bind_map.surface_to_descriptor));
+   bind_map.sampler_to_descriptor = (void *)
+      blob_read_bytes(blob, bind_map.sampler_count *
+                            sizeof(*bind_map.sampler_to_descriptor));
+
+   if (blob->overrun)
+      return NULL;
 
-   memcpy(data, shader->kernel.map, shader->kernel_size);
+   return anv_shader_bin_create(device,
+                                key_data, key_size,
+                                kernel_data, kernel_size,
+                                prog_data, prog_data_size, prog_data_param,
+                                &bind_map);
 }
 
 /* Remaining work:
@@ -142,315 +169,155 @@ anv_shader_bin_write_data(const struct anv_shader_bin *shader, void *data)
  *
  * - Review prog_data struct for size and cacheability: struct
  *   brw_stage_prog_data has binding_table which uses a lot of uint32_t for 8
- *   bit quantities etc; param, pull_param, and image_params are pointers, we
- *   just need the compation map. use bit fields for all bools, eg
- *   dual_src_blend.
+ *   bit quantities etc; use bit fields for all bools, eg dual_src_blend.
  */
 
+static uint32_t
+shader_bin_key_hash_func(const void *void_key)
+{
+   const struct anv_shader_bin_key *key = void_key;
+   return _mesa_hash_data(key->data, key->size);
+}
+
+static bool
+shader_bin_key_compare_func(const void *void_a, const void *void_b)
+{
+   const struct anv_shader_bin_key *a = void_a, *b = void_b;
+   if (a->size != b->size)
+      return false;
+
+   return memcmp(a->data, b->data, a->size) == 0;
+}
+
 void
 anv_pipeline_cache_init(struct anv_pipeline_cache *cache,
-                        struct anv_device *device)
+                        struct anv_device *device,
+                        bool cache_enabled)
 {
    cache->device = device;
-   anv_state_stream_init(&cache->program_stream,
-                         &device->instruction_block_pool);
    pthread_mutex_init(&cache->mutex, NULL);
 
-   cache->kernel_count = 0;
-   cache->total_size = 0;
-   cache->table_size = 1024;
-   const size_t byte_size = cache->table_size * sizeof(cache->hash_table[0]);
-   cache->hash_table = malloc(byte_size);
-
-   /* We don't consider allocation failure fatal, we just start with a 0-sized
-    * cache. */
-   if (cache->hash_table == NULL ||
-       !env_var_as_boolean("ANV_ENABLE_PIPELINE_CACHE", true))
-      cache->table_size = 0;
-   else
-      memset(cache->hash_table, 0xff, byte_size);
+   if (cache_enabled) {
+      cache->cache = _mesa_hash_table_create(NULL, shader_bin_key_hash_func,
+                                             shader_bin_key_compare_func);
+   } else {
+      cache->cache = NULL;
+   }
 }
 
 void
 anv_pipeline_cache_finish(struct anv_pipeline_cache *cache)
 {
-   anv_state_stream_finish(&cache->program_stream);
    pthread_mutex_destroy(&cache->mutex);
-   free(cache->hash_table);
-}
-
-struct cache_entry {
-   unsigned char sha1[20];
-   uint32_t prog_data_size;
-   uint32_t kernel_size;
-   uint32_t surface_count;
-   uint32_t sampler_count;
-   uint32_t image_count;
-
-   char prog_data[0];
-
-   /* kernel follows prog_data at next 64 byte aligned address */
-};
-
-static uint32_t
-entry_size(struct cache_entry *entry)
-{
-   /* This returns the number of bytes needed to serialize an entry, which
-    * doesn't include the alignment padding bytes.
-    */
 
-   struct brw_stage_prog_data *prog_data = (void *)entry->prog_data;
-   const uint32_t param_size =
-      prog_data->nr_params * sizeof(*prog_data->param);
+   if (cache->cache) {
+      /* This is a bit unfortunate.  In order to keep things from randomly
+       * going away, the shader cache has to hold a reference to all shader
+       * binaries it contains.  We unref them when we destroy the cache.
+       */
+      struct hash_entry *entry;
+      hash_table_foreach(cache->cache, entry)
+         anv_shader_bin_unref(cache->device, entry->data);
 
-   const uint32_t map_size =
-      entry->surface_count * sizeof(struct anv_pipeline_binding) +
-      entry->sampler_count * sizeof(struct anv_pipeline_binding);
-
-   return sizeof(*entry) + entry->prog_data_size + param_size + map_size;
-}
-
-void
-anv_hash_shader(unsigned char *hash, const void *key, size_t key_size,
-                struct anv_shader_module *module,
-                const char *entrypoint,
-                const struct anv_pipeline_layout *pipeline_layout,
-                const VkSpecializationInfo *spec_info)
-{
-   struct mesa_sha1 *ctx;
-
-   ctx = _mesa_sha1_init();
-   _mesa_sha1_update(ctx, key, key_size);
-   _mesa_sha1_update(ctx, module->sha1, sizeof(module->sha1));
-   _mesa_sha1_update(ctx, entrypoint, strlen(entrypoint));
-   if (pipeline_layout) {
-      _mesa_sha1_update(ctx, pipeline_layout->sha1,
-                        sizeof(pipeline_layout->sha1));
+      _mesa_hash_table_destroy(cache->cache, NULL);
    }
-   /* hash in shader stage, pipeline layout? */
-   if (spec_info) {
-      _mesa_sha1_update(ctx, spec_info->pMapEntries,
-                        spec_info->mapEntryCount * sizeof spec_info->pMapEntries[0]);
-      _mesa_sha1_update(ctx, spec_info->pData, spec_info->dataSize);
-   }
-   _mesa_sha1_final(ctx, hash);
 }
 
-static uint32_t
-anv_pipeline_cache_search_unlocked(struct anv_pipeline_cache *cache,
-                                   const unsigned char *sha1,
-                                   const struct brw_stage_prog_data **prog_data,
-                                   struct anv_pipeline_bind_map *map)
+static struct anv_shader_bin *
+anv_pipeline_cache_search_locked(struct anv_pipeline_cache *cache,
+                                 const void *key_data, uint32_t key_size)
 {
-   const uint32_t mask = cache->table_size - 1;
-   const uint32_t start = (*(uint32_t *) sha1);
-
-   for (uint32_t i = 0; i < cache->table_size; i++) {
-      const uint32_t index = (start + i) & mask;
-      const uint32_t offset = cache->hash_table[index];
-
-      if (offset == ~0)
-         return NO_KERNEL;
-
-      struct cache_entry *entry =
-         cache->program_stream.block_pool->map + offset;
-      if (memcmp(entry->sha1, sha1, sizeof(entry->sha1)) == 0) {
-         if (prog_data) {
-            assert(map);
-            void *p = entry->prog_data;
-            *prog_data = p;
-            p += entry->prog_data_size;
-            p += (*prog_data)->nr_params * sizeof(*(*prog_data)->param);
-            map->surface_count = entry->surface_count;
-            map->sampler_count = entry->sampler_count;
-            map->image_count = entry->image_count;
-            map->surface_to_descriptor = p;
-            p += map->surface_count * sizeof(struct anv_pipeline_binding);
-            map->sampler_to_descriptor = p;
-         }
-
-         return offset + align_u32(entry_size(entry), 64);
-      }
-   }
+   uint32_t vla[1 + DIV_ROUND_UP(key_size, sizeof(uint32_t))];
+   struct anv_shader_bin_key *key = (void *)vla;
+   key->size = key_size;
+   memcpy(key->data, key_data, key_size);
 
-   /* This can happen if the pipeline cache is disabled via
-    * ANV_ENABLE_PIPELINE_CACHE=false
-    */
-   return NO_KERNEL;
+   struct hash_entry *entry = _mesa_hash_table_search(cache->cache, key);
+   if (entry)
+      return entry->data;
+   else
+      return NULL;
 }
 
-uint32_t
+struct anv_shader_bin *
 anv_pipeline_cache_search(struct anv_pipeline_cache *cache,
-                          const unsigned char *sha1,
-                          const struct brw_stage_prog_data **prog_data,
-                          struct anv_pipeline_bind_map *map)
+                          const void *key_data, uint32_t key_size)
 {
-   uint32_t kernel;
+   if (!cache->cache)
+      return NULL;
 
    pthread_mutex_lock(&cache->mutex);
 
-   kernel = anv_pipeline_cache_search_unlocked(cache, sha1, prog_data, map);
+   struct anv_shader_bin *shader =
+      anv_pipeline_cache_search_locked(cache, key_data, key_size);
 
    pthread_mutex_unlock(&cache->mutex);
 
-   return kernel;
-}
-
-static void
-anv_pipeline_cache_set_entry(struct anv_pipeline_cache *cache,
-                             struct cache_entry *entry, uint32_t entry_offset)
-{
-   const uint32_t mask = cache->table_size - 1;
-   const uint32_t start = (*(uint32_t *) entry->sha1);
-
-   /* We'll always be able to insert when we get here. */
-   assert(cache->kernel_count < cache->table_size / 2);
-
-   for (uint32_t i = 0; i < cache->table_size; i++) {
-      const uint32_t index = (start + i) & mask;
-      if (cache->hash_table[index] == ~0) {
-         cache->hash_table[index] = entry_offset;
-         break;
-      }
-   }
+   /* We increment refcount before handing it to the caller */
+   if (shader)
+      anv_shader_bin_ref(shader);
 
-   cache->total_size += entry_size(entry) + entry->kernel_size;
-   cache->kernel_count++;
+   return shader;
 }
 
-static VkResult
-anv_pipeline_cache_grow(struct anv_pipeline_cache *cache)
+static struct anv_shader_bin *
+anv_pipeline_cache_add_shader(struct anv_pipeline_cache *cache,
+                              const void *key_data, uint32_t key_size,
+                              const void *kernel_data, uint32_t kernel_size,
+                              const struct brw_stage_prog_data *prog_data,
+                              uint32_t prog_data_size,
+                              const void *prog_data_param,
+                              const struct anv_pipeline_bind_map *bind_map)
 {
-   const uint32_t table_size = cache->table_size * 2;
-   const uint32_t old_table_size = cache->table_size;
-   const size_t byte_size = table_size * sizeof(cache->hash_table[0]);
-   uint32_t *table;
-   uint32_t *old_table = cache->hash_table;
-
-   table = malloc(byte_size);
-   if (table == NULL)
-      return VK_ERROR_OUT_OF_HOST_MEMORY;
-
-   cache->hash_table = table;
-   cache->table_size = table_size;
-   cache->kernel_count = 0;
-   cache->total_size = 0;
-
-   memset(cache->hash_table, 0xff, byte_size);
-   for (uint32_t i = 0; i < old_table_size; i++) {
-      const uint32_t offset = old_table[i];
-      if (offset == ~0)
-         continue;
-
-      struct cache_entry *entry =
-         cache->program_stream.block_pool->map + offset;
-      anv_pipeline_cache_set_entry(cache, entry, offset);
-   }
-
-   free(old_table);
+   struct anv_shader_bin *shader =
+      anv_pipeline_cache_search_locked(cache, key_data, key_size);
+   if (shader)
+      return shader;
+
+   struct anv_shader_bin *bin =
+      anv_shader_bin_create(cache->device, key_data, key_size,
+                            kernel_data, kernel_size,
+                            prog_data, prog_data_size, prog_data_param,
+                            bind_map);
+   if (!bin)
+      return NULL;
 
-   return VK_SUCCESS;
-}
+   _mesa_hash_table_insert(cache->cache, bin->key, bin);
 
-static void
-anv_pipeline_cache_add_entry(struct anv_pipeline_cache *cache,
-                             struct cache_entry *entry, uint32_t entry_offset)
-{
-   if (cache->kernel_count == cache->table_size / 2)
-      anv_pipeline_cache_grow(cache);
-
-   /* Failing to grow that hash table isn't fatal, but may mean we don't
-    * have enough space to add this new kernel. Only add it if there's room.
-    */
-   if (cache->kernel_count < cache->table_size / 2)
-      anv_pipeline_cache_set_entry(cache, entry, entry_offset);
+   return bin;
 }
 
-uint32_t
+struct anv_shader_bin *
 anv_pipeline_cache_upload_kernel(struct anv_pipeline_cache *cache,
-                                 const unsigned char *sha1,
-                                 const void *kernel, size_t kernel_size,
-                                 const struct brw_stage_prog_data **prog_data,
-                                 size_t prog_data_size,
-                                 struct anv_pipeline_bind_map *map)
+                                 const void *key_data, uint32_t key_size,
+                                 const void *kernel_data, uint32_t kernel_size,
+                                 const struct brw_stage_prog_data *prog_data,
+                                 uint32_t prog_data_size,
+                                 const struct anv_pipeline_bind_map *bind_map)
 {
-   pthread_mutex_lock(&cache->mutex);
-
-   /* Before uploading, check again that another thread didn't upload this
-    * shader while we were compiling it.
-    */
-   if (sha1) {
-      uint32_t cached_kernel =
-         anv_pipeline_cache_search_unlocked(cache, sha1, prog_data, map);
-      if (cached_kernel != NO_KERNEL) {
-         pthread_mutex_unlock(&cache->mutex);
-         return cached_kernel;
-      }
-   }
-
-   struct cache_entry *entry;
-
-   assert((*prog_data)->nr_pull_params == 0);
-   assert((*prog_data)->nr_image_params == 0);
-
-   const uint32_t param_size =
-      (*prog_data)->nr_params * sizeof(*(*prog_data)->param);
-
-   const uint32_t map_size =
-      map->surface_count * sizeof(struct anv_pipeline_binding) +
-      map->sampler_count * sizeof(struct anv_pipeline_binding);
-
-   const uint32_t preamble_size =
-      align_u32(sizeof(*entry) + prog_data_size + param_size + map_size, 64);
-
-   const uint32_t size = preamble_size + kernel_size;
-
-   assert(size < cache->program_stream.block_pool->block_size);
-   const struct anv_state state =
-      anv_state_stream_alloc(&cache->program_stream, size, 64);
-
-   entry = state.map;
-   entry->prog_data_size = prog_data_size;
-   entry->surface_count = map->surface_count;
-   entry->sampler_count = map->sampler_count;
-   entry->image_count = map->image_count;
-   entry->kernel_size = kernel_size;
-
-   void *p = entry->prog_data;
-   memcpy(p, *prog_data, prog_data_size);
-   p += prog_data_size;
-
-   memcpy(p, (*prog_data)->param, param_size);
-   ((struct brw_stage_prog_data *)entry->prog_data)->param = p;
-   p += param_size;
-
-   memcpy(p, map->surface_to_descriptor,
-          map->surface_count * sizeof(struct anv_pipeline_binding));
-   map->surface_to_descriptor = p;
-   p += map->surface_count * sizeof(struct anv_pipeline_binding);
-
-   memcpy(p, map->sampler_to_descriptor,
-          map->sampler_count * sizeof(struct anv_pipeline_binding));
-   map->sampler_to_descriptor = p;
-
-   if (sha1) {
-      assert(anv_pipeline_cache_search_unlocked(cache, sha1,
-                                                NULL, NULL) == NO_KERNEL);
-
-      memcpy(entry->sha1, sha1, sizeof(entry->sha1));
-      anv_pipeline_cache_add_entry(cache, entry, state.offset);
+   if (cache->cache) {
+      pthread_mutex_lock(&cache->mutex);
+
+      struct anv_shader_bin *bin =
+         anv_pipeline_cache_add_shader(cache, key_data, key_size,
+                                       kernel_data, kernel_size,
+                                       prog_data, prog_data_size,
+                                       prog_data->param, bind_map);
+
+      pthread_mutex_unlock(&cache->mutex);
+
+      /* We increment refcount before handing it to the caller */
+      if (bin)
+         anv_shader_bin_ref(bin);
+
+      return bin;
+   } else {
+      /* In this case, we're not caching it so the caller owns it entirely */
+      return anv_shader_bin_create(cache->device, key_data, key_size,
+                                   kernel_data, kernel_size,
+                                   prog_data, prog_data_size,
+                                   prog_data->param, bind_map);
    }
-
-   pthread_mutex_unlock(&cache->mutex);
-
-   memcpy(state.map + preamble_size, kernel, kernel_size);
-
-   if (!cache->device->info.has_llc)
-      anv_state_clflush(state);
-
-   *prog_data = (const struct brw_stage_prog_data *) entry->prog_data;
-
-   return state.offset + preamble_size;
 }
 
 struct cache_header {
@@ -466,12 +333,20 @@ anv_pipeline_cache_load(struct anv_pipeline_cache *cache,
                         const void *data, size_t size)
 {
    struct anv_device *device = cache->device;
-   struct cache_header header;
-   uint8_t uuid[VK_UUID_SIZE];
+   struct anv_physical_device *pdevice = &device->instance->physicalDevice;
+
+   if (cache->cache == NULL)
+      return;
 
-   if (size < sizeof(header))
+   struct blob_reader blob;
+   blob_reader_init(&blob, data, size);
+
+   struct cache_header header;
+   blob_copy_bytes(&blob, &header, sizeof(header));
+   uint32_t count = blob_read_uint32(&blob);
+   if (blob.overrun)
       return;
-   memcpy(&header, data, sizeof(header));
+
    if (header.header_size < sizeof(header))
       return;
    if (header.header_version != VK_PIPELINE_CACHE_HEADER_VERSION_ONE)
@@ -480,52 +355,27 @@ anv_pipeline_cache_load(struct anv_pipeline_cache *cache,
       return;
    if (header.device_id != device->chipset_id)
       return;
-   anv_device_get_cache_uuid(uuid);
-   if (memcmp(header.uuid, uuid, VK_UUID_SIZE) != 0)
+   if (memcmp(header.uuid, pdevice->pipeline_cache_uuid, VK_UUID_SIZE) != 0)
       return;
 
-   void *end = (void *) data + size;
-   void *p = (void *) data + header.header_size;
-
-   while (p < end) {
-      struct cache_entry *entry = p;
-
-      void *data = entry->prog_data;
-
-      /* Make a copy of prog_data so that it's mutable */
-      uint8_t prog_data_tmp[512];
-      assert(entry->prog_data_size <= sizeof(prog_data_tmp));
-      memcpy(prog_data_tmp, data, entry->prog_data_size);
-      struct brw_stage_prog_data *prog_data = (void *)prog_data_tmp;
-      data += entry->prog_data_size;
-
-      prog_data->param = data;
-      data += prog_data->nr_params * sizeof(*prog_data->param);
-
-      struct anv_pipeline_binding *surface_to_descriptor = data;
-      data += entry->surface_count * sizeof(struct anv_pipeline_binding);
-      struct anv_pipeline_binding *sampler_to_descriptor = data;
-      data += entry->sampler_count * sizeof(struct anv_pipeline_binding);
-      void *kernel = data;
-
-      struct anv_pipeline_bind_map map = {
-         .surface_count = entry->surface_count,
-         .sampler_count = entry->sampler_count,
-         .image_count = entry->image_count,
-         .surface_to_descriptor = surface_to_descriptor,
-         .sampler_to_descriptor = sampler_to_descriptor
-      };
-
-      const struct brw_stage_prog_data *const_prog_data = prog_data;
-
-      anv_pipeline_cache_upload_kernel(cache, entry->sha1,
-                                       kernel, entry->kernel_size,
-                                       &const_prog_data,
-                                       entry->prog_data_size, &map);
-      p = kernel + entry->kernel_size;
+   for (uint32_t i = 0; i < count; i++) {
+      struct anv_shader_bin *bin =
+         anv_shader_bin_create_from_blob(device, &blob);
+      if (!bin)
+         break;
+      _mesa_hash_table_insert(cache->cache, bin->key, bin);
    }
 }
 
+static bool
+pipeline_cache_enabled()
+{
+   static int enabled = -1;
+   if (enabled < 0)
+      enabled = env_var_as_boolean("ANV_ENABLE_PIPELINE_CACHE", true);
+   return enabled;
+}
+
 VkResult anv_CreatePipelineCache(
     VkDevice                                    _device,
     const VkPipelineCacheCreateInfo*            pCreateInfo,
@@ -538,13 +388,13 @@ VkResult anv_CreatePipelineCache(
    assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO);
    assert(pCreateInfo->flags == 0);
 
-   cache = anv_alloc2(&device->alloc, pAllocator,
+   cache = vk_alloc2(&device->alloc, pAllocator,
                        sizeof(*cache), 8,
                        VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
    if (cache == NULL)
       return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
 
-   anv_pipeline_cache_init(cache, device);
+   anv_pipeline_cache_init(cache, device, pipeline_cache_enabled());
 
    if (pCreateInfo->initialDataSize > 0)
       anv_pipeline_cache_load(cache,
@@ -564,9 +414,12 @@ void anv_DestroyPipelineCache(
    ANV_FROM_HANDLE(anv_device, device, _device);
    ANV_FROM_HANDLE(anv_pipeline_cache, cache, _cache);
 
+   if (!cache)
+      return;
+
    anv_pipeline_cache_finish(cache);
 
-   anv_free2(&device->alloc, pAllocator, cache);
+   vk_free2(&device->alloc, pAllocator, cache);
 }
 
 VkResult anv_GetPipelineCacheData(
@@ -577,70 +430,57 @@ VkResult anv_GetPipelineCacheData(
 {
    ANV_FROM_HANDLE(anv_device, device, _device);
    ANV_FROM_HANDLE(anv_pipeline_cache, cache, _cache);
-   struct cache_header *header;
+   struct anv_physical_device *pdevice = &device->instance->physicalDevice;
 
-   const size_t size = sizeof(*header) + cache->total_size;
-
-   if (pData == NULL) {
-      *pDataSize = size;
-      return VK_SUCCESS;
+   struct blob blob;
+   if (pData) {
+      blob_init_fixed(&blob, pData, *pDataSize);
+   } else {
+      blob_init_fixed(&blob, NULL, SIZE_MAX);
    }
 
-   if (*pDataSize < sizeof(*header)) {
+   struct cache_header header = {
+      .header_size = sizeof(struct cache_header),
+      .header_version = VK_PIPELINE_CACHE_HEADER_VERSION_ONE,
+      .vendor_id = 0x8086,
+      .device_id = device->chipset_id,
+   };
+   memcpy(header.uuid, pdevice->pipeline_cache_uuid, VK_UUID_SIZE);
+   blob_write_bytes(&blob, &header, sizeof(header));
+
+   uint32_t count = 0;
+   intptr_t count_offset = blob_reserve_uint32(&blob);
+   if (count_offset < 0) {
       *pDataSize = 0;
+      blob_finish(&blob);
       return VK_INCOMPLETE;
    }
 
-   void *p = pData, *end = pData + *pDataSize;
-   header = p;
-   header->header_size = sizeof(*header);
-   header->header_version = VK_PIPELINE_CACHE_HEADER_VERSION_ONE;
-   header->vendor_id = 0x8086;
-   header->device_id = device->chipset_id;
-   anv_device_get_cache_uuid(header->uuid);
-   p += header->header_size;
-
-   struct cache_entry *entry;
-   for (uint32_t i = 0; i < cache->table_size; i++) {
-      if (cache->hash_table[i] == ~0)
-         continue;
-
-      entry = cache->program_stream.block_pool->map + cache->hash_table[i];
-      const uint32_t size = entry_size(entry);
-      if (end < p + size + entry->kernel_size)
-         break;
-
-      memcpy(p, entry, size);
-      p += size;
-
-      void *kernel = (void *) entry + align_u32(size, 64);
+   VkResult result = VK_SUCCESS;
+   if (cache->cache) {
+      struct hash_entry *entry;
+      hash_table_foreach(cache->cache, entry) {
+         struct anv_shader_bin *shader = entry->data;
+
+         size_t save_size = blob.size;
+         if (!anv_shader_bin_write_to_blob(shader, &blob)) {
+            /* If it fails reset to the previous size and bail */
+            blob.size = save_size;
+            result = VK_INCOMPLETE;
+            break;
+         }
 
-      memcpy(p, kernel, entry->kernel_size);
-      p += entry->kernel_size;
+         count++;
+      }
    }
 
-   *pDataSize = p - pData;
-
-   return VK_SUCCESS;
-}
-
-static void
-anv_pipeline_cache_merge(struct anv_pipeline_cache *dst,
-                         struct anv_pipeline_cache *src)
-{
-   for (uint32_t i = 0; i < src->table_size; i++) {
-      const uint32_t offset = src->hash_table[i];
-      if (offset == ~0)
-         continue;
+   blob_overwrite_uint32(&blob, count_offset, count);
 
-      struct cache_entry *entry =
-         src->program_stream.block_pool->map + offset;
+   *pDataSize = blob.size;
 
-      if (anv_pipeline_cache_search(dst, entry->sha1, NULL, NULL) != NO_KERNEL)
-         continue;
+   blob_finish(&blob);
 
-      anv_pipeline_cache_add_entry(dst, entry, offset);
-   }
+   return result;
 }
 
 VkResult anv_MergePipelineCaches(
@@ -651,10 +491,25 @@ VkResult anv_MergePipelineCaches(
 {
    ANV_FROM_HANDLE(anv_pipeline_cache, dst, destCache);
 
+   if (!dst->cache)
+      return VK_SUCCESS;
+
    for (uint32_t i = 0; i < srcCacheCount; i++) {
       ANV_FROM_HANDLE(anv_pipeline_cache, src, pSrcCaches[i]);
+      if (!src->cache)
+         continue;
+
+      struct hash_entry *entry;
+      hash_table_foreach(src->cache, entry) {
+         struct anv_shader_bin *bin = entry->data;
+         assert(bin);
 
-      anv_pipeline_cache_merge(dst, src);
+         if (_mesa_hash_table_search(dst->cache, bin->key))
+            continue;
+
+         anv_shader_bin_ref(bin);
+         _mesa_hash_table_insert(dst->cache, bin->key, bin);
+      }
    }
 
    return VK_SUCCESS;