Skip to content

Commit a0968d6

Browse files
DynamicTextureArray: avoid empty sparse resize binds
Treat page-aligned sparse resizes that preserve resident capacity as no-ops and document that sparse array size reports page-aligned capacity.
1 parent 93c975c commit a0968d6

3 files changed

Lines changed: 43 additions & 15 deletions

File tree

Graphics/GraphicsTools/interface/DynamicTextureArray.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ class DynamicTextureArray
117117
///
118118
/// Typically `pContext` is null when the method is called from a worker thread.
119119
///
120+
/// For a sparse texture, `NewArraySize` is rounded up to a multiple of
121+
/// DynamicTextureArrayCreateInfo::NumSlicesInMemoryPage. GetArraySize()
122+
/// reports this committed page-aligned resident capacity.
123+
///
120124
/// While a content-preserving resize of a default texture is pending, the
121125
/// method may only be called with the same `NewArraySize`, for example to
122126
/// provide a device or context that was previously unavailable. A different
@@ -197,6 +201,9 @@ class DynamicTextureArray
197201

198202
/// Returns the current number of slices in the texture array.
199203
///
204+
/// For a sparse texture, this is the committed page-aligned resident
205+
/// capacity and may be greater than the size passed to Resize().
206+
///
200207
/// \remarks The method is thread-safe and may be called from worker threads.
201208
/// If it races with Update() or Resize(), the returned value may
202209
/// become stale immediately.

Graphics/GraphicsTools/src/DynamicTextureArray.cpp

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -324,11 +324,24 @@ ITextureView* DynamicTextureArray::GetTextureSRV(TEXTURE_FORMAT ViewFormat) cons
324324

325325
bool DynamicTextureArray::ResizeSparseTexture(IDeviceContext* pContext)
326326
{
327-
const Uint32 CurrArraySize = GetArraySize();
328-
VERIFY_EXPR(m_PendingSize != CurrArraySize);
327+
const Uint32 CurrResidentSize = GetArraySize();
328+
VERIFY_EXPR(m_PendingSize != CurrResidentSize);
329329
VERIFY_EXPR(m_pTexture && m_pMemory);
330330

331-
m_PendingSize = AlignUp(m_PendingSize, m_NumSlicesInPage);
331+
const Uint64 ResidentSize = AlignUp(Uint64{m_PendingSize}, Uint64{m_NumSlicesInPage});
332+
if (ResidentSize > m_pTexture->GetDesc().ArraySize)
333+
{
334+
LOG_ERROR_MESSAGE("Requested sparse dynamic texture array size exceeds the texture capacity after page alignment");
335+
return false;
336+
}
337+
338+
m_PendingSize = StaticCast<Uint32>(ResidentSize);
339+
if (m_PendingSize == CurrResidentSize)
340+
{
341+
// The logical request fits in the currently resident page range. There
342+
// are no memory ranges to bind or unbind.
343+
return true;
344+
}
332345

333346
const Uint64 RequiredMemSize = (m_PendingSize / m_NumSlicesInPage) * m_MemoryPageSize;
334347
if (RequiredMemSize > m_pMemory->GetCapacity())
@@ -347,11 +360,11 @@ bool DynamicTextureArray::ResizeSparseTexture(IDeviceContext* pContext)
347360
}
348361
}
349362

350-
const Uint32 NumSlicesToBind = m_PendingSize > CurrArraySize ?
351-
m_PendingSize - CurrArraySize :
352-
CurrArraySize - m_PendingSize;
363+
const Uint32 NumSlicesToBind = m_PendingSize > CurrResidentSize ?
364+
m_PendingSize - CurrResidentSize :
365+
CurrResidentSize - m_PendingSize;
353366

354-
Uint64 CurrMemOffset = Uint64{(m_PendingSize > CurrArraySize ? CurrArraySize : m_PendingSize) / m_NumSlicesInPage} * m_MemoryPageSize;
367+
Uint64 CurrMemOffset = Uint64{(m_PendingSize > CurrResidentSize ? CurrResidentSize : m_PendingSize) / m_NumSlicesInPage} * m_MemoryPageSize;
355368

