DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/output/output_direct3d.cpp
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 = max(consider_height, userResizeWindowHeight);
00128         int final_width = 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 = adjTexWidth;
00181         d3d->dwTexHeight = adjTexHeight;
00182     }
00183     else if (d3d->square) 
00184     {
00185         int texsize = 2 << int_log2(adjTexWidth > adjTexHeight ? adjTexWidth : adjTexHeight);
00186         d3d->dwTexWidth = d3d->dwTexHeight = texsize;
00187     }
00188     else 
00189     {
00190         d3d->dwTexWidth = 2 << int_log2(adjTexWidth);
00191         d3d->dwTexHeight = 2 << int_log2(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.updateRect();
00261     mainMenu.setRedraw();
00262     GFX_DrawSDLMenu(mainMenu, mainMenu.display_list);
00263 #endif
00264 
00265     return retFlags;
00266 }
00267 
00268 bool OUTPUT_DIRECT3D_StartUpdate(Bit8u* &pixels, Bitu &pitch)
00269 {
00270 #if C_XBRZ
00271     if (sdl_xbrz.enable && sdl_xbrz.scale_on) 
00272     {
00273         sdl_xbrz.renderbuf.resize(sdl.draw.width * sdl.draw.height);
00274         pixels = sdl_xbrz.renderbuf.empty() ? nullptr : reinterpret_cast<Bit8u*>(&sdl_xbrz.renderbuf[0]);
00275         pitch = sdl.draw.width * sizeof(uint32_t);
00276         sdl.updating = true;
00277     }
00278     else
00279     {
00280         sdl.updating = d3d->LockTexture(pixels, pitch);
00281     }
00282 #else
00283     sdl.updating = d3d->LockTexture(pixels, pitch);
00284 #endif
00285     return sdl.updating;
00286 }
00287 
00288 void OUTPUT_DIRECT3D_EndUpdate(const Bit16u *changedLines)
00289 {
00290 #if C_XBRZ
00291     if (sdl_xbrz.enable && sdl_xbrz.scale_on) 
00292     {
00293         // we have xBRZ pseudo render buffer to be output to the pre-sized texture, do the xBRZ part
00294         const int srcWidth = sdl.draw.width;
00295         const int srcHeight = sdl.draw.height;
00296         if (sdl_xbrz.renderbuf.size() == srcWidth * srcHeight && srcWidth > 0 && srcHeight > 0)
00297         {
00298             // we assume render buffer is *not* scaled!
00299             int xbrzWidth = srcWidth * sdl_xbrz.scale_factor;
00300             int xbrzHeight = srcHeight * sdl_xbrz.scale_factor;
00301             sdl_xbrz.pixbuf.resize(xbrzWidth * xbrzHeight);
00302 
00303             const uint32_t* renderBuf = &sdl_xbrz.renderbuf[0]; // help VS compiler a little + support capture by value
00304             uint32_t* xbrzBuf = &sdl_xbrz.pixbuf[0];
00305             xBRZ_Render(renderBuf, xbrzBuf, changedLines, srcWidth, srcHeight, sdl_xbrz.scale_factor);
00306 
00307             // D3D texture can be not of exactly size we expect, so we copy xBRZ buffer to the texture there, adjusting for texture pitch
00308             Bit8u *tgtPix;
00309             Bitu tgtPitch;
00310             if (d3d->LockTexture(tgtPix, tgtPitch) && tgtPix) // if locking fails, target texture can be nullptr
00311             {
00312                 uint32_t* tgtTex = reinterpret_cast<uint32_t*>(static_cast<Bit8u*>(tgtPix));
00313 # if defined(XBRZ_PPL)
00314                 concurrency::task_group tg;
00315                 for (int i = 0; i < xbrzHeight; i += sdl_xbrz.task_granularity)
00316                 {
00317                     tg.run([=] {
00318                         const int iLast = min(i + sdl_xbrz.task_granularity, xbrzHeight);
00319                         xbrz::pitchChange(&xbrzBuf[0], &tgtTex[0], xbrzWidth, xbrzHeight, xbrzWidth * sizeof(uint32_t), tgtPitch, i, iLast, [](uint32_t pix) { return pix; });
00320                     });
00321                 }
00322                 tg.wait();
00323 # else
00324                 xbrz::pitchChange(&xbrzBuf[0], &tgtTex[0], xbrzWidth, xbrzHeight, xbrzWidth * sizeof(uint32_t), tgtPitch, 0, xbrzHeight, [](uint32_t pix) { return pix; });
00325 # endif
00326             }
00327         }
00328     }
00329 #endif
00330 
00331     if (!menu.hidecycles) frames++; //implemented
00332     if (GCC_UNLIKELY(!d3d->UnlockTexture(changedLines)))
00333         E_Exit("Failed to draw screen!");
00334 }
00335 
00336 void OUTPUT_DIRECT3D_Shutdown()
00337 {
00338     if (d3d)
00339         delete d3d;
00340 }
00341 
00342 #endif /*C_DIRECT3D*/