video_core: Use sampler IDs instead pointers in the pipeline config

The previous approach of storing pointers returned by `GetGraphicsSampler`/`GetComputeSampler` caused UB, as these functions can cause reallocation of the sampler slot vector and therefore invalidate the pointers
This commit is contained in:
Wollnashorn 2023-06-16 13:26:44 +02:00
parent a3b7b5b22a
commit 2dc0ff79ec
8 changed files with 68 additions and 23 deletions

View file

@ -87,7 +87,8 @@ void ComputePipeline::Configure() {
texture_cache.SynchronizeComputeDescriptors(); texture_cache.SynchronizeComputeDescriptors();
boost::container::static_vector<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views; boost::container::static_vector<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views;
std::array<GLuint, MAX_TEXTURES> samplers; boost::container::static_vector<VideoCommon::SamplerId, MAX_TEXTURES> samplers;
std::array<GLuint, MAX_TEXTURES> gl_samplers;
std::array<GLuint, MAX_TEXTURES> textures; std::array<GLuint, MAX_TEXTURES> textures;
std::array<GLuint, MAX_IMAGES> images; std::array<GLuint, MAX_IMAGES> images;
GLsizei sampler_binding{}; GLsizei sampler_binding{};
@ -131,7 +132,6 @@ void ComputePipeline::Configure() {
for (u32 index = 0; index < desc.count; ++index) { for (u32 index = 0; index < desc.count; ++index) {
const auto handle{read_handle(desc, index)}; const auto handle{read_handle(desc, index)};
views.push_back({handle.first}); views.push_back({handle.first});
samplers[sampler_binding++] = 0;
} }
} }
for (const auto& desc : info.image_buffer_descriptors) { for (const auto& desc : info.image_buffer_descriptors) {
@ -142,8 +142,8 @@ void ComputePipeline::Configure() {
const auto handle{read_handle(desc, index)}; const auto handle{read_handle(desc, index)};
views.push_back({handle.first}); views.push_back({handle.first});
Sampler* const sampler = texture_cache.GetComputeSampler(handle.second); VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second);
samplers[sampler_binding++] = sampler->Handle(); samplers.push_back(sampler);
} }
} }
for (const auto& desc : info.image_descriptors) { for (const auto& desc : info.image_descriptors) {
@ -186,10 +186,17 @@ void ComputePipeline::Configure() {
const VideoCommon::ImageViewInOut* views_it{views.data() + num_texture_buffers + const VideoCommon::ImageViewInOut* views_it{views.data() + num_texture_buffers +
num_image_buffers}; num_image_buffers};
const VideoCommon::SamplerId* samplers_it{samplers.data()};
texture_binding += num_texture_buffers; texture_binding += num_texture_buffers;
image_binding += num_image_buffers; image_binding += num_image_buffers;
u32 texture_scaling_mask{}; u32 texture_scaling_mask{};
for (const auto& desc : info.texture_buffer_descriptors) {
for (u32 index = 0; index < desc.count; ++index) {
gl_samplers[sampler_binding++] = 0;
}
}
for (const auto& desc : info.texture_descriptors) { for (const auto& desc : info.texture_descriptors) {
for (u32 index = 0; index < desc.count; ++index) { for (u32 index = 0; index < desc.count; ++index) {
ImageView& image_view{texture_cache.GetImageView((views_it++)->id)}; ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
@ -198,6 +205,12 @@ void ComputePipeline::Configure() {
texture_scaling_mask |= 1u << texture_binding; texture_scaling_mask |= 1u << texture_binding;
} }
++texture_binding; ++texture_binding;
const Sampler& sampler{texture_cache.GetSampler(*(samplers_it++))};
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
!image_view.SupportsAnisotropy()};
gl_samplers[sampler_binding++] =
use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() : sampler.Handle();
} }
} }
u32 image_scaling_mask{}; u32 image_scaling_mask{};
@ -228,7 +241,7 @@ void ComputePipeline::Configure() {
if (texture_binding != 0) { if (texture_binding != 0) {
ASSERT(texture_binding == sampler_binding); ASSERT(texture_binding == sampler_binding);
glBindTextures(0, texture_binding, textures.data()); glBindTextures(0, texture_binding, textures.data());
glBindSamplers(0, sampler_binding, samplers.data()); glBindSamplers(0, sampler_binding, gl_samplers.data());
} }
if (image_binding != 0) { if (image_binding != 0) {
glBindImageTextures(0, image_binding, images.data()); glBindImageTextures(0, image_binding, images.data());

View file

@ -275,7 +275,7 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c
template <typename Spec> template <typename Spec>
void GraphicsPipeline::ConfigureImpl(bool is_indexed) { void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
std::array<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views; std::array<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views;
std::array<const Sampler*, MAX_TEXTURES> samplers; std::array<VideoCommon::SamplerId, MAX_TEXTURES> samplers;
size_t views_index{}; size_t views_index{};
size_t samplers_index{}; size_t samplers_index{};
@ -350,7 +350,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
const auto handle{read_handle(desc, index)}; const auto handle{read_handle(desc, index)};
views[views_index++] = {handle.first}; views[views_index++] = {handle.first};
Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)}; VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
samplers[samplers_index++] = sampler; samplers[samplers_index++] = sampler;
} }
} }
@ -444,7 +444,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
program_manager.BindSourcePrograms(source_programs); program_manager.BindSourcePrograms(source_programs);
} }
const VideoCommon::ImageViewInOut* views_it{views.data()}; const VideoCommon::ImageViewInOut* views_it{views.data()};
const Sampler** samplers_it{samplers.data()}; const VideoCommon::SamplerId* samplers_it{samplers.data()};
GLsizei texture_binding = 0; GLsizei texture_binding = 0;
GLsizei image_binding = 0; GLsizei image_binding = 0;
GLsizei sampler_binding{}; GLsizei sampler_binding{};
@ -484,7 +484,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
++texture_binding; ++texture_binding;
++stage_texture_binding; ++stage_texture_binding;
const Sampler& sampler{**(samplers_it++)}; const Sampler& sampler{texture_cache.GetSampler(*(samplers_it++))};
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() && const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
!image_view.SupportsAnisotropy()}; !image_view.SupportsAnisotropy()};
gl_samplers[sampler_binding++] = gl_samplers[sampler_binding++] =

