diff --git a/Source/Core/VideoCommon/Assets/CustomAssetLibrary.cpp b/Source/Core/VideoCommon/Assets/CustomAssetLibrary.cpp index 025da4559b..8a78fed490 100644 --- a/Source/Core/VideoCommon/Assets/CustomAssetLibrary.cpp +++ b/Source/Core/VideoCommon/Assets/CustomAssetLibrary.cpp @@ -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(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(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; diff --git a/Source/Core/VideoCommon/Assets/CustomTextureData.cpp b/Source/Core/VideoCommon/Assets/CustomTextureData.cpp index 04183da3f0..23af429d11 100644 --- a/Source/Core/VideoCommon/Assets/CustomTextureData.cpp +++ b/Source/Core/VideoCommon/Assets/CustomTextureData.cpp @@ -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 conversion_function; + std::function 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 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(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(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; diff --git a/Source/Core/VideoCommon/Assets/CustomTextureData.h b/Source/Core/VideoCommon/Assets/CustomTextureData.h index b7fd6f379e..fe15c05eaa 100644 --- a/Source/Core/VideoCommon/Assets/CustomTextureData.h +++ b/Source/Core/VideoCommon/Assets/CustomTextureData.h @@ -14,18 +14,23 @@ namespace VideoCommon class CustomTextureData { public: - struct Level + struct ArraySlice { - std::vector data; - AbstractTextureFormat format = AbstractTextureFormat::RGBA8; - u32 width = 0; - u32 height = 0; - u32 row_length = 0; + struct Level + { + std::vector data; + AbstractTextureFormat format = AbstractTextureFormat::RGBA8; + u32 width = 0; + u32 height = 0; + u32 row_length = 0; + }; + std::vector m_levels; }; - std::vector m_levels; + std::vector 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 diff --git a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp index 7a1f84ca08..99c3e9487e 100644 --- a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp +++ b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.cpp @@ -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)) diff --git a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h index eeedb42d68..21c5ce7d54 100644 --- a/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h +++ b/Source/Core/VideoCommon/Assets/DirectFilesystemAssetLibrary.h @@ -9,11 +9,10 @@ #include #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 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; diff --git a/Source/Core/VideoCommon/Assets/TextureAsset.cpp b/Source/Core/VideoCommon/Assets/TextureAsset.cpp index 3bf477a58a..f7f3a00849 100644 --- a/Source/Core/VideoCommon/Assets/TextureAsset.cpp +++ b/Source/Core/VideoCommon/Assets/TextureAsset.cpp @@ -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 diff --git a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp index b774324a58..4d8df6a218 100644 --- a/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp +++ b/Source/Core/VideoCommon/GraphicsModSystem/Runtime/Actions/CustomPipelineAction.cpp @@ -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; } } diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 3df1d7f51c..fa677bf1b5 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -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(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(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(asset->m_levels.size())); + level_index < std::min(texLevels, static_cast(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); }