mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-10-31 16:09:03 -05:00 
			
		
		
		
	Merge pull request #3248 from ReinUsesLisp/vk-image
vk_image: Add an image object abstraction
This commit is contained in:
		| @@ -159,6 +159,8 @@ if (ENABLE_VULKAN) | |||||||
|         renderer_vulkan/vk_buffer_cache.h |         renderer_vulkan/vk_buffer_cache.h | ||||||
|         renderer_vulkan/vk_device.cpp |         renderer_vulkan/vk_device.cpp | ||||||
|         renderer_vulkan/vk_device.h |         renderer_vulkan/vk_device.h | ||||||
|  |         renderer_vulkan/vk_image.cpp | ||||||
|  |         renderer_vulkan/vk_image.h | ||||||
|         renderer_vulkan/vk_memory_manager.cpp |         renderer_vulkan/vk_memory_manager.cpp | ||||||
|         renderer_vulkan/vk_memory_manager.h |         renderer_vulkan/vk_memory_manager.h | ||||||
|         renderer_vulkan/vk_resource_manager.cpp |         renderer_vulkan/vk_resource_manager.cpp | ||||||
|   | |||||||
							
								
								
									
										106
									
								
								src/video_core/renderer_vulkan/vk_image.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								src/video_core/renderer_vulkan/vk_image.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | |||||||
