blob: 2b0eb253b9280904961d42a1b912284e4a4dcc08 [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 "src/ui/lib/escher/vk/shader_program.h"
#include "src/ui/lib/escher/escher.h"
#include "src/ui/lib/escher/impl/vulkan_utils.h"
#include "src/ui/lib/escher/resources/resource_recycler.h"
#include "src/ui/lib/escher/third_party/granite/vk/pipeline_layout.h"
#include "src/ui/lib/escher/third_party/granite/vk/shader_utils.h"
#include "src/ui/lib/escher/util/enum_cast.h"
#include "src/ui/lib/escher/util/hasher.h"
#include "src/ui/lib/escher/util/trace_macros.h"
#include "src/ui/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);
}
}
ShaderProgram::ShaderProgram(ResourceManager* manager) : Resource(manager) {}
void ShaderProgram::OnShaderModuleUpdated(ShaderModule* shader_module) {
ClearPipelineLayout();
ClearPipelineStash();
}
void ShaderProgram::ClearPipelineLayout() {
if (!pipeline_layout_)
return;
// We must keep the obsolete pipeline layout alive for just as long as it
// takes for this object's ref-count to 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_);
pipeline_layout_ = nullptr;
// Allow the ref-count to immediately hit zero.
fxl::AdoptRef(keep_alive);
}
void ShaderProgram::ClearPipelineStash() {
if (graphics_pipelines_.empty())
return;
// We must keep the obsolete pipelines alive for just as long as it
// takes for this object's ref-count to 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->graphics_pipelines_ = std::move(graphics_pipelines_);
graphics_pipelines_.clear();
// Allow the ref-count to immediately hit zero.
fxl::AdoptRef(keep_alive);
}
PipelineLayout* ShaderProgram::ObtainPipelineLayout(
const SamplerPtr& immutable_sampler) {
TRACE_DURATION("gfx", "escher::ShaderProgram::ObtainPipelineLayout");
// If we already have a pipeline layout, and the immutable sampler matches,
// just use that.
if (pipeline_layout_ &&
pipeline_layout_->spec().immutable_sampler() == immutable_sampler) {
return pipeline_layout_.get();
}
// Clear out the old pipeline layout, if there is one.
ClearPipelineLayout();
FXL_DCHECK(!pipeline_layout_);
impl::PipelineLayoutSpec spec =
impl::GeneratePipelineLayoutSpec(shader_modules_, immutable_sampler);
// TODO(ES-201): This code assumes we only need to cache a single pipeline
// layout per shader program. Immutable samplers ruin that assumption.
pipeline_layout_ =
escher()->pipeline_layout_cache()->ObtainPipelineLayout(spec);
return pipeline_layout_.get();
}
} // namespace escher