Skip to content

Fix DX12 tight alignment#2830

Merged
locke-lunarg merged 4 commits intoLunarG:devfrom
locke-lunarg:locke-fix-tight-aglin
Apr 7, 2026
Merged

Fix DX12 tight alignment#2830
locke-lunarg merged 4 commits intoLunarG:devfrom
locke-lunarg:locke-fix-tight-aglin

Conversation

@locke-lunarg
Copy link
Copy Markdown
Contributor

@locke-lunarg locke-lunarg commented Mar 30, 2026

closed: https://github.com/LunarG/Projects/issues/1125
Ref: https://microsoft.github.io/DirectX-Specs/d3d/D3D12TightPlacedResourceAlignment.html

Regularly, the alignment of the resource should be D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT(64KB). But when it uses tight alignment, it becomes 8B ~ 256B. It's smaller than a page size(system_page_size_: 4KB). In this case, one page could have two resources. For WriteWatch case, in LoadActiveWriteStates, if it resets WriteWatch on GetWriteWatch, another resource on the same page will miss its update. Although GetWriteWatch could set precise address, OS operates on page by page. It shouldn't have reset WriteWatch until all memory infos get updated.

There are the other two solutions that could fix it.

  1. Force ID3D12Device_Wrapper::GetResourceAllocationInfo to return alignment to multiple of a page size, so a page won't have two resources. But we shouldn't change the original value.

  2. GFXRECON_PAGE_GUARD_EXTERNAL_MEMORY set false could disable WriteWatch. It will use shadow memory, instead of WriteWatch. But shadow memory consumes huge memory. And PageGuardManager::HandleGuardPageViolation interrupts the process to update memory info. It could impact the performance.

@locke-lunarg locke-lunarg added approved-to-run-ci Can run CI check on internal LunarG machines and removed approved-to-run-ci Can run CI check on internal LunarG machines labels Mar 30, 2026
Comment thread framework/util/page_guard_manager.cpp Outdated
Comment on lines +1330 to +1336
#if defined(WIN32)
for (auto& entry : memory_info_)
{
auto& info = entry.second;
ResetWriteWatch(info.aligned_address, info.mapped_range + info.aligned_offset);
}
#endif
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this introduce a race condition in processing modified pages? For example:

  1. PageGuardManager::ProcessMemoryEntries is called. Memory A is found to be modified and is processed. ProcessMemoryEntries continues to processing the remaining modified pages.
  2. After memory A is processed, but before ProcessMemoryEntries calls ResetAllWriteWatch(); (it could be processing other modified memory pages), the application completes another write to memory A.
  3. ResetAllWriteWatch(); is called, clearing the new write watch flag on memory A, causing the application's latest write to be skipped at the next call to ProcessMemoryEntries.

@locke-lunarg locke-lunarg force-pushed the locke-fix-tight-aglin branch 5 times, most recently from 3129b5d to fd0628c Compare March 31, 2026 06:40
@locke-lunarg
Copy link
Copy Markdown
Contributor Author

locke-lunarg commented Mar 31, 2026

@davidd-lunarg Upload a new solution. It updates the memory info on the same page in PageGuardManager::LoadActiveWriteStates, so it doesn't need to delay reset WriteWatch.

@locke-lunarg locke-lunarg marked this pull request as ready for review March 31, 2026 06:44
@locke-lunarg locke-lunarg requested a review from a team as a code owner March 31, 2026 06:44
@locke-lunarg locke-lunarg added the approved-to-run-ci Can run CI check on internal LunarG machines label Mar 31, 2026
Copy link
Copy Markdown
Contributor

@MarkY-LunarG MarkY-LunarG left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I think this is a bug that has been bugging us for a while. So good thing you ran into it in this scenario.

@davidd-lunarg
Copy link
Copy Markdown
Contributor

@davidd-lunarg Upload a new solution. It updates the memory info on the same page in PageGuardManager::LoadActiveWriteStates, so it doesn't need to delay reset WriteWatch.

