1 // Copyright 2016 The SwiftShader Authors. All Rights Reserved.
2 //
3 // 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
6 //
7 //    http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // 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.
14 
15 #include "FrameBufferDD.hpp"
16 
17 #include "Common/Debug.hpp"
18 
19 namespace sw
20 {
21 	extern bool forceWindowed;
22 
23 	GUID secondaryDisplay = {0};
24 
enumDisplayCallback(GUID * guid,char * driverDescription,char * driverName,void * context,HMONITOR monitor)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 
FrameBufferDD(HWND windowHandle,int width,int height,bool fullscreen,bool topLeftOrigin)35 	FrameBufferDD::FrameBufferDD(HWND windowHandle, int width, int height, bool fullscreen, bool topLeftOrigin) : FrameBufferWin(windowHandle, width, height, fullscreen, topLeftOrigin)
36 	{
37 		directDraw = 0;
38 		frontBuffer = 0;
39 		backBuffer = 0;
40 
41 		framebuffer = nullptr;
42 
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 
~FrameBufferDD()57 	FrameBufferDD::~FrameBufferDD()
58 	{
59 		releaseAll();
60 
61 		FreeLibrary(ddraw);
62 	}
63 
createSurfaces()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;
85 			directDraw->CreateSurface(&surfaceDescription, &frontBuffer, 0);
86 
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;
98 
99 			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);
106 
107 			switch(ddsd.ddpfPixelFormat.dwRGBBitCount)
108 			{
109 			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;
113 			}
114 
115 			if((result != DD_OK && result != DDERR_PRIMARYSURFACEALREADYEXISTS) || (format == FORMAT_NULL))
116 			{
117 				assert(!"Failed to initialize graphics: Incompatible display mode.");
118 			}
119 			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;
125 
126 				directDraw->CreateSurface(&ddsd, &backBuffer, 0);
127 
128 				directDraw->CreateClipper(0, &clipper, 0);
129 				clipper->SetHWnd(0, windowHandle);
130 				frontBuffer->SetClipper(clipper);
131 				clipper->Release();
132 			}
133 		}
134 	}
135 
readySurfaces()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 
updateClipper(HWND windowOverride)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 
restoreSurfaces()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 
initFullscreen()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 		{
209 			format = FORMAT_X8R8G8B8;
210 			result = directDraw->SetDisplayMode(width, height, 32);
211 
212 			if(result == DDERR_INVALIDMODE)
213 			{
214 				format = FORMAT_R8G8B8;
215 				result = directDraw->SetDisplayMode(width, height, 24);
216 
217 				if(result == DDERR_INVALIDMODE)
218 				{
219 					format = FORMAT_R5G6B5;
220 					result = directDraw->SetDisplayMode(width, height, 16);
221 
222 					if(result == DDERR_INVALIDMODE)
223 					{
224 						assert(!"Failed to initialize graphics: Display mode not supported.");
225 					}
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 
initWindowed()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 
flip(sw::Surface * source)253 	void FrameBufferDD::flip(sw::Surface *source)
254 	{
255 		copy(source);
256 
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 
blit(sw::Surface * source,const Rect * sourceRect,const Rect * destRect)284 	void FrameBufferDD::blit(sw::Surface *source, const Rect *sourceRect, const Rect *destRect)
285 	{
286 		copy(source);
287 
288 		if(!readySurfaces())
289 		{
290 			return;
291 		}
292 
293 		RECT dRect;
294 
295 		if(destRect)
296 		{
297 			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;
301 		}
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 
flip(HWND windowOverride,sw::Surface * source)323 	void FrameBufferDD::flip(HWND windowOverride, sw::Surface *source)
324 	{
325 		updateClipper(windowOverride);
326 		updateBounds(windowOverride);
327 
328 		flip(source);
329 	}
330 
blit(HWND windowOverride,sw::Surface * source,const Rect * sourceRect,const Rect * destRect)331 	void FrameBufferDD::blit(HWND windowOverride, sw::Surface *source, const Rect *sourceRect, const Rect *destRect)
332 	{
333 		updateClipper(windowOverride);
334 		updateBounds(windowOverride);
335 
336 		blit(source, sourceRect, destRect);
337 	}
338 
screenshot(void * destBuffer)339 	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;
355 			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 
setGammaRamp(GammaRamp * gammaRamp,bool calibrate)371 	void FrameBufferDD::setGammaRamp(GammaRamp *gammaRamp, bool calibrate)
372 	{
373 		IDirectDrawGammaControl *gammaControl = 0;
374 
375 		if(frontBuffer)
376 		{
377 			frontBuffer->QueryInterface(IID_IDirectDrawGammaControl, (void**)&gammaControl);
378 
379 			if(gammaControl)
380 			{
381 				gammaControl->SetGammaRamp(calibrate ? DDSGR_CALIBRATE : 0, (DDGAMMARAMP*)gammaRamp);
382 
383 				gammaControl->Release();
384 			}
385 		}
386 	}
387 
getGammaRamp(GammaRamp * gammaRamp)388 	void FrameBufferDD::getGammaRamp(GammaRamp *gammaRamp)
389 	{
390 		IDirectDrawGammaControl *gammaControl = 0;
391 
392 		if(frontBuffer)
393 		{
394 			frontBuffer->QueryInterface(IID_IDirectDrawGammaControl, (void**)&gammaControl);
395 
396 			if(gammaControl)
397 			{
398 				gammaControl->GetGammaRamp(0, (DDGAMMARAMP*)gammaRamp);
399 
400 				gammaControl->Release();
401 			}
402 		}
403 	}
404 
lock()405 	void *FrameBufferDD::lock()
406 	{
407 		if(framebuffer)
408 		{
409 			return framebuffer;
410 		}
411 
412 		if(!readySurfaces())
413 		{
414 			return nullptr;
415 		}
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;
426 			stride = DDSD.lPitch;
427 
428 			framebuffer = DDSD.lpSurface;
429 
430 			return framebuffer;
431 		}
432 
433 		return nullptr;
434 	}
435 
unlock()436 	void FrameBufferDD::unlock()
437 	{
438 		if(!framebuffer || !backBuffer) return;
439 
440 		backBuffer->Unlock(0);
441 
442 		framebuffer = nullptr;
443 	}
444 
drawText(int x,int y,const char * string,...)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 
getScanline(bool & inVerticalBlank,unsigned int & scanline)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 
releaseAll()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 }
511