|  | // Copyright 2016 The SwiftShader Authors. All Rights Reserved. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //    http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #include "FrameBufferDD.hpp" | 
|  |  | 
|  | #include "Debug.hpp" | 
|  |  | 
|  | namespace sw | 
|  | { | 
|  | extern bool forceWindowed; | 
|  |  | 
|  | GUID secondaryDisplay = {0}; | 
|  |  | 
|  | int __stdcall enumDisplayCallback(GUID* guid, char *driverDescription, char *driverName, void *context, HMONITOR monitor) | 
|  | { | 
|  | if(strcmp(driverName, "\\\\.\\DISPLAY2") == 0) | 
|  | { | 
|  | secondaryDisplay = *guid; | 
|  | } | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | FrameBufferDD::FrameBufferDD(HWND windowHandle, int width, int height, bool fullscreen, bool topLeftOrigin) : FrameBufferWin(windowHandle, width, height, fullscreen, topLeftOrigin) | 
|  | { | 
|  | directDraw = 0; | 
|  | frontBuffer = 0; | 
|  | backBuffer = 0; | 
|  |  | 
|  | locked = 0; | 
|  |  | 
|  | ddraw = LoadLibrary("ddraw.dll"); | 
|  | DirectDrawCreate = (DIRECTDRAWCREATE)GetProcAddress(ddraw, "DirectDrawCreate"); | 
|  | DirectDrawEnumerateExA = (DIRECTDRAWENUMERATEEXA)GetProcAddress(ddraw, "DirectDrawEnumerateExA"); | 
|  |  | 
|  | if(!windowed) | 
|  | { | 
|  | initFullscreen(); | 
|  | } | 
|  | else | 
|  | { | 
|  | initWindowed(); | 
|  | } | 
|  | } | 
|  |  | 
|  | FrameBufferDD::~FrameBufferDD() | 
|  | { | 
|  | releaseAll(); | 
|  |  | 
|  | FreeLibrary(ddraw); | 
|  | } | 
|  |  | 
|  | void FrameBufferDD::createSurfaces() | 
|  | { | 
|  | if(backBuffer) | 
|  | { | 
|  | backBuffer->Release(); | 
|  | backBuffer = 0; | 
|  | } | 
|  |  | 
|  | if(frontBuffer) | 
|  | { | 
|  | frontBuffer->Release(); | 
|  | frontBuffer = 0; | 
|  | } | 
|  |  | 
|  | if(!windowed) | 
|  | { | 
|  | DDSURFACEDESC surfaceDescription = {0}; | 
|  | surfaceDescription.dwSize = sizeof(surfaceDescription); | 
|  | surfaceDescription.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; | 
|  | surfaceDescription.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX; | 
|  | surfaceDescription.dwBackBufferCount = 1; | 
|  | directDraw->CreateSurface(&surfaceDescription, &frontBuffer, 0); | 
|  |  | 
|  | if(frontBuffer) | 
|  | { | 
|  | DDSCAPS surfaceCapabilties = {0}; | 
|  | surfaceCapabilties.dwCaps = DDSCAPS_BACKBUFFER; | 
|  | frontBuffer->GetAttachedSurface(&surfaceCapabilties, &backBuffer); | 
|  | backBuffer->AddRef(); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | IDirectDrawClipper *clipper; | 
|  |  | 
|  | DDSURFACEDESC ddsd = {0}; | 
|  | ddsd.dwSize = sizeof(ddsd); | 
|  | ddsd.dwFlags = DDSD_CAPS; | 
|  | ddsd.ddsCaps.dwCaps	= DDSCAPS_PRIMARYSURFACE; | 
|  |  | 
|  | long result = directDraw->CreateSurface(&ddsd, &frontBuffer, 0); | 
|  | directDraw->GetDisplayMode(&ddsd); | 
|  |  | 
|  | switch(ddsd.ddpfPixelFormat.dwRGBBitCount) | 
|  | { | 
|  | case 32: destFormat = FORMAT_X8R8G8B8; break; | 
|  | case 24: destFormat = FORMAT_R8G8B8;   break; | 
|  | case 16: destFormat = FORMAT_R5G6B5;   break; | 
|  | default: destFormat = FORMAT_NULL;     break; | 
|  | } | 
|  |  | 
|  | if((result != DD_OK && result != DDERR_PRIMARYSURFACEALREADYEXISTS) || (destFormat == FORMAT_NULL)) | 
|  | { | 
|  | assert(!"Failed to initialize graphics: Incompatible display mode."); | 
|  | } | 
|  | else | 
|  | { | 
|  | ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; | 
|  | ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; | 
|  | ddsd.dwWidth = width; | 
|  | ddsd.dwHeight = height; | 
|  |  | 
|  | directDraw->CreateSurface(&ddsd, &backBuffer, 0); | 
|  |  | 
|  | directDraw->CreateClipper(0, &clipper, 0); | 
|  | clipper->SetHWnd(0, windowHandle); | 
|  | frontBuffer->SetClipper(clipper); | 
|  | clipper->Release(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool FrameBufferDD::readySurfaces() | 
|  | { | 
|  | if(!frontBuffer || !backBuffer) | 
|  | { | 
|  | createSurfaces(); | 
|  | } | 
|  |  | 
|  | if(frontBuffer && backBuffer) | 
|  | { | 
|  | if(frontBuffer->IsLost() || backBuffer->IsLost()) | 
|  | { | 
|  | restoreSurfaces(); | 
|  | } | 
|  |  | 
|  | if(frontBuffer && backBuffer) | 
|  | { | 
|  | if(!frontBuffer->IsLost() && !backBuffer->IsLost()) | 
|  | { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | void FrameBufferDD::updateClipper(HWND windowOverride) | 
|  | { | 
|  | if(windowed) | 
|  | { | 
|  | if(frontBuffer) | 
|  | { | 
|  | HWND window = windowOverride ? windowOverride : windowHandle; | 
|  |  | 
|  | IDirectDrawClipper *clipper; | 
|  | frontBuffer->GetClipper(&clipper); | 
|  | clipper->SetHWnd(0, window); | 
|  | clipper->Release(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void FrameBufferDD::restoreSurfaces() | 
|  | { | 
|  | long result1 = frontBuffer->Restore(); | 
|  | long result2 = backBuffer->Restore(); | 
|  |  | 
|  | if(result1 != DD_OK || result2 != DD_OK)   // Surfaces could not be restored; recreate them | 
|  | { | 
|  | createSurfaces(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void FrameBufferDD::initFullscreen() | 
|  | { | 
|  | releaseAll(); | 
|  |  | 
|  | if(true)   // Render to primary display | 
|  | { | 
|  | DirectDrawCreate(0, &directDraw, 0); | 
|  | } | 
|  | else   // Render to secondary display | 
|  | { | 
|  | DirectDrawEnumerateEx(&enumDisplayCallback, 0, DDENUM_ATTACHEDSECONDARYDEVICES); | 
|  | DirectDrawCreate(&secondaryDisplay, &directDraw, 0); | 
|  | } | 
|  |  | 
|  | directDraw->SetCooperativeLevel(windowHandle, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); | 
|  |  | 
|  | long result; | 
|  |  | 
|  | do | 
|  | { | 
|  | destFormat = FORMAT_X8R8G8B8; | 
|  | result = directDraw->SetDisplayMode(width, height, 32); | 
|  |  | 
|  | if(result == DDERR_INVALIDMODE) | 
|  | { | 
|  | destFormat = FORMAT_R8G8B8; | 
|  | result = directDraw->SetDisplayMode(width, height, 24); | 
|  |  | 
|  | if(result == DDERR_INVALIDMODE) | 
|  | { | 
|  | destFormat = FORMAT_R5G6B5; | 
|  | result = directDraw->SetDisplayMode(width, height, 16); | 
|  |  | 
|  | if(result == DDERR_INVALIDMODE) | 
|  | { | 
|  | assert(!"Failed to initialize graphics: Display mode not supported."); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if(result != DD_OK) | 
|  | { | 
|  | Sleep(1); | 
|  | } | 
|  | } | 
|  | while(result != DD_OK); | 
|  |  | 
|  | createSurfaces(); | 
|  |  | 
|  | updateBounds(windowHandle); | 
|  | } | 
|  |  | 
|  | void FrameBufferDD::initWindowed() | 
|  | { | 
|  | releaseAll(); | 
|  |  | 
|  | DirectDrawCreate(0, &directDraw, 0); | 
|  | directDraw->SetCooperativeLevel(windowHandle, DDSCL_NORMAL); | 
|  |  | 
|  | createSurfaces(); | 
|  |  | 
|  | updateBounds(windowHandle); | 
|  | } | 
|  |  | 
|  | void FrameBufferDD::flip(void *source, Format sourceFormat, size_t sourceStride) | 
|  | { | 
|  | copy(source, sourceFormat, sourceStride); | 
|  |  | 
|  | if(!readySurfaces()) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | while(true) | 
|  | { | 
|  | long result; | 
|  |  | 
|  | if(windowed) | 
|  | { | 
|  | result = frontBuffer->Blt(&bounds, backBuffer, 0, DDBLT_WAIT, 0); | 
|  | } | 
|  | else | 
|  | { | 
|  | result = frontBuffer->Flip(0, DDFLIP_NOVSYNC); | 
|  | } | 
|  |  | 
|  | if(result != DDERR_WASSTILLDRAWING) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | Sleep(0); | 
|  | } | 
|  | } | 
|  |  | 
|  | void FrameBufferDD::blit(void *source, const Rect *sourceRect, const Rect *destRect, Format sourceFormat, size_t sourceStride) | 
|  | { | 
|  | copy(source, sourceFormat, sourceStride); | 
|  |  | 
|  | if(!readySurfaces()) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | RECT dRect; | 
|  |  | 
|  | if(destRect) | 
|  | { | 
|  | dRect.bottom = bounds.top + destRect->y1; | 
|  | dRect.left = bounds.left + destRect->x0; | 
|  | dRect.right = bounds.left + destRect->x1; | 
|  | dRect.top = bounds.top + destRect->y0; | 
|  | } | 
|  | else | 
|  | { | 
|  | dRect.bottom = bounds.top + height; | 
|  | dRect.left = bounds.left + 0; | 
|  | dRect.right = bounds.left + width; | 
|  | dRect.top = bounds.top + 0; | 
|  | } | 
|  |  | 
|  | while(true) | 
|  | { | 
|  | long result = frontBuffer->Blt(&dRect, backBuffer, (LPRECT)sourceRect, DDBLT_WAIT, 0); | 
|  |  | 
|  | if(result != DDERR_WASSTILLDRAWING) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | Sleep(0); | 
|  | } | 
|  | } | 
|  |  | 
|  | void FrameBufferDD::flip(HWND windowOverride, void *source, Format sourceFormat, size_t sourceStride) | 
|  | { | 
|  | updateClipper(windowOverride); | 
|  | updateBounds(windowOverride); | 
|  |  | 
|  | flip(source, sourceFormat, sourceStride); | 
|  | } | 
|  |  | 
|  | void FrameBufferDD::blit(HWND windowOverride, void *source, const Rect *sourceRect, const Rect *destRect, Format sourceFormat, size_t sourceStride) | 
|  | { | 
|  | updateClipper(windowOverride); | 
|  | updateBounds(windowOverride); | 
|  |  | 
|  | blit(source, sourceRect, destRect, sourceFormat, sourceStride); | 
|  | } | 
|  |  | 
|  | void FrameBufferDD::screenshot(void *destBuffer) | 
|  | { | 
|  | if(!readySurfaces()) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | DDSURFACEDESC DDSD; | 
|  | DDSD.dwSize = sizeof(DDSD); | 
|  |  | 
|  | long result = frontBuffer->Lock(0, &DDSD, DDLOCK_WAIT, 0); | 
|  |  | 
|  | if(result == DD_OK) | 
|  | { | 
|  | int width = DDSD.dwWidth; | 
|  | int height = DDSD.dwHeight; | 
|  | int stride = DDSD.lPitch; | 
|  |  | 
|  | void *sourceBuffer = DDSD.lpSurface; | 
|  |  | 
|  | for(int y = 0; y < height; y++) | 
|  | { | 
|  | memcpy(destBuffer, sourceBuffer, width * 4);   // FIXME: Assumes 32-bit buffer | 
|  |  | 
|  | (char*&)sourceBuffer += stride; | 
|  | (char*&)destBuffer += 4 * width; | 
|  | } | 
|  |  | 
|  | frontBuffer->Unlock(0); | 
|  | } | 
|  | } | 
|  |  | 
|  | void FrameBufferDD::setGammaRamp(GammaRamp *gammaRamp, bool calibrate) | 
|  | { | 
|  | IDirectDrawGammaControl *gammaControl = 0; | 
|  |  | 
|  | if(frontBuffer) | 
|  | { | 
|  | frontBuffer->QueryInterface(IID_IDirectDrawGammaControl, (void**)&gammaControl); | 
|  |  | 
|  | if(gammaControl) | 
|  | { | 
|  | gammaControl->SetGammaRamp(calibrate ? DDSGR_CALIBRATE : 0, (DDGAMMARAMP*)gammaRamp); | 
|  |  | 
|  | gammaControl->Release(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void FrameBufferDD::getGammaRamp(GammaRamp *gammaRamp) | 
|  | { | 
|  | IDirectDrawGammaControl *gammaControl = 0; | 
|  |  | 
|  | if(frontBuffer) | 
|  | { | 
|  | frontBuffer->QueryInterface(IID_IDirectDrawGammaControl, (void**)&gammaControl); | 
|  |  | 
|  | if(gammaControl) | 
|  | { | 
|  | gammaControl->GetGammaRamp(0, (DDGAMMARAMP*)gammaRamp); | 
|  |  | 
|  | gammaControl->Release(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void *FrameBufferDD::lock() | 
|  | { | 
|  | if(locked) | 
|  | { | 
|  | return locked; | 
|  | } | 
|  |  | 
|  | if(!readySurfaces()) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | DDSURFACEDESC DDSD; | 
|  | DDSD.dwSize = sizeof(DDSD); | 
|  |  | 
|  | long result = backBuffer->Lock(0, &DDSD, DDLOCK_WAIT, 0); | 
|  |  | 
|  | if(result == DD_OK) | 
|  | { | 
|  | width = DDSD.dwWidth; | 
|  | height = DDSD.dwHeight; | 
|  | stride = DDSD.lPitch; | 
|  |  | 
|  | locked = DDSD.lpSurface; | 
|  |  | 
|  | return locked; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void FrameBufferDD::unlock() | 
|  | { | 
|  | if(!locked || !backBuffer) return; | 
|  |  | 
|  | backBuffer->Unlock(0); | 
|  |  | 
|  | locked = 0; | 
|  | } | 
|  |  | 
|  | void FrameBufferDD::drawText(int x, int y, const char *string, ...) | 
|  | { | 
|  | char buffer[256]; | 
|  | va_list arglist; | 
|  |  | 
|  | va_start(arglist, string); | 
|  | vsprintf(buffer, string, arglist); | 
|  | va_end(arglist); | 
|  |  | 
|  | HDC hdc; | 
|  |  | 
|  | backBuffer->GetDC(&hdc); | 
|  |  | 
|  | SetBkColor(hdc, RGB(0, 0, 255)); | 
|  | SetTextColor(hdc, RGB(255, 255, 255)); | 
|  |  | 
|  | TextOut(hdc, x, y, buffer, lstrlen(buffer)); | 
|  |  | 
|  | backBuffer->ReleaseDC(hdc); | 
|  | } | 
|  |  | 
|  | bool FrameBufferDD::getScanline(bool &inVerticalBlank, unsigned int &scanline) | 
|  | { | 
|  | HRESULT result = directDraw->GetScanLine((unsigned long*)&scanline); | 
|  |  | 
|  | if(result == DD_OK) | 
|  | { | 
|  | inVerticalBlank = false; | 
|  | } | 
|  | else if(result == DDERR_VERTICALBLANKINPROGRESS) | 
|  | { | 
|  | inVerticalBlank = true; | 
|  | } | 
|  | else if(result == DDERR_UNSUPPORTED) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | else ASSERT(false); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void FrameBufferDD::releaseAll() | 
|  | { | 
|  | unlock(); | 
|  |  | 
|  | if(backBuffer) | 
|  | { | 
|  | backBuffer->Release(); | 
|  | backBuffer = 0; | 
|  | } | 
|  |  | 
|  | if(frontBuffer) | 
|  | { | 
|  | frontBuffer->Release(); | 
|  | frontBuffer = 0; | 
|  | } | 
|  |  | 
|  | if(directDraw) | 
|  | { | 
|  | directDraw->SetCooperativeLevel(0, DDSCL_NORMAL); | 
|  | directDraw->Release(); | 
|  | directDraw = 0; | 
|  | } | 
|  | } | 
|  | } |