22
23
#include <SDL_syswm.h>
25
27
#include "bspf.hxx"
27
29
#include "Console.hxx"
28
30
#include "Font.hxx"
29
#include "MediaSrc.hxx"
30
31
#include "OSystem.hxx"
31
32
#include "Settings.hxx"
32
#include "Surface.hxx"
34
#include "GLShaderProgs.hxx"
34
36
#include "FrameBufferGL.hxx"
36
// Maybe this code could be cleaner ...
37
static void (APIENTRY* p_glClear)( GLbitfield );
38
static void (APIENTRY* p_glEnable)( GLenum );
39
static void (APIENTRY* p_glDisable)( GLenum );
40
static void (APIENTRY* p_glPushAttrib)( GLbitfield );
41
static const GLubyte* (APIENTRY* p_glGetString)( GLenum );
42
static void (APIENTRY* p_glHint)( GLenum, GLenum );
43
static void (APIENTRY* p_glShadeModel)( GLenum );
46
static void (APIENTRY* p_glMatrixMode)( GLenum );
47
static void (APIENTRY* p_glOrtho)( GLdouble, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble );
48
static void (APIENTRY* p_glViewport)( GLint, GLint, GLsizei, GLsizei );
49
static void (APIENTRY* p_glPushMatrix)( void );
50
static void (APIENTRY* p_glLoadIdentity)( void );
53
static void (APIENTRY* p_glBegin)( GLenum );
54
static void (APIENTRY* p_glEnd)( void );
55
static void (APIENTRY* p_glVertex2i)( GLint, GLint );
56
static void (APIENTRY* p_glTexCoord2f)( GLfloat, GLfloat );
59
static void (APIENTRY* p_glReadPixels)( GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid* );
60
static void (APIENTRY* p_glPixelStorei)( GLenum, GLint );
63
static void (APIENTRY* p_glTexEnvf)( GLenum, GLenum, GLfloat );
64
static void (APIENTRY* p_glGenTextures)( GLsizei, GLuint* ); // 1.1
65
static void (APIENTRY* p_glDeleteTextures)( GLsizei, const GLuint* ); // 1.1
66
static void (APIENTRY* p_glBindTexture)( GLenum, GLuint ); // 1.1
67
static void (APIENTRY* p_glTexImage2D)( GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid* );
68
static void (APIENTRY* p_glTexSubImage2D)( GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid* ); // 1.1
69
static void (APIENTRY* p_glTexParameteri)( GLenum, GLenum, GLint );
38
// OpenGL functions we'll be using in Stella
39
#define OGL_DECLARE(RET,FUNC,PARAMS) static RET (APIENTRY* p_ ## FUNC) PARAMS
41
OGL_DECLARE(void,glClear,(GLbitfield));
42
OGL_DECLARE(void,glEnable,(GLenum));
43
OGL_DECLARE(void,glDisable,(GLenum));
44
OGL_DECLARE(void,glPushAttrib,(GLbitfield));
45
OGL_DECLARE(const GLubyte*,glGetString,(GLenum));
46
OGL_DECLARE(void,glHint,(GLenum, GLenum));
47
OGL_DECLARE(void,glShadeModel,(GLenum));
48
OGL_DECLARE(void,glMatrixMode,(GLenum));
49
OGL_DECLARE(void,glOrtho,(GLdouble, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble));
50
OGL_DECLARE(void,glViewport,(GLint, GLint, GLsizei, GLsizei));
51
OGL_DECLARE(void,glPushMatrix,(void));
52
OGL_DECLARE(void,glLoadIdentity,(void));
53
OGL_DECLARE(void,glBegin,(GLenum));
54
OGL_DECLARE(void,glEnd,(void));
55
OGL_DECLARE(void,glVertex2i,(GLint, GLint));
56
OGL_DECLARE(void,glTexCoord2f,(GLfloat, GLfloat));
57
OGL_DECLARE(void,glReadPixels,(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid*));
58
OGL_DECLARE(void,glPixelStorei,(GLenum, GLint));
59
OGL_DECLARE(void,glTexEnvf,(GLenum, GLenum, GLfloat));
60
OGL_DECLARE(void,glGenTextures,(GLsizei, GLuint*));
61
OGL_DECLARE(void,glDeleteTextures,(GLsizei, const GLuint*));
62
OGL_DECLARE(void,glActiveTexture,(GLenum));
63
OGL_DECLARE(void,glBindTexture,(GLenum, GLuint));
64
OGL_DECLARE(void,glTexImage2D,(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid*));
65
OGL_DECLARE(void,glTexSubImage2D,(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid*));
66
OGL_DECLARE(void,glTexParameteri,(GLenum, GLenum, GLint));
67
OGL_DECLARE(GLuint,glCreateShader,(GLenum));
68
OGL_DECLARE(void,glDeleteShader,(GLuint));
69
OGL_DECLARE(void,glShaderSource,(GLuint, int, const char**, int));
70
OGL_DECLARE(void,glCompileShader,(GLuint));
71
OGL_DECLARE(GLuint,glCreateProgram,(void));
72
OGL_DECLARE(void,glDeleteProgram,(GLuint));
73
OGL_DECLARE(void,glAttachShader,(GLuint, GLuint));
74
OGL_DECLARE(void,glLinkProgram,(GLuint));
75
OGL_DECLARE(void,glUseProgram,(GLuint));
76
OGL_DECLARE(GLint,glGetUniformLocation,(GLuint, const char*));
77
OGL_DECLARE(void,glUniform1i,(GLint, GLint));
78
OGL_DECLARE(void,glUniform1f,(GLint, GLfloat));
79
OGL_DECLARE(void,glCopyTexImage2D,(GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint));
80
OGL_DECLARE(void,glCopyTexSubImage2D,(GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei));
81
OGL_DECLARE(void,glGetIntegerv,(GLenum, GLint*));
82
OGL_DECLARE(void,glTexEnvi,(GLenum, GLenum, GLint));
83
OGL_DECLARE(void,glMultiTexCoord2f,(GLenum, GLfloat, GLfloat));
84
OGL_DECLARE(GLenum,glGetError,(void));
72
87
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
73
88
FrameBufferGL::FrameBufferGL(OSystem* osystem)
74
89
: FrameBuffer(osystem),
91
myFilterParamName("GL_NEAREST"),
76
92
myHaveTexRectEXT(false),
77
myFilterParamName("GL_NEAREST"),
78
myWidthScaleFactor(1.0),
79
myHeightScaleFactor(1.0),
95
// We need a pixel format for palette value calculations
96
// It's done this way (vs directly accessing a FBSurfaceGL object)
97
// since the structure may be needed before any FBSurface's have
99
SDL_Surface* s = SDL_CreateRGBSurface(SDL_SWSURFACE, 1, 1, 16,
100
0x00007c00, 0x000003e0, 0x0000001f, 0x00000000);
101
myPixelFormat = *(s->format);
84
105
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
85
106
FrameBufferGL::~FrameBufferGL()
88
SDL_FreeSurface(myTexture);
90
p_glDeleteTextures(1, &myBuffer.texture);
108
// We're taking responsibility for this surface
93
112
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
112
128
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
113
bool FrameBufferGL::loadFuncs()
129
bool FrameBufferGL::loadFuncs(GLFunctionality functionality)
131
#define OGL_INIT(RET,FUNC,PARAMS) \
132
p_ ## FUNC = (RET(APIENTRY*)PARAMS) SDL_GL_GetProcAddress(#FUNC); if(!p_ ## FUNC) return false
115
134
if(myLibraryLoaded)
117
136
// Fill the function pointers for GL functions
118
137
// If anything fails, we'll know it immediately, and return false
119
// Yes, this syntax is ugly, but I can type it out faster than the time
120
// it takes to figure our macro magic to do it neatly
121
p_glClear = (void(APIENTRY*)(GLbitfield))
122
SDL_GL_GetProcAddress("glClear"); if(!p_glClear) return false;
123
p_glEnable = (void(APIENTRY*)(GLenum))
124
SDL_GL_GetProcAddress("glEnable"); if(!p_glEnable) return false;
125
p_glDisable = (void(APIENTRY*)(GLenum))
126
SDL_GL_GetProcAddress("glDisable"); if(!p_glDisable) return false;
127
p_glPushAttrib = (void(APIENTRY*)(GLbitfield))
128
SDL_GL_GetProcAddress("glPushAttrib"); if(!p_glPushAttrib) return false;
129
p_glGetString = (const GLubyte*(APIENTRY*)(GLenum))
130
SDL_GL_GetProcAddress("glGetString"); if(!p_glGetString) return false;
131
p_glHint = (void(APIENTRY*)(GLenum, GLenum))
132
SDL_GL_GetProcAddress("glHint"); if(!p_glHint) return false;
133
p_glShadeModel = (void(APIENTRY*)(GLenum))
134
SDL_GL_GetProcAddress("glShadeModel"); if(!p_glShadeModel) return false;
136
p_glMatrixMode = (void(APIENTRY*)(GLenum))
137
SDL_GL_GetProcAddress("glMatrixMode"); if(!p_glMatrixMode) return false;
138
p_glOrtho = (void(APIENTRY*)(GLdouble, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble))
139
SDL_GL_GetProcAddress("glOrtho"); if(!p_glOrtho) return false;
140
p_glViewport = (void(APIENTRY*)(GLint, GLint, GLsizei, GLsizei))
141
SDL_GL_GetProcAddress("glViewport"); if(!p_glViewport) return false;
142
p_glPushMatrix = (void(APIENTRY*)(void))
143
SDL_GL_GetProcAddress("glPushMatrix"); if(!p_glPushMatrix) return false;
144
p_glLoadIdentity = (void(APIENTRY*)(void))
145
SDL_GL_GetProcAddress("glLoadIdentity"); if(!p_glLoadIdentity) return false;
147
p_glBegin = (void(APIENTRY*)(GLenum))
148
SDL_GL_GetProcAddress("glBegin"); if(!p_glBegin) return false;
149
p_glEnd = (void(APIENTRY*)(void))
150
SDL_GL_GetProcAddress("glEnd"); if(!p_glEnd) return false;
151
p_glVertex2i = (void(APIENTRY*)(GLint, GLint))
152
SDL_GL_GetProcAddress("glVertex2i"); if(!p_glVertex2i) return false;
153
p_glTexCoord2f = (void(APIENTRY*)(GLfloat, GLfloat))
154
SDL_GL_GetProcAddress("glTexCoord2f"); if(!p_glTexCoord2f) return false;
156
p_glReadPixels = (void(APIENTRY*)(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid*))
157
SDL_GL_GetProcAddress("glReadPixels"); if(!p_glReadPixels) return false;
158
p_glPixelStorei = (void(APIENTRY*)(GLenum, GLint))
159
SDL_GL_GetProcAddress("glPixelStorei"); if(!p_glPixelStorei) return false;
161
p_glTexEnvf = (void(APIENTRY*)(GLenum, GLenum, GLfloat))
162
SDL_GL_GetProcAddress("glTexEnvf"); if(!p_glTexEnvf) return false;
163
p_glGenTextures = (void(APIENTRY*)(GLsizei, GLuint*))
164
SDL_GL_GetProcAddress("glGenTextures"); if(!p_glGenTextures) return false;
165
p_glDeleteTextures = (void(APIENTRY*)(GLsizei, const GLuint*))
166
SDL_GL_GetProcAddress("glDeleteTextures"); if(!p_glDeleteTextures) return false;
167
p_glBindTexture = (void(APIENTRY*)(GLenum, GLuint))
168
SDL_GL_GetProcAddress("glBindTexture"); if(!p_glBindTexture) return false;
169
p_glTexImage2D = (void(APIENTRY*)(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid*))
170
SDL_GL_GetProcAddress("glTexImage2D"); if(!p_glTexImage2D) return false;
171
p_glTexSubImage2D = (void(APIENTRY*)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid*))
172
SDL_GL_GetProcAddress("glTexSubImage2D"); if(!p_glTexSubImage2D) return false;
173
p_glTexParameteri = (void(APIENTRY*)(GLenum, GLenum, GLint))
174
SDL_GL_GetProcAddress("glTexParameteri"); if(!p_glTexParameteri) return false;
138
switch(functionality)
141
OGL_INIT(void,glClear,(GLbitfield));
142
OGL_INIT(void,glEnable,(GLenum));
143
OGL_INIT(void,glDisable,(GLenum));
144
OGL_INIT(void,glPushAttrib,(GLbitfield));
145
OGL_INIT(const GLubyte*,glGetString,(GLenum));
146
OGL_INIT(void,glHint,(GLenum, GLenum));
147
OGL_INIT(void,glShadeModel,(GLenum));
149
OGL_INIT(void,glMatrixMode,(GLenum));
150
OGL_INIT(void,glOrtho,(GLdouble, GLdouble, GLdouble, GLdouble, GLdouble, GLdouble));
151
OGL_INIT(void,glViewport,(GLint, GLint, GLsizei, GLsizei));
152
OGL_INIT(void,glPushMatrix,(void));
153
OGL_INIT(void,glLoadIdentity,(void));
155
OGL_INIT(void,glBegin,(GLenum));
156
OGL_INIT(void,glEnd,(void));
157
OGL_INIT(void,glVertex2i,(GLint, GLint));
158
OGL_INIT(void,glTexCoord2f,(GLfloat, GLfloat));
160
OGL_INIT(void,glReadPixels,(GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, GLvoid*));
161
OGL_INIT(void,glPixelStorei,(GLenum, GLint));
163
OGL_INIT(void,glTexEnvf,(GLenum, GLenum, GLfloat));
164
OGL_INIT(void,glGenTextures,(GLsizei, GLuint*));
165
OGL_INIT(void,glDeleteTextures,(GLsizei, const GLuint*));
166
OGL_INIT(void,glActiveTexture,(GLenum));
167
OGL_INIT(void,glBindTexture,(GLenum, GLuint));
168
OGL_INIT(void,glTexImage2D,(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid*));
169
OGL_INIT(void,glTexSubImage2D,(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid*));
170
OGL_INIT(void,glTexParameteri,(GLenum, GLenum, GLint));
174
OGL_INIT(GLuint,glCreateShader,(GLenum));
175
OGL_INIT(void,glDeleteShader,(GLuint));
176
OGL_INIT(void,glShaderSource,(GLuint, int, const char**, int));
177
OGL_INIT(void,glCompileShader,(GLuint));
178
OGL_INIT(GLuint,glCreateProgram,(void));
179
OGL_INIT(void,glDeleteProgram,(GLuint));
180
OGL_INIT(void,glAttachShader,(GLuint, GLuint));
181
OGL_INIT(void,glLinkProgram,(GLuint));
182
OGL_INIT(void,glUseProgram,(GLuint));
183
OGL_INIT(GLint,glGetUniformLocation,(GLuint, const char*));
184
OGL_INIT(void,glUniform1i,(GLint, GLint));
185
OGL_INIT(void,glUniform1f,(GLint, GLfloat));
186
OGL_INIT(void,glCopyTexImage2D,(GLenum, GLint, GLenum, GLint, GLint, GLsizei, GLsizei, GLint));
187
OGL_INIT(void,glCopyTexSubImage2D,(GLenum, GLint, GLint, GLint, GLint, GLint, GLsizei, GLsizei));
188
OGL_INIT(void,glGetIntegerv,(GLenum, GLint*));
189
OGL_INIT(void,glTexEnvi,(GLenum, GLenum, GLint));
190
OGL_INIT(void,glMultiTexCoord2f,(GLenum, GLfloat, GLfloat));
191
OGL_INIT(GLenum,glGetError,(void));
436
// Texturemap complete texture to surface so we have free scaling
438
uInt32 w = myImageDim.w, h = myImageDim.h;
440
p_glTexSubImage2D(myBuffer.target, 0, 0, 0,
441
myBuffer.texture_width, myBuffer.texture_height,
442
myBuffer.format, myBuffer.type, myBuffer.pixels);
444
p_glTexCoord2f(myBuffer.tex_coord[0], myBuffer.tex_coord[1]); p_glVertex2i(0, 0);
445
p_glTexCoord2f(myBuffer.tex_coord[2], myBuffer.tex_coord[1]); p_glVertex2i(w, 0);
446
p_glTexCoord2f(myBuffer.tex_coord[2], myBuffer.tex_coord[3]); p_glVertex2i(w, h);
447
p_glTexCoord2f(myBuffer.tex_coord[0], myBuffer.tex_coord[3]); p_glVertex2i(0, h);
450
// Overlay UI dialog boxes
453
530
// Now show all changes made to the texture
454
531
SDL_GL_SwapBuffers();
456
532
myDirtyFlag = false;
460
536
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
537
void FrameBufferGL::enablePhosphor(bool enable, int blend)
539
myUsePhosphor = enable;
540
myPhosphorBlend = blend;
542
myRedrawEntireFrame = true;
545
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
546
FBSurface* FrameBufferGL::createSurface(int w, int h, bool isBase) const
548
// Ignore 'isBase' argument; all GL surfaces are separate
549
// Also, since this method will only be called for use in external
550
// dialogs which cannot be scaled, the base and scaled parameters
552
return new FBSurfaceGL((FrameBufferGL&)*this, w, h, w, h);
555
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
461
556
void FrameBufferGL::scanline(uInt32 row, uInt8* data) const
463
558
// Invert the row, since OpenGL rows start at the bottom
464
559
// of the framebuffer
465
row = myImageDim.h + myImageDim.y - row - 1;
560
const GUI::Rect& image = imageRect();
561
row = image.height() + image.y() - row - 1;
467
563
p_glPixelStorei(GL_PACK_ALIGNMENT, 1);
468
p_glReadPixels(myImageDim.x, row, myImageDim.w, 1, GL_RGB, GL_UNSIGNED_BYTE, data);
471
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
472
void FrameBufferGL::toggleFilter()
474
if(myBuffer.filter == GL_NEAREST)
476
myBuffer.filter = GL_LINEAR;
477
myOSystem->settings().setString("gl_filter", "linear");
478
showMessage("Filtering: GL_LINEAR");
482
myBuffer.filter = GL_NEAREST;
483
myOSystem->settings().setString("gl_filter", "nearest");
484
showMessage("Filtering: GL_NEAREST");
487
p_glBindTexture(myBuffer.target, myBuffer.texture);
488
p_glTexParameteri(myBuffer.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
489
p_glTexParameteri(myBuffer.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
490
p_glTexParameteri(myBuffer.target, GL_TEXTURE_MAG_FILTER, myBuffer.filter);
491
p_glTexParameteri(myBuffer.target, GL_TEXTURE_MIN_FILTER, myBuffer.filter);
493
// The filtering has changed, so redraw the entire screen
494
theRedrawTIAIndicator = true;
497
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
498
void FrameBufferGL::hLine(uInt32 x, uInt32 y, uInt32 x2, int color)
500
uInt16* buffer = (uInt16*) myTexture->pixels + y * myBuffer.pitch + x;
564
p_glReadPixels(image.x(), row, image.width(), 1, GL_RGB, GL_UNSIGNED_BYTE, data);
568
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
569
// FBSurfaceGL implementation follows ...
570
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
572
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
573
FBSurfaceGL::FBSurfaceGL(FrameBufferGL& buffer,
574
uInt32 baseWidth, uInt32 baseHeight,
575
uInt32 scaleWidth, uInt32 scaleHeight,
582
myNoiseMaskTexID(NULL),
584
mySubpixelTexture(NULL),
585
myNoiseTexture(NULL),
589
myHeight(scaleHeight),
593
myPhosphorProgram(0),
594
myTextureNoiseProgram(0),
596
myTvFiltersEnabled(false)
598
// Fill buffer struct with valid data
599
// This changes depending on the texturing used
600
myTexCoord[0] = 0.0f;
601
myTexCoord[1] = 0.0f;
602
if(myFB.myHaveTexRectEXT)
604
myTexWidth = baseWidth;
605
myTexHeight = baseHeight;
606
myTexTarget = GL_TEXTURE_RECTANGLE_ARB;
607
myTexCoord[2] = (GLfloat) myTexWidth;
608
myTexCoord[3] = (GLfloat) myTexHeight;
610
// This is a quick fix, a better one will come later
611
myTvFiltersEnabled = false;
615
myTexWidth = power_of_two(baseWidth);
616
myTexHeight = power_of_two(baseHeight);
617
myTexTarget = GL_TEXTURE_2D;
618
myTexCoord[2] = (GLfloat) baseWidth / myTexWidth;
619
myTexCoord[3] = (GLfloat) baseHeight / myTexHeight;
622
// Based on experimentation, the following is the fastest 16-bit
623
// format for OpenGL (on all platforms)
624
myTexture = SDL_CreateRGBSurface(SDL_SWSURFACE,
625
myTexWidth, myTexHeight, 16,
626
0x00007c00, 0x000003e0, 0x0000001f, 0x00000000);
627
myPitch = myTexture->pitch >> 1;
629
// The 'allowFiltering' boolean is only a hint that filtering is allowed
631
// We still need to check if the functionality exists to do it
634
// It's only enabled if we use one of the filters *AND* GLSL is available
635
myTvFiltersEnabled = myFB.myGLSLAvailable &&
636
(myFB.myUseTexture || myFB.myUseNoise ||
637
myFB.myUseBleed || myFB.myUseGLPhosphor);
640
myTvFiltersEnabled = false;
642
// Only do this if TV filters enabled, otherwise it won't be used anyway
643
if(myTvFiltersEnabled)
645
// For a reason that hasn't been investigated yet, some of the filter and mask
646
// texture coordinates need to be swapped in order for it not to render upside down
648
myFilterTexCoord[0] = 0.0f;
649
myFilterTexCoord[3] = 0.0f;
651
if(myFB.myHaveTexRectEXT)
653
myFilterTexWidth = scaleWidth;
654
myFilterTexHeight = scaleHeight;
655
myFilterTexCoord[2] = (GLfloat) myFilterTexWidth;
656
myFilterTexCoord[1] = (GLfloat) myFilterTexHeight;
660
myFilterTexWidth = power_of_two(scaleWidth);
661
myFilterTexHeight = power_of_two(scaleHeight);
662
myFilterTexCoord[2] = (GLfloat) scaleWidth / myFilterTexWidth;
663
myFilterTexCoord[1] = (GLfloat) scaleHeight / myFilterTexHeight;
666
// Only do this if TV and color bleed filters are enabled
667
// This filer applies a color averaging of surrounding pixels for each pixel
670
// Load shader programs. If it fails, don't use this filter.
671
myBleedProgram = genShader(SHADER_BLEED);
672
if(myBleedProgram == 0)
674
myFB.myUseBleed = false;
675
cout << "ERROR: Failed to make bleed programs" << endl;
679
// If the texture and noise filters are enabled together, we can use a single shader
680
// Make sure we can use three textures at once first
682
p_glGetIntegerv(GL_MAX_TEXTURE_UNITS, &texUnits);
683
if(texUnits >= 3 && myFB.myUseTexture && myFB.myUseNoise)
685
// Load shader program. If it fails, don't use this shader.
686
myTextureNoiseProgram = genShader(SHADER_TEXNOISE);
687
if(myTextureNoiseProgram == 0)
689
cout << "ERROR: Failed to make texture/noise program" << endl;
691
// Load shader program. If it fails, don't use this filter.
692
myTextureProgram = genShader(SHADER_TEX);
693
if(myTextureProgram == 0)
695
myFB.myUseTexture = false;
696
cout << "ERROR: Failed to make texture program" << endl;
699
// Load shader program. If it fails, don't use this filter.
700
myNoiseProgram = genShader(SHADER_NOISE);
701
if(myNoiseProgram == 0)
703
myFB.myUseNoise = false;
704
cout << "ERROR: Failed to make noise program" << endl;
708
// Else, detect individual settings
711
if(myFB.myUseTexture)
713
// Load shader program. If it fails, don't use this filter.
714
myTextureProgram = genShader(SHADER_TEX);
715
if(myTextureProgram == 0)
717
myFB.myUseTexture = false;
718
cout << "ERROR: Failed to make texture program" << endl;
724
// Load shader program. If it fails, don't use this filter.
725
myNoiseProgram = genShader(SHADER_NOISE);
726
if(myNoiseProgram == 0)
728
myFB.myUseNoise = false;
729
cout << "ERROR: Failed to make noise program" << endl;
734
// Only do this if TV and color texture filters are enabled
735
// This filter applies an RGB color pixel mask as well as a blackspace mask
736
if(myFB.myUseTexture)
738
// Prepare subpixel texture
739
mySubpixelTexture = SDL_CreateRGBSurface(SDL_SWSURFACE,
740
myFilterTexWidth, myFilterTexHeight, 16,
741
0x00007c00, 0x000003e0, 0x0000001f, 0x00000000);
744
for (uInt32 y = 0; y < (uInt32)myFilterTexHeight; y++)
746
for (uInt32 x = 0; x < (uInt32)myFilterTexWidth; x++)
748
// Cause vertical offset for every other black row if enabled
750
if (!myFB.myTextureStag || x % 6 < 3)
755
// Make a row of black for the mask every so often
756
if (offsetY % 4 == 0)
758
((uInt16*)mySubpixelTexture->pixels)[pCounter] = 0x0000;
760
// Apply the coorect color mask
763
((uInt16*)mySubpixelTexture->pixels)[pCounter] = 0x7c00 >> ((x % 3) * 5);
770
// Only do this if TV and noise filters are enabled
771
// This filter applies a texture filled with gray pixel of random intensities
774
// Get the current number of nose textures to use
775
myNoiseNum = myFB.myNoiseQuality;
777
// Allocate space for noise textures
778
myNoiseTexture = new SDL_Surface*[myNoiseNum];
779
myNoiseMaskTexID = new GLuint[myNoiseNum];
781
// Prepare noise textures
782
for(int i = 0; i < myNoiseNum; i++)
784
myNoiseTexture[i] = SDL_CreateRGBSurface(SDL_SWSURFACE,
785
myFilterTexWidth, myFilterTexHeight, 16,
786
0x00007c00, 0x000003e0, 0x0000001f, 0x00000000);
790
for(int i = 0; i < myNoiseNum; i++)
794
// Attempt to make the numbers as random as possible
795
int temp = (unsigned)time(0) + rand()/4;
798
for (uInt32 y = 0; y < (uInt32)myFilterTexHeight; y++)
800
for (uInt32 x = 0; x < (uInt32)myFilterTexWidth; x++)
802
// choose random 0 - 2
806
int num = rand() % 3;
808
((uInt16*)myNoiseTexture[i]->pixels)[pCounter] = 0x0000;
810
((uInt16*)myNoiseTexture[i]->pixels)[pCounter] = 0x0421;
812
((uInt16*)myNoiseTexture[i]->pixels)[pCounter] = 0x0842;
820
// Only do this if TV and phosphor filters are enabled
821
// This filter merges the past screen with the current one, to give a phosphor burn-off effect
822
if(myFB.myUseGLPhosphor)
824
// Load shader program. If it fails, don't use this filter.
825
myPhosphorProgram = genShader(SHADER_PHOS);
826
if(myPhosphorProgram == 0)
828
myFB.myUseGLPhosphor = false;
829
cout << "ERROR: Failed to make phosphor program" << endl;
834
// Check to see if filters should still be used
835
// Filtering must have been previously enabled, and GLSL must still be
837
myTvFiltersEnabled = myTvFiltersEnabled && myFB.myGLSLAvailable &&
838
(myFB.myUseTexture || myFB.myUseNoise ||
839
myFB.myUseBleed || myFB.myUseGLPhosphor);
841
// Associate the SDL surface with a GL texture object
845
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
846
FBSurfaceGL::~FBSurfaceGL()
849
SDL_FreeSurface(myTexture);
851
if(mySubpixelTexture)
852
SDL_FreeSurface(mySubpixelTexture);
855
for(int i = 0; i < myNoiseNum; i++)
856
SDL_FreeSurface(myNoiseTexture[i]);
861
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
862
void FBSurfaceGL::hLine(uInt32 x, uInt32 y, uInt32 x2, uInt32 color)
864
uInt16* buffer = (uInt16*) myTexture->pixels + y * myPitch + x;
502
*buffer++ = (uInt16) myDefPalette[color];
866
*buffer++ = (uInt16) myFB.myDefPalette[color];
505
869
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
506
void FrameBufferGL::vLine(uInt32 x, uInt32 y, uInt32 y2, int color)
870
void FBSurfaceGL::vLine(uInt32 x, uInt32 y, uInt32 y2, uInt32 color)
508
uInt16* buffer = (uInt16*) myTexture->pixels + y * myBuffer.pitch + x;
872
uInt16* buffer = (uInt16*) myTexture->pixels + y * myPitch + x;
511
*buffer = (uInt16) myDefPalette[color];
512
buffer += myBuffer.pitch;
875
*buffer = (uInt16) myFB.myDefPalette[color];
516
880
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
517
void FrameBufferGL::fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, int color)
881
void FBSurfaceGL::fillRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h, uInt32 color)
519
883
// Fill the rectangle
537
901
if (chr == ' ') return;
538
902
chr = desc.defaultchar;
541
const Int32 w = font->getCharWidth(chr);
542
const Int32 h = font->getFontHeight();
543
904
chr -= desc.firstchar;
544
const uInt32* tmp = desc.bits + (desc.offset ? desc.offset[chr] : (chr * h));
546
uInt16* buffer = (uInt16*) myTexture->pixels + ty * myBuffer.pitch + tx;
547
for(int y = 0; y < h; ++y)
549
const uInt32 ptr = *tmp++;
552
uInt32 mask = 0x80000000;
553
for(int x = 0; x < w; ++x, mask >>= 1)
555
buffer[x] = (uInt16) myDefPalette[color];
557
buffer += myBuffer.pitch;
906
// Get the bounding box of the character
907
int bbw, bbh, bbx, bby;
917
bbw = desc.bbx[chr].w;
918
bbh = desc.bbx[chr].h;
919
bbx = desc.bbx[chr].x;
920
bby = desc.bbx[chr].y;
923
const uInt16* tmp = desc.bits + (desc.offset ? desc.offset[chr] : (chr * desc.fbbh));
924
uInt16* buffer = (uInt16*) myTexture->pixels +
925
(ty + desc.ascent - bby - bbh) * myPitch +
928
for(int y = 0; y < bbh; y++)
930
const uInt16 ptr = *tmp++;
931
uInt16 mask = 0x8000;
933
for(int x = 0; x < bbw; x++, mask >>= 1)
935
buffer[x] = (uInt16) myFB.myDefPalette[color];
561
941
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
562
void FrameBufferGL::drawBitmap(uInt32* bitmap, Int32 tx, Int32 ty,
942
void FBSurfaceGL::drawBitmap(uInt32* bitmap, uInt32 tx, uInt32 ty,
943
uInt32 color, uInt32 h)
565
uInt16* buffer = (uInt16*) myTexture->pixels + ty * myBuffer.pitch + tx;
945
uInt16* buffer = (uInt16*) myTexture->pixels + ty * myPitch + tx;
567
for(int y = 0; y < h; ++y)
947
for(uInt32 y = 0; y < h; ++y)
569
949
uInt32 mask = 0xF0000000;
570
for(int x = 0; x < 8; ++x, mask >>= 4)
950
for(uInt32 x = 0; x < 8; ++x, mask >>= 4)
571
951
if(bitmap[y] & mask)
572
buffer[x] = (uInt16) myDefPalette[color];
952
buffer[x] = (uInt16) myFB.myDefPalette[color];
574
buffer += myBuffer.pitch;
578
958
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
579
void FrameBufferGL::drawSurface(const GUI::Surface* surface, Int32 x, Int32 y)
959
void FBSurfaceGL::drawPixels(uInt32* data, uInt32 tx, uInt32 ty, uInt32 numpixels)
961
uInt16* buffer = (uInt16*) myTexture->pixels + ty * myPitch + tx;
963
for(uInt32 i = 0; i < numpixels; ++i)
964
*buffer++ = (uInt16) data[i];
967
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
968
void FBSurfaceGL::drawSurface(const FBSurface* surface, uInt32 tx, uInt32 ty)
970
const FBSurfaceGL* s = (const FBSurfaceGL*) surface;
581
972
SDL_Rect dstrect;
584
975
SDL_Rect srcrect;
587
srcrect.w = surface->myClipWidth;
588
srcrect.h = surface->myClipHeight;
590
SDL_BlitSurface(surface->myData, &srcrect, myTexture, &dstrect);
593
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
594
void FrameBufferGL::bytesToSurface(GUI::Surface* surface, int row,
595
uInt8* data, int rowbytes) const
597
SDL_Surface* s = surface->myData;
598
uInt16* pixels = (uInt16*) s->pixels;
599
pixels += (row * s->pitch/2);
601
for(int c = 0; c < rowbytes; c += 3)
602
*pixels++ = SDL_MapRGB(s->format, data[c], data[c+1], data[c+2]);
605
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
606
void FrameBufferGL::translateCoords(Int32& x, Int32& y) const
608
// Wow, what a mess :)
609
x = (Int32) ((x - myImageDim.x) / myWidthScaleFactor);
610
y = (Int32) ((y - myImageDim.y) / myHeightScaleFactor);
613
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
614
void FrameBufferGL::addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h)
619
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
620
void FrameBufferGL::enablePhosphor(bool enable, int blend)
622
myUsePhosphor = enable;
623
myPhosphorBlend = blend;
625
theRedrawTIAIndicator = true;
628
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
629
bool FrameBufferGL::createTextures()
633
p_glClear(GL_COLOR_BUFFER_BIT);
634
SDL_GL_SwapBuffers();
635
p_glClear(GL_COLOR_BUFFER_BIT);
636
SDL_FreeSurface(myTexture);
638
if(myBuffer.texture) p_glDeleteTextures(1, &myBuffer.texture);
639
memset(&myBuffer, 0, sizeof(glBufferType));
640
myBuffer.filter = GL_NEAREST;
642
// Fill buffer struct with valid data
643
// This changes depending on the texturing used
644
myBuffer.width = myBaseDim.w;
645
myBuffer.height = myBaseDim.h;
646
myBuffer.tex_coord[0] = 0.0f;
647
myBuffer.tex_coord[1] = 0.0f;
650
myBuffer.texture_width = myBuffer.width;
651
myBuffer.texture_height = myBuffer.height;
652
myBuffer.target = GL_TEXTURE_RECTANGLE_ARB;
653
myBuffer.tex_coord[2] = (GLfloat) myBuffer.texture_width;
654
myBuffer.tex_coord[3] = (GLfloat) myBuffer.texture_height;
658
myBuffer.texture_width = power_of_two(myBuffer.width);
659
myBuffer.texture_height = power_of_two(myBuffer.height);
660
myBuffer.target = GL_TEXTURE_2D;
661
myBuffer.tex_coord[2] = (GLfloat) myBuffer.width / myBuffer.texture_width;
662
myBuffer.tex_coord[3] = (GLfloat) myBuffer.height / myBuffer.texture_height;
665
// Create a texture that best suits the current display depth and system
666
// This code needs to be Apple-specific, otherwise performance is
667
// terrible on a Mac Mini
669
myTexture = SDL_CreateRGBSurface(SDL_SWSURFACE,
670
myBuffer.texture_width, myBuffer.texture_height, 16,
671
0x00007c00, 0x000003e0, 0x0000001f, 0x00000000);
673
myTexture = SDL_CreateRGBSurface(SDL_SWSURFACE,
674
myBuffer.texture_width, myBuffer.texture_height, 16,
675
0x0000f800, 0x000007e0, 0x0000001f, 0x00000000);
677
if(myTexture == NULL)
680
myBuffer.pixels = myTexture->pixels;
681
switch(myTexture->format->BytesPerPixel)
684
myBuffer.pitch = myTexture->pitch/2;
687
myBuffer.pitch = myTexture->pitch;
690
myBuffer.pitch = myTexture->pitch/4;
696
// Create an OpenGL texture from the SDL texture
697
const string& filter = myOSystem->settings().getString("gl_filter");
698
if(filter == "linear")
700
myBuffer.filter = GL_LINEAR;
701
myFilterParamName = "GL_LINEAR";
703
else if(filter == "nearest")
705
myBuffer.filter = GL_NEAREST;
706
myFilterParamName = "GL_NEAREST";
709
p_glGenTextures(1, &myBuffer.texture);
710
p_glBindTexture(myBuffer.target, myBuffer.texture);
711
p_glTexParameteri(myBuffer.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
712
p_glTexParameteri(myBuffer.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
713
p_glTexParameteri(myBuffer.target, GL_TEXTURE_MIN_FILTER, myBuffer.filter);
714
p_glTexParameteri(myBuffer.target, GL_TEXTURE_MAG_FILTER, myBuffer.filter);
978
srcrect.w = s->myWidth;
979
srcrect.h = s->myHeight;
981
SDL_BlitSurface(s->myTexture, &srcrect, myTexture, &dstrect);
984
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
985
void FBSurfaceGL::addDirtyRect(uInt32 x, uInt32 y, uInt32 w, uInt32 h)
987
// OpenGL mode doesn't make use of dirty rectangles
988
// It's faster to just update the entire surface
989
mySurfaceIsDirty = true;
992
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
993
void FBSurfaceGL::getPos(uInt32& x, uInt32& y) const
999
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1000
void FBSurfaceGL::setPos(uInt32 x, uInt32 y)
1006
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1007
void FBSurfaceGL::setWidth(uInt32 w)
1009
// This method can't be used with 'scaled' surface (aka TIA surfaces)
1010
// That shouldn't really matter, though, as all the UI stuff isn't scaled,
1011
// and it's the only thing that uses it
1014
if(myFB.myHaveTexRectEXT)
1015
myTexCoord[2] = (GLfloat) myWidth;
1017
myTexCoord[2] = (GLfloat) myWidth / myTexWidth;
1020
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1021
void FBSurfaceGL::setHeight(uInt32 h)
1023
// This method can't be used with 'scaled' surface (aka TIA surfaces)
1024
// That shouldn't really matter, though, as all the UI stuff isn't scaled,
1025
// and it's the only thing that uses it
1028
if(myFB.myHaveTexRectEXT)
1029
myTexCoord[3] = (GLfloat) myHeight;
1031
myTexCoord[3] = (GLfloat) myHeight / myTexHeight;
1034
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1035
void FBSurfaceGL::translateCoords(Int32& x, Int32& y) const
1041
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1042
void FBSurfaceGL::update()
1044
if(mySurfaceIsDirty)
1048
// Set a boolean to tell which filter is a first render (if any are applied).
1049
// Being a first render means using the Atari frame buffer instead of the
1050
// previous rendered data.
1051
bool firstRender = true;
1053
// Render as usual if no filters are used
1054
if(!myTvFiltersEnabled)
1056
// Texturemap complete texture to surface so we have free scaling
1058
p_glActiveTexture(GL_TEXTURE0);
1059
p_glBindTexture(myTexTarget, myTexID);
1060
p_glTexSubImage2D(myTexTarget, 0, 0, 0, myTexWidth, myTexHeight,
1061
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, myTexture->pixels);
1063
// Pass in texture as a variable
1064
p_glBegin(GL_QUADS);
1065
p_glTexCoord2f(myTexCoord[0], myTexCoord[1]);
1066
p_glVertex2i(myXOrig, myYOrig);
1068
p_glTexCoord2f(myTexCoord[2], myTexCoord[1]);
1069
p_glVertex2i(myXOrig + myWidth, myYOrig);
1071
p_glTexCoord2f(myTexCoord[2], myTexCoord[3]);
1072
p_glVertex2i(myXOrig + myWidth, myYOrig + myHeight);
1074
p_glTexCoord2f(myTexCoord[0], myTexCoord[3]);
1075
p_glVertex2i(myXOrig, myYOrig + myHeight);
1079
// If TV filters are enabled
1080
// TODO - check if this IF is necessary, or can it be chained by else to above
1081
if(myTvFiltersEnabled)
1083
// If combined texture/noise program exists,
1084
// use the combined one; else do them separately
1085
if(myTextureNoiseProgram != 0)
1087
p_glUseProgram(myTextureNoiseProgram);
1089
// Pass in subpixel mask texture
1090
p_glActiveTexture(GL_TEXTURE1);
1091
p_glBindTexture(myTexTarget, mySubMaskTexID);
1092
loc = p_glGetUniformLocation(myTextureNoiseProgram, "texMask");
1093
p_glUniform1i(loc, 1);
1095
// Choose random mask texture
1096
int num = rand() % myNoiseNum;
1097
// Pass in noise mask texture
1098
p_glActiveTexture(GL_TEXTURE2);
1099
p_glBindTexture(myTexTarget, myNoiseMaskTexID[num]);
1100
loc = p_glGetUniformLocation(myTextureNoiseProgram, "noiseMask");
1101
p_glUniform1i(loc, 2);
1103
renderThreeTexture(myTextureNoiseProgram, firstRender);
1105
// We have rendered, set firstRender to false
1106
firstRender = false;
1110
// Check if texture filter is enabled
1111
if(myFB.myUseTexture)
1113
p_glUseProgram(myTextureProgram);
1115
// Pass in subpixel mask texture
1116
p_glActiveTexture(GL_TEXTURE1);
1117
p_glBindTexture(myTexTarget, mySubMaskTexID);
1118
loc = p_glGetUniformLocation(myTextureProgram, "mask");
1119
p_glUniform1i(loc, 1);
1121
renderTwoTexture(myTextureProgram, firstRender);
1123
// We have rendered, set firstRender to false
1124
firstRender = false;
1129
p_glUseProgram(myNoiseProgram);
1131
// Choose random mask texture
1132
int num = rand() % myNoiseNum;
1134
// Pass in noise mask texture
1135
p_glActiveTexture(GL_TEXTURE1);
1136
p_glBindTexture(myTexTarget, myNoiseMaskTexID[num]);
1137
loc = p_glGetUniformLocation(myNoiseProgram, "mask");
1138
p_glUniform1i(loc, 1);
1140
renderTwoTexture(myNoiseProgram, firstRender);
1142
// We have rendered, set firstRender to false
1143
firstRender = false;
1147
// Check if bleed filter is enabled
1150
p_glUseProgram(myBleedProgram);
1152
// Set some values based on high, medium, or low quality bleed. The high quality
1153
// scales by applying additional passes, the low and medium quality scales by using
1154
// a width and height based on the zoom level
1157
if(myFB.myBleedQuality == 2)
1159
// Precalculate pixel shifts
1160
GLfloat pH = 1.0 / myHeight;
1161
GLfloat pW = 1.0 / myWidth;
1162
GLfloat pWx2 = pW * 2.0;
1164
loc = p_glGetUniformLocation(myBleedProgram, "pH");
1165
p_glUniform1f(loc, pH);
1166
loc = p_glGetUniformLocation(myBleedProgram, "pW");
1167
p_glUniform1f(loc, pW);
1168
loc = p_glGetUniformLocation(myBleedProgram, "pWx2");
1169
p_glUniform1f(loc, pWx2);
1171
// Set the number of passes based on zoom level
1172
passes = myFB.getZoomLevel();
1174
// Medium and low quality
1177
// The scaling formula was produced through trial and error
1178
// Precalculate pixel shifts
1179
GLfloat pH = 1.0 / (myHeight / (0.35 * myFB.getZoomLevel()));
1180
GLfloat pW = 1.0 / (myWidth / (0.35 * myFB.getZoomLevel()));
1181
GLfloat pWx2 = pW * 2.0;
1183
loc = p_glGetUniformLocation(myBleedProgram, "pH");
1184
p_glUniform1f(loc, pH);
1185
loc = p_glGetUniformLocation(myBleedProgram, "pW");
1186
p_glUniform1f(loc, pW);
1187
loc = p_glGetUniformLocation(myBleedProgram, "pWx2");
1188
p_glUniform1f(loc, pWx2);
1191
if(myFB.myBleedQuality == 1)
1198
// If we are using a texture effect, we need more bleed
1199
if (myFB.myUseTexture)
1202
for (int i = 0; i < passes; i++)
1204
renderTexture(myBleedProgram, firstRender);
1206
// We have rendered, set firstRender to false
1207
firstRender = false;
1211
// Check if phosphor burn-off filter is enabled
1212
if(myFB.myUseGLPhosphor)
1214
p_glUseProgram(myPhosphorProgram);
1216
// Pass in subpixel mask texture
1217
p_glActiveTexture(GL_TEXTURE1);
1218
p_glBindTexture(myTexTarget, myPhosphorTexID);
1219
loc = p_glGetUniformLocation(myPhosphorProgram, "mask");
1220
p_glUniform1i(loc, 1);
1222
renderTwoTexture(myPhosphorProgram, firstRender);
1224
p_glActiveTexture(GL_TEXTURE1);
1225
p_glBindTexture(myTexTarget, myPhosphorTexID);
1226
// We only need to copy the scaled size, which may be smaller than the texture width
1227
p_glCopyTexSubImage2D(myTexTarget, 0, 0, 0, myXOrig, myYOrig, myWidth, myHeight);
1229
// We have rendered, set firstRender to false
1230
firstRender = false;
1233
// Disable all shader programs for the next rendering pass
1234
// This is placed here since it's a GLSL 2.0-specific function, and
1235
// doesn't exist (and isn't required) for base OpenGL functionality
1239
mySurfaceIsDirty = false;
1241
// Let postFrameUpdate() know that a change has been made
1242
myFB.myDirtyFlag = true;
1246
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1247
void FBSurfaceGL::renderTexture(GLuint program, bool firstRender)
1250
GLfloat texCoord[4];
1252
p_glActiveTexture(GL_TEXTURE0);
1254
// If this is a first render, use the Atari frame buffer
1257
// Pass in Atari frame
1258
p_glBindTexture(myTexTarget, myTexID);
1259
p_glTexSubImage2D(myTexTarget, 0, 0, 0, myTexWidth, myTexHeight,
1260
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, myTexture->pixels);
1262
// Set the texture coord appropriately
1263
texCoord[0] = myTexCoord[0];
1264
texCoord[1] = myTexCoord[1];
1265
texCoord[2] = myTexCoord[2];
1266
texCoord[3] = myTexCoord[3];
1270
// Copy frame buffer to texture, this isn't the fastest way to do it, but it's simple
1271
// (rendering directly to texture instead of copying may be faster)
1272
p_glBindTexture(myTexTarget, myFilterTexID);
1273
// We only need to copy the scaled size, which may be smaller than the texture width
1274
p_glCopyTexSubImage2D(myTexTarget, 0, 0, 0, myXOrig, myYOrig, myWidth, myHeight);
1276
// Set the texture coord appropriately
1277
texCoord[0] = myFilterTexCoord[0];
1278
texCoord[1] = myFilterTexCoord[1];
1279
texCoord[2] = myFilterTexCoord[2];
1280
texCoord[3] = myFilterTexCoord[3];
1283
// Pass the texture to the program
1284
loc = p_glGetUniformLocation(program, "tex");
1285
p_glUniform1i(loc, 0);
1287
// Pass in texture as a variable
1288
p_glBegin(GL_QUADS);
1289
p_glTexCoord2f(texCoord[0], texCoord[1]);
1290
p_glVertex2i(myXOrig, myYOrig);
1292
p_glTexCoord2f(texCoord[2], texCoord[1]);
1293
p_glVertex2i(myXOrig + myWidth, myYOrig);
1295
p_glTexCoord2f(texCoord[2], texCoord[3]);
1296
p_glVertex2i(myXOrig + myWidth, myYOrig + myHeight);
1298
p_glTexCoord2f(texCoord[0], texCoord[3]);
1299
p_glVertex2i(myXOrig, myYOrig + myHeight);
1303
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1304
void FBSurfaceGL::renderTwoTexture(GLuint program, bool firstRender)
1307
GLfloat texCoord[4];
1309
p_glActiveTexture(GL_TEXTURE0);
1311
// If this is a first render, use the Atari frame buffer
1314
// Pass in Atari frame
1315
p_glBindTexture(myTexTarget, myTexID);
1316
p_glTexSubImage2D(myTexTarget, 0, 0, 0, myTexWidth, myTexHeight,
1317
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, myTexture->pixels);
1319
// Set the texture coord appropriately
1320
texCoord[0] = myTexCoord[0];
1321
texCoord[1] = myTexCoord[1];
1322
texCoord[2] = myTexCoord[2];
1323
texCoord[3] = myTexCoord[3];
1327
// Copy frame buffer to texture, this isn't the fastest way to do it, but it's simple
1328
// (rendering directly to texture instead of copying may be faster)
1329
p_glBindTexture(myTexTarget, myFilterTexID);
1330
// We only need to copy the scaled size, which may be smaller than the texture width
1331
p_glCopyTexSubImage2D(myTexTarget, 0, 0, 0, myXOrig, myYOrig, myWidth, myHeight);
1333
// Set the filter texture coord appropriately
1334
texCoord[0] = myFilterTexCoord[0];
1335
texCoord[1] = myFilterTexCoord[1];
1336
texCoord[2] = myFilterTexCoord[2];
1337
texCoord[3] = myFilterTexCoord[3];
1340
// Pass the texture to the program
1341
loc = p_glGetUniformLocation(program, "tex");
1342
p_glUniform1i(loc, 0);
1344
// Pass in textures as variables
1345
p_glBegin(GL_QUADS);
1346
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[0], texCoord[1]);
1347
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[0], myFilterTexCoord[1]);
1348
p_glVertex2i(myXOrig, myYOrig);
1350
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[2], texCoord[1]);
1351
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[2], myFilterTexCoord[1]);
1352
p_glVertex2i(myXOrig + myWidth, myYOrig);
1354
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[2], texCoord[3]);
1355
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[2], myFilterTexCoord[3]);
1356
p_glVertex2i(myXOrig + myWidth, myYOrig + myHeight);
1358
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[0], texCoord[3]);
1359
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[0], myFilterTexCoord[3]);
1360
p_glVertex2i(myXOrig, myYOrig + myHeight);
1364
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1365
void FBSurfaceGL::renderThreeTexture(GLuint program, bool firstRender)
1368
GLfloat texCoord[4];
1370
p_glActiveTexture(GL_TEXTURE0);
1372
// If this is a first render, use the Atari frame buffer
1375
// Pass in Atari frame
1376
p_glBindTexture(myTexTarget, myTexID);
1377
p_glTexSubImage2D(myTexTarget, 0, 0, 0, myTexWidth, myTexHeight,
1378
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, myTexture->pixels);
1380
// Set the texture coord appropriately
1381
texCoord[0] = myTexCoord[0];
1382
texCoord[1] = myTexCoord[1];
1383
texCoord[2] = myTexCoord[2];
1384
texCoord[3] = myTexCoord[3];
1388
// Copy frame buffer to texture, this isn't the fastest way to do it, but it's simple
1389
// (rendering directly to texture instead of copying may be faster)
1390
p_glBindTexture(myTexTarget, myFilterTexID);
1391
// We only need to copy the scaled size, which may be smaller than the texture width
1392
p_glCopyTexSubImage2D(myTexTarget, 0, 0, 0, myXOrig, myYOrig, myWidth, myHeight);
1394
// Set the filter texture coord appropriately
1395
texCoord[0] = myFilterTexCoord[0];
1396
texCoord[1] = myFilterTexCoord[1];
1397
texCoord[2] = myFilterTexCoord[2];
1398
texCoord[3] = myFilterTexCoord[3];
1401
// Pass the texture to the program
1402
loc = p_glGetUniformLocation(program, "tex");
1403
p_glUniform1i(loc, 0);
1405
// Pass in textures as variables
1406
p_glBegin(GL_QUADS);
1407
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[0], texCoord[1]);
1408
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[0], myFilterTexCoord[1]);
1409
p_glMultiTexCoord2f(GL_TEXTURE2, myFilterTexCoord[0], myFilterTexCoord[1]);
1410
p_glVertex2i(myXOrig, myYOrig);
1412
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[2], texCoord[1]);
1413
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[2], myFilterTexCoord[1]);
1414
p_glMultiTexCoord2f(GL_TEXTURE2, myFilterTexCoord[2], myFilterTexCoord[1]);
1415
p_glVertex2i(myXOrig + myWidth, myYOrig);
1417
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[2], texCoord[3]);
1418
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[2], myFilterTexCoord[3]);
1419
p_glMultiTexCoord2f(GL_TEXTURE2, myFilterTexCoord[2], myFilterTexCoord[3]);
1420
p_glVertex2i(myXOrig + myWidth, myYOrig + myHeight);
1422
p_glMultiTexCoord2f(GL_TEXTURE0, texCoord[0], texCoord[3]);
1423
p_glMultiTexCoord2f(GL_TEXTURE1, myFilterTexCoord[0], myFilterTexCoord[3]);
1424
p_glMultiTexCoord2f(GL_TEXTURE2, myFilterTexCoord[0], myFilterTexCoord[3]);
1425
p_glVertex2i(myXOrig, myYOrig + myHeight);
1429
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1430
void FBSurfaceGL::free()
1432
p_glDeleteTextures(1, &myTexID);
1434
// The below is borken up a bit because of the possible combined texture/noise shader
1437
p_glDeleteTextures(1, &myFilterTexID);
1440
p_glDeleteTextures(1, &mySubMaskTexID);
1442
if(myTextureProgram)
1443
p_glDeleteProgram(myTextureProgram);
1445
if(myNoiseMaskTexID)
1447
delete[] myNoiseTexture;
1448
p_glDeleteTextures(myNoiseNum, myNoiseMaskTexID);
1449
delete[] myNoiseMaskTexID;
1453
p_glDeleteProgram(myNoiseProgram);
1457
p_glDeleteTextures(1, &myPhosphorTexID);
1458
p_glDeleteProgram(myPhosphorProgram);
1461
if(myTextureNoiseProgram)
1462
p_glDeleteProgram(myTextureNoiseProgram);
1465
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1466
void FBSurfaceGL::reload()
1468
// This does a 'soft' reset of the surface
1469
// It seems that on some system (notably, OSX), creating a new SDL window
1470
// destroys the GL context, requiring a reload of all textures
1471
// However, destroying the entire FBSurfaceGL object is wasteful, since
1472
// it will also regenerate SDL software surfaces (which are not required
1473
// to be regenerated)
1474
// Basically, all that needs to be done is to re-call glTexImage2D with a
1475
// new texture ID, so that's what we do here
1477
p_glActiveTexture(GL_TEXTURE0);
1478
p_glEnable(myTexTarget);
1480
p_glGenTextures(1, &myTexID);
1481
p_glBindTexture(myTexTarget, myTexID);
1482
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1483
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1484
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1485
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
716
1487
// Finally, create the texture in the most optimal format
717
GLenum tex_intformat;
718
#if defined (MAC_OSX)
719
tex_intformat = GL_RGB5;
720
myBuffer.format = GL_BGRA;
721
myBuffer.type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
723
tex_intformat = GL_RGB;
724
myBuffer.format = GL_RGB;
725
myBuffer.type = GL_UNSIGNED_SHORT_5_6_5;
727
p_glTexImage2D(myBuffer.target, 0, tex_intformat,
728
myBuffer.texture_width, myBuffer.texture_height, 0,
729
myBuffer.format, myBuffer.type, myBuffer.pixels);
734
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
735
GUI::Surface* FrameBufferGL::createSurface(int width, int height) const
737
SDL_PixelFormat* fmt = myTexture->format;
739
SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 16,
740
fmt->Rmask, fmt->Gmask, fmt->Bmask, fmt->Amask);
742
return data ? new GUI::Surface(width, height, data) : NULL;
1488
p_glTexImage2D(myTexTarget, 0, GL_RGB5,
1489
myTexWidth, myTexHeight, 0,
1490
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, myTexture->pixels);
1492
// Do the same for the TV filter textures
1493
// Only do this if TV filters are enabled
1494
if(myTvFiltersEnabled)
1496
// Generate the generic filter texture
1497
p_glGenTextures(1, &myFilterTexID);
1498
p_glBindTexture(myTexTarget, myFilterTexID);
1499
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1500
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1501
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1502
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1503
// Make the initial texture, this will get overwritten later
1504
p_glCopyTexImage2D(myTexTarget, 0, GL_RGB5, 0, 0, myFilterTexWidth, myFilterTexHeight, 0);
1506
// Only do this if TV and color texture filters are enabled
1507
if(myFB.myUseTexture)
1509
// Generate the subpixel mask texture
1510
p_glGenTextures(1, &mySubMaskTexID);
1511
p_glBindTexture(myTexTarget, mySubMaskTexID);
1512
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1513
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1514
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1515
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1517
p_glTexImage2D(myTexTarget, 0, GL_RGB5,
1518
myFilterTexWidth, myFilterTexHeight, 0,
1519
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, mySubpixelTexture->pixels);
1522
// Only do this if TV and noise filters are enabled
1525
// Generate the noise mask textures
1526
p_glGenTextures(myNoiseNum, myNoiseMaskTexID);
1527
for(int i = 0; i < myNoiseNum; i++)
1529
p_glBindTexture(myTexTarget, myNoiseMaskTexID[i]);
1530
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1531
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1532
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1533
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1535
p_glTexImage2D(myTexTarget, 0, GL_RGB5,
1536
myFilterTexWidth, myFilterTexHeight, 0,
1537
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, myNoiseTexture[i]->pixels);
1541
// Only do this if TV and phosphor filters are enabled
1542
if(myFB.myUseGLPhosphor)
1544
// Generate the noise mask textures
1545
p_glGenTextures(1, &myPhosphorTexID);
1546
p_glBindTexture(myTexTarget, myPhosphorTexID);
1547
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1548
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1549
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1550
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1551
// Make the initial texture, this will get overwritten later
1552
p_glCopyTexImage2D(myTexTarget, 0, GL_RGB5, 0, 0, myFilterTexWidth, myFilterTexHeight, 0);
1557
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1558
void FBSurfaceGL::setFilter(const string& name)
1560
// We only do GL_NEAREST or GL_LINEAR for now
1561
GLint filter = GL_NEAREST;
1562
if(name == "linear")
1565
p_glBindTexture(myTexTarget, myTexID);
1566
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1567
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1568
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, filter);
1569
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, filter);
1571
// Do the same for the filter textures
1572
// Only do this if TV filters are enabled
1573
if(myTvFiltersEnabled)
1575
p_glBindTexture(myTexTarget, myFilterTexID);
1576
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1577
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1578
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, filter);
1579
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, filter);
1581
// Only do this if TV and color texture filters are enabled
1582
if(myFB.myUseTexture)
1584
p_glBindTexture(myTexTarget, mySubMaskTexID);
1585
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1586
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1587
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, filter);
1588
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, filter);
1591
// Only do this if TV and noise filters are enabled
1594
for(int i = 0; i < myNoiseNum; i++)
1596
p_glBindTexture(myTexTarget, myNoiseMaskTexID[i]);
1597
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1598
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1599
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, filter);
1600
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, filter);
1604
// Only do this if TV and phosphor filters are enabled
1605
if(myFB.myUseGLPhosphor)
1607
p_glBindTexture(myTexTarget, myPhosphorTexID);
1608
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1609
p_glTexParameteri(myTexTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1610
p_glTexParameteri(myTexTarget, GL_TEXTURE_MIN_FILTER, filter);
1611
p_glTexParameteri(myTexTarget, GL_TEXTURE_MAG_FILTER, filter);
1615
// The filtering has changed, so redraw the entire screen
1616
mySurfaceIsDirty = true;
1619
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1620
GLuint FBSurfaceGL::genShader(ShaderType type)
1627
fFile = "bleed.frag";
1628
fCode = (char*)GLShader::bleed_frag[0];
1631
fFile = "texture.frag";
1632
fCode = (char*)GLShader::texture_frag[0];
1635
fFile = "noise.frag";
1636
fCode = (char*)GLShader::noise_frag[0];
1639
fFile = "phosphor.frag";
1640
fCode = (char*)GLShader::phosphor_frag[0];
1642
case SHADER_TEXNOISE:
1643
fFile = "texture_noise.frag";
1644
fCode = (char*)GLShader::texture_noise_frag[0];
1648
// First try opening an external fragment file
1649
// These shader files are stored in 'BASEDIR/shaders/'
1650
char* buffer = NULL;
1651
const string& filename =
1652
myFB.myOSystem->baseDir() + BSPF_PATH_SEPARATOR + "shaders" +
1653
BSPF_PATH_SEPARATOR + fFile;
1654
ifstream in(filename.c_str());
1655
if(in && in.is_open())
1658
in.seekg(0, std::ios::end);
1659
streampos size = in.tellg();
1664
// Make buffer of proper size;
1665
buffer = new char[size+(streampos)1]; // +1 for '\0'
1668
in.read(buffer, size);
1669
buffer[in.gcount()] = '\0';
1675
// Make the shader program
1676
GLuint fShader = p_glCreateShader(GL_FRAGMENT_SHADER);
1677
GLuint program = p_glCreateProgram();
1678
p_glShaderSource(fShader, 1, (const char**)&fCode, NULL);
1679
p_glCompileShader(fShader);
1680
p_glAttachShader(program, fShader);
1681
p_glLinkProgram(program);
1683
// Go ahead and flag the shader for deletion so it is deleted once the program is
1684
p_glDeleteShader(fShader);
745
1692
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
746
1693
bool FrameBufferGL::myLibraryLoaded = false;
1695
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1696
float FrameBufferGL::myGLVersion = 0.0;
1698
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1699
bool FrameBufferGL::myGLSLAvailable = false;
1701
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1702
bool FrameBufferGL::myFBOAvailable = false;
748
1704
#endif // DISPLAY_OPENGL