1 /*
2  *  Copyright (C) 2002-2021  The DOSBox Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "dosbox.h"
20 
21 #include <cassert>
22 #include <cmath>
23 #include <cstdlib>
24 #include <fstream>
25 #include <sstream>
26 #include <unordered_map>
27 
28 #include <sys/types.h>
29 
30 #include "video.h"
31 #include "render.h"
32 #include "setup.h"
33 #include "control.h"
34 #include "mapper.h"
35 #include "cross.h"
36 #include "hardware.h"
37 #include "support.h"
38 #include "shell.h"
39 #include "string_utils.h"
40 #include "vga.h"
41 
42 #include "render_crt_glsl.h"
43 #include "render_glsl.h"
44 #include "render_scalers.h"
45 
46 Render_t render;
47 ScalerLineHandler_t RENDER_DrawLine;
48 
49 static void RENDER_CallBack( GFX_CallBackFunctions_t function );
50 
Check_Palette(void)51 static void Check_Palette(void) {
52 	/* Clean up any previous changed palette data */
53 	if (render.pal.changed) {
54 		memset(render.pal.modified, 0, sizeof(render.pal.modified));
55 		render.pal.changed = false;
56 	}
57 	if (render.pal.first>render.pal.last)
58 		return;
59 	Bitu i;
60 	switch (render.scale.outMode) {
61 	case scalerMode8:
62 		break;
63 	case scalerMode15:
64 	case scalerMode16:
65 		for (i=render.pal.first;i<=render.pal.last;i++) {
66 			Bit8u r=render.pal.rgb[i].red;
67 			Bit8u g=render.pal.rgb[i].green;
68 			Bit8u b=render.pal.rgb[i].blue;
69 			Bit16u newPal = GFX_GetRGB(r,g,b);
70 			if (newPal != render.pal.lut.b16[i]) {
71 				render.pal.changed = true;
72 				render.pal.modified[i] = 1;
73 				render.pal.lut.b16[i] = newPal;
74 			}
75 		}
76 		break;
77 	case scalerMode32:
78 	default:
79 		for (i=render.pal.first;i<=render.pal.last;i++) {
80 			Bit8u r=render.pal.rgb[i].red;
81 			Bit8u g=render.pal.rgb[i].green;
82 			Bit8u b=render.pal.rgb[i].blue;
83 			Bit32u newPal = GFX_GetRGB(r,g,b);
84 			if (newPal != render.pal.lut.b32[i]) {
85 				render.pal.changed = true;
86 				render.pal.modified[i] = 1;
87 				render.pal.lut.b32[i] = newPal;
88 			}
89 		}
90 		break;
91 	}
92 	/* Setup pal index to startup values */
93 	render.pal.first=256;
94 	render.pal.last=0;
95 }
96 
RENDER_SetPal(Bit8u entry,Bit8u red,Bit8u green,Bit8u blue)97 void RENDER_SetPal(Bit8u entry,Bit8u red,Bit8u green,Bit8u blue) {
98 	render.pal.rgb[entry].red=red;
99 	render.pal.rgb[entry].green=green;
100 	render.pal.rgb[entry].blue=blue;
101 	if (render.pal.first>entry) render.pal.first=entry;
102 	if (render.pal.last<entry) render.pal.last=entry;
103 }
104 
RENDER_EmptyLineHandler(const void *)105 static void RENDER_EmptyLineHandler(const void *)
106 {}
107 
RENDER_StartLineHandler(const void * s)108 static void RENDER_StartLineHandler(const void * s) {
109 	if (s) {
110 		const Bitu *src = (Bitu*)s;
111 		Bitu *cache = (Bitu*)(render.scale.cacheRead);
112 		for (Bits x=render.src.start;x>0;) {
113 			const auto src_ptr = reinterpret_cast<const uint8_t *>(src);
114 			const auto src_val = read_unaligned_size_t(src_ptr);
115 			if (GCC_UNLIKELY(src_val != cache[0])) {
116 				if (!GFX_StartUpdate(render.scale.outWrite, render.scale.outPitch)) {
117 					RENDER_DrawLine = RENDER_EmptyLineHandler;
118 					return;
119 				}
120 				render.scale.outWrite += render.scale.outPitch * Scaler_ChangedLines[0];
121 				RENDER_DrawLine = render.scale.lineHandler;
122 				RENDER_DrawLine( s );
123 				return;
124 			}
125 			x--; src++; cache++;
126 		}
127 	}
128 	render.scale.cacheRead += render.scale.cachePitch;
129 	Scaler_ChangedLines[0] += Scaler_Aspect[ render.scale.inLine ];
130 	render.scale.inLine++;
131 	render.scale.outLine++;
132 }
133 
RENDER_FinishLineHandler(const void * s)134 static void RENDER_FinishLineHandler(const void * s) {
135 	if (s) {
136 		const Bitu *src = (Bitu*)s;
137 		Bitu *cache = (Bitu*)(render.scale.cacheRead);
138 		for (Bits x=render.src.start;x>0;) {
139 			cache[0] = src[0];
140 			x--; src++; cache++;
141 		}
142 	}
143 	render.scale.cacheRead += render.scale.cachePitch;
144 }
145 
146 
RENDER_ClearCacheHandler(const void * src)147 static void RENDER_ClearCacheHandler(const void * src) {
148 	Bitu x, width;
149 	Bit32u *srcLine, *cacheLine;
150 	srcLine = (Bit32u *)src;
151 	cacheLine = (Bit32u *)render.scale.cacheRead;
152 	width = render.scale.cachePitch / 4;
153 	for (x=0;x<width;x++)
154 		cacheLine[x] = ~srcLine[x];
155 	render.scale.lineHandler( src );
156 }
157 
RENDER_StartUpdate(void)158 bool RENDER_StartUpdate(void) {
159 	if (GCC_UNLIKELY(render.updating))
160 		return false;
161 	if (GCC_UNLIKELY(!render.active))
162 		return false;
163 	if (GCC_UNLIKELY(render.frameskip.count<render.frameskip.max)) {
164 		render.frameskip.count++;
165 		return false;
166 	}
167 	render.frameskip.count=0;
168 	if (render.scale.inMode == scalerMode8) {
169 		Check_Palette();
170 	}
171 	render.scale.inLine = 0;
172 	render.scale.outLine = 0;
173 	render.scale.cacheRead = (Bit8u*)&scalerSourceCache;
174 	render.scale.outWrite = 0;
175 	render.scale.outPitch = 0;
176 	Scaler_ChangedLines[0] = 0;
177 	Scaler_ChangedLineIndex = 0;
178 	/* Clearing the cache will first process the line to make sure it's never the same */
179 	if (GCC_UNLIKELY( render.scale.clearCache) ) {
180 //		LOG_MSG("Clearing cache");
181 		//Will always have to update the screen with this one anyway, so let's update already
182 		if (GCC_UNLIKELY(!GFX_StartUpdate( render.scale.outWrite, render.scale.outPitch )))
183 			return false;
184 		render.fullFrame = true;
185 		render.scale.clearCache = false;
186 		RENDER_DrawLine = RENDER_ClearCacheHandler;
187 	} else {
188 		if (render.pal.changed) {
189 			/* Assume pal changes always do a full screen update anyway */
190 			if (GCC_UNLIKELY(!GFX_StartUpdate( render.scale.outWrite, render.scale.outPitch )))
191 				return false;
192 			RENDER_DrawLine = render.scale.linePalHandler;
193 			render.fullFrame = true;
194 		} else {
195 			RENDER_DrawLine = RENDER_StartLineHandler;
196 			if (GCC_UNLIKELY(CaptureState & (CAPTURE_IMAGE|CAPTURE_VIDEO)))
197 				render.fullFrame = true;
198 			else
199 				render.fullFrame = false;
200 		}
201 	}
202 	render.updating = true;
203 	return true;
204 }
205 
RENDER_Halt(void)206 static void RENDER_Halt( void ) {
207 	RENDER_DrawLine = RENDER_EmptyLineHandler;
208 	GFX_EndUpdate( 0 );
209 	render.updating=false;
210 	render.active=false;
211 }
212 
213 extern uint32_t PIC_Ticks;
RENDER_EndUpdate(bool abort)214 void RENDER_EndUpdate( bool abort ) {
215 	if (GCC_UNLIKELY(!render.updating))
216 		return;
217 	RENDER_DrawLine = RENDER_EmptyLineHandler;
218 	if (GCC_UNLIKELY(CaptureState & (CAPTURE_IMAGE|CAPTURE_VIDEO))) {
219 		Bitu pitch, flags;
220 		flags = 0;
221 		if (render.src.dblw != render.src.dblh) {
222 			if (render.src.dblw) flags|=CAPTURE_FLAG_DBLW;
223 			if (render.src.dblh) flags|=CAPTURE_FLAG_DBLH;
224 		}
225 		auto fps = render.src.fps;
226 		pitch = render.scale.cachePitch;
227 		if (render.frameskip.max) {
228 			const double fps_skip = 1 + render.frameskip.max;
229 			fps /= fps_skip;
230 		}
231 		CAPTURE_AddImage(render.src.width, render.src.height, render.src.bpp,
232 		                 pitch, flags, static_cast<float>(fps), (Bit8u *)&scalerSourceCache,
233 		                 (Bit8u *)&render.pal.rgb);
234 	}
235 	if ( render.scale.outWrite ) {
236 		GFX_EndUpdate( abort? NULL : Scaler_ChangedLines );
237 		render.frameskip.hadSkip[render.frameskip.index] = 0;
238 	} else {
239 #if 0
240 		Bitu total = 0, i;
241 		render.frameskip.hadSkip[render.frameskip.index] = 1;
242 		for (i = 0;i<RENDER_SKIP_CACHE;i++)
243 			total += render.frameskip.hadSkip[i];
244 		LOG_MSG( "Skipped frame %d %d", PIC_Ticks, (total * 100) / RENDER_SKIP_CACHE );
245 #endif
246 		if (RENDER_GetForceUpdate()) GFX_EndUpdate(0);
247 	}
248 	render.frameskip.index = (render.frameskip.index + 1) & (RENDER_SKIP_CACHE - 1);
249 	render.updating=false;
250 }
251 
MakeAspectTable(Bitu skip,Bitu height,double scaley,Bitu miny)252 static Bitu MakeAspectTable(Bitu skip,Bitu height,double scaley,Bitu miny) {
253 	Bitu i;
254 	double lines=0;
255 	Bitu linesadded=0;
256 	for (i=0;i<skip;i++)
257 		Scaler_Aspect[i] = 0;
258 
259 	height += skip;
260 	for (i=skip;i<height;i++) {
261 		lines += scaley;
262 		if (lines >= miny) {
263 			Bitu templines = (Bitu)lines;
264 			lines -= templines;
265 			linesadded += templines;
266 			Scaler_Aspect[i] = templines;
267 		} else {
268 			Scaler_Aspect[i] = 0;
269 		}
270 	}
271 	return linesadded;
272 }
273 
274 
RENDER_Reset(void)275 static void RENDER_Reset( void ) {
276 	Bitu width=render.src.width;
277 	Bitu height=render.src.height;
278 	bool dblw=render.src.dblw;
279 	bool dblh=render.src.dblh;
280 
281 	double gfx_scalew;
282 	double gfx_scaleh;
283 
284 	Bitu gfx_flags, xscale, yscale;
285 	ScalerSimpleBlock_t		*simpleBlock = &ScaleNormal1x;
286 	ScalerComplexBlock_t	*complexBlock = 0;
287 	if (render.aspect) {
288 		if (render.src.ratio>1.0) {
289 			gfx_scalew = 1;
290 			gfx_scaleh = render.src.ratio;
291 		} else {
292 			gfx_scalew = (1/render.src.ratio);
293 			gfx_scaleh = 1;
294 		}
295 	} else {
296 		gfx_scalew = 1;
297 		gfx_scaleh = 1;
298 	}
299 
300 	/* Don't do software scaler sizes larger than 4k */
301 	Bitu maxsize_current_input = SCALER_MAXLINE_WIDTH/width;
302 	if (render.scale.size > maxsize_current_input) render.scale.size = maxsize_current_input;
303 
304 	if ((dblh && dblw) || (render.scale.forced && !dblh && !dblw)) {
305 		/* Initialize always working defaults */
306 		if (render.scale.size == 2)
307 			simpleBlock = &ScaleNormal2x;
308 		else if (render.scale.size == 3)
309 			simpleBlock = &ScaleNormal3x;
310 		else
311 			simpleBlock = &ScaleNormal1x;
312 		/* Maybe override them */
313 #if RENDER_USE_ADVANCED_SCALERS>0
314 		switch (render.scale.op) {
315 #if RENDER_USE_ADVANCED_SCALERS>2
316 		case scalerOpAdvInterp:
317 			if (render.scale.size == 2)
318 				complexBlock = &ScaleAdvInterp2x;
319 			else if (render.scale.size == 3)
320 				complexBlock = &ScaleAdvInterp3x;
321 			break;
322 		case scalerOpAdvMame:
323 			if (render.scale.size == 2)
324 				complexBlock = &ScaleAdvMame2x;
325 			else if (render.scale.size == 3)
326 				complexBlock = &ScaleAdvMame3x;
327 			break;
328 		case scalerOpHQ:
329 			if (render.scale.size == 2)
330 				complexBlock = &ScaleHQ2x;
331 			else if (render.scale.size == 3)
332 				complexBlock = &ScaleHQ3x;
333 			break;
334 		case scalerOpSuperSaI:
335 			if (render.scale.size == 2)
336 				complexBlock = &ScaleSuper2xSaI;
337 			break;
338 		case scalerOpSuperEagle:
339 			if (render.scale.size == 2)
340 				complexBlock = &ScaleSuperEagle;
341 			break;
342 		case scalerOpSaI:
343 			if (render.scale.size == 2)
344 				complexBlock = &Scale2xSaI;
345 			break;
346 #endif
347 		case scalerOpTV:
348 			if (render.scale.size == 2)
349 				simpleBlock = &ScaleTV2x;
350 			else if (render.scale.size == 3)
351 				simpleBlock = &ScaleTV3x;
352 			break;
353 		case scalerOpRGB:
354 			if (render.scale.size == 2)
355 				simpleBlock = &ScaleRGB2x;
356 			else if (render.scale.size == 3)
357 				simpleBlock = &ScaleRGB3x;
358 			break;
359 		case scalerOpScan:
360 			if (render.scale.size == 2)
361 				simpleBlock = &ScaleScan2x;
362 			else if (render.scale.size == 3)
363 				simpleBlock = &ScaleScan3x;
364 			break;
365 		default:
366 			break;
367 		}
368 #endif
369 	} else if (dblw) {
370 		simpleBlock = &ScaleNormalDw;
371 		if (width * simpleBlock->xscale > SCALER_MAXLINE_WIDTH) {
372 			// This should only happen if you pick really bad values... but might be worth adding selecting a scaler that fits
373 			simpleBlock = &ScaleNormal1x;
374 		}
375 	} else if (dblh) {
376 		simpleBlock = &ScaleNormalDh;
377 	} else  {
378 forcenormal:
379 		complexBlock = 0;
380 		simpleBlock = &ScaleNormal1x;
381 	}
382 	if (complexBlock) {
383 #if RENDER_USE_ADVANCED_SCALERS>1
384 		if ((width >= SCALER_COMPLEXWIDTH - 16) || height >= SCALER_COMPLEXHEIGHT - 16) {
385 			LOG_MSG("Scaler can't handle this resolution, going back to normal");
386 			goto forcenormal;
387 		}
388 #else
389 		goto forcenormal;
390 #endif
391 		gfx_flags = complexBlock->gfxFlags;
392 		xscale = complexBlock->xscale;
393 		yscale = complexBlock->yscale;
394 //		LOG_MSG("Scaler:%s",complexBlock->name);
395 	} else {
396 		gfx_flags = simpleBlock->gfxFlags;
397 		xscale = simpleBlock->xscale;
398 		yscale = simpleBlock->yscale;
399 //		LOG_MSG("Scaler:%s",simpleBlock->name);
400 	}
401 	switch (render.src.bpp) {
402 	case 8:
403 			render.src.start = ( render.src.width * 1) / sizeof(Bitu);
404 			if (gfx_flags & GFX_CAN_8)
405 				gfx_flags |= GFX_LOVE_8;
406 			else
407 				gfx_flags |= GFX_LOVE_32;
408 			break;
409 	case 15:
410 			render.src.start = ( render.src.width * 2) / sizeof(Bitu);
411 			gfx_flags |= GFX_LOVE_15;
412 			gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY;
413 			break;
414 	case 16:
415 			render.src.start = ( render.src.width * 2) / sizeof(Bitu);
416 			gfx_flags |= GFX_LOVE_16;
417 			gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY;
418 			break;
419 	case 24:
420 		render.src.start = (render.src.width * 3) / sizeof(Bitu);
421 		gfx_flags |= GFX_LOVE_32;
422 		gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY;
423 		break;
424 	case 32:
425 		render.src.start = (render.src.width * 4) / sizeof(Bitu);
426 		gfx_flags |= GFX_LOVE_32;
427 		gfx_flags = (gfx_flags & ~GFX_CAN_8) | GFX_RGBONLY;
428 		break;
429 	}
430 	gfx_flags=GFX_GetBestMode(gfx_flags);
431 	if (!gfx_flags) {
432 		if (!complexBlock && simpleBlock == &ScaleNormal1x)
433 			E_Exit("Failed to create a rendering output");
434 		else
435 			goto forcenormal;
436 	}
437 	width *= xscale;
438 	Bitu skip = complexBlock ? 1 : 0;
439 	if (gfx_flags & GFX_SCALING) {
440 		height = MakeAspectTable(skip, render.src.height, yscale, yscale );
441 	} else {
442 		if ((gfx_flags & GFX_CAN_RANDOM) && gfx_scaleh > 1) {
443 			gfx_scaleh *= yscale;
444 			height = MakeAspectTable( skip, render.src.height, gfx_scaleh, yscale );
445 		} else {
446 			gfx_flags &= ~GFX_CAN_RANDOM;		//Hardware surface when possible
447 			height = MakeAspectTable( skip, render.src.height, yscale, yscale);
448 		}
449 	}
450 
451 	// Setup the scaler variables
452 
453 	if (dblh)
454 		gfx_flags |= GFX_DBL_H;
455 	if (dblw)
456 		gfx_flags |= GFX_DBL_W;
457 
458 #if C_OPENGL
459 	GFX_SetShader(render.shader_src);
460 #endif
461 
462 	// The pixel aspect ratio of the source image, assuming 4:3 screen
463 	const double real_par = (width / 4.0) / (height / 3.0);
464 	const double user_par = (render.aspect ? real_par : 1.0);
465 
466 	gfx_flags = GFX_SetSize(width, height, gfx_flags, gfx_scalew,
467 	                        gfx_scaleh, &RENDER_CallBack, user_par);
468 
469 	if (gfx_flags & GFX_CAN_8)
470 		render.scale.outMode = scalerMode8;
471 	else if (gfx_flags & GFX_CAN_15)
472 		render.scale.outMode = scalerMode15;
473 	else if (gfx_flags & GFX_CAN_16)
474 		render.scale.outMode = scalerMode16;
475 	else if (gfx_flags & GFX_CAN_32)
476 		render.scale.outMode = scalerMode32;
477 	else
478 		E_Exit("Failed to create a rendering output");
479 	ScalerLineBlock_t *lineBlock;
480 	if (gfx_flags & GFX_HARDWARE) {
481 #if RENDER_USE_ADVANCED_SCALERS>1
482 		if (complexBlock) {
483 			lineBlock = &ScalerCache;
484 			render.scale.complexHandler = complexBlock->Linear[ render.scale.outMode ];
485 		} else
486 #endif
487 		{
488 			render.scale.complexHandler = 0;
489 			lineBlock = &simpleBlock->Linear;
490 		}
491 	} else {
492 #if RENDER_USE_ADVANCED_SCALERS>1
493 		if (complexBlock) {
494 			lineBlock = &ScalerCache;
495 			render.scale.complexHandler = complexBlock->Random[ render.scale.outMode ];
496 		} else
497 #endif
498 		{
499 			render.scale.complexHandler = 0;
500 			lineBlock = &simpleBlock->Random;
501 		}
502 	}
503 	switch (render.src.bpp) {
504 	case 8:
505 		render.scale.lineHandler = (*lineBlock)[0][render.scale.outMode];
506 		render.scale.linePalHandler = (*lineBlock)[5][render.scale.outMode];
507 		render.scale.inMode = scalerMode8;
508 		render.scale.cachePitch = render.src.width * 1;
509 		break;
510 	case 15:
511 		render.scale.lineHandler = (*lineBlock)[1][render.scale.outMode];
512 		render.scale.linePalHandler = 0;
513 		render.scale.inMode = scalerMode15;
514 		render.scale.cachePitch = render.src.width * 2;
515 		break;
516 	case 16:
517 		render.scale.lineHandler = (*lineBlock)[2][render.scale.outMode];
518 		render.scale.linePalHandler = 0;
519 		render.scale.inMode = scalerMode16;
520 		render.scale.cachePitch = render.src.width * 2;
521 		break;
522 	case 24:
523 		render.scale.lineHandler = (*lineBlock)[3][render.scale.outMode];
524 		render.scale.linePalHandler = 0;
525 		render.scale.inMode = scalerMode32;
526 		render.scale.cachePitch = render.src.width * 3;
527 		break;
528 	case 32:
529 		render.scale.lineHandler = (*lineBlock)[4][render.scale.outMode];
530 		render.scale.linePalHandler = 0;
531 		render.scale.inMode = scalerMode32;
532 		render.scale.cachePitch = render.src.width * 4;
533 		break;
534 	default:
535 		E_Exit("RENDER:Wrong source bpp %u", render.src.bpp);
536 	}
537 	render.scale.blocks = render.src.width / SCALER_BLOCKSIZE;
538 	render.scale.lastBlock = render.src.width % SCALER_BLOCKSIZE;
539 	render.scale.inHeight = render.src.height;
540 	/* Reset the palette change detection to it's initial value */
541 	render.pal.first= 0;
542 	render.pal.last = 255;
543 	render.pal.changed = false;
544 	memset(render.pal.modified, 0, sizeof(render.pal.modified));
545 	//Finish this frame using a copy only handler
546 	RENDER_DrawLine = RENDER_FinishLineHandler;
547 	render.scale.outWrite = 0;
548 	/* Signal the next frame to first reinit the cache */
549 	render.scale.clearCache = true;
550 	render.active=true;
551 }
552 
RENDER_CallBack(GFX_CallBackFunctions_t function)553 static void RENDER_CallBack( GFX_CallBackFunctions_t function ) {
554 	if (function == GFX_CallBackStop) {
555 		RENDER_Halt( );
556 		return;
557 	} else if (function == GFX_CallBackRedraw) {
558 		render.scale.clearCache = true;
559 		return;
560 	} else if ( function == GFX_CallBackReset) {
561 		GFX_EndUpdate( 0 );
562 		RENDER_Reset();
563 	} else {
564 		E_Exit("Unhandled GFX_CallBackReset %d", function );
565 	}
566 }
567 
RENDER_SetSize(uint32_t width,uint32_t height,unsigned bpp,double fps,double ratio,bool dblw,bool dblh)568 void RENDER_SetSize(uint32_t width,
569                     uint32_t height,
570                     unsigned bpp,
571                     double fps,
572                     double ratio,
573                     bool dblw,
574                     bool dblh)
575 {
576 	RENDER_Halt( );
577 	if (!width || !height || width > SCALER_MAXWIDTH || height > SCALER_MAXHEIGHT) {
578 		return;
579 	}
580 	if ( ratio > 1 ) {
581 		double target = height * ratio + 0.025;
582 		ratio = target / height;
583 	} else {
584 		//This would alter the width of the screen, we don't care about rounding errors here
585 	}
586 	render.src.width = width;
587 	render.src.height = height;
588 	render.src.bpp = bpp;
589 	render.src.dblw = dblw;
590 	render.src.dblh = dblh;
591 	render.src.fps = fps;
592 	render.src.ratio = ratio;
593 	RENDER_Reset( );
594 }
595 
596 extern void GFX_SetTitle(Bit32s cycles, int frameskip,bool paused);
IncreaseFrameSkip(bool pressed)597 static void IncreaseFrameSkip(bool pressed) {
598 	if (!pressed)
599 		return;
600 	if (render.frameskip.max<10) render.frameskip.max++;
601 	LOG_MSG("Frame Skip at %d",render.frameskip.max);
602 	GFX_SetTitle(-1,render.frameskip.max,false);
603 }
604 
DecreaseFrameSkip(bool pressed)605 static void DecreaseFrameSkip(bool pressed) {
606 	if (!pressed)
607 		return;
608 	if (render.frameskip.max>0) render.frameskip.max--;
609 	LOG_MSG("Frame Skip at %d",render.frameskip.max);
610 	GFX_SetTitle(-1,render.frameskip.max,false);
611 }
612 /* Disabled as I don't want to waste a keybind for that. Might be used in the future (Qbix)
613 static void ChangeScaler(bool pressed) {
614 	if (!pressed)
615 		return;
616 	render.scale.op = (scalerOperation)((int)render.scale.op+1);
617 	if((render.scale.op) >= scalerLast || render.scale.size == 1) {
618 		render.scale.op = (scalerOperation)0;
619 		if(++render.scale.size > 3)
620 			render.scale.size = 1;
621 	}
622 	RENDER_CallBack( GFX_CallBackReset );
623 } */
624 
RENDER_GetForceUpdate(void)625 bool RENDER_GetForceUpdate(void) {
626 	return render.forceUpdate;
627 }
628 
RENDER_SetForceUpdate(bool f)629 void RENDER_SetForceUpdate(bool f) {
630 	render.forceUpdate = f;
631 }
632 
633 #if C_OPENGL
RENDER_GetShader(std::string & shader_path,char * old_src)634 static bool RENDER_GetShader(std::string &shader_path, char *old_src)
635 {
636 	const std::unordered_map<std::string, const char *> builtin_shaders = {
637 	        {"advinterp2x", advinterp2x_glsl},
638 	        {"advinterp3x", advinterp3x_glsl},
639 	        {"advmame2x", advmame2x_glsl},
640 	        {"advmame3x", advmame3x_glsl},
641 	        {"crt-easymode-flat", crt_easymode_tweaked_glsl},
642 	        {"crt-fakelottes-flat", crt_fakelottes_tweaked_glsl},
643 	        {"default", sharp_glsl},
644 	        {"rgb2x", rgb2x_glsl},
645 	        {"rgb3x", rgb3x_glsl},
646 	        {"scan2x", scan2x_glsl},
647 	        {"scan3x", scan3x_glsl},
648 	        {"sharp", sharp_glsl},
649 	        {"tv2x", tv2x_glsl},
650 	        {"tv3x", tv3x_glsl},
651 	};
652 
653 	char* src;
654 	std::stringstream buf;
655 	std::ifstream fshader(shader_path.c_str(), std::ios_base::binary);
656 	if (!fshader.is_open())
657 		fshader.open(shader_path + ".glsl", std::ios_base::binary);
658 	if (fshader.is_open()) {
659 		buf << fshader.rdbuf();
660 		fshader.close();
661 	} else if (builtin_shaders.count(shader_path) > 0) {
662 		buf << builtin_shaders.at(shader_path);
663 	}
664 
665 	if (!buf.str().empty()) {
666 		std::string s = buf.str() + '\n';
667 		if (first_shell) {
668 			std::string pre_defs;
669 			Bitu count = first_shell->GetEnvCount();
670 			for (Bitu i=0; i < count; i++) {
671 				std::string env;
672 				if (!first_shell->GetEnvNum(i, env))
673 					continue;
674 				if (env.compare(0, 9, "GLSHADER_")==0) {
675 					size_t brk = env.find('=');
676 					if (brk == std::string::npos) continue;
677 					env[brk] = ' ';
678 					pre_defs += "#define " + env.substr(9) + '\n';
679 				}
680 			}
681 			if (!pre_defs.empty()) {
682 				// if "#version" occurs it must be before anything except comments and whitespace
683 				size_t pos = s.find("#version ");
684 
685 				if (pos != std::string::npos)
686 					pos = s.find('\n', pos + 9);
687 
688 				s.insert(pos, pre_defs);
689 			}
690 		}
691 		// keep the same buffer if contents aren't different
692 		if (old_src==NULL || s != old_src) {
693 			src = strdup(s.c_str());
694 			if (src==NULL) LOG_MSG("WARNING: Couldn't copy shader source");
695 		} else src = old_src;
696 	} else src = NULL;
697 	render.shader_src = src;
698 	return src != NULL;
699 }
700 #endif
701 
RENDER_Init(Section * sec)702 void RENDER_Init(Section * sec) {
703 	Section_prop * section=static_cast<Section_prop *>(sec);
704 
705 	//For restarting the renderer.
706 	static bool running = false;
707 	bool aspect = render.aspect;
708 	Bitu scalersize = render.scale.size;
709 	bool scalerforced = render.scale.forced;
710 	scalerOperation_t scaleOp = render.scale.op;
711 
712 	render.pal.first=256;
713 	render.pal.last=0;
714 	render.aspect=section->Get_bool("aspect");
715 	render.frameskip.max=section->Get_int("frameskip");
716 	render.frameskip.count=0;
717 	VGA_SetMonoPalette(section->Get_string("monochrome_palette"));
718 	std::string cline;
719 	std::string scaler;
720 	//Check for commandline paramters and parse them through the configclass so they get checked against allowed values
721 	if (control->cmdline->FindString("-scaler",cline,true)) {
722 		section->HandleInputline(std::string("scaler=") + cline);
723 	} else if (control->cmdline->FindString("-forcescaler",cline,true)) {
724 		section->HandleInputline(std::string("scaler=") + cline + " forced");
725 	}
726 
727 	Prop_multival* prop = section->Get_multival("scaler");
728 	scaler = prop->GetSection()->Get_string("type");
729 	std::string f = prop->GetSection()->Get_string("force");
730 	render.scale.forced = false;
731 	if(f == "forced") render.scale.forced = true;
732 
733 	const bool in_pixel_perfect_mode = (GFX_GetBestMode(0) & GFX_UNITY_SCALE);
734 
735 	if (scaler == "none" || in_pixel_perfect_mode) { render.scale.op = scalerOpNormal;render.scale.size = 1; }
736 	else if (scaler == "normal2x") { render.scale.op = scalerOpNormal;render.scale.size = 2; }
737 	else if (scaler == "normal3x") { render.scale.op = scalerOpNormal;render.scale.size = 3; }
738 #if RENDER_USE_ADVANCED_SCALERS>2
739 	else if (scaler == "advmame2x") { render.scale.op = scalerOpAdvMame;render.scale.size = 2; }
740 	else if (scaler == "advmame3x") { render.scale.op = scalerOpAdvMame;render.scale.size = 3; }
741 	else if (scaler == "advinterp2x") { render.scale.op = scalerOpAdvInterp;render.scale.size = 2; }
742 	else if (scaler == "advinterp3x") { render.scale.op = scalerOpAdvInterp;render.scale.size = 3; }
743 	else if (scaler == "hq2x") { render.scale.op = scalerOpHQ;render.scale.size = 2; }
744 	else if (scaler == "hq3x") { render.scale.op = scalerOpHQ;render.scale.size = 3; }
745 	else if (scaler == "2xsai") { render.scale.op = scalerOpSaI;render.scale.size = 2; }
746 	else if (scaler == "super2xsai") { render.scale.op = scalerOpSuperSaI;render.scale.size = 2; }
747 	else if (scaler == "supereagle") { render.scale.op = scalerOpSuperEagle;render.scale.size = 2; }
748 #endif
749 #if RENDER_USE_ADVANCED_SCALERS>0
750 	else if (scaler == "tv2x") { render.scale.op = scalerOpTV;render.scale.size = 2; }
751 	else if (scaler == "tv3x") { render.scale.op = scalerOpTV;render.scale.size = 3; }
752 	else if (scaler == "rgb2x"){ render.scale.op = scalerOpRGB;render.scale.size = 2; }
753 	else if (scaler == "rgb3x"){ render.scale.op = scalerOpRGB;render.scale.size = 3; }
754 	else if (scaler == "scan2x"){ render.scale.op = scalerOpScan;render.scale.size = 2; }
755 	else if (scaler == "scan3x"){ render.scale.op = scalerOpScan;render.scale.size = 3; }
756 #endif
757 
758 #if C_OPENGL
759 	assert(control);
760 	const Section *sdl_sec = control->GetSection("sdl");
761 	assert(sdl_sec);
762 	const bool using_opengl = starts_with("opengl",
763 	                                      sdl_sec->GetPropValue("output"));
764 	char* shader_src = render.shader_src;
765 	Prop_path *sh = section->Get_path("glshader");
766 	f = (std::string)sh->GetValue();
767 	if (f.empty() || f=="none") render.shader_src = NULL;
768 	else if (!RENDER_GetShader(sh->realpath,shader_src)) {
769 		std::string path;
770 		Cross::GetPlatformConfigDir(path);
771 		path = path + "glshaders" + CROSS_FILESPLIT + f;
772 		if (!RENDER_GetShader(path,shader_src) && (sh->realpath==f || !RENDER_GetShader(f,shader_src))) {
773 			sh->SetValue("none");
774 			LOG_MSG("RENDER: Shader file '%s' not found", f.c_str());
775 		} else if (using_opengl) {
776 			LOG_MSG("RENDER: Using GLSL shader '%s'", f.c_str());
777 		}
778 	}
779 	if (shader_src!=render.shader_src) free(shader_src);
780 #endif
781 
782 	//If something changed that needs a ReInit
783 	// Only ReInit when there is a src.bpp (fixes crashes on startup and directly changing the scaler without a screen specified yet)
784 	if(running && render.src.bpp && ((render.aspect != aspect) || (render.scale.op != scaleOp) ||
785 				  (render.scale.size != scalersize) || (render.scale.forced != scalerforced) ||
786 #if C_OPENGL
787 				  (render.shader_src != shader_src) ||
788 #endif
789 				   render.scale.forced))
790 		RENDER_CallBack( GFX_CallBackReset );
791 
792 	if(!running) render.updating=true;
793 	running = true;
794 
795 	MAPPER_AddHandler(DecreaseFrameSkip, SDL_SCANCODE_UNKNOWN, 0,
796 	                  "decfskip", "Dec Fskip");
797 	MAPPER_AddHandler(IncreaseFrameSkip, SDL_SCANCODE_UNKNOWN, 0,
798 	                  "incfskip", "Inc Fskip");
799 	GFX_SetTitle(-1,render.frameskip.max,false);
800 }
801