[Treeland] Dp25.3.0+treeland 开机/重启后分辨率变小画面只占左上角 分析
Tofloor
poster avatar
琴软
deepin
6 hours ago
Author

Deepin 25.3.0+treeland 开机/重启后分辨率变小、画面只占左上角 —— 完整诊断与修复报告

适用对象:Deepin 25 (ostree 不可变系统 + treeland 合成器架构)
症状关键词:开机分辨率变小、重启后要手动调回分辨率、登录界面显示巨大、桌面只占屏幕左上角一小块、拔插 HDMI 线就能恢复
文中所有 【替换】 标记处,请改成你自己机器的对应信息。


一、问题表现

机器上有两块屏:内屏(笔记本屏,1920x1080)+ 外接 4K 显示器(3840x2160@60,仅使用外接屏)。

故障时 phenomena 分两种,且经常交替出现:

  1. 登录界面巨大:图标和文字被放大数倍,像 720x400 分辨率在 4K 屏上被拉伸。
  2. 登录后桌面只占左上角 1/4:分辨率显示是 3840x2160,但壁纸/图标/任务栏只落在屏幕左上角一块 1920x1080 的方块里,四周黑边。

共同点:每次冷开机/重启都出现,手动在控制中心改一次分辨率就恢复,下次开机又复发。拔插 HDMI 线也能恢复。

二、运行环境

项 本机值 说明
系统 Deepin 25(ostree 不可变模式,/usr 只读) cat /etc/os-release、deepin-immutable-ctl status 查看
显示服务器 treeland(Wayland 合成器)+ ddm 登录管理器 不是 lightdm/X11,systemctl status treeland.service
GPU AMD 核显(amdgpu 驱动) lspci -k | grep -A3 VGA
显示器 【替换】你的显示器型号与分辨率,如"A 型 32 寸 4K" 老显示器 HDMI VSDB 只报 280MHz 的,属于高危
内屏 【替换】有/无内屏(笔记本=有) 有内屏是"幻影屏"问题的前提之一
内核 【替换】uname -r 的输出 Deepin 25 是 rolling 内核,版本会变,别硬编码

三、判断方法(先分阶段,别瞎猜)

这类问题横跨四层:GRUB → 内核(EDID/DRM) → 合成器(treeland) → 显示布局(dde-daemon)。
每一层都有独立判据。我这次排查最大的教训就是:探针取值方式错了,连续三轮把病灶误判在内核层,而真实问题一半在布局、一半在 greeter 配置。

3.0 先确认是"哪种小"

现象 根因层 能否修
只有开机 logo / GRUB 菜单模糊,进桌面正常 UEFI GOP 模式受限 基本不能
登录界面巨大 / 桌面只占左上角 合成器 + 布局(本报告) ✅ 可修
分辨率对但字太小 缩放 scale 掉了 改 scale
只显示一块屏内容另一块黑 布局问题 ✅ 可修

3.1 排除 GRUB 这条路(很多人第一步就走错)

改 GRUB_GFXMODE 在 Deepin 25 上走不通,三条证据:

# 1) /etc/default/grub.d/11_dde.cfg 会覆盖你改的 /etc/default/grub
cat /etc/default/grub.d/11_dde.cfg

# 2) 改完会被 deepin-grub2 服务整条覆盖回来
journalctl -b | grep -i "grub-gfx"
# 会出现: invalid gfxmode "3840x2160,1920x1080" —— 它的正则只接受单个 宽x高

# 3) 决定性证据:GRUB 阶段根本没有 4K 可选(UEFI GOP 限制)
gdbus call --system --dest org.deepin.dde.Grub2 \
  --object-path /org/deepin/dde/Grub2 \
  --method org.deepin.dde.Grub2.GetAvailableGfxmodes

同时确认内核侧其实是好的(fb0 已经是 4K,说明 GRUB 只影响菜单那 1 秒):

cat /sys/class/graphics/fb0/virtual_size    # 3840,2160 → 内核 fb 层没问题

3.2 内核层:确认 EDID 竞态(真正根因之一)

dde-daemon 的显示器 UUID = @HDMI-A-1||v1,其中 hash = md5(EDID 的前 128 字节)。

# 算当前 EDID 的 UUID 哈希(注意:是前 128 字节,不是整份 256 字节!)
head -c 128 /sys/class/drm/card0-【替换:HDMI-A-1】/edid | md5sum

# 看显示配置里存了哪些 UUID —— 大于 1 套 = EDID 读取过不同结果,即 EDID 不稳
sudo grep -o '@HDMI-A-[0-9]*|[0-9a-f]\{32\}' /var/lib/dde-daemon/display/config.json | sort -u

完整故障链(这条链解释了所有现象):

