1 | /* $Id: ogltest.cpp 98139 2023-01-19 13:50:46Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * OpenGL testcase. Win32 application to run Gallium OpenGL tests.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2019-2023 Oracle and/or its affiliates.
|
---|
8 | *
|
---|
9 | * This file is part of VirtualBox base platform packages, as
|
---|
10 | * available from https://www.virtualbox.org.
|
---|
11 | *
|
---|
12 | * This program is free software; you can redistribute it and/or
|
---|
13 | * modify it under the terms of the GNU General Public License
|
---|
14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
15 | * License.
|
---|
16 | *
|
---|
17 | * This program is distributed in the hope that it will be useful, but
|
---|
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
20 | * General Public License for more details.
|
---|
21 | *
|
---|
22 | * You should have received a copy of the GNU General Public License
|
---|
23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
24 | *
|
---|
25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
26 | */
|
---|
27 |
|
---|
28 | #include "oglrender.h"
|
---|
29 | #include <iprt/string.h>
|
---|
30 |
|
---|
31 | PFNGLBINDBUFFERPROC glBindBuffer;
|
---|
32 | PFNGLDELETEBUFFERSPROC glDeleteBuffers;
|
---|
33 | PFNGLGENBUFFERSPROC glGenBuffers;
|
---|
34 | PFNGLBUFFERDATAPROC glBufferData;
|
---|
35 | PFNGLMAPBUFFERPROC glMapBuffer;
|
---|
36 | PFNGLUNMAPBUFFERPROC glUnmapBuffer;
|
---|
37 | PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
|
---|
38 | PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
|
---|
39 | PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
|
---|
40 | PFNGLCREATESHADERPROC glCreateShader;
|
---|
41 | PFNGLATTACHSHADERPROC glAttachShader;
|
---|
42 | PFNGLCOMPILESHADERPROC glCompileShader;
|
---|
43 | PFNGLCREATEPROGRAMPROC glCreateProgram;
|
---|
44 | PFNGLDELETEPROGRAMPROC glDeleteProgram;
|
---|
45 | PFNGLDELETESHADERPROC glDeleteShader;
|
---|
46 | PFNGLDETACHSHADERPROC glDetachShader;
|
---|
47 | PFNGLLINKPROGRAMPROC glLinkProgram;
|
---|
48 | PFNGLSHADERSOURCEPROC glShaderSource;
|
---|
49 | PFNGLUSEPROGRAMPROC glUseProgram;
|
---|
50 | PFNGLGETPROGRAMIVPROC glGetProgramiv;
|
---|
51 | PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
|
---|
52 | PFNGLGETSHADERIVPROC glGetShaderiv;
|
---|
53 | PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
|
---|
54 | PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor;
|
---|
55 | PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced;
|
---|
56 |
|
---|
57 | class OGLTest
|
---|
58 | {
|
---|
59 | public:
|
---|
60 | OGLTest();
|
---|
61 | ~OGLTest();
|
---|
62 |
|
---|
63 | HRESULT Init(HINSTANCE hInstance, int argc, char **argv, int nCmdShow);
|
---|
64 | int Run();
|
---|
65 |
|
---|
66 | private:
|
---|
67 | HRESULT initWindow(HINSTANCE hInstance, int nCmdShow);
|
---|
68 | HRESULT initOGL();
|
---|
69 | void parseCmdLine(int argc, char **argv);
|
---|
70 | static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
---|
71 |
|
---|
72 | void setCurrentGLCtx(HGLRC hGLRC);
|
---|
73 |
|
---|
74 | int miRenderId;
|
---|
75 | int miRenderStep;
|
---|
76 |
|
---|
77 | HWND mHwnd;
|
---|
78 | HGLRC mhGLRC;
|
---|
79 |
|
---|
80 | OGLRender *mpRender;
|
---|
81 | };
|
---|
82 |
|
---|
83 | OGLTest::OGLTest()
|
---|
84 | :
|
---|
85 | miRenderId(0),
|
---|
86 | miRenderStep(1),
|
---|
87 | mHwnd(0),
|
---|
88 | mhGLRC(0),
|
---|
89 | mpRender(0)
|
---|
90 | {
|
---|
91 | }
|
---|
92 |
|
---|
93 | OGLTest::~OGLTest()
|
---|
94 | {
|
---|
95 | if (mpRender)
|
---|
96 | {
|
---|
97 | delete mpRender;
|
---|
98 | mpRender = 0;
|
---|
99 | }
|
---|
100 |
|
---|
101 | setCurrentGLCtx(NULL);
|
---|
102 | wglDeleteContext(mhGLRC);
|
---|
103 | }
|
---|
104 |
|
---|
105 | void OGLTest::setCurrentGLCtx(HGLRC hGLRC)
|
---|
106 | {
|
---|
107 | if (hGLRC)
|
---|
108 | {
|
---|
109 | HDC const hDC = GetDC(mHwnd);
|
---|
110 | wglMakeCurrent(hDC, mhGLRC);
|
---|
111 | ReleaseDC(mHwnd, hDC);
|
---|
112 | }
|
---|
113 | else
|
---|
114 | {
|
---|
115 | wglMakeCurrent(NULL, NULL);
|
---|
116 | }
|
---|
117 | }
|
---|
118 |
|
---|
119 |
|
---|
120 | LRESULT CALLBACK OGLTest::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
---|
121 | {
|
---|
122 | switch (msg)
|
---|
123 | {
|
---|
124 | case WM_CLOSE:
|
---|
125 | DestroyWindow(hwnd);
|
---|
126 | return 0;
|
---|
127 |
|
---|
128 | case WM_DESTROY:
|
---|
129 | PostQuitMessage(0);
|
---|
130 | return 0;
|
---|
131 |
|
---|
132 | default:
|
---|
133 | return DefWindowProcA(hwnd, msg, wParam, lParam);
|
---|
134 | }
|
---|
135 | }
|
---|
136 |
|
---|
137 | HRESULT OGLTest::initWindow(HINSTANCE hInstance,
|
---|
138 | int nCmdShow)
|
---|
139 | {
|
---|
140 | HRESULT hr = S_OK;
|
---|
141 |
|
---|
142 | WNDCLASSA wc = { 0 };
|
---|
143 | wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
---|
144 | wc.lpfnWndProc = wndProc;
|
---|
145 | wc.cbClsExtra = 0;
|
---|
146 | wc.cbWndExtra = 0;
|
---|
147 | wc.hInstance = hInstance;
|
---|
148 | wc.hIcon = LoadIcon(0, IDI_APPLICATION);
|
---|
149 | wc.hCursor = LoadCursor(0, IDC_ARROW);
|
---|
150 | wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
|
---|
151 | wc.lpszMenuName = 0;
|
---|
152 | wc.lpszClassName = "OGLTestWndClassName";
|
---|
153 |
|
---|
154 | if (RegisterClassA(&wc))
|
---|
155 | {
|
---|
156 | RECT r = {0, 0, 800, 600};
|
---|
157 | AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW, false);
|
---|
158 |
|
---|
159 | mHwnd = CreateWindowA("OGLTestWndClassName",
|
---|
160 | "OGL Test",
|
---|
161 | WS_OVERLAPPEDWINDOW,
|
---|
162 | 100, 100, r.right, r.bottom,
|
---|
163 | 0, 0, hInstance, 0);
|
---|
164 | if (mHwnd)
|
---|
165 | {
|
---|
166 | ShowWindow(mHwnd, nCmdShow);
|
---|
167 | UpdateWindow(mHwnd);
|
---|
168 | }
|
---|
169 | else
|
---|
170 | {
|
---|
171 | TestShowError(hr, "CreateWindow");
|
---|
172 | hr = E_FAIL;
|
---|
173 | }
|
---|
174 | }
|
---|
175 | else
|
---|
176 | {
|
---|
177 | TestShowError(hr, "RegisterClass");
|
---|
178 | hr = E_FAIL;
|
---|
179 | }
|
---|
180 |
|
---|
181 | return hr;
|
---|
182 | }
|
---|
183 |
|
---|
184 | HRESULT OGLTest::initOGL()
|
---|
185 | {
|
---|
186 | HRESULT hr = S_OK;
|
---|
187 |
|
---|
188 | HDC hDC = GetDC(mHwnd);
|
---|
189 |
|
---|
190 | PIXELFORMATDESCRIPTOR pfd;
|
---|
191 | memset(&pfd, 0, sizeof(pfd));
|
---|
192 | pfd.nSize = sizeof(pfd);
|
---|
193 | pfd.nVersion = 1;
|
---|
194 | pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
|
---|
195 | pfd.iPixelType = PFD_TYPE_RGBA;
|
---|
196 | pfd.cColorBits = 32;
|
---|
197 |
|
---|
198 | int pf = ChoosePixelFormat(hDC, &pfd);
|
---|
199 | if (pf)
|
---|
200 | {
|
---|
201 | if (SetPixelFormat(hDC, pf, &pfd))
|
---|
202 | {
|
---|
203 | DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
|
---|
204 |
|
---|
205 | mhGLRC = wglCreateContext(hDC);
|
---|
206 | setCurrentGLCtx(mhGLRC);
|
---|
207 |
|
---|
208 | /* Get a function address, return VERR_NOT_IMPLEMENTED on failure. */
|
---|
209 | #define GLGETPROC_(ProcType, ProcName, NameSuffix) do { \
|
---|
210 | ProcName = (ProcType)wglGetProcAddress(#ProcName NameSuffix); \
|
---|
211 | if (!ProcName) { TestShowError(E_FAIL, #ProcName NameSuffix " missing"); } \
|
---|
212 | } while(0)
|
---|
213 |
|
---|
214 | GLGETPROC_(PFNGLBINDBUFFERPROC , glBindBuffer, "");
|
---|
215 | GLGETPROC_(PFNGLDELETEBUFFERSPROC , glDeleteBuffers, "");
|
---|
216 | GLGETPROC_(PFNGLGENBUFFERSPROC , glGenBuffers, "");
|
---|
217 | GLGETPROC_(PFNGLBUFFERDATAPROC , glBufferData, "");
|
---|
218 | GLGETPROC_(PFNGLMAPBUFFERPROC , glMapBuffer, "");
|
---|
219 | GLGETPROC_(PFNGLUNMAPBUFFERPROC , glUnmapBuffer, "");
|
---|
220 | GLGETPROC_(PFNGLENABLEVERTEXATTRIBARRAYPROC , glEnableVertexAttribArray, "");
|
---|
221 | GLGETPROC_(PFNGLDISABLEVERTEXATTRIBARRAYPROC , glDisableVertexAttribArray, "");
|
---|
222 | GLGETPROC_(PFNGLVERTEXATTRIBPOINTERPROC , glVertexAttribPointer, "");
|
---|
223 | GLGETPROC_(PFNGLCREATESHADERPROC , glCreateShader, "");
|
---|
224 | GLGETPROC_(PFNGLATTACHSHADERPROC , glAttachShader, "");
|
---|
225 | GLGETPROC_(PFNGLCOMPILESHADERPROC , glCompileShader, "");
|
---|
226 | GLGETPROC_(PFNGLCREATEPROGRAMPROC , glCreateProgram, "");
|
---|
227 | GLGETPROC_(PFNGLDELETEPROGRAMPROC , glDeleteProgram, "");
|
---|
228 | GLGETPROC_(PFNGLDELETESHADERPROC , glDeleteShader, "");
|
---|
229 | GLGETPROC_(PFNGLDETACHSHADERPROC , glDetachShader, "");
|
---|
230 | GLGETPROC_(PFNGLLINKPROGRAMPROC , glLinkProgram, "");
|
---|
231 | GLGETPROC_(PFNGLSHADERSOURCEPROC , glShaderSource, "");
|
---|
232 | GLGETPROC_(PFNGLUSEPROGRAMPROC , glUseProgram, "");
|
---|
233 | GLGETPROC_(PFNGLGETPROGRAMIVPROC , glGetProgramiv, "");
|
---|
234 | GLGETPROC_(PFNGLGETPROGRAMINFOLOGPROC , glGetProgramInfoLog, "");
|
---|
235 | GLGETPROC_(PFNGLGETSHADERIVPROC , glGetShaderiv, "");
|
---|
236 | GLGETPROC_(PFNGLGETSHADERINFOLOGPROC , glGetShaderInfoLog, "");
|
---|
237 | GLGETPROC_(PFNGLVERTEXATTRIBDIVISORPROC , glVertexAttribDivisor, "");
|
---|
238 | GLGETPROC_(PFNGLDRAWARRAYSINSTANCEDPROC , glDrawArraysInstanced, "");
|
---|
239 |
|
---|
240 | #undef GLGETPROC_
|
---|
241 |
|
---|
242 | }
|
---|
243 | else
|
---|
244 | {
|
---|
245 | TestShowError(hr, "SetPixelFormat");
|
---|
246 | hr = E_FAIL;
|
---|
247 | }
|
---|
248 | }
|
---|
249 | else
|
---|
250 | {
|
---|
251 | TestShowError(hr, "ChoosePixelFormat");
|
---|
252 | hr = E_FAIL;
|
---|
253 | }
|
---|
254 |
|
---|
255 | ReleaseDC(mHwnd, hDC);
|
---|
256 |
|
---|
257 | return hr;
|
---|
258 | }
|
---|
259 |
|
---|
260 | void OGLTest::parseCmdLine(int argc, char **argv)
|
---|
261 | {
|
---|
262 | /* Very simple: test number followed by step flag.
|
---|
263 | * Default is test 0, step mode: 1
|
---|
264 | */
|
---|
265 |
|
---|
266 | /* First number is the render id. */
|
---|
267 | if (argc >= 2)
|
---|
268 | miRenderId = RTStrToInt32(argv[1]);
|
---|
269 |
|
---|
270 | /* Second number is the step mode. */
|
---|
271 | if (argc >= 3)
|
---|
272 | miRenderStep = RTStrToInt32(argv[2]);
|
---|
273 | }
|
---|
274 |
|
---|
275 | HRESULT OGLTest::Init(HINSTANCE hInstance, int argc, char **argv, int nCmdShow)
|
---|
276 | {
|
---|
277 | parseCmdLine(argc, argv);
|
---|
278 |
|
---|
279 | HRESULT hr = initWindow(hInstance, nCmdShow);
|
---|
280 | if (SUCCEEDED(hr))
|
---|
281 | {
|
---|
282 | mpRender = CreateRender(miRenderId);
|
---|
283 | if (mpRender)
|
---|
284 | {
|
---|
285 | hr = initOGL();
|
---|
286 | if (SUCCEEDED(hr))
|
---|
287 | {
|
---|
288 | setCurrentGLCtx(mhGLRC);
|
---|
289 |
|
---|
290 | hr = mpRender->InitRender();
|
---|
291 | if (FAILED(hr))
|
---|
292 | {
|
---|
293 | TestShowError(hr, "InitRender");
|
---|
294 | }
|
---|
295 |
|
---|
296 | setCurrentGLCtx(NULL);
|
---|
297 | }
|
---|
298 | }
|
---|
299 | else
|
---|
300 | {
|
---|
301 | hr = E_FAIL;
|
---|
302 | }
|
---|
303 | }
|
---|
304 |
|
---|
305 | return hr;
|
---|
306 | }
|
---|
307 |
|
---|
308 | int OGLTest::Run()
|
---|
309 | {
|
---|
310 | bool fFirst = true;
|
---|
311 | MSG msg;
|
---|
312 | do
|
---|
313 | {
|
---|
314 | BOOL fGotMessage;
|
---|
315 | if (miRenderStep)
|
---|
316 | {
|
---|
317 | fGotMessage = GetMessageA(&msg, 0, 0, 0);
|
---|
318 | }
|
---|
319 | else
|
---|
320 | {
|
---|
321 | fGotMessage = PeekMessageA(&msg, 0, 0, 0, PM_REMOVE);
|
---|
322 | }
|
---|
323 |
|
---|
324 | if (fGotMessage)
|
---|
325 | {
|
---|
326 | TranslateMessage(&msg);
|
---|
327 | DispatchMessageA(&msg);
|
---|
328 | }
|
---|
329 |
|
---|
330 | float dt = 0.0f; /* Time in seconds since last render step. @todo Measure. */
|
---|
331 |
|
---|
332 | BOOL fDoRender = FALSE;
|
---|
333 | if (miRenderStep)
|
---|
334 | {
|
---|
335 | if (msg.message == WM_CHAR)
|
---|
336 | {
|
---|
337 | if (msg.wParam == ' ')
|
---|
338 | {
|
---|
339 | fDoRender = TRUE;
|
---|
340 | dt = fFirst ? 0.0f : 0.1f; /* 0.1 second increment per step. */
|
---|
341 | }
|
---|
342 | }
|
---|
343 | }
|
---|
344 | else
|
---|
345 | {
|
---|
346 | fDoRender = TRUE;
|
---|
347 | }
|
---|
348 |
|
---|
349 | if (fDoRender)
|
---|
350 | {
|
---|
351 | if (mpRender)
|
---|
352 | {
|
---|
353 | setCurrentGLCtx(mhGLRC);
|
---|
354 |
|
---|
355 | mpRender->TimeAdvance(dt);
|
---|
356 | mpRender->DoRender();
|
---|
357 |
|
---|
358 | setCurrentGLCtx(NULL);
|
---|
359 |
|
---|
360 | fFirst = false;
|
---|
361 | }
|
---|
362 | }
|
---|
363 | } while (msg.message != WM_QUIT);
|
---|
364 |
|
---|
365 | return msg.wParam;
|
---|
366 | }
|
---|
367 |
|
---|
368 | int main(int argc, char **argv)
|
---|
369 | {
|
---|
370 | int rcExit = RTEXITCODE_FAILURE;
|
---|
371 |
|
---|
372 | OGLTest test;
|
---|
373 | HRESULT hr = test.Init(GetModuleHandleW(NULL), argc, argv, SW_SHOWDEFAULT);
|
---|
374 | if (SUCCEEDED(hr))
|
---|
375 | rcExit = test.Run();
|
---|
376 |
|
---|
377 | return rcExit;
|
---|
378 | }
|
---|