Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Impeller] Vulkan framebuffer fetch via VK_ARM_RASTERIZATION_ORDER_ATTACHMENT_ACCESS #48458

Merged
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion impeller/compiler/code_gen_template.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,14 @@ std::move({{ arg.argument_name }}){% if not loop.is_last %}, {% endif %}
// ===========================================================================
// Metadata for Vulkan =======================================================
// ===========================================================================
static constexpr std::array<DescriptorSetLayout,{{length(buffers)+length(sampled_images)}}> kDescriptorSetLayouts{
static constexpr std::array<DescriptorSetLayout,{{length(buffers)+length(sampled_images)+length(subpass_inputs)}}> kDescriptorSetLayouts{
{% for subpass_input in subpass_inputs %}
DescriptorSetLayout{
{{subpass_input.binding}}, // binding = {{subpass_input.binding}}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think its a bit of a mistake to assume that all bindings are in the set index zero. We should fix that separately as that affects all instances of DescriptorSetLayout in the generated header. For example, if we were to manually specify layout(set=2, binding=n), we'd keel over.

In the same vein, I think its a mistake to assume that we can only have one input attachment and at index zero. I'd also put the input_attachment_index in the descriptor set layout. The previous attempt at this patch did the same.

You should be able to reproduce this by manually specifying the input_attachment_index to anything other than zero and checking the validity of the setup.

For the descriptor set index, we can file a separate followup issue.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think practically speaking we can't really do bindings any other way. The actual render_pass_vk logic can only handle descriptors at index set zero.

Perhaps it would be possible with some refactoring, but it seems really in the weeds of what will yield a performance improvement.

{{subpass_input.descriptor_type}}, // descriptor_type = {{subpass_input.descriptor_type}}
{{to_shader_stage(shader_stage)}}, // shader_stage = {{to_shader_stage(shader_stage)}}
},
{% endfor %}
{% for buffer in buffers %}
DescriptorSetLayout{
{{buffer.binding}}, // binding = {{buffer.binding}}
Expand Down
15 changes: 15 additions & 0 deletions impeller/compiler/reflector.cc
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,21 @@ std::optional<nlohmann::json> Reflector::GenerateTemplateArguments() const {

const auto shader_resources = compiler_->get_shader_resources();

// Subpass Inputs.
{
auto& subpass_inputs = root["subpass_inputs"] = nlohmann::json::array_t{};
if (auto subpass_inputs_json =
ReflectResources(shader_resources.subpass_inputs);
subpass_inputs_json.has_value()) {
for (auto subpass_input : subpass_inputs_json.value()) {
subpass_input["descriptor_type"] = "DescriptorType::kInputAttachment";
subpass_inputs.emplace_back(std::move(subpass_input));
}
} else {
return std::nullopt;
}
}

// Uniform and storage buffers.
{
auto& buffers = root["buffers"] = nlohmann::json::array_t{};
Expand Down
1 change: 1 addition & 0 deletions impeller/core/shader_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ enum class DescriptorType {
kSampledImage,
kImage,
kSampler,
kInputAttachment,
};

struct DescriptorSetLayout {
Expand Down
45 changes: 30 additions & 15 deletions impeller/entity/contents/content_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -221,49 +221,64 @@ ContentContext::ContentContext(
if (context_->GetCapabilities()->SupportsFramebufferFetch()) {
framebuffer_blend_color_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int32_t>(BlendSelectValues::kColor), supports_decal});
{static_cast<int32_t>(BlendSelectValues::kColor), supports_decal},
UseSubpassInput::kYes);
framebuffer_blend_colorburn_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int32_t>(BlendSelectValues::kColorBurn), supports_decal});
{static_cast<int32_t>(BlendSelectValues::kColorBurn), supports_decal},
UseSubpassInput::kYes);
framebuffer_blend_colordodge_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int32_t>(BlendSelectValues::kColorDodge), supports_decal});
{static_cast<int32_t>(BlendSelectValues::kColorDodge), supports_decal},
UseSubpassInput::kYes);
framebuffer_blend_darken_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int32_t>(BlendSelectValues::kDarken), supports_decal});
{static_cast<int32_t>(BlendSelectValues::kDarken), supports_decal},
UseSubpassInput::kYes);
framebuffer_blend_difference_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int32_t>(BlendSelectValues::kDifference), supports_decal});
{static_cast<int32_t>(BlendSelectValues::kDifference), supports_decal},
UseSubpassInput::kYes);
framebuffer_blend_exclusion_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int32_t>(BlendSelectValues::kExclusion), supports_decal});
{static_cast<int32_t>(BlendSelectValues::kExclusion), supports_decal},
UseSubpassInput::kYes);
framebuffer_blend_hardlight_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int32_t>(BlendSelectValues::kHardLight), supports_decal});
{static_cast<int32_t>(BlendSelectValues::kHardLight), supports_decal},
UseSubpassInput::kYes);
framebuffer_blend_hue_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int32_t>(BlendSelectValues::kHue), supports_decal});
{static_cast<int32_t>(BlendSelectValues::kHue), supports_decal},
UseSubpassInput::kYes);
framebuffer_blend_lighten_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int32_t>(BlendSelectValues::kLighten), supports_decal});
{static_cast<int32_t>(BlendSelectValues::kLighten), supports_decal},
UseSubpassInput::kYes);
framebuffer_blend_luminosity_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int32_t>(BlendSelectValues::kLuminosity), supports_decal});
{static_cast<int32_t>(BlendSelectValues::kLuminosity), supports_decal},
UseSubpassInput::kYes);
framebuffer_blend_multiply_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int32_t>(BlendSelectValues::kMultiply), supports_decal});
{static_cast<int32_t>(BlendSelectValues::kMultiply), supports_decal},
UseSubpassInput::kYes);
framebuffer_blend_overlay_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int32_t>(BlendSelectValues::kOverlay), supports_decal});
{static_cast<int32_t>(BlendSelectValues::kOverlay), supports_decal},
UseSubpassInput::kYes);
framebuffer_blend_saturation_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int32_t>(BlendSelectValues::kSaturation), supports_decal});
{static_cast<int32_t>(BlendSelectValues::kSaturation), supports_decal},
UseSubpassInput::kYes);
framebuffer_blend_screen_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int32_t>(BlendSelectValues::kScreen), supports_decal});
{static_cast<int32_t>(BlendSelectValues::kScreen), supports_decal},
UseSubpassInput::kYes);
framebuffer_blend_softlight_pipelines_.CreateDefault(
*context_, options_trianglestrip,
{static_cast<int32_t>(BlendSelectValues::kSoftLight), supports_decal});
{static_cast<int32_t>(BlendSelectValues::kSoftLight), supports_decal},
UseSubpassInput::kYes);
}

