// 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;
|
}
|
}
|
}
|