| // 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 "System/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_A8R8G8B8; 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 = DD_FALSE; |
| |
| do |
| { |
| format = FORMAT_A8R8G8B8; |
| result = directDraw->SetDisplayMode(width, height, 32); |
| |
| 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; |
| } |
| } |
| } |