diff --git a/framework/CMakeLists.txt b/framework/CMakeLists.txt index a2ee4df72..aff97c86c 100644 --- a/framework/CMakeLists.txt +++ b/framework/CMakeLists.txt @@ -104,7 +104,6 @@ set(RENDERING_FILES rendering/postprocessing_pipeline.h rendering/postprocessing_pass.h rendering/postprocessing_renderpass.h - rendering/postprocessing_computepass.h rendering/render_context.h rendering/render_frame.h rendering/render_pipeline.h @@ -113,8 +112,7 @@ set(RENDERING_FILES # Source files rendering/postprocessing_pipeline.cpp rendering/postprocessing_pass.cpp - rendering/postprocessing_renderpass.cpp - rendering/postprocessing_computepass.cpp) + rendering/postprocessing_renderpass.cpp) set(RENDERING_SUBPASSES_FILES # Header files diff --git a/framework/rendering/postprocessing_computepass.cpp b/framework/rendering/postprocessing_computepass.cpp deleted file mode 100644 index f4c83e6ea..000000000 --- a/framework/rendering/postprocessing_computepass.cpp +++ /dev/null @@ -1,278 +0,0 @@ -/* Copyright (c) 2020-2026, Arm Limited and Contributors - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 the "License"; - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "postprocessing_computepass.h" - -#include "postprocessing_pipeline.h" - -namespace vkb -{ -PostProcessingComputePass::PostProcessingComputePass(PostProcessingPipeline *parent, const ShaderSource &cs_source, const ShaderVariant &cs_variant, - std::shared_ptr &&default_sampler) : - PostProcessingPass{parent}, - cs_source{cs_source}, - cs_variant{cs_variant}, - default_sampler{std::move(default_sampler)} -{ - if (this->default_sampler == nullptr) - { - // Setup a sane default sampler if none was passed - VkSamplerCreateInfo sampler_info{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; - sampler_info.minFilter = VK_FILTER_LINEAR; - sampler_info.magFilter = VK_FILTER_LINEAR; - sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; - sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - sampler_info.mipLodBias = 0.0f; - sampler_info.compareOp = VK_COMPARE_OP_NEVER; - sampler_info.minLod = 0.0f; - sampler_info.maxLod = 0.0f; - sampler_info.anisotropyEnable = VK_FALSE; - sampler_info.maxAnisotropy = 0.0f; - sampler_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; - - this->default_sampler = std::make_shared(get_render_context().get_device(), sampler_info); - - // Also create a nearest filtering version as a fallback - sampler_info.minFilter = VK_FILTER_NEAREST; - sampler_info.magFilter = VK_FILTER_NEAREST; - - this->default_sampler_nearest = std::make_shared(get_render_context().get_device(), sampler_info); - } -} - -void PostProcessingComputePass::prepare(vkb::core::CommandBufferC &command_buffer, vkb::rendering::RenderTargetC &default_render_target) -{ - // Build the compute shader upfront - auto &resource_cache = get_render_context().get_device().get_resource_cache(); - resource_cache.request_shader_module(VK_SHADER_STAGE_COMPUTE_BIT, cs_source, cs_variant); -} - -PostProcessingComputePass &PostProcessingComputePass::bind_sampled_image(const std::string &name, core::SampledImage &&new_image) -{ - auto it = sampled_images.find(name); - if (it != sampled_images.end()) - { - it->second = new_image; - } - else - { - sampled_images.emplace(name, new_image); - } - - return *this; -} - -PostProcessingComputePass &PostProcessingComputePass::bind_storage_image(const std::string &name, core::SampledImage &&new_image) -{ - auto it = storage_images.find(name); - if (it != storage_images.end()) - { - it->second = new_image; - } - else - { - storage_images.emplace(name, new_image); - } - - return *this; -} - -void PostProcessingComputePass::transition_images(vkb::core::CommandBufferC &command_buffer, vkb::rendering::RenderTargetC &default_render_target) -{ - BarrierInfo fallback_barrier_src{}; - fallback_barrier_src.pipeline_stage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - fallback_barrier_src.image_read_access = 0; // For UNDEFINED -> STORAGE in first CP - fallback_barrier_src.image_write_access = 0; - const auto prev_pass_barrier_info = get_predecessor_src_barrier_info(fallback_barrier_src); - - // Get compute shader from cache - auto &resource_cache = command_buffer.get_device().get_resource_cache(); - auto &shader_module = resource_cache.request_shader_module(VK_SHADER_STAGE_COMPUTE_BIT, cs_source, cs_variant); - auto &pipeline_layout = resource_cache.request_pipeline_layout({&shader_module}); - - for (const auto &sampled : sampled_images) - { - if (const uint32_t *attachment = sampled.second.get_target_attachment()) - { - auto *sampled_rt = sampled.second.get_render_target(); - if (sampled_rt == nullptr) - { - sampled_rt = &default_render_target; - } - - if (sampled_rt->get_layout(*attachment) == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) - { - // No-op - continue; - } - - vkb::ImageMemoryBarrier barrier; - barrier.old_layout = sampled_rt->get_layout(*attachment); - barrier.new_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - barrier.src_access_mask = prev_pass_barrier_info.image_write_access; - barrier.dst_access_mask = VK_ACCESS_SHADER_READ_BIT; - barrier.src_stage_mask = prev_pass_barrier_info.pipeline_stage; - barrier.dst_stage_mask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - - assert(*attachment < sampled_rt->get_views().size()); - command_buffer.image_memory_barrier(sampled_rt->get_views()[*attachment], barrier); - sampled_rt->set_layout(*attachment, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - } - } - - const auto &bindings = pipeline_layout.get_descriptor_set_layout(0); - - for (const auto &storage : storage_images) - { - if (const uint32_t *attachment = storage.second.get_target_attachment()) - { - auto *storage_rt = storage.second.get_render_target(); - if (storage_rt == nullptr) - { - storage_rt = &default_render_target; - } - - // A storage image is either readonly or writeonly; - // use shader reflection to figure out which case, then transition - // NOTE: Could add a readonly?> cache to make this faster? - auto resource = std::find_if(pipeline_layout.get_resources().begin(), pipeline_layout.get_resources().end(), - [&storage](const auto &res) { - return res.set == 0 && res.name == storage.first; - }); - if (resource == pipeline_layout.get_resources().end()) - { - // No such storage image to bind - continue; - } - - const bool readable = !(resource->qualifiers & ShaderResourceQualifiers::NonReadable); - const bool writable = !(resource->qualifiers & ShaderResourceQualifiers::NonReadable); - - vkb::ImageMemoryBarrier barrier; - barrier.old_layout = storage_rt->get_layout(*attachment); - barrier.new_layout = (readable && !writable) ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_GENERAL; - - if (storage_rt->get_layout(*attachment) == barrier.new_layout) - { - // No-op - continue; - } - - barrier.src_stage_mask = prev_pass_barrier_info.pipeline_stage; - barrier.dst_stage_mask = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - - barrier.src_access_mask = prev_pass_barrier_info.image_write_access; - barrier.dst_access_mask = 0; - if (readable) - { - barrier.dst_access_mask |= VK_ACCESS_SHADER_READ_BIT; - } - if (writable) - { - barrier.dst_access_mask |= VK_ACCESS_SHADER_WRITE_BIT; - } - - assert(*attachment < storage_rt->get_views().size()); - command_buffer.image_memory_barrier(storage_rt->get_views()[*attachment], barrier); - storage_rt->set_layout(*attachment, barrier.new_layout); - } - } -} - -void PostProcessingComputePass::draw(vkb::core::CommandBufferC &command_buffer, vkb::rendering::RenderTargetC &default_render_target) -{ - transition_images(command_buffer, default_render_target); - - // Get compute shader from cache - auto &resource_cache = command_buffer.get_device().get_resource_cache(); - auto &shader_module = resource_cache.request_shader_module(VK_SHADER_STAGE_COMPUTE_BIT, cs_source, cs_variant); - - // Create pipeline layout and bind it - auto &pipeline_layout = resource_cache.request_pipeline_layout({&shader_module}); - command_buffer.bind_pipeline_layout(pipeline_layout); - - const auto &bindings = pipeline_layout.get_descriptor_set_layout(0); - - // Bind samplers to set = 0, binding = - for (const auto &it : sampled_images) - { - if (auto layout_binding = bindings.get_layout_binding(it.first)) - { - const auto &view = it.second.get_image_view(default_render_target); - - // Get the properties for the image format. We need to check whether a linear sampler is valid. - const VkFormatProperties fmtProps = get_render_context().get_device().get_gpu().get_format_properties(view.get_format()); - bool has_linear_filter = (fmtProps.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT); - - const auto &sampler = it.second.get_sampler() ? *it.second.get_sampler() : - (has_linear_filter ? *default_sampler : *default_sampler_nearest); - - command_buffer.bind_image(view, sampler, 0, layout_binding->binding, 0); - } - } - - // Bind storage images to set = 0, binding = - for (const auto &it : storage_images) - { - if (auto layout_binding = bindings.get_layout_binding(it.first)) - { - const auto &view = it.second.get_image_view(default_render_target); - command_buffer.bind_image(view, 0, layout_binding->binding, 0); - } - } - - if (!uniform_data.empty()) - { - auto &render_frame = parent->get_render_context().get_active_frame(); - - uniform_alloc = std::make_unique(render_frame.allocate_buffer(VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, uniform_data.size())); - uniform_alloc->update(uniform_data); - - // Bind buffer to set = 0, binding = 0 - command_buffer.bind_buffer(uniform_alloc->get_buffer(), uniform_alloc->get_offset(), uniform_alloc->get_size(), 0, 0, 0); - } - - if (!push_constants_data.empty()) - { - command_buffer.push_constants(push_constants_data); - } - - // Dispatch compute - command_buffer.dispatch(n_workgroups.x, n_workgroups.y, n_workgroups.z); -} - -PostProcessingComputePass::BarrierInfo PostProcessingComputePass::get_src_barrier_info() const -{ - BarrierInfo info{}; - info.pipeline_stage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - info.image_read_access = VK_ACCESS_SHADER_READ_BIT; - info.image_write_access = VK_ACCESS_SHADER_WRITE_BIT; - return info; -} - -PostProcessingComputePass::BarrierInfo PostProcessingComputePass::get_dst_barrier_info() const -{ - BarrierInfo info{}; - info.pipeline_stage = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - info.image_read_access = VK_ACCESS_SHADER_READ_BIT; - info.image_write_access = VK_ACCESS_SHADER_WRITE_BIT; - return info; -} - -} // namespace vkb \ No newline at end of file diff --git a/framework/rendering/postprocessing_computepass.h b/framework/rendering/postprocessing_computepass.h deleted file mode 100644 index cf9b45e83..000000000 --- a/framework/rendering/postprocessing_computepass.h +++ /dev/null @@ -1,172 +0,0 @@ -/* Copyright (c) 2020-2026, Arm Limited and Contributors - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 the "License"; - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "common/glm_common.h" -#include "core/sampled_image.h" -#include "core/shader_module.h" -#include "postprocessing_pass.h" - -namespace vkb -{ -/** - * @brief Maps in-shader binding names to the core::SampledImage to bind. - */ -using SampledImageMap = std::unordered_map; - -/** - * @brief A compute pass in a vkb::PostProcessingPipeline. - */ -class PostProcessingComputePass : public PostProcessingPass -{ - public: - PostProcessingComputePass(PostProcessingPipeline *parent, const ShaderSource &cs_source, const ShaderVariant &cs_variant = {}, - std::shared_ptr &&default_sampler = {}); - - PostProcessingComputePass(const PostProcessingComputePass &to_copy) = delete; - PostProcessingComputePass &operator=(const PostProcessingComputePass &to_copy) = delete; - - PostProcessingComputePass(PostProcessingComputePass &&to_move) = default; - PostProcessingComputePass &operator=(PostProcessingComputePass &&to_move) = default; - - void prepare(vkb::core::CommandBufferC &command_buffer, vkb::rendering::RenderTargetC &default_render_target) override; - void draw(vkb::core::CommandBufferC &command_buffer, vkb::rendering::RenderTargetC &default_render_target) override; - - /** - * @brief Sets the number of workgroups to be dispatched each draw(). - */ - inline PostProcessingComputePass &set_dispatch_size(glm::tvec3 new_size) - { - n_workgroups = new_size; - return *this; - } - - /** - * @brief Gets the number of workgroups that will be dispatched each draw(). - */ - inline glm::tvec3 get_dispatch_size() const - { - return n_workgroups; - } - - /** - * @brief Maps the names of samplers in the shader to vkb::core::SampledImage. - * These are given as samplers to the subpass, at set 0; they are bound automatically according to their name. - * @remarks PostProcessingPipeline::get_sampler() is used as the default sampler if none is specified. - * The RenderTarget for the current PostprocessingStep is used if none is specified for attachment images. - */ - inline const SampledImageMap &get_sampled_images() const - { - return sampled_images; - } - - /** - * @brief Maps the names of storage images in the shader to vkb::core::SampledImage. - * These are given as image2D / image2DArray / ... to the subpass, at set 0; - * they are bound automatically according to their name. - */ - inline const SampledImageMap &get_storage_images() const - { - return storage_images; - } - - /** - * @brief Changes (or adds) the sampled image at name for this step. - * @remarks If no RenderTarget is specifically set for the core::SampledImage, - * it will default to sample in the RenderTarget currently bound for drawing in the parent PostProcessingRenderpass. - * @remarks Images from RenderTarget attachments are automatically transitioned to SHADER_READ_ONLY_OPTIMAL layout if needed. - */ - PostProcessingComputePass &bind_sampled_image(const std::string &name, core::SampledImage &&new_image); - - /** - * @brief Changes (or adds) the storage image at name for this step. - * @remarks Images from RenderTarget attachments are automatically transitioned to GENERAL layout if needed. - */ - PostProcessingComputePass &bind_storage_image(const std::string &name, core::SampledImage &&new_image); - - /** - * @brief Set the uniform data to be bound at set 0, binding 0. - */ - template - inline PostProcessingComputePass &set_uniform_data(const T &data) - { - uniform_data.reserve(sizeof(data)); - auto data_ptr = reinterpret_cast(&data); - uniform_data.assign(data_ptr, data_ptr + sizeof(data)); - - return *this; - } - - /** - * @copydoc set_uniform_data(const T&) - */ - inline PostProcessingComputePass &set_uniform_data(const std::vector &data) - { - uniform_data = data; - - return *this; - } - - /** - * @brief Set the constants that are pushed before each draw. - */ - template - inline PostProcessingComputePass &set_push_constants(const T &data) - { - push_constants_data.reserve(sizeof(data)); - auto data_ptr = reinterpret_cast(&data); - push_constants_data.assign(data_ptr, data_ptr + sizeof(data)); - - return *this; - } - - /** - * @copydoc set_push_constants(const T&) - */ - inline PostProcessingComputePass &set_push_constants(const std::vector &data) - { - push_constants_data = data; - - return *this; - } - - private: - ShaderSource cs_source; - ShaderVariant cs_variant; - glm::tvec3 n_workgroups{1, 1, 1}; - - std::shared_ptr default_sampler{}; - std::shared_ptr default_sampler_nearest{}; - SampledImageMap sampled_images{}; - SampledImageMap storage_images{}; - - std::vector uniform_data{}; - std::unique_ptr uniform_alloc{}; - std::vector push_constants_data{}; - - /** - * @brief Transitions sampled_images (to SHADER_READ_ONLY_OPTIMAL) - * and storage_images (to GENERAL) as appropriate. - */ - void transition_images(vkb::core::CommandBufferC &command_buffer, vkb::rendering::RenderTargetC &default_render_target); - - BarrierInfo get_src_barrier_info() const override; - BarrierInfo get_dst_barrier_info() const override; -}; - -} // namespace vkb \ No newline at end of file