mirror of https://github.com/PCSX2/pcsx2.git
GS/HW: Fix various issues with texture replacements
- Replacement textures now show in HC usage to give a clearer picture of VRAM usage. - Fixed crashes when loading compressed and mipmapped DDS textures. - Fixed compressed mipmapped textures in Direct3D 12. - Fixed GPU crashes in D3D11/D3D12/Vulkan when compressed textures went down the last 1x1 mipmap level.
This commit is contained in:
parent
bb2016889a
commit
daebb5753a
|
@ -369,14 +369,14 @@ namespace Vulkan
|
|||
}
|
||||
|
||||
void Texture::UpdateFromBuffer(VkCommandBuffer cmdbuf, u32 level, u32 layer, u32 x, u32 y, u32 width, u32 height,
|
||||
u32 row_length, VkBuffer buffer, u32 buffer_offset)
|
||||
u32 buffer_height, u32 row_length, VkBuffer buffer, u32 buffer_offset)
|
||||
{
|
||||
const VkImageLayout old_layout = m_layout;
|
||||
if (old_layout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
||||
TransitionSubresourcesToLayout(
|
||||
cmdbuf, level, 1, layer, 1, old_layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
|
||||
const VkBufferImageCopy bic = {static_cast<VkDeviceSize>(buffer_offset), row_length, height,
|
||||
const VkBufferImageCopy bic = {static_cast<VkDeviceSize>(buffer_offset), row_length, buffer_height,
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, level, layer, 1u}, {static_cast<int32_t>(x), static_cast<int32_t>(y), 0},
|
||||
{width, height, 1u}};
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ namespace Vulkan
|
|||
VkFramebuffer CreateFramebuffer(VkRenderPass render_pass);
|
||||
|
||||
void UpdateFromBuffer(VkCommandBuffer cmdbuf, u32 level, u32 layer, u32 x, u32 y, u32 width, u32 height,
|
||||
u32 row_length, VkBuffer buffer, u32 buffer_offset);
|
||||
u32 buffer_height, u32 row_length, VkBuffer buffer, u32 buffer_offset);
|
||||
|
||||
private:
|
||||
u32 m_width = 0;
|
||||
|
|
|
@ -222,7 +222,7 @@ static bool UploadBufferToTexture(
|
|||
StringUtil::StrideMemCpy(buf.GetCurrentHostPointer(), upload_stride, data, data_stride, row_size, height);
|
||||
buf.CommitMemory(upload_size);
|
||||
|
||||
texture->UpdateFromBuffer(cmdbuf, 0, 0, 0, 0, width, height, upload_stride / texel_size, buf.GetBuffer(), buf_offset);
|
||||
texture->UpdateFromBuffer(cmdbuf, 0, 0, 0, 0, width, height, height, upload_stride / texel_size, buf.GetBuffer(), buf_offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -428,7 +428,7 @@ bool ImGui_ImplVulkan_CreateFontsTexture()
|
|||
std::memcpy(ai.pMappedData, pixels, upload_size);
|
||||
vmaFlushAllocation(g_vulkan_context->GetAllocator(), allocation, 0, upload_size);
|
||||
bd->FontTexture.TransitionToLayout(g_vulkan_context->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
bd->FontTexture.UpdateFromBuffer(g_vulkan_context->GetCurrentInitCommandBuffer(), 0, 0, 0, 0, width, height, width, buffer, 0);
|
||||
bd->FontTexture.UpdateFromBuffer(g_vulkan_context->GetCurrentInitCommandBuffer(), 0, 0, 0, 0, width, height, height, width, buffer, 0);
|
||||
bd->FontTexture.TransitionToLayout(g_vulkan_context->GetCurrentInitCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
// Immediately queue it for freeing after the command buffer finishes, since it's only needed for the copy.
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "GSTexture11.h"
|
||||
#include "GS/GSPng.h"
|
||||
#include "GS/GSPerfMon.h"
|
||||
#include "common/Align.h"
|
||||
|
||||
GSTexture11::GSTexture11(wil::com_ptr_nothrow<ID3D11Texture2D> texture, const D3D11_TEXTURE2D_DESC& desc,
|
||||
GSTexture::Type type, GSTexture::Format format)
|
||||
|
@ -44,7 +45,9 @@ bool GSTexture11::Update(const GSVector4i& r, const void* data, int pitch, int l
|
|||
|
||||
g_perfmon.Put(GSPerfMon::TextureUploads, 1);
|
||||
|
||||
const D3D11_BOX box = {(UINT)r.left, (UINT)r.top, 0U, (UINT)r.right, (UINT)r.bottom, 1U};
|
||||
const u32 bs = GetCompressedBlockSize();
|
||||
const D3D11_BOX box = {Common::AlignDownPow2((u32)r.left, bs), Common::AlignDownPow2((u32)r.top, bs),
|
||||
Common::AlignUpPow2((u32)r.right, bs), Common::AlignUpPow2((u32)r.bottom, bs), 1U};
|
||||
const UINT subresource = layer; // MipSlice + (ArraySlice * MipLevels).
|
||||
|
||||
GSDevice11::GetInstance()->GetD3DContext()->UpdateSubresource(m_texture.get(), subresource, &box, data, pitch, 0);
|
||||
|
|
|
@ -48,13 +48,11 @@ std::unique_ptr<GSTexture12> GSTexture12::Create(Type type, u32 width, u32 heigh
|
|||
{
|
||||
case Type::Texture:
|
||||
{
|
||||
// this is a little annoying. basically, to do mipmap generation, we need to be a render target.
|
||||
const D3D12_RESOURCE_FLAGS flags = (levels > 1) ? D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET : D3D12_RESOURCE_FLAG_NONE;
|
||||
if (levels > 1 && d3d_format != DXGI_FORMAT_R8G8B8A8_UNORM)
|
||||
{
|
||||
Console.Warning("DX12: Refusing to create a %ux%u format %u mipmapped texture", width, height, format);
|
||||
return nullptr;
|
||||
}
|
||||
// This is a little annoying. basically, to do mipmap generation, we need to be a render target.
|
||||
// If it's a compressed texture, we won't be generating mips anyway, so this should be fine.
|
||||
const D3D12_RESOURCE_FLAGS flags = (levels > 1 && !IsCompressedFormat(format)) ?
|
||||
D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET :
|
||||
D3D12_RESOURCE_FLAG_NONE;
|
||||
|
||||
D3D12::Texture texture;
|
||||
if (!texture.Create(width, height, levels, d3d_format, srv_format,
|
||||
|
@ -184,10 +182,12 @@ bool GSTexture12::Update(const GSVector4i& r, const void* data, int pitch, int l
|
|||
|
||||
g_perfmon.Put(GSPerfMon::TextureUploads, 1);
|
||||
|
||||
const u32 width = r.width();
|
||||
const u32 height = r.height();
|
||||
// Footprint and box must be block aligned for compressed textures.
|
||||
const u32 block_size = GetCompressedBlockSize();
|
||||
const u32 width = Common::AlignUpPow2(r.width(), block_size);
|
||||
const u32 height = Common::AlignUpPow2(r.height(), block_size);
|
||||
const u32 upload_pitch = Common::AlignUpPow2<u32>(pitch, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT);
|
||||
const u32 required_size = CalcUploadSize(height, upload_pitch);
|
||||
const u32 required_size = CalcUploadSize(r.height(), upload_pitch);
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION srcloc;
|
||||
srcloc.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
|
@ -250,8 +250,9 @@ bool GSTexture12::Update(const GSVector4i& r, const void* data, int pitch, int l
|
|||
dstloc.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
dstloc.SubresourceIndex = layer;
|
||||
|
||||
const D3D12_BOX srcbox{0u, 0u, 0u, static_cast<UINT>(r.width()), static_cast<UINT>(r.height()), 1u};
|
||||
cmdlist->CopyTextureRegion(&dstloc, r.x, r.y, 0, &srcloc, &srcbox);
|
||||
const D3D12_BOX srcbox{0u, 0u, 0u, width, height, 1u};
|
||||
cmdlist->CopyTextureRegion(&dstloc, Common::AlignDownPow2((u32)r.x, block_size),
|
||||
Common::AlignDownPow2((u32)r.y, block_size), 0, &srcloc, &srcbox);
|
||||
|
||||
if (m_texture.GetState() != D3D12_RESOURCE_STATE_COPY_DEST)
|
||||
m_texture.TransitionSubresourceToState(cmdlist, layer, D3D12_RESOURCE_STATE_COPY_DEST, m_texture.GetState());
|
||||
|
@ -293,7 +294,8 @@ bool GSTexture12::Map(GSMap& m, const GSVector4i* r, int layer)
|
|||
|
||||
void GSTexture12::Unmap()
|
||||
{
|
||||
pxAssert(m_map_level < m_texture.GetLevels());
|
||||
// this can't handle blocks/compressed formats at the moment.
|
||||
pxAssert(m_map_level < m_texture.GetLevels() && !IsCompressedFormat());
|
||||
g_perfmon.Put(GSPerfMon::TextureUploads, 1);
|
||||
|
||||
// TODO: non-tightly-packed formats
|
||||
|
@ -351,6 +353,8 @@ void GSTexture12::Unmap()
|
|||
|
||||
void GSTexture12::GenerateMipmap()
|
||||
{
|
||||
pxAssert(!IsCompressedFormat(m_format));
|
||||
|
||||
for (int dst_level = 1; dst_level < m_mipmap_levels; dst_level++)
|
||||
{
|
||||
const int src_level = dst_level - 1;
|
||||
|
|
|
@ -1644,9 +1644,7 @@ void GSTextureCache::IncAge()
|
|||
HashCacheEntry& e = it->second;
|
||||
if (e.refcount == 0 && ++e.age > max_hash_cache_age)
|
||||
{
|
||||
if (!e.is_replacement)
|
||||
m_hash_cache_memory_usage -= e.texture->GetMemUsage();
|
||||
|
||||
m_hash_cache_memory_usage -= e.texture->GetMemUsage();
|
||||
g_gs_device->Recycle(e.texture);
|
||||
m_hash_cache.erase(it++);
|
||||
}
|
||||
|
@ -2129,6 +2127,7 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
|
|||
// found a replacement texture! insert it into the hash cache, and clear paltex (since it's not indexed)
|
||||
paltex = false;
|
||||
const HashCacheEntry entry{replacement_tex, 1u, 0u, true};
|
||||
m_hash_cache_memory_usage += entry.texture->GetMemUsage();
|
||||
return &m_hash_cache.emplace(key, entry).first->second;
|
||||
}
|
||||
else if (
|
||||
|
@ -3163,6 +3162,9 @@ void GSTextureCache::InvalidateTemporarySource()
|
|||
|
||||
void GSTextureCache::InjectHashCacheTexture(const HashCacheKey& key, GSTexture* tex)
|
||||
{
|
||||
// When we insert we update memory usage. Old texture gets removed below.
|
||||
m_hash_cache_memory_usage += tex->GetMemUsage();
|
||||
|
||||
auto it = m_hash_cache.find(key);
|
||||
if (it == m_hash_cache.end())
|
||||
{
|
||||
|
|
|
@ -68,7 +68,7 @@ static u32 GetBlockCount(u32 extent, u32 block_size)
|
|||
static void CalcBlockMipmapSize(u32 block_size, u32 bytes_per_block, u32 base_width, u32 base_height, u32 mip, u32& width, u32& height, u32& pitch, u32& size)
|
||||
{
|
||||
width = std::max<u32>(base_width >> mip, 1u);
|
||||
height = std::max<u32>(base_width >> mip, 1u);
|
||||
height = std::max<u32>(base_height >> mip, 1u);
|
||||
|
||||
const u32 blocks_wide = GetBlockCount(width, block_size);
|
||||
const u32 blocks_high = GetBlockCount(height, block_size);
|
||||
|
@ -635,9 +635,9 @@ bool DDSLoader(const std::string& filename, GSTextureReplacements::ReplacementTe
|
|||
for (u32 level = 1; level <= info.mip_count; level++)
|
||||
{
|
||||
GSTextureReplacements::ReplacementTexture::MipData md;
|
||||
u32 mip_width, mip_height, mip_size;
|
||||
CalcBlockMipmapSize(info.block_size, info.bytes_per_block, info.width, info.height, level, mip_width, mip_height, md.pitch, mip_size);
|
||||
if (!ReadDDSMipLevel(fp.get(), filename, level, info, mip_width, mip_height, md.data, md.pitch, mip_size))
|
||||
u32 mip_size;
|
||||
CalcBlockMipmapSize(info.block_size, info.bytes_per_block, info.width, info.height, level, md.width, md.height, md.pitch, mip_size);
|
||||
if (!ReadDDSMipLevel(fp.get(), filename, level, info, md.width, md.height, md.data, md.pitch, mip_size))
|
||||
break;
|
||||
|
||||
tex->mips.push_back(std::move(md));
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
#include "common/ScopedGuard.h"
|
||||
|
||||
#include "Config.h"
|
||||
#include "Host.h"
|
||||
#include "IconsFontAwesome5.h"
|
||||
#include "GS/GSLocalMemory.h"
|
||||
#include "GS/Renderers/HW/GSTextureReplacements.h"
|
||||
|
||||
|
@ -325,7 +327,7 @@ void GSTextureReplacements::ReloadReplacementMap()
|
|||
if (!name.has_value())
|
||||
continue;
|
||||
|
||||
DevCon.WriteLn("Found %ux%u replacement '%.*s'", name->Width(), name->Height(), static_cast<int>(filename.size()), filename.data());
|
||||
DbgCon.WriteLn("Found %ux%u replacement '%.*s'", name->Width(), name->Height(), static_cast<int>(filename.size()), filename.data());
|
||||
s_replacement_texture_filenames.emplace(name.value(), std::move(fd.FileName));
|
||||
|
||||
// zero out the CLUT hash, because we need this for checking if there's any replacements with this hash when using paltex
|
||||
|
@ -529,7 +531,10 @@ GSTexture* GSTextureReplacements::CreateReplacementTexture(const ReplacementText
|
|||
static bool log_once = false;
|
||||
if (!log_once)
|
||||
{
|
||||
Console.Warning("Disabling mipmaps on one or more compressed replacement textures.");
|
||||
static const char* message =
|
||||
"Disabling autogenerated mipmaps on one or more compressed replacement textures. Please generate mipmaps when compressing your textures.";
|
||||
Console.Warning(message);
|
||||
Host::AddIconOSDMessage("DisablingReplacementAutoGeneratedMipmap", ICON_FA_EXCLAMATION_CIRCLE, message, Host::OSD_WARNING_DURATION);
|
||||
log_once = true;
|
||||
}
|
||||
|
||||
|
@ -548,10 +553,8 @@ GSTexture* GSTextureReplacements::CreateReplacementTexture(const ReplacementText
|
|||
{
|
||||
for (u32 i = 0; i < static_cast<u32>(rtex.mips.size()); i++)
|
||||
{
|
||||
const u32 mip = i + 1;
|
||||
const u32 mipw = std::max<u32>(rtex.width >> mip, 1u);
|
||||
const u32 miph = std::max<u32>(rtex.height >> mip, 1u);
|
||||
tex->Update(GSVector4i(0, 0, mipw, miph), rtex.mips[i].data.data(), rtex.mips[i].pitch, mip);
|
||||
const ReplacementTexture::MipData& mip = rtex.mips[i];
|
||||
tex->Update(GSVector4i(0, 0, static_cast<int>(mip.width), static_cast<int>(mip.height)), mip.data.data(), mip.pitch, i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ namespace GSTextureReplacements
|
|||
|
||||
struct MipData
|
||||
{
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 pitch;
|
||||
std::vector<u8> data;
|
||||
};
|
||||
|
|
|
@ -262,6 +262,7 @@ bool GSTextureVK::Update(const GSVector4i& r, const void* data, int pitch, int l
|
|||
}
|
||||
|
||||
m_texture.UpdateFromBuffer(cmdbuf, layer, 0, r.x, r.y, width, height,
|
||||
Common::AlignUpPow2(height, GetCompressedBlockSize()),
|
||||
CalcUploadRowLengthFromPitch(upload_pitch), buffer, buffer_offset);
|
||||
m_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
|
@ -303,7 +304,8 @@ bool GSTextureVK::Map(GSMap& m, const GSVector4i* r, int layer)
|
|||
|
||||
void GSTextureVK::Unmap()
|
||||
{
|
||||
pxAssert(m_map_level < m_texture.GetLevels());
|
||||
// this can't handle blocks/compressed formats at the moment.
|
||||
pxAssert(m_map_level < m_texture.GetLevels() && !IsCompressedFormat());
|
||||
g_perfmon.Put(GSPerfMon::TextureUploads, 1);
|
||||
|
||||
// TODO: non-tightly-packed formats
|
||||
|
@ -334,6 +336,7 @@ void GSTextureVK::Unmap()
|
|||
}
|
||||
|
||||
m_texture.UpdateFromBuffer(cmdbuf, m_map_level, 0, m_map_area.x, m_map_area.y, width, height,
|
||||
Common::AlignUpPow2(height, GetCompressedBlockSize()),
|
||||
CalcUploadRowLengthFromPitch(pitch), buffer.GetBuffer(), buffer_offset);
|
||||
m_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
|
|
Loading…
Reference in New Issue