Avoid redundant VkImageView recreation

There are a lot of cases of `VkImageView` being recreated arbitrarily due to it being tied to the ephemeral object `TextureView` rather than `Texture`, this commit flips that by storing all `VkImageView`s inside `Texture` with `TextureView` simply holding a copy of the handle to them. Additionally, this change results in stable `VkImageView` handles and helps in paving the path for framebuffer caching when `VK_KHR_imageless_framebuffer` is unavailable.
This commit is contained in:
PixelyIon 2022-05-01 17:07:20 +05:30
parent 41b2c2dc7b
commit af7f0c301e
2 changed files with 35 additions and 25 deletions

View File

@ -24,10 +24,16 @@ namespace skyline::gpu {
TextureView::TextureView(std::shared_ptr<Texture> texture, vk::ImageViewType type, vk::ImageSubresourceRange range, texture::Format format, vk::ComponentMapping mapping) : texture(std::move(texture)), type(type), format(format), mapping(mapping), range(range) {} TextureView::TextureView(std::shared_ptr<Texture> texture, vk::ImageViewType type, vk::ImageSubresourceRange range, texture::Format format, vk::ComponentMapping mapping) : texture(std::move(texture)), type(type), format(format), mapping(mapping), range(range) {}
vk::ImageView TextureView::GetView() { Texture::TextureViewStorage::TextureViewStorage(vk::ImageViewType type, texture::Format format, vk::ComponentMapping mapping, vk::ImageSubresourceRange range, vk::raii::ImageView &&vkView) : type(type), format(format), mapping(mapping), range(range), vkView(std::move(vkView)) {}
if (view)
return **view;
vk::ImageView TextureView::GetView() {
if (vkView)
return vkView;
auto it{std::find_if(texture->views.begin(), texture->views.end(), [this](const Texture::TextureViewStorage &view) {
return view.type == type && view.format == format && view.mapping == mapping && view.range == range;
})};
if (it == texture->views.end()) {
vk::ImageViewCreateInfo createInfo{ vk::ImageViewCreateInfo createInfo{
.image = texture->GetBacking(), .image = texture->GetBacking(),
.viewType = type, .viewType = type,
@ -36,7 +42,10 @@ namespace skyline::gpu {
.subresourceRange = range, .subresourceRange = range,
}; };
return *view.emplace(texture->gpu.vkDevice, createInfo); it = texture->views.emplace(texture->views.end(), type, format, mapping, range, vk::raii::ImageView{texture->gpu.vkDevice, createInfo});
}
return vkView = *it->vkView;
} }
void TextureView::lock() { void TextureView::lock() {
@ -530,22 +539,10 @@ namespace skyline::gpu {
if (!pFormat) if (!pFormat)
pFormat = format; pFormat = format;
for (auto viewIt{views.begin()}; viewIt != views.end();) {
auto view{viewIt->lock()};
if (view && type == view->type && pFormat == view->format && range == view->range && mapping == view->mapping)
return view;
else if (!view)
viewIt = views.erase(viewIt);
else
++viewIt;
}
if (gpu.traits.quirks.vkImageMutableFormatCostly && pFormat->vkFormat != format->vkFormat) if (gpu.traits.quirks.vkImageMutableFormatCostly && pFormat->vkFormat != format->vkFormat)
Logger::Warn("Creating a view of a texture with a different format without mutable format: {} - {}", vk::to_string(pFormat->vkFormat), vk::to_string(format->vkFormat)); Logger::Warn("Creating a view of a texture with a different format without mutable format: {} - {}", vk::to_string(pFormat->vkFormat), vk::to_string(format->vkFormat));
auto view{std::make_shared<TextureView>(shared_from_this(), type, range, pFormat, mapping)}; return std::make_shared<TextureView>(shared_from_this(), type, range, pFormat, mapping);
views.push_back(view);
return view;
} }
void Texture::CopyFrom(std::shared_ptr<Texture> source, const vk::ImageSubresourceRange &subresource) { void Texture::CopyFrom(std::shared_ptr<Texture> source, const vk::ImageSubresourceRange &subresource) {

View File

@ -284,7 +284,7 @@ namespace skyline::gpu {
*/ */
class TextureView : public FenceCycleDependency, public std::enable_shared_from_this<TextureView> { class TextureView : public FenceCycleDependency, public std::enable_shared_from_this<TextureView> {
private: private:
std::optional<vk::raii::ImageView> view; vk::ImageView vkView{};
public: public:
std::shared_ptr<Texture> texture; std::shared_ptr<Texture> texture;
@ -348,7 +348,20 @@ namespace skyline::gpu {
GpuDirty, //!< The GPU texture has been modified but the CPU mappings have not been updated GpuDirty, //!< The GPU texture has been modified but the CPU mappings have not been updated
} dirtyState{DirtyState::CpuDirty}; //!< The state of the CPU mappings with respect to the GPU texture } dirtyState{DirtyState::CpuDirty}; //!< The state of the CPU mappings with respect to the GPU texture
std::vector<std::weak_ptr<TextureView>> views; //!< TextureView(s) that are backed by this Texture, used for repointing to a new Texture on deletion /**
* @brief Storage for all metadata about a specific view into the buffer, used to prevent redundant view creation and duplication of VkBufferView(s)
*/
struct TextureViewStorage {
vk::ImageViewType type;
texture::Format format;
vk::ComponentMapping mapping;
vk::ImageSubresourceRange range;
vk::raii::ImageView vkView;
TextureViewStorage(vk::ImageViewType type, texture::Format format, vk::ComponentMapping mapping, vk::ImageSubresourceRange range, vk::raii::ImageView&& vkView);
};
std::vector<TextureViewStorage> views;
friend TextureManager; friend TextureManager;
friend TextureView; friend TextureView;