View file

@ -178,7 +178,7 @@ public:
inline void PushImageDescriptors(TextureCache& texture_cache, inline void PushImageDescriptors(TextureCache& texture_cache,
GuestDescriptorQueue& guest_descriptor_queue, GuestDescriptorQueue& guest_descriptor_queue,
const Shader::Info& info, RescalingPushConstant& rescaling, const Shader::Info& info, RescalingPushConstant& rescaling,
const Sampler**& samplers, const VideoCommon::SamplerId*& samplers,
const VideoCommon::ImageViewInOut*& views) { const VideoCommon::ImageViewInOut*& views) {
const u32 num_texture_buffers = Shader::NumDescriptors(info.texture_buffer_descriptors); const u32 num_texture_buffers = Shader::NumDescriptors(info.texture_buffer_descriptors);
const u32 num_image_buffers = Shader::NumDescriptors(info.image_buffer_descriptors); const u32 num_image_buffers = Shader::NumDescriptors(info.image_buffer_descriptors);
@ -187,9 +187,10 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
for (const auto& desc : info.texture_descriptors) { for (const auto& desc : info.texture_descriptors) {
for (u32 index = 0; index < desc.count; ++index) { for (u32 index = 0; index < desc.count; ++index) {
const VideoCommon::ImageViewId image_view_id{(views++)->id}; const VideoCommon::ImageViewId image_view_id{(views++)->id};
const VideoCommon::SamplerId sampler_id{*(samplers++)};
ImageView& image_view{texture_cache.GetImageView(image_view_id)}; ImageView& image_view{texture_cache.GetImageView(image_view_id)};
const VkImageView vk_image_view{image_view.Handle(desc.type)}; const VkImageView vk_image_view{image_view.Handle(desc.type)};
const Sampler& sampler{**(samplers++)}; const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() && const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
!image_view.SupportsAnisotropy()}; !image_view.SupportsAnisotropy()};
const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()

View file

@ -115,7 +115,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
static constexpr size_t max_elements = 64; static constexpr size_t max_elements = 64;
boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views; boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views;
boost::container::static_vector<const Sampler*, max_elements> samplers; boost::container::static_vector<VideoCommon::SamplerId, max_elements> samplers;
const auto& qmd{kepler_compute.launch_description}; const auto& qmd{kepler_compute.launch_description};
const auto& cbufs{qmd.const_buffer_config}; const auto& cbufs{qmd.const_buffer_config};
@ -160,7 +160,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
const auto handle{read_handle(desc, index)}; const auto handle{read_handle(desc, index)};
views.push_back({handle.first}); views.push_back({handle.first});
Sampler* const sampler = texture_cache.GetComputeSampler(handle.second); VideoCommon::SamplerId sampler = texture_cache.GetComputeSamplerId(handle.second);
samplers.push_back(sampler); samplers.push_back(sampler);
} }
} }
@ -192,7 +192,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
buffer_cache.BindHostComputeBuffers(); buffer_cache.BindHostComputeBuffers();
RescalingPushConstant rescaling; RescalingPushConstant rescaling;
const Sampler** samplers_it{samplers.data()}; const VideoCommon::SamplerId* samplers_it{samplers.data()};
const VideoCommon::ImageViewInOut* views_it{views.data()}; const VideoCommon::ImageViewInOut* views_it{views.data()};
PushImageDescriptors(texture_cache, guest_descriptor_queue, info, rescaling, samplers_it, PushImageDescriptors(texture_cache, guest_descriptor_queue, info, rescaling, samplers_it,
views_it); views_it);