356369
const SparseTextureProperties& TexSparseProps = m_pTexture->GetSparseProperties();
357370
const Uint32 NumNormalMips = std::min(m_Desc.MipLevels, TexSparseProps.FirstMipInTail);
@@ -362,8 +375,8 @@ bool DynamicTextureArray::ResizeSparseTexture(IDeviceContext* pContext)
362375
std::vector<SparseTextureMemoryBindRange> MipRanges(size_t{NumSlicesToBind} * (size_t{NumNormalMips} + (HasMipTail ? 1 : 0)));
363376

364377
auto range_it = MipRanges.begin();
365-
Uint32 StartSlice = std::min(CurrArraySize, m_PendingSize);
366-
Uint32 EndSlice = std::max(CurrArraySize, m_PendingSize);
378+
Uint32 StartSlice = std::min(CurrResidentSize, m_PendingSize);
379+
Uint32 EndSlice = std::max(CurrResidentSize, m_PendingSize);
367380
for (Uint32 Slice = StartSlice; Slice != EndSlice; ++Slice)
368381
{
369382
// Bind normal mip levels
@@ -380,7 +393,7 @@ bool DynamicTextureArray::ResizeSparseTexture(IDeviceContext* pContext)
380393
range_it->MipLevel = Mip;
381394
range_it->Region = Box{0, MipProps.StorageWidth, 0, MipProps.StorageHeight, 0, MipProps.Depth};
382395

383-
if (Slice >= CurrArraySize)
396+
if (Slice >= CurrResidentSize)
384397
{
385398
const uint3 NumTilesInMip = GetNumSparseTilesInBox(range_it->Region, TexSparseProps.TileSize);
386399
range_it->pMemory = m_pMemory;
@@ -410,7 +423,7 @@ bool DynamicTextureArray::ResizeSparseTexture(IDeviceContext* pContext)
410423
range_it->MipLevel = TexSparseProps.FirstMipInTail;
411424
range_it->MemorySize = TexSparseProps.MipTailSize;
412425

413-
if (Slice >= CurrArraySize)
426+
if (Slice >= CurrResidentSize)
414427
{
415428
range_it->pMemory = m_pMemory;
416429
range_it->MemoryOffset = CurrMemOffset;
@@ -551,7 +564,7 @@ void DynamicTextureArray::CommitResize(IRenderDevice* pDevice,
551564
ResizeCommitted = true;
552565
}
553566

554-
if (ResizeCommitted)
567+
if (ResizeCommitted && m_PendingSize != CurrArraySize)
555568
{
556569
StoreArraySize(m_PendingSize);
557570

Tests/DiligentCoreAPITest/src/DynamicTextureArrayTest.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -289,9 +289,6 @@ TEST(DynamicTextureArray, ResizeToZeroBeforeInitialization)
289289
EXPECT_EQ(TextureArray.GetTexture(), nullptr);
290290
}
291291

292-
293-
294-
295292
class DynamicTextureArrayResizeTest : public testing::TestWithParam<std::tuple<USAGE, TEXTURE_FORMAT>>
296293
{
297294
};
@@ -439,6 +436,17 @@ TEST_P(DynamicTextureArrayResizeTest, Run)
439436
UpdateSlice(pContext, pTexture, 0);
440437
VerifySlices(pContext, pTexture, 0, 1);
441438

439+
if (Usage == USAGE_SPARSE)
440+
{
441+
ASSERT_EQ(pDynTexArray->GetArraySize(), DynTexArrCI.NumSlicesInMemoryPage);
442+
443+
// One requested slice still requires the same two-slice resident page,
444+
// so the resize completes without issuing an empty sparse bind.
445+
EXPECT_EQ(pDynTexArray->Resize(nullptr, pContext, 1), pTexture);
446+
EXPECT_FALSE(pDynTexArray->PendingUpdate());
447+
EXPECT_EQ(pDynTexArray->GetArraySize(), DynTexArrCI.NumSlicesInMemoryPage);
448+
}
449+
442450
pDynTexArray->Resize(pDevice, pContext, 2);
443451
pTexture = pDynTexArray->Update(nullptr, nullptr);
444452
EXPECT_EQ(pTexture, pDynTexArray->GetTexture());

0 commit comments

Comments
 (0)