冷启动 DDC 读 EDID,个别字节被读坏
   → md5 变成配置里不存在的"另一套 UUID"
   → config.json 查不到该 UUID → dde 回退默认值:低分辨率 + 默认布局
   → 登录界面被放大("很大")、桌面只画左上角一小块
   → 手动改分辨率 / 拔插 HDMI 时 EDID 重读成功 → 恢复,但下次冷启动再来一遍

3.3 合成器层:treeland(greeter)的输出配置

Deepin 25 的登录界面由系统级 treeland.service 渲染(运行身份 User=dde,uid 990,HOME=/var/lib/ddm)。关键坑:全机只有一个真合成器,登录后的 dde-shell/treeland-sd 只是客户端。它的日志默认 StandardOutput=null,journal 里一条都没有——先打开日志:

sudo mkdir -p /etc/systemd/system/treeland.service.d
# drop-in 内容:StandardOutput=journal / StandardError=journal
sudo systemctl daemon-reload && sudo systemctl restart treeland.service  # 会结束当前会话!
journalctl -u treeland.service -b
# 若看到 "Ignoring invalid output dconfig for ..." 说明 greeter 的输出配置没写对/没写上

3.4 布局层:检查"幻影屏"(桌面只占左上角的直接原因)

一条命令定性:

gdbus call --system --dest org.deepin.dde.Display1 \
  --object-path /org/deepin/dde/Display1 --method org.deepin.dde.Display1.GetConfig

# 对照物理现实:
for d in /sys/class/drm/card*-*/; do
  printf "%-18s %-12s %s\n" "$(basename $d)" "$(cat $d/status)" "$(cat $d/enabled)"; done

典型错误配置(本机实测):

DisplayMode = 2   (2=扩展)
组合屏组 Extend:  eDP      1920x1080 (0,0)    主屏=True   ← 物理上 enabled=disabled(幻影屏!)
                  HDMI     3840x2160 (1920,0) 副屏

物理只开 1 块屏、配置里开着 2 块 → 按"主屏尺寸"布点的 dde-shell/桌面/任务栏只落在 (0,0) 那块 1920x1080 的方块里。**DisplayMode 取值:1=镜像 2=扩展 3=仅一个屏幕。**

另外注意:Display1 是按需激活的 D-Bus 服务,会话早期(登录后 3~23 秒)一定不在位,会话组件不会因它后来上线而自动重排——这就是"每次开机都要手动改一次"的机制。让保存的布局等于物理现实是唯一稳定解法。

四、修复方法(四步,每步可独立回滚)

第 1 步:强制 EDID(固定指纹,治"UUID 漂移")

原理:把一份好的 EDID 固定下来,内核每次启动都用它 → 前 128 字节恒定 → UUID 恒定 → 永远命中同一份配置。

前提检查:

zcat /proc/config.gz | grep CONFIG_DRM_LOAD_EDID_FIRMWARE   # 必须 =y
grep MODULES /etc/initramfs-tools/initramfs.conf            # 应为 most

先抓一份好 EDID(在显示正常时抓):

sudo cp /sys/class/drm/card0-【替换:HDMI-A-1】/edid /etc/firmware-edid/【替换:my-monitor.bin】

三件套:

  1. EDID 副本 → /etc/firmware-edid/【替换:my-monitor.bin】
    (放 /etc 而不是 /lib/firmware:ostree 的 /usr 只读,/etc 可写且升级保留)
  2. initramfs hook → /etc/initramfs-tools/hooks/edid-hdmi,把文件拷进 ${DESTDIR}/lib/firmware/edid/
  3. 内核参数 → /etc/default/grub 的 GRUB_CMDLINE_LINUX_DEFAULT 追加:
drm.edid_firmware=【替换:HDMI-A-1:my-monitor.bin】 firmware_class.path=/etc/firmware-edid

为什么需要 firmware_class.path=:实测 Deepin 25 上 amdgpu 不是在 initramfs 阶段加载,
而是 switch_root 后由 udev 从真实根 modprobe。那一刻固件搜索路径是真实的 /lib/firmware,
而 /usr(/lib 指向它)是只读镜像,里面没有你塞的文件 → failed to load edid/xxx.bin (-2)。
firmware_class.path 让内核先去 /etc/firmware-edid 找,绕开只读的 /usr。

然后 sudo update-grub && sudo update-initramfs -u -k all,重启。

第 2 步:裁掉 EDID 里的垃圾低分辨率模式(治"合成器落到 720x400")

EDID 里除了 4K,还声明了一堆 720x400/640x480/1024x768 等小模式(established/standard timings + CTA 扩展里的 VIC)。模式列表按 4K 在前、垃圾在尾部排列;treeland 在没有合法输出配置时会落到列表末尾 → 720x400 → 4K 屏放大 5 倍 = 登录界面巨大。

