1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2026 Open-IOV contributors
// test_tri.c - minimal SDL2 + OpenGL triangle for the offscreen pbuffer
// harness. Intended to be run with SDL_VIDEODRIVER=offscreen and the
// gz_pbuf.so shim preloaded (plus sdl_cap.so to capture the swaps):
//
// SDL_VIDEODRIVER=offscreen DISPLAY=:0 \
// LD_PRELOAD="/tmp/sdl_cap.so /tmp/gz_pbuf.so" GZCAP=/tmp/testframes.bin \
// ./test_tri 20 640 480
//
// It creates an SDL2 GL window/context (the shim turns the context into a
// GLX pbuffer), clears to a colour, draws a rotating triangle, reads the
// centre pixel back (non-black proof), and calls SDL_GL_SwapWindow each
// frame. The sdl_cap.so hook captures the swap; the gz_pbuf.so hook turns
// the swap into glFinish. Build:
// gcc -O2 -o test_tri test_tri.c $(pkg-config --cflags --libs sdl2) -lGL
#include <SDL2/SDL.h>
#include <GL/gl.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int frames = argc > 1 ? atoi(argv[1]) : 20;
int w = argc > 2 ? atoi(argv[2]) : 640;
int h = argc > 3 ? atoi(argv[3]) : 480;
SDL_Window *win;
SDL_GLContext ctx;
int i;
setvbuf(stdout, NULL, _IONBF, 0);
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
fprintf(stderr, "test_tri: SDL_Init: %s\n", SDL_GetError());
return 1;
}
printf("test_tri: videodriver=%s\n",
SDL_GetCurrentVideoDriver() ? SDL_GetCurrentVideoDriver() : "?");
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
win = SDL_CreateWindow("test_tri", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, w, h,
SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN);
if (!win) {
fprintf(stderr, "test_tri: SDL_CreateWindow: %s\n", SDL_GetError());
SDL_Quit();
return 2;
}
ctx = SDL_GL_CreateContext(win);
if (!ctx) {
fprintf(stderr, "test_tri: SDL_GL_CreateContext: %s\n",
SDL_GetError());
SDL_DestroyWindow(win);
SDL_Quit();
return 3;
}
if (SDL_GL_MakeCurrent(win, ctx) != 0)
fprintf(stderr, "test_tri: SDL_GL_MakeCurrent: %s\n",
SDL_GetError());
SDL_GL_SetSwapInterval(0);
printf("test_tri: GL %s | %s | %s\n",
(const char *)glGetString(GL_VENDOR),
(const char *)glGetString(GL_RENDERER),
(const char *)glGetString(GL_VERSION));
for (i = 0; i < frames; i++) {
float a = (float)i * 0.10f;
unsigned char px[4] = { 0, 0, 0, 0 };
glViewport(0, 0, w, h);
glClearColor(0.05f, 0.05f, 0.10f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef(a * 57.29578f, 0.0f, 0.0f, 1.0f);
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.2f, 0.1f);
glVertex2f(0.0f, 0.8f);
glColor3f(0.1f, 1.0f, 0.3f);
glVertex2f(-0.8f, -0.6f);
glColor3f(0.2f, 0.3f, 1.0f);
glVertex2f(0.8f, -0.6f);
glEnd();
glFinish();
glReadPixels(w / 2, h / 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, px);
printf("test_tri: frame=%d centre=%02x%02x%02x%02x err=0x%x\n",
i, px[0], px[1], px[2], px[3], glGetError());
SDL_GL_SwapWindow(win);
SDL_Delay(20);
}
printf("test_tri: done frames=%d %dx%d\n", frames, w, h);
SDL_GL_DeleteContext(ctx);
SDL_DestroyWindow(win);
SDL_Quit();
return 0;
}
|