1package drm
2
3import (
4 "bytes"
5 "testing"
6
7 "github.com/bnema/neferwl/internal/ports"
8 "github.com/bnema/zerowrap"
9 "github.com/stretchr/testify/mock"
10 "golang.org/x/sys/unix"
11)
12
13func TestScaledScanoutRectAndCachedRefusal(t *testing.T) {
14 o, _, commits := testOutput(t, unix.EINVAL)
15 o.cursor = nil
16 o.primary.formats = []ports.DMABufFormat{{Format: fourccXRGB}}
17 o.clientFBs[9] = &clientFB{fbID: 87}
18 s := ports.Scene{OutputWidth: 200, OutputHeight: 100, Windows: []ports.SceneWindow{{ID: 1, Rect: ports.Rect{W: 200, H: 100}, Fullscreen: true}}}
19 c := ports.SurfaceContent{ID: 1, Width: 100, Height: 50, LogicalW: 200, LogicalH: 100, Source: [4]float32{5, 2, 90, 46}, DMABuf: &ports.DMABuf{ID: 9, Format: fourccXRGB}}
20 var log bytes.Buffer
21 o.log = zerowrap.New(zerowrap.Config{Output: &log})
22 surfaces := map[ports.WindowID]ports.SurfaceContent{1: c}
23 if fb, _ := o.scanoutFrame(s, surfaces); fb != 0 || o.reason != "scale_refused" {
24 t.Fatalf("fb %d reason %s", fb, o.reason)
25 }
26 if len(*commits) != 1 || (*commits)[0].flags != atomicTestOnly {
27 t.Fatalf("test commits %+v", *commits)
28 }
29 if v, _ := (*commits)[0].req.value(tPrimary, planeProps["SRC_X"]); v != 5<<16 {
30 t.Fatalf("source x: %d", v)
31 }
32 if v, _ := (*commits)[0].req.value(tPrimary, planeProps["SRC_W"]); v != 90<<16 {
33 t.Fatalf("source width: %d", v)
34 }
35 if v, _ := (*commits)[0].req.value(tPrimary, planeProps["CRTC_W"]); v != 200 {
36 t.Fatalf("destination width: %d", v)
37 }
38 if fb, _ := o.scanoutFrame(s, surfaces); fb != 0 || len(*commits) != 1 {
39 t.Fatal("refused geometry retried")
40 }
41 if !bytes.Contains(log.Bytes(), []byte("scale_refused")) {
42 t.Fatalf("refusal not logged: %s", log.String())
43 }
44}
45
46func TestModesetInvalidatesScaleDecision(t *testing.T) {
47 o, k, commits := testOutput(t)
48 o.cursor = nil
49 o.primary.formats = []ports.DMABufFormat{{Format: fourccXRGB}}
50 fb := &clientFB{fbID: 87}
51 o.clientFBs[9] = fb
52 s := ports.Scene{OutputWidth: 200, OutputHeight: 100, Windows: []ports.SceneWindow{{ID: 1, Rect: ports.Rect{W: 200, H: 100}, Fullscreen: true}}}
53 c := ports.SurfaceContent{ID: 1, Width: 100, Height: 50, LogicalW: 200, LogicalH: 100, DMABuf: &ports.DMABuf{ID: 9, Format: fourccXRGB}}
54 contents := map[ports.WindowID]ports.SurfaceContent{1: c}
55 if got, _ := o.scanoutFrame(s, contents); got != 87 || len(*commits) != 1 || !fb.scaleTestedOK {
56 t.Fatalf("initial scale: %d, commits: %d", got, len(*commits))
57 }
58 k.EXPECT().createBlob(mock.Anything).Return(uint32(77), nil).Once()
59 if err := o.modeset(); err != nil {
60 t.Fatal(err)
61 }
62 if fb.scaleTestedOK || fb.scaleRefused {
63 t.Fatal("cached scale survived modeset")
64 }
65 if got, _ := o.scanoutFrame(s, contents); got != 87 || len(*commits) != 3 || (*commits)[2].flags != atomicTestOnly {
66 t.Fatalf("scale not retested: fb %d commits %+v", got, *commits)
67 }
68}
69
70func TestContentTypeFrameAndReset(t *testing.T) {
71 o, _, commits := testOutput(t)
72 o.cursor = nil
73 o.contentProp = 81
74 o.contentValues = [5]uint64{0, 1, 2, 3, 4}
75 s := ports.Scene{OutputWidth: 200, OutputHeight: 100, Windows: []ports.SceneWindow{{ID: 1, Rect: ports.Rect{W: 200, H: 100}, Fullscreen: true}}}
76 o.wantContent(s, map[ports.WindowID]ports.SurfaceContent{1: {ContentType: ports.ContentGame}})
77 for i := 0; i < 2; i++ {
78 if err := o.commitFrame(70, nil, false, false, pendingFrame{}); err != nil {
79 t.Fatal(err)
80 }
81 }
82 if v, ok := (*commits)[0].req.value(tConn, 81); !ok || v != 4 {
83 t.Fatalf("game hint: %d %v", v, ok)
84 }
85 if _, ok := (*commits)[1].req.value(tConn, 81); ok {
86 t.Fatal("hint repeated")
87 }
88 o.wantContent(ports.Scene{}, nil)
89 if err := o.commitFrame(70, nil, false, false, pendingFrame{}); err != nil {
90 t.Fatal(err)
91 }
92 if v, ok := (*commits)[2].req.value(tConn, 81); !ok || v != 0 {
93 t.Fatalf("reset hint: %d %v", v, ok)
94 }
95 o.contentProp = 0
96 o.wantContent(s, map[ports.WindowID]ports.SurfaceContent{1: {ContentType: ports.ContentGame}})
97 if err := o.commitFrame(70, nil, false, false, pendingFrame{}); err != nil {
98 t.Fatal(err)
99 }
100 if _, ok := (*commits)[3].req.value(tConn, 81); ok {
101 t.Fatal("absent property set")
102 }
103}