custom_tex_manager: Fix a race with async decoding
This commit is contained in:
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
|
Reference in New Issue
Block a user