裁剪思路(对标准 EDID 通用,Python 改字节即可):

# base block
b[0x23]=b[0x24]=b[0x25]=0                    # established timings 清零 → 去掉 720x400@70/640x480/800x600/1024x768
for i in range(8):                           # standard timings 全写 unused
    b[0x26+i*2]=b[0x26+i*2+1]=0x01
# DTD 区 0x36-0x7E 一字不动(DTD1=首选分辨率,还有 monitor name/range 描述符,动了坏事)
b[127] = (0x100 - sum(b[:127]) & 0xFF) & 0xFF   # 重算 base 校验和

# CTA 扩展块(128~255 字节):Video Data Block 只留 4K 的 VIC(HDMI 下 VIC 93~97)
# 删掉所有 CTA-DTD 小模式;保留 HDMI VSDB / HDMI Forum VSDB(后者声明 Max TMDS 600MHz,
# 删了内核会按 HDMI 1.4 的 280MHz 拒掉 4K60);重算 c[127] 校验和

裁完必须核验:① 两段校验和都对;② DTD 时序区一字未改(用 cmp -l 看差异位置);③ 新 EDID 的 md5 与旧不同。

⚠️ 裁 EDID 会改变 md5 → dde 显示器 UUID 变 → 已存配置全部失配,必须和第 3 步一起做,不能只裁一半。

第 3 步:重写显示布局(治"幻影屏",画面只占左上角)

用 Display1.GetConfig → 只改目标字段 → SetConfig 原样发回(round-trip 是关键,手搓 JSON 极易被 schema 拒掉)。要改四处:

  1. DisplayMode = 3(仅一个屏幕)
  2. 单屏组里强制外接屏:Enabled=true, X=0, Y=0, Primary=true
  3. 各屏组的 OnlyOneUuid 指向裁剪后新 EDID 的 UUID(有多种 EDID 指纹时逐个屏组处理)
  4. 扩展屏组里把内屏 Enabled=false、外接屏挪回 (0,0)(双保险)

改完验证:物理已启用屏数 == 配置里启用屏数。

第 4 步:给 greeter 写输出配置(治"登录界面巨大")

DConfig override 的位置和格式两个都极易写错:

❌ 错误 ✅ 正确
位置 $HOME/.local/share/dsg/configs/...(这是 dconfig-daemon 的缓存,应用不读) $HOME/.config/dsg/configs/overrides//.json
格式 整份复制 schema magic: "dsg.config.override" + contents 下每键带元数据(见下)

greeter 的 home 是 /var/lib/ddm,所以目标路径:

/var/lib/ddm/.config/dsg/configs/overrides/org.deepin.dde.treeland/org.deepin.dde.treeland.output.json

格式(每个键从 /usr/share/dsg/configs/org.deepin.dde.treeland/ 的 schema 整份拷贝后只改 value):

{
    "magic": "dsg.config.override",
    "version": "1.0",
    "contents": {
        "width":   { "value": 【替换:3840】,  "serial": 0, "flags": [], "permissions": "readwrite", "visibility": "public" },
        "height":  { "value": 【替换:2160】,  "serial": 0, "flags": [], "permissions": "readwrite", "visibility": "public" },
        "refresh": { "value": 【替换:60000】, "serial": 0, "flags": [], "permissions": "readwrite", "visibility": "public" },
        "scale":   { "value": 【替换:1.75】,  "serial": 0, "flags": [], "permissions": "readwrite", "visibility": "public" }
    }
}

注意:refresh 单位是 mHz(60Hz = 60000);width/height/refresh = 0 表示"从未保存过模式"。
文件属主 dde:dde,权限 a+rX。appid/resource 名以本机 /usr/share/dsg/configs/org.deepin.dde.treeland/ 下的 schema 为准。

五、验证方法

5.1 逐项体检(重启后跑一遍)

  1. EDID 是否被内核采用:
cat /sys/module/drm/parameters/edid_firmware   # 非 (null) = 内核真加载了
dmesg | grep -i "failed to load edid"          # 空 = 加载成功
  1. 指纹是否归一(应从多套变一套):
sudo grep -o '@HDMI-A-[0-9]*|[0-9a-f]\{32\}' /var/lib/dde-daemon/display/config.json | sort -u
  1. 布局是否等于现实(DisplayMode=3、唯一屏 (0,0) 主屏、物理/配置启用数一致)
  2. greeter 配置文件在位(/var/lib/ddm/.config/dsg/configs/overrides/...,键齐全)

5.2 开机全程采样(取证"故障只存在开机几十秒内")

事后用 wlr-randr 看到的永远是修好的值,必须在开机时采样。装一个 systemd oneshot 探针(After=treeland.service,每秒记录一次),重启后回看 60 秒曲线:

