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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 = y;
00285                 rect.bottom = 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                 // This will be handled when SDL catches alt-tab and resets GFX
00804                 return true;
00805                 break;
00806             case D3DERR_DRIVERINTERNALERROR:
00807                 LOG_MSG("D3D:Driver internal error");
00808                 return false;
00809                 break;
00810             case D3DERR_INVALIDCALL:
00811             default:
00812                 LOG_MSG("D3D:Invalid call");
00813                 return false;
00814                 break;
00815         }
00816     }
00817 
00818     return true;
00819 }
00820 
00821 HRESULT CDirect3D::InvalidateDeviceObjects(void)
00822 {
00823     SAFE_RELEASE(lpTexture);
00824 #if C_D3DSHADERS
00825     SAFE_RELEASE(lpWorkTexture1);
00826     SAFE_RELEASE(lpWorkTexture2);
00827     SAFE_RELEASE(lpHq2xLookupTexture);
00828 #if LOG_D3D
00829     SAFE_RELEASE(lpDebugTexture);
00830 #endif
00831 
00832     // Delete pixel shader
00833     if(psEffect) {
00834         delete psEffect;
00835         psEffect = NULL;
00836     }
00837 #endif
00838 
00839     // clear stream source
00840     if(pD3DDevice9)
00841         pD3DDevice9->SetStreamSource(0, NULL, 0, 0);
00842 
00843     SAFE_RELEASE(vertexBuffer);
00844 
00845     return S_OK;
00846 }
00847 
00848 HRESULT CDirect3D::RestoreDeviceObjects(void)
00849 {
00850     unsigned int vertexbuffersize = sizeof(TLVERTEX) * 4;
00851     preProcess = false;
00852 
00853 #if C_D3DSHADERS
00854     if(psActive) {
00855         // Set up pixel shaders
00856         LoadPixelShader();
00857 
00858         if(psEffect && psEffect->hasPreprocess()) {
00859 #if LOG_D3D
00860             LOG_MSG("D3D:Pixel shader preprocess active");
00861 #endif
00862             preProcess = true;
00863             vertexbuffersize = sizeof(TLVERTEX) * 8;
00864         }
00865     }
00866 #endif
00867     // Initialize vertex
00868     pD3DDevice9->SetFVF(D3DFVF_TLVERTEX);
00869 
00870     // Create vertex buffer
00871     if(FAILED(pD3DDevice9->CreateVertexBuffer(vertexbuffersize, D3DUSAGE_WRITEONLY,
00872         D3DFVF_TLVERTEX, D3DPOOL_MANAGED, &vertexBuffer, NULL))) {
00873         LOG_MSG("D3D:Failed to create Vertex Buffer");
00874         return E_FAIL;
00875     }
00876 
00877     // Lock vertex buffer and set vertices
00878     CreateVertex();
00879 
00880     pD3DDevice9->SetStreamSource(0, vertexBuffer, 0, sizeof(TLVERTEX));
00881 
00882     // Turn off culling
00883     pD3DDevice9->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
00884 
00885     // Turn off D3D lighting
00886     pD3DDevice9->SetRenderState(D3DRS_LIGHTING, false);
00887 
00888     // Turn off the zbuffer
00889     pD3DDevice9->SetRenderState(D3DRS_ZENABLE, false);
00890 
00891     CreateDisplayTexture();
00892     SetupSceneScaled();
00893 
00894     if(!psActive) {
00895         pD3DDevice9->SetTexture(0, lpTexture);
00896 
00897         // Disable Shaders
00898         pD3DDevice9->SetVertexShader(0);
00899         pD3DDevice9->SetPixelShader(0);
00900 
00901         pD3DDevice9->SetTransform(D3DTS_PROJECTION, &m_matProj);
00902         pD3DDevice9->SetTransform(D3DTS_VIEW, &m_matView);
00903         pD3DDevice9->SetTransform(D3DTS_WORLD, &m_matWorld);
00904     }
00905 #if C_D3DSHADERS
00906     else if(psEffect) {
00907         if(preProcess) {
00908             // Projection is (0,0,0) -> (1,1,1)
00909             D3DXMatrixOrthoOffCenterLH(&m_matPreProj, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
00910 
00911             // Align texels with pixels
00912             D3DXMatrixTranslation(&m_matPreView, -0.5f/dwTexWidth, 0.5f/dwTexHeight, 0.0f);
00913 
00914             // Identity for world
00915             D3DXMatrixIdentity(&m_matPreWorld);
00916 
00917         } else if(FAILED(psEffect->SetMatrices(m_matProj, m_matView, m_matWorld))) {
00918             LOG_MSG("D3D:Set matrices failed.");
00919             InvalidateDeviceObjects();
00920             return E_FAIL;
00921         }
00922     }
00923 #endif
00924 
00925     return S_OK;
00926 }
00927 
00928 extern void RENDER_SetForceUpdate(bool);
00929 HRESULT CDirect3D::LoadPixelShader(const char * shader, double scalex, double scaley, bool forced)
00930 {
00931     if(!psEnabled) {
00932         RENDER_SetForceUpdate(false);
00933         psActive = false;
00934         return E_FAIL;
00935     }
00936 
00937 #if C_D3DSHADERS
00938     // See if the shader is already running
00939     if((!psEffect) || (strcmp(pshader+8, shader))) {
00940 
00941         // This is called in main thread to test if pixelshader needs to be changed
00942         if((scalex == 0) && (scaley == 0)) return S_OK;
00943 
00944         RENDER_SetForceUpdate(false);
00945         safe_strncpy(pshader+8, shader, 22); pshader[29] = '\0';
00946 #if D3D_THREAD
00947         Wait(false);
00948         thread_command = D3D_LOADPS;
00949         LeaveCriticalSection(&cs);
00950         SDL_SemPost(thread_sem);
00951 
00952         if(FAILED(Wait(true))) return E_FAIL;
00953 #else
00954         if(FAILED(LoadPixelShader())) return E_FAIL;
00955 #endif
00956 
00957     } else {
00958 #if LOG_D3D
00959         LOG_MSG("D3D:Shader %s is already loaded", shader);
00960 #endif
00961         return E_FAIL;
00962     }
00963 
00964     if (psEffect) {
00965 #if LOG_D3D
00966         LOG_MSG("D3D:Shader scale: %.2f", psEffect->getScale());
00967 #endif
00968         // Compare optimal scaling factor
00969         bool dblgfx=((scalex < scaley ? scalex : scaley) >= psEffect->getScale());
00970 
00971         if(dblgfx || forced) {
00972             LOG_MSG("D3D:Pixel shader %s active", shader);
00973             RENDER_SetForceUpdate(psEffect->getForceUpdate());
00974             psActive = true;
00975             return S_OK;
00976         } else {
00977             LOG_MSG("D3D:Pixel shader not needed");
00978             psActive = false;
00979             return E_FAIL;
00980         }
00981     } else psActive = false;
00982 
00983 #endif // C_D3DSHADERS
00984 
00985     return S_OK;
00986 }
00987 
00988 HRESULT CDirect3D::LoadPixelShader(void)
00989 {
00990 #if C_D3DSHADERS
00991     // Load new shader
00992     if(psEffect) {
00993         delete psEffect;
00994         psEffect = NULL;
00995     }
00996 
00997     if(!strcmp(pshader+8, "none")) {
00998         // Returns E_FAIL so that further shader processing is disabled
00999         psActive = false;
01000         return E_FAIL;
01001     }
01002 
01003     psEffect = new ScalingEffect(pD3DDevice9);
01004     if (psEffect == NULL) {
01005         LOG_MSG("D3D:Error creating shader object!");
01006         psActive = false;
01007         return E_FAIL;
01008     }
01009 
01010 #if LOG_D3D
01011     LOG_MSG("D3D:Loading pixel shader from %s", pshader);
01012 #endif
01013 
01014     psEffect->setinputDim((float)dwWidth, (float)dwHeight);
01015     if(FAILED(psEffect->LoadEffect(pshader)) || FAILED(psEffect->Validate())) {
01016         /*LOG_MSG("D3D:Pixel shader error:");
01017 
01018         // The resulting string can exceed 512 char LOG_MSG limit, split on newlines
01019         std::stringstream ss(psEffect->getErrors());
01020         std::string line;
01021         while(std::getline(ss, line)) {
01022             LOG_MSG(" %s", line.c_str());
01023         }
01024 
01025         LOG_MSG("D3D:Pixel shader output disabled");*/
01026         delete psEffect;
01027         psEffect = NULL;
01028         psActive = false;
01029         pshader[8] = '\0';
01030         return E_FAIL;
01031     }
01032 
01033 #endif // C_D3DSHADERS
01034 
01035     return S_OK;
01036 }
01037 
01038 HRESULT CDirect3D::Resize3DEnvironment(Bitu window_width, Bitu window_height, Bitu x, Bitu y, Bitu width, Bitu height, Bitu rwidth, Bitu rheight, bool fullscreen)
01039 {
01040 #if LOG_D3D
01041     LOG_MSG("D3D:Resizing D3D screen...");
01042 #endif
01043 
01044 #if D3D_THREAD
01045     Wait(false);
01046 #endif
01047 
01048         (void)fullscreen; // FIXME: This should be stored and used!
01049 
01050     // set the presentation parameters
01051         d3dpp.BackBufferWidth = window_width;
01052         d3dpp.BackBufferHeight = window_height;
01053 
01054     dwScaledWidth = width;
01055     dwScaledHeight = height;
01056 
01057     dwX = x;
01058     dwY = y;
01059 
01060     dwWidth = rwidth;
01061     dwHeight = rheight;
01062 
01063 #if LOG_D3D
01064     LOG_MSG("D3D:Resolution set to %dx%d%s", d3dpp.BackBufferWidth, d3dpp.BackBufferHeight, fullscreen ? ", fullscreen" : "");
01065 #endif
01066 
01067     HRESULT hr = Resize3DEnvironment();
01068 
01069 #if D3D_THREAD
01070     LeaveCriticalSection(&cs);
01071 #endif
01072     return hr;
01073 }
01074 
01075 HRESULT CDirect3D::Resize3DEnvironment(void)
01076 {
01077     // Release all vidmem objects
01078     HRESULT hr;
01079 
01080 #if LOG_D3D
01081     LOG_MSG("D3D:Resize3DEnvironment() called");
01082 #endif
01083 
01084     if(FAILED(hr=InvalidateDeviceObjects())) {
01085         LOG_MSG("D3D:Failed to invalidate objects");
01086         return hr;
01087     }
01088 
01089     // Reset the device
01090 reset_device:
01091     Bitu i = 20;
01092     // Don't bother too much, when device is already lost
01093     if(deviceLost) i = 5;
01094     deviceLost = false;
01095 
01096     if(FAILED(hr=pD3DDevice9->Reset(&d3dpp))) {
01097         if(hr==D3DERR_DEVICELOST) {
01098             while((hr=pD3DDevice9->TestCooperativeLevel()) != D3DERR_DEVICENOTRESET) {
01099                 if(hr==D3DERR_DRIVERINTERNALERROR) {
01100                     LOG_MSG("D3D:Driver internal error when resetting device!");
01101                     return hr;
01102                 }
01103 #if LOG_D3D
01104                 LOG_MSG("D3D:Wait for D3D device to become available...");
01105 #endif
01106                 Sleep(50); i--;
01107                 if(i == 0) {
01108                     deviceLost = true;
01109 #if LOG_D3D
01110                     LOG_MSG("D3D:Giving up on D3D wait...");
01111 #endif
01112                     // Return ok or dosbox will quit, we'll try to reset the device later
01113                     return S_OK;
01114                 }
01115             }
01116 #if LOG_D3D
01117             LOG_MSG("D3D:Performing another reset...");
01118 #endif
01119             goto reset_device;
01120         } else {
01121             LOG_MSG("D3D:Failed to reset device!");
01122             return hr;
01123         }
01124     }
01125 
01126         backbuffer_clear_countdown = 2;
01127         if (d3dpp.BackBufferCount == 2) backbuffer_clear_countdown++;
01128 
01129 #if 0
01130     // Clear all backbuffers
01131     pD3DDevice9->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
01132 
01133     pD3DDevice9->Present(NULL, NULL, NULL, NULL);
01134     pD3DDevice9->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
01135 
01136     if(d3dpp.BackBufferCount == 2) {
01137         pD3DDevice9->Present(NULL, NULL, NULL, NULL);
01138         pD3DDevice9->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
01139     }
01140 #endif
01141 
01142 #if LOG_D3D
01143     LOG_MSG("D3D:Mode: %dx%d (x %.2fx%.2f) --> scaled size: %dx%d", dwWidth, dwHeight,
01144                     (float)dwScaledWidth/dwWidth, (float)dwScaledHeight/dwHeight, dwScaledWidth, dwScaledHeight);
01145 #endif
01146 
01147     return RestoreDeviceObjects();
01148 }
01149 
01150 HRESULT CDirect3D::CreateDisplayTexture(void)
01151 {
01152     SAFE_RELEASE(lpTexture);
01153 
01154     if((dwTexWidth == 0) || (dwTexHeight == 0))
01155         return S_OK;
01156 
01157 #if LOG_D3D
01158     LOG_MSG("D3D:Creating Textures: %dx%d", dwTexWidth, dwTexHeight);
01159 #endif
01160 
01161     HRESULT hr;
01162 
01163     D3DFORMAT d3dtexformat;
01164 
01165     if(bpp16)
01166         d3dtexformat = D3DFMT_R5G6B5;
01167     else
01168         d3dtexformat = D3DFMT_X8R8G8B8;
01169 
01170     if(!dynamic) {
01171         hr = pD3DDevice9->CreateTexture(dwTexWidth, dwTexHeight, 1, 0, d3dtexformat,
01172                                 D3DPOOL_MANAGED, &lpTexture, NULL);
01173 
01174     } else {
01175         hr = pD3DDevice9->CreateTexture(dwTexWidth, dwTexHeight, 1, D3DUSAGE_DYNAMIC, d3dtexformat,
01176                             D3DPOOL_DEFAULT, &lpTexture, NULL);
01177     }
01178 
01179     if(FAILED(hr)) {
01180         LOG_MSG("D3D:Failed to create %stexture: 0x%lx", (dynamic ? "dynamic " : ""), (unsigned long)hr);
01181 
01182         switch(hr) {
01183         case D3DERR_INVALIDCALL:
01184             LOG_MSG("D3D:Invalid call");
01185             break;
01186         case D3DERR_OUTOFVIDEOMEMORY:
01187             LOG_MSG("D3D:D3DERR_OUTOFVIDEOMEMORY");
01188             break;
01189         case E_OUTOFMEMORY:
01190             LOG_MSG("D3D:E_OUTOFMEMORY");
01191             break;
01192         default:
01193             LOG_MSG("D3D:E_UNKNOWN");
01194         }
01195         return E_FAIL;
01196     }
01197 
01198     // Initialize texture to black
01199     if(LockTexture() == S_OK) {
01200         Bit8u * pixels = (Bit8u *)d3dlr.pBits;
01201 
01202         for(Bitu lines = dwTexHeight; lines; lines--) {
01203             memset(pixels, 0, (dwTexWidth<<2)>>(bpp16?1:0));
01204             pixels += d3dlr.Pitch;
01205         }
01206 
01207         lpTexture->UnlockRect(0);
01208     }
01209 
01210     d3dlr.pBits = NULL;
01211     RECT rect;
01212 
01213     rect.left = rect.top = 0;
01214     rect.right = dwTexWidth; rect.bottom = dwTexHeight;
01215     lpTexture->AddDirtyRect(&rect);
01216 
01217 #if C_D3DSHADERS
01218     if(psActive) {
01219         // Working textures for pixel shader
01220         if(FAILED(hr=pD3DDevice9->CreateTexture(dwTexWidth, dwTexHeight, 1, D3DUSAGE_RENDERTARGET,
01221                             D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &lpWorkTexture1, NULL))) {
01222             LOG_MSG("D3D:Failed to create working texture: 0x%lx", (unsigned long)hr);
01223 
01224             switch(hr) {
01225             case D3DERR_INVALIDCALL:
01226                 LOG_MSG("D3D:Invalid call");
01227                 break;
01228             case D3DERR_OUTOFVIDEOMEMORY:
01229                 LOG_MSG("D3D:D3DERR_OUTOFVIDEOMEMORY");
01230                 break;
01231             case E_OUTOFMEMORY:
01232                 LOG_MSG("D3D:E_OUTOFMEMORY");
01233                 break;
01234             default:
01235                 LOG_MSG("D3D:E_UNKNOWN");
01236             }
01237             return E_FAIL;
01238         }
01239 
01240         if(FAILED(hr=pD3DDevice9->CreateTexture(dwTexWidth, dwTexHeight, 1, D3DUSAGE_RENDERTARGET,
01241                             D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &lpWorkTexture2, NULL))) {
01242             LOG_MSG("D3D:Failed to create working texture: 0x%lx", (unsigned long)hr);
01243 
01244             switch(hr) {
01245             case D3DERR_INVALIDCALL:
01246                 LOG_MSG("D3D:Invalid call");
01247                 break;
01248             case D3DERR_OUTOFVIDEOMEMORY:
01249                 LOG_MSG("D3D:D3DERR_OUTOFVIDEOMEMORY");
01250                 break;
01251             case E_OUTOFMEMORY:
01252                 LOG_MSG("D3D:E_OUTOFMEMORY");
01253                 break;
01254             default:
01255                 LOG_MSG("D3D:E_UNKNOWN");
01256             }
01257             return E_FAIL;
01258         }
01259 
01260         if(FAILED(hr=pD3DDevice9->CreateVolumeTexture(256, 16, 256, 1, 0, D3DFMT_A8R8G8B8,
01261                             D3DPOOL_MANAGED, &lpHq2xLookupTexture, NULL))) {
01262             LOG_MSG("D3D:Failed to create volume texture: 0x%lx", (unsigned long)hr);
01263 
01264             switch(hr) {
01265             case D3DERR_INVALIDCALL:
01266                 LOG_MSG("D3D:Invalid call");
01267                 break;
01268             case D3DERR_OUTOFVIDEOMEMORY:
01269                 LOG_MSG("D3D:D3DERR_OUTOFVIDEOMEMORY");
01270                 break;
01271             case E_OUTOFMEMORY:
01272                 LOG_MSG("D3D:E_OUTOFMEMORY");
01273                 break;
01274             default:
01275                 LOG_MSG("D3D:E_UNKNOWN");
01276             }
01277             return E_FAIL;
01278         }
01279 
01280         // build lookup table
01281         D3DLOCKED_BOX lockedBox;
01282 
01283         if(FAILED(hr = lpHq2xLookupTexture->LockBox(0, &lockedBox, NULL, 0))) {
01284             LOG_MSG("D3D:Failed to lock box of volume texture: 0x%lx", (unsigned long)hr);
01285 
01286             switch(hr) {
01287                 case D3DERR_INVALIDCALL:
01288                     LOG_MSG("D3D:Invalid call");
01289                     break;
01290                 default:
01291                     LOG_MSG("D3D:E_UNKNOWN");
01292             }
01293             return E_FAIL;
01294         }
01295 
01296         BuildHq2xLookupTexture(dwScaledWidth, dwScaledHeight, dwWidth, dwHeight, (Bit8u *)lockedBox.pBits);
01297 
01298 #if LOG_D3D
01299         // Debug: Write look-up texture to file
01300         int fd = _open("hq2xLookupTexture.pam",_O_WRONLY|_O_CREAT|_O_TRUNC|_O_BINARY,0666);
01301         unsigned char table[4096] = HQ2X_D3D_TABLE_DATA;
01302         sprintf((char *)table,"P7\nWIDTH %i\nHEIGHT %i\nMAXVAL 255\nDEPTH 4\nTUPLTYPE RGB_ALPHA\nENDHDR\n",16*HQ2X_RESOLUTION,4096/16*HQ2X_RESOLUTION);
01303         write(fd,table,strlen((char *)table));
01304         write(fd, lockedBox.pBits, HQ2X_RESOLUTION * HQ2X_RESOLUTION * 4096 * 4);
01305         _close(fd);
01306 #endif
01307 
01308         if(FAILED(hr = lpHq2xLookupTexture->UnlockBox(0))) {
01309             LOG_MSG("D3D:Failed to unlock box of volume texture: 0x%lx", (unsigned long)hr);
01310 
01311             switch(hr) {
01312                 case D3DERR_INVALIDCALL:
01313                     LOG_MSG("D3D:Invalid call");
01314                     break;
01315                 default:
01316                     LOG_MSG("D3D:E_UNKNOWN");
01317             }
01318             return E_FAIL;
01319         }
01320 
01321 #if LOG_D3D
01322         // Debug texture for pixel shader
01323         if(FAILED(hr=pD3DDevice9->CreateTexture(dwTexWidth, dwTexHeight, 1, 0, D3DFMT_A8R8G8B8,
01324                             D3DPOOL_SYSTEMMEM, &lpDebugTexture, NULL))) {
01325             LOG_MSG("D3D:Failed to create debug texture: 0x%x", hr);
01326 
01327             switch(hr) {
01328             case D3DERR_INVALIDCALL:
01329                 LOG_MSG("D3D:Invalid call");
01330                 break;
01331             case D3DERR_OUTOFVIDEOMEMORY:
01332                 LOG_MSG("D3D:D3DERR_OUTOFVIDEOMEMORY");
01333                 break;
01334             case E_OUTOFMEMORY:
01335                 LOG_MSG("D3D:E_OUTOFMEMORY");
01336                 break;
01337             default:
01338                 LOG_MSG("D3D:E_UNKNOWN");
01339             }
01340             return E_FAIL;
01341         }
01342 #endif  // LOG_D3D
01343 
01344         // Set textures
01345         if(FAILED(psEffect->SetTextures(lpTexture, lpWorkTexture1, lpWorkTexture2, lpHq2xLookupTexture))) {
01346             LOG_MSG("D3D:Failed to set PS textures");
01347             return false;
01348         }
01349 
01350     }
01351 #endif  // C_D3DSHADERS
01352 
01353     return S_OK;
01354 }
01355 
01356 HRESULT CDirect3D::CreateVertex(void)
01357 {
01358     TLVERTEX* vertices;
01359 
01360     // Texture coordinates
01361     float sizex=1.0f, sizey=1.0f;
01362 
01363     // If texture is larger than DOSBox FB
01364     if(dwTexWidth != dwWidth)
01365         sizex = (float)dwWidth/dwTexWidth;
01366     if(dwTexHeight != dwHeight)
01367         sizey = (float)dwHeight/dwTexHeight;
01368 
01369 #if LOG_D3D
01370     LOG_MSG("D3D:Quad size: %dx%d, tex. coord.: 0,0-->%.2f,%.2f", dwWidth, dwHeight, sizex, sizey);
01371 #endif
01372 
01373     // Lock the vertex buffer
01374     vertexBuffer->Lock(0, 0, (void**)&vertices, 0);
01375 
01376     //Setup vertices
01377     vertices[0].position = D3DXVECTOR3( (float)dwX,                                       (float)dwY,                                    0.0f );
01378     vertices[0].diffuse  = 0xFFFFFFFF;
01379     vertices[0].texcoord = D3DXVECTOR2( 0.0f,                                             0.0f );
01380 
01381     vertices[1].position = D3DXVECTOR3( (float)dwX,                                       (float)(dwY + dwScaledHeight), 0.0f );
01382     vertices[1].diffuse  = 0xFFFFFFFF;
01383     vertices[1].texcoord = D3DXVECTOR2( 0.0f,                                             (float)sizey );
01384     
01385     vertices[2].position = D3DXVECTOR3( (float)(dwX + dwScaledWidth), (float)dwY,                                    0.0f );
01386     vertices[2].diffuse  = 0xFFFFFFFF;
01387     vertices[2].texcoord = D3DXVECTOR2( (float)sizex,                             0.0f );
01388     
01389     vertices[3].position = D3DXVECTOR3( (float)(dwX + dwScaledWidth), (float)(dwY + dwScaledHeight), 0.0f );
01390     vertices[3].diffuse  = 0xFFFFFFFF;
01391     vertices[3].texcoord = D3DXVECTOR2( (float)sizex,                             (float)sizey );
01392 
01393     // Additional vertices required for some PS effects
01394     // FIXME: Recent changes may have BROKEN pixel shader support here!!!!!
01395     if(preProcess) {
01396         vertices[4].position = D3DXVECTOR3( 0.0f, 0.0f, 0.0f);
01397         vertices[4].diffuse  = 0xFFFFFF00;
01398         vertices[4].texcoord = D3DXVECTOR2( 0.0f, 1.0f);
01399         vertices[5].position = D3DXVECTOR3( 0.0f, 1.0f, 0.0f);
01400         vertices[5].diffuse  = 0xFFFFFF00;
01401         vertices[5].texcoord = D3DXVECTOR2( 0.0f, 0.0f);
01402         vertices[6].position = D3DXVECTOR3( 1.0f, 0.0f, 0.0f);
01403         vertices[6].diffuse  = 0xFFFFFF00;
01404         vertices[6].texcoord = D3DXVECTOR2( 1.0f, 1.0f);
01405         vertices[7].position = D3DXVECTOR3( 1.0f, 1.0f, 0.0f);
01406         vertices[7].diffuse  = 0xFFFFFF00;
01407         vertices[7].texcoord = D3DXVECTOR2( 1.0f, 0.0f);
01408     }
01409 
01410     // Unlock the vertex buffer
01411     vertexBuffer->Unlock();
01412 
01413     return S_OK;
01414 }
01415 
01416 void CDirect3D::SetupSceneScaled(void)
01417 {
01418         // TODO: It would probably be nicer to offer an option here whether the user wants
01419         //               point sampling (D3DTEXF_POINT) or linear interpolation (D3DTEXF_LINEAR) when scaling up/down.
01420     pD3DDevice9->SetTextureStageState(0, D3DTSS_COLOROP,   D3DTOP_MODULATE);
01421     pD3DDevice9->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
01422     pD3DDevice9->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
01423     pD3DDevice9->SetTextureStageState(0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE);
01424     pD3DDevice9->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
01425     pD3DDevice9->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
01426     pD3DDevice9->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
01427     pD3DDevice9->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
01428     pD3DDevice9->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
01429 
01430     D3DVIEWPORT9 Viewport;
01431     pD3DDevice9->GetViewport(&Viewport);
01432 
01433     // Projection is screenspace coords
01434     D3DXMatrixOrthoOffCenterLH(&m_matProj, 0.0f, (float)Viewport.Width, 0.0f, (float)Viewport.Height, 0.0f, 1.0f);
01435     {
01436             D3DXMATRIX x;
01437             D3DXMatrixScaling(&x, 1.0f, -1.0f, 1.0f);
01438             m_matProj *= x;
01439     }
01440 
01441     // View matrix with -0.5f offset to avoid a fuzzy picture
01442     D3DXMatrixTranslation(&m_matView, -0.5f, -0.5f, 0.0f);
01443 
01444     // TODO: Re-implement 5:4 monitor autofit
01445 
01446 #if LOG_D3D
01447     LOG_MSG("D3D:Scaled resolution: %.1fx%.1f, factor: %dx%d", sizex, sizey, x, y);
01448 #endif
01449 
01450     D3DXMatrixScaling(&m_matWorld, 1.0, 1.0, 1.0f);
01451 }
01452 
01453 #if !(C_D3DSHADERS)
01454 
01455 D3DXMATRIX* CDirect3D::MatrixOrthoOffCenterLH(D3DXMATRIX *pOut, float l, float r, float b, float t, float zn, float zf)
01456 {
01457     pOut->_11=2.0f/r; pOut->_12=0.0f;   pOut->_13=0.0f;  pOut->_14=0.0f;
01458     pOut->_21=0.0f;   pOut->_22=2.0f/t; pOut->_23=0.0f;  pOut->_24=0.0f;
01459     pOut->_31=0.0f;   pOut->_32=0.0f;   pOut->_33=1.0f;  pOut->_34=0.0f;
01460     pOut->_41=-1.0f;  pOut->_42=-1.0f;  pOut->_43=0.0f;  pOut->_44=1.0f;
01461 
01462     return pOut;
01463 }
01464 
01465 D3DXMATRIX* CDirect3D::MatrixScaling(D3DXMATRIX *pOut, float sx, float sy, float sz)
01466 {
01467     pOut->_11=sx;     pOut->_12=0.0f;   pOut->_13=0.0f;  pOut->_14=0.0f;
01468     pOut->_21=0.0f;   pOut->_22=sy;     pOut->_23=0.0f;  pOut->_24=0.0f;
01469     pOut->_31=0.0f;   pOut->_32=0.0f;   pOut->_33=sz;    pOut->_34=0.0f;
01470     pOut->_41=0.0f;   pOut->_42=0.0f;   pOut->_43=0.0f;  pOut->_44=1.0f;
01471 
01472     return pOut;
01473 }
01474 
01475 D3DXMATRIX* CDirect3D::MatrixTranslation(D3DXMATRIX *pOut, float tx, float ty, float tz)
01476 {
01477     pOut->_11=1.0f;   pOut->_12=0.0f;   pOut->_13=0.0f;  pOut->_14=0.0f;
01478     pOut->_21=0.0f;   pOut->_22=1.0f;   pOut->_23=0.0f;  pOut->_24=0.0f;
01479     pOut->_31=0.0f;   pOut->_32=0.0f;   pOut->_33=1.0f;  pOut->_34=0.0f;
01480     pOut->_41=tx;     pOut->_42=ty;     pOut->_43=tz;    pOut->_44=1.0f;
01481 
01482     return pOut;
01483 }
01484 
01485 #endif  // !(C_D3DSHADERS)
01486 
01487 #endif  // (HAVE_D3D9_H)