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