blob: 0df86fb6142300899344c7da84fbb2a0a9bce446 [file] [log] [blame]
/* Copyright (c) 2017 Hans-Kristian Arntzen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// Based on the following files from the Granite rendering engine:
// - vulkan/shader.hpp
#ifndef SRC_UI_LIB_ESCHER_THIRD_PARTY_GRANITE_VK_PIPELINE_LAYOUT_H_
#define SRC_UI_LIB_ESCHER_THIRD_PARTY_GRANITE_VK_PIPELINE_LAYOUT_H_
#include <vulkan/vulkan.hpp>
#include "src/ui/lib/escher/forward_declarations.h"
#include "src/ui/lib/escher/resources/resource.h"
#include "src/ui/lib/escher/third_party/granite/vk/descriptor_set_layout.h"
#include "src/ui/lib/escher/util/bit_ops.h"
#include "src/ui/lib/escher/util/debug_print.h"
#include "src/ui/lib/escher/util/enum_count.h"
#include "src/ui/lib/escher/util/hashable.h"
#include "src/ui/lib/escher/util/hasher.h"
#include "src/ui/lib/escher/vk/sampler.h"
#include "src/ui/lib/escher/vk/shader_stage.h"
#include "src/ui/lib/escher/vk/vulkan_limits.h"
namespace escher {
namespace impl {
// Aggregate the ShaderModuleResourceLayouts of all ShaderModules that are
// used to create a pipeline.
struct PipelineLayoutSpec : public Hashable {
public:
static constexpr size_t kMaxPushConstantRanges = EnumCount<ShaderStage>();
PipelineLayoutSpec(
uint32_t attribute_mask, uint32_t render_target_mask,
const std::array<DescriptorSetLayout, VulkanLimits::kNumDescriptorSets>&
layouts,
const std::array<vk::PushConstantRange, kMaxPushConstantRanges>& ranges,
uint32_t num_ranges, SamplerPtr immutable_sampler)
: immutable_sampler_(immutable_sampler),
attribute_mask_(attribute_mask),
render_target_mask_(render_target_mask),
descriptor_set_layouts_(layouts),
num_push_constant_ranges_(num_ranges),
push_constant_ranges_(ranges) {
FXL_DCHECK(num_ranges < kMaxPushConstantRanges);
for (uint32_t i = 0; i < VulkanLimits::kNumDescriptorSets; ++i) {
if (descriptor_set_layouts_[i].stages) {
descriptor_set_mask_ |= 1u << i;
}
}
// Compute a hash to quickly decide whether all descriptor sets must be
// invalidated.
Hasher h;
for (uint32_t i = 0; i < num_ranges; ++i) {
h.struc(push_constant_ranges_[i]);
}
push_constant_layout_hash_ = h.value();
}
const SamplerPtr& immutable_sampler() const { return immutable_sampler_; }
uint32_t attribute_mask() const { return attribute_mask_; }
uint32_t render_target_mask() const { return render_target_mask_; }
uint32_t descriptor_set_mask() const { return descriptor_set_mask_; }
const DescriptorSetLayout&
descriptor_set_layouts(uint32_t index) const {
return descriptor_set_layouts_[index];
}
uint32_t num_push_constant_ranges() const {
return num_push_constant_ranges_;
}
const std::array<vk::PushConstantRange, kMaxPushConstantRanges>&
push_constant_ranges() const {
return push_constant_ranges_;
}
Hash push_constant_layout_hash() const { return push_constant_layout_hash_; }
bool operator==(const PipelineLayoutSpec& other) const;
private:
// |Hashable|
Hash GenerateHash() const override;
SamplerPtr immutable_sampler_;
uint32_t attribute_mask_ = 0;
// TODO(ES-83): document.
uint32_t render_target_mask_ = 0;
uint32_t descriptor_set_mask_ = 0;
std::array<DescriptorSetLayout, VulkanLimits::kNumDescriptorSets>
descriptor_set_layouts_ = {};
uint32_t num_push_constant_ranges_ = 0;
std::array<vk::PushConstantRange, kMaxPushConstantRanges>
push_constant_ranges_ = {};
// Allows quick comparison to decide whether the push constant ranges have
// changed. If so, all descriptor sets are invalidated.
// TODO(ES-83): I remember reading why this is necessary... we should
// make note of the section of the Vulkan spec that requires this.
Hash push_constant_layout_hash_ = {0};
};
// TODO(ES-83): extend downward to enclose PipelineLayout. Cannot do this yet
// because there is already a PipelineLayout in impl/vk.
} // namespace impl
// A PipelineLayout encapsulates a VkPipelineLayout object, as well as an
// array of DescriptorSetAllocators that are configured to allocate descriptor
// sets that match the sets required, at each index, by pipelines with this
// layout.
//
// TODO(ES-83): does this need to be a Resource? If these are always
// reffed by pipelines that use them, then it should suffice to keep those
// pipelines alive, right?
class PipelineLayout : public Resource {
public:
static const ResourceTypeInfo kTypeInfo;
const ResourceTypeInfo& type_info() const override { return kTypeInfo; }
PipelineLayout(ResourceRecycler* resource_recycler,
const impl::PipelineLayoutSpec& spec);
~PipelineLayout();
vk::PipelineLayout vk() const { return pipeline_layout_; }
const impl::PipelineLayoutSpec& spec() const { return spec_; }
impl::DescriptorSetAllocator* GetDescriptorSetAllocator(
unsigned set_index) const {
FXL_DCHECK(set_index < VulkanLimits::kNumDescriptorSets);
return descriptor_set_allocators_[set_index];
}
private:
vk::PipelineLayout pipeline_layout_;
// This PipelineLayoutSpec will be used for hashes and equality tests, so it
// should match the construction parameter and not be mutated.
const impl::PipelineLayoutSpec spec_;
impl::DescriptorSetAllocator*
descriptor_set_allocators_[VulkanLimits::kNumDescriptorSets] = {};
};
using PipelineLayoutPtr = fxl::RefPtr<PipelineLayout>;
// Inline function definitions.
inline bool impl::PipelineLayoutSpec::operator==(
const impl::PipelineLayoutSpec& other) const {
return immutable_sampler_ == other.immutable_sampler_ &&
attribute_mask_ == other.attribute_mask_ &&
render_target_mask_ == other.render_target_mask_ &&
descriptor_set_mask_ == other.descriptor_set_mask_ &&
descriptor_set_layouts_ == other.descriptor_set_layouts_ &&
num_push_constant_ranges_ == other.num_push_constant_ranges_ &&
push_constant_ranges_ == other.push_constant_ranges_ &&
push_constant_layout_hash_ == other.push_constant_layout_hash_;
}
inline Hash impl::PipelineLayoutSpec::GenerateHash() const {
Hasher h;
h.struc(immutable_sampler_);
h.u32(attribute_mask_);
h.u32(render_target_mask_);
h.u32(descriptor_set_mask_);
ForEachBitIndex(descriptor_set_mask_, [&](uint32_t index) {
h.struc(descriptor_set_layouts_[index]);
});
// Instead of hashing the push constant ranges again, just hash the hash of
// the push constant ranges that was already computed in the constructor.
h.u64(push_constant_layout_hash_.val);
return h.value();
}
ESCHER_DEBUG_PRINTABLE(impl::PipelineLayoutSpec);
} // namespace escher
#endif // SRC_UI_LIB_ESCHER_THIRD_PARTY_GRANITE_VK_PIPELINE_LAYOUT_H_