DOSBox-X
|
00001 #include <sys/types.h> 00002 #include <assert.h> 00003 #include <math.h> 00004 00005 #include "dosbox.h" 00006 #include <output/output_direct3d.h> 00007 00008 #include "sdlmain.h" 00009 00010 using namespace std; 00011 00012 #if C_DIRECT3D 00013 00014 CDirect3D* d3d = NULL; 00015 00016 static void d3d_init(void) 00017 { 00018 sdl.desktop.want_type = SCREEN_DIRECT3D; 00019 if (!sdl.using_windib) 00020 { 00021 LOG_MSG("Resetting to WINDIB mode"); 00022 SDL_QuitSubSystem(SDL_INIT_VIDEO); 00023 putenv("SDL_VIDEODRIVER=windib"); 00024 sdl.using_windib = true; 00025 if (SDL_InitSubSystem(SDL_INIT_VIDEO)<0) E_Exit("Can't init SDL Video %s", SDL_GetError()); 00026 GFX_SetIcon(); GFX_SetTitle(-1, -1, -1, false); 00027 if (!sdl.desktop.fullscreen) DOSBox_RefreshMenu(); 00028 } 00029 00030 SDL_SysWMinfo wmi; 00031 SDL_VERSION(&wmi.version); 00032 00033 if (!SDL_GetWMInfo(&wmi)) 00034 { 00035 LOG_MSG("SDL:Error retrieving window information"); 00036 LOG_MSG("Failed to get window info"); 00037 OUTPUT_SURFACE_Select(); 00038 } 00039 else 00040 { 00041 if (sdl.desktop.fullscreen) 00042 GFX_CaptureMouse(); 00043 00044 if (d3d) delete d3d; 00045 d3d = new CDirect3D(640, 400); 00046 00047 if (!d3d) 00048 { 00049 LOG_MSG("Failed to create d3d object"); 00050 OUTPUT_SURFACE_Select(); 00051 return; 00052 } 00053 else if (d3d->InitializeDX(wmi.child_window, sdl.desktop.doublebuf) != S_OK) 00054 { 00055 LOG_MSG("Unable to initialize DirectX"); 00056 OUTPUT_SURFACE_Select(); 00057 return; 00058 } 00059 } 00060 00061 # if (C_D3DSHADERS) 00062 if (d3d) { 00063 Section_prop *section = static_cast<Section_prop *>(control->GetSection("sdl")); 00064 Prop_multival* prop = section->Get_multival("pixelshader"); 00065 if (SUCCEEDED(d3d->LoadPixelShader(prop->GetSection()->Get_string("type"), 0, 0))) 00066 if (menu.startup) 00067 GFX_ResetScreen(); 00068 } 00069 # endif 00070 } 00071 00072 00073 // output API below 00074 00075 void OUTPUT_DIRECT3D_Initialize() 00076 { 00077 // nothing to initialize (yet?) 00078 } 00079 00080 void OUTPUT_DIRECT3D_Select() 00081 { 00082 sdl.desktop.want_type = SCREEN_DIRECT3D; 00083 render.aspectOffload = true; 00084 d3d_init(); 00085 00086 #if defined(WIN32) && !defined(C_SDL2) 00087 SDL1_hax_inhibit_WM_PAINT = 1; 00088 #endif 00089 } 00090 00091 Bitu OUTPUT_DIRECT3D_GetBestMode(Bitu flags) 00092 { 00093 flags |= GFX_SCALING; 00094 if (GCC_UNLIKELY(d3d->bpp16)) 00095 flags &= ~(GFX_CAN_8 | GFX_CAN_15 | GFX_CAN_32); 00096 else 00097 flags &= ~(GFX_CAN_8 | GFX_CAN_15 | GFX_CAN_16); 00098 return flags; 00099 } 00100 00101 Bitu OUTPUT_DIRECT3D_SetSize() 00102 { 00103 Bitu retFlags = 0; 00104 00105 Bit16u fixedWidth; 00106 Bit16u fixedHeight; 00107 Bit16u windowWidth; 00108 Bit16u windowHeight; 00109 Bitu adjTexWidth = sdl.draw.width; 00110 Bitu adjTexHeight = sdl.draw.height; 00111 00112 if (sdl.desktop.fullscreen) 00113 { 00114 fixedWidth = sdl.desktop.full.fixed ? sdl.desktop.full.width : 0; 00115 fixedHeight = sdl.desktop.full.fixed ? sdl.desktop.full.height : 0; 00116 } 00117 else 00118 { 00119 fixedWidth = sdl.desktop.window.width; 00120 fixedHeight = sdl.desktop.window.height; 00121 } 00122 00123 if (fixedWidth == 0 || fixedHeight == 0) 00124 { 00125 Bitu consider_height = menu.maxwindow ? currentWindowHeight : 0; 00126 Bitu consider_width = menu.maxwindow ? currentWindowWidth : 0; 00127 int final_height = (int)max(consider_height, userResizeWindowHeight); 00128 int final_width = (int)max(consider_width, userResizeWindowWidth); 00129 00130 fixedWidth = final_width; 00131 fixedHeight = final_height; 00132 } 00133 00134 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW 00135 /* scale the menu bar if the window is large enough */ 00136 { 00137 int cw = fixedWidth, ch = fixedHeight; 00138 Bitu scale = 1; 00139 00140 if (cw == 0) cw = (Bit16u)(sdl.draw.width * sdl.draw.scalex); 00141 if (ch == 0) ch = (Bit16u)(sdl.draw.height * sdl.draw.scaley); 00142 00143 while ((cw / scale) >= (640 * 2) && (ch / scale) >= (400 * 2)) 00144 scale++; 00145 00146 LOG_MSG("menuScale=%lu", (unsigned long)scale); 00147 mainMenu.setScale(scale); 00148 00149 if (mainMenu.isVisible()) 00150 fixedHeight -= mainMenu.menuBox.h; 00151 } 00152 #endif 00153 00154 sdl.clip.x = sdl.clip.y = 0; 00155 if (fixedWidth && fixedHeight) 00156 { 00157 sdl.clip.w = windowWidth = fixedWidth; 00158 sdl.clip.h = windowHeight = fixedHeight; 00159 if (render.aspect) aspectCorrectFitClip(sdl.clip.w, sdl.clip.h, sdl.clip.x, sdl.clip.y, fixedWidth, fixedHeight); 00160 } 00161 else 00162 { 00163 windowWidth = (Bit16u)(sdl.draw.width * sdl.draw.scalex); 00164 windowHeight = (Bit16u)(sdl.draw.height * sdl.draw.scaley); 00165 if (render.aspect) aspectCorrectExtend(windowWidth, windowHeight); 00166 sdl.clip.w = windowWidth; sdl.clip.h = windowHeight; 00167 } 00168 00169 // when xBRZ scaler is used, we can adjust render target size to exactly what xBRZ scaler will output, leaving final scaling to default D3D scaler / shaders 00170 #if C_XBRZ 00171 if (sdl_xbrz.enable && xBRZ_SetScaleParameters(sdl.draw.width, sdl.draw.height, sdl.clip.w, sdl.clip.h)) 00172 { 00173 adjTexWidth = sdl.draw.width * sdl_xbrz.scale_factor; 00174 adjTexHeight = sdl.draw.height * sdl_xbrz.scale_factor; 00175 } 00176 #endif 00177 // Calculate texture size 00178 if ((!d3d->square) && (!d3d->pow2)) 00179 { 00180 d3d->dwTexWidth = (DWORD)adjTexWidth; 00181 d3d->dwTexHeight = (DWORD)adjTexHeight; 00182 } 00183 else if (d3d->square) 00184 { 00185 int texsize = 2 << int_log2((int)(adjTexWidth > adjTexHeight ? adjTexWidth : adjTexHeight)); 00186 d3d->dwTexWidth = d3d->dwTexHeight = texsize; 00187 } 00188 else 00189 { 00190 d3d->dwTexWidth = 2 << int_log2((int)adjTexWidth); 00191 d3d->dwTexHeight = 2 << int_log2((int)adjTexHeight); 00192 } 00193 00194 LOG(LOG_MISC, LOG_DEBUG)("GFX_SetSize Direct3D texture=%ux%u window=%ux%u clip=x,y,w,h=%d,%d,%d,%d", 00195 (unsigned int)d3d->dwTexWidth, 00196 (unsigned int)d3d->dwTexHeight, 00197 (unsigned int)windowWidth, 00198 (unsigned int)windowHeight, 00199 (unsigned int)sdl.clip.x, 00200 (unsigned int)sdl.clip.y, 00201 (unsigned int)sdl.clip.w, 00202 (unsigned int)sdl.clip.h); 00203 00204 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW 00205 if (mainMenu.isVisible()) { 00206 windowHeight += mainMenu.menuBox.h; 00207 sdl.clip.y += mainMenu.menuBox.h; 00208 } 00209 #endif 00210 00211 #if (C_D3DSHADERS) 00212 Section_prop *section = static_cast<Section_prop *>(control->GetSection("sdl")); 00213 if (section) 00214 { 00215 Prop_multival* prop = section->Get_multival("pixelshader"); 00216 std::string f = prop->GetSection()->Get_string("force"); 00217 d3d->LoadPixelShader(prop->GetSection()->Get_string("type"), sdl.draw.scalex, sdl.draw.scaley, (f == "forced")); 00218 } 00219 else 00220 { 00221 LOG_MSG("SDL:D3D:Could not get pixelshader info, shader disabled"); 00222 } 00223 #endif 00224 00225 d3d->aspect = false; 00226 d3d->autofit = false; 00227 00228 // Create a dummy sdl surface 00229 // D3D will hang or crash when using fullscreen with ddraw surface, therefore we hack SDL to provide 00230 // a GDI window with an additional 0x40 flag. If this fails or stock SDL is used, use WINDIB output 00231 if (GCC_UNLIKELY(d3d->bpp16)) 00232 { 00233 sdl.surface = SDL_SetVideoMode(windowWidth, windowHeight, 16, sdl.desktop.fullscreen ? SDL_FULLSCREEN | 0x40 : SDL_RESIZABLE | 0x40); 00234 sdl.deferred_resize = false; 00235 sdl.must_redraw_all = true; 00236 retFlags = GFX_CAN_16 | GFX_SCALING; 00237 } 00238 else 00239 { 00240 sdl.surface = SDL_SetVideoMode(windowWidth, windowHeight, 0, sdl.desktop.fullscreen ? SDL_FULLSCREEN | 0x40 : SDL_RESIZABLE | 0x40); 00241 sdl.deferred_resize = false; 00242 sdl.must_redraw_all = true; 00243 retFlags = GFX_CAN_32 | GFX_SCALING; 00244 } 00245 00246 if (sdl.surface == NULL) 00247 E_Exit("Could not set video mode %ix%i-%i: %s", sdl.clip.w, sdl.clip.h, d3d->bpp16 ? 16 : 32, SDL_GetError()); 00248 00249 if (d3d->dynamic) retFlags |= GFX_HARDWARE; 00250 00251 if (GCC_UNLIKELY(d3d->Resize3DEnvironment(windowWidth, windowHeight, sdl.clip.x, sdl.clip.y, sdl.clip.w, sdl.clip.h, adjTexWidth, adjTexHeight, sdl.desktop.fullscreen) != S_OK)) 00252 retFlags = 0; 00253 00254 #if LOG_D3D 00255 LOG_MSG("SDL:D3D:Display mode set to: %dx%d with %fx%f scale", sdl.clip.w, sdl.clip.h, sdl.draw.scalex, sdl.draw.scaley); 00256 #endif 00257 00258 #if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW 00259 mainMenu.screenWidth = sdl.surface->w; 00260 mainMenu.screenHeight = sdl.surface->h; 00261 mainMenu.updateRect(); 00262 mainMenu.setRedraw(); 00263 GFX_DrawSDLMenu(mainMenu, mainMenu.display_list); 00264 #endif 00265 00266 return retFlags; 00267 } 00268 00269 bool OUTPUT_DIRECT3D_StartUpdate(Bit8u* &pixels, Bitu &pitch) 00270 { 00271 #if C_XBRZ 00272 if (sdl_xbrz.enable && sdl_xbrz.scale_on) 00273 { 00274 sdl_xbrz.renderbuf.resize(sdl.draw.width * sdl.draw.height); 00275 pixels = sdl_xbrz.renderbuf.empty() ? nullptr : reinterpret_cast<Bit8u*>(&sdl_xbrz.renderbuf[0]); 00276 pitch = sdl.draw.width * sizeof(uint32_t); 00277 sdl.updating = true; 00278 } 00279 else 00280 { 00281 sdl.updating = d3d->LockTexture(pixels, pitch); 00282 } 00283 #else 00284 sdl.updating = d3d->LockTexture(pixels, pitch); 00285 #endif 00286 return sdl.updating; 00287 } 00288 00289 void OUTPUT_DIRECT3D_EndUpdate(const Bit16u *changedLines) 00290 { 00291 #if C_XBRZ 00292 if (sdl_xbrz.enable && sdl_xbrz.scale_on) 00293 { 00294 // we have xBRZ pseudo render buffer to be output to the pre-sized texture, do the xBRZ part 00295 const int srcWidth = sdl.draw.width; 00296 const int srcHeight = sdl.draw.height; 00297 if (sdl_xbrz.renderbuf.size() == srcWidth * srcHeight && srcWidth > 0 && srcHeight > 0) 00298 { 00299 // we assume render buffer is *not* scaled! 00300 int xbrzWidth = srcWidth * sdl_xbrz.scale_factor; 00301 int xbrzHeight = srcHeight * sdl_xbrz.scale_factor; 00302 sdl_xbrz.pixbuf.resize(xbrzWidth * xbrzHeight); 00303 00304 const uint32_t* renderBuf = &sdl_xbrz.renderbuf[0]; // help VS compiler a little + support capture by value 00305 uint32_t* xbrzBuf = &sdl_xbrz.pixbuf[0]; 00306 xBRZ_Render(renderBuf, xbrzBuf, changedLines, srcWidth, srcHeight, sdl_xbrz.scale_factor); 00307 00308 // D3D texture can be not of exactly size we expect, so we copy xBRZ buffer to the texture there, adjusting for texture pitch 00309 Bit8u *tgtPix; 00310 Bitu tgtPitch; 00311 if (d3d->LockTexture(tgtPix, tgtPitch) && tgtPix) // if locking fails, target texture can be nullptr 00312 { 00313 uint32_t* tgtTex = reinterpret_cast<uint32_t*>(static_cast<Bit8u*>(tgtPix)); 00314 # if defined(XBRZ_PPL) 00315 concurrency::task_group tg; 00316 for (int i = 0; i < xbrzHeight; i += sdl_xbrz.task_granularity) 00317 { 00318 tg.run([=] { 00319 const int iLast = min(i + sdl_xbrz.task_granularity, xbrzHeight); 00320 xbrz::pitchChange(&xbrzBuf[0], &tgtTex[0], (int)xbrzWidth, (int)xbrzHeight, (int)(xbrzWidth * sizeof(uint32_t)), (int)tgtPitch, (int)i, (int)iLast, [](uint32_t pix) { return pix; }); 00321 }); 00322 } 00323 tg.wait(); 00324 # else 00325 xbrz::pitchChange(&xbrzBuf[0], &tgtTex[0], xbrzWidth, xbrzHeight, xbrzWidth * sizeof(uint32_t), tgtPitch, 0, xbrzHeight, [](uint32_t pix) { return pix; }); 00326 # endif 00327 } 00328 } 00329 } 00330 #endif 00331 00332 if (!menu.hidecycles) frames++; //implemented 00333 if (GCC_UNLIKELY(!d3d->UnlockTexture(changedLines))) 00334 E_Exit("Failed to draw screen!"); 00335 } 00336 00337 void OUTPUT_DIRECT3D_Shutdown() 00338 { 00339 if (d3d) 00340 delete d3d; 00341 } 00342 00343 #endif /*C_DIRECT3D*/