1/*
2 * Copyright © Microsoft Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24#include <gtest/gtest.h>
25
26#include <windows.h>
27#include <GL/gl.h>
28
29#undef GetMessage
30
31class window
32{
33public:
34   window(UINT width = 64, UINT height = 64);
35   ~window();
36
37   HWND get_hwnd() const { return _window; };
38   HDC get_hdc() const { return _hdc; };
39   bool valid() const { return _window && _hdc && _hglrc; }
40   void show() {
41      ShowWindow(_window, SW_SHOW);
42   }
43
44   void recreate_attribs(const int *attribList);
45
46private:
47   HWND _window = nullptr;
48   HDC _hdc = nullptr;
49   HGLRC _hglrc = nullptr;
50};
51
52window::window(uint32_t width, uint32_t height)
53{
54   _window = CreateWindowW(
55      L"STATIC",
56      L"OpenGLTestWindow",
57      WS_OVERLAPPEDWINDOW,
58      0,
59      0,
60      width,
61      height,
62      NULL,
63      NULL,
64      NULL,
65      NULL
66   );
67
68   if (_window == nullptr)
69      return;
70
71   _hdc = ::GetDC(_window);
72
73   PIXELFORMATDESCRIPTOR pfd = {
74       sizeof(PIXELFORMATDESCRIPTOR),  /* size */
75       1,                              /* version */
76       PFD_SUPPORT_OPENGL |
77       PFD_DRAW_TO_WINDOW |
78       PFD_DOUBLEBUFFER,               /* support double-buffering */
79       PFD_TYPE_RGBA,                  /* color type */
80       8,                              /* prefered color depth */
81       0, 0, 0, 0, 0, 0,               /* color bits (ignored) */
82       0,                              /* no alpha buffer */
83       0,                              /* alpha bits (ignored) */
84       0,                              /* no accumulation buffer */
85       0, 0, 0, 0,                     /* accum bits (ignored) */
86       32,                             /* depth buffer */
87       0,                              /* no stencil buffer */
88       0,                              /* no auxiliary buffers */
89       PFD_MAIN_PLANE,                 /* main layer */
90       0,                              /* reserved */
91       0, 0, 0,                        /* no layer, visible, damage masks */
92   };
93   int pixel_format = ChoosePixelFormat(_hdc, &pfd);
94   if (pixel_format == 0)
95      return;
96   if (!SetPixelFormat(_hdc, pixel_format, &pfd))
97      return;
98
99   _hglrc = wglCreateContext(_hdc);
100   if (!_hglrc)
101      return;
102
103   wglMakeCurrent(_hdc, _hglrc);
104}
105
106void window::recreate_attribs(const int *attribs)
107{
108   using pwglCreateContextAttribsARB = HGLRC(WINAPI*)(HDC, HGLRC, const int *);
109   auto wglCreateContextAttribsARB = (pwglCreateContextAttribsARB)wglGetProcAddress("wglCreateContextAttribsARB");
110   if (!wglCreateContextAttribsARB)
111      GTEST_FAIL() << "failed to get wglCreateContextAttribsARB";
112
113   wglMakeCurrent(nullptr, nullptr);
114   wglDeleteContext(_hglrc);
115   _hglrc = wglCreateContextAttribsARB(_hdc, nullptr, attribs);
116   if (!_hglrc)
117      return;
118
119   wglMakeCurrent(_hdc, _hglrc);
120}
121
122window::~window()
123{
124   if (_hglrc) {
125      wglMakeCurrent(NULL, NULL);
126      wglDeleteContext(_hglrc);
127   }
128   if (_hdc)
129      ReleaseDC(_window, _hdc);
130   if (_window)
131      DestroyWindow(_window);
132}
133
134TEST(wgl, basic_create)
135{
136   window wnd;
137   ASSERT_TRUE(wnd.valid());
138
139   const char *version = (const char *)glGetString(GL_VERSION);
140   ASSERT_NE(strstr(version, "Mesa"), nullptr);
141}
142
143#ifdef GALLIUM_D3D12
144/* Fixture for tests for the d3d12 backend. Will be skipped if
145 * the environment isn't set up to run them.
146 */
147#include <directx/d3d12.h>
148#include <dxguids/dxguids.h>
149#include <wrl/client.h>
150#include <memory>
151using Microsoft::WRL::ComPtr;
152
153class d3d12 : public ::testing::Test
154{
155   void SetUp() override;
156};
157
158void d3d12::SetUp()
159{
160   window wnd;
161   ASSERT_TRUE(wnd.valid());
162
163   const char *renderer = (const char *)glGetString(GL_RENDERER);
164   if (!strstr(renderer, "D3D12"))
165      GTEST_SKIP();
166}
167
168static bool
169info_queue_has_swapchain(ID3D12DebugDevice *debug_device, ID3D12InfoQueue *info_queue)
170{
171   info_queue->PushEmptyStorageFilter();
172
173   debug_device->ReportLiveDeviceObjects(D3D12_RLDO_DETAIL);
174
175   uint32_t num_messages = info_queue->GetNumStoredMessages();
176   for (uint32_t i = 0; i < num_messages; ++i) {
177      SIZE_T message_size = 0;
178      info_queue->GetMessage(i, nullptr, &message_size);
179      EXPECT_GT(message_size, 0);
180
181      std::unique_ptr<byte[]> message_bytes(new byte[message_size]);
182      D3D12_MESSAGE *message = (D3D12_MESSAGE *)message_bytes.get();
183      info_queue->GetMessage(i, message, &message_size);
184
185      if (strstr(message->pDescription, "SwapChain")) {
186         info_queue->ClearStoredMessages();
187         info_queue->PopStorageFilter();
188         return true;
189      }
190   }
191   info_queue->ClearStoredMessages();
192   info_queue->PopStorageFilter();
193   return false;
194}
195
196TEST_F(d3d12, swapchain_cleanup)
197{
198   ComPtr<ID3D12InfoQueue> info_queue;
199   ComPtr<ID3D12DebugDevice> debug_device;
200   if (FAILED(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&info_queue))) ||
201       FAILED(info_queue.As(&debug_device)))
202      GTEST_SKIP();
203
204   ASSERT_FALSE(info_queue_has_swapchain(debug_device.Get(), info_queue.Get()));
205
206   {
207      window wnd;
208      wnd.show();
209      glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
210      glClear(GL_COLOR_BUFFER_BIT);
211      SwapBuffers(wnd.get_hdc());
212
213      ASSERT_TRUE(info_queue_has_swapchain(debug_device.Get(), info_queue.Get()));
214   }
215
216   ASSERT_FALSE(info_queue_has_swapchain(debug_device.Get(), info_queue.Get()));
217}
218
219#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
220#define WGL_LOSE_CONTEXT_ON_RESET_ARB               0x8252
221using pglGetGraphicsResetStatusARB = GLenum(APIENTRY*)();
222TEST_F(d3d12, context_reset)
223{
224   ComPtr<ID3D12Device5> device;
225   if (FAILED(D3D12CreateDevice(nullptr, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&device))))
226      GTEST_SKIP();
227
228   const int attribs[] = { WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, WGL_LOSE_CONTEXT_ON_RESET_ARB, 0 };
229
230   {
231      window wnd;
232      wnd.recreate_attribs(attribs);
233      EXPECT_TRUE(wnd.valid());
234
235      wnd.show();
236      glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
237      glClear(GL_COLOR_BUFFER_BIT);
238      SwapBuffers(wnd.get_hdc());
239
240      auto glGetGraphicsResetStatusARB = (pglGetGraphicsResetStatusARB)wglGetProcAddress("glGetGraphicsResetStatusARB");
241      if (!glGetGraphicsResetStatusARB)
242         GTEST_FAIL() << "Couldn't get reset function";
243
244      EXPECT_EQ(glGetGraphicsResetStatusARB(), NO_ERROR);
245
246      device->RemoveDevice();
247      device.Reset();
248
249      EXPECT_NE(glGetGraphicsResetStatusARB(), NO_ERROR);
250   }
251
252   {
253      window wnd;
254      EXPECT_TRUE(wnd.valid());
255
256      wnd.recreate_attribs(attribs);
257      EXPECT_TRUE(wnd.valid());
258
259      wnd.show();
260      auto glGetGraphicsResetStatusARB = (pglGetGraphicsResetStatusARB)wglGetProcAddress("glGetGraphicsResetStatusARB");
261      EXPECT_EQ(glGetGraphicsResetStatusARB(), NO_ERROR);
262
263      glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
264      glClear(GL_COLOR_BUFFER_BIT);
265      SwapBuffers(wnd.get_hdc());
266   }
267}
268#endif
269