From c34bc45bf1d143b3c8e5f04f0e8cabcd2edde0cb Mon Sep 17 00:00:00 2001 From: GPUCode Date: Thu, 2 Mar 2023 16:06:02 +0200 Subject: [PATCH] custom_tex_manager: Fix a race with async decoding --- src/common/image_util.cpp | 2 -- .../rasterizer_cache/custom_tex_manager.cpp | 29 +++++++++++-------- .../rasterizer_cache/custom_tex_manager.h | 14 +++++++-- src/video_core/rasterizer_cache/utils.cpp | 8 ++++- src/video_core/rasterizer_cache/utils.h | 11 +++---- .../renderer_vulkan/vk_texture_runtime.h | 1 - 6 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/common/image_util.cpp b/src/common/image_util.cpp index f4291303d..c19977bdc 100644 --- a/src/common/image_util.cpp +++ b/src/common/image_util.cpp @@ -100,8 +100,6 @@ bool LoadDDSKTX(std::span dds_data, std::span out_data) { ddsktx_sub_data sub_data{}; ddsktx_get_sub(&tc, &sub_data, dds_data.data(), size, 0, 0, 0); - - ASSERT(out_data.size() == sub_data.size_bytes); std::memcpy(out_data.data(), sub_data.buff, sub_data.size_bytes); return true; diff --git a/src/video_core/rasterizer_cache/custom_tex_manager.cpp b/src/video_core/rasterizer_cache/custom_tex_manager.cpp index 722ecfc13..5a3c64335 100644 --- a/src/video_core/rasterizer_cache/custom_tex_manager.cpp +++ b/src/video_core/rasterizer_cache/custom_tex_manager.cpp @@ -87,17 +87,17 @@ void CustomTexManager::FindCustomTextures() { FileUtil::GetAllFilesFromNestedEntries(texture_dir, textures); // Reserve space for all the textures in the folder - const size_t num_textures = textures.size(); + const std::size_t num_textures = textures.size(); custom_textures.resize(num_textures); - const auto load = [&](u32 begin, u32 end) { + const auto load = [&](std::size_t begin, std::size_t end) { u32 width{}; u32 height{}; u32 format{}; unsigned long long hash{}; std::string ext(3, ' '); - for (u32 i = begin; i < end; i++) { + for (std::size_t i = begin; i < end; i++) { const auto& file = textures[i]; const std::string& path = file.physicalName; if (file.isDirectory || !file.virtualName.starts_with("tex1_")) { @@ -233,16 +233,25 @@ CustomTexture& CustomTexManager::GetTexture(u64 data_hash) { } void CustomTexManager::DecodeToStaging(CustomTexture& texture, StagingData& staging) { - if (texture.decoded) { + if (texture.state == DecodeState::Decoded) { // Nothing to do here, just copy over the data ASSERT_MSG(staging.size == texture.staging_size, "Incorrect staging size for custom texture with hash {:016X}", texture.hash); std::memcpy(staging.mapped.data(), texture.data.data(), texture.data.size()); return; } + if (texture.state == DecodeState::Pending) { + // Can occur if a texture is re-uploaded shortly after a decode started. + // Since this is quite rare just wait for the data. + LOG_WARNING(Render, "Texture requested while pending decode!"); + texture.state.wait(DecodeState::Pending); + std::memcpy(staging.mapped.data(), texture.data.data(), texture.data.size()); + return; + } // Set an atomic flag in staging data so the backend can wait until the data is finished - staging.flag = &texture.flag; + staging.flag = &texture.state; + texture.state = DecodeState::Pending; const auto decode = [this, &texture, mapped = staging.mapped]() { // Read the file this is potentially the most expensive step @@ -267,18 +276,14 @@ void CustomTexManager::DecodeToStaging(CustomTexture& texture, StagingData& stag break; case CustomFileFormat::DDS: case CustomFileFormat::KTX: - // Compressed formats don't need CPU decoding and must be pre-flippede + // Compressed formats don't need CPU decoding and must be pre-flipped. LoadDDSKTX(file_data.Span(), decoded_data); break; } - // Copy it over to the staging memory - texture.decoded = true; + // Copy it over to the staging memory and notify the backend that decode is done, std::memcpy(mapped.data(), decoded_data.data(), decoded_data.size()); - - // Notify the backend that decode is done - texture.flag.test_and_set(); - texture.flag.notify_all(); + texture.MarkDecoded(); }; workers->QueueWork(std::move(decode)); diff --git a/src/video_core/rasterizer_cache/custom_tex_manager.h b/src/video_core/rasterizer_cache/custom_tex_manager.h index 7061dbeab..9ab007f53 100644 --- a/src/video_core/rasterizer_cache/custom_tex_manager.h +++ b/src/video_core/rasterizer_cache/custom_tex_manager.h @@ -28,6 +28,12 @@ enum class CustomFileFormat : u32 { KTX = 2, }; +enum class DecodeState : u32 { + None = 0, + Pending = 1, + Decoded = 2, +}; + struct CustomTexture { u32 width; u32 height; @@ -37,12 +43,16 @@ struct CustomTexture { std::string path; std::size_t staging_size; std::vector data; - std::atomic_flag flag; - bool decoded = false; + std::atomic state{}; operator bool() const noexcept { return hash != 0; } + + void MarkDecoded() noexcept { + state = DecodeState::Decoded; + state.notify_all(); + } }; class CustomTexManager { diff --git a/src/video_core/rasterizer_cache/utils.cpp b/src/video_core/rasterizer_cache/utils.cpp index f580a693e..1e437fe03 100644 --- a/src/video_core/rasterizer_cache/utils.cpp +++ b/src/video_core/rasterizer_cache/utils.cpp @@ -3,13 +3,19 @@ // Refer to the license.txt file included. #include "common/assert.h" +#include "video_core/rasterizer_cache/custom_tex_manager.h" #include "video_core/rasterizer_cache/surface_params.h" #include "video_core/rasterizer_cache/texture_codec.h" #include "video_core/rasterizer_cache/utils.h" -#include "video_core/texture/texture_decode.h" namespace VideoCore { +void StagingData::Wait() const noexcept { + if (flag) { + flag->wait(DecodeState::Pending); + } +} + void EncodeTexture(const SurfaceParams& surface_info, PAddr start_addr, PAddr end_addr, std::span source, std::span dest, bool convert) { const PixelFormat format = surface_info.pixel_format; diff --git a/src/video_core/rasterizer_cache/utils.h b/src/video_core/rasterizer_cache/utils.h index b7e63691d..2c08002f2 100644 --- a/src/video_core/rasterizer_cache/utils.h +++ b/src/video_core/rasterizer_cache/utils.h @@ -81,18 +81,15 @@ struct BufferTextureCopy { u32 texture_level; }; +enum class DecodeState : u32; + struct StagingData { u32 size = 0; std::span mapped{}; u64 buffer_offset = 0; - const std::atomic_flag* flag{}; + const std::atomic* flag{}; - void Wait() const noexcept { - if (!flag) { - return; - } - flag->wait(false); - } + void Wait() const noexcept; }; struct TextureCubeConfig { diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.h b/src/video_core/renderer_vulkan/vk_texture_runtime.h index 9afeafc75..564dec1a0 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.h +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.h @@ -6,7 +6,6 @@ #include #include -#include #include "video_core/rasterizer_cache/framebuffer_base.h" #include "video_core/rasterizer_cache/rasterizer_cache_base.h" #include "video_core/rasterizer_cache/surface_base.h"