From 93b18c3bfabe5d463a4867ad8c85a883515fbb1f Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Sun, 19 Nov 2006 02:10:25 +0000 Subject: [PATCH] SVN r383 (trunk) --- docs/rh-log.txt | 22 + src/sdl/sdlvideo.cpp | 4 + src/v_video.cpp | 11 + src/v_video.h | 9 + src/win32/fb_d3d9.cpp | 722 ++++++++++++++++++ src/win32/fb_ddraw.cpp | 1320 ++++++++++++++++++++++++++++++++ src/win32/win32iface.h | 112 ++- src/win32/win32video.cpp | 1533 +++++--------------------------------- zdoom.vcproj | 8 + 9 files changed, 2382 insertions(+), 1359 deletions(-) create mode 100644 src/win32/fb_d3d9.cpp create mode 100644 src/win32/fb_ddraw.cpp diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 858eddf58..5a053550e 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,25 @@ +November 18, 2006 +- Fixed: DFrameBuffer and related classes did not use DECLARE_CLASS macros, + so there was no guarantee that they would allocate the proper amount of + memory. This tripped me up when I was using D3DFB and it was writing beyond + the end of allocated memory. +- Added a new D3DFB class that should be more compatible with modern systems + than the 8-bit paletted DDrawFB. + Pros: + - Much cleaner code. + - No performance penalty when running in a window. + - Slightly faster fullscreen performance on Geforce 6 and 7 cards. + - Vista ought to love it. + Cons: + - Requires Pixel Shader 1.4 or better. + - Fullscreen on an ATI Mobility x300 is a little slower (but still faster + than DirectDraw in a window). + Note that this is not hardware accelerated rendering. The screen is still + drawn as before by the CPU to an 8-bit paletted surface. The difference is + in how that surface makes its way to the visible display. Here, the surface + is copied to an 8-bit texture, and a pixel shader converts it to RGB when + drawing it. + November 14, 2006 (Changes by Graf Zahl) - Made AActor's xscale and yscale properties full precision fixed point so now larger scales than 4 can be done. diff --git a/src/sdl/sdlvideo.cpp b/src/sdl/sdlvideo.cpp index fed080d3d..402ad9923 100644 --- a/src/sdl/sdlvideo.cpp +++ b/src/sdl/sdlvideo.cpp @@ -20,6 +20,7 @@ class SDLFB : public DFrameBuffer { + DECLARE_CLASS(SDLFB, DFrameBuffer) public: SDLFB (int width, int height, bool fullscreen); ~SDLFB (); @@ -56,7 +57,10 @@ private: bool NotPaletted; void UpdateColors (); + + SDLFB () {} }; +IMPLEMENT_CLASS(SDLFB, DFrameBuffer) struct MiniModeInfo { diff --git a/src/v_video.cpp b/src/v_video.cpp index aac21ebf8..486677f0c 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -88,6 +88,13 @@ CVAR (Int, vid_defbits, 8, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, vid_fps, false, 0) CVAR (Bool, ticker, false, 0) CVAR (Int, vid_showpalette, 0, 0) +CUSTOM_CVAR (Bool, vid_vsync, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (screen != NULL) + { + screen->SetVSync (*self); + } +} CUSTOM_CVAR (Float, dimamount, 0.2f, CVAR_ARCHIVE) { @@ -724,6 +731,10 @@ void DFrameBuffer::CopyFromBuff (BYTE *src, int srcPitch, int width, int height, } } +void DFrameBuffer::SetVSync (bool vsync) +{ +} + // // V_SetResolution // diff --git a/src/v_video.h b/src/v_video.h index 3dc1e9c08..b68ecf742 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -185,6 +185,8 @@ protected: bool ClipBox (int &left, int &top, int &width, int &height, const BYTE *&src, const int srcpitch) const; + DCanvas() {} + private: // Keep track of canvases, for automatic destruction at exit DCanvas *Next; @@ -206,6 +208,8 @@ public: protected: BYTE *MemBuffer; + + DSimpleCanvas() {} }; // A canvas that represents the actual display. The video code is responsible @@ -253,6 +257,9 @@ public: // Returns true if running fullscreen. virtual bool IsFullscreen () = 0; + // Changes the vsync setting, if supported by the device. + virtual void SetVSync (bool vsync); + #ifdef _WIN32 virtual void PaletteChanged () = 0; virtual int QueryNewPalette () = 0; @@ -262,6 +269,8 @@ protected: void DrawRateStuff (); void CopyFromBuff (BYTE *src, int srcPitch, int width, int height, BYTE *dest); + DFrameBuffer () {} + private: DWORD LastMS, LastSec, FrameCount, LastCount, LastTic; }; diff --git a/src/win32/fb_d3d9.cpp b/src/win32/fb_d3d9.cpp new file mode 100644 index 000000000..1f8ecc3c0 --- /dev/null +++ b/src/win32/fb_d3d9.cpp @@ -0,0 +1,722 @@ +/* +** fb_d3d9.cpp +** Code to let ZDoom use Direct3D 9 as a simple framebuffer +** +**--------------------------------------------------------------------------- +** 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. +**--------------------------------------------------------------------------- +** +** This file does _not_ implement hardware-acclerated rendering. It is just +** a means of getting the pixel data to the screen in a more reliable +** method on modern hardware by copying the entire frame to a texture, +** drawing that to the screen, and presenting. +*/ + +// HEADER FILES ------------------------------------------------------------ + +#ifdef _DEBUG +#define D3D_DEBUG_INFO +#endif +#define DIRECT3D_VERSION 0x0900 +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include + +#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" + +#include + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +IMPLEMENT_CLASS(D3DFB) + +struct FBVERTEX +{ + FLOAT x, y, z, rhw; + FLOAT tu, tv; +}; +#define D3DFVF_FBVERTEX (D3DFVF_XYZRHW|D3DFVF_TEX1) + +// 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 VidResizing; + +EXTERN_CVAR (Bool, fullscreen) +EXTERN_CVAR (Float, Gamma) +EXTERN_CVAR (Int, vid_displaybits) +EXTERN_CVAR (Bool, vid_vsync) + +extern IDirect3D9 *D3D; + +extern cycle_t BlitCycles; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +#if 0 +// This is the HLSL code: + +// Technically, Palette only needs to be a sampler1D, but that +// produces assembly code to copy index.x to index.y, which is +// totally unnecessary. + +sampler2D Image : register(s0); +sampler2D Palette : register(s1); +float4 Flash : register(c0); +float4 InvFlash : register(c1); + +float4 main (float2 texCoord : TEXCOORD0) : COLOR +{ + half4 index = tex2D (Image, texCoord); + + // For some reason, this adjustment is needed on NVidia hardware. + // If this is not done, then all palette index >=240 look up + // palette index + 1. ATI behaves as expacted and does not need + // this adjustment. Fortunately, this produces correct results + // on both hardware with no perceptible performance impact, so + // I only need to use one shader. + index.x = clamp(index.x - 7.65931418e-6, 0.0, 1.0); + + float4 rgb = tex2D (Palette, index); + return Flash + rgb * InvFlash; +} + +// +// Generated by Microsoft (R) D3DX9 Shader Compiler 9.15.779.0000 +// +// fxc paltex.ps /Tps_1_4 /VnPalTexShaderDef /Fh +// +// +// Parameters: +// +// float4 Flash; +// sampler2D Image; +// float4 InvFlash; +// sampler2D Palette; +// +// +// Registers: +// +// Name Reg Size +// ------------ ----- ---- +// Flash c0 1 +// InvFlash c1 1 +// Image s0 1 +// Palette s1 1 +// + + ps_1_4 + def c2, -7.65931418e-006, 0, 0, 0 + texld r0, t0 + add_sat r0.x, r0.x, c2.x + phase + texld r1, r0 + mad r0, r1, c1, c0 + +// approximately 4 instruction slots used (2 texture, 2 arithmetic) +#endif + +const DWORD PalTexShaderDef[] = +{ + 0xffff0104, 0x003bfffe, 0x42415443, 0x0000001c, 0x000000b4, 0xffff0104, + 0x00000004, 0x0000001c, 0x00000100, 0x000000ad, 0x0000006c, 0x00000002, + 0x00020001, 0x00000074, 0x00000000, 0x00000084, 0x00000003, 0x00000001, + 0x0000008c, 0x00000000, 0x0000009c, 0x00010002, 0x00020001, 0x00000074, + 0x00000000, 0x000000a5, 0x00010003, 0x00000001, 0x0000008c, 0x00000000, + 0x73616c46, 0xabab0068, 0x00030001, 0x00040001, 0x00000001, 0x00000000, + 0x67616d49, 0xabab0065, 0x000c0004, 0x00010001, 0x00000001, 0x00000000, + 0x46766e49, 0x6873616c, 0x6c615000, 0x65747465, 0x5f737000, 0x00345f31, + 0x7263694d, 0x666f736f, 0x52282074, 0x33442029, 0x20395844, 0x64616853, + 0x43207265, 0x69706d6f, 0x2072656c, 0x35312e39, 0x3937372e, 0x3030302e, + 0xabab0030, 0x00000051, 0xa00f0002, 0xb7008081, 0x00000000, 0x00000000, + 0x00000000, 0x00000042, 0x800f0000, 0xb0e40000, 0x00000002, 0x80110000, + 0x80000000, 0xa0000002, 0x0000fffd, 0x00000042, 0x800f0001, 0x80e40000, + 0x00000004, 0x800f0000, 0x80e40001, 0xa0e40001, 0xa0e40000, 0x0000ffff +}; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// CODE -------------------------------------------------------------------- + +D3DFB::D3DFB (int width, int height, bool fullscreen) + : BaseWinFB (width, height) +{ + D3DPRESENT_PARAMETERS d3dpp; + int i; + + D3DDevice = NULL; + VertexBuffer = NULL; + FBTexture = NULL; + PaletteTexture = NULL; + PalTexShader = NULL; + FBFormat = D3DFMT_UNKNOWN; + PalFormat = D3DFMT_UNKNOWN; + VSync = vid_vsync; + + Gamma = 1.0; + memset (FlashConstants, 0, sizeof(FlashConstants)); + FlashConstants[1][3] = 1.f; // Always use alpha from palette (which is always 1, so meh) + FlashColor = 0; + FlashAmount = 0; + + NeedGammaUpdate = false; + NeedPalUpdate = false; + + if (MemBuffer == NULL) + { + return; + } + + for (i = 0; i < 256; i++) + { + GammaTable[i] = i; + } + memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); + + Windowed = !(static_cast(Video)->GoFullscreen (fullscreen)); + + TrueHeight = height; + if (fullscreen) + { + for (Win32Video::ModeInfo *mode = static_cast(Video)->m_Modes; mode != NULL; mode = mode->next) + { + if (mode->width == Width && mode->height == Height) + { + TrueHeight = mode->realheight; + break; + } + } + } + + FillPresentParameters (&d3dpp, fullscreen, VSync); + + HRESULT hr; + + if (FAILED(hr = D3D->CreateDevice (D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, Window, + D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &D3DDevice))) + { + D3DDevice = NULL; + if (fullscreen) + { + d3dpp.BackBufferFormat = D3DFMT_R5G6B5; + if (FAILED(D3D->CreateDevice (D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, Window, + D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &D3DDevice))) + { + D3DDevice = NULL; + } + } + } + if (D3DDevice != NULL) + { + CreateResources (); + D3DDevice->Clear (0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.f, 0); + D3DDevice->BeginScene(); + D3DDevice->EndScene(); + D3DDevice->Present(NULL, NULL, NULL, NULL); + } +} + +D3DFB::~D3DFB () +{ + ReleaseResources (); + if (D3DDevice != NULL) + { + // Do not release the D3DDevice in fullscreen mode. + D3DPRESENT_PARAMETERS d3dpp; + FillPresentParameters (&d3dpp, false, true); + //D3DDevice->Reset (&d3dpp); + D3DDevice->Release(); + //Sleep (1000); + } +} + +void D3DFB::FillPresentParameters (D3DPRESENT_PARAMETERS *pp, bool fullscreen, bool vsync) +{ + memset (pp, 0, sizeof(*pp)); + pp->Windowed = !fullscreen; + pp->SwapEffect = D3DSWAPEFFECT_DISCARD; + pp->BackBufferWidth = Width; + pp->BackBufferHeight = TrueHeight; + pp->BackBufferFormat = fullscreen ? D3DFMT_X8R8G8B8 : D3DFMT_UNKNOWN; + pp->hDeviceWindow = Window; + pp->PresentationInterval = vsync ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE; +} + +bool D3DFB::CreateResources () +{ + if (!Windowed) + { + ShowWindow (Window, SW_SHOW); + // Remove the window border in fullscreen mode + SetWindowLongPtr (Window, GWL_STYLE, WS_POPUP|WS_VISIBLE|WS_SYSMENU); + } + else + { + // Resize the window to match desired dimensions + int sizew = Width + GetSystemMetrics (SM_CXSIZEFRAME)*2; + int sizeh = Height + GetSystemMetrics (SM_CYSIZEFRAME) * 2 + + GetSystemMetrics (SM_CYCAPTION); + LOG2 ("Resize window to %dx%d\n", sizew, sizeh); + VidResizing = true; + // Make sure the window has a border in windowed mode + SetWindowLongPtr (Window, GWL_STYLE, WS_VISIBLE|WS_OVERLAPPEDWINDOW); + if (!SetWindowPos (Window, NULL, 0, 0, sizew, sizeh, + SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER)) + { + LOG1 ("SetWindowPos failed because %08lx\n", GetLastError()); + } + VidResizing = false; + ShowWindow (Window, SW_SHOWNORMAL); + } + if (FAILED(D3DDevice->CreatePixelShader (PalTexShaderDef, &PalTexShader))) + { + return false; + } + if (!CreateFBTexture() || !CreatePaletteTexture()) + { + return false; + } + if (!CreateVertexes()) + { + return false; + } + SetGamma (Gamma); + return true; +} + +void D3DFB::ReleaseResources () +{ + if (FBTexture != NULL) + { + FBTexture->Release(); + FBTexture = NULL; + } + if (VertexBuffer != NULL) + { + VertexBuffer->Release(); + VertexBuffer = NULL; + } + if (PaletteTexture != NULL) + { + PaletteTexture->Release(); + PaletteTexture = NULL; + } + if (PalTexShader != NULL) + { + PalTexShader->Release(); + PalTexShader = NULL; + } +} + +bool D3DFB::Reset () +{ + D3DPRESENT_PARAMETERS d3dpp; + + // Free resources created with D3DPOOL_DEFAULT. + if (FBTexture != NULL) + { + FBTexture->Release(); + FBTexture = NULL; + } + if (VertexBuffer != NULL) + { + VertexBuffer->Release(); + VertexBuffer = NULL; + } + FillPresentParameters (&d3dpp, !Windowed, VSync); + if (!SUCCEEDED(D3DDevice->Reset (&d3dpp))) + { + return false; + } + if (!CreateFBTexture() || !CreateVertexes()) + { + return false; + } + return true; +} + +bool D3DFB::CreateFBTexture () +{ + if (FAILED(D3DDevice->CreateTexture (Width, Height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL))) + { + int pow2width, pow2height, i; + + for (i = 1; i < Width; i <<= 1) {} pow2width = i; + for (i = 1; i < Height; i <<= 1) {} pow2height = i; + + if (FAILED(D3DDevice->CreateTexture (pow2width, pow2height, 1, D3DUSAGE_DYNAMIC, D3DFMT_L8, D3DPOOL_DEFAULT, &FBTexture, NULL))) + { + return false; + } + else + { + FBWidth = pow2width; + FBHeight = pow2height; + } + } + else + { + FBWidth = Width; + FBHeight = Height; + } + return true; +} + +bool D3DFB::CreatePaletteTexture () +{ + if (FAILED(D3DDevice->CreateTexture (256, 1, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &PaletteTexture, NULL))) + { + if (FAILED(D3DDevice->CreateTexture (256, 1, 1, 0, D3DFMT_R5G6B5, D3DPOOL_MANAGED, &PaletteTexture, NULL))) + { + return false; + } + else + { + PalFormat = D3DFMT_R5G6B5; + } + } + else + { + PalFormat = D3DFMT_A8R8G8B8; + } + return true; +} + +bool D3DFB::CreateVertexes () +{ + float top = (TrueHeight - Height) * 0.5f - 0.5f; + float right = float(Width) + 0.5f; + float bot = float(Height) + top + 1.f; + float texright = float(Width) / float(FBWidth); + float texbot = float(Height) / float(FBHeight); + FBVERTEX verts[4] = + { + { -0.5f, top, 0.5f, 1.f, 0.f, 0.f }, + { right, top, 0.5f, 1.f, texright, 0.f }, + { right, bot, 0.5f, 1.f, texright, texbot }, + { -0.5f, bot, 0.5f, 1.f, 0.f, texbot } + }; + void *pverts; + + if (FAILED(D3DDevice->CreateVertexBuffer (sizeof(verts), D3DUSAGE_WRITEONLY, D3DFVF_FBVERTEX, D3DPOOL_DEFAULT, &VertexBuffer, NULL)) || + FAILED(VertexBuffer->Lock (0, sizeof(verts), &pverts, 0))) + { + return false; + } + else + { + memcpy (pverts, verts, sizeof(verts)); + VertexBuffer->Unlock(); + } + return true; +} + +int D3DFB::GetPageCount () +{ + return 1; +} + +void D3DFB::PaletteChanged () +{ +} + +int D3DFB::QueryNewPalette () +{ + return 0; +} + +bool D3DFB::IsValid () +{ + return D3DDevice != NULL; +} + +HRESULT D3DFB::GetHR () +{ + return 0; +} + +bool D3DFB::IsFullscreen () +{ + return !Windowed; +} + +bool D3DFB::Lock () +{ + return Lock(true); +} + +bool D3DFB::Lock (bool buffered) +{ + if (LockCount++ > 0) + { + return false; + } + + Buffer = MemBuffer; + return false; +} + +void D3DFB::Unlock () +{ + LOG1 ("Unlock <%d>\n", LockCount); + + if (LockCount == 0) + { + return; + } + + if (UpdatePending && LockCount == 1) + { + Update (); + } + else if (--LockCount == 0) + { + Buffer = NULL; + } +} + +void D3DFB::Update () +{ + bool pchanged = false; + + LOG3 ("Update <%d,%c:%d>\n", LockCount, AppActive?'Y':'N', SessionState); + + if (LockCount != 1) + { + //I_FatalError ("Framebuffer must have exactly 1 lock to be updated"); + if (LockCount > 0) + { + UpdatePending = true; + --LockCount; + } + return; + } + + DrawRateStuff (); + + if (NeedGammaUpdate) + { + NeedGammaUpdate = false; + CalcGamma (Gamma, GammaTable); + NeedPalUpdate = true; + } + + if (NeedPalUpdate) + { + UploadPalette(); + } + + BlitCycles = 0; + clock (BlitCycles); + + LockCount = 0; + PaintToWindow (); + + unclock (BlitCycles); + LOG1 ("cycles = %d\n", BlitCycles); + + Buffer = NULL; + UpdatePending = false; +} + +bool D3DFB::PaintToWindow () +{ + D3DLOCKED_RECT lockrect; + HRESULT hr; + + if (LockCount != 0) + { + return false; + } + hr = D3DDevice->TestCooperativeLevel(); + if (FAILED(hr)) + { + if (hr != D3DERR_DEVICENOTRESET || !Reset()) + { + Sleep (1); + return false; + } + } + if (SUCCEEDED(FBTexture->LockRect (0, &lockrect, NULL, D3DLOCK_DISCARD))) + { + if (lockrect.Pitch == Pitch) + { + memcpy (lockrect.pBits, MemBuffer, Width * Height); + } + else + { + BYTE *dest = (BYTE *)lockrect.pBits; + BYTE *src = MemBuffer; + for (int y = 0; y < Height; y++) + { + memcpy (dest, src, Width); + dest += lockrect.Pitch; + src += Pitch; + } + } + FBTexture->UnlockRect (0); + } + if (TrueHeight != Height) + { + // Letterbox! Draw black top and bottom borders. + int topborder = (TrueHeight - Height) / 2; + D3DRECT rects[2] = { { 0, 0, Width, topborder }, { 0, Height + topborder, Width, TrueHeight } }; + D3DDevice->Clear (2, rects, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.f, 0); + } + D3DDevice->BeginScene(); + D3DDevice->SetTexture (0, FBTexture); + D3DDevice->SetTexture (1, PaletteTexture); + D3DDevice->SetStreamSource (0, VertexBuffer, 0, sizeof(FBVERTEX)); + D3DDevice->SetFVF (D3DFVF_FBVERTEX); + D3DDevice->SetPixelShader (PalTexShader); + D3DDevice->SetPixelShaderConstantF (0, FlashConstants[0], 2); + D3DDevice->DrawPrimitive (D3DPT_TRIANGLEFAN, 0, 2); + D3DDevice->EndScene(); + return SUCCEEDED(D3DDevice->Present(NULL, NULL, NULL, NULL)); +} + +void D3DFB::UploadPalette () +{ + D3DLOCKED_RECT lockrect; + + if (SUCCEEDED(PaletteTexture->LockRect (0, &lockrect, NULL, 0))) + { + NeedPalUpdate = false; + + if (PalFormat == D3DFMT_A8R8G8B8) + { + BYTE *pix = (BYTE *)lockrect.pBits; + for (int i = 0; i < 256; ++i, pix += 4) + { + pix[0] = GammaTable[SourcePalette[i].b]; + pix[1] = GammaTable[SourcePalette[i].g]; + pix[2] = GammaTable[SourcePalette[i].r]; + pix[3] = 255; + } + } + else + { + WORD *pix = (WORD *)lockrect.pBits; + for (int i = 0; i < 256; ++i, ++pix) + { + *pix = ((GammaTable[SourcePalette[i].r] >> 3) << 11) | + ((GammaTable[SourcePalette[i].g] >> 2) << 5) | + (GammaTable[SourcePalette[i].b] >> 3); + } + } + PaletteTexture->UnlockRect (0); + } +} + +PalEntry *D3DFB::GetPalette () +{ + return SourcePalette; +} + +void D3DFB::UpdatePalette () +{ + NeedPalUpdate = true; +} + +bool D3DFB::SetGamma (float gamma) +{ + LOG1 ("SetGamma %g\n", gamma); + Gamma = gamma; + NeedGammaUpdate = true; + return true; +} + +bool D3DFB::SetFlash (PalEntry rgb, int amount) +{ + FlashColor = rgb; + FlashAmount = amount; + + // Fill in the constants for the pixel shader to do linear interpolation between the palette and the flash: + float r = rgb.r / 255.f, g = rgb.g / 255.f, b = rgb.b / 255.f, a = amount / 256.f; + FlashConstants[0][0] = r * a; + FlashConstants[0][1] = g * a; + FlashConstants[0][2] = b * a; + a = 1 - a; + FlashConstants[1][0] = a; + FlashConstants[1][1] = a; + FlashConstants[1][2] = a; + return true; +} + +void D3DFB::GetFlash (PalEntry &rgb, int &amount) +{ + rgb = FlashColor; + amount = FlashAmount; +} + +void D3DFB::GetFlashedPalette (PalEntry pal[256]) +{ + memcpy (pal, SourcePalette, 256*sizeof(PalEntry)); + if (FlashAmount) + { + DoBlending (pal, pal, 256, FlashColor.r, FlashColor.g, FlashColor.b, FlashAmount); + } +} + +void D3DFB::SetVSync (bool vsync) +{ + if (VSync != vsync) + { + VSync = vsync; + Reset(); + } +} + +void D3DFB::Blank () +{ + // Only used by movie player, which isn't working with D3D9 yet. +} diff --git a/src/win32/fb_ddraw.cpp b/src/win32/fb_ddraw.cpp new file mode 100644 index 000000000..bd76cc8f5 --- /dev/null +++ b/src/win32/fb_ddraw.cpp @@ -0,0 +1,1320 @@ +/* +** fb_ddraw.cpp +** Code to let ZDoom use DirectDraw 3 +** +**--------------------------------------------------------------------------- +** 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. +**--------------------------------------------------------------------------- +** +*/ + + +// HEADER FILES ------------------------------------------------------------ + +#define DIRECTDRAW_VERSION 0x0300 +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include + +#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_CLASS(DDrawFB) + +// 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 VidResizing; + +EXTERN_CVAR (Bool, fullscreen) +EXTERN_CVAR (Float, Gamma) + +extern IDirectDraw2 *DDraw; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +CVAR (Bool, vid_palettehack, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Bool, vid_attachedsurfaces, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Bool, vid_noblitter, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CVAR (Int, vid_displaybits, 8, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +CUSTOM_CVAR (Float, rgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (screen != NULL) + { + screen->SetGamma (Gamma); + } +} +CUSTOM_CVAR (Float, ggamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (screen != NULL) + { + screen->SetGamma (Gamma); + } +} +CUSTOM_CVAR (Float, bgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (screen != NULL) + { + screen->SetGamma (Gamma); + } +} + +cycle_t BlitCycles; + +// CODE -------------------------------------------------------------------- + +DDrawFB::DDrawFB (int width, int height, bool fullscreen) + : BaseWinFB (width, height) +{ + int i; + + LastHR = 0; + + Palette = NULL; + PrimarySurf = NULL; + BackSurf = NULL; + BackSurf2 = NULL; + BlitSurf = NULL; + Clipper = NULL; + GDIPalette = NULL; + ClipRegion = NULL; + ClipSize = 0; + BufferCount = 1; + Gamma = 1.0; + BufferPitch = Pitch; + FlipFlags = vid_vsync ? DDFLIP_WAIT : DDFLIP_WAIT|DDFLIP_NOVSYNC; + + NeedGammaUpdate = false; + NeedPalUpdate = false; + NeedResRecreate = false; + MustBuffer = false; + BufferingNow = false; + WasBuffering = false; + Write8bit = false; + UpdatePending = false; + UseBlitter = false; + + FlashAmount = 0; + + if (MemBuffer == NULL) + { + return; + } + + for (i = 0; i < 256; i++) + { + PalEntries[i].peRed = GPalette.BaseColors[i].r; + PalEntries[i].peGreen = GPalette.BaseColors[i].g; + PalEntries[i].peBlue = GPalette.BaseColors[i].b; + GammaTable[0][i] = GammaTable[1][i] = GammaTable[2][i] = i; + } + memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); + + MustBuffer = false; + + Windowed = !(static_cast(Video)->GoFullscreen (fullscreen)); + + if (vid_noblitter) + { + LOG ("Blitter forced off\n"); + } + else + { + DDCAPS hwcaps = { sizeof(DDCAPS) }; + HRESULT hr = DDraw->GetCaps (&hwcaps, NULL); + if (SUCCEEDED(hr)) + { + LOG2 ("dwCaps = %08lx, dwSVBCaps = %08lx\n", hwcaps.dwCaps, hwcaps.dwSVBCaps); + if (hwcaps.dwCaps & DDCAPS_BLT) + { + LOG ("Driver supports blits\n"); + if (hwcaps.dwSVBCaps & DDCAPS_CANBLTSYSMEM) + { + LOG ("Driver can blit from system memory\n"); + if (hwcaps.dwCaps & DDCAPS_BLTQUEUE) + { + LOG ("Driver supports asynchronous blits\n"); + UseBlitter = true; + } + else + { + LOG ("Driver does not support asynchronous blits\n"); + } + } + else + { + LOG ("Driver cannot blit from system memory\n"); + } + } + else + { + LOG ("Driver does not support blits\n"); + } + } + } + + if (!CreateResources ()) + { + if (PrimarySurf != NULL) + { + PrimarySurf->Release (); + PrimarySurf = NULL; + } + } +} + +DDrawFB::~DDrawFB () +{ + ReleaseResources (); +} + +bool DDrawFB::CreateResources () +{ + DDSURFACEDESC ddsd = { sizeof(ddsd), }; + HRESULT hr; + int bits; + + BufferCount = 1; + + if (!Windowed) + { + ShowWindow (Window, SW_SHOW); + // Remove the window border in fullscreen mode + SetWindowLongPtr (Window, GWL_STYLE, WS_VISIBLE|WS_SYSMENU); + + TrueHeight = Height; + for (Win32Video::ModeInfo *mode = static_cast(Video)->m_Modes; mode != NULL; mode = mode->next) + { + if (mode->width == Width && mode->height == Height) + { + TrueHeight = mode->realheight; + break; + } + } + hr = DDraw->SetDisplayMode (Width, TrueHeight, bits = vid_displaybits, 0, 0); + if (FAILED(hr)) + { + bits = 32; + while (FAILED(hr) && bits >= 8) + { + hr = DDraw->SetDisplayMode (Width, Height, bits, 0, 0); + bits -= 8; + } + if (FAILED(hr)) + { + LastHR = hr; + return false; + } + } + LOG3 ("Mode set to %d x %d x %d\n", Width, Height, bits); + + if (vid_attachedsurfaces && OSPlatform == os_WinNT4) + { + if (!CreateSurfacesAttached ()) + return false; + } + else + { + if (!CreateSurfacesComplex ()) + return false; + } + + if (UseBlitter) + { + UseBlitter = CreateBlitterSource (); + } + } + else + { + MustBuffer = true; + + LOG ("Running in a window\n"); + TrueHeight = Height; + + // Create the primary surface + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + do + { + hr = DDraw->CreateSurface (&ddsd, &PrimarySurf, NULL); + LOG1 ("Create primary: %08lx\n", hr); + } while (0); + if (FAILED(hr)) + { + LastHR = hr; + return false; + } + + MaybeCreatePalette (); + + // Resize the window to match desired dimensions + int sizew = Width + GetSystemMetrics (SM_CXSIZEFRAME)*2; + int sizeh = Height + GetSystemMetrics (SM_CYSIZEFRAME) * 2 + + GetSystemMetrics (SM_CYCAPTION); + LOG2 ("Resize window to %dx%d\n", sizew, sizeh); + VidResizing = true; + // Make sure the window has a border in windowed mode + SetWindowLongPtr (Window, GWL_STYLE, WS_VISIBLE|WS_OVERLAPPEDWINDOW); + if (!SetWindowPos (Window, NULL, 0, 0, sizew, sizeh, + SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER)) + { + LOG1 ("SetWindowPos failed because %08lx\n", GetLastError()); + } + VidResizing = false; + ShowWindow (Window, SW_SHOWNORMAL); + + // Create the clipper + hr = DDraw->CreateClipper (0, &Clipper, NULL); + LOG1 ("Create clipper: %08lx\n", hr); + if (FAILED(hr)) + { + LastHR = hr; + return false; + } + // Associate the clipper with the window + Clipper->SetHWnd (0, Window); + PrimarySurf->SetClipper (Clipper); + LOG1 ("Clipper @ %p set\n", Clipper); + + // Create the backbuffer + ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; + ddsd.dwWidth = Width; + ddsd.dwHeight = Height; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | (UseBlitter ? DDSCAPS_SYSTEMMEMORY : 0); + + hr = DDraw->CreateSurface (&ddsd, &BackSurf, NULL); + LOG1 ("Create backbuffer: %08lx\n", hr); + if (FAILED(hr)) + { + LastHR = hr; + return false; + } + LockingSurf = BackSurf; + LOG1 ("LockingSurf and BackSurf @ %p\n", BackSurf); + LOG ("Created backbuf\n"); + } + SetGamma (Gamma); + SetFlash (Flash, FlashAmount); + return true; +} + +bool DDrawFB::CreateSurfacesAttached () +{ + DDSURFACEDESC ddsd = { sizeof(ddsd), }; + HRESULT hr; + + LOG ("creating surfaces using AddAttachedSurface\n"); + + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_VIDEOMEMORY; + hr = DDraw->CreateSurface (&ddsd, &PrimarySurf, NULL); + if (FAILED(hr)) + { + LastHR = hr; + return false; + } + + LOG1 ("Primary surface @ %p\n", PrimarySurf); + + // Under NT 4 and with bad DDraw drivers under 9x (and maybe Win2k?) + // if the palette is not attached to the primary surface before any + // back buffers are added to it, colors 0 and 255 will remain black + // and white respectively. + MaybeCreatePalette (); + + // Try for triple buffering. Unbuffered output is only allowed if + // we manage to get triple buffering. Even with double buffering, + // framerate can slow significantly compared to triple buffering, + // so we force buffering in that case, which effectively emulates + // triple buffering (after a fashion). + if (!AddBackBuf (&BackSurf, 1) || !AddBackBuf (&BackSurf2, 2)) + { +// MustBuffer = true; + } + if (BackSurf != NULL) + { + DDSCAPS caps = { DDSCAPS_BACKBUFFER, }; + hr = PrimarySurf->GetAttachedSurface (&caps, &LockingSurf); + if (FAILED (hr)) + { + LOG1 ("Could not get attached surface: %08lx\n", hr); + if (BackSurf2 != NULL) + { + PrimarySurf->DeleteAttachedSurface (0, BackSurf2); + BackSurf2->Release (); + BackSurf2 = NULL; + } + PrimarySurf->DeleteAttachedSurface (0, BackSurf); + BackSurf->Release (); + BackSurf = NULL; +// MustBuffer = true; + LockingSurf = PrimarySurf; + } + else + { + BufferCount = (BackSurf2 != NULL) ? 3 : 2; + LOG ("Got attached surface\n"); + } + } + else + { + LOG ("No flip chain\n"); + LockingSurf = PrimarySurf; + } + return true; +} + +bool DDrawFB::AddBackBuf (LPDIRECTDRAWSURFACE *surface, int num) +{ + DDSURFACEDESC ddsd = { sizeof(ddsd), }; + HRESULT hr; + + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.dwWidth = Width; + ddsd.dwHeight = Height; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; + hr = DDraw->CreateSurface (&ddsd, surface, NULL); + if (FAILED(hr)) + { + LOG2 ("could not create back buf %d: %08lx\n", num, hr); + return false; + } + else + { + LOG2 ("BackBuf %d created @ %p\n", num, *surface); + hr = PrimarySurf->AddAttachedSurface (*surface); + if (FAILED(hr)) + { + LOG2 ("could not add back buf %d: %08lx\n", num, hr); + (*surface)->Release (); + *surface = NULL; + return false; + } + else + { + LOG1 ("Attachment of back buf %d succeeded\n", num); + } + } + return true; +} + +bool DDrawFB::CreateSurfacesComplex () +{ + DDSURFACEDESC ddsd = { sizeof(ddsd), }; + HRESULT hr; + int tries = 2; + + LOG ("creating surfaces using a complex primary\n"); + + // Try for triple buffering first. + // If that fails, try for double buffering. + // If that fails, settle for single buffering. + // If that fails, then give up. + // + // However, if using the blitter, then do not triple buffer the + // primary surface, because that is effectively like quadruple + // buffering and player response starts feeling too sluggish. + ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_VIDEOMEMORY + | DDSCAPS_FLIP | DDSCAPS_COMPLEX; + do + { + LOG1 ("Try #%d\n", tries); + ddsd.dwBackBufferCount = UseBlitter ? 1 : 2; + hr = DDraw->CreateSurface (&ddsd, &PrimarySurf, NULL); + if (FAILED(hr)) + { + if (hr == DDERR_NOEXCLUSIVEMODE) + { + LOG ("Exclusive mode was lost, so restoring it now.\n"); + hr = DDraw->SetCooperativeLevel (Window, DDSCL_ALLOWMODEX | DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + LOG1 ("SetCooperativeLevel result: %08lx\n", hr); + hr = DDraw->SetDisplayMode (Width, Height, 8, 0, 0); + //hr = DDraw->RestoreDisplayMode (); + LOG1 ("SetDisplayMode result: %08lx\n", hr); + ++tries; + hr = E_FAIL; + continue; + } + + LOG1 ("Could not create with 2 backbuffers: %lx\n", hr); + ddsd.dwBackBufferCount = 1; + hr = DDraw->CreateSurface (&ddsd, &PrimarySurf, NULL); + if (FAILED(hr)) + { + LOG1 ("Could not create with 1 backbuffer: %lx\n", hr); + ddsd.ddsCaps.dwCaps &= ~DDSCAPS_FLIP | DDSCAPS_COMPLEX; + ddsd.dwBackBufferCount = 0; + hr = DDraw->CreateSurface (&ddsd, &PrimarySurf, NULL); + if (FAILED (hr)) + { + LOG1 ("Could not create with 0 backbuffers: %lx\n", hr); + if (tries == 2) + { + LOG ("Retrying without DDSCAPS_VIDEOMEMORY\n"); + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE + | DDSCAPS_FLIP | DDSCAPS_COMPLEX; + } + } + } + } + } while (FAILED(hr) && --tries); + + if (FAILED(hr)) + { + LastHR = hr; + return false; + } + + LOG1 ("Complex surface chain @ %p\n", PrimarySurf); + if (PrimarySurf == NULL) + { + LOG ("It's NULL but it didn't fail?!?\n"); + LastHR = E_FAIL; + return false; + } + + if (ddsd.dwBackBufferCount == 0) + { + LOG ("No flip chain\n"); +// MustBuffer = true; + LockingSurf = PrimarySurf; + } + else + { + DDSCAPS caps = { DDSCAPS_BACKBUFFER, }; + hr = PrimarySurf->GetAttachedSurface (&caps, &LockingSurf); + if (FAILED (hr)) + { + LOG1 ("Could not get attached surface: %08lx\n", hr); +// MustBuffer = true; + LockingSurf = PrimarySurf; + } + else + { + BufferCount = ddsd.dwBackBufferCount + 1; + LOG1 ("Got attached surface. %d buffers\n", BufferCount); + } + } + + MaybeCreatePalette (); + return true; +} + +bool DDrawFB::CreateBlitterSource () +{ + DDSURFACEDESC ddsd = { sizeof(ddsd), }; + HRESULT hr; + + LOG ("Creating surface for blitter source\n"); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_BACKBUFFERCOUNT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY + | DDSCAPS_FLIP | DDSCAPS_COMPLEX; + ddsd.dwBackBufferCount = 2; + ddsd.dwWidth = (Width==1024?1024+16:Width); + ddsd.dwHeight = Height; + hr = DDraw->CreateSurface (&ddsd, &BlitSurf, NULL); + if (FAILED(hr)) + { + LOG1 ("Trying to create blitter source with only one surface (%08lx)\n", hr); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; + hr = DDraw->CreateSurface (&ddsd, &BlitSurf, NULL); + if (FAILED(hr)) + { + LOG1 ("Could not create blitter source: %08lx\n", hr); + MustBuffer = true; + return false; + } + BufferCount = MAX (BufferCount, 1); + } + else + { + BufferCount = MAX (BufferCount, 2); + } + LOG1 ("Blitter source created successfully @ %p\n", BlitSurf); + return true; +} + +void DDrawFB::MaybeCreatePalette () +{ + DDPIXELFORMAT fmt = { sizeof(fmt), }; + HRESULT hr; + int i; + + UsePfx = false; + + // If the surface needs a palette, try to create one. If the palette + // cannot be created, the result is ugly but non-fatal. + hr = PrimarySurf->GetPixelFormat (&fmt); + if (SUCCEEDED (hr) && (fmt.dwFlags & DDPF_PALETTEINDEXED8)) + { + LOG ("Surface is paletted\n"); + GPfx.SetFormat (fmt.dwRGBBitCount, + fmt.dwRBitMask, fmt.dwGBitMask, fmt.dwBBitMask); + + if (Windowed) + { + struct { LOGPALETTE head; PALETTEENTRY filler[255]; } pal; + + LOG ("Writing in a window\n"); + Write8bit = true; + pal.head.palVersion = 0x300; + pal.head.palNumEntries = 256; + memcpy (pal.head.palPalEntry, PalEntries, 256*sizeof(PalEntries[0])); + for (i = 0; i < 256; i++) + { + pal.head.palPalEntry[i].peFlags = 0; + } + GDIPalette = CreatePalette (&pal.head); + LOG ("Created GDI palette\n"); + if (GDIPalette != NULL) + { + HDC dc = GetDC (Window); + SelectPalette (dc, GDIPalette, FALSE); + RealizePalette (dc); + ReleaseDC (Window, dc); + RebuildColorTable (); + } + } + else + { + hr = DDraw->CreatePalette (DDPCAPS_8BIT|DDPCAPS_ALLOW256, PalEntries, &Palette, NULL); + if (FAILED(hr)) + { + LOG ("Could not create palette\n"); + Palette = NULL; // NULL it just to be safe + } + else + { + hr = PrimarySurf->SetPalette (Palette); + if (FAILED(hr)) + { + LOG ("Could not attach palette to surface\n"); + Palette->Release (); + Palette = NULL; + } + else + { + // The palette was supposed to have been initialized with + // the correct colors, but some drivers don't do that. + // (On the other hand, the docs for the SetPalette method + // don't state that the surface will be set to the + // palette's colors when it gets set, so this might be + // legal behavior. Wish I knew...) + NeedPalUpdate = true; + } + } + } + } + else + { + LOG ("Surface is direct color\n"); + UsePfx = true; + GPfx.SetFormat (fmt.dwRGBBitCount, + fmt.dwRBitMask, fmt.dwGBitMask, fmt.dwBBitMask); + GPfx.SetPalette (GPalette.BaseColors); + } +} + +void DDrawFB::ReleaseResources () +{ + if (LockCount) + { + LockCount = 1; + Unlock (); + } + + if (ClipRegion != NULL) + { + delete[] ClipRegion; + ClipRegion = NULL; + } + if (Clipper != NULL) + { + Clipper->Release (); + Clipper = NULL; + } + if (PrimarySurf != NULL) + { + //Blank (); + PrimarySurf->Release (); + PrimarySurf = NULL; + } + if (BackSurf != NULL) + { + BackSurf->Release (); + BackSurf = NULL; + } + if (BackSurf2 != NULL) + { + BackSurf2->Release (); + BackSurf2 = NULL; + } + if (BlitSurf != NULL) + { + BlitSurf->Release (); + BlitSurf = NULL; + } + if (Palette != NULL) + { + Palette->Release (); + Palette = NULL; + } + if (GDIPalette != NULL) + { + HDC dc = GetDC (Window); + SelectPalette (dc, (HPALETTE)GetStockObject (DEFAULT_PALETTE), TRUE); + DeleteObject (GDIPalette); + ReleaseDC (Window, dc); + GDIPalette = NULL; + } + LockingSurf = NULL; +} + +int DDrawFB::GetPageCount () +{ + return MustBuffer ? 1 : BufferCount+1; +} + +void DDrawFB::PaletteChanged () +{ + // Somebody else changed the palette. If we are running fullscreen, + // they are obviously jerks, and we need to restore our own palette. + if (!Windowed) + { + if (Palette != NULL) + { + // It is not enough to set NeedPalUpdate to true. Some palette + // entries might now be reserved for system usage, and nothing + // we do will change them. The only way I have found to fix this + // is to recreate all our surfaces and the palette from scratch. + + // IMPORTANT: Do not recreate the resources here. The screen might + // be locked for a drawing operation. Do it later the next time + // somebody tries to lock it. + NeedResRecreate = true; + } + } + else + { + QueryNewPalette (); + } +} + +int DDrawFB::QueryNewPalette () +{ + LOG ("QueryNewPalette\n"); + if (GDIPalette == NULL && Windowed) + { + if (Write8bit) + { + RebuildColorTable (); + } + return 0; + } + + HDC dc = GetDC (Window); + HPALETTE oldPal = SelectPalette (dc, GDIPalette, FALSE); + int i = RealizePalette (dc); + SelectPalette (dc, oldPal, TRUE); + RealizePalette (dc); + ReleaseDC (Window, dc); + if (i != 0) + { + RebuildColorTable (); + } + return i; +} + +void DDrawFB::RebuildColorTable () +{ + int i; + + if (Write8bit) + { + PALETTEENTRY syspal[256]; + HDC dc = GetDC (Window); + + GetSystemPaletteEntries (dc, 0, 256, syspal); + + for (i = 0; i < 256; i++) + { + swap (syspal[i].peRed, syspal[i].peBlue); + } + for (i = 0; i < 256; i++) + { + GPfxPal.Pal8[i] = BestColor ((uint32 *)syspal, PalEntries[i].peRed, + PalEntries[i].peGreen, PalEntries[i].peBlue); + } + } +} + +bool DDrawFB::IsValid () +{ + return PrimarySurf != NULL; +} + +HRESULT DDrawFB::GetHR () +{ + return LastHR; +} + +bool DDrawFB::Lock () +{ + return Lock (false); +} + +bool DDrawFB::Lock (bool useSimpleCanvas) +{ + bool wasLost; + +// LOG2 (" Lock (%d) <%d>\n", buffered, LockCount); + + if (LockCount++ > 0) + { + return false; + } + + wasLost = false; + + if (NeedResRecreate) + { + NeedResRecreate = false; + ReleaseResources (); + CreateResources (); + } + + LOG5 ("Lock %d %d %d %d %d\n", AppActive, SessionState, MustBuffer, useSimpleCanvas, UseBlitter); + + if (!AppActive || SessionState || MustBuffer || useSimpleCanvas || !UseBlitter) + { + Buffer = MemBuffer; + Pitch = BufferPitch; + BufferingNow = true; + } + else + { + HRESULT hr GCCNOWARN = BlitSurf->Flip (NULL, DDFLIP_WAIT); + LOG1 ("Blit flip = %08lx\n", hr); + LockSurfRes res = LockSurf (NULL, BlitSurf); + + if (res == NoGood) + { // We must have a surface locked before returning, + // but we could not lock the hardware surface, so buffer + // for this frame. + Buffer = MemBuffer; + Pitch = BufferPitch; + BufferingNow = true; + } + else + { + wasLost = (res == GoodWasLost); + } + } + + wasLost = wasLost || (BufferingNow != WasBuffering); + WasBuffering = BufferingNow; + return wasLost; +} + +void DDrawFB::Unlock () +{ + LOG1 ("Unlock <%d>\n", LockCount); + + if (LockCount == 0) + { + return; + } + + if (UpdatePending && LockCount == 1) + { + Update (); + } + else if (--LockCount == 0) + { + if (!BufferingNow) + { + if (BlitSurf == NULL) + { + LockingSurf->Unlock (NULL); + } + else + { + BlitSurf->Unlock (NULL); + } + } + Buffer = NULL; + } +} + +DDrawFB::LockSurfRes DDrawFB::LockSurf (LPRECT lockrect, LPDIRECTDRAWSURFACE toLock) +{ + HRESULT hr; + DDSURFACEDESC desc = { sizeof(desc), }; + bool wasLost = false; + bool lockingLocker = false; + + if (toLock == NULL) + { + lockingLocker = true; + if (LockingSurf == NULL) + { + if (!CreateResources ()) + { + if (LastHR != DDERR_UNSUPPORTEDMODE) + { + I_FatalError ("Could not rebuild framebuffer: %08lx", LastHR); + } + else + { + LOG ("Display is in unsupported mode right now.\n"); + return NoGood; + } + } + } + toLock = LockingSurf; + } + + hr = toLock->Lock (lockrect, &desc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); + LOG3 ("LockSurf %p (%d): %08lx\n", toLock, lockingLocker, hr); + + if (hr == DDERR_SURFACELOST) + { + wasLost = true; + if (FAILED (AttemptRestore ())) + { + return NoGood; + } + if (BlitSurf && FAILED(BlitSurf->IsLost ())) + { + LOG ("Restore blitter surface\n"); + hr = BlitSurf->Restore (); + if (FAILED (hr)) + { + LOG1 ("Could not restore blitter surface: %08lx", hr); + BlitSurf->Release (); + if (BlitSurf == toLock) + { + BlitSurf = NULL; + return NoGood; + } + BlitSurf = NULL; + } + } + if (lockingLocker) + { + toLock = LockingSurf; + } + LOG ("Trying to lock again\n"); + hr = toLock->Lock (lockrect, &desc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); + if (hr == DDERR_SURFACELOST && Windowed) + { // If this is NT, the user probably opened the Windows NT Security dialog. + // If this is not NT, trying to recreate everything from scratch won't hurt. + ReleaseResources (); + if (!CreateResources ()) + { + if (LastHR != DDERR_UNSUPPORTEDMODE) + { + I_FatalError ("Could not rebuild framebuffer: %08lx", LastHR); + } + else + { + LOG ("Display is in unsupported mode right now.\n"); + return NoGood; + } + } + if (lockingLocker) + { + toLock = LockingSurf; + } + hr = toLock->Lock (lockrect, &desc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); + } + } + if (FAILED (hr)) + { // Still could not restore the surface, so don't draw anything + //I_FatalError ("Could not lock framebuffer: %08lx", hr); + LOG1 ("Final result after restoration attempts: %08lx\n", hr); + return NoGood; + } + Buffer = (BYTE *)desc.lpSurface; + Pitch = desc.lPitch; + BufferingNow = false; + return wasLost ? GoodWasLost : Good; +} + +HRESULT DDrawFB::AttemptRestore () +{ + LOG ("Restore primary\n"); + HRESULT hr = PrimarySurf->Restore (); + if (hr == DDERR_WRONGMODE && Windowed) + { // The user changed the screen mode + LOG ("DDERR_WRONGMODE and windowed, so recreating all resources\n"); + ReleaseResources (); + if (!CreateResources ()) + { + LOG1 ("Could not recreate framebuffer: %08lx", LastHR); + return LastHR; + } + } + else if (FAILED (hr)) + { + LOG1 ("Could not restore primary surface: %08lx", hr); + return hr; + } + if (BackSurf && FAILED(BackSurf->IsLost ())) + { + LOG ("Restore backbuffer\n"); + hr = BackSurf->Restore (); + if (FAILED (hr)) + { + I_FatalError ("Could not restore backbuffer: %08lx", hr); + } + } + if (BackSurf2 && FAILED(BackSurf2->IsLost ())) + { + LOG ("Restore backbuffer 2\n"); + hr = BackSurf2->Restore (); + if (FAILED (hr)) + { + I_FatalError ("Could not restore backbuffer 2: %08lx", hr); + } + } + return 0; +} + +void DDrawFB::Update () +{ + bool pchanged = false; + int i; + + LOG3 ("Update <%d,%c:%d>\n", LockCount, AppActive?'Y':'N', SessionState); + + if (LockCount != 1) + { + //I_FatalError ("Framebuffer must have exactly 1 lock to be updated"); + if (LockCount > 0) + { + UpdatePending = true; + --LockCount; + } + return; + } + + DrawRateStuff (); + + if (NeedGammaUpdate) + { + NeedGammaUpdate = false; + CalcGamma (Windowed || rgamma == 0.f ? Gamma : Gamma * rgamma, GammaTable[0]); + CalcGamma (Windowed || ggamma == 0.f ? Gamma : Gamma * ggamma, GammaTable[1]); + CalcGamma (Windowed || bgamma == 0.f ? Gamma : Gamma * bgamma, GammaTable[2]); + NeedPalUpdate = true; + } + + if (NeedPalUpdate || vid_palettehack) + { + NeedPalUpdate = false; + if (Palette != NULL || GDIPalette != NULL) + { + for (i = 0; i < 256; i++) + { + PalEntries[i].peRed = GammaTable[0][SourcePalette[i].r]; + PalEntries[i].peGreen = GammaTable[1][SourcePalette[i].g]; + PalEntries[i].peBlue = GammaTable[2][SourcePalette[i].b]; + } + if (FlashAmount) + { + DoBlending ((PalEntry *)PalEntries, (PalEntry *)PalEntries, + 256, GammaTable[2][Flash.b], GammaTable[1][Flash.g], GammaTable[0][Flash.r], + FlashAmount); + } + if (Palette != NULL) + { + pchanged = true; + } + else + { + /* Argh! Too slow! + SetPaletteEntries (GDIPalette, 0, 256, PalEntries); + HDC dc = GetDC (Window); + SelectPalette (dc, GDIPalette, FALSE); + RealizePalette (dc); + ReleaseDC (Window, dc); + */ + RebuildColorTable (); + } + } + else + { + for (i = 0; i < 256; i++) + { + ((PalEntry *)PalEntries)[i].r = GammaTable[0][SourcePalette[i].r]; + ((PalEntry *)PalEntries)[i].g = GammaTable[1][SourcePalette[i].g]; + ((PalEntry *)PalEntries)[i].b = GammaTable[2][SourcePalette[i].b]; + } + if (FlashAmount) + { + DoBlending ((PalEntry *)PalEntries, (PalEntry *)PalEntries, + 256, GammaTable[0][Flash.r], GammaTable[1][Flash.g], GammaTable[2][Flash.b], + FlashAmount); + } + GPfx.SetPalette ((PalEntry *)PalEntries); + } + } + + BlitCycles = 0; + clock (BlitCycles); + + if (BufferingNow) + { + LockCount = 0; + if ((Windowed || AppActive) && !SessionState && !PaintToWindow()) + { + if (LockSurf (NULL, NULL) != NoGood) + { + BYTE *writept = Buffer + (TrueHeight - Height)/2*Pitch; + LOG3 ("Copy %dx%d (%d)\n", Width, Height, BufferPitch); + if (UsePfx) + { + GPfx.Convert (MemBuffer, BufferPitch, + writept, Pitch, Width, Height, + FRACUNIT, FRACUNIT, 0, 0); + } + else + { + CopyFromBuff (MemBuffer, BufferPitch, Width, Height, writept); + } + if (TrueHeight != Height) + { + // Letterbox time! Draw black top and bottom borders. + int topborder = (TrueHeight - Height) / 2; + int botborder = TrueHeight - topborder - Height; + memset (Buffer, 0, Pitch*topborder); + memset (writept + Height*Pitch, 0, Pitch*botborder); + } + LockingSurf->Unlock (NULL); + } + } + } + else + { + if (BlitSurf != NULL) + { + HRESULT hr; + BlitSurf->Unlock (NULL); + RECT srcRect = { 0, 0, Width, Height }; + hr = LockingSurf->BltFast (0, 0, BlitSurf, &srcRect, DDBLTFAST_NOCOLORKEY|DDBLTFAST_WAIT); + if (FAILED (hr)) + { + LOG1 ("Could not blit: %08lx\n", hr); + if (hr == DDERR_SURFACELOST) + { + if (SUCCEEDED (AttemptRestore ())) + { + hr = LockingSurf->BltFast (0, 0, BlitSurf, &srcRect, DDBLTFAST_NOCOLORKEY|DDBLTFAST_WAIT); + if (FAILED (hr)) + { + LOG1 ("Blit retry also failed: %08lx\n", hr); + } + } + } + } + else + { + LOG ("Blit ok\n"); + } + } + else + { + LockingSurf->Unlock (NULL); + } + } + + unclock (BlitCycles); + LOG1 ("cycles = %d\n", BlitCycles); + + Buffer = NULL; + LockCount = 0; + UpdatePending = false; + + if (!Windowed && AppActive && !SessionState /*&& !UseBlitter && !MustBuffer*/) + { + HRESULT hr = PrimarySurf->Flip (NULL, FlipFlags); + LOG1 ("Flip = %08lx\n", hr); + if (hr == DDERR_INVALIDPARAMS) + { + if (FlipFlags & DDFLIP_NOVSYNC) + { + FlipFlags &= ~DDFLIP_NOVSYNC; + Printf ("Can't disable vsync\n"); + PrimarySurf->Flip (NULL, FlipFlags); + } + } + } + + if (pchanged && AppActive && !SessionState) + { + Palette->SetEntries (0, 0, 256, PalEntries); + } +} + +bool DDrawFB::PaintToWindow () +{ + if (Windowed && LockCount == 0) + { + HRESULT hr; + RECT rect; + GetClientRect (Window, &rect); + if (rect.right != 0 && rect.bottom != 0) + { + // Use blit to copy/stretch to window's client rect + ClientToScreen (Window, (POINT*)&rect.left); + ClientToScreen (Window, (POINT*)&rect.right); + LOG ("Paint to window\n"); + if (LockSurf (NULL, NULL) != NoGood) + { + GPfx.Convert (MemBuffer, BufferPitch, + Buffer, Pitch, Width, Height, + FRACUNIT, FRACUNIT, 0, 0); + LockingSurf->Unlock (NULL); + if (FAILED (hr = PrimarySurf->Blt (&rect, BackSurf, NULL, DDBLT_WAIT|DDBLT_ASYNC, NULL))) + { + if (hr == DDERR_SURFACELOST) + { + PrimarySurf->Restore (); + } + PrimarySurf->Blt (&rect, BackSurf, NULL, DDBLT_WAIT, NULL); + } + } + Buffer = NULL; + LOG ("Did paint to window\n"); + } + return true; + } + return false; +} + +PalEntry *DDrawFB::GetPalette () +{ + return SourcePalette; +} + +void DDrawFB::UpdatePalette () +{ + NeedPalUpdate = true; +} + +bool DDrawFB::SetGamma (float gamma) +{ + LOG1 ("SetGamma %g\n", gamma); + Gamma = gamma; + NeedGammaUpdate = true; + return true; +} + +bool DDrawFB::SetFlash (PalEntry rgb, int amount) +{ + Flash = rgb; + FlashAmount = amount; + NeedPalUpdate = true; + return true; +} + +void DDrawFB::GetFlash (PalEntry &rgb, int &amount) +{ + rgb = Flash; + amount = FlashAmount; +} + +// Q: Should I gamma adjust the returned palette? +// A: No. PNG screenshots save the gamma value, so there is no need. +void DDrawFB::GetFlashedPalette (PalEntry pal[256]) +{ + memcpy (pal, SourcePalette, 256*sizeof(PalEntry)); + if (FlashAmount) + { + DoBlending (pal, pal, 256, Flash.r, Flash.g, Flash.b, FlashAmount); + } +} + +void DDrawFB::SetVSync (bool vsync) +{ + LOG1 ("vid_vsync set to %d\n", vsync); + FlipFlags = vsync ? DDFLIP_WAIT : DDFLIP_WAIT|DDFLIP_NOVSYNC; +} + + +void DDrawFB::Blank () +{ + if (IsFullscreen ()) + { + DDBLTFX blitFX = { sizeof(blitFX) }; + + blitFX.dwFillColor = 0; + DDraw->FlipToGDISurface (); + PrimarySurf->Blt (NULL, NULL, NULL, DDBLT_COLORFILL, &blitFX); + } +} + +ADD_STAT (blit) +{ + FString out; + out.Format ( + "blit=%04.1f ms", + (double)BlitCycles * SecondsPerCycle * 1000 + ); + return out; +} diff --git a/src/win32/win32iface.h b/src/win32/win32iface.h index aa2c38638..0c11600bf 100644 --- a/src/win32/win32iface.h +++ b/src/win32/win32iface.h @@ -31,19 +31,32 @@ ** */ +#ifndef DIRECTDRAW_VERSION +#define DIRECTDRAW_VERSION 0x0300 +#endif +#ifndef DIRECT3D_VERSION +#define DIRECT3D_VERSION 0x0900 +#endif + #define WIN32_LEAN_AND_MEAN #include #include +#include #include "hardware.h" #include "v_video.h" +EXTERN_CVAR (Bool, vid_vsync) + class Win32Video : public IVideo { public: Win32Video (int parm); ~Win32Video (); + bool InitD3D9(); + void InitDDraw(); + EDisplayType GetDisplayType () { return DISPLAY_Both; } bool FullscreenChanged (bool fs); void SetWindowedScale (float scale); @@ -82,20 +95,24 @@ class Win32Video : public IVideo void AddMode (int x, int y, int bits, int baseHeight); void FreeModes (); - void NewDDMode (int x, int y); static HRESULT WINAPI EnumDDModesCB (LPDDSURFACEDESC desc, void *modes); + void AddD3DModes (D3DFORMAT format); + void AddLetterboxModes (); friend class DDrawFB; + friend class D3DFB; }; class BaseWinFB : public DFrameBuffer { + DECLARE_ABSTRACT_CLASS(BaseWinFB, DFrameBuffer) public: BaseWinFB (int width, int height) : DFrameBuffer (width, height), Windowed (true) {} bool IsFullscreen () { return !Windowed; } virtual void Blank () = 0; virtual bool PaintToWindow () = 0; + virtual HRESULT GetHR () = 0; protected: virtual bool CreateResources () = 0; @@ -105,10 +122,13 @@ protected: friend int I_PlayMovie (const char *name); friend class Win32Video; + + BaseWinFB() {} }; class DDrawFB : public BaseWinFB { + DECLARE_CLASS(DDrawFB, BaseWinFB) public: DDrawFB (int width, int height, bool fullscreen); ~DDrawFB (); @@ -128,11 +148,11 @@ public: int GetPageCount (); int QueryNewPalette (); void PaletteChanged (); - HRESULT GetHR () { return LastHR; } + void SetVSync (bool vsync); + HRESULT GetHR (); void Blank (); bool PaintToWindow (); - void DoSpeedTest (); private: enum LockSurfRes { NoGood, Good, GoodWasLost }; @@ -152,6 +172,7 @@ private: BYTE GammaTable[3][256]; PalEntry SourcePalette[256]; PALETTEENTRY PalEntries[256]; + DWORD FlipFlags; LPDIRECTDRAWPALETTE Palette; LPDIRECTDRAWSURFACE PrimarySurf; @@ -160,7 +181,6 @@ private: LPDIRECTDRAWSURFACE BlitSurf; LPDIRECTDRAWSURFACE LockingSurf; LPDIRECTDRAWCLIPPER Clipper; - //IDirectDrawGammaControl *GammaControl; HPALETTE GDIPalette; BYTE *ClipRegion; DWORD ClipSize; @@ -181,4 +201,88 @@ private: bool UpdatePending; // On final unlock, call Update() bool UseBlitter; // Use blitter to copy from sys mem to video mem bool UsePfx; + + DDrawFB() {} }; + +class D3DFB : public BaseWinFB +{ + DECLARE_CLASS(D3DFB, BaseWinFB) +public: + D3DFB (int width, int height, bool fullscreen); + ~D3DFB (); + + bool IsValid (); + bool Lock (); + bool Lock (bool buffered); + void Unlock (); + void Update (); + PalEntry *GetPalette (); + void GetFlashedPalette (PalEntry palette[256]); + void UpdatePalette (); + bool SetGamma (float gamma); + bool SetFlash (PalEntry rgb, int amount); + void GetFlash (PalEntry &rgb, int &amount); + int GetPageCount (); + bool IsFullscreen (); + void PaletteChanged (); + int QueryNewPalette (); + void Blank (); + bool PaintToWindow (); + void SetVSync (bool vsync); + HRESULT GetHR (); + +private: + bool CreateResources(); + void ReleaseResources(); + bool CreateFBTexture(); + bool CreatePaletteTexture(); + bool CreateVertexes(); + void UploadPalette(); + void FillPresentParameters (D3DPRESENT_PARAMETERS *pp, bool fullscreen, bool vsync); + bool Reset(); + + BYTE GammaTable[256]; + PalEntry SourcePalette[256]; + float FlashConstants[2][4]; + PalEntry FlashColor; + int FlashAmount; + int TrueHeight; + float Gamma; + bool UpdatePending; + bool NeedPalUpdate; + bool NeedGammaUpdate; + D3DFORMAT FBFormat; + D3DFORMAT PalFormat; + int FBWidth, FBHeight; + bool VSync; + + IDirect3DDevice9 *D3DDevice; + IDirect3DVertexBuffer9 *VertexBuffer; + IDirect3DTexture9 *FBTexture; + IDirect3DTexture9 *PaletteTexture; + IDirect3DPixelShader9 *PalTexShader; + + D3DFB() {} +}; + +#if 0 +#define STARTLOG do { if (!dbg) dbg = fopen ("k:/vid.log", "w"); } while(0) +#define STOPLOG do { if (dbg) { fclose (dbg); dbg=NULL; } } while(0) +#define LOG(x) do { if (dbg) { fprintf (dbg, x); fflush (dbg); } } while(0) +#define LOG1(x,y) do { if (dbg) { fprintf (dbg, x, y); fflush (dbg); } } while(0) +#define LOG2(x,y,z) do { if (dbg) { fprintf (dbg, x, y, z); fflush (dbg); } } while(0) +#define LOG3(x,y,z,zz) do { if (dbg) { fprintf (dbg, x, y, z, zz); fflush (dbg); } } while(0) +#define LOG4(x,y,z,a,b) do { if (dbg) { fprintf (dbg, x, y, z, a, b); fflush (dbg); } } while(0) +#define LOG5(x,y,z,a,b,c) do { if (dbg) { fprintf (dbg, x, y, z, a, b, c); fflush (dbg); } } while(0) +FILE *dbg; +#else +#define STARTLOG +#define STOPLOG +#define LOG(x) +#define LOG1(x,y) +#define LOG2(x,y,z) +#define LOG3(x,y,z,zz) +#define LOG4(x,y,z,a,b) +#define LOG5(x,y,z,a,b,c) +#endif diff --git a/src/win32/win32video.cpp b/src/win32/win32video.cpp index 26ffa54ba..4e07c7b11 100644 --- a/src/win32/win32video.cpp +++ b/src/win32/win32video.cpp @@ -1,6 +1,6 @@ /* ** win32video.cpp -** Code to let ZDoom use DirectDraw +** Code to let ZDoom draw to the screen ** **--------------------------------------------------------------------------- ** Copyright 1998-2006 Randy Heit @@ -32,14 +32,24 @@ ** */ +#ifdef _DEBUG +#define D3D_DEBUG_INFO +#endif +#define DIRECTDRAW_VERSION 0x0300 +#define DIRECT3D_VERSION 0x0900 + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include // HEADER FILES ------------------------------------------------------------ -#define DIRECTDRAW_VERSION 0x0300 #define WIN32_LEAN_AND_MEAN #include #include +#include #include #define USE_WINDOWS_DWORD @@ -58,37 +68,11 @@ // MACROS ------------------------------------------------------------------ -#define true TRUE -#define false FALSE - -#if 0 -#define STARTLOG do { if (!dbg) dbg = fopen ("k:/vid.log", "w"); } while(0) -#define STOPLOG do { if (dbg) { fclose (dbg); dbg=NULL; } } while(0) -#define LOG(x) do { if (dbg) { fprintf (dbg, x); fflush (dbg); } } while(0) -#define LOG1(x,y) do { if (dbg) { fprintf (dbg, x, y); fflush (dbg); } } while(0) -#define LOG2(x,y,z) do { if (dbg) { fprintf (dbg, x, y, z); fflush (dbg); } } while(0) -#define LOG3(x,y,z,zz) do { if (dbg) { fprintf (dbg, x, y, z, zz); fflush (dbg); } } while(0) -#define LOG4(x,y,z,a,b) do { if (dbg) { fprintf (dbg, x, y, z, a, b); fflush (dbg); } } while(0) -#define LOG5(x,y,z,a,b,c) do { if (dbg) { fprintf (dbg, x, y, z, a, b, c); fflush (dbg); } } while(0) -FILE *dbg; -#else -#define STARTLOG -#define STOPLOG -#define LOG(x) -#define LOG1(x,y) -#define LOG2(x,y,z) -#define LOG3(x,y,z,zz) -#define LOG4(x,y,z,a,b) -#define LOG5(x,y,z,a,b,c) -#endif - // TYPES ------------------------------------------------------------------- -class CVidError : public CRecoverableError -{ - CVidError() : CRecoverableError() {} - CVidError(const char *message) : CRecoverableError(message) {} -}; +IMPLEMENT_ABSTRACT_CLASS(BaseWinFB) + +typedef IDirect3D9 *(*DIRECT3DCREATE9FUNC)(UINT SDKVersion); // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -110,54 +94,118 @@ EXTERN_CVAR (Float, Gamma) // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static IDirectDraw2 *DDraw; -static cycle_t BlitCycles; -static DWORD FlipFlags; +static HMODULE D3D9_dll; // PUBLIC DATA DEFINITIONS ------------------------------------------------- -CVAR (Bool, vid_palettehack, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, vid_attachedsurfaces, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Bool, vid_noblitter, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CVAR (Int, vid_displaybits, 8, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -CUSTOM_CVAR (Bool, vid_vsync, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - LOG1 ("vid_vsync set to %d\n", *self); - FlipFlags = self ? DDFLIP_WAIT : DDFLIP_WAIT|DDFLIP_NOVSYNC; -} -CUSTOM_CVAR (Float, rgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (screen != NULL) - { - screen->SetGamma (Gamma); - } -} -CUSTOM_CVAR (Float, ggamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (screen != NULL) - { - screen->SetGamma (Gamma); - } -} -CUSTOM_CVAR (Float, bgamma, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -{ - if (screen != NULL) - { - screen->SetGamma (Gamma); - } -} +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; - ModeInfo *mode, *nextmode; dderr = DirectDrawCreate (NULL, &ddraw1, NULL); @@ -200,41 +248,7 @@ Win32Video::Win32Video (int parm) AddMode (320, 200, 8, 200); AddMode (320, 240, 8, 240); } - // Now add 16:9 and 16:10 resolutions you can use in a window or letterboxed - 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); - } - } - } -} - -Win32Video::~Win32Video () -{ - FreeModes (); - - if (DDraw != NULL) - { - if (m_IsFullscreen) - { - DDraw->SetCooperativeLevel (NULL, DDSCL_NORMAL); - } - DDraw->Release(); - DDraw = NULL; - } - - ShowWindow (Window, SW_HIDE); - - STOPLOG; + AddLetterboxModes (); } // Returns true if fullscreen, false otherwise @@ -244,6 +258,12 @@ bool Win32Video::GoFullscreen (bool yes) 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; @@ -277,28 +297,66 @@ bool Win32Video::GoFullscreen (bool yes) // Flips to the GDI surface and clears it; used by the movie player void Win32Video::BlankForGDI () { - static_cast (screen)->Blank (); + static_cast (screen)->Blank (); } // Mode enumeration -------------------------------------------------------- HRESULT WINAPI Win32Video::EnumDDModesCB (LPDDSURFACEDESC desc, void *data) { - if (desc->ddpfPixelFormat.dwRGBBitCount == 8 && - (desc->dwWidth & 7) == 0 && - desc->dwHeight <= MAXHEIGHT && - desc->dwWidth <= MAXWIDTH && - desc->dwHeight >= 200 && - desc->dwWidth >= 320) - { - ((Win32Video *)data)->AddMode (desc->dwWidth, desc->dwHeight, 8, desc->dwHeight); - } - + ((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; @@ -378,6 +436,7 @@ DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool fullscr static int retry = 0; static int owidth, oheight; + BaseWinFB *fb; PalEntry flashColor; int flashAmount; @@ -401,7 +460,14 @@ DFrameBuffer *Win32Video::CreateFrameBuffer (int width, int height, bool fullscr flashAmount = 0; } - DDrawFB *fb = new DDrawFB (width, height, fullscreen); + 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 @@ -479,1246 +545,3 @@ void Win32Video::SetWindowedScale (float scale) } // FrameBuffer implementation ----------------------------------------------- - -DDrawFB::DDrawFB (int width, int height, bool fullscreen) - : BaseWinFB (width, height) -{ - int i; - - LastHR = 0; - - Palette = NULL; - PrimarySurf = NULL; - BackSurf = NULL; - BackSurf2 = NULL; - BlitSurf = NULL; - Clipper = NULL; - GDIPalette = NULL; - ClipRegion = NULL; - ClipSize = 0; - BufferCount = 1; - Gamma = 1.0; - BufferPitch = Pitch; - FlipFlags = vid_vsync ? DDFLIP_WAIT : DDFLIP_WAIT|DDFLIP_NOVSYNC; - - NeedGammaUpdate = false; - NeedPalUpdate = false; - NeedResRecreate = false; - MustBuffer = false; - BufferingNow = false; - WasBuffering = false; - Write8bit = false; - UpdatePending = false; - UseBlitter = false; - - FlashAmount = 0; - - if (MemBuffer == NULL) - { - return; - } - - for (i = 0; i < 256; i++) - { - PalEntries[i].peRed = GPalette.BaseColors[i].r; - PalEntries[i].peGreen = GPalette.BaseColors[i].g; - PalEntries[i].peBlue = GPalette.BaseColors[i].b; - GammaTable[0][i] = GammaTable[1][i] = GammaTable[2][i] = i; - } - memcpy (SourcePalette, GPalette.BaseColors, sizeof(PalEntry)*256); - - MustBuffer = false; - - Windowed = !(static_cast(Video)->GoFullscreen (fullscreen)); - - if (vid_noblitter) - { - LOG ("Blitter forced off\n"); - } - else - { - DDCAPS hwcaps = { sizeof(DDCAPS) }; - HRESULT hr = DDraw->GetCaps (&hwcaps, NULL); - if (SUCCEEDED(hr)) - { - LOG2 ("dwCaps = %08lx, dwSVBCaps = %08lx\n", hwcaps.dwCaps, hwcaps.dwSVBCaps); - if (hwcaps.dwCaps & DDCAPS_BLT) - { - LOG ("Driver supports blits\n"); - if (hwcaps.dwSVBCaps & DDCAPS_CANBLTSYSMEM) - { - LOG ("Driver can blit from system memory\n"); - if (hwcaps.dwCaps & DDCAPS_BLTQUEUE) - { - LOG ("Driver supports asynchronous blits\n"); - UseBlitter = true; - } - else - { - LOG ("Driver does not support asynchronous blits\n"); - } - } - else - { - LOG ("Driver cannot blit from system memory\n"); - } - } - else - { - LOG ("Driver does not support blits\n"); - } - } - } - - if (!CreateResources ()) - { - if (PrimarySurf != NULL) - { - PrimarySurf->Release (); - PrimarySurf = NULL; - } - } -} - -DDrawFB::~DDrawFB () -{ - ReleaseResources (); -} - -bool DDrawFB::CreateResources () -{ - DDSURFACEDESC ddsd = { sizeof(ddsd), }; - HRESULT hr; - int bits; - - BufferCount = 1; - - if (!Windowed) - { - ShowWindow (Window, SW_SHOW); - // Remove the window border in fullscreen mode - SetWindowLongPtr (Window, GWL_STYLE, WS_VISIBLE|WS_SYSMENU); - - TrueHeight = Height; - for (Win32Video::ModeInfo *mode = static_cast(Video)->m_Modes; mode != NULL; mode = mode->next) - { - if (mode->width == Width && mode->height == Height) - { - TrueHeight = mode->realheight; - break; - } - } - hr = DDraw->SetDisplayMode (Width, TrueHeight, bits = vid_displaybits, 0, 0); - if (FAILED(hr)) - { - bits = 32; - while (FAILED(hr) && bits >= 8) - { - hr = DDraw->SetDisplayMode (Width, Height, bits, 0, 0); - bits -= 8; - } - if (FAILED(hr)) - { - LastHR = hr; - return false; - } - } - LOG3 ("Mode set to %d x %d x %d\n", Width, Height, bits); - - if (vid_attachedsurfaces && OSPlatform == os_WinNT4) - { - if (!CreateSurfacesAttached ()) - return false; - } - else - { - if (!CreateSurfacesComplex ()) - return false; - } - - if (UseBlitter) - { - UseBlitter = CreateBlitterSource (); - } - } - else - { - MustBuffer = true; - - LOG ("Running in a window\n"); - TrueHeight = Height; - - // Create the primary surface - ddsd.dwFlags = DDSD_CAPS; - ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; - do - { - hr = DDraw->CreateSurface (&ddsd, &PrimarySurf, NULL); - LOG1 ("Create primary: %08lx\n", hr); - } while (0); - if (FAILED(hr)) - { - LastHR = hr; - return false; - } - - MaybeCreatePalette (); - - // Resize the window to match desired dimensions - int sizew = Width + GetSystemMetrics (SM_CXSIZEFRAME)*2; - int sizeh = Height + GetSystemMetrics (SM_CYSIZEFRAME) * 2 + - GetSystemMetrics (SM_CYCAPTION); - LOG2 ("Resize window to %dx%d\n", sizew, sizeh); - VidResizing = true; - // Make sure the window has a border in windowed mode - SetWindowLongPtr (Window, GWL_STYLE, WS_VISIBLE|WS_OVERLAPPEDWINDOW); - if (!SetWindowPos (Window, NULL, 0, 0, sizew, sizeh, - SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER)) - { - LOG1 ("SetWindowPos failed because %08lx\n", GetLastError()); - } - VidResizing = false; - ShowWindow (Window, SW_SHOWNORMAL); - - // Create the clipper - hr = DDraw->CreateClipper (0, &Clipper, NULL); - LOG1 ("Create clipper: %08lx\n", hr); - if (FAILED(hr)) - { - LastHR = hr; - return false; - } - // Associate the clipper with the window - Clipper->SetHWnd (0, Window); - PrimarySurf->SetClipper (Clipper); - LOG1 ("Clipper @ %p set\n", Clipper); - - // Create the backbuffer - ddsd.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; - ddsd.dwWidth = Width; - ddsd.dwHeight = Height; - ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | (UseBlitter ? DDSCAPS_SYSTEMMEMORY : 0); - - hr = DDraw->CreateSurface (&ddsd, &BackSurf, NULL); - LOG1 ("Create backbuffer: %08lx\n", hr); - if (FAILED(hr)) - { - LastHR = hr; - return false; - } - LockingSurf = BackSurf; - LOG1 ("LockingSurf and BackSurf @ %p\n", BackSurf); - LOG ("Created backbuf\n"); - } - SetGamma (Gamma); - SetFlash (Flash, FlashAmount); - return true; -} - -bool DDrawFB::CreateSurfacesAttached () -{ - DDSURFACEDESC ddsd = { sizeof(ddsd), }; - HRESULT hr; - - LOG ("creating surfaces using AddAttachedSurface\n"); - - ddsd.dwFlags = DDSD_CAPS; - ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_VIDEOMEMORY; - hr = DDraw->CreateSurface (&ddsd, &PrimarySurf, NULL); - if (FAILED(hr)) - { - LastHR = hr; - return false; - } - - LOG1 ("Primary surface @ %p\n", PrimarySurf); - - // Under NT 4 and with bad DDraw drivers under 9x (and maybe Win2k?) - // if the palette is not attached to the primary surface before any - // back buffers are added to it, colors 0 and 255 will remain black - // and white respectively. - MaybeCreatePalette (); - - // Try for triple buffering. Unbuffered output is only allowed if - // we manage to get triple buffering. Even with double buffering, - // framerate can slow significantly compared to triple buffering, - // so we force buffering in that case, which effectively emulates - // triple buffering (after a fashion). - if (!AddBackBuf (&BackSurf, 1) || !AddBackBuf (&BackSurf2, 2)) - { -// MustBuffer = true; - } - if (BackSurf != NULL) - { - DDSCAPS caps = { DDSCAPS_BACKBUFFER, }; - hr = PrimarySurf->GetAttachedSurface (&caps, &LockingSurf); - if (FAILED (hr)) - { - LOG1 ("Could not get attached surface: %08lx\n", hr); - if (BackSurf2 != NULL) - { - PrimarySurf->DeleteAttachedSurface (0, BackSurf2); - BackSurf2->Release (); - BackSurf2 = NULL; - } - PrimarySurf->DeleteAttachedSurface (0, BackSurf); - BackSurf->Release (); - BackSurf = NULL; -// MustBuffer = true; - LockingSurf = PrimarySurf; - } - else - { - BufferCount = (BackSurf2 != NULL) ? 3 : 2; - LOG ("Got attached surface\n"); - } - } - else - { - LOG ("No flip chain\n"); - LockingSurf = PrimarySurf; - } - return true; -} - -bool DDrawFB::AddBackBuf (LPDIRECTDRAWSURFACE *surface, int num) -{ - DDSURFACEDESC ddsd = { sizeof(ddsd), }; - HRESULT hr; - - ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; - ddsd.dwWidth = Width; - ddsd.dwHeight = Height; - ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY; - hr = DDraw->CreateSurface (&ddsd, surface, NULL); - if (FAILED(hr)) - { - LOG2 ("could not create back buf %d: %08lx\n", num, hr); - return false; - } - else - { - LOG2 ("BackBuf %d created @ %p\n", num, *surface); - hr = PrimarySurf->AddAttachedSurface (*surface); - if (FAILED(hr)) - { - LOG2 ("could not add back buf %d: %08lx\n", num, hr); - (*surface)->Release (); - *surface = NULL; - return false; - } - else - { - LOG1 ("Attachment of back buf %d succeeded\n", num); - } - } - return true; -} - -bool DDrawFB::CreateSurfacesComplex () -{ - DDSURFACEDESC ddsd = { sizeof(ddsd), }; - HRESULT hr; - int tries = 2; - - LOG ("creating surfaces using a complex primary\n"); - - // Try for triple buffering first. - // If that fails, try for double buffering. - // If that fails, settle for single buffering. - // If that fails, then give up. - // - // However, if using the blitter, then do not triple buffer the - // primary surface, because that is effectively like quadruple - // buffering and player response starts feeling too sluggish. - ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; - ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_VIDEOMEMORY - | DDSCAPS_FLIP | DDSCAPS_COMPLEX; - do - { - LOG1 ("Try #%d\n", tries); - ddsd.dwBackBufferCount = UseBlitter ? 1 : 2; - hr = DDraw->CreateSurface (&ddsd, &PrimarySurf, NULL); - if (FAILED(hr)) - { - if (hr == DDERR_NOEXCLUSIVEMODE) - { - LOG ("Exclusive mode was lost, so restoring it now.\n"); - hr = DDraw->SetCooperativeLevel (Window, DDSCL_ALLOWMODEX | DDSCL_ALLOWREBOOT | DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); - LOG1 ("SetCooperativeLevel result: %08lx\n", hr); - hr = DDraw->SetDisplayMode (Width, Height, 8, 0, 0); - //hr = DDraw->RestoreDisplayMode (); - LOG1 ("SetDisplayMode result: %08lx\n", hr); - ++tries; - hr = E_FAIL; - continue; - } - - LOG1 ("Could not create with 2 backbuffers: %lx\n", hr); - ddsd.dwBackBufferCount = 1; - hr = DDraw->CreateSurface (&ddsd, &PrimarySurf, NULL); - if (FAILED(hr)) - { - LOG1 ("Could not create with 1 backbuffer: %lx\n", hr); - ddsd.ddsCaps.dwCaps &= ~DDSCAPS_FLIP | DDSCAPS_COMPLEX; - ddsd.dwBackBufferCount = 0; - hr = DDraw->CreateSurface (&ddsd, &PrimarySurf, NULL); - if (FAILED (hr)) - { - LOG1 ("Could not create with 0 backbuffers: %lx\n", hr); - if (tries == 2) - { - LOG ("Retrying without DDSCAPS_VIDEOMEMORY\n"); - ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE - | DDSCAPS_FLIP | DDSCAPS_COMPLEX; - } - } - } - } - } while (FAILED(hr) && --tries); - - if (FAILED(hr)) - { - LastHR = hr; - return false; - } - - LOG1 ("Complex surface chain @ %p\n", PrimarySurf); - if (PrimarySurf == NULL) - { - LOG ("It's NULL but it didn't fail?!?\n"); - LastHR = E_FAIL; - return false; - } - - if (ddsd.dwBackBufferCount == 0) - { - LOG ("No flip chain\n"); -// MustBuffer = true; - LockingSurf = PrimarySurf; - } - else - { - DDSCAPS caps = { DDSCAPS_BACKBUFFER, }; - hr = PrimarySurf->GetAttachedSurface (&caps, &LockingSurf); - if (FAILED (hr)) - { - LOG1 ("Could not get attached surface: %08lx\n", hr); -// MustBuffer = true; - LockingSurf = PrimarySurf; - } - else - { - BufferCount = ddsd.dwBackBufferCount + 1; - LOG1 ("Got attached surface. %d buffers\n", BufferCount); - } - } - - MaybeCreatePalette (); - return true; -} - -bool DDrawFB::CreateBlitterSource () -{ - DDSURFACEDESC ddsd = { sizeof(ddsd), }; - HRESULT hr; - - LOG ("Creating surface for blitter source\n"); - ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_BACKBUFFERCOUNT; - ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY - | DDSCAPS_FLIP | DDSCAPS_COMPLEX; - ddsd.dwBackBufferCount = 2; - ddsd.dwWidth = (Width==1024?1024+16:Width); - ddsd.dwHeight = Height; - hr = DDraw->CreateSurface (&ddsd, &BlitSurf, NULL); - if (FAILED(hr)) - { - LOG1 ("Trying to create blitter source with only one surface (%08lx)\n", hr); - ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; - ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; - hr = DDraw->CreateSurface (&ddsd, &BlitSurf, NULL); - if (FAILED(hr)) - { - LOG1 ("Could not create blitter source: %08lx\n", hr); - MustBuffer = true; - return false; - } - BufferCount = MAX (BufferCount, 1); - } - else - { - BufferCount = MAX (BufferCount, 2); - } - LOG1 ("Blitter source created successfully @ %p\n", BlitSurf); - return true; -} - -void DDrawFB::MaybeCreatePalette () -{ - DDPIXELFORMAT fmt = { sizeof(fmt), }; - HRESULT hr; - int i; - - UsePfx = false; - - // If the surface needs a palette, try to create one. If the palette - // cannot be created, the result is ugly but non-fatal. - hr = PrimarySurf->GetPixelFormat (&fmt); - if (SUCCEEDED (hr) && (fmt.dwFlags & DDPF_PALETTEINDEXED8)) - { - LOG ("Surface is paletted\n"); - GPfx.SetFormat (fmt.dwRGBBitCount, - fmt.dwRBitMask, fmt.dwGBitMask, fmt.dwBBitMask); - - if (Windowed) - { - struct { LOGPALETTE head; PALETTEENTRY filler[255]; } pal; - - LOG ("Writing in a window\n"); - Write8bit = true; - pal.head.palVersion = 0x300; - pal.head.palNumEntries = 256; - memcpy (pal.head.palPalEntry, PalEntries, 256*sizeof(PalEntries[0])); - for (i = 0; i < 256; i++) - { - pal.head.palPalEntry[i].peFlags = 0; - } - GDIPalette = CreatePalette (&pal.head); - LOG ("Created GDI palette\n"); - if (GDIPalette != NULL) - { - HDC dc = GetDC (Window); - SelectPalette (dc, GDIPalette, FALSE); - RealizePalette (dc); - ReleaseDC (Window, dc); - RebuildColorTable (); - } - } - else - { - hr = DDraw->CreatePalette (DDPCAPS_8BIT|DDPCAPS_ALLOW256, PalEntries, &Palette, NULL); - if (FAILED(hr)) - { - LOG ("Could not create palette\n"); - Palette = NULL; // NULL it just to be safe - } - else - { - hr = PrimarySurf->SetPalette (Palette); - if (FAILED(hr)) - { - LOG ("Could not attach palette to surface\n"); - Palette->Release (); - Palette = NULL; - } - else - { - // The palette was supposed to have been initialized with - // the correct colors, but some drivers don't do that. - // (On the other hand, the docs for the SetPalette method - // don't state that the surface will be set to the - // palette's colors when it gets set, so this might be - // legal behavior. Wish I knew...) - NeedPalUpdate = true; - } - } - } - } - else - { - LOG ("Surface is direct color\n"); - UsePfx = true; - GPfx.SetFormat (fmt.dwRGBBitCount, - fmt.dwRBitMask, fmt.dwGBitMask, fmt.dwBBitMask); - GPfx.SetPalette (GPalette.BaseColors); - } -} - -void DDrawFB::ReleaseResources () -{ - if (LockCount) - { - LockCount = 1; - Unlock (); - } - - if (ClipRegion != NULL) - { - delete[] ClipRegion; - ClipRegion = NULL; - } - if (Clipper != NULL) - { - Clipper->Release (); - Clipper = NULL; - } - if (PrimarySurf != NULL) - { - //Blank (); - PrimarySurf->Release (); - PrimarySurf = NULL; - } - if (BackSurf != NULL) - { - BackSurf->Release (); - BackSurf = NULL; - } - if (BackSurf2 != NULL) - { - BackSurf2->Release (); - BackSurf2 = NULL; - } - if (BlitSurf != NULL) - { - BlitSurf->Release (); - BlitSurf = NULL; - } - if (Palette != NULL) - { - Palette->Release (); - Palette = NULL; - } - if (GDIPalette != NULL) - { - HDC dc = GetDC (Window); - SelectPalette (dc, (HPALETTE)GetStockObject (DEFAULT_PALETTE), TRUE); - DeleteObject (GDIPalette); - ReleaseDC (Window, dc); - GDIPalette = NULL; - } - LockingSurf = NULL; -} - -int DDrawFB::GetPageCount () -{ - return MustBuffer ? 1 : BufferCount+1; -} - -void DDrawFB::PaletteChanged () -{ - // Somebody else changed the palette. If we are running fullscreen, - // they are obviously jerks, and we need to restore our own palette. - if (!Windowed) - { - if (Palette != NULL) - { - // It is not enough to set NeedPalUpdate to true. Some palette - // entries might now be reserved for system usage, and nothing - // we do will change them. The only way I have found to fix this - // is to recreate all our surfaces and the palette from scratch. - - // IMPORTANT: Do not recreate the resources here. The screen might - // be locked for a drawing operation. Do it later the next time - // somebody tries to lock it. - NeedResRecreate = true; - } - } - else - { - QueryNewPalette (); - } -} - -int DDrawFB::QueryNewPalette () -{ - LOG ("QueryNewPalette\n"); - if (GDIPalette == NULL && Windowed) - { - if (Write8bit) - { - RebuildColorTable (); - } - return 0; - } - - HDC dc = GetDC (Window); - HPALETTE oldPal = SelectPalette (dc, GDIPalette, FALSE); - int i = RealizePalette (dc); - SelectPalette (dc, oldPal, TRUE); - RealizePalette (dc); - ReleaseDC (Window, dc); - if (i != 0) - { - RebuildColorTable (); - } - return i; -} - -void DDrawFB::RebuildColorTable () -{ - int i; - - if (Write8bit) - { - PALETTEENTRY syspal[256]; - HDC dc = GetDC (Window); - - GetSystemPaletteEntries (dc, 0, 256, syspal); - - for (i = 0; i < 256; i++) - { - swap (syspal[i].peRed, syspal[i].peBlue); - } - for (i = 0; i < 256; i++) - { - GPfxPal.Pal8[i] = BestColor ((uint32 *)syspal, PalEntries[i].peRed, - PalEntries[i].peGreen, PalEntries[i].peBlue); - } - } -} - -bool DDrawFB::IsValid () -{ - return PrimarySurf != NULL; -} - -bool DDrawFB::Lock () -{ - return Lock (false); -} - -bool DDrawFB::Lock (bool useSimpleCanvas) -{ - bool wasLost; - -// LOG2 (" Lock (%d) <%d>\n", buffered, LockCount); - - if (LockCount++ > 0) - { - return false; - } - - wasLost = false; - - if (NeedResRecreate) - { - NeedResRecreate = false; - ReleaseResources (); - CreateResources (); - } - - LOG5 ("Lock %d %d %d %d %d\n", AppActive, SessionState, MustBuffer, useSimpleCanvas, UseBlitter); - - if (!AppActive || SessionState || MustBuffer || useSimpleCanvas || !UseBlitter) - { - Buffer = MemBuffer; - Pitch = BufferPitch; - BufferingNow = true; - } - else - { - HRESULT hr GCCNOWARN = BlitSurf->Flip (NULL, DDFLIP_WAIT); - LOG1 ("Blit flip = %08lx\n", hr); - LockSurfRes res = LockSurf (NULL, BlitSurf); - - if (res == NoGood) - { // We must have a surface locked before returning, - // but we could not lock the hardware surface, so buffer - // for this frame. - Buffer = MemBuffer; - Pitch = BufferPitch; - BufferingNow = true; - } - else - { - wasLost = (res == GoodWasLost); - } - } - - wasLost = wasLost || (BufferingNow != WasBuffering); - WasBuffering = BufferingNow; - return wasLost; -} - -void DDrawFB::Unlock () -{ - LOG1 ("Unlock <%d>\n", LockCount); - - if (LockCount == 0) - { - return; - } - - if (UpdatePending && LockCount == 1) - { - Update (); - } - else if (--LockCount == 0) - { - if (!BufferingNow) - { - if (BlitSurf == NULL) - { - LockingSurf->Unlock (NULL); - } - else - { - BlitSurf->Unlock (NULL); - } - } - Buffer = NULL; - } -} - -DDrawFB::LockSurfRes DDrawFB::LockSurf (LPRECT lockrect, LPDIRECTDRAWSURFACE toLock) -{ - HRESULT hr; - DDSURFACEDESC desc = { sizeof(desc), }; - bool wasLost = false; - bool lockingLocker = false; - - if (toLock == NULL) - { - lockingLocker = true; - if (LockingSurf == NULL) - { - if (!CreateResources ()) - { - if (LastHR != DDERR_UNSUPPORTEDMODE) - { - I_FatalError ("Could not rebuild framebuffer: %08lx", LastHR); - } - else - { - LOG ("Display is in unsupported mode right now.\n"); - return NoGood; - } - } - } - toLock = LockingSurf; - } - - hr = toLock->Lock (lockrect, &desc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); - LOG3 ("LockSurf %p (%d): %08lx\n", toLock, lockingLocker, hr); - - if (hr == DDERR_SURFACELOST) - { - wasLost = true; - if (FAILED (AttemptRestore ())) - { - return NoGood; - } - if (BlitSurf && FAILED(BlitSurf->IsLost ())) - { - LOG ("Restore blitter surface\n"); - hr = BlitSurf->Restore (); - if (FAILED (hr)) - { - LOG1 ("Could not restore blitter surface: %08lx", hr); - BlitSurf->Release (); - if (BlitSurf == toLock) - { - BlitSurf = NULL; - return NoGood; - } - BlitSurf = NULL; - } - } - if (lockingLocker) - { - toLock = LockingSurf; - } - LOG ("Trying to lock again\n"); - hr = toLock->Lock (lockrect, &desc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); - if (hr == DDERR_SURFACELOST && Windowed) - { // If this is NT, the user probably opened the Windows NT Security dialog. - // If this is not NT, trying to recreate everything from scratch won't hurt. - ReleaseResources (); - if (!CreateResources ()) - { - if (LastHR != DDERR_UNSUPPORTEDMODE) - { - I_FatalError ("Could not rebuild framebuffer: %08lx", LastHR); - } - else - { - LOG ("Display is in unsupported mode right now.\n"); - return NoGood; - } - } - if (lockingLocker) - { - toLock = LockingSurf; - } - hr = toLock->Lock (lockrect, &desc, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT | DDLOCK_WRITEONLY, NULL); - } - } - if (FAILED (hr)) - { // Still could not restore the surface, so don't draw anything - //I_FatalError ("Could not lock framebuffer: %08lx", hr); - LOG1 ("Final result after restoration attempts: %08lx\n", hr); - return NoGood; - } - Buffer = (BYTE *)desc.lpSurface; - Pitch = desc.lPitch; - BufferingNow = false; - return wasLost ? GoodWasLost : Good; -} - -HRESULT DDrawFB::AttemptRestore () -{ - LOG ("Restore primary\n"); - HRESULT hr = PrimarySurf->Restore (); - if (hr == DDERR_WRONGMODE && Windowed) - { // The user changed the screen mode - LOG ("DDERR_WRONGMODE and windowed, so recreating all resources\n"); - ReleaseResources (); - if (!CreateResources ()) - { - LOG1 ("Could not recreate framebuffer: %08lx", LastHR); - return LastHR; - } - } - else if (FAILED (hr)) - { - LOG1 ("Could not restore primary surface: %08lx", hr); - return hr; - } - if (BackSurf && FAILED(BackSurf->IsLost ())) - { - LOG ("Restore backbuffer\n"); - hr = BackSurf->Restore (); - if (FAILED (hr)) - { - I_FatalError ("Could not restore backbuffer: %08lx", hr); - } - } - if (BackSurf2 && FAILED(BackSurf2->IsLost ())) - { - LOG ("Restore backbuffer 2\n"); - hr = BackSurf2->Restore (); - if (FAILED (hr)) - { - I_FatalError ("Could not restore backbuffer 2: %08lx", hr); - } - } - return 0; -} - -void DDrawFB::Update () -{ - bool pchanged = false; - int i; - - LOG3 ("Update <%d,%c:%d>\n", LockCount, AppActive?'Y':'N', SessionState); - - if (LockCount != 1) - { - //I_FatalError ("Framebuffer must have exactly 1 lock to be updated"); - if (LockCount > 0) - { - UpdatePending = true; - --LockCount; - } - return; - } - - DrawRateStuff (); - - if (NeedGammaUpdate) - { - NeedGammaUpdate = false; - CalcGamma (Windowed || rgamma == 0.f ? Gamma : Gamma * rgamma, GammaTable[0]); - CalcGamma (Windowed || ggamma == 0.f ? Gamma : Gamma * ggamma, GammaTable[1]); - CalcGamma (Windowed || bgamma == 0.f ? Gamma : Gamma * bgamma, GammaTable[2]); - NeedPalUpdate = true; - } - - if (NeedPalUpdate || vid_palettehack) - { - NeedPalUpdate = false; - if (Palette != NULL || GDIPalette != NULL) - { - for (i = 0; i < 256; i++) - { - PalEntries[i].peRed = GammaTable[0][SourcePalette[i].r]; - PalEntries[i].peGreen = GammaTable[1][SourcePalette[i].g]; - PalEntries[i].peBlue = GammaTable[2][SourcePalette[i].b]; - } - if (FlashAmount) - { - DoBlending ((PalEntry *)PalEntries, (PalEntry *)PalEntries, - 256, GammaTable[2][Flash.b], GammaTable[1][Flash.g], GammaTable[0][Flash.r], - FlashAmount); - } - if (Palette != NULL) - { - pchanged = true; - } - else - { - /* Argh! Too slow! - SetPaletteEntries (GDIPalette, 0, 256, PalEntries); - HDC dc = GetDC (Window); - SelectPalette (dc, GDIPalette, FALSE); - RealizePalette (dc); - ReleaseDC (Window, dc); - */ - RebuildColorTable (); - } - } - else - { - for (i = 0; i < 256; i++) - { - ((PalEntry *)PalEntries)[i].r = GammaTable[0][SourcePalette[i].r]; - ((PalEntry *)PalEntries)[i].g = GammaTable[1][SourcePalette[i].g]; - ((PalEntry *)PalEntries)[i].b = GammaTable[2][SourcePalette[i].b]; - } - if (FlashAmount) - { - DoBlending ((PalEntry *)PalEntries, (PalEntry *)PalEntries, - 256, GammaTable[0][Flash.r], GammaTable[1][Flash.g], GammaTable[2][Flash.b], - FlashAmount); - } - GPfx.SetPalette ((PalEntry *)PalEntries); - } - } - - BlitCycles = 0; - clock (BlitCycles); - - if (BufferingNow) - { - LockCount = 0; - if ((Windowed || AppActive) && !SessionState && !PaintToWindow()) - { - if (LockSurf (NULL, NULL) != NoGood) - { - BYTE *writept = Buffer + (TrueHeight - Height)/2*Pitch; - LOG3 ("Copy %dx%d (%d)\n", Width, Height, BufferPitch); - if (UsePfx) - { - GPfx.Convert (MemBuffer, BufferPitch, - writept, Pitch, Width, Height, - FRACUNIT, FRACUNIT, 0, 0); - } - else - { - CopyFromBuff (MemBuffer, BufferPitch, Width, Height, writept); - } - if (TrueHeight != Height) - { - // Letterbox time! Draw black top and bottom borders. - int topborder = (TrueHeight - Height) / 2; - int botborder = TrueHeight - topborder - Height; - memset (Buffer, 0, Pitch*topborder); - memset (writept + Height*Pitch, 0, Pitch*botborder); - } - LockingSurf->Unlock (NULL); - } - } - } - else - { - if (BlitSurf != NULL) - { - HRESULT hr; - BlitSurf->Unlock (NULL); - RECT srcRect = { 0, 0, Width, Height }; - hr = LockingSurf->BltFast (0, 0, BlitSurf, &srcRect, DDBLTFAST_NOCOLORKEY|DDBLTFAST_WAIT); - if (FAILED (hr)) - { - LOG1 ("Could not blit: %08lx\n", hr); - if (hr == DDERR_SURFACELOST) - { - if (SUCCEEDED (AttemptRestore ())) - { - hr = LockingSurf->BltFast (0, 0, BlitSurf, &srcRect, DDBLTFAST_NOCOLORKEY|DDBLTFAST_WAIT); - if (FAILED (hr)) - { - LOG1 ("Blit retry also failed: %08lx\n", hr); - } - } - } - } - else - { - LOG ("Blit ok\n"); - } - } - else - { - LockingSurf->Unlock (NULL); - } - } - - unclock (BlitCycles); - LOG1 ("cycles = %d\n", BlitCycles); - - Buffer = NULL; - LockCount = 0; - UpdatePending = false; - - if (!Windowed && AppActive && !SessionState /*&& !UseBlitter && !MustBuffer*/) - { - HRESULT hr = PrimarySurf->Flip (NULL, FlipFlags); - LOG1 ("Flip = %08lx\n", hr); - if (hr == DDERR_INVALIDPARAMS) - { - if (FlipFlags & DDFLIP_NOVSYNC) - { - FlipFlags &= ~DDFLIP_NOVSYNC; - Printf ("Can't disable vsync\n"); - PrimarySurf->Flip (NULL, FlipFlags); - } - } - } - - if (pchanged && AppActive && !SessionState) - { - Palette->SetEntries (0, 0, 256, PalEntries); - } -} - -bool DDrawFB::PaintToWindow () -{ - if (Windowed && LockCount == 0) - { - HRESULT hr; - RECT rect; - GetClientRect (Window, &rect); - if (rect.right != 0 && rect.bottom != 0) - { - // Use blit to copy/stretch to window's client rect - ClientToScreen (Window, (POINT*)&rect.left); - ClientToScreen (Window, (POINT*)&rect.right); - LOG ("Paint to window\n"); - if (LockSurf (NULL, NULL) != NoGood) - { - GPfx.Convert (MemBuffer, BufferPitch, - Buffer, Pitch, Width, Height, - FRACUNIT, FRACUNIT, 0, 0); - LockingSurf->Unlock (NULL); - if (FAILED (hr = PrimarySurf->Blt (&rect, BackSurf, NULL, DDBLT_WAIT|DDBLT_ASYNC, NULL))) - { - if (hr == DDERR_SURFACELOST) - { - PrimarySurf->Restore (); - } - PrimarySurf->Blt (&rect, BackSurf, NULL, DDBLT_WAIT, NULL); - } - } - Buffer = NULL; - LOG ("Did paint to window\n"); - } - return true; - } - return false; -} - -PalEntry *DDrawFB::GetPalette () -{ - return SourcePalette; -} - -void DDrawFB::UpdatePalette () -{ - NeedPalUpdate = true; -} - -bool DDrawFB::SetGamma (float gamma) -{ - LOG1 ("SetGamma %g\n", gamma); - Gamma = gamma; - NeedGammaUpdate = true; - return true; -} - -bool DDrawFB::SetFlash (PalEntry rgb, int amount) -{ - Flash = rgb; - FlashAmount = amount; - NeedPalUpdate = true; - return true; -} - -void DDrawFB::GetFlash (PalEntry &rgb, int &amount) -{ - rgb = Flash; - amount = FlashAmount; -} - -// Q: Should I gamma adjust the returned palette? -// A: No. PNG screenshots save the gamma value, so there is no need. -void DDrawFB::GetFlashedPalette (PalEntry pal[256]) -{ - memcpy (pal, SourcePalette, 256*sizeof(PalEntry)); - if (FlashAmount) - { - DoBlending (pal, pal, 256, Flash.r, Flash.g, Flash.b, FlashAmount); - } -} - -void DDrawFB::Blank () -{ - if (IsFullscreen ()) - { - DDBLTFX blitFX = { sizeof(blitFX) }; - - blitFX.dwFillColor = 0; - DDraw->FlipToGDISurface (); - PrimarySurf->Blt (NULL, NULL, NULL, DDBLT_COLORFILL, &blitFX); - } -} - -void DDrawFB::DoSpeedTest () -{ - static const int testarea = 2048*1536*60; - LARGE_INTEGER start, end, diff, speed; - unsigned long vspeed, mspeed; - int area, realtestarea, repetitions; - - if (LockSurf (NULL, NULL) == NoGood) - { - Printf ("Failed to lock video memory\n"); - } - - area = Width * Height; - repetitions = testarea / area; - realtestarea = repetitions * area; - - QueryPerformanceCounter (&start); - do - { - memset (Buffer, repetitions, area); - } while (--repetitions); - QueryPerformanceCounter (&end); - LockingSurf->Unlock (NULL); - - QueryPerformanceFrequency (&speed); - diff.QuadPart = end.QuadPart - start.QuadPart; - vspeed = (unsigned long)(realtestarea * speed.QuadPart / diff.QuadPart); - - repetitions = testarea / area; - QueryPerformanceCounter (&start); - do - { - memset (MemBuffer, repetitions, area); - } while (--repetitions); - QueryPerformanceCounter (&end); - - diff.QuadPart = end.QuadPart - start.QuadPart; - mspeed = (unsigned long)(realtestarea * speed.QuadPart / diff.QuadPart); - - Printf ("Write to vid mem:%9luk/sec\n", vspeed / 1024); - Printf ("Write to sys mem:%9luk/sec\n", mspeed / 1024); -} - -CCMD (vid_speedtest) -{ - if (screen != NULL) - { - static_cast(screen)->DoSpeedTest (); - } -} - -ADD_STAT (blit) -{ - FString out; - out.Format ( - "blit=%04.1f ms", - (double)BlitCycles * SecondsPerCycle * 1000 - ); - return out; -} diff --git a/zdoom.vcproj b/zdoom.vcproj index 4d9bf870c..cccf27e61 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -5040,6 +5040,14 @@ RelativePath="src\win32\eaxedit.cpp" > + + + +