Merge pull request #12102 from iwubcode/cubemap_custom_texture
VideoCommon: add ability to load cube maps into custom texture data
This commit is contained in:
commit
900439ea0d
|
@ -15,9 +15,12 @@ namespace
|
|||
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;
|
||||
}
|
||||
|
@ -30,51 +33,58 @@ CustomAssetLibrary::LoadInfo CustomAssetLibrary::LoadGameTexture(const AssetID&
|
|||
return {};
|
||||
|
||||
// Note: 'LoadTexture()' ensures we have a level loaded
|
||||
const auto& first_mip = data->m_levels[0];
|
||||
|
||||
// Verify that each mip level is the correct size (divide by 2 each time).
|
||||
u32 current_mip_width = first_mip.width;
|
||||
u32 current_mip_height = first_mip.height;
|
||||
for (u32 mip_level = 1; mip_level < static_cast<u32>(data->m_levels.size()); mip_level++)
|
||||
for (std::size_t slice_index = 0; slice_index < data->m_slices.size(); slice_index++)
|
||||
{
|
||||
if (current_mip_width != 1 || current_mip_height != 1)
|
||||
{
|
||||
current_mip_width = std::max(current_mip_width / 2, 1u);
|
||||
current_mip_height = std::max(current_mip_height / 2, 1u);
|
||||
auto& slice = data->m_slices[slice_index];
|
||||
const auto& first_mip = slice.m_levels[0];
|
||||
|
||||
const VideoCommon::CustomTextureData::Level& level = data->m_levels[mip_level];
|
||||
if (current_mip_width == level.width && current_mip_height == level.height)
|
||||
continue;
|
||||
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Invalid custom game texture size {}x{} for texture asset {}. Mipmap level {} "
|
||||
"must be {}x{}.",
|
||||
level.width, level.height, asset_id, mip_level, current_mip_width,
|
||||
current_mip_height);
|
||||
}
|
||||
else
|
||||
// Verify that each mip level is the correct size (divide by 2 each time).
|
||||
u32 current_mip_width = first_mip.width;
|
||||
u32 current_mip_height = first_mip.height;
|
||||
for (u32 mip_level = 1; mip_level < static_cast<u32>(slice.m_levels.size()); mip_level++)
|
||||
{
|
||||
// It is invalid to have more than a single 1x1 mipmap.
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Custom game texture {} has too many 1x1 mipmaps. Skipping extra levels.",
|
||||
asset_id);
|
||||
if (current_mip_width != 1 || current_mip_height != 1)
|
||||
{
|
||||
current_mip_width = std::max(current_mip_width / 2, 1u);
|
||||
current_mip_height = std::max(current_mip_height / 2, 1u);
|
||||
|
||||
const VideoCommon::CustomTextureData::ArraySlice::Level& level = slice.m_levels[mip_level];
|
||||
if (current_mip_width == level.width && current_mip_height == level.height)
|
||||
continue;
|
||||
|
||||
ERROR_LOG_FMT(VIDEO,
|
||||
"Invalid custom game texture size {}x{} for texture asset {}. Slice {} with "
|
||||
"mipmap level {} "
|
||||
"must be {}x{}.",
|
||||
level.width, level.height, asset_id, slice_index, mip_level,
|
||||
current_mip_width, current_mip_height);
|
||||
}
|
||||
else
|
||||
{
|
||||
// It is invalid to have more than a single 1x1 mipmap.
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO,
|
||||
"Custom game texture {} has too many 1x1 mipmaps for slice {}. Skipping extra levels.",
|
||||
asset_id, slice_index);
|
||||
}
|
||||
|
||||
// Drop this mip level and any others after it.
|
||||
while (slice.m_levels.size() > mip_level)
|
||||
slice.m_levels.pop_back();
|
||||
}
|
||||
|
||||
// Drop this mip level and any others after it.
|
||||
while (data->m_levels.size() > mip_level)
|
||||
data->m_levels.pop_back();
|
||||
}
|
||||
// All levels have to have the same format.
|
||||
if (std::any_of(slice.m_levels.begin(), slice.m_levels.end(),
|
||||
[&first_mip](const VideoCommon::CustomTextureData::ArraySlice::Level& l) {
|
||||
return l.format != first_mip.format;
|
||||
}))
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
VIDEO, "Custom game texture {} has inconsistent formats across mip levels for slice {}.",
|
||||
asset_id, slice_index);
|
||||
|
||||
// All levels have to have the same format.
|
||||
if (std::any_of(data->m_levels.begin(), data->m_levels.end(),
|
||||
[&first_mip](const VideoCommon::CustomTextureData::Level& l) {
|
||||
return l.format != first_mip.format;
|
||||
}))
|
||||
{
|
||||
ERROR_LOG_FMT(VIDEO, "Custom game texture {} has inconsistent formats across mip levels.",
|
||||
asset_id);
|
||||
|
||||
return {};
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return load_info;
|
||||
|
|
|
@ -62,6 +62,19 @@ struct DDS_PIXELFORMAT
|
|||
#define DDS_PAL8A 0x00000021 // DDPF_PALETTEINDEXED8 | DDPF_ALPHAPIXELS
|
||||
#define DDS_BUMPDUDV 0x00080000 // DDPF_BUMPDUDV
|
||||
|
||||
#define DDS_CUBEMAP_POSITIVEX 0x00000600 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX
|
||||
#define DDS_CUBEMAP_NEGATIVEX 0x00000a00 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX
|
||||
#define DDS_CUBEMAP_POSITIVEY 0x00001200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY
|
||||
#define DDS_CUBEMAP_NEGATIVEY 0x00002200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY
|
||||
#define DDS_CUBEMAP_POSITIVEZ 0x00004200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ
|
||||
#define DDS_CUBEMAP_NEGATIVEZ 0x00008200 // DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ
|
||||
|
||||
#define DDS_CUBEMAP_ALLFACES \
|
||||
(DDS_CUBEMAP_POSITIVEX | DDS_CUBEMAP_NEGATIVEX | DDS_CUBEMAP_POSITIVEY | DDS_CUBEMAP_NEGATIVEY | \
|
||||
DDS_CUBEMAP_POSITIVEZ | DDS_CUBEMAP_NEGATIVEZ)
|
||||
|
||||
#define DDS_CUBEMAP 0x00000200 // DDSCAPS2_CUBEMAP
|
||||
|
||||
#ifndef MAKEFOURCC
|
||||
#define MAKEFOURCC(ch0, ch1, ch2, ch3) \
|
||||
((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) | ((uint32_t)(uint8_t)(ch2) << 16) | \
|
||||
|
@ -143,12 +156,13 @@ struct DDSLoadInfo
|
|||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
u32 mip_count = 0;
|
||||
u32 array_size = 0;
|
||||
AbstractTextureFormat format = AbstractTextureFormat::RGBA8;
|
||||
size_t first_mip_offset = 0;
|
||||
size_t first_mip_size = 0;
|
||||
u32 first_mip_row_length = 0;
|
||||
|
||||
std::function<void(VideoCommon::CustomTextureData::Level*)> conversion_function;
|
||||
std::function<void(VideoCommon::CustomTextureData::ArraySlice::Level*)> conversion_function;
|
||||
};
|
||||
|
||||
static constexpr u32 GetBlockCount(u32 extent, u32 block_size)
|
||||
|
@ -171,7 +185,7 @@ static u32 CalculateMipCount(u32 width, u32 height)
|
|||
return mip_count;
|
||||
}
|
||||
|
||||
static void ConvertTexture_X8B8G8R8(VideoCommon::CustomTextureData::Level* level)
|
||||
static void ConvertTexture_X8B8G8R8(VideoCommon::CustomTextureData::ArraySlice::Level* level)
|
||||
{
|
||||
u8* data_ptr = level->data.data();
|
||||
for (u32 row = 0; row < level->height; row++)
|
||||
|
@ -185,7 +199,7 @@ static void ConvertTexture_X8B8G8R8(VideoCommon::CustomTextureData::Level* level
|
|||
}
|
||||
}
|
||||
|
||||
static void ConvertTexture_A8R8G8B8(VideoCommon::CustomTextureData::Level* level)
|
||||
static void ConvertTexture_A8R8G8B8(VideoCommon::CustomTextureData::ArraySlice::Level* level)
|
||||
{
|
||||
u8* data_ptr = level->data.data();
|
||||
for (u32 row = 0; row < level->height; row++)
|
||||
|
@ -202,7 +216,7 @@ static void ConvertTexture_A8R8G8B8(VideoCommon::CustomTextureData::Level* level
|
|||
}
|
||||
}
|
||||
|
||||
static void ConvertTexture_X8R8G8B8(VideoCommon::CustomTextureData::Level* level)
|
||||
static void ConvertTexture_X8R8G8B8(VideoCommon::CustomTextureData::ArraySlice::Level* level)
|
||||
{
|
||||
u8* data_ptr = level->data.data();
|
||||
for (u32 row = 0; row < level->height; row++)
|
||||
|
@ -219,7 +233,7 @@ static void ConvertTexture_X8R8G8B8(VideoCommon::CustomTextureData::Level* level
|
|||
}
|
||||
}
|
||||
|
||||
static void ConvertTexture_R8G8B8(VideoCommon::CustomTextureData::Level* level)
|
||||
static void ConvertTexture_R8G8B8(VideoCommon::CustomTextureData::ArraySlice::Level* level)
|
||||
{
|
||||
std::vector<u8> new_data(level->row_length * level->height * sizeof(u32));
|
||||
|
||||
|
@ -297,13 +311,26 @@ static bool ParseDDSHeader(File::IOFile& file, DDSLoadInfo* info)
|
|||
if (!file.ReadBytes(&dxt10_header, sizeof(dxt10_header)))
|
||||
return false;
|
||||
|
||||
// Can't handle array textures here. Doesn't make sense to use them, anyway.
|
||||
if (dxt10_header.resourceDimension != DDS_DIMENSION_TEXTURE2D || dxt10_header.arraySize != 1)
|
||||
return false;
|
||||
|
||||
info->array_size = dxt10_header.arraySize;
|
||||
header_size += sizeof(dxt10_header);
|
||||
dxt10_format = dxt10_header.dxgiFormat;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (header.dwCaps2 & DDS_CUBEMAP)
|
||||
{
|
||||
if ((header.dwCaps2 & DDS_CUBEMAP_ALLFACES) != DDS_CUBEMAP_ALLFACES)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
info->array_size = 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
info->array_size = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Currently, we only handle compressed textures here, and leave the rest to the SOIL loader.
|
||||
// In the future, this could be extended, but these isn't much benefit in doing so currently.
|
||||
|
@ -369,6 +396,20 @@ static bool ParseDDSHeader(File::IOFile& file, DDSLoadInfo* info)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (header.dwCaps2 & DDS_CUBEMAP)
|
||||
{
|
||||
if ((header.dwCaps2 & DDS_CUBEMAP_ALLFACES) != DDS_CUBEMAP_ALLFACES)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
info->array_size = 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
info->array_size = 1;
|
||||
}
|
||||
|
||||
// All these formats are RGBA, just with byte swapping.
|
||||
info->format = AbstractTextureFormat::RGBA8;
|
||||
info->block_size = 1;
|
||||
|
@ -416,9 +457,10 @@ static bool ParseDDSHeader(File::IOFile& file, DDSLoadInfo* info)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool ReadMipLevel(VideoCommon::CustomTextureData::Level* level, File::IOFile& file,
|
||||
const std::string& filename, u32 mip_level, const DDSLoadInfo& info,
|
||||
u32 width, u32 height, u32 row_length, size_t size)
|
||||
static bool ReadMipLevel(VideoCommon::CustomTextureData::ArraySlice::Level* level,
|
||||
File::IOFile& file, const std::string& filename, u32 mip_level,
|
||||
const DDSLoadInfo& info, u32 width, u32 height, u32 row_length,
|
||||
size_t size)
|
||||
{
|
||||
// D3D11 cannot handle block compressed textures where the first mip level is
|
||||
// not a multiple of the block size.
|
||||
|
@ -463,43 +505,50 @@ bool LoadDDSTexture(CustomTextureData* texture, const std::string& filename)
|
|||
if (!ParseDDSHeader(file, &info))
|
||||
return false;
|
||||
|
||||
// Read first mip level, as it may have a custom pitch.
|
||||
CustomTextureData::Level first_level;
|
||||
if (!file.Seek(info.first_mip_offset, File::SeekOrigin::Begin) ||
|
||||
!ReadMipLevel(&first_level, file, filename, 0, info, info.width, info.height,
|
||||
info.first_mip_row_length, info.first_mip_size))
|
||||
{
|
||||
if (!file.Seek(info.first_mip_offset, File::SeekOrigin::Begin))
|
||||
return false;
|
||||
}
|
||||
|
||||
texture->m_levels.push_back(std::move(first_level));
|
||||
|
||||
// Read in any remaining mip levels in the file.
|
||||
// If the .dds file does not contain a full mip chain, we'll fall back to the old path.
|
||||
u32 mip_width = info.width;
|
||||
u32 mip_height = info.height;
|
||||
for (u32 i = 1; i < info.mip_count; i++)
|
||||
for (u32 arr_i = 0; arr_i < info.array_size; arr_i++)
|
||||
{
|
||||
mip_width = std::max(mip_width / 2, 1u);
|
||||
mip_height = std::max(mip_height / 2, 1u);
|
||||
auto& slice = texture->m_slices.emplace_back();
|
||||
// Read first mip level, as it may have a custom pitch.
|
||||
CustomTextureData::ArraySlice::Level first_level;
|
||||
if (!ReadMipLevel(&first_level, file, filename, 0, info, info.width, info.height,
|
||||
info.first_mip_row_length, info.first_mip_size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pitch can't be specified with each mip level, so we have to calculate it ourselves.
|
||||
u32 blocks_wide = GetBlockCount(mip_width, info.block_size);
|
||||
u32 blocks_high = GetBlockCount(mip_height, info.block_size);
|
||||
u32 mip_row_length = blocks_wide * info.block_size;
|
||||
size_t mip_size = blocks_wide * static_cast<size_t>(info.bytes_per_block) * blocks_high;
|
||||
CustomTextureData::Level level;
|
||||
if (!ReadMipLevel(&level, file, filename, i, info, mip_width, mip_height, mip_row_length,
|
||||
mip_size))
|
||||
break;
|
||||
slice.m_levels.push_back(std::move(first_level));
|
||||
|
||||
texture->m_levels.push_back(std::move(level));
|
||||
// Read in any remaining mip levels in the file.
|
||||
// If the .dds file does not contain a full mip chain, we'll fall back to the old path.
|
||||
u32 mip_width = info.width;
|
||||
u32 mip_height = info.height;
|
||||
for (u32 i = 1; i < info.mip_count; i++)
|
||||
{
|
||||
mip_width = std::max(mip_width / 2, 1u);
|
||||
mip_height = std::max(mip_height / 2, 1u);
|
||||
|
||||
// Pitch can't be specified with each mip level, so we have to calculate it ourselves.
|
||||
u32 blocks_wide = GetBlockCount(mip_width, info.block_size);
|
||||
u32 blocks_high = GetBlockCount(mip_height, info.block_size);
|
||||
u32 mip_row_length = blocks_wide * info.block_size;
|
||||
size_t mip_size = blocks_wide * static_cast<size_t>(info.bytes_per_block) * blocks_high;
|
||||
CustomTextureData::ArraySlice::Level level;
|
||||
if (!ReadMipLevel(&level, file, filename, i, info, mip_width, mip_height, mip_row_length,
|
||||
mip_size))
|
||||
break;
|
||||
|
||||
slice.m_levels.push_back(std::move(level));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadDDSTexture(CustomTextureData::Level* level, const std::string& filename, u32 mip_level)
|
||||
bool LoadDDSTexture(CustomTextureData::ArraySlice::Level* level, const std::string& filename,
|
||||
u32 mip_level)
|
||||
{
|
||||
// Only loading a single mip level.
|
||||
File::IOFile file;
|
||||
|
@ -515,7 +564,7 @@ bool LoadDDSTexture(CustomTextureData::Level* level, const std::string& filename
|
|||
info.first_mip_row_length, info.first_mip_size);
|
||||
}
|
||||
|
||||
bool LoadPNGTexture(CustomTextureData::Level* level, const std::string& filename)
|
||||
bool LoadPNGTexture(CustomTextureData::ArraySlice::Level* level, const std::string& filename)
|
||||
{
|
||||
if (!level) [[unlikely]]
|
||||
return false;
|
||||
|
|
|
@ -14,18 +14,23 @@ namespace VideoCommon
|
|||
class CustomTextureData
|
||||
{
|
||||
public:
|
||||
struct Level
|
||||
struct ArraySlice
|
||||
{
|
||||
std::vector<u8> data;
|
||||
AbstractTextureFormat format = AbstractTextureFormat::RGBA8;
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
u32 row_length = 0;
|
||||
struct Level
|
||||
{
|
||||
std::vector<u8> data;
|
||||
AbstractTextureFormat format = AbstractTextureFormat::RGBA8;
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
u32 row_length = 0;
|
||||
};
|
||||
std::vector<Level> m_levels;
|
||||
};
|
||||
std::vector<Level> m_levels;
|
||||
std::vector<ArraySlice> m_slices;
|
||||
};
|
||||
|
||||
bool LoadDDSTexture(CustomTextureData* texture, const std::string& filename);
|
||||
bool LoadDDSTexture(CustomTextureData::Level* level, const std::string& filename, u32 mip_level);
|
||||
bool LoadPNGTexture(CustomTextureData::Level* level, const std::string& filename);
|
||||
bool LoadDDSTexture(CustomTextureData::ArraySlice::Level* level, const std::string& filename,
|
||||
u32 mip_level);
|
||||
bool LoadPNGTexture(CustomTextureData::ArraySlice::Level* level, const std::string& filename);
|
||||
} // namespace VideoCommon
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 levels, create one to pass into LoadPNGTexture
|
||||
if (data->m_levels.empty())
|
||||
data->m_levels.push_back({});
|
||||
// If we have no slices, create one
|
||||
if (data->m_slices.empty())
|
||||
data->m_slices.push_back({});
|
||||
|
||||
if (!LoadPNGTexture(&data->m_levels[0], PathToString(asset_path)))
|
||||
auto& slice = data->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)))
|
||||
{
|
||||
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)};
|
||||
|
@ -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;
|
||||
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
@ -35,7 +34,7 @@ public:
|
|||
|
||||
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;
|
||||
|
|
|
@ -47,7 +47,7 @@ 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.",
|
||||
|
@ -55,9 +55,28 @@ bool GameTextureAsset::Validate(u32 native_width, u32 native_height) const
|
|||
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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue