30 changes: 20 additions & 10 deletions Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp
Expand Up @@ -9,7 +9,6 @@
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/StringUtil.h"
#include "VideoCommon/Assets/CustomTextureData.h"
#include "VideoCommon/Assets/MaterialAsset.h"
#include "VideoCommon/Assets/ShaderAsset.h"

Expand All @@ -34,9 +33,12 @@ std::chrono::system_clock::time_point FileTimeToSysTime(std::filesystem::file_ti
std::size_t GetAssetSize(const CustomTextureData& data)
{
std::size_t total = 0;
for (const auto& level : data.m_levels)
for (const auto& slice : data.m_slices)
{
total += level.data.size();
for (const auto& level : slice.m_levels)
{
total += level.data.size();
}
}
return total;
}
Expand Down Expand Up @@ -247,24 +249,32 @@ CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadTexture(const Ass
return {};
}

if (!LoadMips(asset_path, data))
if (data->m_slices.empty()) [[unlikely]]
data->m_slices.push_back({});

if (!LoadMips(asset_path, &data->m_slices[0]))
return {};

return LoadInfo{GetAssetSize(*data), FileTimeToSysTime(last_loaded_time)};
}
else if (ext == ".png")
{
// If we have no slices, create one
if (data->m_slices.empty())
data->m_slices.push_back({});

auto& slice = data->m_slices[0];
// If we have no levels, create one to pass into LoadPNGTexture
if (data->m_levels.empty())
data->m_levels.push_back({});
if (slice.m_levels.empty())
slice.m_levels.push_back({});

if (!LoadPNGTexture(&data->m_levels[0], PathToString(asset_path)))
if (!LoadPNGTexture(&slice.m_levels[0], PathToString(asset_path)))
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - could not load png texture!", asset_id);
return {};
}

if (!LoadMips(asset_path, data))
if (!LoadMips(asset_path, &slice))
return {};

return LoadInfo{GetAssetSize(*data), FileTimeToSysTime(last_loaded_time)};
Expand All @@ -282,7 +292,7 @@ void DirectFilesystemAssetLibrary::SetAssetIDMapData(const AssetID& asset_id,
}

