/* * Copyright (C) 2002-2020 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include "dosbox.h" #include "video.h" #include "render.h" #include "setup.h" #include "control.h" #include "mapper.h" #include "cross.h" #include "hardware.h" #include "support.h" #include "sdlmain.h" #include "shell.h" #include "render_scalers.h" #include "render_glsl.h" #if defined(__SSE__) #include #include #endif Render_t render; int eurAscii = -1; Bitu last_gfx_flags = 0; ScalerLineHandler_t RENDER_DrawLine; uint32_t GFX_palette32bpp[256] = {0}; unsigned int GFX_GetBShift(); void RENDER_CallBack( GFX_CallBackFunctions_t function ); static void Check_Palette(void) { /* Clean up any previous changed palette data */ if (render.pal.changed) { memset(render.pal.modified, 0, sizeof(render.pal.modified)); render.pal.changed = false; } if (render.pal.first>render.pal.last) return; Bitu i; switch (render.scale.outMode) { case scalerMode8: GFX_SetPalette(render.pal.first,render.pal.last-render.pal.first+1,(GFX_PalEntry *)&render.pal.rgb[render.pal.first]); break; case scalerMode15: case scalerMode16: for (i=render.pal.first;i<=render.pal.last;i++) { Bit8u r=render.pal.rgb[i].red; Bit8u g=render.pal.rgb[i].green; Bit8u b=render.pal.rgb[i].blue; Bit16u newPal = (Bit16u)GFX_GetRGB(r,g,b); if (newPal != render.pal.lut.b16[i]) { render.pal.changed = true; render.pal.modified[i] = 1; render.pal.lut.b16[i] = newPal; } } break; case scalerMode32: default: for (i=render.pal.first;i<=render.pal.last;i++) { Bit8u r=render.pal.rgb[i].red; Bit8u g=render.pal.rgb[i].green; Bit8u b=render.pal.rgb[i].blue; Bit32u newPal = (Bit32u)GFX_GetRGB(r,g,b); if (newPal != render.pal.lut.b32[i]) { render.pal.changed = true; render.pal.modified[i] = 1; render.pal.lut.b32[i] = newPal; } } break; } /* Setup pal index to startup values */ render.pal.first=256; render.pal.last=0; } void RENDER_SetPal(Bit8u entry,Bit8u red,Bit8u green,Bit8u blue) { if (GFX_GetBShift() == 0) { GFX_palette32bpp[entry] = ((uint32_t)red << (uint32_t)16) + ((uint32_t)green << (uint32_t)8) + ((uint32_t)blue << (uint32_t)0); } else { GFX_palette32bpp[entry] = ((uint32_t)blue << (uint32_t)16) + ((uint32_t)green << (uint32_t)8) + ((uint32_t)red << (uint32_t)0); } render.pal.rgb[entry].red=red; render.pal.rgb[entry].green=green; render.pal.rgb[entry].blue=blue; if (render.pal.first>entry) render.pal.first=entry; if (render.pal.last= (Bits)simd_inc) { __m128i v = _mm_loadu_si128((const __m128i*)src); __m128i c = _mm_loadu_si128((const __m128i*)cache); __m128i cmp = _mm_cmpeq_epi32(v, c); if (GCC_UNLIKELY(_mm_movemask_epi8(cmp) != 0xFFFF)) goto cacheMiss; count-=(Bits)simd_inc; src+=simd_inc; cache+=simd_inc; } #undef MY_SIZEOF_INT_P } else #endif { while (count) { if (GCC_UNLIKELY(src[0] != cache[0])) goto cacheMiss; count--; src++; cache++; } } } /* cacheHit */ return true; cacheMiss: return false; } #if defined(C_SCALER_FULL_LINE) static unsigned int RENDER_scaler_countdown = 0; static const unsigned int RENDER_scaler_countdown_init = 12; static INLINE void cn_ScalerAddLines( Bitu changed, Bitu count ) { if ((Scaler_ChangedLineIndex & 1) == changed ) { Scaler_ChangedLines[Scaler_ChangedLineIndex] += count; } else { Scaler_ChangedLines[++Scaler_ChangedLineIndex] = count; } render.scale.outWrite += render.scale.outPitch * count; } static void RENDER_DrawLine_countdown(const void * s); static void RENDER_DrawLine_countdown_wait(const void * s) { if (RENDER_DrawLine_scanline_cacheHit(s)) { // line has not changed render.scale.inLine++; render.scale.cacheRead += render.scale.cachePitch; cn_ScalerAddLines(0,Scaler_Aspect[ render.scale.outLine++ ]); } else { RENDER_scaler_countdown = RENDER_scaler_countdown_init; RENDER_DrawLine = RENDER_DrawLine_countdown; RENDER_DrawLine( s ); } } static void RENDER_DrawLine_countdown(const void * s) { render.scale.lineHandler(s); if (--RENDER_scaler_countdown == 0) RENDER_DrawLine = RENDER_DrawLine_countdown_wait; } #endif static void RENDER_StartLineHandler(const void * s) { if (RENDER_DrawLine_scanline_cacheHit(s)) { // line has not changed render.scale.cacheRead += render.scale.cachePitch; Scaler_ChangedLines[0] += Scaler_Aspect[ render.scale.inLine ]; render.scale.inLine++; render.scale.outLine++; } else { if (!GFX_StartUpdate( render.scale.outWrite, render.scale.outPitch )) { RENDER_DrawLine = RENDER_EmptyLineHandler; return; } render.scale.outWrite += render.scale.outPitch * Scaler_ChangedLines[0]; #if defined(C_SCALER_FULL_LINE) RENDER_scaler_countdown = RENDER_scaler_countdown_init; RENDER_DrawLine = RENDER_DrawLine_countdown; #else RENDER_DrawLine = render.scale.lineHandler; #endif RENDER_DrawLine( s ); } } static void RENDER_FinishLineHandler(const void * s) { if (s) { const Bitu *src = (Bitu*)s; Bitu *cache = (Bitu*)(render.scale.cacheRead); for (Bitu x=render.src.start;x>0;) { cache[0] = src[0]; x--; src++; cache++; } } render.scale.cacheRead += render.scale.cachePitch; } static void RENDER_ClearCacheHandler(const void * src) { Bitu x, width; Bit32u *srcLine, *cacheLine; srcLine = (Bit32u *)src; cacheLine = (Bit32u *)render.scale.cacheRead; width = render.scale.cachePitch / 4; for (x=0;x= miny) { Bitu templines = (Bitu)lines; lines -= templines; linesadded += templines; Scaler_Aspect[i] = (Bit8u)templines; } else { Scaler_Aspect[i] = 0; } } return linesadded; } void RENDER_Reset( void ) { Bitu width=render.src.width; Bitu height=render.src.height; bool dblw=render.src.dblw; bool dblh=render.src.dblh; double gfx_scalew; double gfx_scaleh; if (width == 0 || height == 0) return; Bitu gfx_flags, xscale, yscale; ScalerSimpleBlock_t *simpleBlock = &ScaleNormal1x; ScalerComplexBlock_t *complexBlock = 0; gfx_scalew = 1; gfx_scaleh = 1; #if !C_XBRZ if (render.aspect == ASPECT_TRUE && !render.aspectOffload) #else if (render.aspect == ASPECT_TRUE && !render.aspectOffload && !(sdl_xbrz.enable && sdl_xbrz.scale_on)) #endif { if (render.src.ratio>1.0) { gfx_scalew = 1; gfx_scaleh = render.src.ratio; } else { gfx_scalew = (1.0/render.src.ratio); gfx_scaleh = 1; } } if ((dblh && dblw) || (render.scale.forced && dblh == dblw/*this branch works best with equal scaling in both directions*/)) { /* Initialize always working defaults */ if (render.scale.size == 2) simpleBlock = &ScaleNormal2x; else if (render.scale.size == 3) simpleBlock = &ScaleNormal3x; else if (render.scale.size == 1 && !(dblh || dblw) && render.scale.hardware) simpleBlock = &ScaleNormal1x; else if (render.scale.size == 4 && !(dblh || dblw) && render.scale.hardware) simpleBlock = &ScaleNormal2x; else if (render.scale.size == 6 && !(dblh || dblw) && render.scale.hardware) simpleBlock = &ScaleNormal3x; else if (render.scale.size == 4 && !render.scale.hardware) simpleBlock = &ScaleNormal4x; else if (render.scale.size == 5 && !render.scale.hardware) simpleBlock = &ScaleNormal5x; else if (render.scale.size == 8 && !(dblh || dblw) && render.scale.hardware) simpleBlock = &ScaleNormal4x; else if (render.scale.size == 10 && !(dblh || dblw) && render.scale.hardware) simpleBlock = &ScaleNormal5x; /* Maybe override them */ #if RENDER_USE_ADVANCED_SCALERS>0 switch (render.scale.op) { #if RENDER_USE_ADVANCED_SCALERS>2 case scalerOpAdvInterp: if (render.scale.size == 2) complexBlock = &ScaleAdvInterp2x; else if (render.scale.size == 3) complexBlock = &ScaleAdvInterp3x; break; case scalerOpAdvMame: if (render.scale.size == 2) complexBlock = &ScaleAdvMame2x; else if (render.scale.size == 3) complexBlock = &ScaleAdvMame3x; break; case scalerOpHQ: if (render.scale.size == 2) complexBlock = &ScaleHQ2x; else if (render.scale.size == 3) complexBlock = &ScaleHQ3x; break; case scalerOpSuperSaI: if (render.scale.size == 2) complexBlock = &ScaleSuper2xSaI; break; case scalerOpSuperEagle: if (render.scale.size == 2) complexBlock = &ScaleSuperEagle; break; case scalerOpSaI: if (render.scale.size == 2) complexBlock = &Scale2xSaI; break; #endif case scalerOpTV: if (render.scale.size == 2) simpleBlock = &ScaleTV2x; else if (render.scale.size == 3) simpleBlock = &ScaleTV3x; break; case scalerOpRGB: if (render.scale.size == 2) simpleBlock = &ScaleRGB2x; else if (render.scale.size == 3) simpleBlock = &ScaleRGB3x; break; case scalerOpScan: if (render.scale.size == 2) simpleBlock = &ScaleScan2x; else if (render.scale.size == 3) simpleBlock = &ScaleScan3x; break; case scalerOpGray: if (render.scale.size == 1){ simpleBlock = &ScaleGrayNormal; }else if (render.scale.size == 2){ simpleBlock = &ScaleGray2x; } break; default: break; } #endif } else if (dblw && !render.scale.hardware) { if(scalerOpGray == render.scale.op){ simpleBlock = &ScaleGrayDw; }else{ if (render.scale.forced && render.scale.size >= 2) simpleBlock = &ScaleNormal2xDw; else simpleBlock = &ScaleNormalDw; } } else if (dblh && !render.scale.hardware) { //Check whether tv2x and scan2x is selected if(scalerOpGray == render.scale.op){ simpleBlock = &ScaleGrayDh; }else if(scalerOpTV == render.scale.op){ simpleBlock = &ScaleTVDh; }else if(scalerOpScan == render.scale.op){ simpleBlock = &ScaleScanDh; }else{ if (render.scale.forced && render.scale.size >= 2) simpleBlock = &ScaleNormal2xDh; else simpleBlock = &ScaleNormalDh; } } else { forcenormal: complexBlock = 0; if(scalerOpGray==render.scale.op){ simpleBlock = &ScaleGrayNormal; }else{ simpleBlock = &ScaleNormal1x; } } if (complexBlock) { #if RENDER_USE_ADVANCED_SCALERS>1 if ((width >= SCALER_COMPLEXWIDTH - 16) || height >= SCALER_COMPLEXHEIGHT - 16) { LOG_MSG("Scaler can't handle this resolution, going back to normal"); goto forcenormal; } #else goto forcenormal; #endif gfx_flags = complexBlock->gfxFlags; xscale = complexBlock->xscale; yscale = complexBlock->yscale; // LOG_MSG("Scaler:%s",complexBlock->name); } else { gfx_flags = simpleBlock->gfxFlags; xscale = simpleBlock->xscale; yscale = simpleBlock->yscale; // LOG_MSG("Scaler:%s",simpleBlock->name); } switch (render.src.bpp) { case 8: render.src.start = ( render.src.width * 1) / sizeof(Bitu); if (gfx_flags & GFX_CAN_8) gfx_flags |= GFX_LOVE_8; else gfx_flags |= GFX_LOVE_32; break; case 15: render.src.start = ( render.src.width * 2) / sizeof(Bitu); gfx_flags |= GFX_LOVE_15; gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY; break; case 16: render.src.start = ( render.src.width * 2) / sizeof(Bitu); gfx_flags |= GFX_LOVE_16; gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY; break; case 32: render.src.start = ( render.src.width * 4) / sizeof(Bitu); gfx_flags |= GFX_LOVE_32; gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY; break; default: render.src.start = ( render.src.width * 1) / sizeof(Bitu); if (gfx_flags & GFX_CAN_8) gfx_flags |= GFX_LOVE_8; else gfx_flags |= GFX_LOVE_32; break; } #if !defined(C_SDL2) gfx_flags=GFX_GetBestMode(gfx_flags); #else gfx_flags &= ~GFX_SCALING; gfx_flags |= GFX_RGBONLY | GFX_CAN_RANDOM; #endif if (!gfx_flags) { if (!complexBlock && simpleBlock == &ScaleNormal1x) E_Exit("Failed to create a rendering output"); else goto forcenormal; } width *= xscale; Bitu skip = complexBlock ? 1 : 0; if (gfx_flags & GFX_SCALING) { if(render.scale.size == 1 && render.scale.hardware) { //hardware_none if(dblh) gfx_scaleh *= 1; if(dblw) gfx_scalew *= 1; } else if(render.scale.size == 4 && render.scale.hardware) { if(dblh) gfx_scaleh *= 2; if(dblw) gfx_scalew *= 2; } else if(render.scale.size == 6 && render.scale.hardware) { if(dblh && dblw) { gfx_scaleh *= 3; gfx_scalew *= 3; } else if(dblh) { gfx_scaleh *= 2; } else if(dblw) gfx_scalew *= 2; } else if(render.scale.size == 8 && render.scale.hardware) { //hardware4x if(dblh) gfx_scaleh *= 4; if(dblw) gfx_scalew *= 4; } else if(render.scale.size == 10 && render.scale.hardware) { //hardware5x if(dblh && dblw) { gfx_scaleh *= 5; gfx_scalew *= 5; } else if(dblh) { gfx_scaleh *= 4; } else if(dblw) gfx_scalew *= 4; } height = MakeAspectTable(skip, render.src.height, (double)yscale, yscale ); } else { // Print a warning when hardware scalers are selected, hopefully the first // video mode will not have dblh or dblw or AR will be wrong if (render.scale.hardware) { LOG_MSG("Output does not support hardware scaling, switching to normal scalers"); render.scale.hardware=false; } if ((gfx_flags & GFX_CAN_RANDOM) && gfx_scaleh > 1) { gfx_scaleh *= yscale; height = MakeAspectTable( skip, render.src.height, gfx_scaleh, yscale ); } else { gfx_flags &= ~GFX_CAN_RANDOM; //Hardware surface when possible height = MakeAspectTable( skip, render.src.height, (double)yscale, yscale); } } /* update the aspect ratio */ sdl.srcAspect.x = (int)(render.src.width * (render.src.dblw ? 2 : 1)); sdl.srcAspect.y = (int)floor((render.src.height * (render.src.dblh ? 2 : 1) * render.src.ratio) + 0.5); sdl.srcAspect.xToY = (double)sdl.srcAspect.x / sdl.srcAspect.y; sdl.srcAspect.yToX = (double)sdl.srcAspect.y / sdl.srcAspect.x; LOG_MSG("Aspect ratio: %u x %u xToY=%.3f yToX=%.3f",sdl.srcAspect.x,sdl.srcAspect.y,sdl.srcAspect.xToY,sdl.srcAspect.yToX); /* Setup the scaler variables */ #if C_OPENGL GFX_SetShader(render.shader_src); #endif gfx_flags=GFX_SetSize(width,height,gfx_flags,gfx_scalew,gfx_scaleh,&RENDER_CallBack); if (gfx_flags & GFX_CAN_8) render.scale.outMode = scalerMode8; else if (gfx_flags & GFX_CAN_15) render.scale.outMode = scalerMode15; else if (gfx_flags & GFX_CAN_16) render.scale.outMode = scalerMode16; else if (gfx_flags & GFX_CAN_32) render.scale.outMode = scalerMode32; else E_Exit("Failed to create a rendering output"); ScalerLineBlock_t *lineBlock; if (gfx_flags & GFX_HARDWARE) { #if RENDER_USE_ADVANCED_SCALERS>1 if (complexBlock) { lineBlock = &ScalerCache; render.scale.complexHandler = complexBlock->Linear[ render.scale.outMode ]; } else #endif { render.scale.complexHandler = 0; lineBlock = &simpleBlock->Linear; } } else { #if RENDER_USE_ADVANCED_SCALERS>1 if (complexBlock) { lineBlock = &ScalerCache; render.scale.complexHandler = complexBlock->Random[ render.scale.outMode ]; } else #endif { render.scale.complexHandler = 0; lineBlock = &simpleBlock->Random; } } switch (render.src.bpp) { case 8: render.scale.lineHandler = (*lineBlock)[0][render.scale.outMode]; render.scale.linePalHandler = (*lineBlock)[4][render.scale.outMode]; render.scale.inMode = scalerMode8; render.scale.cachePitch = render.src.width * 1; break; case 15: render.scale.lineHandler = (*lineBlock)[1][render.scale.outMode]; render.scale.linePalHandler = 0; render.scale.inMode = scalerMode15; render.scale.cachePitch = render.src.width * 2; break; case 16: render.scale.lineHandler = (*lineBlock)[2][render.scale.outMode]; render.scale.linePalHandler = 0; render.scale.inMode = scalerMode16; render.scale.cachePitch = render.src.width * 2; break; case 32: render.scale.lineHandler = (*lineBlock)[3][render.scale.outMode]; render.scale.linePalHandler = 0; render.scale.inMode = scalerMode32; render.scale.cachePitch = render.src.width * 4; break; default: //render.src.bpp=8; render.scale.lineHandler = (*lineBlock)[0][render.scale.outMode]; render.scale.linePalHandler = (*lineBlock)[4][render.scale.outMode]; render.scale.inMode = scalerMode8; render.scale.cachePitch = render.src.width * 1; break; //E_Exit("RENDER:Wrong source bpp %d", render.src.bpp ); } render.scale.blocks = render.src.width / SCALER_BLOCKSIZE; render.scale.lastBlock = render.src.width % SCALER_BLOCKSIZE; render.scale.inHeight = render.src.height; /* Reset the palette change detection to it's initial value */ render.pal.first= 0; render.pal.last = 255; render.pal.changed = false; memset(render.pal.modified, 0, sizeof(render.pal.modified)); //Finish this frame using a copy only handler RENDER_DrawLine = RENDER_FinishLineHandler; render.scale.outWrite = 0; /* Signal the next frame to first reinit the cache */ render.scale.clearCache = true; render.active=true; last_gfx_flags = gfx_flags; } void RENDER_CallBack( GFX_CallBackFunctions_t function ) { if (function == GFX_CallBackStop) { RENDER_Halt( ); return; } else if (function == GFX_CallBackRedraw) { render.scale.clearCache = true; return; } else if ( function == GFX_CallBackReset) { GFX_EndUpdate( 0 ); RENDER_Reset(); } else { E_Exit("Unhandled GFX_CallBackReset %d", function ); } } void RENDER_SetSize(Bitu width,Bitu height,Bitu bpp,float fps,double scrn_ratio) { RENDER_Halt( ); if (!width || !height || width > SCALER_MAXWIDTH || height > SCALER_MAXHEIGHT) { LOG(LOG_MISC,LOG_WARN)("RENDER_SetSize() rejected video mode %u x %u",(unsigned int)width,(unsigned int)height); return; } // figure out doublewidth/height values bool dblw = false; bool dblh = false; double ratio = (((double)width)/((double)height))/scrn_ratio; if(ratio > 1.6) { dblh=true; ratio /= 2.0; } else if(ratio < 0.75) { dblw=true; ratio *= 2.0; } else if(width < 370 && height < 280) { dblw=true; dblh=true; } LOG_MSG("pixratio %1.3f, dw %s, dh %s",ratio,dblw?"true":"false",dblh?"true":"false"); if ( ratio > 1.0 ) { double target = height * ratio + 0.025; ratio = target / height; } else { //This would alter the width of the screen, we don't care about rounding errors here } render.src.width=width; render.src.height=height; render.src.bpp=bpp; render.src.dblw=dblw; render.src.dblh=dblh; render.src.fps=fps; render.src.ratio=ratio; render.src.scrn_ratio=scrn_ratio; RENDER_Reset( ); } /*void BlankDisplay(void); static void BlankTestRefresh(bool pressed) { (void)pressed; BlankDisplay(); }*/ //extern void GFX_SetTitle(Bit32s cycles, Bits frameskip, Bits timing, bool paused); static void IncreaseFrameSkip(bool pressed) { if (!pressed) return; if (render.frameskip.max<10) render.frameskip.max++; LOG_MSG("Frame Skip at %d",(int)render.frameskip.max); GFX_SetTitle(-1,(Bits)render.frameskip.max,-1,false); } static void DecreaseFrameSkip(bool pressed) { if (!pressed) return; if (render.frameskip.max>0) render.frameskip.max--; LOG_MSG("Frame Skip at %d",(int)render.frameskip.max); GFX_SetTitle(-1,(Bits)render.frameskip.max,-1,false); } /* Disabled as I don't want to waste a keybind for that. Might be used in the future (Qbix) static void ChangeScaler(bool pressed) { if (!pressed) return; render.scale.op = (scalerOperation)((int)render.scale.op+1); if((render.scale.op) >= scalerLast || render.scale.size == 1) { render.scale.op = (scalerOperation)0; if(++render.scale.size > 3) render.scale.size = 1; } RENDER_CallBack( GFX_CallBackReset ); } */ #include "vga.h" void RENDER_UpdateFromScalerSetting(void); bool RENDER_GetForceUpdate(void) { return render.forceUpdate; } void RENDER_SetForceUpdate(bool f) { render.forceUpdate = f; } #if C_OPENGL static bool RENDER_GetShader(std::string& shader_path, char *old_src) { char* src; std::stringstream buf; std::ifstream fshader(shader_path.c_str(), std::ios_base::binary); if (!fshader.is_open()) fshader.open((shader_path + ".glsl").c_str(), std::ios_base::binary); if (fshader.is_open()) { buf << fshader.rdbuf(); fshader.close(); } else if (shader_path == "advinterp2x") buf << advinterp2x_glsl; else if (shader_path == "advinterp3x") buf << advinterp3x_glsl; else if (shader_path == "advmame2x") buf << advmame2x_glsl; else if (shader_path == "advmame3x") buf << advmame3x_glsl; else if (shader_path == "rgb2x") buf << rgb2x_glsl; else if (shader_path == "rgb3x") buf << rgb3x_glsl; else if (shader_path == "scan2x") buf << scan2x_glsl; else if (shader_path == "scan3x") buf << scan3x_glsl; else if (shader_path == "tv2x") buf << tv2x_glsl; else if (shader_path == "tv3x") buf << tv3x_glsl; else if (shader_path == "sharp") buf << sharp_glsl; if (!buf.str().empty()) { std::string s = buf.str(); if (first_shell) { std::string pre_defs; Bitu count = first_shell->GetEnvCount(); for (Bitu i=0; i < count; i++) { std::string env; if (!first_shell->GetEnvNum(i, env)) continue; if (env.compare(0, 9, "GLSHADER_")==0) { size_t brk = env.find('='); if (brk == std::string::npos) continue; env[brk] = ' '; pre_defs += "#define " + env.substr(9) + '\n'; } } if (!pre_defs.empty()) { // if "#version" occurs it must be before anything except comments and whitespace size_t pos = buf.str().find("#version "); if (pos != std::string::npos) pos = buf.str().find('\n', pos+9); if (pos == std::string::npos) pos = 0; else ++pos; s = buf.str().insert(pos, pre_defs); } } // keep the same buffer if contents aren't different if (old_src==NULL || s != old_src) { src = strdup(s.c_str()); if (src==NULL) LOG_MSG("WARNING: Couldn't copy shader source"); } else src = old_src; } else src = NULL; render.shader_src = src; return src != NULL; } #endif void RENDER_UpdateFrameskipMenu(void) { char tmp[64]; for (unsigned int f=0;f <= 10;f++) { sprintf(tmp,"frameskip_%u",f); DOSBoxMenu::item &item = mainMenu.get_item(tmp); item.check(render.frameskip.max == f); } } void VGA_SetupDrawing(Bitu /*val*/); void RENDER_UpdateScalerMenu(void); void RENDER_OnSectionPropChange(Section *x) { (void)x;//UNUSED Section_prop * section = static_cast(control->GetSection("render")); bool p_doublescan = vga.draw.doublescan_set; bool p_char9 = vga.draw.char9_set; int p_aspect = render.aspect; std::string s_aspect = section->Get_string("aspect"); render.aspect = ASPECT_FALSE; if (s_aspect == "true" || s_aspect == "1" || s_aspect == "yes") render.aspect = ASPECT_TRUE; #if C_SURFACE_POSTRENDER_ASPECT if (s_aspect == "nearest") render.aspect = ASPECT_NEAREST; if (s_aspect == "bilinear") render.aspect = ASPECT_BILINEAR; #endif render.frameskip.max = (Bitu)section->Get_int("frameskip"); vga.draw.doublescan_set=section->Get_bool("doublescan"); vga.draw.char9_set=section->Get_bool("char9"); if (render.aspect != p_aspect || vga.draw.doublescan_set != p_doublescan || vga.draw.char9_set != p_char9) RENDER_CallBack(GFX_CallBackReset); if (vga.draw.doublescan_set != p_doublescan || vga.draw.char9_set != p_char9) VGA_StartResize(); mainMenu.get_item("vga_9widetext").check(vga.draw.char9_set).refresh_item(mainMenu); mainMenu.get_item("doublescan").check(vga.draw.doublescan_set).refresh_item(mainMenu); mainMenu.get_item("mapper_aspratio").check(render.aspect).refresh_item(mainMenu); #if C_XBRZ xBRZ_Change_Options(section); #endif RENDER_UpdateFrameskipMenu(); RENDER_UpdateFromScalerSetting(); RENDER_UpdateScalerMenu(); } std::string RENDER_GetScaler(void) { Section_prop * section=static_cast(control->GetSection("render")); Prop_multival* prop = section->Get_multival("scaler"); return prop->GetSection()->Get_string("type"); } extern const char *scaler_menu_opts[][2]; void RENDER_UpdateScalerMenu(void) { const std::string scaler = RENDER_GetScaler(); mainMenu.get_item("scaler_forced").check(render.scale.forced).refresh_item(mainMenu); for (size_t i=0;scaler_menu_opts[i][0] != NULL;i++) { const std::string name = std::string("scaler_set_") + scaler_menu_opts[i][0]; mainMenu.get_item(name).check(scaler == scaler_menu_opts[i][0]).refresh_item(mainMenu); } } void RENDER_UpdateFromScalerSetting(void) { Section_prop * section=static_cast(control->GetSection("render")); Prop_multival* prop = section->Get_multival("scaler"); std::string f = prop->GetSection()->Get_string("force"); std::string scaler = prop->GetSection()->Get_string("type"); #if C_XBRZ bool old_xBRZ_enable = sdl_xbrz.enable; sdl_xbrz.enable = false; #endif bool p_forced = render.scale.forced; unsigned int p_size = (unsigned int)(render.scale.size); bool p_hardware = render.scale.hardware; unsigned int p_op = render.scale.op; render.scale.forced = false; if(f == "forced") render.scale.forced = true; if (scaler == "none") { render.scale.op = scalerOpNormal; render.scale.size = 1; render.scale.hardware=false; } else if (scaler == "normal2x") { render.scale.op = scalerOpNormal; render.scale.size = 2; render.scale.hardware=false; } else if (scaler == "normal3x") { render.scale.op = scalerOpNormal; render.scale.size = 3; render.scale.hardware=false; } else if (scaler == "normal4x") { render.scale.op = scalerOpNormal; render.scale.size = 4; render.scale.hardware=false; } else if (scaler == "normal5x") { render.scale.op = scalerOpNormal; render.scale.size = 5; render.scale.hardware=false; } #if RENDER_USE_ADVANCED_SCALERS>2 else if (scaler == "advmame2x") { render.scale.op = scalerOpAdvMame; render.scale.size = 2; render.scale.hardware=false; } else if (scaler == "advmame3x") { render.scale.op = scalerOpAdvMame; render.scale.size = 3; render.scale.hardware=false; } else if (scaler == "advinterp2x") { render.scale.op = scalerOpAdvInterp; render.scale.size = 2; render.scale.hardware=false; } else if (scaler == "advinterp3x") { render.scale.op = scalerOpAdvInterp; render.scale.size = 3; render.scale.hardware=false; } else if (scaler == "hq2x") { render.scale.op = scalerOpHQ; render.scale.size = 2; render.scale.hardware=false; } else if (scaler == "hq3x") { render.scale.op = scalerOpHQ; render.scale.size = 3; render.scale.hardware=false; } else if (scaler == "2xsai") { render.scale.op = scalerOpSaI; render.scale.size = 2; render.scale.hardware=false; } else if (scaler == "super2xsai") { render.scale.op = scalerOpSuperSaI; render.scale.size = 2; render.scale.hardware=false; } else if (scaler == "supereagle") { render.scale.op = scalerOpSuperEagle; render.scale.size = 2; render.scale.hardware=false; } #endif #if RENDER_USE_ADVANCED_SCALERS>0 else if (scaler == "tv2x") { render.scale.op = scalerOpTV; render.scale.size = 2; render.scale.hardware=false; } else if (scaler == "tv3x") { render.scale.op = scalerOpTV; render.scale.size = 3; render.scale.hardware=false; } else if (scaler == "rgb2x"){ render.scale.op = scalerOpRGB; render.scale.size = 2; render.scale.hardware=false; } else if (scaler == "rgb3x"){ render.scale.op = scalerOpRGB; render.scale.size = 3; render.scale.hardware=false; } else if (scaler == "scan2x"){ render.scale.op = scalerOpScan; render.scale.size = 2; render.scale.hardware=false; } else if (scaler == "scan3x"){ render.scale.op = scalerOpScan; render.scale.size = 3; render.scale.hardware=false; } else if (scaler == "gray"){ render.scale.op = scalerOpGray; render.scale.size = 1; render.scale.hardware=false; } else if (scaler == "gray2x"){ render.scale.op = scalerOpGray; render.scale.size = 2; render.scale.hardware=false; } #endif else if (scaler == "hardware_none") { render.scale.op = scalerOpNormal; render.scale.size = 1; render.scale.hardware=true; } else if (scaler == "hardware2x") { render.scale.op = scalerOpNormal; render.scale.size = 4; render.scale.hardware=true; } else if (scaler == "hardware3x") { render.scale.op = scalerOpNormal; render.scale.size = 6; render.scale.hardware=true; } else if (scaler == "hardware4x") { render.scale.op = scalerOpNormal; render.scale.size = 8; render.scale.hardware=true; } else if (scaler == "hardware5x") { render.scale.op = scalerOpNormal; render.scale.size = 10; render.scale.hardware=true; } #if C_XBRZ else if (scaler == "xbrz" || scaler == "xbrz_bilinear") { render.scale.op = scalerOpNormal; render.scale.size = 1; render.scale.hardware = false; vga.draw.doublescan_set = false; sdl_xbrz.enable = true; sdl_xbrz.postscale_bilinear = (scaler == "xbrz_bilinear"); } #endif bool reset = false; #if C_XBRZ if (old_xBRZ_enable != sdl_xbrz.enable) reset = true; #endif if (p_forced != render.scale.forced) reset = true; if (p_size != render.scale.size) reset = true; if (p_hardware != render.scale.hardware) reset = true; if (p_op != render.scale.op) reset = true; if (reset) RENDER_CallBack(GFX_CallBackReset); } void RENDER_Init() { Section_prop * section=static_cast(control->GetSection("render")); LOG(LOG_MISC,LOG_DEBUG)("Initializing renderer"); control->GetSection("render")->onpropchange.push_back(&RENDER_OnSectionPropChange); vga.draw.doublescan_set=section->Get_bool("doublescan"); vga.draw.char9_set=section->Get_bool("char9"); eurAscii = section->Get_int("euro"); if (eurAscii != -1 && (eurAscii < 33 || eurAscii > 255)) { LOG_MSG("Euro ASCII value has to be between 33 and 255\n"); eurAscii = -1; } //Set monochrome mode color and brightness vga.draw.monochrome_pal=0; vga.draw.monochrome_bright=1; Prop_multival* prop = section->Get_multival("monochrome_pal"); std::string s_bright = prop->GetSection()->Get_string("bright"); std::string s_color = prop->GetSection()->Get_string("color"); LOG_MSG("monopal: %s, %s", s_color.c_str(), s_bright.c_str()); if("bright"==s_bright){ vga.draw.monochrome_bright=0; } if("green"==s_color){ vga.draw.monochrome_pal=0; }else if("amber"==s_color){ vga.draw.monochrome_pal=1; }else if("gray"==s_color){ vga.draw.monochrome_pal=2; }else if("white"==s_color){ vga.draw.monochrome_pal=3; } //For restarting the renderer. static bool running = false; int aspect = render.aspect; Bitu scalersize = render.scale.size; bool scalerforced = render.scale.forced; scalerOperation_t scaleOp = render.scale.op; render.scale.cacheRead = NULL; render.scale.outWrite = NULL; render.pal.first=0; render.pal.last=255; std::string s_aspect = section->Get_string("aspect"); render.aspect = ASPECT_FALSE; if (s_aspect == "true" || s_aspect == "1") render.aspect = ASPECT_TRUE; #if C_SURFACE_POSTRENDER_ASPECT if (s_aspect == "nearest") render.aspect = ASPECT_NEAREST; if (s_aspect == "bilinear") render.aspect = ASPECT_BILINEAR; #endif render.frameskip.max=(Bitu)section->Get_int("frameskip"); MAPPER_AddHandler(DecreaseFrameSkip,MK_nothing,0,"decfskip","Dec Fskip"); MAPPER_AddHandler(IncreaseFrameSkip,MK_nothing,0,"incfskip","Inc Fskip"); DOSBoxMenu::item *item; MAPPER_AddHandler(&AspectRatio_mapper_shortcut, MK_nothing, 0, "aspratio", "AspRatio", &item); item->set_text("Fit to aspect ratio"); // DEBUG option mainMenu.alloc_item(DOSBoxMenu::item_type_id,"debug_blankrefreshtest"); mainMenu.get_item("vga_9widetext").check(vga.draw.char9_set).refresh_item(mainMenu); mainMenu.get_item("doublescan").check(vga.draw.doublescan_set).refresh_item(mainMenu); mainMenu.get_item("mapper_aspratio").check(render.aspect).refresh_item(mainMenu); RENDER_UpdateFrameskipMenu(); /* BUG FIX: Some people's dosbox.conf files have frameskip=-1 WTF?? */ /* without this fix, nothing displays, EVER */ if ((int)render.frameskip.max < 0) render.frameskip.max = 0; render.frameskip.count=0; render.forceUpdate=false; std::string cline; std::string scaler; //Check for commandline paramters and parse them through the configclass so they get checked against allowed values if (control->cmdline->FindString("-scaler",cline,true)) { section->HandleInputline(std::string("scaler=") + cline); } else if (control->cmdline->FindString("-forcescaler",cline,true)) { section->HandleInputline(std::string("scaler=") + cline + " forced"); } RENDER_UpdateFromScalerSetting(); vga_alt_new_mode = control->opt_alt_vga_render || section->Get_bool("alt render"); if (vga_alt_new_mode) LOG_MSG("Alternative VGA render engine not yet fully implemented!"); render.autofit=section->Get_bool("autofit"); #if C_OPENGL char* shader_src = render.shader_src; Prop_path *sh = section->Get_path("glshader"); std::string f = (std::string)sh->GetValue(); if (f.empty() || f=="none") render.shader_src = NULL; else if (!RENDER_GetShader(sh->realpath,shader_src)) { std::string path; Cross::GetPlatformConfigDir(path); path = path + "glshaders" + CROSS_FILESPLIT + f; if (!RENDER_GetShader(path,shader_src) && (sh->realpath==f || !RENDER_GetShader(f,shader_src))) { sh->SetValue("none"); LOG_MSG("Shader file \"%s\" not found", f.c_str()); } } else LOG_MSG("Loaded GLSL shader: %s\n", f.c_str()); if (shader_src!=render.shader_src) free(shader_src); #endif //If something changed that needs a ReInit // Only ReInit when there is a src.bpp (fixes crashes on startup and directly changing the scaler without a screen specified yet) if(running && render.src.bpp && ((render.aspect != aspect) || (render.scale.op != scaleOp) || (render.scale.size != scalersize) || (render.scale.forced != scalerforced) || #if C_OPENGL (render.shader_src != shader_src) || #endif render.scale.forced)) RENDER_CallBack( GFX_CallBackReset ); if(!running) render.updating=true; running = true; GFX_SetTitle(-1,(Bits)render.frameskip.max,-1,false); RENDER_UpdateScalerMenu(); } //save state support namespace { class SerializeRender : public SerializeGlobalPOD { public: SerializeRender() : SerializeGlobalPOD("Render") {} private: virtual void getBytes(std::ostream& stream) { SerializeGlobalPOD::getBytes(stream); // - pure data WRITE_POD( &render.src, render.src ); WRITE_POD( &render.pal, render.pal ); WRITE_POD( &render.updating, render.updating ); WRITE_POD( &render.active, render.active ); WRITE_POD( &render.fullFrame, render.fullFrame ); WRITE_POD( &render.frameskip, render.frameskip ); WRITE_POD( &render.aspect, render.aspect ); WRITE_POD( &render.scale, render.scale ); } virtual void setBytes(std::istream& stream) { SerializeGlobalPOD::setBytes(stream); // - pure data READ_POD( &render.src, render.src ); READ_POD( &render.pal, render.pal ); READ_POD( &render.updating, render.updating ); READ_POD( &render.active, render.active ); READ_POD( &render.fullFrame, render.fullFrame ); READ_POD( &render.frameskip, render.frameskip ); READ_POD( &render.aspect, render.aspect ); READ_POD( &render.scale, render.scale ); //*************************************** //*************************************** // reset screen //memset( &render.frameskip, 0, sizeof(render.frameskip) ); if (render.aspect==ASPECT_FALSE) { render.scale.clearCache = true; if( render.scale.outWrite ) { GFX_EndUpdate(NULL); } RENDER_SetSize( render.src.width, render.src.height, render.src.bpp, render.src.fps, render.src.ratio ); } else GFX_ResetScreen(); } } dummy; }