View file

@ -298,7 +298,7 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) {
template <typename Spec> template <typename Spec>
void GraphicsPipeline::ConfigureImpl(bool is_indexed) { void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views; std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views;
std::array<const Sampler*, MAX_IMAGE_ELEMENTS> samplers; std::array<VideoCommon::SamplerId, MAX_IMAGE_ELEMENTS> samplers;
size_t sampler_index{}; size_t sampler_index{};
size_t view_index{}; size_t view_index{};
@ -367,7 +367,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
const auto handle{read_handle(desc, index)}; const auto handle{read_handle(desc, index)};
views[view_index++] = {handle.first}; views[view_index++] = {handle.first};
Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)}; VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
samplers[sampler_index++] = sampler; samplers[sampler_index++] = sampler;
} }
} }
@ -453,7 +453,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
RescalingPushConstant rescaling; RescalingPushConstant rescaling;
RenderAreaPushConstant render_area; RenderAreaPushConstant render_area;
const Sampler** samplers_it{samplers.data()}; const VideoCommon::SamplerId* samplers_it{samplers.data()};
const VideoCommon::ImageViewInOut* views_it{views.data()}; const VideoCommon::ImageViewInOut* views_it{views.data()};
const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE { const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
buffer_cache.BindHostStageBuffers(stage); buffer_cache.BindHostStageBuffers(stage);

View file

@ -46,7 +46,6 @@ ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_in
ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {} ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {}
bool ImageViewBase::SupportsAnisotropy() const noexcept { bool ImageViewBase::SupportsAnisotropy() const noexcept {
using namespace VideoCommon;
switch (format) { switch (format) {
case PixelFormat::R8_UNORM: case PixelFormat::R8_UNORM:
case PixelFormat::R8_SNORM: case PixelFormat::R8_SNORM:

View file

@ -222,30 +222,50 @@ void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) {
template <class P> template <class P>
typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) { typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) {
return &slot_samplers[GetGraphicsSamplerId(index)];
}
template <class P>
typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) {
return &slot_samplers[GetComputeSamplerId(index)];
}
template <class P>
SamplerId TextureCache<P>::GetGraphicsSamplerId(u32 index) {
if (index > channel_state->graphics_sampler_table.Limit()) { if (index > channel_state->graphics_sampler_table.Limit()) {
LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
return &slot_samplers[NULL_SAMPLER_ID]; return NULL_SAMPLER_ID;
} }
const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index); const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index);
SamplerId& id = channel_state->graphics_sampler_ids[index]; SamplerId& id = channel_state->graphics_sampler_ids[index];
if (is_new) { if (is_new) {
id = FindSampler(descriptor); id = FindSampler(descriptor);
} }
return &slot_samplers[id]; return id;
} }
template <class P> template <class P>
typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) { SamplerId TextureCache<P>::GetComputeSamplerId(u32 index) {
if (index > channel_state->compute_sampler_table.Limit()) { if (index > channel_state->compute_sampler_table.Limit()) {
LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index); LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
return &slot_samplers[NULL_SAMPLER_ID]; return NULL_SAMPLER_ID;
} }
const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index); const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index);
SamplerId& id = channel_state->compute_sampler_ids[index]; SamplerId& id = channel_state->compute_sampler_ids[index];
if (is_new) { if (is_new) {
id = FindSampler(descriptor); id = FindSampler(descriptor);
} }
return &slot_samplers[id]; return id;
}
template <class P>
const typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) const noexcept {
return slot_samplers[id];
}
template <class P>
typename P::Sampler& TextureCache<P>::GetSampler(SamplerId id) noexcept {
return slot_samplers[id];
} }
template <class P> template <class P>

View file

@ -159,6 +159,18 @@ public:
/// Get the sampler from the compute descriptor table in the specified index /// Get the sampler from the compute descriptor table in the specified index
Sampler* GetComputeSampler(u32 index); Sampler* GetComputeSampler(u32 index);
/// Get the sampler id from the graphics descriptor table in the specified index
SamplerId GetGraphicsSamplerId(u32 index);
/// Get the sampler id from the compute descriptor table in the specified index
SamplerId GetComputeSamplerId(u32 index);
/// Return a constant reference to the given sampler id
[[nodiscard]] const Sampler& GetSampler(SamplerId id) const noexcept;
/// Return a reference to the given sampler id
[[nodiscard]] Sampler& GetSampler(SamplerId id) noexcept;
/// Refresh the state for graphics image view and sampler descriptors /// Refresh the state for graphics image view and sampler descriptors
void SynchronizeGraphicsDescriptors(); void SynchronizeGraphicsDescriptors();