qzdoom/src/win32/win32video.cpp
Randy Heit 86ed101522 - Added a manifest with a trustinfo section to updaterevision.exe so that
the build process can run uninterrupted under Vista.
- Fixed: DIRECT3DCREATE9FUNC needs WINAPI added to its prototype so that it
  works properly in release build, which defaults to __fastcall.


SVN r385 (trunk)
2006-11-23 02:16:43 +00:00

547 lines
14 KiB
C++

/*
** win32video.cpp
** Code to let ZDoom draw to the screen
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#ifdef _DEBUG
#define D3D_DEBUG_INFO
#endif
#define DIRECTDRAW_VERSION 0x0300
#define DIRECT3D_VERSION 0x0900
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <ddraw.h>
#include <d3d9.h>
// HEADER FILES ------------------------------------------------------------
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <ddraw.h>
#include <d3d9.h>
#include <stdio.h>
#define USE_WINDOWS_DWORD
#include "doomtype.h"
#include "c_dispatch.h"
#include "templates.h"
#include "i_system.h"
#include "i_video.h"
#include "v_video.h"
#include "v_pfx.h"
#include "stats.h"
#include "doomerrors.h"
#include "win32iface.h"
// MACROS ------------------------------------------------------------------
// TYPES -------------------------------------------------------------------
IMPLEMENT_ABSTRACT_CLASS(BaseWinFB)
typedef IDirect3D9 *(WINAPI *DIRECT3DCREATE9FUNC)(UINT SDKVersion);
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
void DoBlending (const PalEntry *from, PalEntry *to, int count, int r, int g, int b, int a);
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern HWND Window;
extern IVideo *Video;
extern BOOL AppActive;
extern int SessionState;
extern bool FullscreenReset;
extern bool VidResizing;
EXTERN_CVAR (Bool, fullscreen)
EXTERN_CVAR (Float, Gamma)
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static HMODULE D3D9_dll;
// PUBLIC DATA DEFINITIONS -------------------------------------------------
IDirectDraw2 *DDraw;
IDirect3D9 *D3D;
IDirect3DDevice9 *D3Device;
CVAR (Bool, vid_forceddraw, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
// CODE --------------------------------------------------------------------
Win32Video::Win32Video (int parm)
: m_Modes (NULL),
m_IsFullscreen (false)
{
if (!InitD3D9())
{
InitDDraw();
}
}
Win32Video::~Win32Video ()
{
FreeModes ();
if (DDraw != NULL)
{
if (m_IsFullscreen)
{
DDraw->SetCooperativeLevel (NULL, DDSCL_NORMAL);
}
DDraw->Release();
DDraw = NULL;
}
if (D3D != NULL)
{
D3D->Release();
D3D = NULL;
}
ShowWindow (Window, SW_HIDE);
STOPLOG;
}
bool Win32Video::InitD3D9 ()
{
DIRECT3DCREATE9FUNC direct3d_create_9;
if (vid_forceddraw)
{
return false;
}
// Load the Direct3D 9 library.
if ((D3D9_dll = LoadLibraryA ("d3d9.dll")) == NULL)
{
return false;
}
// Create the IDirect3D object.
if ((direct3d_create_9 = (DIRECT3DCREATE9FUNC)GetProcAddress (D3D9_dll, "Direct3DCreate9")) == NULL)
{
goto closelib;
}
if ((D3D = direct3d_create_9 (D3D_SDK_VERSION)) == NULL)
{
goto closelib;
}
// Check that we have at least PS 1.4 available.
D3DCAPS9 devcaps;
if (FAILED(D3D->GetDeviceCaps (D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &devcaps)))
{
goto d3drelease;
}
if ((devcaps.PixelShaderVersion & 0xFFFF) < 0x104)
{
goto d3drelease;
}
if (!(devcaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES))
{
goto d3drelease;
}
// Enumerate available display modes.
FreeModes ();
AddD3DModes (D3DFMT_X8R8G8B8);
AddD3DModes (D3DFMT_R5G6B5);
AddLetterboxModes ();
if (m_Modes == NULL)
{ // Too bad. We didn't find any modes for D3D9. We probably won't find any
// for DDraw either...
goto d3drelease;
}
return true;
d3drelease:
D3D->Release();
D3D = NULL;
closelib:
FreeLibrary (D3D9_dll);
return false;
}
void Win32Video::InitDDraw ()
{
LPDIRECTDRAW ddraw1;
STARTLOG;
HRESULT dderr;
dderr = DirectDrawCreate (NULL, &ddraw1, NULL);
if (FAILED(dderr))
I_FatalError ("Could not create DirectDraw object: %08lx", dderr);
dderr = ddraw1->QueryInterface (IID_IDirectDraw2, (LPVOID*)&DDraw);
if (FAILED(dderr))
{
ddraw1->Release ();
DDraw = NULL;
I_FatalError ("Could not initialize IDirectDraw2 interface: %08lx", dderr);
}
// Okay, we have the IDirectDraw2 interface now, so we can release the
// really old-fashioned IDirectDraw one.
ddraw1->Release ();
DDraw->SetCooperativeLevel (Window, DDSCL_NORMAL);
FreeModes ();
dderr = DDraw->EnumDisplayModes (0, NULL, this, EnumDDModesCB);
if (FAILED(dderr))
{
DDraw->Release ();
DDraw = NULL;
I_FatalError ("Could not enumerate display modes: %08lx", dderr);
}
if (m_Modes == NULL)
{
DDraw->Release ();
DDraw = NULL;
I_FatalError ("DirectDraw returned no display modes.\n\n"
"If you started ZDoom from a fullscreen DOS box, run it from "
"a DOS window instead. If that does not work, you may need to reboot.");
}
if (OSPlatform == os_Win95)
{
// Windows 95 will let us use Mode X. If we didn't find any linear
// modes in the loop above, add the Mode X modes here.
AddMode (320, 200, 8, 200);
AddMode (320, 240, 8, 240);
}
AddLetterboxModes ();
}
// Returns true if fullscreen, false otherwise
bool Win32Video::GoFullscreen (bool yes)
{
static const char *const yestypes[2] = { "windowed", "fullscreen" };
HRESULT hr[2];
int count;
// FIXME: Do this right for D3D. (This function is only called by the movie player when using D3D.)
if (D3D != NULL)
{
return yes;
}
if (m_IsFullscreen == yes)
return yes;
for (count = 0; count < 2; ++count)
{
LOG1 ("fullscreen: %d\n", yes);
hr[count] = DDraw->SetCooperativeLevel (Window, !yes ? DDSCL_NORMAL :
DDSCL_ALLOWMODEX | DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
if (SUCCEEDED(hr[count]))
{
if (count != 0)
{
// Ack! Cannot print because the screen does not exist right now.
// Printf ("Setting %s mode failed. Error %08lx\n",
// yestypes[!yes], hr[0]);
}
m_IsFullscreen = yes;
return yes;
}
yes = !yes;
}
I_FatalError ("Could not set %s mode: %08lx\n"
"Could not set %s mode: %08lx\n",
yestypes[yes], hr[0], yestypes[!yes], hr[1]);
// Appease the compiler, even though we never return if we get here.
return false;
}
// Flips to the GDI surface and clears it; used by the movie player
void Win32Video::BlankForGDI ()
{
static_cast<BaseWinFB *> (screen)->Blank ();
}
// Mode enumeration --------------------------------------------------------
HRESULT WINAPI Win32Video::EnumDDModesCB (LPDDSURFACEDESC desc, void *data)
{
((Win32Video *)data)->AddMode (desc->dwWidth, desc->dwHeight, 8, desc->dwHeight);
return DDENUMRET_OK;
}
void Win32Video::AddD3DModes (D3DFORMAT format)
{
UINT modecount, i;
D3DDISPLAYMODE mode;
modecount = D3D->GetAdapterModeCount (D3DADAPTER_DEFAULT, format);
for (i = 0; i < modecount; ++i)
{
if (D3D_OK == D3D->EnumAdapterModes (D3DADAPTER_DEFAULT, format, i, &mode))
{
AddMode (mode.Width, mode.Height, 8, mode.Height);
}
}
}
// Add 16:9 and 16:10 resolutions you can use in a window or letterboxed
void Win32Video::AddLetterboxModes ()
{
ModeInfo *mode, *nextmode;
for (mode = m_Modes; mode != NULL; mode = nextmode)
{
nextmode = mode->next;
if (mode->realheight == mode->height && mode->height * 4/3 == mode->width)
{
if (mode->width >= 360)
{
AddMode (mode->width, mode->width * 9/16, mode->bits, mode->height);
}
if (mode->width > 640)
{
AddMode (mode->width, mode->width * 10/16, mode->bits, mode->height);
}
}
}
}
void Win32Video::AddMode (int x, int y, int bits, int y2)
{
// Reject modes that do not meet certain criteria.
if ((x & 7) != 0 ||
y > MAXHEIGHT ||
x > MAXWIDTH ||
y < 200 ||
x < 320)
{
return;
}
ModeInfo **probep = &m_Modes;
ModeInfo *probe = m_Modes;
// This mode may have been already added to the list because it is
// enumerated multiple times at different refresh rates. If it's
// not present, add it to the right spot in the list; otherwise, do nothing.
// Modes are sorted first by width, then by height, then by depth. In each
// case the order is ascending.
for (; probe != 0; probep = &probe->next, probe = probe->next)
{
if (probe->width > x) break;
if (probe->width < x) continue;
// Width is equal
if (probe->height > y) break;
if (probe->height < y) continue;
// Height is equal
if (probe->bits > bits) break;
if (probe->bits < bits) continue;
// Bits is equal
return;
}
*probep = new ModeInfo (x, y, bits, y2);
(*probep)->next = probe;
}
void Win32Video::FreeModes ()
{
ModeInfo *mode = m_Modes;
while (mode)
{
ModeInfo *tempmode = mode;
mode = mode->next;
delete tempmode;
}
m_Modes = NULL;
}
// This only changes how the iterator lists modes
bool Win32Video::FullscreenChanged (bool fs)
{
LOG1 ("FS-changed: %d\n", fs);
m_IteratorFS = fs;
return true;
}
void Win32Video::StartModeIterator (int bits)
{
m_IteratorMode = m_Modes;
m_IteratorBits = bits;
}
bool Win32Video::NextMode (int *width, int *height, bool *letterbox)
{
if (m_IteratorMode)
{
while (m_IteratorMode && m_IteratorMode->bits != m_IteratorBits)
{
m_IteratorMode = m_IteratorMode->next;
}
if (m_IteratorMode)
{
*width = m_IteratorMode->width;
*height = m_IteratorMode->height;
if (letterbox != NULL) *letterbox = m_IteratorMode->realheight != m_IteratorMode->height;
m_IteratorMode = m_IteratorMode->next;
return true;
}
}
return false;
}
DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool fullscreen, DFrameBuffer *old)
{
static int retry = 0;
static int owidth, oheight;
BaseWinFB *fb;
PalEntry flashColor;
int flashAmount;
LOG4 ("CreateFB %d %d %d %p\n", width, height, fullscreen, old);
if (old != NULL)
{ // Reuse the old framebuffer if its attributes are the same
BaseWinFB *fb = static_cast<BaseWinFB *> (old);
if (fb->Width == width &&
fb->Height == height &&
fb->Windowed == !fullscreen)
{
return old;
}
old->GetFlash (flashColor, flashAmount);
delete old;
}
else
{
flashColor = 0;
flashAmount = 0;
}
if (D3D != NULL)
{
fb = new D3DFB (width, height, fullscreen);
}
else
{
fb = new DDrawFB (width, height, fullscreen);
}
LOG1 ("New fb created @ %p\n", fb);
// If we could not create the framebuffer, try again with slightly
// different parameters in this order:
// 1. Try with the closest size
// 2. Try in the opposite screen mode with the original size
// 3. Try in the opposite screen mode with the closest size
// This is a somewhat confusing mass of recursion here.
while (fb == NULL || !fb->IsValid ())
{
static HRESULT hr;
if (fb != NULL)
{
if (retry == 0)
{
hr = fb->GetHR ();
}
delete fb;
LOG1 ("fb is bad: %08lx\n", hr);
}
else
{
LOG ("Could not create fb at all\n");
}
screen = NULL;
LOG1 ("Retry number %d\n", retry);
switch (retry)
{
case 0:
owidth = width;
oheight = height;
case 2:
// Try a different resolution. Hopefully that will work.
I_ClosestResolution (&width, &height, 8);
LOG2 ("Retry with size %d,%d\n", width, height);
break;
case 1:
// Try changing fullscreen mode. Maybe that will work.
width = owidth;
height = oheight;
fullscreen = !fullscreen;
LOG1 ("Retry with fullscreen %d\n", fullscreen);
break;
default:
// I give up!
LOG3 ("Could not create new screen (%d x %d): %08lx", owidth, oheight, hr);
I_FatalError ("Could not create new screen (%d x %d): %08lx", owidth, oheight, hr);
}
++retry;
fb = static_cast<DDrawFB *>(CreateFrameBuffer (width, height, fullscreen, NULL));
}
retry = 0;
if (fb->IsFullscreen() != fullscreen)
{
Video->FullscreenChanged (!fullscreen);
}
fb->SetFlash (flashColor, flashAmount);
return fb;
}
void Win32Video::SetWindowedScale (float scale)
{
// FIXME
}
// FrameBuffer implementation -----------------------------------------------