|  | // Copyright 2018 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <memory> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "video_core/renderer_vulkan/declarations.h" | ||||||
|  | #include "video_core/renderer_vulkan/vk_device.h" | ||||||
|  | #include "video_core/renderer_vulkan/vk_image.h" | ||||||
|  | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||||||
|  |  | ||||||
|  | namespace Vulkan { | ||||||
|  |  | ||||||
|  | VKImage::VKImage(const VKDevice& device, VKScheduler& scheduler, | ||||||
|  |                  const vk::ImageCreateInfo& image_ci, vk::ImageAspectFlags aspect_mask) | ||||||
|  |     : device{device}, scheduler{scheduler}, format{image_ci.format}, aspect_mask{aspect_mask}, | ||||||
|  |       image_num_layers{image_ci.arrayLayers}, image_num_levels{image_ci.mipLevels} { | ||||||
|  |     UNIMPLEMENTED_IF_MSG(image_ci.queueFamilyIndexCount != 0, | ||||||
|  |                          "Queue family tracking is not implemented"); | ||||||
|  |  | ||||||
|  |     const auto dev = device.GetLogical(); | ||||||
|  |     image = dev.createImageUnique(image_ci, nullptr, device.GetDispatchLoader()); | ||||||
|  |  | ||||||
|  |     const u32 num_ranges = image_num_layers * image_num_levels; | ||||||
|  |     barriers.resize(num_ranges); | ||||||
|  |     subrange_states.resize(num_ranges, {{}, image_ci.initialLayout}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VKImage::~VKImage() = default; | ||||||
|  |  | ||||||
|  | void VKImage::Transition(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels, | ||||||
|  |                          vk::PipelineStageFlags new_stage_mask, vk::AccessFlags new_access, | ||||||
|  |                          vk::ImageLayout new_layout) { | ||||||
|  |     if (!HasChanged(base_layer, num_layers, base_level, num_levels, new_access, new_layout)) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::size_t cursor = 0; | ||||||
|  |     for (u32 layer_it = 0; layer_it < num_layers; ++layer_it) { | ||||||
|  |         for (u32 level_it = 0; level_it < num_levels; ++level_it, ++cursor) { | ||||||
|  |             const u32 layer = base_layer + layer_it; | ||||||
|  |             const u32 level = base_level + level_it; | ||||||
|  |             auto& state = GetSubrangeState(layer, level); | ||||||
|  |             barriers[cursor] = vk::ImageMemoryBarrier( | ||||||
|  |                 state.access, new_access, state.layout, new_layout, VK_QUEUE_FAMILY_IGNORED, | ||||||
|  |                 VK_QUEUE_FAMILY_IGNORED, *image, {aspect_mask, level, 1, layer, 1}); | ||||||
|  |             state.access = new_access; | ||||||
|  |             state.layout = new_layout; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     scheduler.RequestOutsideRenderPassOperationContext(); | ||||||
|  |  | ||||||
|  |     scheduler.Record([barriers = barriers, cursor](auto cmdbuf, auto& dld) { | ||||||
|  |         // TODO(Rodrigo): Implement a way to use the latest stage across subresources. | ||||||
|  |         constexpr auto stage_stub = vk::PipelineStageFlagBits::eAllCommands; | ||||||
|  |         cmdbuf.pipelineBarrier(stage_stub, stage_stub, {}, 0, nullptr, 0, nullptr, | ||||||
|  |                                static_cast<u32>(cursor), barriers.data(), dld); | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool VKImage::HasChanged(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels, | ||||||
|  |                          vk::AccessFlags new_access, vk::ImageLayout new_layout) noexcept { | ||||||
|  |     const bool is_full_range = base_layer == 0 && num_layers == image_num_layers && | ||||||
|  |                                base_level == 0 && num_levels == image_num_levels; | ||||||
|  |     if (!is_full_range) { | ||||||
|  |         state_diverged = true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!state_diverged) { | ||||||
|  |         auto& state = GetSubrangeState(0, 0); | ||||||
|  |         if (state.access != new_access || state.layout != new_layout) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     for (u32 layer_it = 0; layer_it < num_layers; ++layer_it) { | ||||||
|  |         for (u32 level_it = 0; level_it < num_levels; ++level_it) { | ||||||
|  |             const u32 layer = base_layer + layer_it; | ||||||
|  |             const u32 level = base_level + level_it; | ||||||
|  |             auto& state = GetSubrangeState(layer, level); | ||||||
|  |             if (state.access != new_access || state.layout != new_layout) { | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void VKImage::CreatePresentView() { | ||||||
|  |     // Image type has to be 2D to be presented. | ||||||
|  |     const vk::ImageViewCreateInfo image_view_ci({}, *image, vk::ImageViewType::e2D, format, {}, | ||||||
|  |                                                 {aspect_mask, 0, 1, 0, 1}); | ||||||
|  |     const auto dev = device.GetLogical(); | ||||||
|  |     const auto& dld = device.GetDispatchLoader(); | ||||||
|  |     present_view = dev.createImageViewUnique(image_view_ci, nullptr, dld); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | VKImage::SubrangeState& VKImage::GetSubrangeState(u32 layer, u32 level) noexcept { | ||||||
|  |     return subrange_states[static_cast<std::size_t>(layer * image_num_levels) + | ||||||
|  |                            static_cast<std::size_t>(level)]; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Vulkan | ||||||
							
								
								
									
										84
									
								
								src/video_core/renderer_vulkan/vk_image.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/video_core/renderer_vulkan/vk_image.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | // Copyright 2018 yuzu Emulator Project | ||||||
|  | // Licensed under GPLv2 or any later version | ||||||
|  | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include <memory> | ||||||
|  | #include <vector> | ||||||
|  |  | ||||||
|  | #include "common/common_types.h" | ||||||
|  | #include "video_core/renderer_vulkan/declarations.h" | ||||||
|  |  | ||||||
|  | namespace Vulkan { | ||||||
|  |  | ||||||
|  | class VKDevice; | ||||||
|  | class VKScheduler; | ||||||
|  |  | ||||||
|  | class VKImage { | ||||||
|  | public: | ||||||
|  |     explicit VKImage(const VKDevice& device, VKScheduler& scheduler, | ||||||
|  |                      const vk::ImageCreateInfo& image_ci, vk::ImageAspectFlags aspect_mask); | ||||||
|  |     ~VKImage(); | ||||||
|  |  | ||||||
|  |     /// Records in the passed command buffer an image transition and updates the state of the image. | ||||||
|  |     void Transition(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels, | ||||||
|  |                     vk::PipelineStageFlags new_stage_mask, vk::AccessFlags new_access, | ||||||
|  |                     vk::ImageLayout new_layout); | ||||||
|  |  | ||||||
|  |     /// Returns a view compatible with presentation, the image has to be 2D. | ||||||
|  |     vk::ImageView GetPresentView() { | ||||||
|  |         if (!present_view) { | ||||||
|  |             CreatePresentView(); | ||||||
|  |         } | ||||||
|  |         return *present_view; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Returns the Vulkan image handler. | ||||||
|  |     vk::Image GetHandle() const { | ||||||
|  |         return *image; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Returns the Vulkan format for this image. | ||||||
|  |     vk::Format GetFormat() const { | ||||||
|  |         return format; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Returns the Vulkan aspect mask. | ||||||
|  |     vk::ImageAspectFlags GetAspectMask() const { | ||||||
|  |         return aspect_mask; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | private: | ||||||
|  |     struct SubrangeState final { | ||||||
|  |         vk::AccessFlags access{};                             ///< Current access bits. | ||||||
|  |         vk::ImageLayout layout = vk::ImageLayout::eUndefined; ///< Current image layout. | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     bool HasChanged(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels, | ||||||
|  |                     vk::AccessFlags new_access, vk::ImageLayout new_layout) noexcept; | ||||||
|  |  | ||||||
|  |     /// Creates a presentation view. | ||||||
|  |     void CreatePresentView(); | ||||||
|  |  | ||||||
|  |     /// Returns the subrange state for a layer and layer. | ||||||
|  |     SubrangeState& GetSubrangeState(u32 layer, u32 level) noexcept; | ||||||
|  |  | ||||||
|  |     const VKDevice& device; ///< Device handler. | ||||||
|  |     VKScheduler& scheduler; ///< Device scheduler. | ||||||
|  |  | ||||||
|  |     const vk::Format format;                ///< Vulkan format. | ||||||
|  |     const vk::ImageAspectFlags aspect_mask; ///< Vulkan aspect mask. | ||||||
|  |     const u32 image_num_layers;             ///< Number of layers. | ||||||
|  |     const u32 image_num_levels;             ///< Number of mipmap levels. | ||||||
|  |  | ||||||
|  |     UniqueImage image;            ///< Image handle. | ||||||
|  |     UniqueImageView present_view; ///< Image view compatible with presentation. | ||||||
|  |  | ||||||
|  |     std::vector<vk::ImageMemoryBarrier> barriers; ///< Pool of barriers. | ||||||
|  |     std::vector<SubrangeState> subrange_states;   ///< Current subrange state. | ||||||
|  |  | ||||||
|  |     bool state_diverged = false; ///< True when subresources mismatch in layout. | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | } // namespace Vulkan | ||||||
		Reference in New Issue
	
	Block a user
	 Fernando Sahmkow
					Fernando Sahmkow