On 2026-06-24 11:52, Christian König wrote:
> On 6/24/26 17:30, Harry Wentland wrote:
>> On 2026-06-16 03:31, Christian König wrote:
>>> On 6/16/26 09:10, Matthew Schwartz wrote:
>>>> Native scanout buffers on APUs are pinned with the VRAM|GTT domain, so
>>>> under VRAM carveout pressure a swapchain can end up split across VRAM and
>>>> GTT. The scanout buffer's memory type then changes from one flip to the
>>>> next, and amdgpu_dm_crtc_mem_type_changed() rejects an async page flip
>>>> across the change. The result is repeated async page flip failures,
>>>> observed as choppy updates under carveout pressure, until the buffers
>>>> reconverge to a single domain.
>>>
>>> That's intentional behavior.
>>>
>>>> Pin native scanout buffers in VRAM only so the swapchain stays in one
>>>> memory domain. Restrict this to APUs whose carveout is larger than
>>
>> Above you mention that under VRAM pressure a swapchain can end up split
>> across VRAM and GTT. Wouldn't restricting the swapchain to VRAM now mean
>> that in those cases you fail to allocate the swapchain entirely?
>
> Yes, exactly that.
>
> My educated guess is that the display server then falls back to using a copy
> instead of a flip and that helps saving memory somehow (e.g. less scanout
> buffers alocated concurrently).
>
> Would it somehow be possible to get DC to dynamically switch between VRAM and
> GTT?
>
DCN can't switch between mapped and unmapped memory. I'm not a memory
management expert but wouldn't GTT be in GART (mapped) and VRAM in
the (unmapped) FB aperture?
>From DCHUB HW doc:
"No change from mapped to unmapped or unmapped to mapped is
allowed for immediate f lip"
If so, we can't async flip between them.
Harry
> Regards,
> Christian
>
>>
>> Harry
>>
>>>> AMDGPU_SG_THRESHOLD, so small-carveout parts keep their existing VRAM|GTT
>>>> placement, and fall back to GTT when the buffer does not fit in VRAM, so
>>>> the flip still succeeds and the swapchain stays in one domain. Imported
>>>> buffers may only be pinnable in GTT, so leave those on the default
>>>> domains.
>>>
>>> The display guys need to take a closer look at that, but it sounds like
>>> what we used to have before and that caused problems.
>>>
>>> We somehow need to change the DC stuff to allow switching between VRAM and
>>> GTT frame buffers to fully fix this.
>>>
>>> Regards,
>>> Christian.
>>>
>>>>
>>>> Signed-off-by: Matthew Schwartz <[email protected]>
>>>> ---
>>>> Hi,
>>>>
>>>> This came up while testing my kernel patch to fix mem_type detection for
>>>> async flips here:
>>>> https://lore.kernel.org/amd-gfx/[email protected]/
>>>>
>>>> I found a new issue where splitting a swapchain between VRAM and GTT
>>>> causes a noticeable stutter in gameplay if gamescope is using direct
>>>> scanout and tearing is enabled while a game is already running.
>>>>
>>>> Once a swapchain is split across the VRAM carveout and GTT, the scanout
>>>> buffer's mem_type changes from one flip to the next, so
>>>> amdgpu_dm_crtc_mem_type_changed() rejects the async flip. Under direct
>>>> scanout with tearing that rejection recurs every time the displayed buffer
>>>> crosses domains, which is what surfaces as the choppiness.
>>>>
>>>> With this patch, I can enable tearing on top of an already-disabled frame
>>>> limit mid-game and no longer reproduce the choppiness.
>>>>
>>>> amdgpu_gem_info confirms the swapchain converges to a single domain
>>>> instead of splitting across VRAM and GTT.
>>>>
>>>> Before:
>>>> 0x00000f81: 3981312 byte GTT exported as ino:275 NO_CPU_ACCESS
>>>> CPU_GTT_USWC VRAM_CLEARED VRAM_CONTIGUOUS EXPLICIT_SYNC write
>>>> fence:drm_sched gfx_0.0.0 seq 88248 signalled
>>>> 0x00000f82: 3981312 byte GTT exported as ino:276 NO_CPU_ACCESS
>>>> CPU_GTT_USWC VRAM_CLEARED VRAM_CONTIGUOUS EXPLICIT_SYNC write
>>>> fence:drm_sched gfx_0.0.0 seq 88224 signalled
>>>> 0x00000f83: 3981312 byte VRAM VISIBLE pin count 1 exported as ino:277
>>>> NO_CPU_ACCESS CPU_GTT_USWC VRAM_CLEARED VRAM_CONTIGUOUS EXPLICIT_SYNC
>>>> write fence:drm_sched gfx_0.0.0 seq 88236 signalled
>>>>
>>>> After:
>>>> 0x00000f82: 3981312 byte VRAM VISIBLE pin count 1 exported as ino:548
>>>> NO_CPU_ACCESS CPU_GTT_USWC VRAM_CLEARED VRAM_CONTIGUOUS EXPLICIT_SYNC
>>>> write fence:drm_sched gfx_0.0.0 seq 822258 signalled
>>>> 0x00000f83: 3981312 byte VRAM VISIBLE exported as ino:549
>>>> NO_CPU_ACCESS CPU_GTT_USWC VRAM_CLEARED VRAM_CONTIGUOUS EXPLICIT_SYNC
>>>> write fence:drm_sched gfx_0.0.0 seq 822255 signalled
>>>> 0x00000f84: 3981312 byte VRAM VISIBLE exported as ino:550
>>>> NO_CPU_ACCESS CPU_GTT_USWC VRAM_CLEARED VRAM_CONTIGUOUS EXPLICIT_SYNC
>>>> write fence:drm_sched gfx_0.0.0 seq 822261 signalled
>>>>
>>>> Does this seem like the correct approach to take for fixing the observed
>>>> issue? I wanted to start with an RFC to make sure I didn't overlook
>>>> anything obvious or miss any better methods of fixing this.
>>>>
>>>> Thanks,
>>>> Matt
>>>> ---
>>>> .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 29 +++++++++++++++++--
>>>> 1 file changed, 26 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
>>>> b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
>>>> index 23a9faa2ea89..b99f938e58ec 100644
>>>> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
>>>> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
>>>> @@ -932,6 +932,7 @@ static int amdgpu_dm_plane_helper_prepare_fb(struct
>>>> drm_plane *plane,
>>>> struct amdgpu_bo *rbo;
>>>> struct dm_plane_state *dm_plane_state_new, *dm_plane_state_old;
>>>> uint32_t domain;
>>>> + bool pin_vram_only;
>>>> int r;
>>>>
>>>> if (!new_state->fb) {
>>>> @@ -958,13 +959,35 @@ static int amdgpu_dm_plane_helper_prepare_fb(struct
>>>> drm_plane *plane,
>>>> if (r)
>>>> goto error_unlock;
>>>>
>>>> - if (plane->type != DRM_PLANE_TYPE_CURSOR)
>>>> - domain = amdgpu_display_supported_domains(adev, rbo->flags);
>>>> - else
>>>> + /*
>>>> + * Pin native scanout in VRAM on APUs so a swapchain stays in one
>>>> + * memory domain. A VRAM/GTT split changes its mem_type between flips
>>>> + * and amdgpu_dm_crtc_mem_type_changed() rejects the async flip. Skip
>>>> + * small carveouts that may not fit, and imported buffers.
>>>> + */
>>>> + pin_vram_only = plane->type != DRM_PLANE_TYPE_CURSOR &&
>>>> + (adev->flags & AMD_IS_APU) &&
>>>> + !rbo->tbo.base.import_attach &&
>>>> + adev->gmc.real_vram_size > AMDGPU_SG_THRESHOLD;
>>>> +
>>>> + if (plane->type == DRM_PLANE_TYPE_CURSOR || pin_vram_only)
>>>> domain = AMDGPU_GEM_DOMAIN_VRAM;
>>>> + else
>>>> + domain = amdgpu_display_supported_domains(adev, rbo->flags);
>>>>
>>>> rbo->flags |= AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS;
>>>> r = amdgpu_bo_pin(rbo, domain);
>>>> + if (r == -ENOMEM && pin_vram_only) {
>>>> + /*
>>>> + * VRAM could not fit the buffer. Fall back to GTT where
>>>> + * allowed so the swapchain stays in one domain.
>>>> + */
>>>> + domain = amdgpu_display_supported_domains(adev, rbo->flags);
>>>> + if (domain & AMDGPU_GEM_DOMAIN_GTT) {
>>>> + domain = AMDGPU_GEM_DOMAIN_GTT;
>>>> + r = amdgpu_bo_pin(rbo, domain);
>>>> + }
>>>> + }
>>>> if (unlikely(r != 0)) {
>>>> if (r != -ERESTARTSYS)
>>>> DRM_ERROR("Failed to pin framebuffer with error %d\n",
>>>> r);
>>>
>>
>