DOSBox-X
|
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)