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