VirtualBox

source: vbox/trunk/src/VBox/Additions/3D/win/VBoxGL/VBoxGL.c@ 98103

Last change on this file since 98103 was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.7 KB
Line 
1/* $Id: VBoxGL.c 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VirtualBox Windows Guest Mesa3D - OpenGL driver.
4 */
5
6/*
7 * Copyright (C) 2018-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 "GaDrvEnvKMT.h"
29
30#include "stw_winsys.h"
31#include "stw_device.h"
32#include "stw_context.h"
33
34#include "pipe/p_state.h"
35#include "svga3d_reg.h"
36
37#include <iprt/asm.h>
38
39#include <common/wddm/VBoxMPIf.h>
40
41#include <Psapi.h>
42
43static const char *g_pszSvgaDll =
44#ifdef VBOX_WOW64
45 "VBoxSVGA-x86.dll"
46#else
47 "VBoxSVGA.dll"
48#endif
49;
50
51static struct GaDrvFunctions
52{
53 PFNGaDrvScreenCreate pfnGaDrvScreenCreate;
54 PFNGaDrvScreenDestroy pfnGaDrvScreenDestroy;
55 PFNGaDrvGetWDDMEnv pfnGaDrvGetWDDMEnv;
56 PFNGaDrvGetContextId pfnGaDrvGetContextId;
57 PFNGaDrvGetSurfaceId pfnGaDrvGetSurfaceId;
58 PFNGaDrvContextFlush pfnGaDrvContextFlush;
59} g_drvfuncs;
60
61
62static HMODULE gaDrvLoadSVGA(struct GaDrvFunctions *pDrvFuncs)
63{
64 struct VBOXWDDMDLLPROC aDrvProcs[] =
65 {
66 { "GaDrvScreenCreate", (FARPROC *)&pDrvFuncs->pfnGaDrvScreenCreate },
67 { "GaDrvScreenDestroy", (FARPROC *)&pDrvFuncs->pfnGaDrvScreenDestroy },
68 { "GaDrvGetWDDMEnv", (FARPROC *)&pDrvFuncs->pfnGaDrvGetWDDMEnv },
69 { "GaDrvGetContextId", (FARPROC *)&pDrvFuncs->pfnGaDrvGetContextId },
70 { "GaDrvGetSurfaceId", (FARPROC *)&pDrvFuncs->pfnGaDrvGetSurfaceId },
71 { "GaDrvContextFlush", (FARPROC *)&pDrvFuncs->pfnGaDrvContextFlush },
72 { NULL, NULL }
73 };
74
75 HMODULE hmod = VBoxWddmLoadSystemDll(g_pszSvgaDll);
76 if (hmod)
77 {
78 VBoxWddmLoadAdresses(hmod, aDrvProcs);
79 }
80 return hmod;
81}
82
83struct stw_shared_surface
84{
85 D3DKMT_HANDLE hResource;
86 D3DKMT_HANDLE hSurface;
87 uint32_t u32Sid;
88};
89
90static NTSTATUS vboxKmtPresent(D3DKMT_HANDLE hContext, HWND hwnd, D3DKMT_HANDLE hSource, LONG lWidth, LONG lHeight)
91{
92 RECT r;
93 r.left = 0;
94 r.top = 0;
95 r.right = lWidth;
96 r.bottom = lHeight;
97
98 D3DKMT_PRESENT PresentData;
99 memset(&PresentData, 0, sizeof(PresentData));
100 PresentData.hContext = hContext;
101 PresentData.hWindow = hwnd;
102 PresentData.hSource = hSource;
103 PresentData.hDestination = 0;
104 PresentData.Flags.Blt = 1;
105 PresentData.Flags.SrcRectValid = 1;
106 PresentData.Flags.DstRectValid = 1;
107 PresentData.SrcRect = r;
108 PresentData.SubRectCnt = 1;
109 PresentData.pSrcSubRects = &r;
110 PresentData.DstRect = r;
111
112 D3DKMTFUNCTIONS const *d3dkmt = D3DKMTFunctions();
113 NTSTATUS Status = d3dkmt->pfnD3DKMTPresent(&PresentData);
114 return Status;
115}
116
117NTSTATUS vboxKmtOpenSharedSurface(D3DKMT_HANDLE hAdapter, D3DKMT_HANDLE hDevice, D3DKMT_HANDLE hSharedSurface, struct stw_shared_surface *pSurf)
118{
119 D3DKMTFUNCTIONS const *d3dkmt = D3DKMTFunctions();
120
121 D3DKMT_QUERYRESOURCEINFO QueryResourceInfoData;
122 memset(&QueryResourceInfoData, 0, sizeof(QueryResourceInfoData));
123 QueryResourceInfoData.hDevice = hDevice;
124 QueryResourceInfoData.hGlobalShare = hSharedSurface;
125
126 NTSTATUS Status = d3dkmt->pfnD3DKMTQueryResourceInfo(&QueryResourceInfoData);
127 if (Status == STATUS_SUCCESS)
128 {
129 D3DDDI_OPENALLOCATIONINFO OpenAllocationInfoData;
130 memset(&OpenAllocationInfoData, 0, sizeof(OpenAllocationInfoData));
131
132 D3DKMT_OPENRESOURCE OpenResourceData;
133 memset(&OpenResourceData, 0, sizeof(OpenResourceData));
134 OpenResourceData.hDevice = hDevice;
135 OpenResourceData.hGlobalShare = hSharedSurface;
136 OpenResourceData.NumAllocations = 1;
137 OpenResourceData.pOpenAllocationInfo = &OpenAllocationInfoData;
138 if (QueryResourceInfoData.PrivateRuntimeDataSize)
139 {
140 OpenResourceData.pPrivateRuntimeData = malloc(QueryResourceInfoData.PrivateRuntimeDataSize);
141 if (OpenResourceData.pPrivateRuntimeData == NULL)
142 {
143 Status = STATUS_NOT_SUPPORTED;
144 }
145 OpenResourceData.PrivateRuntimeDataSize = QueryResourceInfoData.PrivateRuntimeDataSize;
146 }
147 if (QueryResourceInfoData.ResourcePrivateDriverDataSize)
148 {
149 OpenResourceData.pResourcePrivateDriverData = malloc(QueryResourceInfoData.ResourcePrivateDriverDataSize);
150 if (OpenResourceData.pResourcePrivateDriverData == NULL)
151 {
152 Status = STATUS_NOT_SUPPORTED;
153 }
154 OpenResourceData.ResourcePrivateDriverDataSize = QueryResourceInfoData.ResourcePrivateDriverDataSize;
155 }
156 if (QueryResourceInfoData.TotalPrivateDriverDataSize)
157 {
158 OpenResourceData.pTotalPrivateDriverDataBuffer = malloc(QueryResourceInfoData.TotalPrivateDriverDataSize);
159 if (OpenResourceData.pTotalPrivateDriverDataBuffer == NULL)
160 {
161 Status = STATUS_NOT_SUPPORTED;
162 }
163 OpenResourceData.TotalPrivateDriverDataBufferSize = QueryResourceInfoData.TotalPrivateDriverDataSize;
164 }
165
166 if (Status == STATUS_SUCCESS)
167 {
168 Status = d3dkmt->pfnD3DKMTOpenResource(&OpenResourceData);
169 if (Status == STATUS_SUCCESS)
170 {
171 if (OpenAllocationInfoData.PrivateDriverDataSize == sizeof(VBOXWDDM_ALLOCINFO))
172 {
173 VBOXWDDM_ALLOCINFO *pVBoxAllocInfo = (VBOXWDDM_ALLOCINFO *)OpenAllocationInfoData.pPrivateDriverData;
174 pSurf->hResource = OpenResourceData.hResource;
175 pSurf->hSurface = OpenAllocationInfoData.hAllocation;
176 pSurf->u32Sid = pVBoxAllocInfo->hostID;
177 }
178 else if (OpenAllocationInfoData.PrivateDriverDataSize == sizeof(VBOXDXALLOCATIONDESC))
179 {
180 //VBOXDXALLOCATIONDESC *pAllocDesc = (VBOXDXALLOCATIONDESC *)OpenAllocationInfoData.PrivateDriverDataSize;
181 pSurf->hResource = OpenResourceData.hResource;
182 pSurf->hSurface = OpenAllocationInfoData.hAllocation;
183
184 VBOXDISPIFESCAPE_SVGAGETSID data;
185 memset(&data, 0, sizeof(data));
186 data.EscapeHdr.escapeCode = VBOXESC_SVGAGETSID;
187 data.hAllocation = OpenAllocationInfoData.hAllocation;
188 // data.u32Sid = 0;
189
190 D3DKMT_ESCAPE EscapeData;
191 memset(&EscapeData, 0, sizeof(EscapeData));
192 EscapeData.hAdapter = hAdapter;
193 EscapeData.hDevice = hDevice;
194 EscapeData.Type = D3DKMT_ESCAPE_DRIVERPRIVATE;
195 // EscapeData.Flags.HardwareAccess = 0;
196 EscapeData.pPrivateDriverData = &data;
197 EscapeData.PrivateDriverDataSize = sizeof(data);
198 // EscapeData.hContext = 0;
199 Status = d3dkmt->pfnD3DKMTEscape(&EscapeData);
200 if (Status == STATUS_SUCCESS)
201 pSurf->u32Sid = data.u32Sid;
202 else
203 Assert(0);
204 }
205 else
206 Assert(0);
207 }
208 }
209
210 if (OpenResourceData.pPrivateRuntimeData != NULL)
211 {
212 free(OpenResourceData.pPrivateRuntimeData);
213 }
214 if (OpenResourceData.pResourcePrivateDriverData != NULL)
215 {
216 free(OpenResourceData.pResourcePrivateDriverData);
217 }
218 if (OpenResourceData.pTotalPrivateDriverDataBuffer != NULL)
219 {
220 free(OpenResourceData.pTotalPrivateDriverDataBuffer);
221 }
222 }
223
224 return Status;
225}
226
227NTSTATUS vboxKmtCloseSharedSurface(D3DKMT_HANDLE hDevice, struct stw_shared_surface *pSurf)
228{
229 D3DKMTFUNCTIONS const *d3dkmt = D3DKMTFunctions();
230
231 D3DKMT_DESTROYALLOCATION DestroyAllocationData;
232 memset(&DestroyAllocationData, 0, sizeof(DestroyAllocationData));
233 DestroyAllocationData.hDevice = hDevice;
234 DestroyAllocationData.hResource = pSurf->hResource;
235 /* "If the OpenGL ICD sets the handle in the hResource member to a non-NULL value,
236 * the ICD must set phAllocationList to NULL." and
237 * "the AllocationCount member is ignored by the OpenGL runtime."
238 */
239 // DestroyAllocationData.phAllocationList = NULL;
240 // DestroyAllocationData.AllocationCount = 0;
241
242 NTSTATUS Status = d3dkmt->pfnD3DKMTDestroyAllocation(&DestroyAllocationData);
243 Assert(Status == STATUS_SUCCESS);
244 return Status;
245}
246
247
248static struct pipe_screen *
249wddm_screen_create(HDC hDC)
250{
251 RT_NOREF(hDC); /** @todo Use it? */
252 struct pipe_screen *screen = NULL;
253
254 if (gaDrvLoadSVGA(&g_drvfuncs))
255 {
256 WDDMGalliumDriverEnv const *pEnv = GaDrvEnvKmtCreate();
257 if (pEnv)
258 {
259 /// @todo pEnv to include destructor callback, to be called from winsys screen destructor?
260 screen = g_drvfuncs.pfnGaDrvScreenCreate(pEnv);
261 }
262 }
263
264 return screen;
265}
266
267static void
268wddm_present(struct pipe_screen *screen,
269 struct pipe_context *context,
270 struct pipe_resource *res,
271 HDC hDC)
272{
273 RT_NOREF(context);
274 struct stw_context *ctx = stw_current_context();
275 struct pipe_context *pipe = ctx->st->pipe;
276
277 const WDDMGalliumDriverEnv *pEnv = g_drvfuncs.pfnGaDrvGetWDDMEnv(screen);
278 if (pEnv)
279 {
280 /* Get context and kernel-mode handle of the resource. */
281 uint32_t u32Cid = g_drvfuncs.pfnGaDrvGetContextId(pipe);
282 D3DKMT_HANDLE hContext = GaDrvEnvKmtContextHandle(pEnv, u32Cid);
283
284 uint32_t u32SourceSid = g_drvfuncs.pfnGaDrvGetSurfaceId(screen, res);
285 D3DKMT_HANDLE hSource = GaDrvEnvKmtSurfaceHandle(pEnv, u32SourceSid);
286
287 HWND hwnd = WindowFromDC(hDC);
288
289 vboxKmtPresent(hContext, hwnd, hSource, res->width0, res->height0);
290 }
291}
292
293static boolean
294wddm_get_adapter_luid(struct pipe_screen *screen,
295 HDC hDC,
296 LUID *pAdapterLuid)
297{
298 RT_NOREF(hDC); /** @todo Use it? */
299 const WDDMGalliumDriverEnv *pEnv = g_drvfuncs.pfnGaDrvGetWDDMEnv(screen);
300 if (pEnv)
301 {
302 GaDrvEnvKmtAdapterLUID(pEnv, pAdapterLuid);
303 return true;
304 }
305
306 return false;
307}
308
309static struct stw_shared_surface *
310wddm_shared_surface_open(struct pipe_screen *screen,
311 HANDLE hSharedSurface)
312{
313 struct stw_shared_surface *surface = NULL;
314
315 const WDDMGalliumDriverEnv *pEnv = g_drvfuncs.pfnGaDrvGetWDDMEnv(screen);
316 if (pEnv)
317 {
318 surface = (struct stw_shared_surface *)malloc(sizeof(struct stw_shared_surface));
319 if (surface)
320 {
321 D3DKMT_HANDLE hAdapter = GaDrvEnvKmtAdapterHandle(pEnv);
322 D3DKMT_HANDLE hDevice = GaDrvEnvKmtDeviceHandle(pEnv);
323 NTSTATUS Status = vboxKmtOpenSharedSurface(hAdapter, hDevice, (D3DKMT_HANDLE)(uintptr_t)hSharedSurface, surface);
324 if (Status != STATUS_SUCCESS)
325 {
326 free(surface);
327 surface = NULL;
328 }
329 }
330 }
331 return surface;
332}
333
334static void
335wddm_shared_surface_close(struct pipe_screen *screen,
336 struct stw_shared_surface *surface)
337{
338 const WDDMGalliumDriverEnv *pEnv = g_drvfuncs.pfnGaDrvGetWDDMEnv(screen);
339 if (pEnv)
340 {
341 D3DKMT_HANDLE hDevice = GaDrvEnvKmtDeviceHandle(pEnv);
342 vboxKmtCloseSharedSurface(hDevice, surface);
343 }
344 free(surface);
345}
346
347static void
348wddm_compose(struct pipe_screen *screen,
349 struct pipe_resource *res,
350 struct stw_shared_surface *dest,
351 LPCRECT pRect,
352 ULONGLONG PresentHistoryToken)
353{
354 struct stw_context *ctx = stw_current_context();
355 struct pipe_context *pipe = ctx->st->pipe;
356
357 /* The ICD asked to present something, make sure that any outstanding commends are submitted. */
358 g_drvfuncs.pfnGaDrvContextFlush(pipe);
359
360 uint32_t u32SourceSid = g_drvfuncs.pfnGaDrvGetSurfaceId(screen, res);
361
362 /* Generate SVGA_3D_CMD_SURFACE_COPY command for these resources. */
363 struct
364 {
365 SVGA3dCmdHeader header;
366 SVGA3dCmdSurfaceCopy surfaceCopy;
367 SVGA3dCopyBox box;
368 } command;
369
370 command.header.id = SVGA_3D_CMD_SURFACE_COPY;
371 command.header.size = sizeof(command) - sizeof(SVGA3dCmdHeader);
372
373 command.surfaceCopy.src.sid = u32SourceSid;
374 command.surfaceCopy.src.face = 0;
375 command.surfaceCopy.src.mipmap = 0;
376 command.surfaceCopy.dest.sid = dest->u32Sid;
377 command.surfaceCopy.dest.face = 0;
378 command.surfaceCopy.dest.mipmap = 0;
379
380 command.box.x = pRect->left;
381 command.box.y = pRect->top;
382 command.box.z = 0;
383 command.box.w = pRect->right - pRect->left;
384 command.box.h = pRect->bottom - pRect->top;
385 command.box.d = 1;
386 command.box.srcx = 0;
387 command.box.srcy = 0;
388 command.box.srcz = 0;
389
390 const WDDMGalliumDriverEnv *pEnv = g_drvfuncs.pfnGaDrvGetWDDMEnv(screen);
391 if (pEnv)
392 {
393 uint32_t u32Cid = g_drvfuncs.pfnGaDrvGetContextId(pipe);
394 GaDrvEnvKmtRenderCompose(pEnv, u32Cid, &command, sizeof(command), PresentHistoryToken);
395 }
396}
397
398static unsigned
399wddm_get_pfd_flags(struct pipe_screen *screen)
400{
401 (void)screen;
402 return stw_pfd_gdi_support | stw_pfd_double_buffer;
403}
404
405static const char *
406wddm_get_name(void)
407{
408 return "VBoxGL";
409}
410
411static const struct stw_winsys stw_winsys = {
412 wddm_screen_create,
413 wddm_present,
414 wddm_get_adapter_luid,
415 wddm_shared_surface_open,
416 wddm_shared_surface_close,
417 wddm_compose,
418 wddm_get_pfd_flags,
419 NULL, /* create_framebuffer */
420 wddm_get_name,
421};
422
423#ifdef DEBUG
424typedef BOOL WINAPI FNGetModuleInformation(HANDLE hProcess, HMODULE hModule, LPMODULEINFO lpmodinfo, DWORD cb);
425typedef FNGetModuleInformation *PFNGetModuleInformation;
426
427static PFNGetModuleInformation g_pfnGetModuleInformation = NULL;
428static HMODULE g_hModPsapi = NULL;
429static PVOID g_VBoxWDbgVEHandler = NULL;
430
431static bool vboxVDbgIsAddressInModule(PVOID pv, const char *pszModuleName)
432{
433 HMODULE hMod = GetModuleHandleA(pszModuleName);
434 if (!hMod)
435 return false;
436
437 if (!g_pfnGetModuleInformation)
438 return false;
439
440 HANDLE hProcess = GetCurrentProcess();
441 MODULEINFO ModuleInfo = {0};
442 if (!g_pfnGetModuleInformation(hProcess, hMod, &ModuleInfo, sizeof(ModuleInfo)))
443 return false;
444
445 return (uintptr_t)ModuleInfo.lpBaseOfDll <= (uintptr_t)pv
446 && (uintptr_t)pv < (uintptr_t)ModuleInfo.lpBaseOfDll + ModuleInfo.SizeOfImage;
447}
448
449static bool vboxVDbgIsExceptionIgnored(PEXCEPTION_RECORD pExceptionRecord)
450{
451 /* Module (dll) names for GetModuleHandle.
452 * Exceptions originated from these modules will be ignored.
453 */
454 static const char *apszIgnoredModuleNames[] =
455 {
456 NULL
457 };
458
459 int i = 0;
460 while (apszIgnoredModuleNames[i])
461 {
462 if (vboxVDbgIsAddressInModule(pExceptionRecord->ExceptionAddress, apszIgnoredModuleNames[i]))
463 return true;
464
465 ++i;
466 }
467
468 return false;
469}
470
471static LONG WINAPI vboxVDbgVectoredHandler(struct _EXCEPTION_POINTERS *pExceptionInfo) RT_NOTHROW_DEF
472{
473 static volatile bool g_fAllowIgnore = true; /* Might be changed in kernel debugger. */
474
475 PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
476 /* PCONTEXT pContextRecord = pExceptionInfo->ContextRecord; */
477
478 switch (pExceptionRecord->ExceptionCode)
479 {
480 default:
481 break;
482 case EXCEPTION_BREAKPOINT:
483 case EXCEPTION_ACCESS_VIOLATION:
484 case EXCEPTION_STACK_OVERFLOW:
485 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
486 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
487 case EXCEPTION_FLT_INVALID_OPERATION:
488 case EXCEPTION_INT_DIVIDE_BY_ZERO:
489 case EXCEPTION_ILLEGAL_INSTRUCTION:
490 if (g_fAllowIgnore && vboxVDbgIsExceptionIgnored(pExceptionRecord))
491 break;
492 ASMBreakpoint();
493 break;
494 case 0x40010006: /* OutputDebugStringA? */
495 case 0x4001000a: /* OutputDebugStringW? */
496 break;
497 }
498 return EXCEPTION_CONTINUE_SEARCH;
499}
500
501static void vboxVDbgVEHandlerRegister(void)
502{
503 Assert(!g_VBoxWDbgVEHandler);
504 g_VBoxWDbgVEHandler = AddVectoredExceptionHandler(1, vboxVDbgVectoredHandler);
505 Assert(g_VBoxWDbgVEHandler);
506
507 g_hModPsapi = GetModuleHandleA("Psapi.dll"); /* Usually already loaded. */
508 if (g_hModPsapi)
509 g_pfnGetModuleInformation = (PFNGetModuleInformation)GetProcAddress(g_hModPsapi, "GetModuleInformation");
510}
511
512static void vboxVDbgVEHandlerUnregister(void)
513{
514 Assert(g_VBoxWDbgVEHandler);
515 ULONG uResult = RemoveVectoredExceptionHandler(g_VBoxWDbgVEHandler);
516 Assert(uResult); RT_NOREF(uResult);
517 g_VBoxWDbgVEHandler = NULL;
518
519 g_hModPsapi = NULL;
520 g_pfnGetModuleInformation = NULL;
521}
522#endif /* DEBUG */
523
524BOOL WINAPI DllMain(HINSTANCE hDLLInst,
525 DWORD fdwReason,
526 LPVOID lpvReserved)
527{
528 RT_NOREF2(hDLLInst, lpvReserved);
529
530 switch (fdwReason)
531 {
532 case DLL_PROCESS_ATTACH:
533#ifdef DEBUG
534 vboxVDbgVEHandlerRegister();
535#endif
536 D3DKMTLoad();
537 stw_init(&stw_winsys);
538 stw_init_thread();
539 break;
540
541 case DLL_PROCESS_DETACH:
542#ifdef DEBUG
543 vboxVDbgVEHandlerUnregister();
544#endif
545 break;
546
547 case DLL_THREAD_ATTACH:
548 stw_init_thread();
549 break;
550
551 case DLL_THREAD_DETACH:
552 stw_cleanup_thread();
553 break;
554
555 default:
556 if (lpvReserved == NULL)
557 {
558 // We're being unloaded from the process.
559 stw_cleanup_thread();
560 stw_cleanup();
561 }
562 else
563 {
564 // Process itself is terminating, and all threads and modules are
565 // being detached.
566 //
567 // The order threads (including llvmpipe rasterizer threads) are
568 // destroyed can not be relied up, so it's not safe to cleanup.
569 //
570 // However global destructors (e.g., LLVM's) will still be called, and
571 // if Microsoft OPENGL32.DLL's DllMain is called after us, it will
572 // still try to invoke DrvDeleteContext to destroys all outstanding,
573 // so set stw_dev to NULL to return immediately if that happens.
574 stw_dev = NULL;
575 }
576 break;
577 }
578
579 return TRUE;
580}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use