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

Reply via email to