mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-19 00:38:06 +02:00
Signed-off-by: Samuel Pitoiset <samuel.pitoiset@gmail.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/32254>
733 lines
25 KiB
C
733 lines
25 KiB
C
/*
|
|
* Copyright © 2015 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include "radv_pipeline_cache.h"
|
|
#include "util/disk_cache.h"
|
|
#include "util/macros.h"
|
|
#include "util/mesa-blake3.h"
|
|
#include "util/mesa-sha1.h"
|
|
#include "util/u_atomic.h"
|
|
#include "util/u_debug.h"
|
|
#include "nir_serialize.h"
|
|
#include "radv_debug.h"
|
|
#include "radv_descriptor_set.h"
|
|
#include "radv_pipeline.h"
|
|
#include "radv_pipeline_compute.h"
|
|
#include "radv_pipeline_graphics.h"
|
|
#include "radv_pipeline_binary.h"
|
|
#include "radv_pipeline_rt.h"
|
|
#include "radv_shader.h"
|
|
#include "vk_pipeline.h"
|
|
#include "vk_util.h"
|
|
|
|
#include "aco_interface.h"
|
|
|
|
void
|
|
radv_hash_graphics_spirv_to_nir(blake3_hash hash, const struct radv_shader_stage *stage,
|
|
const struct radv_spirv_to_nir_options *options)
|
|
{
|
|
struct mesa_blake3 ctx;
|
|
_mesa_blake3_init(&ctx);
|
|
_mesa_blake3_update(&ctx, &stage->key, sizeof(stage->key));
|
|
_mesa_blake3_update(&ctx, options, sizeof(*options));
|
|
_mesa_blake3_update(&ctx, stage->shader_sha1, sizeof(stage->shader_sha1));
|
|
_mesa_blake3_final(&ctx, hash);
|
|
}
|
|
|
|
static void
|
|
radv_shader_destroy(struct vk_device *_device, struct vk_pipeline_cache_object *object)
|
|
{
|
|
struct radv_device *device = container_of(_device, struct radv_device, vk);
|
|
struct radv_shader *shader = container_of(object, struct radv_shader, base);
|
|
|
|
if (device->shader_use_invisible_vram) {
|
|
/* Wait for any pending upload to complete, or we'll be writing into freed shader memory. */
|
|
radv_shader_wait_for_upload(device, shader->upload_seq);
|
|
}
|
|
|
|
radv_free_shader_memory(device, shader->alloc);
|
|
|
|
free(shader->code);
|
|
free(shader->spirv);
|
|
free(shader->nir_string);
|
|
free(shader->disasm_string);
|
|
free(shader->ir_string);
|
|
free(shader->statistics);
|
|
free(shader->debug_info);
|
|
|
|
vk_pipeline_cache_object_finish(&shader->base);
|
|
free(shader);
|
|
}
|
|
|
|
struct radv_shader *
|
|
radv_shader_deserialize(struct radv_device *device, const void *key_data, size_t key_size, struct blob_reader *blob)
|
|
{
|
|
const struct radv_shader_binary *binary = blob_read_bytes(blob, sizeof(struct radv_shader_binary));
|
|
|
|
struct radv_shader *shader;
|
|
radv_shader_create_uncached(device, binary, false, NULL, &shader);
|
|
if (!shader)
|
|
return NULL;
|
|
|
|
assert(key_size == sizeof(shader->hash));
|
|
memcpy(shader->hash, key_data, key_size);
|
|
blob_skip_bytes(blob, binary->total_size - sizeof(struct radv_shader_binary));
|
|
|
|
return shader;
|
|
}
|
|
|
|
static struct vk_pipeline_cache_object *
|
|
radv_shader_cache_deserialize(struct vk_pipeline_cache *cache, const void *key_data, size_t key_size,
|
|
struct blob_reader *blob)
|
|
{
|
|
struct radv_device *device = container_of(cache->base.device, struct radv_device, vk);
|
|
struct radv_shader *shader;
|
|
|
|
shader = radv_shader_deserialize(device, key_data, key_size, blob);
|
|
|
|
return shader ? &shader->base : NULL;
|
|
}
|
|
|
|
void
|
|
radv_shader_serialize(struct radv_shader *shader, struct blob *blob)
|
|
{
|
|
size_t stats_size = shader->statistics ? aco_num_statistics * sizeof(uint32_t) : 0;
|
|
size_t code_size = shader->code_size;
|
|
uint32_t total_size = sizeof(struct radv_shader_binary_legacy) + code_size + stats_size;
|
|
|
|
struct radv_shader_binary_legacy binary = {
|
|
.base =
|
|
{
|
|
.type = RADV_BINARY_TYPE_LEGACY,
|
|
.config = shader->config,
|
|
.info = shader->info,
|
|
.total_size = total_size,
|
|
},
|
|
.code_size = code_size,
|
|
.exec_size = shader->exec_size,
|
|
.ir_size = 0,
|
|
.disasm_size = 0,
|
|
.stats_size = stats_size,
|
|
};
|
|
|
|
blob_write_bytes(blob, &binary, sizeof(struct radv_shader_binary_legacy));
|
|
blob_write_bytes(blob, shader->statistics, stats_size);
|
|
blob_write_bytes(blob, shader->code, code_size);
|
|
}
|
|
|
|
static bool
|
|
radv_shader_cache_serialize(struct vk_pipeline_cache_object *object, struct blob *blob)
|
|
{
|
|
struct radv_shader *shader = container_of(object, struct radv_shader, base);
|
|
|
|
radv_shader_serialize(shader, blob);
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
radv_is_cache_disabled(const struct radv_device *device, const struct vk_pipeline_cache *cache)
|
|
{
|
|
const struct radv_physical_device *pdev = radv_device_physical(device);
|
|
const struct radv_instance *instance = radv_physical_device_instance(pdev);
|
|
|
|
/* The buffer address used for debug printf is hardcoded. */
|
|
if (device->printf.buffer_addr)
|
|
return true;
|
|
|
|
/* Pipeline caches can be disabled with RADV_DEBUG=nocache, with MESA_GLSL_CACHE_DISABLE=1 and
|
|
* when ACO_DEBUG is used. MESA_GLSL_CACHE_DISABLE is done elsewhere.
|
|
*/
|
|
if ((instance->debug_flags & RADV_DEBUG_NO_CACHE) || (pdev->use_llvm ? 0 : aco_get_codegen_flags()))
|
|
return true;
|
|
|
|
if (!cache) {
|
|
/* When the application doesn't provide a pipeline cache and the in-memory cache is also
|
|
* disabled.
|
|
*/
|
|
cache = device->mem_cache;
|
|
if (!cache)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
struct radv_shader *
|
|
radv_shader_create(struct radv_device *device, struct vk_pipeline_cache *cache, const struct radv_shader_binary *binary,
|
|
bool skip_cache)
|
|
{
|
|
if (radv_is_cache_disabled(device, cache) || skip_cache) {
|
|
struct radv_shader *shader;
|
|
radv_shader_create_uncached(device, binary, false, NULL, &shader);
|
|
return shader;
|
|
}
|
|
|
|
if (!cache)
|
|
cache = device->mem_cache;
|
|
|
|
blake3_hash hash;
|
|
_mesa_blake3_compute(binary, binary->total_size, hash);
|
|
|
|
struct vk_pipeline_cache_object *shader_obj;
|
|
shader_obj = vk_pipeline_cache_create_and_insert_object(cache, hash, sizeof(hash), binary, binary->total_size,
|
|
&radv_shader_ops);
|
|
|
|
return shader_obj ? container_of(shader_obj, struct radv_shader, base) : NULL;
|
|
}
|
|
|
|
const struct vk_pipeline_cache_object_ops radv_shader_ops = {
|
|
.serialize = radv_shader_cache_serialize,
|
|
.deserialize = radv_shader_cache_deserialize,
|
|
.destroy = radv_shader_destroy,
|
|
};
|
|
|
|
struct radv_pipeline_cache_object {
|
|
struct vk_pipeline_cache_object base;
|
|
unsigned num_shaders;
|
|
uint32_t data_size;
|
|
void *data; /* Generic data stored alongside the shaders */
|
|
uint8_t sha1[SHA1_DIGEST_LENGTH];
|
|
struct radv_shader *shaders[];
|
|
};
|
|
|
|
const struct vk_pipeline_cache_object_ops radv_pipeline_ops;
|
|
|
|
static struct radv_pipeline_cache_object *
|
|
radv_pipeline_cache_object_create(struct vk_device *device, unsigned num_shaders, const void *hash, unsigned data_size)
|
|
{
|
|
const size_t size =
|
|
sizeof(struct radv_pipeline_cache_object) + (num_shaders * sizeof(struct radv_shader *)) + data_size;
|
|
|
|
struct radv_pipeline_cache_object *object = vk_alloc(&device->alloc, size, 8, VK_SYSTEM_ALLOCATION_SCOPE_CACHE);
|
|
if (!object)
|
|
return NULL;
|
|
|
|
vk_pipeline_cache_object_init(device, &object->base, &radv_pipeline_ops, object->sha1, SHA1_DIGEST_LENGTH);
|
|
object->num_shaders = num_shaders;
|
|
object->data = &object->shaders[num_shaders];
|
|
object->data_size = data_size;
|
|
memcpy(object->sha1, hash, SHA1_DIGEST_LENGTH);
|
|
memset(object->shaders, 0, sizeof(object->shaders[0]) * num_shaders);
|
|
memset(object->data, 0, data_size);
|
|
|
|
return object;
|
|
}
|
|
|
|
static void
|
|
radv_pipeline_cache_object_destroy(struct vk_device *_device, struct vk_pipeline_cache_object *object)
|
|
{
|
|
struct radv_device *device = container_of(_device, struct radv_device, vk);
|
|
struct radv_pipeline_cache_object *pipeline_obj = container_of(object, struct radv_pipeline_cache_object, base);
|
|
|
|
for (unsigned i = 0; i < pipeline_obj->num_shaders; i++) {
|
|
if (pipeline_obj->shaders[i])
|
|
radv_shader_unref(device, pipeline_obj->shaders[i]);
|
|
}
|
|
|
|
vk_pipeline_cache_object_finish(&pipeline_obj->base);
|
|
vk_free(&_device->alloc, pipeline_obj);
|
|
}
|
|
|
|
static struct vk_pipeline_cache_object *
|
|
radv_pipeline_cache_object_deserialize(struct vk_pipeline_cache *cache, const void *key_data, size_t key_size,
|
|
struct blob_reader *blob)
|
|
{
|
|
struct radv_device *device = container_of(cache->base.device, struct radv_device, vk);
|
|
assert(key_size == SHA1_DIGEST_LENGTH);
|
|
unsigned total_size = blob->end - blob->current;
|
|
unsigned num_shaders = blob_read_uint32(blob);
|
|
unsigned data_size = blob_read_uint32(blob);
|
|
|
|
struct radv_pipeline_cache_object *object;
|
|
object = radv_pipeline_cache_object_create(&device->vk, num_shaders, key_data, data_size);
|
|
if (!object)
|
|
return NULL;
|
|
|
|
object->base.data_size = total_size;
|
|
|
|
for (unsigned i = 0; i < num_shaders; i++) {
|
|
const uint8_t *hash = blob_read_bytes(blob, sizeof(blake3_hash));
|
|
struct vk_pipeline_cache_object *shader =
|
|
vk_pipeline_cache_lookup_object(cache, hash, sizeof(blake3_hash), &radv_shader_ops, NULL);
|
|
|
|
if (!shader) {
|
|
/* If some shader could not be created from cache, better return NULL here than having
|
|
* an incomplete cache object which needs to be fixed up later.
|
|
*/
|
|
vk_pipeline_cache_object_unref(&device->vk, &object->base);
|
|
return NULL;
|
|
}
|
|
|
|
object->shaders[i] = container_of(shader, struct radv_shader, base);
|
|
}
|
|
|
|
blob_copy_bytes(blob, object->data, data_size);
|
|
|
|
return &object->base;
|
|
}
|
|
|
|
static bool
|
|
radv_pipeline_cache_object_serialize(struct vk_pipeline_cache_object *object, struct blob *blob)
|
|
{
|
|
struct radv_pipeline_cache_object *pipeline_obj = container_of(object, struct radv_pipeline_cache_object, base);
|
|
|
|
blob_write_uint32(blob, pipeline_obj->num_shaders);
|
|
blob_write_uint32(blob, pipeline_obj->data_size);
|
|
|
|
for (unsigned i = 0; i < pipeline_obj->num_shaders; i++)
|
|
blob_write_bytes(blob, pipeline_obj->shaders[i]->hash, sizeof(pipeline_obj->shaders[i]->hash));
|
|
|
|
blob_write_bytes(blob, pipeline_obj->data, pipeline_obj->data_size);
|
|
|
|
return true;
|
|
}
|
|
|
|
const struct vk_pipeline_cache_object_ops radv_pipeline_ops = {
|
|
.serialize = radv_pipeline_cache_object_serialize,
|
|
.deserialize = radv_pipeline_cache_object_deserialize,
|
|
.destroy = radv_pipeline_cache_object_destroy,
|
|
};
|
|
|
|
static void
|
|
radv_report_pso_cache_stats(struct radv_device *device, const struct radv_pipeline *pipeline, bool cache_hit)
|
|
{
|
|
const struct radv_physical_device *pdev = radv_device_physical(device);
|
|
const struct radv_instance *instance = radv_physical_device_instance(pdev);
|
|
|
|
if (!(instance->debug_flags & RADV_DEBUG_PSO_CACHE_STATS))
|
|
return;
|
|
|
|
/* Only gather PSO cache stats for application pipelines. */
|
|
if (pipeline->is_internal)
|
|
return;
|
|
|
|
assert(pipeline->type < ARRAY_SIZE(device->pso_cache_stats));
|
|
|
|
simple_mtx_lock(&device->pso_cache_stats_mtx);
|
|
|
|
if (cache_hit) {
|
|
device->pso_cache_stats[pipeline->type].hits++;
|
|
} else {
|
|
device->pso_cache_stats[pipeline->type].misses++;
|
|
}
|
|
|
|
fprintf(
|
|
stderr,
|
|
"radv: PSO cache stats: gfx (hits=%d, misses=%d), gfx_lib (hits=%d, misses=%d), compute (hits=%d, misses=%d), rt "
|
|
"(hits=%d, misses=%d)\n",
|
|
device->pso_cache_stats[RADV_PIPELINE_GRAPHICS].hits, device->pso_cache_stats[RADV_PIPELINE_GRAPHICS].misses,
|
|
device->pso_cache_stats[RADV_PIPELINE_GRAPHICS_LIB].hits,
|
|
device->pso_cache_stats[RADV_PIPELINE_GRAPHICS_LIB].misses, device->pso_cache_stats[RADV_PIPELINE_COMPUTE].hits,
|
|
device->pso_cache_stats[RADV_PIPELINE_COMPUTE].misses, device->pso_cache_stats[RADV_PIPELINE_RAY_TRACING].hits,
|
|
device->pso_cache_stats[RADV_PIPELINE_RAY_TRACING].misses);
|
|
|
|
simple_mtx_unlock(&device->pso_cache_stats_mtx);
|
|
}
|
|
|
|
static struct radv_pipeline_cache_object *
|
|
radv_pipeline_cache_object_search(struct radv_device *device, struct vk_pipeline_cache *cache,
|
|
const struct radv_pipeline *pipeline, bool *found_in_application_cache)
|
|
{
|
|
*found_in_application_cache = false;
|
|
|
|
if (radv_is_cache_disabled(device, cache))
|
|
return false;
|
|
|
|
bool *found = found_in_application_cache;
|
|
if (!cache) {
|
|
cache = device->mem_cache;
|
|
found = NULL;
|
|
}
|
|
|
|
struct vk_pipeline_cache_object *object =
|
|
vk_pipeline_cache_lookup_object(cache, pipeline->sha1, SHA1_DIGEST_LENGTH, &radv_pipeline_ops, found);
|
|
|
|
radv_report_pso_cache_stats(device, pipeline, !!object);
|
|
|
|
if (!object)
|
|
return false;
|
|
|
|
return container_of(object, struct radv_pipeline_cache_object, base);
|
|
}
|
|
|
|
bool
|
|
radv_graphics_pipeline_cache_search(struct radv_device *device, struct vk_pipeline_cache *cache,
|
|
struct radv_graphics_pipeline *pipeline, bool *found_in_application_cache)
|
|
{
|
|
struct radv_pipeline_cache_object *pipeline_obj;
|
|
|
|
pipeline_obj = radv_pipeline_cache_object_search(device, cache, &pipeline->base, found_in_application_cache);
|
|
if (!pipeline_obj)
|
|
return false;
|
|
|
|
for (unsigned i = 0; i < pipeline_obj->num_shaders; i++) {
|
|
gl_shader_stage s = pipeline_obj->shaders[i]->info.stage;
|
|
if (s == MESA_SHADER_VERTEX && i > 0) {
|
|
/* The GS copy-shader is a VS placed after all other stages */
|
|
assert(i == pipeline_obj->num_shaders - 1 && pipeline->base.shaders[MESA_SHADER_GEOMETRY]);
|
|
pipeline->base.gs_copy_shader = radv_shader_ref(pipeline_obj->shaders[i]);
|
|
} else {
|
|
pipeline->base.shaders[s] = radv_shader_ref(pipeline_obj->shaders[i]);
|
|
}
|
|
}
|
|
|
|
pipeline->base.cache_object = &pipeline_obj->base;
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
radv_compute_pipeline_cache_search(struct radv_device *device, struct vk_pipeline_cache *cache,
|
|
struct radv_compute_pipeline *pipeline, bool *found_in_application_cache)
|
|
{
|
|
struct radv_pipeline_cache_object *pipeline_obj;
|
|
|
|
pipeline_obj = radv_pipeline_cache_object_search(device, cache, &pipeline->base, found_in_application_cache);
|
|
if (!pipeline_obj)
|
|
return false;
|
|
|
|
assert(pipeline_obj->num_shaders == 1);
|
|
pipeline->base.shaders[MESA_SHADER_COMPUTE] = radv_shader_ref(pipeline_obj->shaders[0]);
|
|
|
|
pipeline->base.cache_object = &pipeline_obj->base;
|
|
return true;
|
|
}
|
|
|
|
void
|
|
radv_pipeline_cache_insert(struct radv_device *device, struct vk_pipeline_cache *cache, struct radv_pipeline *pipeline)
|
|
{
|
|
if (radv_is_cache_disabled(device, cache))
|
|
return;
|
|
|
|
if (!cache)
|
|
cache = device->mem_cache;
|
|
|
|
/* Count shaders */
|
|
unsigned num_shaders = 0;
|
|
for (unsigned i = 0; i < MESA_VULKAN_SHADER_STAGES; ++i)
|
|
num_shaders += pipeline->shaders[i] ? 1 : 0;
|
|
num_shaders += pipeline->gs_copy_shader ? 1 : 0;
|
|
|
|
struct radv_pipeline_cache_object *pipeline_obj;
|
|
pipeline_obj = radv_pipeline_cache_object_create(&device->vk, num_shaders, pipeline->sha1, 0);
|
|
|
|
if (!pipeline_obj)
|
|
return;
|
|
|
|
unsigned idx = 0;
|
|
for (unsigned i = 0; i < MESA_VULKAN_SHADER_STAGES; ++i) {
|
|
if (pipeline->shaders[i])
|
|
pipeline_obj->shaders[idx++] = radv_shader_ref(pipeline->shaders[i]);
|
|
}
|
|
/* Place the GS copy-shader after all other stages */
|
|
if (pipeline->gs_copy_shader)
|
|
pipeline_obj->shaders[idx++] = radv_shader_ref(pipeline->gs_copy_shader);
|
|
|
|
assert(idx == num_shaders);
|
|
|
|
/* Add the object to the cache */
|
|
pipeline->cache_object = vk_pipeline_cache_add_object(cache, &pipeline_obj->base);
|
|
}
|
|
|
|
struct radv_ray_tracing_stage_cache_data {
|
|
uint32_t stack_size : 31;
|
|
uint32_t has_shader : 1;
|
|
uint8_t sha1[SHA1_DIGEST_LENGTH];
|
|
struct radv_ray_tracing_stage_info info;
|
|
};
|
|
|
|
struct radv_ray_tracing_pipeline_cache_data {
|
|
uint32_t has_traversal_shader : 1;
|
|
uint32_t is_library : 1;
|
|
uint32_t num_stages;
|
|
struct radv_ray_tracing_stage_cache_data stages[];
|
|
};
|
|
|
|
bool
|
|
radv_ray_tracing_pipeline_cache_search(struct radv_device *device, struct vk_pipeline_cache *cache,
|
|
struct radv_ray_tracing_pipeline *pipeline,
|
|
bool *found_in_application_cache)
|
|
{
|
|
struct radv_pipeline_cache_object *pipeline_obj;
|
|
|
|
pipeline_obj = radv_pipeline_cache_object_search(device, cache, &pipeline->base.base, found_in_application_cache);
|
|
if (!pipeline_obj)
|
|
return false;
|
|
|
|
struct radv_ray_tracing_pipeline_cache_data *data = pipeline_obj->data;
|
|
|
|
bool complete = true;
|
|
unsigned idx = 0;
|
|
|
|
if (data->has_traversal_shader)
|
|
pipeline->base.base.shaders[MESA_SHADER_INTERSECTION] = radv_shader_ref(pipeline_obj->shaders[idx++]);
|
|
|
|
const uint32_t num_stages = data->num_stages;
|
|
for (unsigned i = 0; i < num_stages; i++) {
|
|
pipeline->stages[i].stack_size = data->stages[i].stack_size;
|
|
pipeline->stages[i].info = data->stages[i].info;
|
|
memcpy(pipeline->stages[i].sha1, data->stages[i].sha1, sizeof(pipeline->stages[i].sha1));
|
|
|
|
if (data->stages[i].has_shader)
|
|
pipeline->stages[i].shader = radv_shader_ref(pipeline_obj->shaders[idx++]);
|
|
|
|
if (data->is_library) {
|
|
pipeline->stages[i].nir = radv_pipeline_cache_lookup_nir_handle(device, cache, pipeline->stages[i].sha1);
|
|
complete &= pipeline->stages[i].nir != NULL;
|
|
}
|
|
}
|
|
|
|
assert(idx == pipeline_obj->num_shaders);
|
|
|
|
pipeline->base.base.cache_object = &pipeline_obj->base;
|
|
return complete;
|
|
}
|
|
|
|
void
|
|
radv_ray_tracing_pipeline_cache_insert(struct radv_device *device, struct vk_pipeline_cache *cache,
|
|
struct radv_ray_tracing_pipeline *pipeline, unsigned num_stages)
|
|
{
|
|
if (radv_is_cache_disabled(device, cache))
|
|
return;
|
|
|
|
if (!cache)
|
|
cache = device->mem_cache;
|
|
|
|
/* Skip insertion on cache hit.
|
|
* This branch can be triggered if a cache_object was found but not all NIR shaders could be
|
|
* looked up. The cache_object is already complete in that case.
|
|
*/
|
|
if (pipeline->base.base.cache_object)
|
|
return;
|
|
|
|
/* Count compiled shaders excl. library shaders */
|
|
unsigned num_shaders = pipeline->base.base.shaders[MESA_SHADER_INTERSECTION] ? 1 : 0;
|
|
for (unsigned i = 0; i < num_stages; ++i)
|
|
num_shaders += pipeline->stages[i].shader ? 1 : 0;
|
|
|
|
uint32_t data_size = sizeof(struct radv_ray_tracing_pipeline_cache_data) +
|
|
num_stages * sizeof(struct radv_ray_tracing_stage_cache_data);
|
|
|
|
struct radv_pipeline_cache_object *pipeline_obj =
|
|
radv_pipeline_cache_object_create(&device->vk, num_shaders, pipeline->base.base.sha1, data_size);
|
|
struct radv_ray_tracing_pipeline_cache_data *data = pipeline_obj->data;
|
|
|
|
data->is_library = !!(pipeline->base.base.create_flags & VK_PIPELINE_CREATE_2_LIBRARY_BIT_KHR);
|
|
data->has_traversal_shader = !!pipeline->base.base.shaders[MESA_SHADER_INTERSECTION];
|
|
|
|
unsigned idx = 0;
|
|
if (data->has_traversal_shader)
|
|
pipeline_obj->shaders[idx++] = radv_shader_ref(pipeline->base.base.shaders[MESA_SHADER_INTERSECTION]);
|
|
|
|
data->num_stages = num_stages;
|
|
|
|
for (unsigned i = 0; i < num_stages; ++i) {
|
|
data->stages[i].stack_size = pipeline->stages[i].stack_size;
|
|
data->stages[i].info = pipeline->stages[i].info;
|
|
data->stages[i].has_shader = !!pipeline->stages[i].shader;
|
|
memcpy(data->stages[i].sha1, pipeline->stages[i].sha1, sizeof(pipeline->stages[i].sha1));
|
|
|
|
if (pipeline->stages[i].shader)
|
|
pipeline_obj->shaders[idx++] = radv_shader_ref(pipeline->stages[i].shader);
|
|
}
|
|
assert(idx == num_shaders);
|
|
|
|
/* Add the object to the cache */
|
|
pipeline->base.base.cache_object = vk_pipeline_cache_add_object(cache, &pipeline_obj->base);
|
|
}
|
|
|
|
nir_shader *
|
|
radv_pipeline_cache_lookup_nir(struct radv_device *device, struct vk_pipeline_cache *cache, gl_shader_stage stage,
|
|
const blake3_hash key)
|
|
{
|
|
const struct radv_physical_device *pdev = radv_device_physical(device);
|
|
|
|
if (radv_is_cache_disabled(device, cache))
|
|
return NULL;
|
|
|
|
if (!cache)
|
|
cache = device->mem_cache;
|
|
|
|
return vk_pipeline_cache_lookup_nir(cache, key, sizeof(blake3_hash), &pdev->nir_options[stage], NULL, NULL);
|
|
}
|
|
|
|
void
|
|
radv_pipeline_cache_insert_nir(struct radv_device *device, struct vk_pipeline_cache *cache, const blake3_hash key,
|
|
const nir_shader *nir)
|
|
{
|
|
if (radv_is_cache_disabled(device, cache))
|
|
return;
|
|
|
|
if (!cache)
|
|
cache = device->mem_cache;
|
|
|
|
vk_pipeline_cache_add_nir(cache, key, sizeof(blake3_hash), nir);
|
|
}
|
|
|
|
struct vk_pipeline_cache_object *
|
|
radv_pipeline_cache_lookup_nir_handle(struct radv_device *device, struct vk_pipeline_cache *cache, const uint8_t *sha1)
|
|
{
|
|
if (radv_is_cache_disabled(device, cache))
|
|
return NULL;
|
|
|
|
if (!cache)
|
|
cache = device->mem_cache;
|
|
|
|
return vk_pipeline_cache_lookup_object(cache, sha1, SHA1_DIGEST_LENGTH, &vk_raw_data_cache_object_ops, NULL);
|
|
}
|
|
|
|
struct nir_shader *
|
|
radv_pipeline_cache_handle_to_nir(struct radv_device *device, struct vk_pipeline_cache_object *object)
|
|
{
|
|
const struct radv_physical_device *pdev = radv_device_physical(device);
|
|
struct blob_reader blob;
|
|
struct vk_raw_data_cache_object *nir_object = container_of(object, struct vk_raw_data_cache_object, base);
|
|
blob_reader_init(&blob, nir_object->data, nir_object->data_size);
|
|
nir_shader *nir = nir_deserialize(NULL, NULL, &blob);
|
|
|
|
if (blob.overrun) {
|
|
ralloc_free(nir);
|
|
return NULL;
|
|
}
|
|
nir->options = &pdev->nir_options[nir->info.stage];
|
|
|
|
return nir;
|
|
}
|
|
|
|
struct vk_pipeline_cache_object *
|
|
radv_pipeline_cache_nir_to_handle(struct radv_device *device, struct vk_pipeline_cache *cache, struct nir_shader *nir,
|
|
const uint8_t *sha1, bool cached)
|
|
{
|
|
if (!cache)
|
|
cache = device->mem_cache;
|
|
|
|
struct blob blob;
|
|
blob_init(&blob);
|
|
nir_serialize(&blob, nir, true);
|
|
|
|
if (blob.out_of_memory) {
|
|
blob_finish(&blob);
|
|
return NULL;
|
|
}
|
|
|
|
void *data;
|
|
size_t size;
|
|
blob_finish_get_buffer(&blob, &data, &size);
|
|
struct vk_pipeline_cache_object *object;
|
|
|
|
if (cached && !radv_is_cache_disabled(device, cache)) {
|
|
object = vk_pipeline_cache_create_and_insert_object(cache, sha1, SHA1_DIGEST_LENGTH, data, size,
|
|
&vk_raw_data_cache_object_ops);
|
|
} else {
|
|
struct vk_raw_data_cache_object *nir_object =
|
|
vk_raw_data_cache_object_create(&device->vk, sha1, SHA1_DIGEST_LENGTH, data, size);
|
|
object = nir_object ? &nir_object->base : NULL;
|
|
}
|
|
|
|
free(data);
|
|
return object;
|
|
}
|
|
|
|
VkResult
|
|
radv_pipeline_cache_get_binaries(struct radv_device *device, const VkAllocationCallbacks *pAllocator,
|
|
const unsigned char *sha1, struct util_dynarray *pipeline_binaries,
|
|
uint32_t *num_binaries, bool *found_in_internal_cache)
|
|
{
|
|
struct vk_pipeline_cache *cache = device->mem_cache;
|
|
VkResult result;
|
|
|
|
*found_in_internal_cache = false;
|
|
|
|
if (radv_is_cache_disabled(device, cache))
|
|
return VK_SUCCESS;
|
|
|
|
struct vk_pipeline_cache_object *object =
|
|
vk_pipeline_cache_lookup_object(cache, sha1, SHA1_DIGEST_LENGTH, &radv_pipeline_ops, NULL);
|
|
if (!object)
|
|
return VK_SUCCESS;
|
|
|
|
struct radv_pipeline_cache_object *pipeline_obj = container_of(object, struct radv_pipeline_cache_object, base);
|
|
|
|
bool complete = true;
|
|
bool is_rt = false;
|
|
for (unsigned i = 0; i < pipeline_obj->num_shaders; i++) {
|
|
if (gl_shader_stage_is_rt(pipeline_obj->shaders[i]->info.stage)) {
|
|
is_rt = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (is_rt) {
|
|
struct radv_ray_tracing_pipeline_cache_data *data = pipeline_obj->data;
|
|
struct radv_shader *traversal_shader = NULL;
|
|
unsigned idx = 0;
|
|
|
|
if (data->has_traversal_shader)
|
|
traversal_shader = pipeline_obj->shaders[idx++];
|
|
|
|
for (unsigned i = 0; i < data->num_stages; i++) {
|
|
const struct radv_ray_tracing_stage_cache_data *stage_data = &data->stages[i];
|
|
struct vk_pipeline_cache_object *nir = NULL;
|
|
struct radv_shader *shader = NULL;
|
|
|
|
if (stage_data->has_shader)
|
|
shader = pipeline_obj->shaders[idx++];
|
|
|
|
if (data->is_library)
|
|
nir = radv_pipeline_cache_lookup_nir_handle(device, cache, data->stages[i].sha1);
|
|
|
|
result = radv_create_pipeline_binary_from_rt_shader(device, pAllocator, shader, false, data->stages[i].sha1,
|
|
&stage_data->info, stage_data->stack_size, nir,
|
|
pipeline_binaries, num_binaries);
|
|
|
|
if (data->is_library)
|
|
complete &= nir != NULL;
|
|
|
|
if (nir)
|
|
vk_pipeline_cache_object_unref(&device->vk, nir);
|
|
|
|
if (result != VK_SUCCESS)
|
|
goto fail;
|
|
}
|
|
|
|
if (traversal_shader) {
|
|
result = radv_create_pipeline_binary_from_rt_shader(device, pAllocator, traversal_shader, true,
|
|
traversal_shader->hash, NULL, 0, NULL, pipeline_binaries,
|
|
num_binaries);
|
|
if (result != VK_SUCCESS)
|
|
goto fail;
|
|
}
|
|
} else {
|
|
struct radv_shader *gs_copy_shader = NULL;
|
|
|
|
for (unsigned i = 0; i < pipeline_obj->num_shaders; i++) {
|
|
struct radv_shader *shader = pipeline_obj->shaders[i];
|
|
gl_shader_stage s = shader->info.stage;
|
|
|
|
if (s == MESA_SHADER_VERTEX && i > 0) {
|
|
/* The GS copy-shader is a VS placed after all other stages */
|
|
gs_copy_shader = shader;
|
|
} else {
|
|
result =
|
|
radv_create_pipeline_binary_from_shader(device, pAllocator, shader, pipeline_binaries, num_binaries);
|
|
if (result != VK_SUCCESS)
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
if (gs_copy_shader) {
|
|
result = radv_create_pipeline_binary_from_shader(device, pAllocator, gs_copy_shader, pipeline_binaries,
|
|
num_binaries);
|
|
if (result != VK_SUCCESS)
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
*found_in_internal_cache = complete;
|
|
|
|
fail:
|
|
vk_pipeline_cache_object_unref(&device->vk, &pipeline_obj->base);
|
|
return result;
|
|
}
|