blob: 10fb434c1d14664e8d69d665c9a1ad33caeb5bc3 [file] [log] [blame]
// Copyright 2018 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 "lib/escher/vk/shader_program.h"
#include "lib/escher/escher.h"
#include "lib/escher/impl/vulkan_utils.h"
#include "lib/escher/resources/resource_recycler.h"
#include "lib/escher/third_party/granite/vk/pipeline_layout.h"
#include "lib/escher/third_party/granite/vk/shader_utils.h"
#include "lib/escher/util/enum_cast.h"
#include "lib/escher/util/hasher.h"
#include "lib/escher/util/trace_macros.h"
#include "lib/escher/vk/impl/pipeline_layout_cache.h"
namespace escher {
const ResourceTypeInfo ShaderProgram::kTypeInfo("ShaderProgram",
ResourceType::kResource,
ResourceType::kShaderProgram);
ShaderProgramPtr ShaderProgram::NewGraphics(
ResourceRecycler* resource_recycler,
std::vector<ShaderModulePtr> shader_modules) {
auto prog = new ShaderProgram(resource_recycler, std::move(shader_modules));
return fxl::AdoptRef(prog);
}
ShaderProgramPtr ShaderProgram::NewCompute(ResourceRecycler* resource_recycler,
ShaderModulePtr shader_module) {
auto prog = new ShaderProgram(resource_recycler, std::move(shader_module));
return fxl::AdoptRef(prog);
}
ShaderProgram::ShaderProgram(ResourceRecycler* resource_recycler,
std::vector<ShaderModulePtr> shader_modules)
: Resource(resource_recycler) {
for (auto& mod : shader_modules) {
FXL_DCHECK(mod && mod->shader_stage() != ShaderStage::kEnumCount);
auto index = EnumCast(mod->shader_stage());
FXL_DCHECK(!shader_modules_[index]) << "multiply-defined stage.";
mod->AddShaderModuleListener(this);
shader_modules_[index] = std::move(mod);
}
}
ShaderProgram::ShaderProgram(ResourceRecycler* resource_recycler,
ShaderModulePtr shader_module)
: Resource(resource_recycler) {
FXL_DCHECK(shader_module &&
shader_module->shader_stage() == ShaderStage::kCompute);
shader_module->AddShaderModuleListener(this);
shader_modules_[EnumCast(ShaderStage::kCompute)] = std::move(shader_module);
}
ShaderProgram::~ShaderProgram() {
for (auto& mod : shader_modules_) {
if (mod) {
mod->RemoveShaderModuleListener(this);
}
}
vk::Device device = vk_device();
for (auto& pipeline : graphics_pipelines_) {
device.destroyPipeline(pipeline.second);
}
if (compute_pipeline_) {
device.destroyPipeline(compute_pipeline_);
}
}
ShaderProgram::ShaderProgram(ResourceManager* manager) : Resource(manager) {}
void ShaderProgram::OnShaderModuleUpdated(ShaderModule* shader_module) {
if (pipeline_layout_) {
// We must keep the obsolete pipelines for just as long as if this object's
// ref-count hit zero. The easiest way to do this is to move them into
// another ShaderProgram and immediately deref it.
auto keep_alive = new ShaderProgram(owner());
keep_alive->KeepAlive(sequence_number());
keep_alive->pipeline_layout_ = std::move(pipeline_layout_);
keep_alive->graphics_pipelines_ = std::move(graphics_pipelines_);
keep_alive->compute_pipeline_ = compute_pipeline_;
// Clear values so that they will be lazily recreated when requested.
pipeline_layout_ = nullptr;
graphics_pipelines_.clear();
compute_pipeline_ = vk::Pipeline();
// Allow the ref-count to immediately hit zero.
fxl::AdoptRef(keep_alive);
}
}
void ShaderProgram::ObtainPipelineLayout() {
TRACE_DURATION("gfx", "escher::ShaderProgram::ObtainPipelineLayout");
FXL_DCHECK(graphics_pipelines_.empty());
FXL_DCHECK(!compute_pipeline_);
FXL_DCHECK(!pipeline_layout_);
impl::PipelineLayoutSpec spec;
impl::GeneratePipelineLayoutSpec(shader_modules_, &spec);
// Compute a hash to quickly decide whether all descriptor sets must be
// invalidated.
Hasher h;
h.struc(spec.push_constant_ranges);
spec.push_constant_layout_hash = h.value();
pipeline_layout_ =
escher()->pipeline_layout_cache()->ObtainPipelineLayout(spec);
// Unlike graphics pipelines, which may have multiple variants depending on
// e.g. stencil buffer configuration, there is only ever one variant of a
// compute shader. Therefore, it can be created immediately.
if (auto& compute_module = GetModuleForStage(ShaderStage::kCompute)) {
TRACE_DURATION("gfx",
"escher::ShaderProgram::ObtainPipelineLayout [create "
"compute pipeline]");
// Need to revisit when the Vulkan spec allows compute shaders to be used
// within render passes.
FXL_DCHECK(!GetModuleForStage(ShaderStage::kVertex));
FXL_DCHECK(!GetModuleForStage(ShaderStage::kGeometry));
FXL_DCHECK(!GetModuleForStage(ShaderStage::kFragment));
FXL_DCHECK(!GetModuleForStage(ShaderStage::kTessellationControl));
FXL_DCHECK(!GetModuleForStage(ShaderStage::kTessellationEvaluation));
vk::ComputePipelineCreateInfo info;
info.layout = pipeline_layout_->vk();
info.stage.stage = vk::ShaderStageFlagBits::eCompute;
info.stage.module = compute_module->vk();
info.stage.pName = "main";
compute_pipeline_ = ESCHER_CHECKED_VK_RESULT(
vk_device().createComputePipeline(nullptr, info));
}
}
} // namespace escher