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