VideoCommon: instead of using 'CustomTextureData' directly, use 'TextureData' for texture assets, this allows us to provide additional metadata for textures. Such as a sampler or type information (to distinguish cube maps)

This commit is contained in:
iwubcode 2023-09-06 00:16:26 -05:00
parent 1b7a590b4b
commit 0e8f8ea930
13 changed files with 138 additions and 93 deletions

View File

@ -6,7 +6,7 @@
#include <algorithm>
#include "Common/Logging/Log.h"
#include "VideoCommon/Assets/CustomTextureData.h"
#include "VideoCommon/Assets/TextureAsset.h"
namespace VideoCommon
{
@ -26,16 +26,26 @@ std::size_t GetAssetSize(const CustomTextureData& data)
}
} // namespace
CustomAssetLibrary::LoadInfo CustomAssetLibrary::LoadGameTexture(const AssetID& asset_id,
CustomTextureData* data)
TextureData* data)
{
const auto load_info = LoadTexture(asset_id, data);
if (load_info.m_bytes_loaded == 0)
return {};
// Note: 'LoadTexture()' ensures we have a level loaded
for (std::size_t slice_index = 0; slice_index < data->m_slices.size(); slice_index++)
if (data->m_type != TextureData::Type::Type_Texture2D)
{
auto& slice = data->m_slices[slice_index];
ERROR_LOG_FMT(
VIDEO,
"Custom asset '{}' is not a valid game texture, it is expected to be a 2d texture "
"but was a '{}'.",
asset_id, data->m_type);
return {};
}
// Note: 'LoadTexture()' ensures we have a level loaded
for (std::size_t slice_index = 0; slice_index < data->m_texture.m_slices.size(); slice_index++)
{
auto& slice = data->m_texture.m_slices[slice_index];
const auto& first_mip = slice.m_levels[0];
// Verify that each mip level is the correct size (divide by 2 each time).

View File

@ -10,9 +10,9 @@
namespace VideoCommon
{
class CustomTextureData;
struct MaterialData;
struct PixelShaderData;
struct TextureData;
// This class provides functionality to load
// specific data (like textures). Where this data
@ -32,14 +32,14 @@ public:
};
// Loads a texture, if there are no levels, bytes loaded will be empty
virtual LoadInfo LoadTexture(const AssetID& asset_id, CustomTextureData* data) = 0;
virtual LoadInfo LoadTexture(const AssetID& asset_id, TextureData* data) = 0;
// Gets the last write time for a given asset id
virtual TimeType GetLastAssetWriteTime(const AssetID& asset_id) const = 0;
// Loads a texture as a game texture, providing additional checks like confirming
// each mip level size is correct and that the format is consistent across the data
LoadInfo LoadGameTexture(const AssetID& asset_id, CustomTextureData* data);
LoadInfo LoadGameTexture(const AssetID& asset_id, TextureData* data);
// Loads a pixel shader
virtual LoadInfo LoadPixelShader(const AssetID& asset_id, PixelShaderData* data) = 0;

View File

@ -77,13 +77,6 @@ void CustomAssetLoader ::Shutdown()
m_total_bytes_loaded = 0;
}
std::shared_ptr<RawTextureAsset>
CustomAssetLoader::LoadTexture(const CustomAssetLibrary::AssetID& asset_id,
std::shared_ptr<CustomAssetLibrary> library)
{
return LoadOrCreateAsset<RawTextureAsset>(asset_id, m_textures, std::move(library));
}
std::shared_ptr<GameTextureAsset>
CustomAssetLoader::LoadGameTexture(const CustomAssetLibrary::AssetID& asset_id,
std::shared_ptr<CustomAssetLibrary> library)

View File

@ -38,9 +38,6 @@ public:
// Loads happen asynchronously where the data will be set now or in the future
// Callees are expected to query the underlying data with 'GetData()'
// from the 'CustomLoadableAsset' class to determine if the data is ready for use
std::shared_ptr<RawTextureAsset> LoadTexture(const CustomAssetLibrary::AssetID& asset_id,
std::shared_ptr<CustomAssetLibrary> library);
std::shared_ptr<GameTextureAsset> LoadGameTexture(const CustomAssetLibrary::AssetID& asset_id,
std::shared_ptr<CustomAssetLibrary> library);
@ -80,7 +77,6 @@ private:
static constexpr auto TIME_BETWEEN_ASSET_MONITOR_CHECKS = std::chrono::milliseconds{500};
std::map<CustomAssetLibrary::AssetID, std::weak_ptr<RawTextureAsset>> m_textures;
std::map<CustomAssetLibrary::AssetID, std::weak_ptr<GameTextureAsset>> m_game_textures;
std::map<CustomAssetLibrary::AssetID, std::weak_ptr<PixelShaderAsset>> m_pixel_shaders;
std::map<CustomAssetLibrary::AssetID, std::weak_ptr<MaterialAsset>> m_materials;

View File

@ -12,6 +12,7 @@
#include "Common/StringUtil.h"
#include "VideoCommon/Assets/MaterialAsset.h"
#include "VideoCommon/Assets/ShaderAsset.h"
#include "VideoCommon/Assets/TextureAsset.h"
namespace VideoCommon
{
@ -219,66 +220,126 @@ CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadMaterial(const As
}
CustomAssetLibrary::LoadInfo DirectFilesystemAssetLibrary::LoadTexture(const AssetID& asset_id,
CustomTextureData* data)
TextureData* data)
{
const auto asset_map = GetAssetMapForID(asset_id);
// Raw texture is expected to have one asset mapped
if (asset_map.empty() || asset_map.size() > 1)
// Texture can optionally have a metadata file as well
if (asset_map.empty() || asset_map.size() > 2)
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - raw texture expected to have one file mapped!",
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - raw texture expected to have one or two files mapped!",
asset_id);
return {};
}
const auto& asset_path = asset_map.begin()->second;
const auto metadata = asset_map.find("metadata");
const auto texture_path = asset_map.find("texture");
if (texture_path == asset_map.end())
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have a texture entry mapped!", asset_id);
return {};
}
std::size_t metadata_size = 0;
if (metadata != asset_map.end())
{
std::error_code ec;
const auto last_loaded_time = std::filesystem::last_write_time(asset_path, ec);
metadata_size = std::filesystem::file_size(metadata->second, ec);
if (ec)
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - failed to get last write time with error '{}'!",
ERROR_LOG_FMT(VIDEO,
"Asset '{}' error - failed to get texture metadata file size with error '{}'!",
asset_id, ec);
return {};
}
auto ext = PathToString(asset_path.extension());
std::string json_data;
if (!File::ReadFileToString(PathToString(metadata->second), json_data))
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - failed to load the json file '{}',", asset_id,
PathToString(metadata->second));
return {};
}
picojson::value root;
const auto error = picojson::parse(root, json_data);
if (!error.empty())
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' error - failed to load the json file '{}', due to parse error: {}",
asset_id, PathToString(metadata->second), error);
return {};
}
if (!root.is<picojson::object>())
{
ERROR_LOG_FMT(
VIDEO,
"Asset '{}' error - failed to load the json file '{}', due to root not being an object!",
asset_id, PathToString(metadata->second));
return {};
}
const auto& root_obj = root.get<picojson::object>();
if (!TextureData::FromJson(asset_id, root_obj, data))
{
return {};
}
}
else
{
data->m_type = TextureData::Type::Type_Texture2D;
}
auto ext = PathToString(texture_path->second.extension());
Common::ToLower(&ext);
if (ext == ".dds")
{
if (!LoadDDSTexture(data, PathToString(asset_path)))
if (!LoadDDSTexture(&data->m_texture, PathToString(texture_path->second)))
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - could not load dds texture!", asset_id);
return {};
}
if (data->m_slices.empty()) [[unlikely]]
data->m_slices.push_back({});
if (data->m_texture.m_slices.empty()) [[unlikely]]
data->m_texture.m_slices.push_back({});
if (!LoadMips(asset_path, &data->m_slices[0]))
if (!LoadMips(texture_path->second, &data->m_texture.m_slices[0]))
return {};
return LoadInfo{GetAssetSize(*data), FileTimeToSysTime(last_loaded_time)};
return LoadInfo{GetAssetSize(data->m_texture) + metadata_size, GetLastAssetWriteTime(asset_id)};
}
else if (ext == ".png")
{
// If we have no slices, create one
if (data->m_slices.empty())
data->m_slices.push_back({});
// PNG could support more complicated texture types in the future
// but for now just error
if (data->m_type != TextureData::Type::Type_Texture2D)
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - PNG is not supported for texture type '{}'!",
asset_id, data->m_type);
return {};
}
auto& slice = data->m_slices[0];
// If we have no slices, create one
if (data->m_texture.m_slices.empty())
data->m_texture.m_slices.push_back({});
auto& slice = data->m_texture.m_slices[0];
// If we have no levels, create one to pass into LoadPNGTexture
if (slice.m_levels.empty())
slice.m_levels.push_back({});
if (!LoadPNGTexture(&slice.m_levels[0], PathToString(asset_path)))
if (!LoadPNGTexture(&slice.m_levels[0], PathToString(texture_path->second)))
{
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - could not load png texture!", asset_id);
return {};
}
if (!LoadMips(asset_path, &slice))
if (!LoadMips(texture_path->second, &slice))
return {};
return LoadInfo{GetAssetSize(*data), FileTimeToSysTime(last_loaded_time)};
return LoadInfo{GetAssetSize(data->m_texture) + metadata_size, GetLastAssetWriteTime(asset_id)};
}
ERROR_LOG_FMT(VIDEO, "Asset '{}' error - extension '{}' unknown!", asset_id, ext);

View File

@ -20,7 +20,7 @@ class DirectFilesystemAssetLibrary final : public CustomAssetLibrary
public:
using AssetMap = std::map<std::string, std::filesystem::path>;
LoadInfo LoadTexture(const AssetID& asset_id, CustomTextureData* data) override;
LoadInfo LoadTexture(const AssetID& asset_id, TextureData* data) override;
LoadInfo LoadPixelShader(const AssetID& asset_id, PixelShaderData* data) override;
LoadInfo LoadMaterial(const AssetID& asset_id, MaterialData* data) override;

View File

@ -97,20 +97,6 @@ bool ParseSampler(const VideoCommon::CustomAssetLibrary::AssetID& asset_id,
return true;
}
} // namespace
CustomAssetLibrary::LoadInfo RawTextureAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id)
{
auto potential_data = std::make_shared<CustomTextureData>();
const auto loaded_info = m_owning_library->LoadTexture(asset_id, potential_data.get());
if (loaded_info.m_bytes_loaded == 0)
return {};
{
std::lock_guard lk(m_data_lock);
m_loaded = true;
m_data = std::move(potential_data);
}
return loaded_info;
}
bool TextureData::FromJson(const CustomAssetLibrary::AssetID& asset_id,
const picojson::object& json, TextureData* data)
{
@ -160,7 +146,7 @@ bool TextureData::FromJson(const CustomAssetLibrary::AssetID& asset_id,
CustomAssetLibrary::LoadInfo GameTextureAsset::LoadImpl(const CustomAssetLibrary::AssetID& asset_id)
{
auto potential_data = std::make_shared<CustomTextureData>();
auto potential_data = std::make_shared<TextureData>();
const auto loaded_info = m_owning_library->LoadGameTexture(asset_id, potential_data.get());
if (loaded_info.m_bytes_loaded == 0)
return {};
@ -184,7 +170,7 @@ bool GameTextureAsset::Validate(u32 native_width, u32 native_height) const
return false;
}
if (m_data->m_slices.empty())
if (m_data->m_texture.m_slices.empty())
{
ERROR_LOG_FMT(VIDEO,
"Game texture can't be validated for asset '{}' because no data was available.",
@ -192,7 +178,7 @@ bool GameTextureAsset::Validate(u32 native_width, u32 native_height) const
return false;
}
if (m_data->m_slices.size() > 1)
if (m_data->m_texture.m_slices.size() > 1)
{
ERROR_LOG_FMT(
VIDEO,
@ -201,7 +187,7 @@ bool GameTextureAsset::Validate(u32 native_width, u32 native_height) const
return false;
}
const auto& slice = m_data->m_slices[0];
const auto& slice = m_data->m_texture.m_slices[0];
if (slice.m_levels.empty())
{
ERROR_LOG_FMT(

View File

@ -3,23 +3,16 @@
#pragma once
#include <fmt/format.h>
#include <picojson.h>
#include "Common/EnumFormatter.h"
#include "VideoCommon/Assets/CustomAsset.h"
#include "VideoCommon/Assets/CustomTextureData.h"
#include "VideoCommon/RenderState.h"
namespace VideoCommon
{
class RawTextureAsset final : public CustomLoadableAsset<CustomTextureData>
{
public:
using CustomLoadableAsset::CustomLoadableAsset;
private:
CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) override;
};
struct TextureData
{
static bool FromJson(const CustomAssetLibrary::AssetID& asset_id, const picojson::object& json,
@ -32,11 +25,11 @@ struct TextureData
Type_Max = Type_TextureCube
};
Type m_type;
CustomTextureData m_data;
CustomTextureData m_texture;
SamplerState m_sampler;
};
class GameTextureAsset final : public CustomLoadableAsset<CustomTextureData>
class GameTextureAsset final : public CustomLoadableAsset<TextureData>
{
public:
using CustomLoadableAsset::CustomLoadableAsset;
@ -49,3 +42,10 @@ private:
CustomAssetLibrary::LoadInfo LoadImpl(const CustomAssetLibrary::AssetID& asset_id) override;
};
} // namespace VideoCommon
template <>
struct fmt::formatter<VideoCommon::TextureData::Type>
: EnumFormatter<VideoCommon::TextureData::Type::Type_Max>
{
constexpr formatter() : EnumFormatter({"Undefined", "Texture2D", "TextureCube"}) {}
};

View File

@ -423,7 +423,7 @@ void CustomPipelineAction::OnTextureCreate(GraphicsModActionData::TextureCreate*
auto data = game_texture.m_asset->GetData();
if (data)
{
if (data->m_slices.empty() || data->m_slices[0].m_levels.empty())
if (data->m_texture.m_slices.empty() || data->m_texture.m_slices[0].m_levels.empty())
{
ERROR_LOG_FMT(
VIDEO,
@ -431,15 +431,16 @@ void CustomPipelineAction::OnTextureCreate(GraphicsModActionData::TextureCreate*
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)
else if (create->texture_width != data->m_texture.m_slices[0].m_levels[0].width ||
create->texture_height != data->m_texture.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_slices[0].m_levels[0].width, data->m_slices[0].m_levels[0].height);
data->m_texture.m_slices[0].m_levels[0].width,
data->m_texture.m_slices[0].m_levels[0].height);
m_valid = false;
}
}

View File

@ -130,8 +130,8 @@ void HiresTexture::Update()
{
// Since this is just a texture (single file) the mapper doesn't really matter
// just provide a string
s_file_library->SetAssetIDMapData(
filename, std::map<std::string, std::filesystem::path>{{"", StringToPath(path)}});
s_file_library->SetAssetIDMapData(filename, std::map<std::string, std::filesystem::path>{
{"texture", StringToPath(path)}});
if (g_ActiveConfig.bCacheHiresTextures)
{

View File

@ -1606,7 +1606,7 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
}
std::vector<VideoCommon::CachedAsset<VideoCommon::GameTextureAsset>> cached_game_assets;
std::vector<std::shared_ptr<VideoCommon::CustomTextureData>> data_for_assets;
std::vector<VideoCommon::CustomTextureData*> data_for_assets;
bool has_arbitrary_mipmaps = false;
bool skip_texture_dump = false;
std::shared_ptr<HiresTexture> hires_texture;
@ -1640,12 +1640,12 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
auto data = asset->GetData();
if (data)
{
if (!data->m_slices.empty())
if (!data->m_texture.m_slices.empty())
{
if (!data->m_slices[0].m_levels.empty())
if (!data->m_texture.m_slices[0].m_levels.empty())
{
height = data->m_slices[0].m_levels[0].height;
width = data->m_slices[0].m_levels[0].width;
height = data->m_texture.m_slices[0].m_levels[0].height;
width = data->m_texture.m_slices[0].m_levels[0].width;
}
}
}
@ -1667,7 +1667,7 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
{
if (cached_asset.m_asset->Validate(texture_info.GetRawWidth(), texture_info.GetRawHeight()))
{
data_for_assets.push_back(std::move(data));
data_for_assets.push_back(&data->m_texture);
}
}
}
@ -1687,8 +1687,7 @@ RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSamp
// 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,
std::vector<std::shared_ptr<VideoCommon::CustomTextureData>> assets_data,
const int safety_color_sample_size, std::vector<VideoCommon::CustomTextureData*> assets_data,
const bool custom_arbitrary_mipmaps, bool skip_texture_dump)
{
#ifdef __APPLE__
@ -1705,7 +1704,7 @@ RcTcacheEntry TextureCacheBase::CreateTextureEntry(
assets_data.begin(), assets_data.end(), [](const auto& lhs, const auto& rhs) {
return lhs->m_slices[0].m_levels.size() < rhs->m_slices[0].m_levels.size();
});
return max_element->get()->m_slices[0].m_levels.size();
return (*max_element)->m_slices[0].m_levels.size();
};
const u32 texLevels = no_mips ? 1 : (u32)calculate_max_levels();
const auto& first_level = assets_data[0]->m_slices[0].m_levels[0];

View File

@ -346,10 +346,9 @@ private:
void SetBackupConfig(const VideoConfig& config);
RcTcacheEntry
CreateTextureEntry(const TextureCreationInfo& creation_info, const TextureInfo& texture_info,
int safety_color_sample_size,
std::vector<std::shared_ptr<VideoCommon::CustomTextureData>> assets_data,
RcTcacheEntry CreateTextureEntry(const TextureCreationInfo& creation_info,
const TextureInfo& texture_info, int safety_color_sample_size,
std::vector<VideoCommon::CustomTextureData*> assets_data,
bool custom_arbitrary_mipmaps, bool skip_texture_dump);
RcTcacheEntry GetXFBFromCache(u32 address, u32 width, u32 height, u32 stride);

View File

@ -44,7 +44,7 @@ A full example is given below:
"name": "normal_texture",
"data":
{
"": "normal_texture.png"
"texture": "normal_texture.png"
}
}
],