Skip to content

Commit 0d368d5

Browse files
committed
Addendum #4 to 5bb4daa: Fix additional cause of 'white textures'
1 parent df19e6c commit 0d368d5

File tree

1 file changed

+72
-36
lines changed

1 file changed

+72
-36
lines changed

Client/game_sa/CRenderWareSA.TextureReplacing.cpp

Lines changed: 72 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -56,39 +56,45 @@ namespace
5656
using TextureSwapMap = std::unordered_map<RwTexture*, RwTexture*>;
5757

5858
// Content-hashed key avoids allocations
59-
struct TextureNameHash
59+
struct SafeTextureName
6060
{
61-
std::size_t operator()(const char* s) const noexcept
62-
{
63-
if (!s)
64-
return 0;
61+
char m_name[RW_TEXTURE_NAME_LENGTH];
62+
std::size_t m_len;
6563

66-
std::size_t h = 2166136261u;
67-
for (std::size_t i = 0; i < RW_TEXTURE_NAME_LENGTH; ++i)
64+
SafeTextureName(const char* str)
65+
{
66+
if (str)
6867
{
69-
const unsigned char c = static_cast<unsigned char>(s[i]);
70-
if (c == 0)
71-
break;
72-
h ^= c;
73-
h *= 16777619u;
68+
m_len = strnlen(str, RW_TEXTURE_NAME_LENGTH);
69+
memcpy(m_name, str, m_len);
70+
if (m_len < RW_TEXTURE_NAME_LENGTH)
71+
m_name[m_len] = '\0';
72+
}
73+
else
74+
{
75+
m_len = 0;
76+
m_name[0] = '\0';
7477
}
75-
return h;
7678
}
79+
80+
bool operator==(const SafeTextureName& other) const noexcept { return m_len == other.m_len && memcmp(m_name, other.m_name, m_len) == 0; }
7781
};
7882

79-
struct TextureNameEq
83+
struct SafeTextureNameHash
8084
{
81-
bool operator()(const char* a, const char* b) const noexcept
85+
std::size_t operator()(const SafeTextureName& key) const noexcept
8286
{
83-
if (a == b)
84-
return true;
85-
if (!a || !b)
86-
return false;
87-
return strncmp(a, b, RW_TEXTURE_NAME_LENGTH) == 0;
87+
std::size_t h = 2166136261u;
88+
for (std::size_t i = 0; i < key.m_len; ++i)
89+
{
90+
h ^= static_cast<unsigned char>(key.m_name[i]);
91+
h *= 16777619u;
92+
}
93+
return h;
8894
}
8995
};
9096

91-
using TxdTextureMap = std::unordered_map<const char*, RwTexture*, TextureNameHash, TextureNameEq>;
97+
using TxdTextureMap = std::unordered_map<SafeTextureName, RwTexture*, SafeTextureNameHash>;
9298
struct ReplacementShaderKey
9399
{
94100
SReplacementTextures* pReplacement;
@@ -272,7 +278,17 @@ namespace
272278

273279
bool IsValid(RwTexDictionary* pCurrentTxd) const noexcept
274280
{
275-
return pTxd != nullptr && pTxd == pCurrentTxd && pListHead == pCurrentTxd->textures.root.next;
281+
if (pTxd == nullptr || pTxd != pCurrentTxd || pListHead != pCurrentTxd->textures.root.next)
282+
return false;
283+
284+
if (pListHead && pListHead != &pCurrentTxd->textures.root)
285+
{
286+
RwTexture* pFirstTex = (RwTexture*)((char*)pListHead - offsetof(RwTexture, TXDList));
287+
auto it = textureMap.find(pFirstTex->name);
288+
if (it == textureMap.end() || it->second != pFirstTex)
289+
return false;
290+
}
291+
return true;
276292
}
277293
};
278294
std::unordered_map<unsigned short, SCachedTxdTextureMap> g_TxdTextureMapCache;
@@ -756,13 +772,13 @@ namespace
756772

757773
if (!bModelLoaded)
758774
{
759-
RequeuePendingReplacement(usModelId, entry, false);
775+
RequeuePendingReplacement(usModelId, entry, true);
760776
continue;
761777
}
762778

763779
if (bParentStreamingBusy)
764780
{
765-
RequeuePendingReplacement(usModelId, entry, false);
781+
RequeuePendingReplacement(usModelId, entry, true);
766782
continue;
767783
}
768784

@@ -775,18 +791,10 @@ namespace
775791

776792
++uiProcessedCount;
777793

778-
const uint32_t uiStartSerial = g_uiIsolationDeniedSerial;
779-
const bool bApplied = pRenderWareSA->ModelInfoTXDAddTextures(entry.pReplacement, usModelId);
794+
const bool bApplied = pRenderWareSA->ModelInfoTXDAddTextures(entry.pReplacement, usModelId);
780795
if (!bApplied)
781796
{
782-
if (WasIsolationDenied(uiStartSerial))
783-
{
784-
RequeuePendingReplacement(usModelId, entry, false);
785-
}
786-
else
787-
{
788-
RequeuePendingReplacement(usModelId, entry, true);
789-
}
797+
RequeuePendingReplacement(usModelId, entry, true);
790798
}
791799
else
792800
{
@@ -1657,7 +1665,14 @@ namespace
16571665
if (!CanDestroyOrphanedTexture(pTexture))
16581666
return;
16591667

1660-
reinterpret_cast<RwRaster* volatile&>(pTexture->raster) = nullptr;
1668+
// If this is the final reference, RwTextureDestroy will attempt to free the raster.
1669+
// We only clear it if refs <= 1. If refs > 1, the struct won't be freed anyway, so clearing
1670+
// the raster just needlessly breaks any other materials still keeping this copy alive.
1671+
if (pTexture->refs <= 1)
1672+
{
1673+
reinterpret_cast<RwRaster* volatile&>(pTexture->raster) = nullptr;
1674+
}
1675+
16611676
RwTextureDestroy(pTexture);
16621677
}
16631678

@@ -1762,7 +1777,22 @@ namespace
17621777
// Streaming can destroy and reload a TXD at the same address (pool/allocator reuse),
17631778
// which leaves the cache with dangling texture name pointers.
17641779
RwListEntry* pListHead = pVehicleTxd->textures.root.next;
1765-
if (pVehicleTxd != g_pCachedVehicleTxd || pListHead != g_pCachedVehicleTxdListHead)
1780+
RwTexture* pFirstTex = nullptr;
1781+
if (pListHead && pListHead != &pVehicleTxd->textures.root)
1782+
pFirstTex = (RwTexture*)((char*)pListHead - offsetof(RwTexture, TXDList));
1783+
1784+
bool bInvalidate = (pVehicleTxd != g_pCachedVehicleTxd || pListHead != g_pCachedVehicleTxdListHead);
1785+
1786+
if (!bInvalidate && pFirstTex && !g_CachedVehicleTxdMap.empty())
1787+
{
1788+
auto it = g_CachedVehicleTxdMap.find(pFirstTex->name);
1789+
if (it == g_CachedVehicleTxdMap.end() || it->second != pFirstTex)
1790+
{
1791+
bInvalidate = true;
1792+
}
1793+
}
1794+
1795+
if (bInvalidate)
17661796
{
17671797
g_CachedVehicleTxdMap.clear();
17681798
g_pCachedVehicleTxd = pVehicleTxd;
@@ -3998,6 +4028,8 @@ bool CRenderWareSA::ModelInfoTXDAddTextures(SReplacementTextures* pReplacementTe
39984028
CTxdStore_RemoveRef(usParentTxdId);
39994029
ClearIsolatedTxdLastUse(usModelId);
40004030
ClearPendingIsolatedModel(usModelId);
4031+
ClearPendingReplacementStateForModel(usModelId);
4032+
g_PendingReplacementByModel.erase(usModelId);
40014033
g_IsolatedTxdByModel.erase(itPrevIsolated);
40024034
QueuePendingReplacement(usModelId, pReplacementTextures, 0, 0);
40034035
return false;
@@ -4073,6 +4105,8 @@ bool CRenderWareSA::ModelInfoTXDAddTextures(SReplacementTextures* pReplacementTe
40734105
}
40744106
ClearIsolatedTxdLastUse(usModelId);
40754107
ClearPendingIsolatedModel(usModelId);
4108+
ClearPendingReplacementStateForModel(usModelId);
4109+
g_PendingReplacementByModel.erase(usModelId);
40764110
g_IsolatedTxdByModel.erase(itIsolated);
40774111
QueuePendingReplacement(usModelId, pReplacementTextures, 0, 0);
40784112
return false;
@@ -4094,6 +4128,8 @@ bool CRenderWareSA::ModelInfoTXDAddTextures(SReplacementTextures* pReplacementTe
40944128
CTxdStore_RemoveRef(usParentTxdId);
40954129
ClearIsolatedTxdLastUse(usModelId);
40964130
ClearPendingIsolatedModel(usModelId);
4131+
ClearPendingReplacementStateForModel(usModelId);
4132+
g_PendingReplacementByModel.erase(usModelId);
40974133
g_IsolatedTxdByModel.erase(itIsolated);
40984134
QueuePendingReplacement(usModelId, pReplacementTextures, 0, 0);
40994135
return false;

0 commit comments

Comments
 (0)