mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 13:20:03 +00:00 
			
		
		
		
	renderer_vulkan: Optimize descriptor binding (#7142)
For each draw, Citra will rebind all descriptor set slots and may redundantly re-bind descriptor-sets that were already bound. Instead it should only bind the descriptor-sets that have either changed or have had their buffer-offsets changed. This also allows entire calls to `vkCmdBindDescriptorSets` to be removed in the case that nothing has changed between draw calls.
This commit is contained in:
		
							parent
							
								
									5118798c30
								
							
						
					
					
						commit
						312068eebf
					
				
					 1 changed files with 53 additions and 11 deletions
				
			
		|  | @ -205,19 +205,55 @@ bool PipelineCache::BindPipeline(const PipelineInfo& info, bool wait_built) { | ||||||
|         return false; |         return false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     for (u32 i = 0; i < NUM_RASTERIZER_SETS; i++) { |     u32 new_descriptors_start = 0; | ||||||
|         if (!set_dirty[i]) { |     std::span<vk::DescriptorSet> new_descriptors_span{}; | ||||||
|             continue; |     std::span<u32> new_offsets_span{}; | ||||||
|         } | 
 | ||||||
|         bound_descriptor_sets[i] = descriptor_set_providers[i].Acquire(update_data[i]); |     // Ensure all the descriptor sets are set at least once at the beginning.
 | ||||||
|         set_dirty[i] = false; |     if (scheduler.IsStateDirty(StateFlags::DescriptorSets)) { | ||||||
|  |         set_dirty.set(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (set_dirty.any()) { | ||||||
|  |         for (u32 i = 0; i < NUM_RASTERIZER_SETS; i++) { | ||||||
|  |             if (!set_dirty.test(i)) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             bound_descriptor_sets[i] = descriptor_set_providers[i].Acquire(update_data[i]); | ||||||
|  |         } | ||||||
|  |         new_descriptors_span = bound_descriptor_sets; | ||||||
|  | 
 | ||||||
|  |         // Only send new offsets if the buffer descriptor-set changed.
 | ||||||
|  |         if (set_dirty.test(0)) { | ||||||
|  |             new_offsets_span = offsets; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Try to compact the number of updated descriptor-set slots to the ones that have actually
 | ||||||
|  |         // changed
 | ||||||
|  |         if (!set_dirty.all()) { | ||||||
|  |             const u64 dirty_mask = set_dirty.to_ulong(); | ||||||
|  |             new_descriptors_start = static_cast<u32>(std::countr_zero(dirty_mask)); | ||||||
|  |             const u32 new_descriptors_end = 64u - static_cast<u32>(std::countl_zero(dirty_mask)); | ||||||
|  |             const u32 new_descriptors_size = new_descriptors_end - new_descriptors_start; | ||||||
|  | 
 | ||||||
|  |             new_descriptors_span = | ||||||
|  |                 new_descriptors_span.subspan(new_descriptors_start, new_descriptors_size); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         set_dirty.reset(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     boost::container::static_vector<vk::DescriptorSet, NUM_RASTERIZER_SETS> new_descriptors( | ||||||
|  |         new_descriptors_span.begin(), new_descriptors_span.end()); | ||||||
|  |     boost::container::static_vector<u32, NUM_DYNAMIC_OFFSETS> new_offsets(new_offsets_span.begin(), | ||||||
|  |                                                                           new_offsets_span.end()); | ||||||
|  | 
 | ||||||
|     const bool is_dirty = scheduler.IsStateDirty(StateFlags::Pipeline); |     const bool is_dirty = scheduler.IsStateDirty(StateFlags::Pipeline); | ||||||
|     const bool pipeline_dirty = (current_pipeline != pipeline) || is_dirty; |     const bool pipeline_dirty = (current_pipeline != pipeline) || is_dirty; | ||||||
|     scheduler.Record([this, is_dirty, pipeline_dirty, pipeline, |     scheduler.Record([this, is_dirty, pipeline_dirty, pipeline, | ||||||
|                       current_dynamic = current_info.dynamic, dynamic = info.dynamic, |                       current_dynamic = current_info.dynamic, dynamic = info.dynamic, | ||||||
|                       descriptor_sets = bound_descriptor_sets, offsets = offsets, |                       new_descriptors_start, descriptor_sets = std::move(new_descriptors), | ||||||
|  |                       offsets = std::move(new_offsets), | ||||||
|                       current_rasterization = current_info.rasterization, |                       current_rasterization = current_info.rasterization, | ||||||
|                       current_depth_stencil = current_info.depth_stencil, |                       current_depth_stencil = current_info.depth_stencil, | ||||||
|                       rasterization = info.rasterization, |                       rasterization = info.rasterization, | ||||||
|  | @ -318,13 +354,16 @@ bool PipelineCache::BindPipeline(const PipelineInfo& info, bool wait_built) { | ||||||
|             } |             } | ||||||
|             cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline->Handle()); |             cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline->Handle()); | ||||||
|         } |         } | ||||||
|         cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, *pipeline_layout, 0, | 
 | ||||||
|                                   descriptor_sets, offsets); |         if (descriptor_sets.size()) { | ||||||
|  |             cmdbuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, *pipeline_layout, | ||||||
|  |                                       new_descriptors_start, descriptor_sets, offsets); | ||||||
|  |         } | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     current_info = info; |     current_info = info; | ||||||
|     current_pipeline = pipeline; |     current_pipeline = pipeline; | ||||||
|     scheduler.MarkStateNonDirty(StateFlags::Pipeline); |     scheduler.MarkStateNonDirty(StateFlags::Pipeline | StateFlags::DescriptorSets); | ||||||
| 
 | 
 | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  | @ -497,7 +536,10 @@ void PipelineCache::BindTexelBuffer(u32 binding, vk::BufferView buffer_view) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void PipelineCache::SetBufferOffset(u32 binding, size_t offset) { | void PipelineCache::SetBufferOffset(u32 binding, size_t offset) { | ||||||
|     offsets[binding] = static_cast<u32>(offset); |     if (offsets[binding] != static_cast<u32>(offset)) { | ||||||
|  |         offsets[binding] = static_cast<u32>(offset); | ||||||
|  |         set_dirty[0] = true; | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool PipelineCache::IsCacheValid(std::span<const u8> data) const { | bool PipelineCache::IsCacheValid(std::span<const u8> data) const { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue