blob: 46ed89f476828cb8971b388e7c16e05455b40f1c [file] [log] [blame]
Nicolas Capens0bac2852016-05-07 06:09:58 -04001// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
John Bauman89401822014-05-06 15:04:28 -04002//
Nicolas Capens0bac2852016-05-07 06:09:58 -04003// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
John Bauman89401822014-05-06 15:04:28 -04006//
Nicolas Capens0bac2852016-05-07 06:09:58 -04007// http://www.apache.org/licenses/LICENSE-2.0
John Bauman89401822014-05-06 15:04:28 -04008//
Nicolas Capens0bac2852016-05-07 06:09:58 -04009// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
John Bauman89401822014-05-06 15:04:28 -040014
15#include "FrameBufferDD.hpp"
16
Nicolas Capens708c24b2017-10-26 13:07:10 -040017#include "Common/Debug.hpp"
John Bauman89401822014-05-06 15:04:28 -040018
19namespace sw
20{
21 extern bool forceWindowed;
22
23 GUID secondaryDisplay = {0};
24
25 int __stdcall enumDisplayCallback(GUID* guid, char *driverDescription, char *driverName, void *context, HMONITOR monitor)
26 {
27 if(strcmp(driverName, "\\\\.\\DISPLAY2") == 0)
28 {
29 secondaryDisplay = *guid;
30 }
31
32 return 1;
33 }
34
John Bauman66b8ab22014-05-06 15:57:45 -040035 FrameBufferDD::FrameBufferDD(HWND windowHandle, int width, int height, bool fullscreen, bool topLeftOrigin) : FrameBufferWin(windowHandle, width, height, fullscreen, topLeftOrigin)
John Bauman89401822014-05-06 15:04:28 -040036 {
37 directDraw = 0;
38 frontBuffer = 0;
39 backBuffer = 0;
40
Nicolas Capense5a96372017-08-11 15:14:25 -040041 framebuffer = nullptr;
John Bauman89401822014-05-06 15:04:28 -040042
43 ddraw = LoadLibrary("ddraw.dll");
44 DirectDrawCreate = (DIRECTDRAWCREATE)GetProcAddress(ddraw, "DirectDrawCreate");
45 DirectDrawEnumerateExA = (DIRECTDRAWENUMERATEEXA)GetProcAddress(ddraw, "DirectDrawEnumerateExA");
46
47 if(!windowed)
48 {
49 initFullscreen();
50 }
51 else
52 {
53 initWindowed();
54 }
55 }
56
57 FrameBufferDD::~FrameBufferDD()
58 {
59 releaseAll();
60
61 FreeLibrary(ddraw);
62 }
63
64 void FrameBufferDD::createSurfaces()
65 {
66 if(backBuffer)
67 {
68 backBuffer->Release();
69 backBuffer = 0;
70 }
71
72 if(frontBuffer)
73 {
74 frontBuffer->Release();
75 frontBuffer = 0;
76 }
77
78 if(!windowed)
79 {
80 DDSURFACEDESC surfaceDescription = {0};
81 surfaceDescription.dwSize = sizeof(surfaceDescription);
82 surfaceDescription.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
83 surfaceDescription.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
84 surfaceDescription.dwBackBufferCount = 1;
Alexis Hetu1d672442016-06-23 11:24:00 -040085 directDraw->CreateSurface(&surfaceDescription, &frontBuffer, 0);
John Bauman89401822014-05-06 15:04:28 -040086
87 if(frontBuffer)
88 {
89 DDSCAPS surfaceCapabilties = {0};
90 surfaceCapabilties.dwCaps = DDSCAPS_BACKBUFFER;
91 frontBuffer->GetAttachedSurface(&surfaceCapabilties, &backBuffer);
92 backBuffer->AddRef();
93 }
94 }
95 else
96 {
97 IDirectDrawClipper *clipper;
Nicolas Capens0bac2852016-05-07 06:09:58 -040098
John Bauman89401822014-05-06 15:04:28 -040099 DDSURFACEDESC ddsd = {0};
100 ddsd.dwSize = sizeof(ddsd);
101 ddsd.dwFlags = DDSD_CAPS;
102 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
103
104 long result = directDraw->CreateSurface(&ddsd, &frontBuffer, 0);
105 directDraw->GetDisplayMode(&ddsd);
Nicolas Capens0bac2852016-05-07 06:09:58 -0400106
Nicolas Capens10219e72014-05-07 00:17:20 -0400107 switch(ddsd.ddpfPixelFormat.dwRGBBitCount)
108 {
Nicolas Capense5a96372017-08-11 15:14:25 -0400109 case 32: format = FORMAT_X8R8G8B8; break;
110 case 24: format = FORMAT_R8G8B8; break;
111 case 16: format = FORMAT_R5G6B5; break;
112 default: format = FORMAT_NULL; break;
Nicolas Capens10219e72014-05-07 00:17:20 -0400113 }
John Bauman89401822014-05-06 15:04:28 -0400114
Nicolas Capense5a96372017-08-11 15:14:25 -0400115 if((result != DD_OK && result != DDERR_PRIMARYSURFACEALREADYEXISTS) || (format == FORMAT_NULL))
John Bauman89401822014-05-06 15:04:28 -0400116 {
John Bauman19bac1e2014-05-06 15:23:49 -0400117 assert(!"Failed to initialize graphics: Incompatible display mode.");
John Bauman89401822014-05-06 15:04:28 -0400118 }
John Bauman19bac1e2014-05-06 15:23:49 -0400119 else
120 {
121 ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
122 ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
123 ddsd.dwWidth = width;
124 ddsd.dwHeight = height;
John Bauman89401822014-05-06 15:04:28 -0400125
John Bauman19bac1e2014-05-06 15:23:49 -0400126 directDraw->CreateSurface(&ddsd, &backBuffer, 0);
John Bauman89401822014-05-06 15:04:28 -0400127
John Bauman19bac1e2014-05-06 15:23:49 -0400128 directDraw->CreateClipper(0, &clipper, 0);
129 clipper->SetHWnd(0, windowHandle);
130 frontBuffer->SetClipper(clipper);
131 clipper->Release();
132 }
John Bauman89401822014-05-06 15:04:28 -0400133 }
134 }
135
136 bool FrameBufferDD::readySurfaces()
137 {
138 if(!frontBuffer || !backBuffer)
139 {
140 createSurfaces();
141 }
142
143 if(frontBuffer && backBuffer)
144 {
145 if(frontBuffer->IsLost() || backBuffer->IsLost())
146 {
147 restoreSurfaces();
148 }
149
150 if(frontBuffer && backBuffer)
151 {
152 if(!frontBuffer->IsLost() && !backBuffer->IsLost())
153 {
154 return true;
155 }
156 }
157 }
158
159 return false;
160 }
161
162 void FrameBufferDD::updateClipper(HWND windowOverride)
163 {
164 if(windowed)
165 {
166 if(frontBuffer)
167 {
168 HWND window = windowOverride ? windowOverride : windowHandle;
169
170 IDirectDrawClipper *clipper;
171 frontBuffer->GetClipper(&clipper);
172 clipper->SetHWnd(0, window);
173 clipper->Release();
174 }
175 }
176 }
177
178 void FrameBufferDD::restoreSurfaces()
179 {
180 long result1 = frontBuffer->Restore();
181 long result2 = backBuffer->Restore();
182
183 if(result1 != DD_OK || result2 != DD_OK) // Surfaces could not be restored; recreate them
184 {
185 createSurfaces();
186 }
187 }
188
189 void FrameBufferDD::initFullscreen()
190 {
191 releaseAll();
192
193 if(true) // Render to primary display
194 {
195 DirectDrawCreate(0, &directDraw, 0);
196 }
197 else // Render to secondary display
198 {
199 DirectDrawEnumerateEx(&enumDisplayCallback, 0, DDENUM_ATTACHEDSECONDARYDEVICES);
200 DirectDrawCreate(&secondaryDisplay, &directDraw, 0);
201 }
202
203 directDraw->SetCooperativeLevel(windowHandle, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
204
205 long result;
206
207 do
208 {
Nicolas Capense5a96372017-08-11 15:14:25 -0400209 format = FORMAT_X8R8G8B8;
Nicolas Capens10219e72014-05-07 00:17:20 -0400210 result = directDraw->SetDisplayMode(width, height, 32);
John Bauman89401822014-05-06 15:04:28 -0400211
212 if(result == DDERR_INVALIDMODE)
213 {
Nicolas Capense5a96372017-08-11 15:14:25 -0400214 format = FORMAT_R8G8B8;
Nicolas Capens10219e72014-05-07 00:17:20 -0400215 result = directDraw->SetDisplayMode(width, height, 24);
John Bauman89401822014-05-06 15:04:28 -0400216
217 if(result == DDERR_INVALIDMODE)
218 {
Nicolas Capense5a96372017-08-11 15:14:25 -0400219 format = FORMAT_R5G6B5;
Nicolas Capens10219e72014-05-07 00:17:20 -0400220 result = directDraw->SetDisplayMode(width, height, 16);
John Bauman89401822014-05-06 15:04:28 -0400221
222 if(result == DDERR_INVALIDMODE)
223 {
John Bauman19bac1e2014-05-06 15:23:49 -0400224 assert(!"Failed to initialize graphics: Display mode not supported.");
John Bauman89401822014-05-06 15:04:28 -0400225 }
226 }
227 }
228
229 if(result != DD_OK)
230 {
231 Sleep(1);
232 }
233 }
234 while(result != DD_OK);
235
236 createSurfaces();
237
238 updateBounds(windowHandle);
239 }
240
241 void FrameBufferDD::initWindowed()
242 {
243 releaseAll();
244
245 DirectDrawCreate(0, &directDraw, 0);
246 directDraw->SetCooperativeLevel(windowHandle, DDSCL_NORMAL);
247
248 createSurfaces();
249
250 updateBounds(windowHandle);
251 }
252
Nicolas Capens241f7892015-12-30 23:40:45 -0500253 void FrameBufferDD::flip(sw::Surface *source)
John Bauman89401822014-05-06 15:04:28 -0400254 {
Nicolas Capens241f7892015-12-30 23:40:45 -0500255 copy(source);
John Bauman89401822014-05-06 15:04:28 -0400256
257 if(!readySurfaces())
258 {
259 return;
260 }
261
262 while(true)
263 {
264 long result;
265
266 if(windowed)
267 {
268 result = frontBuffer->Blt(&bounds, backBuffer, 0, DDBLT_WAIT, 0);
269 }
270 else
271 {
272 result = frontBuffer->Flip(0, DDFLIP_NOVSYNC);
273 }
274
275 if(result != DDERR_WASSTILLDRAWING)
276 {
277 break;
278 }
279
280 Sleep(0);
281 }
282 }
283
Nicolas Capens241f7892015-12-30 23:40:45 -0500284 void FrameBufferDD::blit(sw::Surface *source, const Rect *sourceRect, const Rect *destRect)
John Bauman89401822014-05-06 15:04:28 -0400285 {
Nicolas Capens241f7892015-12-30 23:40:45 -0500286 copy(source);
John Bauman89401822014-05-06 15:04:28 -0400287
288 if(!readySurfaces())
289 {
290 return;
291 }
292
293 RECT dRect;
294
295 if(destRect)
296 {
John Bauman19bac1e2014-05-06 15:23:49 -0400297 dRect.bottom = bounds.top + destRect->y1;
298 dRect.left = bounds.left + destRect->x0;
299 dRect.right = bounds.left + destRect->x1;
300 dRect.top = bounds.top + destRect->y0;
John Bauman89401822014-05-06 15:04:28 -0400301 }
302 else
303 {
304 dRect.bottom = bounds.top + height;
305 dRect.left = bounds.left + 0;
306 dRect.right = bounds.left + width;
307 dRect.top = bounds.top + 0;
308 }
309
310 while(true)
311 {
312 long result = frontBuffer->Blt(&dRect, backBuffer, (LPRECT)sourceRect, DDBLT_WAIT, 0);
313
314 if(result != DDERR_WASSTILLDRAWING)
315 {
316 break;
317 }
318
319 Sleep(0);
320 }
321 }
322
Nicolas Capens241f7892015-12-30 23:40:45 -0500323 void FrameBufferDD::flip(HWND windowOverride, sw::Surface *source)
John Bauman66b8ab22014-05-06 15:57:45 -0400324 {
325 updateClipper(windowOverride);
326 updateBounds(windowOverride);
327
Nicolas Capens241f7892015-12-30 23:40:45 -0500328 flip(source);
John Bauman66b8ab22014-05-06 15:57:45 -0400329 }
330
Nicolas Capens241f7892015-12-30 23:40:45 -0500331 void FrameBufferDD::blit(HWND windowOverride, sw::Surface *source, const Rect *sourceRect, const Rect *destRect)
John Bauman66b8ab22014-05-06 15:57:45 -0400332 {
333 updateClipper(windowOverride);
334 updateBounds(windowOverride);
335
Nicolas Capens241f7892015-12-30 23:40:45 -0500336 blit(source, sourceRect, destRect);
John Bauman66b8ab22014-05-06 15:57:45 -0400337 }
338
John Bauman89401822014-05-06 15:04:28 -0400339 void FrameBufferDD::screenshot(void *destBuffer)
340 {
341 if(!readySurfaces())
342 {
343 return;
344 }
345
346 DDSURFACEDESC DDSD;
347 DDSD.dwSize = sizeof(DDSD);
348
349 long result = frontBuffer->Lock(0, &DDSD, DDLOCK_WAIT, 0);
350
351 if(result == DD_OK)
352 {
353 int width = DDSD.dwWidth;
354 int height = DDSD.dwHeight;
John Bauman89401822014-05-06 15:04:28 -0400355 int stride = DDSD.lPitch;
356
357 void *sourceBuffer = DDSD.lpSurface;
358
359 for(int y = 0; y < height; y++)
360 {
361 memcpy(destBuffer, sourceBuffer, width * 4); // FIXME: Assumes 32-bit buffer
362
363 (char*&)sourceBuffer += stride;
364 (char*&)destBuffer += 4 * width;
365 }
366
367 frontBuffer->Unlock(0);
368 }
369 }
370
371 void FrameBufferDD::setGammaRamp(GammaRamp *gammaRamp, bool calibrate)
372 {
373 IDirectDrawGammaControl *gammaControl = 0;
374
375 if(frontBuffer)
376 {
Nicolas Capens0bac2852016-05-07 06:09:58 -0400377 frontBuffer->QueryInterface(IID_IDirectDrawGammaControl, (void**)&gammaControl);
John Bauman89401822014-05-06 15:04:28 -0400378
379 if(gammaControl)
380 {
381 gammaControl->SetGammaRamp(calibrate ? DDSGR_CALIBRATE : 0, (DDGAMMARAMP*)gammaRamp);
382
383 gammaControl->Release();
384 }
385 }
386 }
387
388 void FrameBufferDD::getGammaRamp(GammaRamp *gammaRamp)
389 {
390 IDirectDrawGammaControl *gammaControl = 0;
391
392 if(frontBuffer)
393 {
Nicolas Capens0bac2852016-05-07 06:09:58 -0400394 frontBuffer->QueryInterface(IID_IDirectDrawGammaControl, (void**)&gammaControl);
John Bauman89401822014-05-06 15:04:28 -0400395
396 if(gammaControl)
397 {
398 gammaControl->GetGammaRamp(0, (DDGAMMARAMP*)gammaRamp);
399
400 gammaControl->Release();
401 }
402 }
403 }
404
405 void *FrameBufferDD::lock()
406 {
Nicolas Capense5a96372017-08-11 15:14:25 -0400407 if(framebuffer)
John Bauman89401822014-05-06 15:04:28 -0400408 {
Nicolas Capense5a96372017-08-11 15:14:25 -0400409 return framebuffer;
John Bauman89401822014-05-06 15:04:28 -0400410 }
Nicolas Capens0bac2852016-05-07 06:09:58 -0400411
John Bauman89401822014-05-06 15:04:28 -0400412 if(!readySurfaces())
413 {
Nicolas Capense5a96372017-08-11 15:14:25 -0400414 return nullptr;
John Bauman89401822014-05-06 15:04:28 -0400415 }
416
417 DDSURFACEDESC DDSD;
418 DDSD.dwSize = sizeof(DDSD);
419
420 long result = backBuffer->Lock(0, &DDSD, DDLOCK_WAIT, 0);
421
422 if(result == DD_OK)
423 {
424 width = DDSD.dwWidth;
425 height = DDSD.dwHeight;
John Bauman89401822014-05-06 15:04:28 -0400426 stride = DDSD.lPitch;
427
Nicolas Capense5a96372017-08-11 15:14:25 -0400428 framebuffer = DDSD.lpSurface;
John Bauman89401822014-05-06 15:04:28 -0400429
Nicolas Capense5a96372017-08-11 15:14:25 -0400430 return framebuffer;
John Bauman89401822014-05-06 15:04:28 -0400431 }
432
Nicolas Capense5a96372017-08-11 15:14:25 -0400433 return nullptr;
John Bauman89401822014-05-06 15:04:28 -0400434 }
435
436 void FrameBufferDD::unlock()
437 {
Nicolas Capense5a96372017-08-11 15:14:25 -0400438 if(!framebuffer || !backBuffer) return;
John Bauman89401822014-05-06 15:04:28 -0400439
440 backBuffer->Unlock(0);
441
Nicolas Capense5a96372017-08-11 15:14:25 -0400442 framebuffer = nullptr;
John Bauman89401822014-05-06 15:04:28 -0400443 }
444
445 void FrameBufferDD::drawText(int x, int y, const char *string, ...)
446 {
447 char buffer[256];
448 va_list arglist;
449
450 va_start(arglist, string);
451 vsprintf(buffer, string, arglist);
452 va_end(arglist);
453
454 HDC hdc;
455
456 backBuffer->GetDC(&hdc);
457
458 SetBkColor(hdc, RGB(0, 0, 255));
459 SetTextColor(hdc, RGB(255, 255, 255));
460
461 TextOut(hdc, x, y, buffer, lstrlen(buffer));
462
463 backBuffer->ReleaseDC(hdc);
464 }
465
466 bool FrameBufferDD::getScanline(bool &inVerticalBlank, unsigned int &scanline)
467 {
468 HRESULT result = directDraw->GetScanLine((unsigned long*)&scanline);
469
470 if(result == DD_OK)
471 {
472 inVerticalBlank = false;
473 }
474 else if(result == DDERR_VERTICALBLANKINPROGRESS)
475 {
476 inVerticalBlank = true;
477 }
478 else if(result == DDERR_UNSUPPORTED)
479 {
480 return false;
481 }
482 else ASSERT(false);
483
484 return true;
485 }
486
487 void FrameBufferDD::releaseAll()
488 {
489 unlock();
490
491 if(backBuffer)
492 {
493 backBuffer->Release();
494 backBuffer = 0;
495 }
496
497 if(frontBuffer)
498 {
499 frontBuffer->Release();
500 frontBuffer = 0;
501 }
502
503 if(directDraw)
504 {
505 directDraw->SetCooperativeLevel(0, DDSCL_NORMAL);
506 directDraw->Release();
507 directDraw = 0;
508 }
509 }
510}