blob: 56b265ed541c2e6886a74417ae9b1beaf280debf [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ui/lib/escher/vk/pipeline_builder.h"
#include "src/ui/lib/escher/impl/vulkan_utils.h"
#include "src/ui/lib/escher/util/hasher.h"
#include "src/ui/lib/escher/util/trace_macros.h"
namespace escher {
static Hash GenerateHash(const void* bytes, size_t num_bytes) {
TRACE_DURATION("gfx", "PipelineBuilder::GenerateHash");
Hasher h;
if (num_bytes % 4 == 0) {
h.data(reinterpret_cast<const uint32_t*>(bytes), num_bytes / 4);
} else {
h.data(reinterpret_cast<const uint8_t*>(bytes), num_bytes);
}
return h.value();
}
PipelineBuilder::PipelineBuilder(vk::Device device) : device_(device), weak_factory_(this) {
FX_DCHECK(device);
// There is no StorePipelineCacheDataCallback, so hash will never be computed again.
hash_.val = 0;
}
PipelineBuilder::PipelineBuilder(vk::Device device, const void* initial_cache_data,
size_t initial_cache_data_size,
StorePipelineCacheDataCallback store_data_callback)
: device_(device), store_data_callback_(std::move(store_data_callback)), weak_factory_(this) {
FX_DCHECK(device);
FX_DCHECK(store_data_callback_);
FX_DCHECK(initial_cache_data || initial_cache_data_size == 0);
TRACE_DURATION("gfx", "PipelineBuilder[constructor]");
// Use the initial data to populate a VkPipelineCache.
vk::PipelineCacheCreateInfo info;
info.initialDataSize = initial_cache_data_size;
info.pInitialData = initial_cache_data;
cache_ = ESCHER_CHECKED_VK_RESULT(device_.createPipelineCache(info));
// Initialize the hash for comparison in |MaybeStorePipelineCacheData()|; the callback will only
// be invoked when the cache data changes.
hash_ = GenerateHash(initial_cache_data, initial_cache_data_size);
}
PipelineBuilder::~PipelineBuilder() {
MaybeStorePipelineCacheData();
device_.destroyPipelineCache(cache_);
}
vk::Pipeline PipelineBuilder::BuildGraphicsPipeline(const vk::GraphicsPipelineCreateInfo& info,
bool do_logging) {
TRACE_DURATION("gfx", "PipelineBuilder::BuildGraphicsPipeline");
if (do_logging && log_creation_callback_) {
TRACE_DURATION("gfx", "PipelineBuilder::BuildGraphicsPipeline[logging]");
log_creation_callback_(&info, nullptr);
}
created_pipeline_since_last_store_ = true;
auto pipeline = ESCHER_CHECKED_VK_RESULT(device_.createGraphicsPipeline(cache_, info));
return pipeline;
}
vk::Pipeline PipelineBuilder::BuildComputePipeline(const vk::ComputePipelineCreateInfo& info,
bool do_logging) {
TRACE_DURATION("gfx", "PipelineBuilder::BuildComputePipeline");
if (do_logging && log_creation_callback_) {
TRACE_DURATION("gfx", "PipelineBuilder::BuildComputePipeline[logging]");
log_creation_callback_(nullptr, &info);
}
created_pipeline_since_last_store_ = true;
return ESCHER_CHECKED_VK_RESULT(device_.createComputePipeline(cache_, info));
}
void PipelineBuilder::MaybeStorePipelineCacheData() {
if (created_pipeline_since_last_store_ && store_data_callback_) {
created_pipeline_since_last_store_ = false;
std::vector<uint8_t> bytes;
{
TRACE_DURATION("gfx", "PipelineBuilder::MaybeStorePipelineCacheData[vulkan]");
bytes = ESCHER_CHECKED_VK_RESULT(device_.getPipelineCacheData(cache_));
}
// We'll only invoke the callback when the cache data changes.
auto new_hash = GenerateHash(bytes.data(), bytes.size());
if (hash_ != new_hash) {
TRACE_DURATION("gfx", "PipelineBuilder::MaybeStorePipelineCacheData[callback]", "hash",
TA_UINT64(new_hash.val), "num_bytes", TA_UINT64(bytes.size()));
hash_ = new_hash;
store_data_callback_(std::move(bytes));
}
}
}
} // namespace escher