Thanks! The changes look good to me. Since this touches code paths in the PageGuardManager that are used by all APIs and platforms during capture, it should be well-tested before merging. A couple other questions I have are:

  1. Does this have any significant affect on performance or capture file size?
  2. A while back, GFXRECON_PAGE_GUARD_ALIGN_BUFFER_SIZES was set to true by default for Vulkan (PR Make GFXRECON_PAGE_GUARD_ALIGN_BUFFER_SIZES true by default #1431 and Issue Make GFXRECON_PAGE_GUARD_ALIGN_BUFFER_SIZES true by default #1228). Does this change that requirement? FYI @bradgrantham-lunarg
  3. Does this have any impact or side effects for capture of aliased/overlapping resources?

@MarkY-LunarG
Copy link
Copy Markdown
Contributor

  1. A while back, GFXRECON_PAGE_GUARD_ALIGN_BUFFER_SIZES was set to true by default for Vulkan (PR Make GFXRECON_PAGE_GUARD_ALIGN_BUFFER_SIZES true by default #1431 and Issue Make GFXRECON_PAGE_GUARD_ALIGN_BUFFER_SIZES true by default #1228). Does this change that requirement? FYI @bradgrantham-lunarg

Yes, I believe so. I think @ziga-lunarg was the one who ran into issues and suggested forcing it.

@davidd-lunarg
Copy link
Copy Markdown
Contributor

  1. A while back, GFXRECON_PAGE_GUARD_ALIGN_BUFFER_SIZES was set to true by default for Vulkan. Does this change that requirement?

Looking into it more, I think GFXRECON_PAGE_GUARD_ALIGN_BUFFER_SIZES is still required for default Vulkan memory capture mode (page guard + shadow memory) to avoid application read and write to the same memory page from causing corruption. This change might make it possible to disable GFXRECON_PAGE_GUARD_ALIGN_BUFFER_SIZES when using GFXRECON_PAGE_GUARD_EXTERNAL_MEMORY. GFXRECON_PAGE_GUARD_EXTERNAL_MEMORY is the default mode for DX12 but it is only available on Windows.

I don't think this PR should make any changes to GFXRECON_PAGE_GUARD_ALIGN_BUFFER_SIZES, but I did want to make note of it.

When it uses DX12 tight alignment, the alignment could be 8B~256B.
It's much smaller than a page of memory, like 4KB.
One page could have two resources, so if it reset the WriteWatch, the
another resource on the same page will miss its update.
1. rename page_to_memory_infos_ to page_to_memory_infos_for_write_watch_.

2, Check use_write_watch before using.

3. Only add once by cheking entry.second

4. Add assert to check bounds
@locke-lunarg locke-lunarg force-pushed the locke-fix-tight-aglin branch from d91c8b3 to 664bb2e Compare April 7, 2026 00:35
@locke-lunarg
Copy link
Copy Markdown
Contributor Author

@davidlunarg Fixed the bug that you messaged to me.

#2853 We can revisit this issue later.

@locke-lunarg locke-lunarg merged commit b21fe07 into LunarG:dev Apr 7, 2026
10 checks passed
@panos-lunarg
Copy link
Copy Markdown
Contributor

Recently I encountered a problem that sounds very similar to what is described in the original message by Locke.
An application updates a memory region (uploads vertex data) with 2 different methods:

  1. Two vkCmdCopyBuffer
  2. By directly writing to the mapped memory (caught by page_guard)

The problem is that the FillMemoryCommand blocks have both size and alignment of the system's page size and as a result they clobber the writes done by the CmdCopyBuffers. The following image should help visualize the problem:

573481307-795f5f3d-d2c0-4786-ab2b-58d8fbfb5b5e

Does this PR have the potential to solve this problem too?

bartosz-muszarski-arm pushed a commit to ARM-software/gfxreconstruct-arm that referenced this pull request Apr 13, 2026
* Reset WriteWatch later

When it uses DX12 tight alignment, the alignment could be 8B~256B.
It's much smaller than a page of memory, like 4KB.
One page could have two resources, so if it reset the WriteWatch, the
another resource on the same page will miss its update.

* Revert "Reset WriteWatch later"

This reverts commit 7df7643.

* Update all memory info on the page

* Bugfix

1. rename page_to_memory_infos_ to page_to_memory_infos_for_write_watch_.

2, Check use_write_watch before using.

3. Only add once by cheking entry.second

4. Add assert to check bounds

Change-Id: Ib2f0f08836daea38e1b8cf34836e52fc2c5e2ee8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved-to-run-ci Can run CI check on internal LunarG machines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants