Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge pull request #12167 from iwubcode/use_texture_data_for_texture_…
…assets

VideoCommon: use 'TextureData' for texture assets to support texture metadata
  • Loading branch information
AdmiralCurtiss committed Oct 10, 2023
2 parents 1b7a590 + 0e8f8ea commit a2f21da
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 91 deletions.
18 changes: 14 additions & 4 deletions Source/Core/VideoCommon/Assets/CustomAssetLibrary.cpp
Expand Up @@ -6,7 +6,7 @@
#include <algorithm>

#include "Common/Logging/Log.h"
#include "VideoCommon/Assets/CustomTextureData.h"
#include "VideoCommon/Assets/TextureAsset.h"

namespace VideoCommon
{
Expand All @@ -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 {};

if (data->m_type != TextureData::Type::Type_Texture2D)
{
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_slices.size(); slice_index++)
for (std::size_t slice_index = 0; slice_index < data->m_texture.m_slices.size(); slice_index++)
{
auto& slice = data->m_slices[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).
Expand Down
6 changes: 3 additions & 3 deletions Source/Core/VideoCommon/Assets/CustomAssetLibrary.h
Expand Up @@ -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
Expand All @@ -32,14 +32,14 @@ class CustomAssetLibrary
};

// 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;
Expand Down
7 changes: 0 additions & 7 deletions Source/Core/VideoCommon/Assets/CustomAssetLoader.cpp
Expand Up @@ -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)
Expand Down
4 changes: 0 additions & 4 deletions Source/Core/VideoCommon/Assets/CustomAssetLoader.h
Expand Up @@ -38,9 +38,6 @@ class CustomAssetLoader
// 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);

Expand Down Expand Up @@ -80,7 +77,6 @@ class CustomAssetLoader

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;
Expand Down
105 changes: 83 additions & 22 deletions Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp
Expand Up @@ -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
{
Expand Down Expand Up @@ -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;

std::error_code ec;
const auto last_loaded_time = std::filesystem::last_write_time(asset_path, ec);
if (ec)
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 '{}' error - failed to get last write time with error '{}'!",
asset_id, ec);
ERROR_LOG_FMT(VIDEO, "Asset '{}' expected to have a texture entry mapped!", asset_id);
return {};
}
auto ext = PathToString(asset_path.extension());

std::size_t metadata_size = 0;
if (metadata != asset_map.end())
{
std::error_code ec;
metadata_size = std::filesystem::file_size(metadata->second, ec);
if (ec)
{
ERROR_LOG_FMT(VIDEO,
"Asset '{}' error - failed to get texture metadata file size with error '{}'!",
asset_id, ec);
return {};
}

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")
{
// 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 {};
}

// If we have no slices, create one
if (data->m_slices.empty())
data->m_slices.push_back({});
if (data->m_texture.m_slices.empty())
data->m_texture.m_slices.push_back({});

auto& slice = data->m_slices[0];
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);
Expand Down
Expand Up @@ -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;

Expand Down
22 changes: 4 additions & 18 deletions Source/Core/VideoCommon/Assets/TextureAsset.cpp
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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 {};
Expand All @@ -184,15 +170,15 @@ 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.",
GetAssetId());
return false;
}

if (m_data->m_slices.size() > 1)
if (m_data->m_texture.m_slices.size() > 1)
{
ERROR_LOG_FMT(
VIDEO,
Expand All @@ -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(
Expand Down
22 changes: 11 additions & 11 deletions Source/Core/VideoCommon/Assets/TextureAsset.h
Expand Up @@ -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,
Expand All @@ -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;
Expand All @@ -49,3 +42,10 @@ class GameTextureAsset final : public CustomLoadableAsset<CustomTextureData>
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"}) {}
};

0 comments on commit a2f21da

Please sign in to comment.