--- /dev/null
+/*
+ * Copyright © 2023 Rose Hudson
+ * Copyright (c) 2022 Amazon.com, Inc. or its affiliates.
+ * Copyright © 2018 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "compiler/shader_enums.h"
+#include "util/blob.h"
+#include "util/build_id.h"
+#include "util/disk_cache.h"
+#include "util/mesa-sha1.h"
+#include "agx_bo.h"
+#include "agx_disk_cache.h"
+#include "agx_state.h"
+
+/**
+ * Compute a disk cache key for the given uncompiled shader and shader key.
+ */
+static void
+agx_disk_cache_compute_key(struct disk_cache *cache,
+ const struct agx_uncompiled_shader *uncompiled,
+ const union asahi_shader_key *shader_key,
+ gl_shader_stage stage, cache_key cache_key)
+{
+ uint8_t data[sizeof(uncompiled->nir_sha1) + sizeof(*shader_key)];
+ int hash_size = sizeof(uncompiled->nir_sha1);
+ int key_size;
+ if (stage == MESA_SHADER_VERTEX)
+ key_size = sizeof(shader_key->vs);
+ else if (stage == MESA_SHADER_FRAGMENT)
+ key_size = sizeof(shader_key->fs);
+ else if (gl_shader_stage_is_compute(stage))
+ key_size = 0;
+ else
+ unreachable("Unsupported shader stage");
+
+ memcpy(data, uncompiled->nir_sha1, hash_size);
+
+ if (key_size)
+ memcpy(data + hash_size, shader_key, key_size);
+
+ disk_cache_compute_key(cache, data, hash_size + key_size, cache_key);
+}
+
+/**
+ * Store the given compiled shader in the disk cache.
+ *
+ * This should only be called on newly compiled shaders. No checking is
+ * done to prevent repeated stores of the same shader.
+ */
+void
+agx_disk_cache_store(struct disk_cache *cache,
+ const struct agx_uncompiled_shader *uncompiled,
+ const union asahi_shader_key *key,
+ const struct agx_compiled_shader *binary)
+{
+#ifdef ENABLE_SHADER_CACHE
+ if (!cache)
+ return;
+
+ assert(binary->bo->ptr.cpu != NULL && "shaders must be CPU mapped");
+
+ gl_shader_stage stage = uncompiled->nir->info.stage;
+ cache_key cache_key;
+ agx_disk_cache_compute_key(cache, uncompiled, key, stage, cache_key);
+
+ struct blob blob;
+ blob_init(&blob);
+
+ uint32_t shader_size = binary->bo->size;
+ blob_write_uint32(&blob, shader_size);
+ blob_write_bytes(&blob, binary->bo->ptr.cpu, shader_size);
+ blob_write_bytes(&blob, &binary->info, sizeof(binary->info));
+ blob_write_uint32(&blob, binary->push_range_count);
+ blob_write_bytes(&blob, binary->push, sizeof(binary->push));
+
+ disk_cache_put(cache, cache_key, blob.data, blob.size, NULL);
+ blob_finish(&blob);
+#endif
+}
+
+/**
+ * Search for a compiled shader in the disk cache.
+ */
+struct agx_compiled_shader *
+agx_disk_cache_retrieve(struct agx_screen *screen,
+ const struct agx_uncompiled_shader *uncompiled,
+ const union asahi_shader_key *key)
+{
+#ifdef ENABLE_SHADER_CACHE
+ struct disk_cache *cache = screen->disk_cache;
+ if (!cache)
+ return NULL;
+
+ gl_shader_stage stage = uncompiled->nir->info.stage;
+ cache_key cache_key;
+ agx_disk_cache_compute_key(cache, uncompiled, key, stage, cache_key);
+
+ size_t size;
+ void *buffer = disk_cache_get(cache, cache_key, &size);
+ if (!buffer)
+ return NULL;
+
+ struct agx_compiled_shader *binary = CALLOC_STRUCT(agx_compiled_shader);
+
+ struct blob_reader blob;
+ blob_reader_init(&blob, buffer, size);
+
+ uint32_t binary_size = blob_read_uint32(&blob);
+ binary->bo = agx_bo_create(&screen->dev, binary_size,
+ AGX_BO_EXEC | AGX_BO_LOW_VA, "Executable");
+ blob_copy_bytes(&blob, binary->bo->ptr.cpu, binary_size);
+
+ blob_copy_bytes(&blob, &binary->info, sizeof(binary->info));
+ binary->push_range_count = blob_read_uint32(&blob);
+ blob_copy_bytes(&blob, binary->push, sizeof(binary->push));
+
+ free(buffer);
+ return binary;
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * Initialise the on-disk shader cache.
+ */
+void
+agx_disk_cache_init(struct agx_screen *screen)
+{
+#ifdef ENABLE_SHADER_CACHE
+ const char *renderer = screen->pscreen.get_name(&screen->pscreen);
+
+ const struct build_id_note *note =
+ build_id_find_nhdr_for_addr(agx_disk_cache_init);
+ assert(note && build_id_length(note) == 20);
+
+ const uint8_t *id_sha1 = build_id_data(note);
+ assert(id_sha1);
+
+ char timestamp[41];
+ _mesa_sha1_format(timestamp, id_sha1);
+
+ uint64_t driver_flags = screen->dev.debug;
+ screen->disk_cache = disk_cache_create(renderer, timestamp, driver_flags);
+#endif
+}
--- /dev/null
+/*
+ * © Copyright 2023 Rose Hudson
+ * © Copyright 2018 Alyssa Rosenzweig
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include "util/disk_cache.h"
+#include "agx_state.h"
+
+#ifndef AGX_DISK_CACHE_H
+#define AGX_DISK_CACHE_H
+
+void agx_disk_cache_store(struct disk_cache *cache,
+ const struct agx_uncompiled_shader *uncompiled,
+ const union asahi_shader_key *key,
+ const struct agx_compiled_shader *binary);
+
+struct agx_compiled_shader *
+agx_disk_cache_retrieve(struct agx_screen *screen,
+ const struct agx_uncompiled_shader *uncompiled,
+ const union asahi_shader_key *key);
+
+void agx_disk_cache_init(struct agx_screen *screen);
+
+#endif
#include "util/u_screen.h"
#include "util/u_upload_mgr.h"
#include "agx_device.h"
+#include "agx_disk_cache.h"
#include "agx_public.h"
#include "agx_state.h"
#include "magic.h"
}
static void
-agx_destroy_screen(struct pipe_screen *screen)
+agx_destroy_screen(struct pipe_screen *pscreen)
{
- u_transfer_helper_destroy(screen->transfer_helper);
- agx_close_device(agx_device(screen));
+ struct agx_screen *screen = agx_screen(pscreen);
+
+ u_transfer_helper_destroy(pscreen->transfer_helper);
+ agx_close_device(&screen->dev);
+ disk_cache_destroy(screen->disk_cache);
ralloc_free(screen);
}
return agx_resource(prsrc)->layout.format;
}
+static struct disk_cache *
+agx_get_disk_shader_cache(struct pipe_screen *pscreen)
+{
+ return agx_screen(pscreen)->disk_cache;
+}
+
static const struct u_transfer_vtbl transfer_vtbl = {
.resource_create = agx_resource_create,
.resource_destroy = agx_resource_destroy,
screen->fence_reference = agx_fence_reference;
screen->fence_finish = agx_fence_finish;
screen->get_compiler_options = agx_get_compiler_options;
+ screen->get_disk_shader_cache = agx_get_disk_shader_cache;
screen->resource_create = u_transfer_helper_resource_create;
screen->resource_destroy = u_transfer_helper_resource_destroy;
U_TRANSFER_HELPER_SEPARATE_Z32S8 | U_TRANSFER_HELPER_SEPARATE_STENCIL |
U_TRANSFER_HELPER_MSAA_MAP | U_TRANSFER_HELPER_Z24_IN_Z32F);
+ agx_disk_cache_init(agx_screen);
+
return screen;
}
#include "asahi/lib/agx_ppp.h"
#include "asahi/lib/agx_usc.h"
#include "compiler/nir/nir.h"
+#include "compiler/nir/nir_serialize.h"
#include "gallium/auxiliary/nir/tgsi_to_nir.h"
#include "gallium/auxiliary/tgsi/tgsi_from_mesa.h"
#include "gallium/auxiliary/util/u_blend.h"
#include "util/u_prim.h"
#include "util/u_transfer.h"
#include "agx_state.h"
+#include "agx_disk_cache.h"
static struct pipe_stream_output_target *
agx_create_stream_output_target(struct pipe_context *pctx,
ralloc_free(nir);
util_dynarray_fini(&binary);
+ return compiled;
+}
+
+static struct agx_compiled_shader *
+agx_get_shader_variant(struct agx_screen *screen,
+ struct agx_uncompiled_shader *so,
+ struct util_debug_callback *debug,
+ union asahi_shader_key *key)
+{
+ struct agx_compiled_shader *compiled =
+ agx_disk_cache_retrieve(screen, so, key);
+
+ if (!compiled) {
+ compiled = agx_compile_variant(&screen->dev, so, debug, key);
+ agx_disk_cache_store(screen->disk_cache, so, key, compiled);
+ }
+
/* key may be destroyed after we return, so clone it before using it as a
* hash table key. The clone is logically owned by the hash table.
*/
union asahi_shader_key *cloned_key =
ralloc(so->variants, union asahi_shader_key);
- memcpy(cloned_key, key_, sizeof(union asahi_shader_key));
+ memcpy(cloned_key, key, sizeof(union asahi_shader_key));
+ _mesa_hash_table_insert(so->variants, cloned_key, compiled);
- struct hash_entry *he =
- _mesa_hash_table_insert(so->variants, cloned_key, compiled);
- return he->data;
+ return compiled;
}
static void *
asahi_fs_shader_key_equal);
}
+ struct blob blob;
+ blob_init(&blob);
+ nir_serialize(&blob, so->nir, true);
+ _mesa_sha1_compute(blob.data, blob.size, so->nir_sha1);
+ blob_finish(&blob);
+
/* For shader-db, precompile a shader with a default key. This could be
* improved but hopefully this is acceptable for now.
*/
return true;
}
- struct agx_device *dev = agx_device(ctx->base.screen);
- *out = agx_compile_variant(dev, so, &ctx->base.debug, key);
+ struct agx_screen *screen = agx_screen(ctx->base.screen);
+ *out = agx_get_shader_variant(screen, so, &ctx->base.debug, key);
return true;
}
#include "gallium/include/pipe/p_screen.h"
#include "gallium/include/pipe/p_state.h"
#include "util/bitset.h"
+#include "util/disk_cache.h"
#include "util/hash_table.h"
#include "agx_meta.h"
struct agx_uncompiled_shader {
struct pipe_shader_state base;
struct nir_shader *nir;
+ uint8_t nir_sha1[20];
struct hash_table *variants;
/* Set on VS, passed to FS for linkage */
struct pipe_screen pscreen;
struct agx_device dev;
struct sw_winsys *winsys;
+ struct disk_cache *disk_cache;
};
static inline struct agx_screen *
files_asahi = files(
'agx_batch.c',
'agx_blit.c',
+ 'agx_disk_cache.c',
'agx_pipe.c',
'agx_nir_lower_sysvals.c',
'agx_query.c',