DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/output/direct3d/direct3d.cpp
00001 /*
00002  *  Direct3D rendering code by gulikoza
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA.
00017  */
00018 
00019 #include "dosbox.h"
00020 #include "control.h"
00021 #include "menu.h"
00022 
00023 #if (HAVE_D3D9_H) && defined(WIN32)
00024 
00025 extern Bitu currentWindowWidth, currentWindowHeight;
00026 
00027 #include "direct3d.h"
00028 #include "render.h" // IMPLEMENTED
00029 #include <sstream>
00030 
00031 #if LOG_D3D && D3D_THREAD
00032 #define EnterCriticalSection(x) EnterLOGCriticalSection(x, __LINE__)
00033 
00034 void CDirect3D::EnterLOGCriticalSection(LPCRITICAL_SECTION lpCriticalSection, int line)
00035 {
00036     Bitu i = 0;
00037     static int oldline = -1;
00038     while(!TryEnterCriticalSection(lpCriticalSection)) {
00039         i++; Sleep(0);
00040         if(!(i&0xFFF)) {
00041             LOG_MSG("D3D:Possible deadlock in %u (line: %d, oldline: %d, active command: %d)", SDL_ThreadID(), line, oldline, thread_command);
00042         }
00043     }
00044     if(i) LOG_MSG("D3D:Thread %u waited %u to enter critical section (line: %d, oldline: %d, active command: %d)", SDL_ThreadID(), i, line, oldline, thread_command);
00045     oldline = line;
00046 }
00047 #endif
00048 
00049 #include "d3d_components.h"
00050 
00051 HRESULT CDirect3D::InitializeDX(HWND wnd, bool triplebuf)
00052 {
00053 #if LOG_D3D
00054     LOG_MSG("D3D:Starting Direct3D");
00055 #endif
00056 
00057         backbuffer_clear_countdown = 0;
00058 
00059     // Check for display window
00060     if(!wnd) {
00061         LOG_MSG("Error: No display window set!");
00062         return E_FAIL;
00063     }
00064 
00065     hwnd = wnd;
00066 
00067         if (mhmodDX9 == NULL)
00068                 mhmodDX9 = LoadLibrary("d3d9.dll");
00069     if (mhmodDX9 == NULL)
00070                 return E_FAIL;
00071 
00072     // Set the presentation parameters
00073     ZeroMemory(&d3dpp, sizeof(d3dpp));
00074     d3dpp.BackBufferWidth = dwWidth;
00075     d3dpp.BackBufferHeight = dwHeight;
00076     d3dpp.BackBufferCount = 1;
00077     d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
00078     d3dpp.Windowed = true;
00079     d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
00080         Section_prop * sec=static_cast<Section_prop *>(control->GetSection("vsync"));
00081         if(sec) {
00082                         d3dpp.PresentationInterval = (!strcmp(sec->Get_string("vsyncmode"),"host"))?D3DPRESENT_INTERVAL_DEFAULT:D3DPRESENT_INTERVAL_IMMEDIATE;
00083         }
00084 
00085 
00086     if(triplebuf) {
00087         LOG_MSG("D3D:Using triple buffering");
00088         d3dpp.BackBufferCount = 2;
00089     }
00090 
00091     // Init Direct3D
00092     if(FAILED(InitD3D())) {
00093         DestroyD3D();
00094         LOG_MSG("Error: Unable to initialize DirectX9!");
00095         return E_FAIL;
00096     }
00097 
00098 #if D3D_THREAD
00099 #if LOG_D3D
00100     LOG_MSG("D3D:Starting worker thread from thread: %u", SDL_ThreadID());
00101 #endif
00102 
00103     thread_run = true;
00104     thread_command = D3D_IDLE;
00105     thread = SDL_CreateThread(EntryPoint, this);
00106     SDL_SemWait(thread_ack);
00107 #endif
00108 
00109     return S_OK;
00110 }
00111 
00112 #if D3D_THREAD
00113 // Wait for D3D to finish processing and return the result
00114 HRESULT CDirect3D::Wait(bool unlock) {
00115     HRESULT res;
00116 
00117     EnterCriticalSection(&cs);
00118     while(thread_command != D3D_IDLE) {
00119         wait = true;
00120         LeaveCriticalSection(&cs);
00121 #if LOG_D3D
00122         LOG_MSG("D3D:Waiting for D3D thread to finish processing...(command: %d)", thread_command);
00123 #endif
00124         SDL_SemWait(thread_ack);
00125         EnterCriticalSection(&cs);
00126         wait = false;
00127     }
00128 
00129 #if LOG_D3D
00130     if(SDL_SemValue(thread_ack)) LOG_MSG("D3D:Semaphore has value: %d!", SDL_SemValue(thread_ack));
00131 #endif
00132 
00133     res = thread_hr;
00134     if(unlock) LeaveCriticalSection(&cs);
00135     return res;
00136 }
00137 #endif
00138 
00139 #if D3D_THREAD
00140 int CDirect3D::Start(void)
00141 {
00142 #if LOG_D3D
00143     LOG_MSG("D3D:Worker thread %u starting", SDL_ThreadID());
00144 #endif
00145 
00146     SDL_SemPost(thread_ack);
00147 
00148     EnterCriticalSection(&cs);
00149     while(thread_run) {
00150 
00151         HRESULT hr;
00152         D3D_state tmp = thread_command;
00153         LeaveCriticalSection(&cs);
00154 
00155         if(tmp == D3D_IDLE) {
00156             SDL_SemWait(thread_sem);
00157             EnterCriticalSection(&cs);
00158             continue;
00159         }
00160 
00161         switch(tmp) {
00162             case D3D_LOADPS: hr = LoadPixelShader(); break;
00163             case D3D_LOCK: hr = LockTexture(); break;
00164             case D3D_UNLOCK: (UnlockTexture() ? hr = S_OK : hr = E_FAIL); break;
00165             default: hr = S_OK; break;
00166         }
00167 
00168         EnterCriticalSection(&cs);
00169         thread_hr = hr;
00170         thread_command = D3D_IDLE;
00171         if(wait) {
00172                 LeaveCriticalSection(&cs);
00173                 SDL_SemPost(thread_ack);
00174                 EnterCriticalSection(&cs);
00175         }
00176     }
00177 
00178 #if LOG_D3D
00179     LOG_MSG("D3D:Thread %u is finishing...", SDL_ThreadID());
00180 #endif
00181     LeaveCriticalSection(&cs);
00182 
00183     return 0;
00184 }
00185 #endif
00186 
00187 bool CDirect3D::LockTexture(Bit8u * & pixels,Bitu & pitch)
00188 {
00189 #if D3D_THREAD
00190     Wait(false);
00191 
00192     // Locks take a bit, waiting for the worker thread to do it will most certainly
00193     // take us waiting in the kernel mode...try to lock the texture directly...
00194     if(FAILED(LockTexture())) {
00195 
00196         // OK, let the worker thread do it...
00197         thread_command = D3D_LOCK;
00198         LeaveCriticalSection(&cs);
00199         SDL_SemPost(thread_sem);
00200 
00201         if(FAILED(Wait(false))) {
00202             LeaveCriticalSection(&cs);
00203             LOG_MSG("D3D:No texture to draw to!?");
00204             return false;
00205         }
00206     }
00207     LeaveCriticalSection(&cs);
00208 #else
00209     if(FAILED(LockTexture())) {
00210         LOG_MSG("D3D:No texture to draw to!?");
00211         return false;
00212     }
00213 #endif
00214 
00215     pixels=(Bit8u *)d3dlr.pBits;
00216     pitch=d3dlr.Pitch;
00217     return true;
00218 }
00219 
00220 HRESULT CDirect3D::LockTexture(void)
00221 {
00222     // Lock the surface and write the pixels
00223     static DWORD d3dflags = D3DLOCK_NO_DIRTY_UPDATE;
00224 lock_texture:
00225     if(GCC_UNLIKELY(!lpTexture || deviceLost)) {
00226         // Try to reset the device, but only when running main thread
00227 #if D3D_THREAD
00228         if((SDL_ThreadID() != SDL_GetThreadID(thread)))
00229 #endif
00230         {
00231             Resize3DEnvironment();
00232         }
00233         if(GCC_UNLIKELY(!lpTexture || deviceLost)) {
00234             LOG_MSG("D3D:Device is lost, locktexture() failed...");
00235             return E_FAIL;
00236         }
00237         // Success, continue as planned...
00238     }
00239     if(GCC_UNLIKELY(lpTexture->LockRect(0, &d3dlr, NULL, d3dflags) != D3D_OK)) {
00240         if(d3dflags) {
00241             d3dflags = 0;
00242             LOG_MSG("D3D:Cannot lock texture, fallback to compatible mode");
00243             goto lock_texture;
00244         } else {
00245             LOG_MSG("D3D:Failed to lock texture!");
00246         }
00247         return E_FAIL;
00248     }
00249 
00250     return S_OK;
00251 }
00252 
00253 bool CDirect3D::UnlockTexture(const Bit16u *changed)
00254 {
00255         changedLines = changed;
00256 #if D3D_THREAD
00257         Wait(false);
00258         thread_command = D3D_UNLOCK;
00259         LeaveCriticalSection(&cs);
00260 
00261         SDL_SemPost(thread_sem);
00262         return true;
00263 #else
00264         return UnlockTexture();
00265 #endif
00266 }
00267 
00268 bool CDirect3D::UnlockTexture(void)
00269 {
00270     if(GCC_UNLIKELY(deviceLost)) return false;
00271     // Support EndUpdate without new data...needed by some shaders
00272     if(GCC_UNLIKELY(!d3dlr.pBits)) return D3DSwapBuffers();
00273     lpTexture->UnlockRect(0);
00274     d3dlr.pBits = NULL;
00275     RECT rect;
00276     rect.left = 0;
00277     rect.right = dwWidth;
00278     if(!dynamic && changedLines) {
00279         Bitu y = 0, index = 0;
00280         while(y < dwHeight) {
00281             if(!(index & 1)) {
00282                 y += changedLines[index];
00283             } else {
00284                 rect.top = (LONG)y;
00285                 rect.bottom = (LONG)(y + changedLines[index]);
00286                 lpTexture->AddDirtyRect(&rect);
00287                 y += changedLines[index];
00288             }
00289             index++;
00290         }
00291     } else {
00292         rect.top = 0; rect.bottom = dwHeight;
00293         lpTexture->AddDirtyRect(&rect);
00294     }
00295 
00296     return D3DSwapBuffers();
00297 }
00298 
00299 HRESULT CDirect3D::InitD3D(void)
00300 {
00301     IDirect3D9 *(APIENTRY *pDirect3DCreate9)(UINT) = (IDirect3D9 *(APIENTRY *)(UINT))GetProcAddress(mhmodDX9, "Direct3DCreate9");
00302     if(!pDirect3DCreate9)
00303         return E_FAIL;
00304 
00305     // create the IDirect3D9 object
00306     pD3D9 = pDirect3DCreate9(D3D_SDK_VERSION);
00307     if(pD3D9 == NULL)
00308         return E_FAIL;
00309 
00310     D3DCAPS9 d3dCaps;
00311     // get device capabilities
00312     ZeroMemory(&d3dCaps, sizeof(d3dCaps));
00313     if(FAILED(pD3D9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps)))
00314         return E_FAIL;
00315 
00316     // get the display mode
00317     D3DDISPLAYMODE d3ddm;
00318     pD3D9->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
00319     d3dpp.BackBufferFormat = d3ddm.Format;
00320 
00321     HRESULT hr;
00322 
00323     // Check if hardware vertex processing is available
00324     if(d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) {
00325         // Create device with hardware vertex processing
00326         hr = pD3D9->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL, hwnd,
00327             D3DCREATE_HARDWARE_VERTEXPROCESSING|0x00000800L|D3DCREATE_FPU_PRESERVE, &d3dpp, &pD3DDevice9);
00328     } else {
00329         // Create device with software vertex processing
00330         hr = pD3D9->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL, hwnd,
00331             D3DCREATE_SOFTWARE_VERTEXPROCESSING|0x00000800L|D3DCREATE_FPU_PRESERVE, &d3dpp, &pD3DDevice9);
00332     }
00333 
00334     // Make sure device was created
00335     if(FAILED(hr)) {
00336         LOG_MSG("D3D:Unable to create D3D device!");
00337         return E_FAIL;
00338     }
00339 
00340     if(FAILED(pD3D9->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3ddm.Format,
00341                             0, D3DRTYPE_TEXTURE, D3DFMT_X8R8G8B8))) {
00342         if(FAILED(pD3D9->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3ddm.Format,
00343                             0, D3DRTYPE_TEXTURE, D3DFMT_R5G6B5))) {
00344             DestroyD3D();
00345             LOG_MSG("Error: Cannot find a working texture color format!");
00346             return E_FAIL;
00347         }
00348 
00349         bpp16 = true;
00350         LOG_MSG("D3D:Running in 16-bit color mode");
00351     }
00352 
00353     if(d3dCaps.TextureCaps & D3DPTEXTURECAPS_SQUAREONLY) {
00354 #if LOG_D3D
00355         LOG_MSG("D3D:Square-only textures");
00356 #endif
00357         square = true;
00358     } else {
00359 #if LOG_D3D
00360         LOG_MSG("D3D:Non-square textures");
00361 #endif
00362         square = false;
00363     }
00364 
00365     if(d3dCaps.TextureCaps & D3DPTEXTURECAPS_POW2) {
00366         if(d3dCaps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL) {
00367 #if LOG_D3D
00368             LOG_MSG("D3D:Conditional non-pow2 texture size support");
00369 #endif
00370             pow2 = false;
00371         } else {
00372 #if LOG_D3D
00373             LOG_MSG("D3D:Textures must be a power of 2 in size");
00374 #endif
00375             pow2 = true;
00376         }
00377     } else {
00378 #if LOG_D3D
00379        LOG_MSG("D3D:Textures do not need to be a power of 2 in size");
00380 #endif
00381        pow2 = false;
00382     }
00383 
00384     if(d3dCaps.Caps2 & D3DCAPS2_DYNAMICTEXTURES) {
00385 #if LOG_D3D
00386         LOG_MSG("D3D:Dynamic textures supported");
00387 #endif
00388         dynamic = true;
00389     } else {
00390         LOG_MSG("D3D:Dynamic textures NOT supported. Performance will suffer!");
00391         dynamic = false;
00392     }
00393 
00394 #if LOG_D3D
00395     LOG_MSG("D3D:Max texture width: %d", d3dCaps.MaxTextureWidth);
00396     LOG_MSG("D3D:Max texture height: %d", d3dCaps.MaxTextureHeight);
00397 #endif
00398 
00399     if((d3dCaps.MaxTextureWidth < 1024) || (d3dCaps.MaxTextureHeight < 1024)) {
00400         DestroyD3D();
00401         LOG_MSG("Error: Your card does not support large textures!");
00402         return E_FAIL;
00403     }
00404 
00405 #if C_D3DSHADERS
00406     // allow scale2x_ps14.fx with 1.4 shaders, everything else requires 2.0
00407     if(d3dCaps.PixelShaderVersion >= D3DPS_VERSION(1,4)) {
00408 #if LOG_D3D
00409         LOG_MSG("D3D:Hardware PS version %d.%d", D3DSHADER_VERSION_MAJOR(d3dCaps.PixelShaderVersion),
00410                                                     D3DSHADER_VERSION_MINOR(d3dCaps.PixelShaderVersion));
00411 #endif
00412         if(d3dCaps.PixelShaderVersion == D3DPS_VERSION(1,4))
00413             LOG_MSG("D3D:Hardware PS version 1.4 detected. Most shaders probably won't work...");
00414         if((d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) && (dynamic)) {
00415             psEnabled = true;
00416             square = true;
00417             pow2 = true;                        // pow2 texture size has to be enabled as well
00418         } else {
00419             LOG_MSG("D3D:Error when initializing pixel shader support. Disabling shaders.");
00420             psEnabled = false;
00421         }
00422     }
00423     else {
00424         LOG_MSG("D3D:Hardware PS version too low. Disabling support for shaders.");
00425         psEnabled = false;
00426     }
00427 #endif
00428 
00429     DWORD               dwNumAdapterModes;
00430     D3DDISPLAYMODE      DisplayMode;
00431     DWORD               m;
00432 
00433     dwNumModes = 0;
00434 
00435     if(bpp16)
00436         dwNumAdapterModes = pD3D9->GetAdapterModeCount(D3DADAPTER_DEFAULT, D3DFMT_R5G6B5);
00437     else
00438         dwNumAdapterModes = pD3D9->GetAdapterModeCount(D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8);
00439 
00440     if(dwNumAdapterModes == 0) {
00441         LOG_MSG("D3D:No display modes found");
00442         return E_FAIL;
00443     }
00444 
00445 #if LOG_D3D
00446     LOG_MSG("D3D:Found %d display modes", dwNumAdapterModes);
00447 #endif
00448     modes = (D3DDISPLAYMODE*)malloc(sizeof(D3DDISPLAYMODE)*dwNumAdapterModes);
00449 
00450     if(!modes) {
00451         LOG_MSG("D3D:Error allocating memory!");
00452         DestroyD3D();
00453         return E_FAIL;
00454     }
00455 
00456     for(iMode=0;iMode<dwNumAdapterModes;iMode++) {
00457         // Get the display mode attributes
00458         if(bpp16)
00459             pD3D9->EnumAdapterModes(D3DADAPTER_DEFAULT, D3DFMT_R5G6B5, iMode, &DisplayMode);
00460         else
00461             pD3D9->EnumAdapterModes(D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8, iMode, &DisplayMode);
00462 
00463         // Check if the mode already exists
00464         for(m=0;m<dwNumModes;m++) {
00465             if((modes[m].Width == DisplayMode.Width) && (modes[m].Height == DisplayMode.Height) &&
00466                 (modes[m].Format == DisplayMode.Format))
00467                 break;
00468         }
00469 
00470         // If we found a new mode, add it to the list of modes
00471         if(m==dwNumModes) {
00472 #if LOG_D3D
00473             LOG_MSG("D3D:Display mode: %dx%dx%d", DisplayMode.Width, DisplayMode.Height, DisplayMode.Format);
00474 #endif
00475             // Try to sort resolutions
00476             if(dwNumModes>0) {
00477                 if(((modes[m - 1].Width == DisplayMode.Width) && (modes[m - 1].Height > DisplayMode.Height)) ||
00478                    (modes[m - 1].Width  > DisplayMode.Width)) {
00479                     modes[m].Width  = modes[m - 1].Width;
00480                     modes[m].Height = modes[m - 1].Height;
00481                     modes[m].Format = modes[m - 1].Format;
00482                     m--;
00483                 }
00484             }
00485 
00486             modes[m].Width       = DisplayMode.Width;
00487             modes[m].Height      = DisplayMode.Height;
00488             modes[m].Format      = DisplayMode.Format;
00489             modes[m].RefreshRate = 0;
00490             ++dwNumModes;
00491         }
00492     }
00493 
00494     // Free some unused memory
00495     modes = (D3DDISPLAYMODE*)realloc(modes, sizeof(D3DDISPLAYMODE)*dwNumModes);
00496 
00497     if(!modes) {
00498         LOG_MSG("D3D:Error allocating memory!");
00499         DestroyD3D();
00500         return E_FAIL;
00501     }
00502 
00503     dwTexHeight = 0;
00504     dwTexWidth = 0;
00505 
00506     return S_OK;
00507 }
00508 
00509 void CDirect3D::DestroyD3D(void)
00510 {
00511 #if D3D_THREAD
00512     // Kill child thread
00513     if(thread != NULL) {
00514         Wait(false);
00515         thread_run = false;
00516         thread_command = D3D_IDLE;
00517         LeaveCriticalSection(&cs);
00518         SDL_SemPost(thread_sem);
00519         SDL_WaitThread(thread, NULL);
00520         thread = NULL;
00521     }
00522 #endif
00523 
00524     InvalidateDeviceObjects();
00525 
00526     // Delete D3D device
00527     SAFE_RELEASE(pD3DDevice9);
00528     SAFE_RELEASE(pD3D9);
00529 
00530     if(modes) {
00531         free(modes);
00532         modes = NULL;
00533     }
00534 }
00535 
00536 // copy a rect from the SDL surface to the Direct3D9 backbuffer
00537 void CDirect3D::UpdateRectFromSDLSurface(int x,int y,int w,int h) {
00538         if (x < 0 || y < 0 || (unsigned int)(x+w) > d3dpp.BackBufferWidth || (unsigned int)(y+h) > d3dpp.BackBufferHeight)
00539                 return;
00540         if (w <= 0 || h <= 0)
00541                 return;
00542 
00543         IDirect3DSurface9 *bbsurf = NULL;
00544         IDirect3DSurface9 *tsurf = NULL;
00545 
00546         if (pD3DDevice9->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &bbsurf) == D3D_OK) {
00547                 if (pD3DDevice9->CreateOffscreenPlainSurface(w, h, d3dpp.BackBufferFormat, D3DPOOL_SYSTEMMEM, &tsurf, NULL) == D3D_OK) {
00548                         D3DLOCKED_RECT rl;
00549 
00550                         if (tsurf->LockRect(&rl, NULL, 0) == D3D_OK) {
00551                                 unsigned char *GFX_GetSurfacePtr(size_t *pitch, unsigned int x, unsigned int y);
00552                                 void GFX_ReleaseSurfacePtr(void);
00553 
00554                                 size_t sdl_pitch = 0,sdl_copy;
00555                                 unsigned char *sdl_surface = GFX_GetSurfacePtr(&sdl_pitch, x, y);
00556 
00557                                 if (sdl_surface != NULL) {
00558                                         sdl_copy = w * (bpp16 ? 2 : 4);
00559 
00560 //                                      fprintf(stderr,"sdl_copy=%u sdl_pitch=%u dxpitch=%u\n",
00561 //                                              (unsigned int)sdl_copy,(unsigned int)sdl_pitch,(unsigned int)rl.Pitch);
00562 
00563                                         for (unsigned int iy=0;iy < (unsigned int)h;iy++) {
00564                                                 unsigned char *sp = sdl_surface + (iy * sdl_pitch);
00565                                                 unsigned char *dp = (unsigned char*)rl.pBits + (iy * rl.Pitch);
00566 
00567                                                 memcpy(dp, sp, sdl_copy);
00568                                         }
00569 
00570                                         GFX_ReleaseSurfacePtr();
00571                                 }
00572 
00573                                 tsurf->UnlockRect();
00574 
00575                                 RECT rc;
00576                                 POINT pt;
00577 
00578                                 rc.top = 0;
00579                                 rc.left = 0;
00580                                 rc.right = w;
00581                                 rc.bottom = h;
00582                                 pt.x = 0;
00583                                 pt.y = 0;
00584 
00585                                 pD3DDevice9->UpdateSurface(/*source*/tsurf, &rc, /*dest*/bbsurf, &pt);
00586                         }
00587                 }
00588         }
00589 
00590         SAFE_RELEASE(bbsurf);
00591         SAFE_RELEASE(tsurf);
00592 }
00593 
00594 // copy a rect to the SDL surface from the Direct3D9 backbuffer
00595 void CDirect3D::UpdateRectToSDLSurface(int x,int y,int w,int h) {
00596         if (x < 0 || y < 0 || (unsigned int)(x+w) > d3dpp.BackBufferWidth || (unsigned int)(y+h) > d3dpp.BackBufferHeight)
00597                 return;
00598         if (w <= 0 || h <= 0)
00599                 return;
00600 
00601         IDirect3DSurface9 *bbsurf = NULL;
00602         IDirect3DSurface9 *tsurf = NULL;
00603 
00604         if (pD3DDevice9->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &bbsurf) == D3D_OK) {
00605                 /* NTS: Microsoft doesn't seem to offer a way to capture only a part of the backbuffer :( */
00606                 if (pD3DDevice9->CreateOffscreenPlainSurface(d3dpp.BackBufferWidth, d3dpp.BackBufferHeight, d3dpp.BackBufferFormat, D3DPOOL_SYSTEMMEM, &tsurf, NULL) == D3D_OK) {
00607                         D3DLOCKED_RECT rl;
00608 
00609                         if (pD3DDevice9->GetRenderTargetData(/*source*/bbsurf, /*dest*/tsurf) != D3D_OK)
00610                                 fprintf(stderr,"FAIL\n");
00611 
00612                         if (tsurf->LockRect(&rl, NULL, 0) == D3D_OK) {
00613                                 unsigned char *GFX_GetSurfacePtr(size_t *pitch, unsigned int x, unsigned int y);
00614                                 void GFX_ReleaseSurfacePtr(void);
00615 
00616                                 size_t sdl_pitch = 0,sdl_copy;
00617                                 unsigned char *sdl_surface = GFX_GetSurfacePtr(&sdl_pitch, x, y);
00618 
00619                                 if (sdl_surface != NULL) {
00620                                         sdl_copy = w * (bpp16 ? 2 : 4);
00621 
00622 //                                      fprintf(stderr,"sdl_copy=%u sdl_pitch=%u dxpitch=%u\n",
00623 //                                              (unsigned int)sdl_copy,(unsigned int)sdl_pitch,(unsigned int)rl.Pitch);
00624 
00625                                         for (unsigned int iy=0;iy < (unsigned int)h;iy++) {
00626                                                 unsigned char *sp = (unsigned char*)rl.pBits + ((iy + y) * rl.Pitch) + (x * (bpp16 ? 2 : 4));
00627                                                 unsigned char *dp = sdl_surface + (iy * sdl_pitch);
00628 
00629                                                 memcpy(dp, sp, sdl_copy);
00630                                         }
00631 
00632                                         GFX_ReleaseSurfacePtr();
00633                                 }
00634 
00635                                 tsurf->UnlockRect();
00636                         }
00637                 }
00638         }
00639 
00640         SAFE_RELEASE(bbsurf);
00641         SAFE_RELEASE(tsurf);
00642 }
00643 
00644 // Draw a textured quad on the back-buffer
00645 bool CDirect3D::D3DSwapBuffers(void)
00646 {
00647     HRESULT hr;
00648     UINT uPasses;
00649 
00650         if (backbuffer_clear_countdown > 0) {
00651                 backbuffer_clear_countdown--;
00652                 pD3DDevice9->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
00653         }
00654 
00655 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW
00656     // If the SDL drawn menus are visible, copy the SDL surface to the Direct3D surface to keep them visible on-screen
00657     if (mainMenu.isVisible() && mainMenu.menuBox.h != 0 && dwY >= mainMenu.menuBox.h)
00658         UpdateRectFromSDLSurface(0, 0, mainMenu.menuBox.w, mainMenu.menuBox.h);
00659 #endif
00660 
00661     // begin rendering
00662     pD3DDevice9->BeginScene();
00663 
00664 #if C_D3DSHADERS
00665     /* PS 2.0 path */
00666     if(psActive) {
00667 
00668         if(preProcess) {
00669 
00670             // Set preprocess matrices
00671             if(FAILED(psEffect->SetMatrices(m_matPreProj, m_matPreView, m_matPreWorld))) {
00672                 LOG_MSG("D3D:Set matrices failed.");
00673                 return false;
00674             }
00675 
00676             // Save render target
00677             LPDIRECT3DSURFACE9 lpRenderTarget;
00678             pD3DDevice9->GetRenderTarget(0, &lpRenderTarget);
00679             LPDIRECT3DTEXTURE9 lpWorkTexture = lpWorkTexture1;
00680 pass2:
00681             // Change the render target
00682             LPDIRECT3DSURFACE9 lpNewRenderTarget;
00683             lpWorkTexture->GetSurfaceLevel(0, &lpNewRenderTarget);
00684 
00685             if(FAILED(pD3DDevice9->SetRenderTarget(0, lpNewRenderTarget))) {
00686                 LOG_MSG("D3D:Failed to set render target");
00687                 return false;
00688             }
00689 
00690             SAFE_RELEASE(lpNewRenderTarget);
00691 
00692             uPasses = 0;
00693             if(FAILED(psEffect->Begin((lpWorkTexture==lpWorkTexture1) ?
00694                     (ScalingEffect::Preprocess1):(ScalingEffect::Preprocess2), &uPasses))) {
00695                 LOG_MSG("D3D:Failed to begin PS");
00696                 return false;
00697             }
00698 
00699             for(UINT uPass=0;uPass<uPasses;uPass++) {
00700                 hr=psEffect->BeginPass(uPass);
00701                 if(FAILED(hr)) {
00702                     LOG_MSG("D3D:Failed to begin pass %d", uPass);
00703                     return false;
00704                 }
00705 
00706                 // Render the vertex buffer contents
00707                 pD3DDevice9->DrawPrimitive(D3DPT_TRIANGLESTRIP, 4, 2);
00708                 psEffect->EndPass();
00709             }
00710 
00711             if(FAILED(psEffect->End())) {
00712                 LOG_MSG("D3D:Failed to end effect");
00713                 return false;
00714             }
00715 
00716 #if DEBUG_PS
00717             // Save rendertarget data
00718             LPDIRECT3DSURFACE9 lpTexRenderTarget;
00719             lpWorkTexture->GetSurfaceLevel(0, &lpTexRenderTarget);
00720             lpDebugTexture->GetSurfaceLevel(0, &lpNewRenderTarget);
00721             if(FAILED(hr=pD3DDevice9->GetRenderTargetData(lpTexRenderTarget, lpNewRenderTarget))) {
00722                 LOG_MSG("D3D:Unable to get render target data: 0x%x", hr);
00723                 SAFE_RELEASE(lpTexRenderTarget);
00724                 SAFE_RELEASE(lpNewRenderTarget);
00725             } else {
00726                 SAFE_RELEASE(lpTexRenderTarget);
00727                 SAFE_RELEASE(lpNewRenderTarget);
00728                 LOG_MSG("D3D:Got render target data, writing debug file (%dx%d)", dwTexWidth, dwTexHeight);
00729                 if(lpDebugTexture->LockRect(0, &d3dlr, NULL, D3DLOCK_READONLY) == D3D_OK) {
00730                     FILE * debug = fopen(((lpWorkTexture==lpWorkTexture1)?"pass1.raw":"pass2.raw"), "wb");
00731                     if(debug == NULL) {
00732                         LOG_MSG("D3D:Unable to create file!");
00733                     } else {
00734                         for(int i = 0; i < dwTexHeight; i++) {
00735                             Bit8u * ptr = (Bit8u*)d3dlr.pBits;
00736                             for(int j = 0; j < dwTexWidth; j++) {
00737                                 fwrite(ptr, 3, sizeof(char), debug);
00738                                 ptr += 4;
00739                             }
00740                             d3dlr.pBits = (Bit8u*)d3dlr.pBits + d3dlr.Pitch;
00741                         }
00742                         fclose(debug);
00743                     }
00744                     lpDebugTexture->UnlockRect(0);
00745                 }
00746                 d3dlr.pBits = NULL;
00747             }
00748 #endif
00749 
00750             if((psEffect->hasPreprocess2()) && (lpWorkTexture == lpWorkTexture1)) {
00751                 lpWorkTexture = lpWorkTexture2;
00752                 goto pass2;
00753             }
00754 
00755             // Reset the rendertarget
00756             pD3DDevice9->SetRenderTarget(0, lpRenderTarget);
00757             SAFE_RELEASE(lpRenderTarget);
00758 
00759             // Set matrices for final pass
00760             if(FAILED(psEffect->SetMatrices(m_matProj, m_matView, m_matWorld))) {
00761                 LOG_MSG("D3D:Set matrices failed.");
00762                 return false;
00763             }
00764         }
00765 
00766         uPasses = 0;
00767 
00768         if(FAILED(psEffect->Begin(ScalingEffect::Combine, &uPasses))) {
00769             LOG_MSG("D3D:Failed to begin PS");
00770             return false;
00771         }
00772 
00773         for(UINT uPass=0;uPass<uPasses;uPass++) {
00774             hr=psEffect->BeginPass(uPass);
00775             if(FAILED(hr)) {
00776                 LOG_MSG("D3D:Failed to begin pass %d", uPass);
00777                 return false;
00778             }
00779 
00780             // Render the vertex buffer contents
00781             pD3DDevice9->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
00782             psEffect->EndPass();
00783         }
00784 
00785         if(FAILED(psEffect->End())) {
00786             LOG_MSG("D3D:Failed to end effect");
00787             return false;
00788         }
00789 
00790     } else
00791 #endif
00792     {
00793         // Normal path
00794         pD3DDevice9->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
00795     }
00796 
00797     // end rendering
00798     pD3DDevice9->EndScene();
00799 
00800     if(GCC_UNLIKELY(hr=pD3DDevice9->Present(NULL, NULL, NULL, NULL)) != D3D_OK) {
00801         switch(hr) {
00802             case D3DERR_DEVICELOST:
00803         LOG_MSG("D3D:Driver device lost");
00804         if (!deviceLost) {
00805             deviceLost = true;
00806             void RENDER_CallBack(GFX_CallBackFunctions_t f);
00807             RENDER_CallBack(GFX_CallBackRedraw);
00808         }
00809                 return false;
00810                 break;
00811             case D3DERR_DRIVERINTERNALERROR:
00812                 LOG_MSG("D3D:Driver internal error");
00813                 return false;
00814                 break;
00815             case D3DERR_INVALIDCALL:
00816             default:
00817                 LOG_MSG("D3D:Invalid call");
00818                 return false;
00819                 break;
00820         }
00821     }
00822 
00823     return true;
00824 }
00825 
00826 HRESULT CDirect3D::InvalidateDeviceObjects(void)
00827 {
00828     SAFE_RELEASE(lpTexture);
00829 #if C_D3DSHADERS
00830     SAFE_RELEASE(lpWorkTexture1);
00831     SAFE_RELEASE(lpWorkTexture2);
00832     SAFE_RELEASE(lpHq2xLookupTexture);
00833 #if LOG_D3D
00834     SAFE_RELEASE(lpDebugTexture);
00835 #endif
00836 
00837     // Delete pixel shader
00838     if(psEffect) {
00839         delete psEffect;
00840         psEffect = NULL;
00841     }
00842 #endif
00843 
00844     // clear stream source
00845     if(pD3DDevice9)
00846         pD3DDevice9->SetStreamSource(0, NULL, 0, 0);
00847 
00848     SAFE_RELEASE(vertexBuffer);
00849 
00850     return S_OK;
00851 }
00852 
00853 HRESULT CDirect3D::RestoreDeviceObjects(void)
00854 {
00855     unsigned int vertexbuffersize = sizeof(TLVERTEX) * 4;
00856     preProcess = false;
00857 
00858 #if C_D3DSHADERS
00859     if(psActive) {
00860         // Set up pixel shaders
00861         LoadPixelShader();
00862 
00863         if(psEffect && psEffect->hasPreprocess()) {
00864 #if LOG_D3D
00865             LOG_MSG("D3D:Pixel shader preprocess active");
00866 #endif
00867             preProcess = true;
00868             vertexbuffersize = sizeof(TLVERTEX) * 8;
00869         }
00870     }
00871 #endif
00872     // Initialize vertex
00873     pD3DDevice9->SetFVF(D3DFVF_TLVERTEX);
00874 
00875     // Create vertex buffer
00876     if(FAILED(pD3DDevice9->CreateVertexBuffer(vertexbuffersize, D3DUSAGE_WRITEONLY,
00877         D3DFVF_TLVERTEX, D3DPOOL_MANAGED, &vertexBuffer, NULL))) {
00878         LOG_MSG("D3D:Failed to create Vertex Buffer");
00879         return E_FAIL;
00880     }
00881 
00882     // Lock vertex buffer and set vertices
00883     CreateVertex();
00884 
00885     pD3DDevice9->SetStreamSource(0, vertexBuffer, 0, sizeof(TLVERTEX));
00886 
00887     // Turn off culling
00888     pD3DDevice9->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
00889 
00890     // Turn off D3D lighting
00891     pD3DDevice9->SetRenderState(D3DRS_LIGHTING, false);
00892 
00893     // Turn off the zbuffer
00894     pD3DDevice9->SetRenderState(D3DRS_ZENABLE, false);
00895 
00896     CreateDisplayTexture();
00897     SetupSceneScaled();
00898 
00899     if(!psActive) {
00900         pD3DDevice9->SetTexture(0, lpTexture);
00901 
00902         // Disable Shaders
00903         pD3DDevice9->SetVertexShader(0);
00904         pD3DDevice9->SetPixelShader(0);
00905 
00906         pD3DDevice9->SetTransform(D3DTS_PROJECTION, &m_matProj);
00907         pD3DDevice9->SetTransform(D3DTS_VIEW, &m_matView);
00908         pD3DDevice9->SetTransform(D3DTS_WORLD, &m_matWorld);
00909     }
00910 #if C_D3DSHADERS
00911     else if(psEffect) {
00912         if(preProcess) {
00913             // Projection is (0,0,0) -> (1,1,1)
00914             D3DXMatrixOrthoOffCenterLH(&m_matPreProj, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
00915 
00916             // Align texels with pixels
00917             D3DXMatrixTranslation(&m_matPreView, -0.5f/dwTexWidth, 0.5f/dwTexHeight, 0.0f);
00918 
00919             // Identity for world
00920             D3DXMatrixIdentity(&m_matPreWorld);
00921 
00922         } else if(FAILED(psEffect->SetMatrices(m_matProj, m_matView, m_matWorld))) {
00923             LOG_MSG("D3D:Set matrices failed.");
00924             InvalidateDeviceObjects();
00925             return E_FAIL;
00926         }
00927     }
00928 #endif
00929 
00930     return S_OK;
00931 }
00932 
00933 extern void RENDER_SetForceUpdate(bool);
00934 HRESULT CDirect3D::LoadPixelShader(const char * shader, double scalex, double scaley, bool forced)
00935 {
00936     if(!psEnabled) {
00937         RENDER_SetForceUpdate(false);
00938         psActive = false;
00939         return E_FAIL;
00940     }
00941 
00942 #if C_D3DSHADERS
00943     // See if the shader is already running
00944     if((!psEffect) || (strcmp(pshader+8, shader))) {
00945 
00946         // This is called in main thread to test if pixelshader needs to be changed
00947         if((scalex == 0) && (scaley == 0)) return S_OK;
00948 
00949         RENDER_SetForceUpdate(false);
00950         safe_strncpy(pshader+8, shader, 22); pshader[29] = '\0';
00951 #if D3D_THREAD
00952         Wait(false);
00953         thread_command = D3D_LOADPS;
00954         LeaveCriticalSection(&cs);
00955         SDL_SemPost(thread_sem);
00956 
00957         if(FAILED(Wait(true))) return E_FAIL;
00958 #else
00959         if(FAILED(LoadPixelShader())) return E_FAIL;
00960 #endif
00961 
00962     } else {
00963 #if LOG_D3D
00964         LOG_MSG("D3D:Shader %s is already loaded", shader);
00965 #endif
00966         return E_FAIL;
00967     }
00968 
00969     if (psEffect) {
00970 #if LOG_D3D
00971         LOG_MSG("D3D:Shader scale: %.2f", psEffect->getScale());
00972 #endif
00973         // Compare optimal scaling factor
00974         bool dblgfx=((scalex < scaley ? scalex : scaley) >= psEffect->getScale());
00975 
00976         if(dblgfx || forced) {
00977             LOG_MSG("D3D:Pixel shader %s active", shader);
00978             RENDER_SetForceUpdate(psEffect->getForceUpdate());
00979             psActive = true;
00980             return S_OK;
00981         } else {
00982             LOG_MSG("D3D:Pixel shader not needed");
00983             psActive = false;
00984             return E_FAIL;
00985         }
00986     } else psActive = false;
00987 
00988 #endif // C_D3DSHADERS
00989 
00990     return S_OK;
00991 }
00992 
00993 HRESULT CDirect3D::LoadPixelShader(void)
00994 {
00995 #if C_D3DSHADERS
00996     // Load new shader
00997     if(psEffect) {
00998         delete psEffect;
00999         psEffect = NULL;
01000     }
01001 
01002     if(!strcmp(pshader+8, "none")) {
01003         // Returns E_FAIL so that further shader processing is disabled
01004         psActive = false;
01005         return E_FAIL;
01006     }
01007 
01008     psEffect = new ScalingEffect(pD3DDevice9);
01009     if (psEffect == NULL) {
01010         LOG_MSG("D3D:Error creating shader object!");
01011         psActive = false;
01012         return E_FAIL;
01013     }
01014 
01015 #if LOG_D3D
01016     LOG_MSG("D3D:Loading pixel shader from %s", pshader);
01017 #endif
01018 
01019     psEffect->setinputDim((float)dwWidth, (float)dwHeight);
01020     if(FAILED(psEffect->LoadEffect(pshader)) || FAILED(psEffect->Validate())) {
01021         /*LOG_MSG("D3D:Pixel shader error:");
01022 
01023         // The resulting string can exceed 512 char LOG_MSG limit, split on newlines
01024         std::stringstream ss(psEffect->getErrors());
01025         std::string line;
01026         while(std::getline(ss, line)) {
01027             LOG_MSG(" %s", line.c_str());
01028         }
01029 
01030         LOG_MSG("D3D:Pixel shader output disabled");*/
01031         delete psEffect;
01032         psEffect = NULL;
01033         psActive = false;
01034         pshader[8] = '\0';
01035         return E_FAIL;
01036     }
01037 
01038 #endif // C_D3DSHADERS
01039 
01040     return S_OK;
01041 }
01042 
01043 HRESULT CDirect3D::Resize3DEnvironment(Bitu window_width, Bitu window_height, Bitu x, Bitu y, Bitu width, Bitu height, Bitu rwidth, Bitu rheight, bool fullscreen)
01044 {
01045 #if LOG_D3D
01046     LOG_MSG("D3D:Resizing D3D screen...");
01047 #endif
01048 
01049 #if D3D_THREAD
01050     Wait(false);
01051 #endif
01052 
01053         (void)fullscreen; // FIXME: This should be stored and used!
01054 
01055     // set the presentation parameters
01056         d3dpp.BackBufferWidth = (UINT)window_width;
01057         d3dpp.BackBufferHeight = (UINT)window_height;
01058 
01059     dwScaledWidth = (DWORD)width;
01060     dwScaledHeight = (DWORD)height;
01061 
01062     dwX = (DWORD)x;
01063     dwY = (DWORD)y;
01064 
01065     dwWidth = (DWORD)rwidth;
01066     dwHeight = (DWORD)rheight;
01067 
01068 #if LOG_D3D
01069     LOG_MSG("D3D:Resolution set to %dx%d%s", d3dpp.BackBufferWidth, d3dpp.BackBufferHeight, fullscreen ? ", fullscreen" : "");
01070 #endif
01071 
01072     HRESULT hr = Resize3DEnvironment();
01073 
01074 #if D3D_THREAD
01075     LeaveCriticalSection(&cs);
01076 #endif
01077     return hr;
01078 }
01079 
01080 HRESULT CDirect3D::Resize3DEnvironment(void)
01081 {
01082     // Release all vidmem objects
01083     HRESULT hr;
01084 
01085 #if LOG_D3D
01086     LOG_MSG("D3D:Resize3DEnvironment() called");
01087 #endif
01088 
01089     if(FAILED(hr=InvalidateDeviceObjects())) {
01090         LOG_MSG("D3D:Failed to invalidate objects");
01091         return hr;
01092     }
01093 
01094     // Reset the device
01095 reset_device:
01096     Bitu i = 20;
01097     // Don't bother too much, when device is already lost
01098     if(deviceLost) i = 5;
01099     deviceLost = false;
01100 
01101     if(FAILED(hr=pD3DDevice9->Reset(&d3dpp))) {
01102         if(hr==D3DERR_DEVICELOST) {
01103             while((hr=pD3DDevice9->TestCooperativeLevel()) != D3DERR_DEVICENOTRESET) {
01104                 if(hr==D3DERR_DRIVERINTERNALERROR) {
01105                     LOG_MSG("D3D:Driver internal error when resetting device!");
01106                     return hr;
01107                 }
01108 #if LOG_D3D
01109                 LOG_MSG("D3D:Wait for D3D device to become available...");
01110 #endif
01111                 Sleep(50); i--;
01112                 if(i == 0) {
01113                     deviceLost = true;
01114 #if LOG_D3D
01115                     LOG_MSG("D3D:Giving up on D3D wait...");
01116 #endif
01117                     // Return ok or dosbox will quit, we'll try to reset the device later
01118                     return S_OK;
01119                 }
01120             }
01121 #if LOG_D3D
01122             LOG_MSG("D3D:Performing another reset...");
01123 #endif
01124             goto reset_device;
01125         } else {
01126             LOG_MSG("D3D:Failed to reset device!");
01127             return hr;
01128         }
01129     }
01130 
01131         backbuffer_clear_countdown = 2;
01132         if (d3dpp.BackBufferCount == 2) backbuffer_clear_countdown++;
01133 
01134 #if 0
01135     // Clear all backbuffers
01136     pD3DDevice9->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
01137 
01138     pD3DDevice9->Present(NULL, NULL, NULL, NULL);
01139     pD3DDevice9->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
01140 
01141     if(d3dpp.BackBufferCount == 2) {
01142         pD3DDevice9->Present(NULL, NULL, NULL, NULL);
01143         pD3DDevice9->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
01144     }
01145 #endif
01146 
01147 #if LOG_D3D
01148     LOG_MSG("D3D:Mode: %dx%d (x %.2fx%.2f) --> scaled size: %dx%d", dwWidth, dwHeight,
01149                     (float)dwScaledWidth/dwWidth, (float)dwScaledHeight/dwHeight, dwScaledWidth, dwScaledHeight);
01150 #endif
01151 
01152     return RestoreDeviceObjects();
01153 }
01154 
01155 HRESULT CDirect3D::CreateDisplayTexture(void)
01156 {
01157     SAFE_RELEASE(lpTexture);
01158 
01159     if((dwTexWidth == 0) || (dwTexHeight == 0))
01160         return S_OK;
01161 
01162 #if LOG_D3D
01163     LOG_MSG("D3D:Creating Textures: %dx%d", dwTexWidth, dwTexHeight);
01164 #endif
01165 
01166     HRESULT hr;
01167 
01168     D3DFORMAT d3dtexformat;
01169 
01170     if(bpp16)
01171         d3dtexformat = D3DFMT_R5G6B5;
01172     else
01173         d3dtexformat = D3DFMT_X8R8G8B8;
01174 
01175     if(!dynamic) {
01176         hr = pD3DDevice9->CreateTexture(dwTexWidth, dwTexHeight, 1, 0, d3dtexformat,
01177                                 D3DPOOL_MANAGED, &lpTexture, NULL);
01178 
01179     } else {
01180         hr = pD3DDevice9->CreateTexture(dwTexWidth, dwTexHeight, 1, D3DUSAGE_DYNAMIC, d3dtexformat,
01181                             D3DPOOL_DEFAULT, &lpTexture, NULL);
01182     }
01183 
01184     if(FAILED(hr)) {
01185         LOG_MSG("D3D:Failed to create %stexture: 0x%lx", (dynamic ? "dynamic " : ""), (unsigned long)hr);
01186 
01187         switch(hr) {
01188         case D3DERR_INVALIDCALL:
01189             LOG_MSG("D3D:Invalid call");
01190             break;
01191         case D3DERR_OUTOFVIDEOMEMORY:
01192             LOG_MSG("D3D:D3DERR_OUTOFVIDEOMEMORY");
01193             break;
01194         case E_OUTOFMEMORY:
01195             LOG_MSG("D3D:E_OUTOFMEMORY");
01196             break;
01197         default:
01198             LOG_MSG("D3D:E_UNKNOWN");
01199         }
01200         return E_FAIL;
01201     }
01202 
01203     // Initialize texture to black
01204     if(LockTexture() == S_OK) {
01205         Bit8u * pixels = (Bit8u *)d3dlr.pBits;
01206 
01207         for(Bitu lines = dwTexHeight; lines; lines--) {
01208             memset(pixels, 0, (dwTexWidth<<2)>>(bpp16?1:0));
01209             pixels += d3dlr.Pitch;
01210         }
01211 
01212         lpTexture->UnlockRect(0);
01213     }
01214 
01215     d3dlr.pBits = NULL;
01216     RECT rect;
01217 
01218     rect.left = rect.top = 0;
01219     rect.right = dwTexWidth; rect.bottom = dwTexHeight;
01220     lpTexture->AddDirtyRect(&rect);
01221 
01222 #if C_D3DSHADERS
01223     if(psActive) {
01224         // Working textures for pixel shader
01225         if(FAILED(hr=pD3DDevice9->CreateTexture(dwTexWidth, dwTexHeight, 1, D3DUSAGE_RENDERTARGET,
01226                             D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &lpWorkTexture1, NULL))) {
01227             LOG_MSG("D3D:Failed to create working texture: 0x%lx", (unsigned long)hr);
01228 
01229             switch(hr) {
01230             case D3DERR_INVALIDCALL:
01231                 LOG_MSG("D3D:Invalid call");
01232                 break;
01233             case D3DERR_OUTOFVIDEOMEMORY:
01234                 LOG_MSG("D3D:D3DERR_OUTOFVIDEOMEMORY");
01235                 break;
01236             case E_OUTOFMEMORY:
01237                 LOG_MSG("D3D:E_OUTOFMEMORY");
01238                 break;
01239             default:
01240                 LOG_MSG("D3D:E_UNKNOWN");
01241             }
01242             return E_FAIL;
01243         }
01244 
01245         if(FAILED(hr=pD3DDevice9->CreateTexture(dwTexWidth, dwTexHeight, 1, D3DUSAGE_RENDERTARGET,
01246                             D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &lpWorkTexture2, NULL))) {
01247             LOG_MSG("D3D:Failed to create working texture: 0x%lx", (unsigned long)hr);
01248 
01249             switch(hr) {
01250             case D3DERR_INVALIDCALL:
01251                 LOG_MSG("D3D:Invalid call");
01252                 break;
01253             case D3DERR_OUTOFVIDEOMEMORY:
01254                 LOG_MSG("D3D:D3DERR_OUTOFVIDEOMEMORY");
01255                 break;
01256             case E_OUTOFMEMORY:
01257                 LOG_MSG("D3D:E_OUTOFMEMORY");
01258                 break;
01259             default:
01260                 LOG_MSG("D3D:E_UNKNOWN");
01261             }
01262             return E_FAIL;
01263         }
01264 
01265         if(FAILED(hr=pD3DDevice9->CreateVolumeTexture(256, 16, 256, 1, 0, D3DFMT_A8R8G8B8,
01266                             D3DPOOL_MANAGED, &lpHq2xLookupTexture, NULL))) {
01267             LOG_MSG("D3D:Failed to create volume texture: 0x%lx", (unsigned long)hr);
01268 
01269             switch(hr) {
01270             case D3DERR_INVALIDCALL:
01271                 LOG_MSG("D3D:Invalid call");
01272                 break;
01273             case D3DERR_OUTOFVIDEOMEMORY:
01274                 LOG_MSG("D3D:D3DERR_OUTOFVIDEOMEMORY");
01275                 break;
01276             case E_OUTOFMEMORY:
01277                 LOG_MSG("D3D:E_OUTOFMEMORY");
01278                 break;
01279             default:
01280                 LOG_MSG("D3D:E_UNKNOWN");
01281             }
01282             return E_FAIL;
01283         }
01284 
01285         // build lookup table
01286         D3DLOCKED_BOX lockedBox;
01287 
01288         if(FAILED(hr = lpHq2xLookupTexture->LockBox(0, &lockedBox, NULL, 0))) {
01289             LOG_MSG("D3D:Failed to lock box of volume texture: 0x%lx", (unsigned long)hr);
01290 
01291             switch(hr) {
01292                 case D3DERR_INVALIDCALL:
01293                     LOG_MSG("D3D:Invalid call");
01294                     break;
01295                 default:
01296                     LOG_MSG("D3D:E_UNKNOWN");
01297             }
01298             return E_FAIL;
01299         }
01300 
01301         BuildHq2xLookupTexture(dwScaledWidth, dwScaledHeight, dwWidth, dwHeight, (Bit8u *)lockedBox.pBits);
01302 
01303 #if LOG_D3D
01304         // Debug: Write look-up texture to file
01305         int fd = _open("hq2xLookupTexture.pam",_O_WRONLY|_O_CREAT|_O_TRUNC|_O_BINARY,0666);
01306         unsigned char table[4096] = HQ2X_D3D_TABLE_DATA;
01307         sprintf((char *)table,"P7\nWIDTH %i\nHEIGHT %i\nMAXVAL 255\nDEPTH 4\nTUPLTYPE RGB_ALPHA\nENDHDR\n",16*HQ2X_RESOLUTION,4096/16*HQ2X_RESOLUTION);
01308         write(fd,table,strlen((char *)table));
01309         write(fd, lockedBox.pBits, HQ2X_RESOLUTION * HQ2X_RESOLUTION * 4096 * 4);
01310         _close(fd);
01311 #endif
01312 
01313         if(FAILED(hr = lpHq2xLookupTexture->UnlockBox(0))) {
01314             LOG_MSG("D3D:Failed to unlock box of volume texture: 0x%lx", (unsigned long)hr);
01315 
01316             switch(hr) {
01317                 case D3DERR_INVALIDCALL:
01318                     LOG_MSG("D3D:Invalid call");
01319                     break;
01320                 default:
01321                     LOG_MSG("D3D:E_UNKNOWN");
01322             }
01323             return E_FAIL;
01324         }
01325 
01326 #if LOG_D3D
01327         // Debug texture for pixel shader
01328         if(FAILED(hr=pD3DDevice9->CreateTexture(dwTexWidth, dwTexHeight, 1, 0, D3DFMT_A8R8G8B8,
01329                             D3DPOOL_SYSTEMMEM, &lpDebugTexture, NULL))) {
01330             LOG_MSG("D3D:Failed to create debug texture: 0x%x", hr);
01331 
01332             switch(hr) {
01333             case D3DERR_INVALIDCALL:
01334                 LOG_MSG("D3D:Invalid call");
01335                 break;
01336             case D3DERR_OUTOFVIDEOMEMORY:
01337                 LOG_MSG("D3D:D3DERR_OUTOFVIDEOMEMORY");
01338                 break;
01339             case E_OUTOFMEMORY:
01340                 LOG_MSG("D3D:E_OUTOFMEMORY");
01341                 break;
01342             default:
01343                 LOG_MSG("D3D:E_UNKNOWN");
01344             }
01345             return E_FAIL;
01346         }
01347 #endif  // LOG_D3D
01348 
01349         // Set textures
01350         if(FAILED(psEffect->SetTextures(lpTexture, lpWorkTexture1, lpWorkTexture2, lpHq2xLookupTexture))) {
01351             LOG_MSG("D3D:Failed to set PS textures");
01352             return false;
01353         }
01354 
01355     }
01356 #endif  // C_D3DSHADERS
01357 
01358     return S_OK;
01359 }
01360 
01361 HRESULT CDirect3D::CreateVertex(void)
01362 {
01363     TLVERTEX* vertices;
01364 
01365     // Texture coordinates
01366     float sizex=1.0f, sizey=1.0f;
01367 
01368     // If texture is larger than DOSBox FB
01369     if(dwTexWidth != dwWidth)
01370         sizex = (float)dwWidth/dwTexWidth;
01371     if(dwTexHeight != dwHeight)
01372         sizey = (float)dwHeight/dwTexHeight;
01373 
01374 #if LOG_D3D
01375     LOG_MSG("D3D:Quad size: %dx%d, tex. coord.: 0,0-->%.2f,%.2f", dwWidth, dwHeight, sizex, sizey);
01376 #endif
01377 
01378     // Lock the vertex buffer
01379     vertexBuffer->Lock(0, 0, (void**)&vertices, 0);
01380 
01381     //Setup vertices
01382     vertices[0].position = D3DXVECTOR3( (float)dwX,                                       (float)dwY,                                    0.0f );
01383     vertices[0].diffuse  = 0xFFFFFFFF;
01384     vertices[0].texcoord = D3DXVECTOR2( 0.0f,                                             0.0f );
01385 
01386     vertices[1].position = D3DXVECTOR3( (float)dwX,                                       (float)(dwY + dwScaledHeight), 0.0f );
01387     vertices[1].diffuse  = 0xFFFFFFFF;
01388     vertices[1].texcoord = D3DXVECTOR2( 0.0f,                                             (float)sizey );
01389     
01390     vertices[2].position = D3DXVECTOR3( (float)(dwX + dwScaledWidth), (float)dwY,                                    0.0f );
01391     vertices[2].diffuse  = 0xFFFFFFFF;
01392     vertices[2].texcoord = D3DXVECTOR2( (float)sizex,                             0.0f );
01393     
01394     vertices[3].position = D3DXVECTOR3( (float)(dwX + dwScaledWidth), (float)(dwY + dwScaledHeight), 0.0f );
01395     vertices[3].diffuse  = 0xFFFFFFFF;
01396     vertices[3].texcoord = D3DXVECTOR2( (float)sizex,                             (float)sizey );
01397 
01398     // Additional vertices required for some PS effects
01399     // FIXME: Recent changes may have BROKEN pixel shader support here!!!!!
01400     if(preProcess) {
01401         vertices[4].position = D3DXVECTOR3( 0.0f, 0.0f, 0.0f);
01402         vertices[4].diffuse  = 0xFFFFFF00;
01403         vertices[4].texcoord = D3DXVECTOR2( 0.0f, 1.0f);
01404         vertices[5].position = D3DXVECTOR3( 0.0f, 1.0f, 0.0f);
01405         vertices[5].diffuse  = 0xFFFFFF00;
01406         vertices[5].texcoord = D3DXVECTOR2( 0.0f, 0.0f);
01407         vertices[6].position = D3DXVECTOR3( 1.0f, 0.0f, 0.0f);
01408         vertices[6].diffuse  = 0xFFFFFF00;
01409         vertices[6].texcoord = D3DXVECTOR2( 1.0f, 1.0f);
01410         vertices[7].position = D3DXVECTOR3( 1.0f, 1.0f, 0.0f);
01411         vertices[7].diffuse  = 0xFFFFFF00;
01412         vertices[7].texcoord = D3DXVECTOR2( 1.0f, 0.0f);
01413     }
01414 
01415     // Unlock the vertex buffer
01416     vertexBuffer->Unlock();
01417 
01418     return S_OK;
01419 }
01420 
01421 void CDirect3D::SetupSceneScaled(void)
01422 {
01423         // TODO: It would probably be nicer to offer an option here whether the user wants
01424         //               point sampling (D3DTEXF_POINT) or linear interpolation (D3DTEXF_LINEAR) when scaling up/down.
01425     pD3DDevice9->SetTextureStageState(0, D3DTSS_COLOROP,   D3DTOP_MODULATE);
01426     pD3DDevice9->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
01427     pD3DDevice9->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
01428     pD3DDevice9->SetTextureStageState(0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE);
01429     pD3DDevice9->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
01430     pD3DDevice9->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
01431     pD3DDevice9->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
01432     pD3DDevice9->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
01433     pD3DDevice9->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
01434 
01435     D3DVIEWPORT9 Viewport;
01436     pD3DDevice9->GetViewport(&Viewport);
01437 
01438     // Projection is screenspace coords
01439     D3DXMatrixOrthoOffCenterLH(&m_matProj, 0.0f, (float)Viewport.Width, 0.0f, (float)Viewport.Height, 0.0f, 1.0f);
01440     {
01441             D3DXMATRIX x;
01442             D3DXMatrixScaling(&x, 1.0f, -1.0f, 1.0f);
01443             m_matProj *= x;
01444     }
01445 
01446     // View matrix with -0.5f offset to avoid a fuzzy picture
01447     D3DXMatrixTranslation(&m_matView, -0.5f, -0.5f, 0.0f);
01448 
01449     // TODO: Re-implement 5:4 monitor autofit
01450 
01451 #if LOG_D3D
01452     LOG_MSG("D3D:Scaled resolution: %.1fx%.1f, factor: %dx%d", sizex, sizey, x, y);
01453 #endif
01454 
01455     D3DXMatrixScaling(&m_matWorld, 1.0, 1.0, 1.0f);
01456 }
01457 
01458 #if !(C_D3DSHADERS)
01459 
01460 D3DXMATRIX* CDirect3D::MatrixOrthoOffCenterLH(D3DXMATRIX *pOut, float l, float r, float b, float t, float zn, float zf)
01461 {
01462     pOut->_11=2.0f/r; pOut->_12=0.0f;   pOut->_13=0.0f;  pOut->_14=0.0f;
01463     pOut->_21=0.0f;   pOut->_22=2.0f/t; pOut->_23=0.0f;  pOut->_24=0.0f;
01464     pOut->_31=0.0f;   pOut->_32=0.0f;   pOut->_33=1.0f;  pOut->_34=0.0f;
01465     pOut->_41=-1.0f;  pOut->_42=-1.0f;  pOut->_43=0.0f;  pOut->_44=1.0f;
01466 
01467     return pOut;
01468 }
01469 
01470 D3DXMATRIX* CDirect3D::MatrixScaling(D3DXMATRIX *pOut, float sx, float sy, float sz)
01471 {
01472     pOut->_11=sx;     pOut->_12=0.0f;   pOut->_13=0.0f;  pOut->_14=0.0f;
01473     pOut->_21=0.0f;   pOut->_22=sy;     pOut->_23=0.0f;  pOut->_24=0.0f;
01474     pOut->_31=0.0f;   pOut->_32=0.0f;   pOut->_33=sz;    pOut->_34=0.0f;
01475     pOut->_41=0.0f;   pOut->_42=0.0f;   pOut->_43=0.0f;  pOut->_44=1.0f;
01476 
01477     return pOut;
01478 }
01479 
01480 D3DXMATRIX* CDirect3D::MatrixTranslation(D3DXMATRIX *pOut, float tx, float ty, float tz)
01481 {
01482     pOut->_11=1.0f;   pOut->_12=0.0f;   pOut->_13=0.0f;  pOut->_14=0.0f;
01483     pOut->_21=0.0f;   pOut->_22=1.0f;   pOut->_23=0.0f;  pOut->_24=0.0f;
01484     pOut->_31=0.0f;   pOut->_32=0.0f;   pOut->_33=1.0f;  pOut->_34=0.0f;
01485     pOut->_41=tx;     pOut->_42=ty;     pOut->_43=tz;    pOut->_44=1.0f;
01486 
01487     return pOut;
01488 }
01489 
01490 #endif  // !(C_D3DSHADERS)
01491 
01492 #endif  // (HAVE_D3D9_H)