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_sub_data sub_data{};
|
||||||
ddsktx_get_sub(&tc, &sub_data, dds_data.data(), size, 0, 0, 0);
|
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);
|
std::memcpy(out_data.data(), sub_data.buff, sub_data.size_bytes);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -87,17 +87,17 @@ void CustomTexManager::FindCustomTextures() {
|
|||||||
FileUtil::GetAllFilesFromNestedEntries(texture_dir, textures);
|
FileUtil::GetAllFilesFromNestedEntries(texture_dir, textures);
|
||||||
|
|
||||||
// Reserve space for all the textures in the folder
|
// 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);
|
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 width{};
|
||||||
u32 height{};
|
u32 height{};
|
||||||
u32 format{};
|
u32 format{};
|
||||||
unsigned long long hash{};
|
unsigned long long hash{};
|
||||||
std::string ext(3, ' ');
|
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 auto& file = textures[i];
|
||||||
const std::string& path = file.physicalName;
|
const std::string& path = file.physicalName;
|
||||||
if (file.isDirectory || !file.virtualName.starts_with("tex1_")) {
|
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) {
|
void CustomTexManager::DecodeToStaging(CustomTexture& texture, StagingData& staging) {
|
||||||
if (texture.decoded) {
|
if (texture.state == DecodeState::Decoded) {
|
||||||
// Nothing to do here, just copy over the data
|
// Nothing to do here, just copy over the data
|
||||||
ASSERT_MSG(staging.size == texture.staging_size,
|
ASSERT_MSG(staging.size == texture.staging_size,
|
||||||
"Incorrect staging size for custom texture with hash {:016X}", texture.hash);
|
"Incorrect staging size for custom texture with hash {:016X}", texture.hash);
|
||||||
std::memcpy(staging.mapped.data(), texture.data.data(), texture.data.size());
|
std::memcpy(staging.mapped.data(), texture.data.data(), texture.data.size());
|
||||||
return;
|
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
|
// 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]() {
|
const auto decode = [this, &texture, mapped = staging.mapped]() {
|
||||||
// Read the file this is potentially the most expensive step
|
// Read the file this is potentially the most expensive step
|
||||||
@ -267,18 +276,14 @@ void CustomTexManager::DecodeToStaging(CustomTexture& texture, StagingData& stag
|
|||||||
break;
|
break;
|
||||||
case CustomFileFormat::DDS:
|
case CustomFileFormat::DDS:
|
||||||
case CustomFileFormat::KTX:
|
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);
|
LoadDDSKTX(file_data.Span(), decoded_data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy it over to the staging memory
|
// Copy it over to the staging memory and notify the backend that decode is done,
|
||||||
texture.decoded = true;
|
|
||||||
std::memcpy(mapped.data(), decoded_data.data(), decoded_data.size());
|
std::memcpy(mapped.data(), decoded_data.data(), decoded_data.size());
|
||||||
|
texture.MarkDecoded();
|
||||||
// Notify the backend that decode is done
|
|
||||||
texture.flag.test_and_set();
|
|
||||||
texture.flag.notify_all();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
workers->QueueWork(std::move(decode));
|
workers->QueueWork(std::move(decode));
|
||||||
|
@ -28,6 +28,12 @@ enum class CustomFileFormat : u32 {
|
|||||||
KTX = 2,
|
KTX = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class DecodeState : u32 {
|
||||||
|
None = 0,
|
||||||
|
Pending = 1,
|
||||||
|
Decoded = 2,
|
||||||
|
};
|
||||||
|
|
||||||
struct CustomTexture {
|
struct CustomTexture {
|
||||||
u32 width;
|
u32 width;
|
||||||
u32 height;
|
u32 height;
|
||||||
@ -37,12 +43,16 @@ struct CustomTexture {
|
|||||||
std::string path;
|
std::string path;
|
||||||
std::size_t staging_size;
|
std::size_t staging_size;
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
std::atomic_flag flag;
|
std::atomic<DecodeState> state{};
|
||||||
bool decoded = false;
|
|
||||||
|
|
||||||
operator bool() const noexcept {
|
operator bool() const noexcept {
|
||||||
return hash != 0;
|
return hash != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MarkDecoded() noexcept {
|
||||||
|
state = DecodeState::Decoded;
|
||||||
|
state.notify_all();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CustomTexManager {
|
class CustomTexManager {
|
||||||
|
@ -3,13 +3,19 @@
|
|||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "common/assert.h"
|
#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/surface_params.h"
|
||||||
#include "video_core/rasterizer_cache/texture_codec.h"
|
#include "video_core/rasterizer_cache/texture_codec.h"
|
||||||
#include "video_core/rasterizer_cache/utils.h"
|
#include "video_core/rasterizer_cache/utils.h"
|
||||||
#include "video_core/texture/texture_decode.h"
|
|
||||||
|
|
||||||
namespace VideoCore {
|
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,
|
void EncodeTexture(const SurfaceParams& surface_info, PAddr start_addr, PAddr end_addr,
|
||||||
std::span<u8> source, std::span<u8> dest, bool convert) {
|
std::span<u8> source, std::span<u8> dest, bool convert) {
|
||||||
const PixelFormat format = surface_info.pixel_format;
|
const PixelFormat format = surface_info.pixel_format;
|
||||||
|
@ -81,18 +81,15 @@ struct BufferTextureCopy {
|
|||||||
u32 texture_level;
|
u32 texture_level;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class DecodeState : u32;
|
||||||
|
|
||||||
struct StagingData {
|
struct StagingData {
|
||||||
u32 size = 0;
|
u32 size = 0;
|
||||||
std::span<u8> mapped{};
|
std::span<u8> mapped{};
|
||||||
u64 buffer_offset = 0;
|
u64 buffer_offset = 0;
|
||||||
const std::atomic_flag* flag{};
|
const std::atomic<DecodeState>* flag{};
|
||||||
|
|
||||||
void Wait() const noexcept {
|
void Wait() const noexcept;
|
||||||
if (!flag) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
flag->wait(false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TextureCubeConfig {
|
struct TextureCubeConfig {
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <vulkan/vulkan_hash.hpp>
|
|
||||||
#include "video_core/rasterizer_cache/framebuffer_base.h"
|
#include "video_core/rasterizer_cache/framebuffer_base.h"
|
||||||
#include "video_core/rasterizer_cache/rasterizer_cache_base.h"
|
#include "video_core/rasterizer_cache/rasterizer_cache_base.h"
|
||||||
#include "video_core/rasterizer_cache/surface_base.h"
|
#include "video_core/rasterizer_cache/surface_base.h"
|
||||||
|
Reference in New Issue
Block a user