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 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 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 long result = 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 bitDepth = DDSD.ddpfPixelFormat.dwRGBBitCount; 356 int stride = DDSD.lPitch; 357 358 void *sourceBuffer = DDSD.lpSurface; 359 360 for(int y = 0; y < height; y++) 361 { 362 memcpy(destBuffer, sourceBuffer, width * 4); // FIXME: Assumes 32-bit buffer 363 364 (char*&)sourceBuffer += stride; 365 (char*&)destBuffer += 4 * width; 366 } 367 368 frontBuffer->Unlock(0); 369 } 370 } 371 372 void FrameBufferDD::setGammaRamp(GammaRamp *gammaRamp, bool calibrate) 373 { 374 IDirectDrawGammaControl *gammaControl = 0; 375 376 if(frontBuffer) 377 { 378 frontBuffer->QueryInterface(IID_IDirectDrawGammaControl, (void**)&gammaControl); 379 380 if(gammaControl) 381 { 382 gammaControl->SetGammaRamp(calibrate ? DDSGR_CALIBRATE : 0, (DDGAMMARAMP*)gammaRamp); 383 384 gammaControl->Release(); 385 } 386 } 387 } 388 389 void FrameBufferDD::getGammaRamp(GammaRamp *gammaRamp) 390 { 391 IDirectDrawGammaControl *gammaControl = 0; 392 393 if(frontBuffer) 394 { 395 frontBuffer->QueryInterface(IID_IDirectDrawGammaControl, (void**)&gammaControl); 396 397 if(gammaControl) 398 { 399 gammaControl->GetGammaRamp(0, (DDGAMMARAMP*)gammaRamp); 400 401 gammaControl->Release(); 402 } 403 } 404 } 405 406 void *FrameBufferDD::lock() 407 { 408 if(locked) 409 { 410 return locked; 411 } 412 413 if(!readySurfaces()) 414 { 415 return 0; 416 } 417 418 DDSURFACEDESC DDSD; 419 DDSD.dwSize = sizeof(DDSD); 420 421 long result = backBuffer->Lock(0, &DDSD, DDLOCK_WAIT, 0); 422 423 if(result == DD_OK) 424 { 425 width = DDSD.dwWidth; 426 height = DDSD.dwHeight; 427 int bitDepth = DDSD.ddpfPixelFormat.dwRGBBitCount; 428 stride = DDSD.lPitch; 429 430 locked = DDSD.lpSurface; 431 432 return locked; 433 } 434 435 return 0; 436 } 437 438 void FrameBufferDD::unlock() 439 { 440 if(!locked || !backBuffer) return; 441 442 backBuffer->Unlock(0); 443 444 locked = 0; 445 } 446 447 void FrameBufferDD::drawText(int x, int y, const char *string, ...) 448 { 449 char buffer[256]; 450 va_list arglist; 451 452 va_start(arglist, string); 453 vsprintf(buffer, string, arglist); 454 va_end(arglist); 455 456 HDC hdc; 457 458 backBuffer->GetDC(&hdc); 459 460 SetBkColor(hdc, RGB(0, 0, 255)); 461 SetTextColor(hdc, RGB(255, 255, 255)); 462 463 TextOut(hdc, x, y, buffer, lstrlen(buffer)); 464 465 backBuffer->ReleaseDC(hdc); 466 } 467 468 bool FrameBufferDD::getScanline(bool &inVerticalBlank, unsigned int &scanline) 469 { 470 HRESULT result = directDraw->GetScanLine((unsigned long*)&scanline); 471 472 if(result == DD_OK) 473 { 474 inVerticalBlank = false; 475 } 476 else if(result == DDERR_VERTICALBLANKINPROGRESS) 477 { 478 inVerticalBlank = true; 479 } 480 else if(result == DDERR_UNSUPPORTED) 481 { 482 return false; 483 } 484 else ASSERT(false); 485 486 return true; 487 } 488 489 void FrameBufferDD::releaseAll() 490 { 491 unlock(); 492 493 if(backBuffer) 494 { 495 backBuffer->Release(); 496 backBuffer = 0; 497 } 498 499 if(frontBuffer) 500 { 501 frontBuffer->Release(); 502 frontBuffer = 0; 503 } 504 505 if(directDraw) 506 { 507 directDraw->SetCooperativeLevel(0, DDSCL_NORMAL); 508 directDraw->Release(); 509 directDraw = 0; 510 } 511 } 512} 513