bool DirectFilesystemAssetLibrary::LoadMips(const std::filesystem::path& asset_path,
CustomTextureData* data)
CustomTextureData::ArraySlice* data)
{
if (!data) [[unlikely]]
return false;
Expand All @@ -304,7 +314,7 @@ bool DirectFilesystemAssetLibrary::LoadMips(const std::filesystem::path& asset_p
if (!File::Exists(full_path))
return true;

VideoCommon::CustomTextureData::Level level;
VideoCommon::CustomTextureData::ArraySlice::Level level;
if (extension_lower == ".dds")
{
if (!LoadDDSTexture(&level, full_path, mip_level))
Expand Down
5 changes: 2 additions & 3 deletions Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h
Expand Up @@ -9,11 +9,10 @@
#include <string>

#include "VideoCommon/Assets/CustomAssetLibrary.h"
#include "VideoCommon/Assets/CustomTextureData.h"

namespace VideoCommon
{
class CustomTextureData;

// This class implements 'CustomAssetLibrary' and loads any assets
// directly from the filesystem
class DirectFilesystemAssetLibrary final : public CustomAssetLibrary
Expand All @@ -35,7 +34,7 @@ class DirectFilesystemAssetLibrary final : public CustomAssetLibrary

private:
// Loads additional mip levels into the texture structure until _mip<N> texture is not found
bool LoadMips(const std::filesystem::path& asset_path, CustomTextureData* data);
bool LoadMips(const std::filesystem::path& asset_path, CustomTextureData::ArraySlice* data);

// Gets the asset map given an asset id
AssetMap GetAssetMapForID(const AssetID& asset_id) const;
Expand Down
23 changes: 21 additions & 2 deletions Source/Core/VideoCommon/Assets/TextureAsset.cpp
Expand Up @@ -47,17 +47,36 @@ bool GameTextureAsset::Validate(u32 native_width, u32 native_height) const
return false;
}

if (m_data->m_levels.empty())
if (m_data->m_slices.empty())
{
ERROR_LOG_FMT(VIDEO,
"Game texture can't be validated for asset '{}' because no data was available.",
GetAssetId());
return false;
}

if (m_data->m_slices.size() > 1)
{
ERROR_LOG_FMT(
VIDEO,
"Game texture can't be validated for asset '{}' because it has more slices than expected.",
GetAssetId());
return false;
}

const auto& slice = m_data->m_slices[0];
if (slice.m_levels.empty())
{
ERROR_LOG_FMT(
VIDEO,
"Game texture can't be validated for asset '{}' because first slice has no data available.",
GetAssetId());
return false;
}

// Verify that the aspect ratio of the texture hasn't changed, as this could have
// side-effects.
const VideoCommon::CustomTextureData::Level& first_mip = m_data->m_levels[0];
const VideoCommon::CustomTextureData::ArraySlice::Level& first_mip = slice.m_levels[0];
if (first_mip.width * native_height != first_mip.height * native_width)
{
// Note: this feels like this should return an error but
Expand Down
Expand Up @@ -423,15 +423,23 @@ void CustomPipelineAction::OnTextureCreate(GraphicsModActionData::TextureCreate*
auto data = game_texture.m_asset->GetData();
if (data)
{
if (create->texture_width != data->m_levels[0].width ||
create->texture_height != data->m_levels[0].height)
if (data->m_slices.empty() || data->m_slices[0].m_levels.empty())
{
ERROR_LOG_FMT(
VIDEO,
"Custom pipeline for texture '{}' has asset '{}' that does not have any texture data",
create->texture_name, game_texture.m_asset->GetAssetId());
m_valid = false;
}
else if (create->texture_width != data->m_slices[0].m_levels[0].width ||
create->texture_height != data->m_slices[0].m_levels[0].height)
{
ERROR_LOG_FMT(VIDEO,
"Custom pipeline for texture '{}' has asset '{}' that does not match "
"the width/height of the texture loaded. Texture {}x{} vs asset {}x{}",
create->texture_name, game_texture.m_asset->GetAssetId(),
create->texture_width, create->texture_height, data->m_levels[0].width,
data->m_levels[0].height);
create->texture_width, create->texture_height,
data->m_slices[0].m_levels[0].width, data->m_slices[0].m_levels[0].height);
m_valid = false;
}
}
Expand Down
23 changes: 15 additions & 8 deletions Source/Core/VideoCommon/TextureCacheBase.cpp
Expand Up @@ -1640,10 +1640,13 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
auto data = asset->GetData();
if (data)
{
if (!data->m_levels.empty())
if (!data->m_slices.empty())
{
height = data->m_levels[0].height;
width = data->m_levels[0].width;
if (!data->m_slices[0].m_levels.empty())
{
height = data->m_slices[0].m_levels[0].height;
width = data->m_slices[0].m_levels[0].width;
}
}
}
}
Expand Down Expand Up @@ -1679,6 +1682,9 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
return entry;
}

// Note: the following function assumes all CustomTextureData has a single slice. This is verified
// with the 'GameTexture::Validate' function after the data is loaded. Only a single slice is
// expected because each texture is loaded into a texture array
RcTcacheEntry TextureCacheBase::CreateTextureEntry(
const TextureCreationInfo& creation_info, const TextureInfo& texture_info,
const int safety_color_sample_size,
Expand All @@ -1697,12 +1703,12 @@ RcTcacheEntry TextureCacheBase::CreateTextureEntry(
const auto calculate_max_levels = [&]() {
const auto max_element = std::max_element(
assets_data.begin(), assets_data.end(), [](const auto& lhs, const auto& rhs) {
return lhs->m_levels.size() < rhs->m_levels.size();
return lhs->m_slices[0].m_levels.size() < rhs->m_slices[0].m_levels.size();
});
return max_element->get()->m_levels.size();
return max_element->get()->m_slices[0].m_levels.size();
};
const u32 texLevels = no_mips ? 1 : (u32)calculate_max_levels();
const auto& first_level = assets_data[0]->m_levels[0];
const auto& first_level = assets_data[0]->m_slices[0].m_levels[0];
const TextureConfig config(first_level.width, first_level.height, texLevels,
static_cast<u32>(assets_data.size()), 1, first_level.format, 0);
entry = AllocateCacheEntry(config);
Expand All @@ -1711,11 +1717,12 @@ RcTcacheEntry TextureCacheBase::CreateTextureEntry(
for (u32 data_index = 0; data_index < static_cast<u32>(assets_data.size()); data_index++)
{
const auto asset = assets_data[data_index];
const auto& slice = asset->m_slices[0];
for (u32 level_index = 0;
level_index < std::min(texLevels, static_cast<u32>(asset->m_levels.size()));
level_index < std::min(texLevels, static_cast<u32>(slice.m_levels.size()));
++level_index)
{
const auto& level = asset->m_levels[level_index];
const auto& level = slice.m_levels[level_index];
entry->texture->Load(level_index, level.width, level.height, level.row_length,
level.data.data(), level.data.size(), data_index);
}
Expand Down