blend_color_pipelines_.CreateDefault(
Expand Down
5 changes: 4 additions & 1 deletion impeller/entity/contents/content_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "impeller/entity/entity.h"
#include "impeller/renderer/capabilities.h"
#include "impeller/renderer/pipeline.h"
#include "impeller/renderer/pipeline_descriptor.h"
#include "impeller/renderer/render_target.h"
#include "impeller/typographer/typographer_context.h"

Expand Down Expand Up @@ -713,13 +714,15 @@ class ContentContext {

void CreateDefault(const Context& context,
const ContentContextOptions& options,
const std::initializer_list<int32_t>& constants = {}) {
const std::initializer_list<int32_t>& constants = {},
UseSubpassInput subpass_input = UseSubpassInput::kNo) {
auto desc =
PipelineT::Builder::MakeDefaultPipelineDescriptor(context, constants);
if (!desc.has_value()) {
VALIDATION_LOG << "Failed to create default pipeline.";
return;
}
desc->SetUseSubpassInput(subpass_input);
options.ApplyToPipelineDescriptor(*desc);
SetDefault(options, std::make_unique<PipelineT>(context, desc));
}
Expand Down
10 changes: 10 additions & 0 deletions impeller/entity/shaders/blending/framebuffer_blend.frag
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,23 @@
layout(constant_id = 0) const int blend_type = 0;
layout(constant_id = 1) const int supports_decal = 1;

#ifdef IMPELLER_TARGET_OPENGLES
layout(set = 0,
binding = 0,
input_attachment_index = 0) uniform subpassInput uSub;

vec4 ReadDestination() {
return subpassLoad(uSub);
}
#else
layout(set = 0,
binding = 0,
input_attachment_index = 0) uniform subpassInputMS uSub;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😮‍💨


vec4 ReadDestination() {
return subpassLoad(uSub, gl_SampleID);
}
#endif

uniform sampler2D texture_sampler_src;

Expand Down
4 changes: 4 additions & 0 deletions impeller/playground/backend/vulkan/playground_impl_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "flutter/fml/logging.h"
#include "flutter/fml/mapping.h"
#include "impeller/entity/vk/entity_shaders_vk.h"
#include "impeller/entity/vk/framebuffer_blend_shaders_vk.h"
#include "impeller/entity/vk/modern_shaders_vk.h"
#include "impeller/fixtures/vk/fixtures_shaders_vk.h"
#include "impeller/playground/imgui/vk/imgui_shaders_vk.h"
Expand All @@ -33,6 +34,9 @@ ShaderLibraryMappingsForPlayground() {
impeller_entity_shaders_vk_length),
std::make_shared<fml::NonOwnedMapping>(impeller_modern_shaders_vk_data,
impeller_modern_shaders_vk_length),
std::make_shared<fml::NonOwnedMapping>(
impeller_framebuffer_blend_shaders_vk_data,
impeller_framebuffer_blend_shaders_vk_length),
std::make_shared<fml::NonOwnedMapping>(
impeller_fixtures_shaders_vk_data,
impeller_fixtures_shaders_vk_length),
Expand Down
20 changes: 14 additions & 6 deletions impeller/renderer/backend/vulkan/allocator_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "impeller/renderer/backend/vulkan/device_buffer_vk.h"
#include "impeller/renderer/backend/vulkan/formats_vk.h"
#include "impeller/renderer/backend/vulkan/texture_vk.h"
#include "vulkan/vulkan_enums.hpp"

namespace impeller {

Expand Down Expand Up @@ -148,6 +149,7 @@ AllocatorVK::AllocatorVK(std::weak_ptr<Context> context,
allocator_.reset(allocator);
supports_memoryless_textures_ =
capabilities.SupportsDeviceTransientTextures();
supports_framebuffer_fetch_ = capabilities.SupportsFramebufferFetch();
is_valid_ = true;
}

Expand All @@ -167,7 +169,8 @@ static constexpr vk::ImageUsageFlags ToVKImageUsageFlags(
PixelFormat format,
TextureUsageMask usage,
StorageMode mode,
bool supports_memoryless_textures) {
bool supports_memoryless_textures,
bool supports_framebuffer_fetch) {
vk::ImageUsageFlags vk_usage;

switch (mode) {
Expand All @@ -187,6 +190,9 @@ static constexpr vk::ImageUsageFlags ToVKImageUsageFlags(
} else {
vk_usage |= vk::ImageUsageFlagBits::eColorAttachment;
}
if (mode == StorageMode::kDeviceTransient && supports_framebuffer_fetch) {
vk_usage |= vk::ImageUsageFlagBits::eInputAttachment;
}
}

if (usage & static_cast<TextureUsageMask>(TextureUsage::kShaderRead)) {
Expand Down Expand Up @@ -263,7 +269,8 @@ class AllocatedTextureSourceVK final : public TextureSourceVK {
const TextureDescriptor& desc,
VmaAllocator allocator,
vk::Device device,
bool supports_memoryless_textures)
bool supports_memoryless_textures,
bool supports_framebuffer_fetch)
: TextureSourceVK(desc), resource_(std::move(resource_manager)) {
FML_DCHECK(desc.format != PixelFormat::kUnknown);
TRACE_EVENT0("impeller", "CreateDeviceTexture");
Expand All @@ -281,9 +288,9 @@ class AllocatedTextureSourceVK final : public TextureSourceVK {
image_info.arrayLayers = ToArrayLayerCount(desc.type);
image_info.tiling = vk::ImageTiling::eOptimal;
image_info.initialLayout = vk::ImageLayout::eUndefined;
image_info.usage =
ToVKImageUsageFlags(desc.format, desc.usage, desc.storage_mode,
supports_memoryless_textures);
image_info.usage = ToVKImageUsageFlags(
desc.format, desc.usage, desc.storage_mode,
supports_memoryless_textures, supports_framebuffer_fetch);
image_info.sharingMode = vk::SharingMode::eExclusive;

VmaAllocationCreateInfo alloc_nfo = {};
Expand Down Expand Up @@ -412,7 +419,8 @@ std::shared_ptr<Texture> AllocatorVK::OnCreateTexture(
desc, //
allocator_.get(), //
device_holder->GetDevice(), //
supports_memoryless_textures_ //
supports_memoryless_textures_, //
supports_framebuffer_fetch_ //
);
if (!source->IsValid()) {
return nullptr;
Expand Down
1 change: 1 addition & 0 deletions impeller/renderer/backend/vulkan/allocator_vk.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class AllocatorVK final : public Allocator {
ISize max_texture_size_;
bool is_valid_ = false;
bool supports_memoryless_textures_ = false;
bool supports_framebuffer_fetch_ = false;
// TODO(jonahwilliams): figure out why CI can't create these buffer pools.
bool created_buffer_pool_ = true;
uint32_t frame_count_ = 0;
Expand Down
35 changes: 29 additions & 6 deletions impeller/renderer/backend/vulkan/binding_helpers_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "impeller/renderer/backend/vulkan/vk.h"
#include "impeller/renderer/command.h"
#include "impeller/renderer/compute_command.h"
#include "vulkan/vulkan_core.h"

namespace impeller {

Expand Down Expand Up @@ -117,7 +118,8 @@ static bool BindBuffers(const Bindings& bindings,
fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateAndBindDescriptorSets(
const ContextVK& context,
const std::shared_ptr<CommandEncoderVK>& encoder,
const std::vector<Command>& commands) {
const std::vector<Command>& commands,
const TextureVK& input_attachment) {
if (commands.empty()) {
return std::vector<vk::DescriptorSet>{};
}
Expand All @@ -127,19 +129,22 @@ fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateAndBindDescriptorSets(
// to allocate a correctly sized descriptor pool.
size_t buffer_count = 0;
size_t samplers_count = 0;
size_t subpass_count = 0;
std::vector<vk::DescriptorSetLayout> layouts;
layouts.reserve(commands.size());

for (const auto& command : commands) {
buffer_count += command.vertex_bindings.buffers.size();
buffer_count += command.fragment_bindings.buffers.size();
samplers_count += command.fragment_bindings.sampled_images.size();
subpass_count +=
command.pipeline->GetDescriptor().UsesSubpassInput() ? 1 : 0;

layouts.emplace_back(
PipelineVK::Cast(*command.pipeline).GetDescriptorSetLayout());
}
auto descriptor_result =
encoder->AllocateDescriptorSets(buffer_count, samplers_count, layouts);
auto descriptor_result = encoder->AllocateDescriptorSets(
buffer_count, samplers_count, subpass_count, layouts);
if (!descriptor_result.ok()) {
return descriptor_result.status();
}
Expand All @@ -153,9 +158,9 @@ fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateAndBindDescriptorSets(
std::vector<vk::DescriptorImageInfo> images;
std::vector<vk::DescriptorBufferInfo> buffers;
std::vector<vk::WriteDescriptorSet> writes;
images.reserve(samplers_count);
images.reserve(samplers_count + subpass_count);
buffers.reserve(buffer_count);
writes.reserve(samplers_count + buffer_count);
writes.reserve(samplers_count + buffer_count + subpass_count);

auto& allocator = *context.GetResourceAllocator();
auto desc_index = 0u;
Expand All @@ -173,6 +178,24 @@ fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateAndBindDescriptorSets(
return fml::Status(fml::StatusCode::kUnknown,
"Failed to bind texture or buffer.");
}

if (command.pipeline->GetDescriptor().UsesSubpassInput()) {
vk::DescriptorImageInfo image_info;
image_info.imageLayout = vk::ImageLayout::eGeneral;
image_info.sampler = VK_NULL_HANDLE;
image_info.imageView = input_attachment.GetImageView();
images.push_back(image_info);

vk::WriteDescriptorSet write_set;
write_set.dstSet = descriptor_sets[desc_index];
// MAGIC! see description in pipeline_library_vk.cc
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just look this up on the descriptor sets.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because the subpass doesn't show up in host builds I can't use any of the constants.

write_set.dstBinding = 64u;
write_set.descriptorCount = 1u;
write_set.descriptorType = vk::DescriptorType::eInputAttachment;
write_set.pImageInfo = &images.back();

writes.push_back(write_set);
}
desc_index += 1;
}

Expand Down Expand Up @@ -203,7 +226,7 @@ fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateAndBindDescriptorSets(
ComputePipelineVK::Cast(*command.pipeline).GetDescriptorSetLayout());
}
auto descriptor_result =
encoder->AllocateDescriptorSets(buffer_count, samplers_count, layouts);
encoder->AllocateDescriptorSets(buffer_count, samplers_count, 0, layouts);
if (!descriptor_result.ok()) {
return descriptor_result.status();
}
Expand Down
4 changes: 3 additions & 1 deletion impeller/renderer/backend/vulkan/binding_helpers_vk.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "fml/status_or.h"
#include "impeller/renderer/backend/vulkan/context_vk.h"
#include "impeller/renderer/backend/vulkan/texture_vk.h"
#include "impeller/renderer/command.h"
#include "impeller/renderer/compute_command.h"

Expand All @@ -16,7 +17,8 @@ namespace impeller {
fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateAndBindDescriptorSets(
const ContextVK& context,
const std::shared_ptr<CommandEncoderVK>& encoder,
const std::vector<Command>& commands);
const std::vector<Command>& commands,
const TextureVK& input_attachment);

fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateAndBindDescriptorSets(
const ContextVK& context,
Expand Down