cat /sys/class/graphics/fb0/virtual_size   # 每秒都应稳定在 3840,2160,无跳变

合格的判据:fb0 从第 1 秒起就是目标分辨率且全程无跳变。

5.3 验证时的三个"噪音",别误判为故障

  1. journal 里旧的 failed to load edid (-2) 记录来自历史启动;本次启动 fb0 正常即说明成功。
  2. dde-dconfig -u 990 get ... 读 greeter 配置报 "No Permission" 只是该 CLI 的读取权限限制,treeland 读 override 文件不受影响。
  3. 探针里 HDMI-A-1@crtc-N=-(取不到模式)是 sysfs/debugfs 解析的正常现象,分辨率以 fb0 为准。

六、踩坑清单(每条都真实付出过时间)

  1. 改 GRUB_GFXMODE 必被覆盖:deepin-grub2 服务的正则只接受单个 宽x高,写逗号列表被判非法后整条回写。且 UEFI GOP 本身就给不出 4K,这条路不通。
  2. cmp 强制EDID /sys/.../edid 是无效判据:两者本来就该相等,据此判断"已生效"会误判。唯一有效判据是 dmesg 里有无 failed to load edid。
  3. EDID 塞进 initramfs 无效:amdgpu 在真实根阶段才加载,固件搜索路径是只读的 /usr;必须用 firmware_class.path=。
  4. 校验 initrd 必须用 uname -r 那份:ls /boot/initrd.img-* | head -1 按字母序会校验错内核,得出假通过。
  5. [ -s /sys/.../edid ] 永远为假:sysfs 文件 stat 大小恒为 0。只能 [ -e ] + cat 后数长度。
  6. 算 UUID 哈希必须用前 128 字节:用整份 256 字节的 md5 对不上任何已存 UUID,会误以为出现了"第三套指纹"。
  7. 取 crtc 实际模式必须按 connector→crtc 映射:debugfs 里直接抓第一个 mode 拿到的常是物理关闭的内屏 CRTC(720x400 文本模式),会把整个排查方向带偏。
  8. fb0 是滞后指标:尺寸在 fbdev 创建时定死,不跟随后续模式变化;它和 debugfs 矛盾时先怀疑自己的取值方式。
  9. busctl --system list-names 不是有效子命令:静默无输出,会误判"服务不存在"。用 NameHasOwner 或 busctl list。
  10. dde-dconfig set 只写存储不通知运行中的桌面:改缩放要走会话 DBus(org.deepin.dde.Appearance1.SetScaleFactor)。
  11. DConfig override 位置/格式写错不报错但无效:必须在 .config/dsg/configs/overrides/ 下且带 magic+元数据。

七、回滚与兜底

  • 黑屏时:GRUB 菜单按 e,删掉含 drm.edid_firmware= 的那段,Ctrl+X 启动。内核找不到固件只会打 warning 后回退真实 EDID,不会变砖。
  • 完整回滚:删 initramfs hook + 清掉 /etc/default/grub 里加的两个参数 + 删 greeter override 文件 + 恢复 config.json 备份 + sudo update-grub && sudo update-initramfs -u -k all。

八、物理层建议(不改配置的那部分)

老 4K 显示器(HDMI VSDB Max TMDS 只报 280MHz)的链路处在临界点,冷启动极易握手失败。即使做完上面全部修复,也建议:

  • 换 HDMI 2.0 认证短线(≤2m)
  • 显示器保持待机、不要彻底断电
  • 先开显示器再开主机

附:需要替换的本机信息汇总

占位 去哪查
HDMI-A-1(连接器名) ls /sys/class/drm/ 里 status=connected 的那个
my-monitor.bin(EDID 文件名) 随意,与内核参数保持一致即可
3840x2160@60(目标分辨率) cat /sys/class/drm/card0-*/modes 里你的首选
refresh 的 mHz 值 目标刷新率 × 1000(60Hz → 60000)
greeter uid / HOME systemctl cat treeland.service 看 User=,getent passwd <用户名>
scale 值 控制中心里你习惯的缩放倍数

以上是我在 Deepin 25 上从误判到确诊、跨 GRUB/内核/合成器/布局四层排查的完整记录。如果你的现象是"登录界面巨大"或"桌面只占左上角",建议直接从 3.4 的幻影屏检查入手——那是最快能定性的一条命令。

Reply Favorite View the author
All Replies
avatar
𰻞𰻝面
deepin
4 hours ago
#1

我在25.1.1时遇到过,明明是1280x800的屏,显示的应该是1920x1200的分辨率,还无法调整。屏幕显示的东西非常小,但截图时会变成只有左上角显示

Reply View the author