diff --git a/src/util/disk_cache.c b/src/util/disk_cache.c index e2133372185..2280f53ac97 100644 --- a/src/util/disk_cache.c +++ b/src/util/disk_cache.c @@ -278,6 +278,23 @@ disk_cache_create(const char *gpu_name, const char *driver_id, if (!cache) return NULL; + /* If MESA_DISK_CACHE_SINGLE_FILE is unset and MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ + * is set, then enable additional Fossilize RO caches together with the RW + * cache. At first we will check cache entry presence in the RO caches and + * if entry isn't found there, then we'll fall back to the RW cache. + */ + if (cache_type != DISK_CACHE_SINGLE_FILE && !cache->path_init_failed && + debug_get_bool_option("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", false)) { + + /* Create read-only cache used for sharing prebuilt shaders. + * If cache entry will be found in this cache, then the main cache + * will be bypassed. + */ + cache->foz_ro_cache = disk_cache_type_create(gpu_name, driver_id, + driver_flags, + DISK_CACHE_SINGLE_FILE); + } + return cache; } @@ -294,6 +311,9 @@ disk_cache_destroy(struct disk_cache *cache) util_queue_finish(&cache->cache_queue); util_queue_destroy(&cache->cache_queue); + if (cache->foz_ro_cache) + disk_cache_destroy(cache->foz_ro_cache); + if (cache->type == DISK_CACHE_SINGLE_FILE) foz_destroy(&cache->foz_db); @@ -565,6 +585,8 @@ disk_cache_get(struct disk_cache *cache, const cache_key key, size_t *size) if (cache->blob_get_cb) { buf = blob_get_compressed(cache, key, size); + } else if (cache->foz_ro_cache) { + buf = disk_cache_load_item_foz(cache->foz_ro_cache, key, size); } else if (cache->type == DISK_CACHE_SINGLE_FILE) { buf = disk_cache_load_item_foz(cache, key, size); } else if (cache->type == DISK_CACHE_DATABASE) { diff --git a/src/util/disk_cache_os.h b/src/util/disk_cache_os.h index d36bf3d343e..b54af0b38aa 100644 --- a/src/util/disk_cache_os.h +++ b/src/util/disk_cache_os.h @@ -99,6 +99,9 @@ struct disk_cache { unsigned hits; unsigned misses; } stats; + + /* Internal RO FOZ cache for combined use of RO and RW caches. */ + struct disk_cache *foz_ro_cache; }; struct cache_entry_file_data { diff --git a/src/util/fossilize_db.c b/src/util/fossilize_db.c index 6c2bf0c58e4..bfedc42e101 100644 --- a/src/util/fossilize_db.c +++ b/src/util/fossilize_db.c @@ -40,6 +40,8 @@ #include #include +#include "util/u_debug.h" + #include "crc32.h" #include "hash_table.h" #include "mesa-sha1.h" @@ -270,28 +272,32 @@ foz_prepare(struct foz_db *foz_db, char *cache_path) { char *filename = NULL; char *idx_filename = NULL; - if (!create_foz_db_filenames(cache_path, "foz_cache", &filename, &idx_filename)) - goto fail; - - /* Open the default foz dbs for read/write. If the files didn't already exist - * create them. - */ - foz_db->file[0] = fopen(filename, "a+b"); - foz_db->db_idx = fopen(idx_filename, "a+b"); - - free(filename); - free(idx_filename); - - if (!check_files_opened_successfully(foz_db->file[0], foz_db->db_idx)) - goto fail; simple_mtx_init(&foz_db->mtx, mtx_plain); simple_mtx_init(&foz_db->flock_mtx, mtx_plain); foz_db->mem_ctx = ralloc_context(NULL); foz_db->index_db = _mesa_hash_table_u64_create(NULL); - if (!load_foz_dbs(foz_db, foz_db->db_idx, 0, false)) - goto fail; + /* Open the default foz dbs for read/write. If the files didn't already exist + * create them. + */ + if (debug_get_bool_option("MESA_DISK_CACHE_SINGLE_FILE", false)) { + if (!create_foz_db_filenames(cache_path, "foz_cache", + &filename, &idx_filename)) + goto fail; + + foz_db->file[0] = fopen(filename, "a+b"); + foz_db->db_idx = fopen(idx_filename, "a+b"); + + free(filename); + free(idx_filename); + + if (!check_files_opened_successfully(foz_db->file[0], foz_db->db_idx)) + goto fail; + + if (!load_foz_dbs(foz_db, foz_db->db_idx, 0, false)) + goto fail; + } uint8_t file_idx = 1; char *foz_dbs = getenv("MESA_DISK_CACHE_READ_ONLY_FOZ_DBS"); @@ -383,7 +389,7 @@ foz_read_entry(struct foz_db *foz_db, const uint8_t *cache_key_160bit, struct foz_db_entry *entry = _mesa_hash_table_u64_search(foz_db->index_db, hash); - if (!entry) { + if (!entry && foz_db->db_idx) { update_foz_index(foz_db, foz_db->db_idx, 0); entry = _mesa_hash_table_u64_search(foz_db->index_db, hash); } @@ -444,7 +450,7 @@ foz_write_entry(struct foz_db *foz_db, const uint8_t *cache_key_160bit, { uint64_t hash = truncate_hash_to_64bits(cache_key_160bit); - if (!foz_db->alive) + if (!foz_db->alive || !foz_db->file[0]) return false; /* The flock is per-fd, not per thread, we do it outside of the main mutex to avoid having to diff --git a/src/util/tests/cache_test.cpp b/src/util/tests/cache_test.cpp index f0164b4fc72..5d0e7d9b293 100644 --- a/src/util/tests/cache_test.cpp +++ b/src/util/tests/cache_test.cpp @@ -773,3 +773,169 @@ TEST_F(Cache, Database) EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP " again"; #endif } + +TEST_F(Cache, Combined) +{ + const char *driver_id = "make_check"; + char blob[] = "This is a RO blob"; + uint8_t dummy_key[20] = { 0 }; + uint8_t blob_key[20]; + char foz_rw_idx_file[1024]; + char foz_ro_idx_file[1024]; + char foz_rw_file[1024]; + char foz_ro_file[1024]; + char *result; + size_t size; + +#ifndef ENABLE_SHADER_CACHE + GTEST_SKIP() << "ENABLE_SHADER_CACHE not defined."; +#else + setenv("MESA_DISK_CACHE_SINGLE_FILE", "true", 1); + setenv("MESA_DISK_CACHE_DATABASE", "false", 1); + +#ifdef SHADER_CACHE_DISABLE_BY_DEFAULT + setenv("MESA_SHADER_CACHE_DISABLE", "false", 1); +#endif /* SHADER_CACHE_DISABLE_BY_DEFAULT */ + + /* Enable Fossilize read-write cache. */ + setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "true", 1); + + test_disk_cache_create(mem_ctx, CACHE_DIR_NAME_SF, driver_id); + + /* Create Fossilize writable cache. */ + struct disk_cache *cache_sf_wr = disk_cache_create("combined_test", + driver_id, 0); + + disk_cache_compute_key(cache_sf_wr, blob, sizeof(blob), blob_key); + + /* Ensure that disk_cache_get returns nothing before anything is added. */ + result = (char *) disk_cache_get(cache_sf_wr, blob_key, &size); + EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)"; + EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)"; + + /* Put blob entry to the cache. */ + disk_cache_put(cache_sf_wr, blob_key, blob, sizeof(blob), NULL); + disk_cache_wait_for_idle(cache_sf_wr); + + result = (char *) disk_cache_get(cache_sf_wr, blob_key, &size); + EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)"; + EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)"; + free(result); + + /* Rename file foz_cache.foz -> ro_cache.foz */ + sprintf(foz_rw_file, "%s/foz_cache.foz", cache_sf_wr->path); + sprintf(foz_ro_file, "%s/ro_cache.foz", cache_sf_wr->path); + EXPECT_EQ(rename(foz_rw_file, foz_ro_file), 0) << "foz_cache.foz renaming failed"; + + /* Rename file foz_cache_idx.foz -> ro_cache_idx.foz */ + sprintf(foz_rw_idx_file, "%s/foz_cache_idx.foz", cache_sf_wr->path); + sprintf(foz_ro_idx_file, "%s/ro_cache_idx.foz", cache_sf_wr->path); + EXPECT_EQ(rename(foz_rw_idx_file, foz_ro_idx_file), 0) << "foz_cache_idx.foz renaming failed"; + + disk_cache_destroy(cache_sf_wr); + + /* Disable Fossilize read-write cache. */ + setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "false", 1); + + /* Set up Fossilize read-only cache. */ + setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "true", 1); + setenv("MESA_DISK_CACHE_READ_ONLY_FOZ_DBS", "ro_cache", 1); + + /* Create FOZ cache that fetches the RO cache. Note that this produces + * empty RW cache files. */ + struct disk_cache *cache_sf_ro = disk_cache_create("combined_test", + driver_id, 0); + + /* Blob entry must present because it shall be retrieved from the + * ro_cache.foz */ + result = (char *) disk_cache_get(cache_sf_ro, blob_key, &size); + EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)"; + EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)"; + free(result); + + disk_cache_destroy(cache_sf_ro); + + /* Remove empty FOZ RW cache files created above. We only need RO cache. */ + EXPECT_EQ(unlink(foz_rw_file), 0); + EXPECT_EQ(unlink(foz_rw_idx_file), 0); + + setenv("MESA_DISK_CACHE_SINGLE_FILE", "false", 1); + setenv("MESA_DISK_CACHE_DATABASE", "true", 1); + + /* Create MESA-DB cache with enabled retrieval from the read-only + * cache. */ + struct disk_cache *cache_mesa_db = disk_cache_create("combined_test", + driver_id, 0); + + /* Dummy entry must not present in any of the caches. Foz cache + * reloads index if cache entry is missing. This is a sanity-check + * for foz_read_entry(), it should work properly with a disabled + * FOZ RW cache. */ + result = (char *) disk_cache_get(cache_mesa_db, dummy_key, &size); + EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)"; + EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)"; + + /* Blob entry must present because it shall be retrieved from the + * read-only cache. */ + result = (char *) disk_cache_get(cache_mesa_db, blob_key, &size); + EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)"; + EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)"; + free(result); + + disk_cache_destroy(cache_mesa_db); + + /* Disable read-only cache. */ + setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "false", 1); + + /* Create MESA-DB cache with disabled retrieval from the read-only + * cache. */ + cache_mesa_db = disk_cache_create("combined_test", driver_id, 0); + + /* Blob entry must not present in the cache because we disable the + * read-only cache. */ + result = (char *) disk_cache_get(cache_mesa_db, blob_key, &size); + EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)"; + EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)"; + + disk_cache_destroy(cache_mesa_db); + + /* Create default multi-file cache. */ + setenv("MESA_DISK_CACHE_DATABASE", "false", 1); + + /* Enable read-only cache. */ + setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "true", 1); + + /* Create multi-file cache with enabled retrieval from the + * read-only cache. */ + struct disk_cache *cache_multifile = disk_cache_create("combined_test", + driver_id, 0); + + /* Blob entry must present because it shall be retrieved from the + * read-only cache. */ + result = (char *) disk_cache_get(cache_multifile, blob_key, &size); + EXPECT_STREQ(blob, result) << "disk_cache_get of existing item (pointer)"; + EXPECT_EQ(size, sizeof(blob)) << "disk_cache_get of existing item (size)"; + free(result); + + disk_cache_destroy(cache_multifile); + + /* Disable read-only cache. */ + setenv("MESA_DISK_CACHE_COMBINE_RW_WITH_RO_FOZ", "false", 1); + unsetenv("MESA_DISK_CACHE_READ_ONLY_FOZ_DBS"); + + /* Create multi-file cache with disabled retrieval from the + * read-only cache. */ + cache_multifile = disk_cache_create("combined_test", driver_id, 0); + + /* Blob entry must not present in the cache because we disabled the + * read-only cache. */ + result = (char *) disk_cache_get(cache_multifile, blob_key, &size); + EXPECT_EQ(result, nullptr) << "disk_cache_get with non-existent item (pointer)"; + EXPECT_EQ(size, 0) << "disk_cache_get with non-existent item (size)"; + + disk_cache_destroy(cache_multifile); + + int err = rmrf_local(CACHE_TEST_TMP); + EXPECT_EQ(err, 0) << "Removing " CACHE_TEST_TMP " again"; +#endif +}