Home | History | Annotate | Download | only in Main
      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 "Debug.hpp"
     18 
     19 namespace 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 
     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 		locked = 0;
     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 
     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;
     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: destFormat = FORMAT_X8R8G8B8; break;
    110 			case 24: destFormat = FORMAT_R8G8B8;   break;
    111 			case 16: destFormat = FORMAT_R5G6B5;   break;
    112 			default: destFormat = FORMAT_NULL;     break;
    113 			}
    114 
    115 			if((result != DD_OK && result != DDERR_PRIMARYSURFACEALREADYEXISTS) || (destFormat == 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 
    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 		{
    209 			destFormat = FORMAT_X8R8G8B8;
    210 			result = directDraw->SetDisplayMode(width, height, 32);
    211 
    212 			if(result == DDERR_INVALIDMODE)
    213 			{
    214 				destFormat = FORMAT_R8G8B8;
    215 				result = directDraw->SetDisplayMode(width, height, 24);
    216 
    217 				if(result == DDERR_INVALIDMODE)
    218 				{
    219 					destFormat = 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 
    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 
    253 	void FrameBufferDD::flip(void *source, Format sourceFormat, size_t sourceStride)
    254 	{
    255 		copy(source, sourceFormat, sourceStride);
    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 
    284 	void FrameBufferDD::blit(void *source, const Rect *sourceRect, const Rect *destRect, Format sourceFormat, size_t sourceStride)
    285 	{
    286 		copy(source, sourceFormat, sourceStride);
    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 
    323 	void FrameBufferDD::flip(HWND windowOverride, void *source, Format sourceFormat, size_t sourceStride)
    324 	{
    325 		updateClipper(windowOverride);
    326 		updateBounds(windowOverride);
    327 
    328 		flip(source, sourceFormat, sourceStride);
    329 	}
    330 
    331 	void FrameBufferDD::blit(HWND windowOverride, void *source, const Rect *sourceRect, const Rect *destRect, Format sourceFormat, size_t sourceStride)
    332 	{
    333 		updateClipper(windowOverride);
    334 		updateBounds(windowOverride);
    335 
    336 		blit(source, sourceRect, destRect, sourceFormat, sourceStride);
    337 	}
    338 
    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 
    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 
    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 
    405 	void *FrameBufferDD::lock()
    406 	{
    407 		if(locked)
    408 		{
    409 			return locked;
    410 		}
    411 
    412 		if(!readySurfaces())
    413 		{
    414 			return 0;
    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 			locked = DDSD.lpSurface;
    429 
    430 			return locked;
    431 		}
    432 
    433 		return 0;
    434 	}
    435 
    436 	void FrameBufferDD::unlock()
    437 	{
    438 		if(!locked || !backBuffer) return;
    439 
    440 		backBuffer->Unlock(0);
    441 
    442 		locked = 0;
    443 	}
    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 }
    511