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?
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);
>