| // 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 "Common/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; | |
| framebuffer = nullptr; | |
| 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: format = FORMAT_X8R8G8B8; break; | |
| case 24: format = FORMAT_R8G8B8; break; | |
| case 16: format = FORMAT_R5G6B5; break; | |
| default: format = FORMAT_NULL; break; | |
| } | |
| if((result != DD_OK && result != DDERR_PRIMARYSURFACEALREADYEXISTS) || (format == 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 | |
| { | |
| format = FORMAT_X8R8G8B8; | |
| result = directDraw->SetDisplayMode(width, height, 32); | |
| if(result == DDERR_INVALIDMODE) | |
| { | |
| format = FORMAT_R8G8B8; | |
| result = directDraw->SetDisplayMode(width, height, 24); | |
| if(result == DDERR_INVALIDMODE) | |
| { | |
| format = 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(sw::Surface *source) | |
| { | |
| copy(source); | |
| 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(sw::Surface *source, const Rect *sourceRect, const Rect *destRect) | |
| { | |
| copy(source); | |
| 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, sw::Surface *source) | |
| { | |
| updateClipper(windowOverride); | |
| updateBounds(windowOverride); | |
| flip(source); | |
| } | |
| void FrameBufferDD::blit(HWND windowOverride, sw::Surface *source, const Rect *sourceRect, const Rect *destRect) | |
| { | |
| updateClipper(windowOverride); | |
| updateBounds(windowOverride); | |
| blit(source, sourceRect, destRect); | |
| } | |
| 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(framebuffer) | |
| { | |
| return framebuffer; | |
| } | |
| if(!readySurfaces()) | |
| { | |
| return nullptr; | |
| } | |
| 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; | |
| framebuffer = DDSD.lpSurface; | |
| return framebuffer; | |
| } | |
| return nullptr; | |
| } | |
| void FrameBufferDD::unlock() | |
| { | |
| if(!framebuffer || !backBuffer) return; | |
| backBuffer->Unlock(0); | |
| framebuffer = nullptr; | |
| } | |
| 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; | |
| } | |
| } | |
| } |