| // 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 |