diff options
| author | shuffle2 <godisgovernment@gmail.com> | 2017-06-18 12:57:05 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-06-18 12:57:05 -0700 |
| commit | e63c33783049753784a24675bfa0792e54684fbd (patch) | |
| tree | 3d984535025f4cb97489c9665220ac4237e7761c /Source/Core/VideoCommon | |
| parent | 09457d3a4245ec9d4f2fdac3d1d0873a3354b858 (diff) | |
| parent | e4896d39bd927b0b8e109e0334fa15cdb8e004f3 (diff) | |
Merge pull request #5305 from iwubcode/abstract_texture
Abstract Texture
Diffstat (limited to 'Source/Core/VideoCommon')
| -rw-r--r-- | Source/Core/VideoCommon/AbstractTexture.cpp | 44 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/AbstractTexture.h | 33 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/HiresTextures.cpp | 4 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/HiresTextures.h | 6 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/HiresTextures_DDSLoader.cpp | 10 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/TextureCacheBase.cpp | 214 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/TextureCacheBase.h | 133 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/TextureConfig.cpp | 18 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/TextureConfig.h | 43 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/VideoCommon.h | 9 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/VideoCommon.vcxproj | 4 | ||||
| -rw-r--r-- | Source/Core/VideoCommon/VideoCommon.vcxproj.filters | 12 |
13 files changed, 309 insertions, 223 deletions
diff --git a/Source/Core/VideoCommon/AbstractTexture.cpp b/Source/Core/VideoCommon/AbstractTexture.cpp new file mode 100644 index 0000000000..1b6fac8bff --- /dev/null +++ b/Source/Core/VideoCommon/AbstractTexture.cpp @@ -0,0 +1,44 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include <algorithm> + +#include "VideoCommon/AbstractTexture.h" + +AbstractTexture::AbstractTexture(const TextureConfig& c) : m_config(c) +{ +} + +AbstractTexture::~AbstractTexture() = default; + +bool AbstractTexture::Save(const std::string& filename, unsigned int level) +{ + return false; +} + +bool AbstractTexture::IsCompressedHostTextureFormat(AbstractTextureFormat format) +{ + // This will need to be changed if we add any other uncompressed formats. + return format != AbstractTextureFormat::RGBA8; +} + +size_t AbstractTexture::CalculateHostTextureLevelPitch(AbstractTextureFormat format, u32 row_length) +{ + switch (format) + { + case AbstractTextureFormat::DXT1: + return static_cast<size_t>(std::max(1u, row_length / 4)) * 8; + case AbstractTextureFormat::DXT3: + case AbstractTextureFormat::DXT5: + return static_cast<size_t>(std::max(1u, row_length / 4)) * 16; + case AbstractTextureFormat::RGBA8: + default: + return static_cast<size_t>(row_length) * 4; + } +} + +const TextureConfig AbstractTexture::GetConfig() const +{ + return m_config; +} diff --git a/Source/Core/VideoCommon/AbstractTexture.h b/Source/Core/VideoCommon/AbstractTexture.h new file mode 100644 index 0000000000..2b06d9c401 --- /dev/null +++ b/Source/Core/VideoCommon/AbstractTexture.h @@ -0,0 +1,33 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/CommonTypes.h" +#include "Common/MathUtil.h" +#include "VideoCommon/TextureConfig.h" +#include "VideoCommon/VideoCommon.h" + +class AbstractTexture +{ +public: + explicit AbstractTexture(const TextureConfig& c); + virtual ~AbstractTexture(); + virtual void Bind(unsigned int stage) = 0; + virtual bool Save(const std::string& filename, unsigned int level); + + virtual void CopyRectangleFromTexture(const AbstractTexture* source, + const MathUtil::Rectangle<int>& srcrect, + const MathUtil::Rectangle<int>& dstrect) = 0; + virtual void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, + size_t buffer_size) = 0; + + static bool IsCompressedHostTextureFormat(AbstractTextureFormat format); + static size_t CalculateHostTextureLevelPitch(AbstractTextureFormat format, u32 row_length); + + const TextureConfig GetConfig() const; + +protected: + const TextureConfig m_config; +}; diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index a50cd2774a..9eeb9f93bb 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -1,4 +1,5 @@ set(SRCS + AbstractTexture.cpp AsyncRequests.cpp BoundingBox.cpp BPFunctions.cpp @@ -30,6 +31,7 @@ set(SRCS RenderState.cpp Statistics.cpp TextureCacheBase.cpp + TextureConfig.cpp TextureConversionShader.cpp TextureDecoder_Common.cpp VertexLoader.cpp diff --git a/Source/Core/VideoCommon/HiresTextures.cpp b/Source/Core/VideoCommon/HiresTextures.cpp index 95a70efc59..7b4a1d387c 100644 --- a/Source/Core/VideoCommon/HiresTextures.cpp +++ b/Source/Core/VideoCommon/HiresTextures.cpp @@ -533,7 +533,7 @@ bool HiresTexture::LoadTexture(Level& level, const std::vector<u8>& buffer) // Images loaded by SOIL are converted to RGBA. level.width = static_cast<u32>(width); level.height = static_cast<u32>(height); - level.format = HostTextureFormat::RGBA8; + level.format = AbstractTextureFormat::RGBA8; level.data = ImageDataPointer(data, SOIL_free_image_data); level.row_length = level.width; level.data_size = static_cast<size_t>(level.row_length) * 4 * level.height; @@ -555,7 +555,7 @@ HiresTexture::~HiresTexture() { } -HostTextureFormat HiresTexture::GetFormat() const +AbstractTextureFormat HiresTexture::GetFormat() const { return m_levels.at(0).format; } diff --git a/Source/Core/VideoCommon/HiresTextures.h b/Source/Core/VideoCommon/HiresTextures.h index 30bb96d40c..3e7625c993 100644 --- a/Source/Core/VideoCommon/HiresTextures.h +++ b/Source/Core/VideoCommon/HiresTextures.h @@ -9,7 +9,7 @@ #include <vector> #include "Common/CommonTypes.h" -#include "VideoCommon/VideoCommon.h" +#include "VideoCommon/TextureConfig.h" class HiresTexture { @@ -32,13 +32,13 @@ public: ~HiresTexture(); - HostTextureFormat GetFormat() const; + AbstractTextureFormat GetFormat() const; struct Level { Level(); ImageDataPointer data; - HostTextureFormat format = HostTextureFormat::RGBA8; + AbstractTextureFormat format = AbstractTextureFormat::RGBA8; u32 width = 0; u32 height = 0; u32 row_length = 0; diff --git a/Source/Core/VideoCommon/HiresTextures_DDSLoader.cpp b/Source/Core/VideoCommon/HiresTextures_DDSLoader.cpp index 27af3b6828..5b9bbeced9 100644 --- a/Source/Core/VideoCommon/HiresTextures_DDSLoader.cpp +++ b/Source/Core/VideoCommon/HiresTextures_DDSLoader.cpp @@ -142,7 +142,7 @@ struct DDSLoadInfo u32 width = 0; u32 height = 0; u32 mip_count = 0; - HostTextureFormat format = HostTextureFormat::RGBA8; + AbstractTextureFormat format = AbstractTextureFormat::RGBA8; size_t first_mip_offset = 0; size_t first_mip_size = 0; u32 first_mip_row_length = 0; @@ -300,21 +300,21 @@ bool ParseDDSHeader(File::IOFile& file, DDSLoadInfo* info) // In the future, this could be extended, but these isn't much benefit in doing so currently. if (header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '1') || dxt10_format == 71) { - info->format = HostTextureFormat::DXT1; + info->format = AbstractTextureFormat::DXT1; info->block_size = 4; info->bytes_per_block = 8; needs_s3tc = true; } else if (header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '3') || dxt10_format == 74) { - info->format = HostTextureFormat::DXT3; + info->format = AbstractTextureFormat::DXT3; info->block_size = 4; info->bytes_per_block = 16; needs_s3tc = true; } else if (header.ddspf.dwFourCC == MAKEFOURCC('D', 'X', 'T', '5') || dxt10_format == 77) { - info->format = HostTextureFormat::DXT5; + info->format = AbstractTextureFormat::DXT5; info->block_size = 4; info->bytes_per_block = 16; needs_s3tc = true; @@ -353,7 +353,7 @@ bool ParseDDSHeader(File::IOFile& file, DDSLoadInfo* info) } // All these formats are RGBA, just with byte swapping. - info->format = HostTextureFormat::RGBA8; + info->format = AbstractTextureFormat::RGBA8; info->block_size = 1; info->bytes_per_block = header.ddspf.dwRGBBitCount / 8; } diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 72978f6752..c61fa1fe5e 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -39,35 +39,18 @@ static const u64 TEXHASH_INVALID = 0; // Sonic the Fighters (inside Sonic Gems Collection) loops a 64 frames animation static const int TEXTURE_KILL_THRESHOLD = 64; static const int TEXTURE_POOL_KILL_THRESHOLD = 3; -static const int FRAMECOUNT_INVALID = 0; std::unique_ptr<TextureCacheBase> g_texture_cache; -TextureCacheBase::TCacheEntryBase::~TCacheEntryBase() +TextureCacheBase::TCacheEntry::TCacheEntry(std::unique_ptr<AbstractTexture> tex) + : texture(std::move(tex)) { } -bool TextureCacheBase::IsCompressedHostTextureFormat(HostTextureFormat format) +TextureCacheBase::TCacheEntry::~TCacheEntry() { - // This will need to be changed if we add any other uncompressed formats. - return format != HostTextureFormat::RGBA8; -} - -size_t TextureCacheBase::CalculateHostTextureLevelPitch(HostTextureFormat format, u32 row_length) -{ - switch (format) - { - case HostTextureFormat::DXT1: - return static_cast<size_t>(std::max(1u, row_length / 4)) * 8; - - case HostTextureFormat::DXT3: - case HostTextureFormat::DXT5: - return static_cast<size_t>(std::max(1u, row_length / 4)) * 16; - - case HostTextureFormat::RGBA8: - default: - return static_cast<size_t>(row_length) * 4; - } + for (auto& reference : references) + reference->references.erase(this); } void TextureCacheBase::CheckTempSize(size_t required_size) @@ -106,10 +89,6 @@ void TextureCacheBase::Invalidate() textures_by_address.clear(); textures_by_hash.clear(); - for (auto& rt : texture_pool) - { - delete rt.second; - } texture_pool.clear(); } @@ -197,13 +176,12 @@ void TextureCacheBase::Cleanup(int _frameCount) TexPool::iterator tcend2 = texture_pool.end(); while (iter2 != tcend2) { - if (iter2->second->frameCount == FRAMECOUNT_INVALID) + if (iter2->second.frameCount == FRAMECOUNT_INVALID) { - iter2->second->frameCount = _frameCount; + iter2->second.frameCount = _frameCount; } - if (_frameCount > TEXTURE_POOL_KILL_THRESHOLD + iter2->second->frameCount) + if (_frameCount > TEXTURE_POOL_KILL_THRESHOLD + iter2->second.frameCount) { - delete iter2->second; iter2 = texture_pool.erase(iter2); } else @@ -213,7 +191,7 @@ void TextureCacheBase::Cleanup(int _frameCount) } } -bool TextureCacheBase::TCacheEntryBase::OverlapsMemoryRange(u32 range_address, u32 range_size) const +bool TextureCacheBase::TCacheEntry::OverlapsMemoryRange(u32 range_address, u32 range_size) const { if (addr + size_in_bytes <= range_address) return false; @@ -236,14 +214,14 @@ void TextureCacheBase::SetBackupConfig(const VideoConfig& config) backup_config.gpu_texture_decoding = config.bEnableGPUTextureDecoding; } -TextureCacheBase::TCacheEntryBase* TextureCacheBase::ApplyPaletteToEntry(TCacheEntryBase* entry, - u8* palette, u32 tlutfmt) +TextureCacheBase::TCacheEntry* TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, + u8* palette, u32 tlutfmt) { - TCacheEntryConfig new_config = entry->config; + TextureConfig new_config = entry->texture->GetConfig(); new_config.levels = 1; new_config.rendertarget = true; - TCacheEntryBase* decoded_entry = AllocateTexture(new_config); + TCacheEntry* decoded_entry = AllocateCacheEntry(new_config); if (!decoded_entry) return nullptr; @@ -259,10 +237,10 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::ApplyPaletteToEntry(TCacheE return decoded_entry; } -void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntryBase** entry, - u32 new_width, u32 new_height) +void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntry* entry, u32 new_width, + u32 new_height) { - if ((*entry)->config.width == new_width && (*entry)->config.height == new_height) + if (entry->GetWidth() == new_width && entry->GetHeight() == new_height) { return; } @@ -274,41 +252,24 @@ void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntryBas return; } - TextureCacheBase::TCacheEntryConfig newconfig; + TextureConfig newconfig; newconfig.width = new_width; newconfig.height = new_height; - newconfig.layers = (*entry)->config.layers; + newconfig.layers = entry->GetNumLayers(); newconfig.rendertarget = true; - TCacheEntryBase* newentry = AllocateTexture(newconfig); - if (newentry) + std::unique_ptr<AbstractTexture> new_texture = AllocateTexture(newconfig); + if (new_texture) { - newentry->SetGeneralParameters((*entry)->addr, (*entry)->size_in_bytes, (*entry)->format); - newentry->SetDimensions((*entry)->native_width, (*entry)->native_height, 1); - newentry->SetHashes((*entry)->base_hash, (*entry)->hash); - newentry->frameCount = frameCount; - newentry->is_efb_copy = (*entry)->is_efb_copy; - MathUtil::Rectangle<int> srcrect, dstrect; - srcrect.left = 0; - srcrect.top = 0; - srcrect.right = (*entry)->config.width; - srcrect.bottom = (*entry)->config.height; - dstrect.left = 0; - dstrect.top = 0; - dstrect.right = new_width; - dstrect.bottom = new_height; - newentry->CopyRectangleFromTexture(*entry, srcrect, dstrect); - - // Keep track of the pointer for textures_by_hash - if ((*entry)->textures_by_hash_iter != textures_by_hash.end()) - { - newentry->textures_by_hash_iter = textures_by_hash.emplace((*entry)->hash, newentry); - } - - InvalidateTexture(GetTexCacheIter(*entry)); - - *entry = newentry; - textures_by_address.emplace((*entry)->addr, *entry); + new_texture->CopyRectangleFromTexture(entry->texture.get(), + entry->texture->GetConfig().GetRect(), + new_texture->GetConfig().GetRect()); + entry->texture.swap(new_texture); + + auto config = new_texture->GetConfig(); + // At this point new_texture has the old texture in it, + // we can potentially reuse this, so let's move it back to the pool + texture_pool.emplace(config, TexPoolEntry(std::move(new_texture))); } else { @@ -316,9 +277,8 @@ void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntryBas } } -TextureCacheBase::TCacheEntryBase* -TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8* palette, - u32 tlutfmt) +TextureCacheBase::TCacheEntry* +TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, u8* palette, u32 tlutfmt) { // If the flag may_have_overlapping_textures is cleared, there are no overlapping EFB copies, // which aren't applied already. It is set for new textures, and for the affected range @@ -346,7 +306,7 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8* auto iter = FindOverlappingTextures(entry_to_update->addr, entry_to_update->size_in_bytes); while (iter.first != iter.second) { - TCacheEntryBase* entry = iter.first->second; + TCacheEntry* entry = iter.first->second; if (entry != entry_to_update && entry->IsEfbCopy() && entry->references.count(entry_to_update) == 0 && entry->OverlapsMemoryRange(entry_to_update->addr, entry_to_update->size_in_bytes) && @@ -356,7 +316,7 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8* { if (isPaletteTexture) { - TCacheEntryBase* decoded_entry = ApplyPaletteToEntry(entry, palette, tlutfmt); + TCacheEntry* decoded_entry = ApplyPaletteToEntry(entry, palette, tlutfmt); if (decoded_entry) { // Link the efb copy with the partially updated texture, so we won't apply this partial @@ -404,15 +364,14 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8* std::min(entry->native_height - src_y, entry_to_update->native_height - dst_y); // If one of the textures is scaled, scale both with the current efb scaling factor - if (entry_to_update->native_width != entry_to_update->config.width || - entry_to_update->native_height != entry_to_update->config.height || - entry->native_width != entry->config.width || - entry->native_height != entry->config.height) + if (entry_to_update->native_width != entry_to_update->GetWidth() || + entry_to_update->native_height != entry_to_update->GetHeight() || + entry->native_width != entry->GetWidth() || entry->native_height != entry->GetHeight()) { - ScaleTextureCacheEntryTo(&entry_to_update, + ScaleTextureCacheEntryTo(entry_to_update, g_renderer->EFBToScaledX(entry_to_update->native_width), g_renderer->EFBToScaledY(entry_to_update->native_height)); - ScaleTextureCacheEntryTo(&entry, g_renderer->EFBToScaledX(entry->native_width), + ScaleTextureCacheEntryTo(entry, g_renderer->EFBToScaledX(entry->native_width), g_renderer->EFBToScaledY(entry->native_height)); src_x = g_renderer->EFBToScaledX(src_x); @@ -432,7 +391,7 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8* dstrect.top = dst_y; dstrect.right = (dst_x + copy_width); dstrect.bottom = (dst_y + copy_height); - entry_to_update->CopyRectangleFromTexture(entry, srcrect, dstrect); + entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, dstrect); if (isPaletteTexture) { @@ -460,7 +419,7 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8* return entry_to_update; } -void TextureCacheBase::DumpTexture(TCacheEntryBase* entry, std::string basename, unsigned int level) +void TextureCacheBase::DumpTexture(TCacheEntry* entry, std::string basename, unsigned int level) { std::string szDir = File::GetUserPath(D_DUMPTEXTURES_IDX) + SConfig::GetInstance().GetGameID(); @@ -475,7 +434,7 @@ void TextureCacheBase::DumpTexture(TCacheEntryBase* entry, std::string basename, std::string filename = szDir + "/" + basename + ".png"; if (!File::Exists(filename)) - entry->Save(filename, level); + entry->texture->Save(filename, level); } static u32 CalculateLevelSize(u32 level_0_size, u32 level) @@ -484,8 +443,7 @@ static u32 CalculateLevelSize(u32 level_0_size, u32 level) } // Used by TextureCacheBase::Load -TextureCacheBase::TCacheEntryBase* TextureCacheBase::ReturnEntry(unsigned int stage, - TCacheEntryBase* entry) +TextureCacheBase::TCacheEntry* TextureCacheBase::ReturnEntry(unsigned int stage, TCacheEntry* entry) { entry->frameCount = FRAMECOUNT_INVALID; bound_textures[stage] = entry; @@ -500,7 +458,7 @@ void TextureCacheBase::BindTextures() for (size_t i = 0; i < bound_textures.size(); ++i) { if (bound_textures[i]) - bound_textures[i]->Bind(static_cast<u32>(i)); + bound_textures[i]->texture->Bind(static_cast<u32>(i)); } } @@ -509,7 +467,7 @@ void TextureCacheBase::UnbindTextures() bound_textures.fill(nullptr); } -TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) +TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage) { const FourTexUnits& tex = bpmem.tex[stage >> 2]; const u32 id = stage & 3; @@ -651,7 +609,7 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) while (iter != iter_range.second) { - TCacheEntryBase* entry = iter->second; + TCacheEntry* entry = iter->second; // Do not load strided EFB copies, they are not meant to be used directly if (entry->IsEfbCopy() && entry->native_width == nativeW && entry->native_height == nativeH && entry->memory_stride == entry->BytesPerRow()) @@ -714,7 +672,7 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) if (unconverted_copy != textures_by_address.end()) { - TCacheEntryBase* decoded_entry = + TCacheEntry* decoded_entry = ApplyPaletteToEntry(unconverted_copy->second, &texMem[tlutaddr], tlutfmt); if (decoded_entry) @@ -737,7 +695,7 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) TexHashCache::iterator hash_iter = hash_range.first; while (hash_iter != hash_range.second) { - TCacheEntryBase* entry = hash_iter->second; + TCacheEntry* entry = hash_iter->second; // All parameters, except the address, need to match here if (entry->format == full_format && entry->native_levels >= tex_levels && entry->native_width == nativeW && entry->native_height == nativeH) @@ -791,13 +749,13 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) !(from_tmem && texformat == GX_TF_RGBA8); // create the entry/texture - TCacheEntryConfig config; + TextureConfig config; config.width = width; config.height = height; config.levels = texLevels; - config.format = hires_tex ? hires_tex->GetFormat() : HostTextureFormat::RGBA8; + config.format = hires_tex ? hires_tex->GetFormat() : AbstractTextureFormat::RGBA8; - TCacheEntryBase* entry = AllocateTexture(config); + TCacheEntry* entry = AllocateCacheEntry(config); GFX_DEBUGGER_PAUSE_AT(NEXT_NEW_TEXTURE, true); if (!entry) @@ -807,8 +765,10 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) if (hires_tex) { const auto& level = hires_tex->m_levels[0]; - entry->Load(0, level.width, level.height, level.row_length, level.data.get(), level.data_size); + entry->texture->Load(0, level.width, level.height, level.row_length, level.data.get(), + level.data_size); } + if (!hires_tex && decode_on_gpu) { u32 row_stride = bytes_per_block * (expandedWidth / bsw); @@ -832,7 +792,7 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) TexDecoder_DecodeRGBA8FromTmem(temp, src_data, src_data_gb, expandedWidth, expandedHeight); } - entry->Load(0, width, height, expandedWidth, temp, decoded_texture_size); + entry->texture->Load(0, width, height, expandedWidth, temp, decoded_texture_size); } iter = textures_by_address.emplace(address, entry); @@ -862,8 +822,8 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) for (u32 level_index = 1; level_index != texLevels; ++level_index) { const auto& level = hires_tex->m_levels[level_index]; - entry->Load(level_index, level.width, level.height, level.row_length, level.data.get(), - level.data_size); + entry->texture->Load(level_index, level.width, level.height, level.row_length, + level.data.get(), level.data_size); } } else @@ -905,7 +865,8 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage) size_t decoded_mip_size = expanded_mip_width * sizeof(u32) * expanded_mip_height; TexDecoder_Decode(temp, mip_src_data, expanded_mip_width, expanded_mip_height, texformat, tlut, (TlutFormat)tlutfmt); - entry->Load(level, mip_width, mip_height, expanded_mip_width, temp, decoded_mip_size); + entry->texture->Load(level, mip_width, mip_height, expanded_mip_width, temp, + decoded_mip_size); } mip_src_data += mip_size; @@ -1357,7 +1318,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo auto iter = FindOverlappingTextures(dstAddr, covered_range); while (iter.first != iter.second) { - TCacheEntryBase* entry = iter.first->second; + TCacheEntry* entry = iter.first->second; if (entry->OverlapsMemoryRange(dstAddr, covered_range)) { if (invalidate_textures) @@ -1373,13 +1334,13 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo if (copy_to_vram) { // create the texture - TCacheEntryConfig config; + TextureConfig config; config.rendertarget = true; config.width = scaled_tex_w; config.height = scaled_tex_h; config.layers = FramebufferManagerBase::GetEFBLayers(); - TCacheEntryBase* entry = AllocateTexture(config); + TCacheEntry* entry = AllocateCacheEntry(config); if (entry) { @@ -1390,7 +1351,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo entry->SetEfbCopy(dstStride); entry->is_custom_tex = false; - entry->FromRenderTarget(is_depth_copy, srcRect, scaleByHalf, cbufid, colmat); + CopyEFBToCacheEntry(entry, is_depth_copy, srcRect, scaleByHalf, cbufid, colmat); u64 hash = entry->CalculateHash(); entry->SetHashes(hash, hash); @@ -1398,9 +1359,10 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo if (g_ActiveConfig.bDumpEFBTarget) { static int count = 0; - entry->Save(StringFromFormat("%sefb_frame_%i.png", - File::GetUserPath(D_DUMPTEXTURES_IDX).c_str(), count++), - 0); + entry->texture->Save(StringFromFormat("%sefb_frame_%i.png", + File::GetUserPath(D_DUMPTEXTURES_IDX).c_str(), + count++), + 0); } textures_by_address.emplace(dstAddr, entry); @@ -1408,14 +1370,26 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo } } -TextureCacheBase::TCacheEntryBase* -TextureCacheBase::AllocateTexture(const TCacheEntryConfig& config) +TextureCacheBase::TCacheEntry* TextureCacheBase::AllocateCacheEntry(const TextureConfig& config) +{ + std::unique_ptr<AbstractTexture> texture = AllocateTexture(config); + + if (!texture) + { + return nullptr; + } + TCacheEntry* cacheEntry = new TCacheEntry(std::move(texture)); + cacheEntry->textures_by_hash_iter = textures_by_hash.end(); + return cacheEntry; +} + +std::unique_ptr<AbstractTexture> TextureCacheBase::AllocateTexture(const TextureConfig& config) { TexPool::iterator iter = FindMatchingTextureFromPool(config); - TextureCacheBase::TCacheEntryBase* entry; + std::unique_ptr<AbstractTexture> entry; if (iter != texture_pool.end()) { - entry = iter->second; + entry = std::move(iter->second.texture); texture_pool.erase(iter); } else @@ -1427,13 +1401,11 @@ TextureCacheBase::AllocateTexture(const TCacheEntryConfig& config) INCSTAT(stats.numTexturesCreated); } - entry->textures_by_hash_iter = textures_by_hash.end(); - entry->may_have_overlapping_textures = true; return entry; } TextureCacheBase::TexPool::iterator -TextureCacheBase::FindMatchingTextureFromPool(const TCacheEntryConfig& config) +TextureCacheBase::FindMatchingTextureFromPool(const TextureConfig& config) { // Find a texture from the pool that does not have a frameCount of FRAMECOUNT_INVALID. // This prevents a texture from being used twice in a single frame with different data, @@ -1442,13 +1414,13 @@ TextureCacheBase::FindMatchingTextureFromPool(const TCacheEntryConfig& config) // As non-render-target textures are usually static, this should not matter much. auto range = texture_pool.equal_range(config); auto matching_iter = std::find_if(range.first, range.second, [](const auto& iter) { - return iter.first.rendertarget || iter.second->frameCount != FRAMECOUNT_INVALID; + return iter.first.rendertarget || iter.second.frameCount != FRAMECOUNT_INVALID; }); return matching_iter != range.second ? matching_iter : texture_pool.end(); } TextureCacheBase::TexAddrCache::iterator -TextureCacheBase::GetTexCacheIter(TextureCacheBase::TCacheEntryBase* entry) +TextureCacheBase::GetTexCacheIter(TextureCacheBase::TCacheEntry* entry) { auto iter_range = textures_by_address.equal_range(entry->addr); TexAddrCache::iterator iter = iter_range.first; @@ -1486,7 +1458,7 @@ TextureCacheBase::InvalidateTexture(TexAddrCache::iterator iter) if (iter == textures_by_address.end()) return textures_by_address.end(); - TCacheEntryBase* entry = iter->second; + TCacheEntry* entry = iter->second; if (entry->textures_by_hash_iter != textures_by_hash.end()) { @@ -1494,15 +1466,13 @@ TextureCacheBase::InvalidateTexture(TexAddrCache::iterator iter) entry->textures_by_hash_iter = textures_by_hash.end(); } - entry->DestroyAllReferences(); - - entry->frameCount = FRAMECOUNT_INVALID; - texture_pool.emplace(entry->config, entry); + auto config = entry->texture->GetConfig(); + texture_pool.emplace(config, TexPoolEntry(std::move(entry->texture))); return textures_by_address.erase(iter); } -u32 TextureCacheBase::TCacheEntryBase::BytesPerRow() const +u32 TextureCacheBase::TCacheEntry::BytesPerRow() const { const u32 blockW = TexDecoder_GetBlockWidthInTexels(format); @@ -1517,7 +1487,7 @@ u32 TextureCacheBase::TCacheEntryBase::BytesPerRow() const return numBlocksX * bytes_per_block; } -u32 TextureCacheBase::TCacheEntryBase::NumBlocksY() const +u32 TextureCacheBase::TCacheEntry::NumBlocksY() const { u32 blockH = TexDecoder_GetBlockHeightInTexels(format); // Round up source height to multiple of block size @@ -1526,7 +1496,7 @@ u32 TextureCacheBase::TCacheEntryBase::NumBlocksY() const return actualHeight / blockH; } -void TextureCacheBase::TCacheEntryBase::SetEfbCopy(u32 stride) +void TextureCacheBase::TCacheEntry::SetEfbCopy(u32 stride) { is_efb_copy = true; memory_stride = stride; @@ -1536,7 +1506,7 @@ void TextureCacheBase::TCacheEntryBase::SetEfbCopy(u32 stride) size_in_bytes = memory_stride * NumBlocksY(); } -u64 TextureCacheBase::TCacheEntryBase::CalculateHash() const +u64 TextureCacheBase::TCacheEntry::CalculateHash() const { u8* ptr = Memory::GetPointer(addr); if (memory_stride == BytesPerRow()) diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index 7f2491a5c7..23f25f48fc 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -12,7 +12,9 @@ #include <unordered_set> #include "Common/CommonTypes.h" +#include "VideoCommon/AbstractTexture.h" #include "VideoCommon/BPMemory.h" +#include "VideoCommon/TextureConfig.h" #include "VideoCommon/TextureDecoder.h" #include "VideoCommon/VideoCommon.h" @@ -20,40 +22,14 @@ struct VideoConfig; class TextureCacheBase { -public: - struct TCacheEntryConfig - { - constexpr TCacheEntryConfig() = default; - - bool operator==(const TCacheEntryConfig& o) const - { - return std::tie(width, height, levels, layers, format, rendertarget) == - std::tie(o.width, o.height, o.levels, o.layers, o.format, o.rendertarget); - } - - struct Hasher : std::hash<u64> - { - size_t operator()(const TCacheEntryConfig& c) const - { - u64 id = (u64)c.rendertarget << 63 | (u64)c.format << 50 | (u64)c.layers << 48 | - (u64)c.levels << 32 | (u64)c.height << 16 | (u64)c.width; - return std::hash<u64>::operator()(id); - } - }; - - u32 width = 0; - u32 height = 0; - u32 levels = 1; - u32 layers = 1; - HostTextureFormat format = HostTextureFormat::RGBA8; - bool rendertarget = false; - }; +private: + static const int FRAMECOUNT_INVALID = 0; - struct TCacheEntryBase +public: + struct TCacheEntry { - const TCacheEntryConfig config; - // common members + std::unique_ptr<AbstractTexture> texture; u32 addr; u32 size_in_bytes; u64 base_hash; @@ -62,23 +38,27 @@ public: u32 memory_stride; bool is_efb_copy; bool is_custom_tex; - bool may_have_overlapping_textures; + bool may_have_overlapping_textures = true; unsigned int native_width, native_height; // Texture dimensions from the GameCube's point of view unsigned int native_levels; // used to delete textures which haven't been used for TEXTURE_KILL_THRESHOLD frames - int frameCount; + int frameCount = FRAMECOUNT_INVALID; // Keep an iterator to the entry in textures_by_hash, so it does not need to be searched when // removing the cache entry - std::multimap<u64, TCacheEntryBase*>::iterator textures_by_hash_iter; + std::multimap<u64, TCacheEntry*>::iterator textures_by_hash_iter; // This is used to keep track of both: // * efb copies used by this partially updated texture // * partially updated textures which refer to this efb copy - std::unordered_set<TCacheEntryBase*> references; + std::unordered_set<TCacheEntry*> references; + + explicit TCacheEntry(std::unique_ptr<AbstractTexture> tex); + + ~TCacheEntry(); void SetGeneralParameters(u32 _addr, u32 _size, u32 _format) { @@ -103,38 +83,15 @@ public: } // This texture entry is used by the other entry as a sub-texture - void CreateReference(TCacheEntryBase* other_entry) + void CreateReference(TCacheEntry* other_entry) { // References are two-way, so they can easily be destroyed later this->references.emplace(other_entry); other_entry->references.emplace(this); } - void DestroyAllReferences() - { - for (auto& reference : references) - reference->references.erase(this); - - references.clear(); - } - void SetEfbCopy(u32 stride); - TCacheEntryBase(const TCacheEntryConfig& c) : config(c) {} - virtual ~TCacheEntryBase(); - - virtual void Bind(unsigned int stage) = 0; - virtual bool Save(const std::string& filename, unsigned int level) = 0; - - virtual void CopyRectangleFromTexture(const TCacheEntryBase* source, - const MathUtil::Rectangle<int>& srcrect, - const MathUtil::Rectangle<int>& dstrect) = 0; - - virtual void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer, - size_t buffer_size) = 0; - virtual void FromRenderTarget(bool is_depth_copy, const EFBRectangle& srcRect, bool scaleByHalf, - unsigned int cbufid, const float* colmat) = 0; - bool OverlapsMemoryRange(u32 range_address, u32 range_size) const; bool IsEfbCopy() const { return is_efb_copy; } @@ -142,14 +99,16 @@ public: u32 BytesPerRow() const; u64 CalculateHash() const; + + u32 GetWidth() const { return texture->GetConfig().width; } + u32 GetHeight() const { return texture->GetConfig().height; } + u32 GetNumLevels() const { return texture->GetConfig().levels; } + u32 GetNumLayers() const { return texture->GetConfig().layers; } + AbstractTextureFormat GetFormat() const { return texture->GetConfig().format; } }; virtual ~TextureCacheBase(); // needs virtual for DX11 dtor - // TODO: Move these to AbstractTexture once it is finished. - static bool IsCompressedHostTextureFormat(HostTextureFormat format); - static size_t CalculateHostTextureLevelPitch(HostTextureFormat format, u32 row_length); - void OnConfigChanged(VideoConfig& config); // Removes textures which aren't used for more than TEXTURE_KILL_THRESHOLD frames, @@ -158,8 +117,6 @@ public: void Invalidate(); - virtual TCacheEntryBase* CreateTexture(const TCacheEntryConfig& config) = 0; - virtual void CopyEFB(u8* dst, const EFBCopyFormat& format, u32 native_width, u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride, bool is_depth_copy, const EFBRectangle& src_rect, bool scale_by_half) = 0; @@ -167,14 +124,14 @@ public: virtual bool CompileShaders() = 0; virtual void DeleteShaders() = 0; - TCacheEntryBase* Load(const u32 stage); + TCacheEntry* Load(const u32 stage); void UnbindTextures(); virtual void BindTextures(); void CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat, u32 dstStride, bool is_depth_copy, const EFBRectangle& srcRect, bool isIntensity, bool scaleByHalf); - virtual void ConvertTexture(TCacheEntryBase* entry, TCacheEntryBase* unconverted, void* palette, + virtual void ConvertTexture(TCacheEntry* entry, TCacheEntry* unconverted, void* palette, TlutFormat format) = 0; // Returns true if the texture data and palette formats are supported by the GPU decoder. @@ -187,7 +144,7 @@ public: // width, height are the size of the image in pixels. // aligned_width, aligned_height are the size of the image in pixels, aligned to the block size. // row_stride is the number of bytes for a row of blocks, not pixels. - virtual void DecodeTextureOnGPU(TCacheEntryBase* entry, u32 dst_level, const u8* data, + virtual void DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u8* data, size_t data_size, TextureFormat format, u32 width, u32 height, u32 aligned_width, u32 aligned_height, u32 row_stride, const u8* palette, TlutFormat palette_format) @@ -200,38 +157,50 @@ protected: alignas(16) u8* temp = nullptr; size_t temp_size = 0; - std::array<TCacheEntryBase*, 8> bound_textures{}; + std::array<TCacheEntry*, 8> bound_textures{}; private: - typedef std::multimap<u32, TCacheEntryBase*> TexAddrCache; - typedef std::multimap<u64, TCacheEntryBase*> TexHashCache; - typedef std::unordered_multimap<TCacheEntryConfig, TCacheEntryBase*, TCacheEntryConfig::Hasher> - TexPool; + // Minimal version of TCacheEntry just for TexPool + struct TexPoolEntry + { + std::unique_ptr<AbstractTexture> texture; + int frameCount = FRAMECOUNT_INVALID; + TexPoolEntry(std::unique_ptr<AbstractTexture> tex) : texture(std::move(tex)) {} + }; + typedef std::multimap<u32, TCacheEntry*> TexAddrCache; + typedef std::multimap<u64, TCacheEntry*> TexHashCache; + typedef std::unordered_multimap<TextureConfig, TexPoolEntry, TextureConfig::Hasher> TexPool; void SetBackupConfig(const VideoConfig& config); - TCacheEntryBase* ApplyPaletteToEntry(TCacheEntryBase* entry, u8* palette, u32 tlutfmt); + TCacheEntry* ApplyPaletteToEntry(TCacheEntry* entry, u8* palette, u32 tlutfmt); - void ScaleTextureCacheEntryTo(TCacheEntryBase** entry, u32 new_width, u32 new_height); - TCacheEntryBase* DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8* palette, - u32 tlutfmt); + void ScaleTextureCacheEntryTo(TCacheEntry* entry, u32 new_width, u32 new_height); + TCacheEntry* DoPartialTextureUpdates(TCacheEntry* entry_to_update, u8* palette, u32 tlutfmt); - void DumpTexture(TCacheEntryBase* entry, std::string basename, unsigned int level); + void DumpTexture(TCacheEntry* entry, std::string basename, unsigned int level); void CheckTempSize(size_t required_size); - TCacheEntryBase* AllocateTexture(const TCacheEntryConfig& config); - TexPool::iterator FindMatchingTextureFromPool(const TCacheEntryConfig& config); - TexAddrCache::iterator GetTexCacheIter(TCacheEntryBase* entry); + TCacheEntry* AllocateCacheEntry(const TextureConfig& config); + std::unique_ptr<AbstractTexture> AllocateTexture(const TextureConfig& config); + TexPool::iterator FindMatchingTextureFromPool(const TextureConfig& config); + TexAddrCache::iterator GetTexCacheIter(TCacheEntry* entry); // Return all possible overlapping textures. As addr+size of the textures is not // indexed, this may return false positives. std::pair<TexAddrCache::iterator, TexAddrCache::iterator> FindOverlappingTextures(u32 addr, u32 size_in_bytes); + virtual std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) = 0; + + virtual void CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy, + const EFBRectangle& src_rect, bool scale_by_half, + unsigned int cbuf_id, const float* colmat) = 0; + // Removes and unlinks texture from texture cache and returns it to the pool TexAddrCache::iterator InvalidateTexture(TexAddrCache::iterator t_iter); - TCacheEntryBase* ReturnEntry(unsigned int stage, TCacheEntryBase* entry); + TCacheEntry* ReturnEntry(unsigned int stage, TCacheEntry* entry); TexAddrCache textures_by_address; TexHashCache textures_by_hash; diff --git a/Source/Core/VideoCommon/TextureConfig.cpp b/Source/Core/VideoCommon/TextureConfig.cpp new file mode 100644 index 0000000000..7a6930c798 --- /dev/null +++ b/Source/Core/VideoCommon/TextureConfig.cpp @@ -0,0 +1,18 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "VideoCommon/TextureConfig.h" + +#include <tuple> + +bool TextureConfig::operator==(const TextureConfig& o) const +{ + return std::tie(width, height, levels, layers, format, rendertarget) == + std::tie(o.width, o.height, o.levels, o.layers, o.format, o.rendertarget); +} + +MathUtil::Rectangle<int> TextureConfig::GetRect() const +{ + return {0, 0, static_cast<int>(width), static_cast<int>(height)}; +} diff --git a/Source/Core/VideoCommon/TextureConfig.h b/Source/Core/VideoCommon/TextureConfig.h new file mode 100644 index 0000000000..27a0c935a1 --- /dev/null +++ b/Source/Core/VideoCommon/TextureConfig.h @@ -0,0 +1,43 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include <cstddef> +#include <functional> + +#include "Common/CommonTypes.h" +#include "Common/MathUtil.h" + +enum class AbstractTextureFormat : u32 +{ + RGBA8, + DXT1, + DXT3, + DXT5 +}; + +struct TextureConfig +{ + constexpr TextureConfig() = default; + bool operator==(const TextureConfig& o) const; + MathUtil::Rectangle<int> GetRect() const; + + u32 width = 0; + u32 height = 0; + u32 levels = 1; + u32 layers = 1; + AbstractTextureFormat format = AbstractTextureFormat::RGBA8; + bool rendertarget = false; + + struct Hasher : std::hash<u64> + { + size_t operator()(const TextureConfig& c) const + { + u64 id = (u64)c.rendertarget << 63 | (u64)c.format << 50 | (u64)c.layers << 48 | + (u64)c.levels << 32 | (u64)c.height << 16 | (u64)c.width; + return std::hash<u64>::operator()(id); + } + }; +}; diff --git a/Source/Core/VideoCommon/VideoCommon.h b/Source/Core/VideoCommon/VideoCommon.h index cff2883318..e25b2b66a5 100644 --- a/Source/Core/VideoCommon/VideoCommon.h +++ b/Source/Core/VideoCommon/VideoCommon.h @@ -77,15 +77,6 @@ enum class APIType Nothing }; -// Texture formats that videocommon can upload/use. -enum class HostTextureFormat : u32 -{ - RGBA8, - DXT1, - DXT3, - DXT5 -}; - inline u32 RGBA8ToRGBA6ToRGBA8(u32 src) { u32 color = src; diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj index 88d3e4b759..97adb25ac6 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj @@ -36,6 +36,7 @@ </ImportGroup> <PropertyGroup Label="UserMacros" /> <ItemGroup> + <ClCompile Include="AbstractTexture.cpp" /> <ClCompile Include="AsyncRequests.cpp" /> <ClCompile Include="AVIDump.cpp" /> <ClCompile Include="BoundingBox.cpp" /> @@ -68,6 +69,7 @@ <ClCompile Include="GeometryShaderGen.cpp" /> <ClCompile Include="GeometryShaderManager.cpp" /> <ClCompile Include="TextureCacheBase.cpp" /> + <ClCompile Include="TextureConfig.cpp" /> <ClCompile Include="TextureConversionShader.cpp" /> <ClCompile Include="VertexLoader.cpp" /> <ClCompile Include="VertexLoaderBase.cpp" /> @@ -89,6 +91,7 @@ <ClCompile Include="XFStructs.cpp" /> </ItemGroup> <ItemGroup> + <ClInclude Include="AbstractTexture.h" /> <ClInclude Include="AsyncRequests.h" /> <ClInclude Include="AVIDump.h" /> <ClInclude Include="BoundingBox.h" /> @@ -124,6 +127,7 @@ <ClInclude Include="GeometryShaderGen.h" /> <ClInclude Include="GeometryShaderManager.h" /> <ClInclude Include="TextureCacheBase.h" /> + <ClInclude Include="TextureConfig.h" /> <ClInclude Include="TextureConversionShader.h" /> <ClInclude Include="TextureDecoder.h" /> <ClInclude Include="VertexLoader.h" /> diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters index eb45d40ffc..be09444957 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters @@ -167,6 +167,12 @@ <ClCompile Include="HiresTextures_DDSLoader.cpp"> <Filter>Util</Filter> </ClCompile> + <ClCompile Include="TextureConfig.cpp"> + <Filter>Base</Filter> + </ClCompile> + <ClCompile Include="AbstractTexture.cpp"> + <Filter>Base</Filter> + </C1Compile> </ItemGroup> <ItemGroup> <ClInclude Include="CommandProcessor.h" /> @@ -317,6 +323,12 @@ <ClInclude Include="SamplerCommon.h"> <Filter>Util</Filter> </ClInclude> + <ClInclude Include="TextureConfig.h"> + <Filter>Base</Filter> + </ClInclude> + <ClInclude Include="AbstractTexture.h"> + <Filter>Base</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <Text Include="CMakeLists.txt" /> |
