diff --git a/src/xrt/compositor/CMakeLists.txt b/src/xrt/compositor/CMakeLists.txt index 46834bde8..1bfae24c3 100644 --- a/src/xrt/compositor/CMakeLists.txt +++ b/src/xrt/compositor/CMakeLists.txt @@ -146,6 +146,8 @@ if(XRT_HAVE_VULKAN) comp_util STATIC util/comp_base.h util/comp_base.c + util/comp_high_level_scratch.c + util/comp_high_level_scratch.h util/comp_layer_accum.h util/comp_layer_accum.c util/comp_render.h diff --git a/src/xrt/compositor/util/comp_high_level_scratch.c b/src/xrt/compositor/util/comp_high_level_scratch.c new file mode 100644 index 000000000..d1ad4e755 --- /dev/null +++ b/src/xrt/compositor/util/comp_high_level_scratch.c @@ -0,0 +1,144 @@ +// Copyright 2019-2024, Collabora, Ltd. +// Copyright 2024-2025, NVIDIA CORPORATION. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Higher level interface for scratch images. + * @author Jakob Bornecrantz + * @author Andrei Aristarkhov + * @author Gareth Morgan + * @author Rylie Pavlik + * @ingroup comp_main + */ + + +#include "comp_high_level_scratch.h" + + +void +chl_scratch_init(struct chl_scratch *scratch) +{ + for (uint32_t i = 0; i < ARRAY_SIZE(scratch->views); i++) { + comp_scratch_single_images_init(&scratch->views[i].cssi); + } +} + +void +chl_scratch_fini(struct chl_scratch *scratch) +{ + for (uint32_t i = 0; i < ARRAY_SIZE(scratch->views); i++) { + comp_scratch_single_images_destroy(&scratch->views[i].cssi); + } +} + +bool +chl_scratch_ensure(struct chl_scratch *scratch, + struct render_resources *rr, + uint32_t view_count, + VkExtent2D extent, + const VkFormat format) +{ + struct vk_bundle *vk = rr->vk; + bool bret = false; + + // Is everything already correct? + if (scratch->view_count == view_count && // + scratch->extent.width == extent.width && // + scratch->extent.height == extent.height && // + scratch->format == format) { + return true; + } + + // Free all old resources. + chl_scratch_free_resources(scratch, rr); + + // Shared render pass between all scratch images. + bret = render_gfx_render_pass_init( // + &scratch->render_pass, // rgrp + rr, // struct render_resources + format, // format + VK_ATTACHMENT_LOAD_OP_CLEAR, // load_op + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // final_layout + if (!bret) { + VK_ERROR(vk, "render_gfx_render_pass_init: false"); + return false; + } + + // Need to track if the render pass has been initialized. + scratch->render_pass_initialized = true; + + for (uint32_t i = 0; i < view_count; i++) { + // Helper. + struct comp_scratch_single_images *cssi = &scratch->views[i].cssi; + + if (format == VK_FORMAT_R8G8B8A8_SRGB) { + // Special creation function for the mutable format. + bret = comp_scratch_single_images_ensure_mutable(cssi, vk, extent); + } else { + bret = comp_scratch_single_images_ensure(cssi, vk, extent, format); + } + + if (!bret) { + VK_ERROR(vk, "comp_scratch_single_images_ensure[_mutable]: false"); + // Free any that has already been allocated. + chl_scratch_free_resources(scratch, rr); + return false; + } + + for (uint32_t k = 0; k < COMP_SCRATCH_NUM_IMAGES; k++) { + + /* + * For graphics parts we use the same image view as the + * source. In other words the sRGB image view for the + * non-linear formats. + */ + VkImageView target_image_view = chl_scratch_get_sample_view(scratch, i, k); + + render_gfx_target_resources_init( // + &scratch->views[i].targets[k], // rtr + rr, // struct render_resources + &scratch->render_pass, // struct render_gfx_render_pass + target_image_view, // target + extent); // extent + } + + /* + * Update the count, doing it this way means free_resources + * will free the allocated images correctly. The count is one + * more then the index. + */ + scratch->view_count = i + 1; + } + + // Update the cached values. + scratch->extent = extent; + scratch->format = format; + + return true; +} + +void +chl_scratch_free_resources(struct chl_scratch *scratch, struct render_resources *rr) +{ + struct vk_bundle *vk = rr->vk; + + for (uint32_t i = 0; i < scratch->view_count; i++) { + for (uint32_t k = 0; k < COMP_SCRATCH_NUM_IMAGES; k++) { + render_gfx_target_resources_fini(&scratch->views[i].targets[k]); + } + + comp_scratch_single_images_free(&scratch->views[i].cssi, vk); + } + + // Nothing allocated. + scratch->view_count = 0; + scratch->extent.width = 0; + scratch->extent.height = 0; + scratch->format = VK_FORMAT_UNDEFINED; + + // Do this after the image targets as they reference the render pass. + if (scratch->render_pass_initialized) { + render_gfx_render_pass_fini(&scratch->render_pass); + scratch->render_pass_initialized = false; + } +} diff --git a/src/xrt/compositor/util/comp_high_level_scratch.h b/src/xrt/compositor/util/comp_high_level_scratch.h new file mode 100644 index 000000000..12271721c --- /dev/null +++ b/src/xrt/compositor/util/comp_high_level_scratch.h @@ -0,0 +1,200 @@ +// Copyright 2019-2024, Collabora, Ltd. +// Copyright 2024-2025, NVIDIA CORPORATION. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Higher level interface for scratch images. + * @author Jakob Bornecrantz + * @ingroup comp_util + */ + +#pragma once + + +#include "render/render_interface.h" + +#include "comp_scratch.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * Scratch images that can be used for staging buffers. + * + * @ingroup comp_util + */ +struct chl_scratch +{ + //! Shared render passed for the views. + struct render_gfx_render_pass render_pass; + + struct + { + //! Per-view scratch images. + struct comp_scratch_single_images cssi; + + //! Targets for rendering to the scratch buffer. + struct render_gfx_target_resources targets[COMP_SCRATCH_NUM_IMAGES]; + } views[XRT_MAX_VIEWS]; + + /*! + * Number of views that has been ensured and have Vulkan resources, + * all comp_scratch_single_images are always inited. + */ + uint32_t view_count; + + //! The extent used to create the images. + VkExtent2D extent; + + //! Format requested. + VkFormat format; + + //! Has the render pass been initialized. + bool render_pass_initialized; +}; + +/*! + * Must becalled before used and before the scratch images are registered with + * the u_var system. + * + * @memberof chl_scratch + */ +void +chl_scratch_init(struct chl_scratch *scratch); + +/*! + * Resources must be manually called before calling this functions, and the + * scratch images unregistered from the u_var system. + * + * @memberof chl_scratch + */ +void +chl_scratch_fini(struct chl_scratch *scratch); + +/*! + * Ensure the scratch images and the render target resources are created. + * + * @memberof chl_scratch + */ +bool +chl_scratch_ensure(struct chl_scratch *scratch, + struct render_resources *rr, + uint32_t view_count, + VkExtent2D extent, + const VkFormat format); + +/*! + * Free all Vulkan resources that this scratch has created. + * + * @memberof chl_scratch + */ +void +chl_scratch_free_resources(struct chl_scratch *scratch, struct render_resources *rr); + +/*! + * Get the image, see @ref comp_scratch_single_images_get_image. + * + * @memberof chl_scratch + */ +static inline VkImage +chl_scratch_get_image(struct chl_scratch *scratch, uint32_t view_index, uint32_t image_index) +{ + return comp_scratch_single_images_get_image(&scratch->views[view_index].cssi, image_index); +} + +/*! + * Get the sample view, see @ref comp_scratch_single_images_get_sample_view. + * + * @memberof chl_scratch + */ +static inline VkImageView +chl_scratch_get_sample_view(struct chl_scratch *scratch, uint32_t view_index, uint32_t image_index) +{ + return comp_scratch_single_images_get_sample_view(&scratch->views[view_index].cssi, image_index); +} + +/*! + * Get the storage view, see @ref comp_scratch_single_images_get_storage_view. + * + * @memberof chl_scratch + */ +static inline VkImageView +chl_scratch_get_storage_view(struct chl_scratch *scratch, uint32_t view_index, uint32_t image_index) +{ + return comp_scratch_single_images_get_storage_view(&scratch->views[view_index].cssi, image_index); +} + + +/* + * + * State + * + */ + +/*! + * Per view frame state tracking which index was gotten and if it was used. + * + * @ingroup comp_util + */ +struct chl_scratch_state_view +{ + uint32_t index; + + bool used; +}; + +/*! + * Used to track the index of images gotten for the images, and if it has been + * used. The user will need to mark images as used. + * + * @ingroup comp_util + */ +struct chl_scratch_state +{ + struct chl_scratch_state_view views[XRT_MAX_VIEWS]; +}; + +/*! + * Zeros out the struct and calls get on all the images, setting the @p index + * field on the state for each view. + * + * @memberof chl_scratch_state + */ +static inline void +chl_scratch_state_init_and_get(struct chl_scratch_state *scratch_state, struct chl_scratch *scratch) +{ + U_ZERO(scratch_state); + + // Loop over all the of the images in the scratch view. + for (uint32_t i = 0; i < scratch->view_count; i++) { + comp_scratch_single_images_get(&scratch->views[i].cssi, &scratch_state->views[i].index); + } +} + +/*! + * Calls discard or done on all the scratch images, it calls done if the @p used + * field is set to true. + * + * @memberof chl_scratch_state + */ +static inline void +chl_scratch_state_discard_or_done(struct chl_scratch_state *scratch_state, struct chl_scratch *scratch) +{ + // Loop over all the of the images in the scratch view. + for (uint32_t i = 0; i < scratch->view_count; i++) { + if (scratch_state->views[i].used) { + comp_scratch_single_images_done(&scratch->views[i].cssi); + } else { + comp_scratch_single_images_discard(&scratch->views[i].cssi); + } + } + + U_ZERO(scratch_state); +} + + +#ifdef __cplusplus +} +#endif