custom_tex_manager: Fix a race with async decoding

This commit is contained in:
GPUCode
2023-03-02 16:06:02 +02:00
parent 19617f32c8
commit c34bc45bf1
6 changed files with 40 additions and 25 deletions

View File

@ -100,8 +100,6 @@ bool LoadDDSKTX(std::span<const u8> dds_data, std::span<u8> 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;

View File

@ -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));

View File

@ -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<u8> data;
std::atomic_flag flag;
bool decoded = false;
std::atomic<DecodeState> state{};
operator bool() const noexcept {
return hash != 0;
}
void MarkDecoded() noexcept {
state = DecodeState::Decoded;
state.notify_all();
}
};
class CustomTexManager {

View File

@ -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<u8> source, std::span<u8> dest, bool convert) {
const PixelFormat format = surface_info.pixel_format;

View File

@ -81,18 +81,15 @@ struct BufferTextureCopy {
u32 texture_level;
};
enum class DecodeState : u32;
struct StagingData {
u32 size = 0;
std::span<u8> mapped{};
u64 buffer_offset = 0;
const std::atomic_flag* flag{};
const std::atomic<DecodeState>* flag{};
void Wait() const noexcept {
if (!flag) {
return;
}
flag->wait(false);
}
void Wait() const noexcept;
};
struct TextureCubeConfig {

View File

@ -6,7 +6,6 @@
#include <set>
#include <span>
#include <vulkan/vulkan_hash.hpp>
#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"