v0.2.0 13c6a90

targets_test.go RawHistoryFolder

1package vulkan
2
3import (
4	"image/color"
5	"math"
6	"testing"
7
8	"github.com/bnema/neferwl/internal/ports"
9	vk "github.com/bnema/purego-vulkan/vulkan"
10)
11
12// Frames drawn into exported targets reach their dmabufs: another renderer
13// importing a target (as KMS scans it out) sees the frame, and readback
14// reads the target drawn last.
15func TestExportTargets(t *testing.T) {
16	r, err := New(64, 32)
17	if err != nil {
18		t.Skipf("Vulkan unavailable: %v", err)
19	}
20	defer r.Close()
21	bufs, err := r.ExportTargets(2, nil)
22	if err != nil {
23		t.Skipf("no exportable targets: %v", err)
24	}
25	defer func() {
26		for _, b := range bufs {
27			b.Planes[0].File.Close()
28		}
29	}()
30	if len(bufs) != 2 || bufs[0].Width != 64 || bufs[0].Format != fourccXRGB || bufs[0].Planes[0].Stride < 64*4 {
31		t.Fatalf("targets %+v", bufs)
32	}
33	colors := []string{"#ff0000", "#0000ff"}
34	for i, c := range colors {
35		r.UseTarget(i)
36		if err := render(r, ports.Scene{Background: c}, nil); err != nil {
37			t.Fatal(err)
38		}
39	}
40	if got := readPixels(t, r).At(3, 3); got != (color.RGBA{0, 0, 255, 255}) {
41		t.Fatalf("readback of the last target %v", got)
42	}
43	// Import target 0 as a client buffer, like the display would.
44	viewer, err := New(64, 32)
45	if err != nil {
46		t.Fatal(err)
47	}
48	defer viewer.Close()
49	b := bufs[0]
50	b.ID = 1
51	importable := false
52	for _, f := range viewer.DMABuf().Formats {
53		importable = importable || f.Format == b.Format && f.Modifier == b.Modifier
54	}
55	if !importable {
56		t.Skipf("modifier %#x not importable", b.Modifier)
57	}
58	scene := ports.Scene{Background: "#000000", Windows: []ports.SceneWindow{{ID: 1, Rect: ports.Rect{W: 64, H: 32}}}}
59	contents := map[ports.WindowID]ports.SurfaceContent{1: {ID: 1, Width: 64, Height: 32, Opaque: true, DMABuf: &b}}
60	if err := render(viewer, scene, contents); err != nil {
61		t.Fatal(err)
62	}
63	if got := readPixels(t, viewer).At(10, 10); got != (color.RGBA{255, 0, 0, 255}) {
64		t.Fatalf("target 0 as seen by the display %v", got)
65	}
66}
67
68// The exported HDR image carries PQ while Pixels remains the composed SDR
69// image. A test-only transfer-src readback checks the actual shader output.
70func TestHDRExportTarget(t *testing.T) {
71	r, err := New(32, 16)
72	if err != nil {
73		t.Skipf("Vulkan unavailable: %v", err)
74	}
75	defer r.Close()
76	r.SetHDR(203)
77	// Only this GPU test requests transfer-src on an HDR target.
78	r.hdrReadback = true
79	if r.physical == 0 {
80		t.Skip("no exportable GPU")
81	}
82	r.hdrMods = r.probeModifiers(r.physical, vk.FormatA2r10g10b10UnormPack32)
83	if len(r.hdrMods) == 0 {
84		t.Skip("no HDR modifier supporting transfer-src")
85	}
86	bufs, err := r.ExportTargets(1, r.hdrMods)
87	if err != nil {
88		t.Skipf("no HDR-exportable target: %v", err)
89	}
90	defer bufs[0].Planes[0].File.Close()
91	if bufs[0].Format != fourccXR30 {
92		t.Fatalf("format: %#x", bufs[0].Format)
93	}
94
95	// A real second frame with an unchanged scene must retain the internal
96	// image and use a partial-damage load from its actual prior layout.
97	scene := ports.Scene{Seq: 1, Background: "#ffffff"}
98	for _, tc := range []struct {
99		bg  string
100		rgb [3]float64
101	}{
102		{"#ffffff", [3]float64{1, 1, 1}},
103		{"#ff0000", [3]float64{1, 0, 0}},
104		{"#0000ff", [3]float64{0, 0, 1}},
105		{"#808080", [3]float64{128.0 / 255, 128.0 / 255, 128.0 / 255}},
106	} {
107		scene.Seq++
108		scene.Background = tc.bg
109		if err := render(r, scene, nil); err != nil {
110			t.Fatal(err)
111		}
112		checkHDRPixel(t, r, tc.rgb)
113	}
114	before := r.redrawn
115	if err := render(r, scene, nil); err != nil {
116		t.Fatal(err)
117	}
118	if r.redrawn-before >= r.width*r.height {
119		t.Fatalf("unchanged HDR frame redrew %d pixels", r.redrawn-before)
120	}
121	checkHDRPixel(t, r, [3]float64{128.0 / 255, 128.0 / 255, 128.0 / 255})
122	if r.hdrOwn.layout != vk.ImageLayoutTransferSrcOptimal {
123		t.Fatalf("internal layout after HDR: %v", r.hdrOwn.layout)
124	}
125	if got := readPixels(t, r).RGBAAt(0, 0); got.R != 128 || got.G != 128 || got.B != 128 {
126		t.Fatalf("SDR readback: %v", got)
127	}
128}
129
130// Test-only target readback: production HDR images do not have transfer-src.
131func checkHDRPixel(t *testing.T, r *Renderer, rgb [3]float64) {
132	t.Helper()
133	v := pqTargetWords(t, r)[0]
134	red, green, blue := hdrPixel(rgb[0], rgb[1], rgb[2], 203)
135	for i, want := range []float64{blue, green, red} {
136		got := float64(v>>uint(i*10)&1023) / 1023
137		if math.Abs(got-want) > 0.005 {
138			t.Fatalf("rgb %v channel %d: PQ %.5f want %.5f", rgb, i, got, want)
139		}
140	}
141}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 25

Tags 10

↑↓ moveenter openesc close