GL2D.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. #include "StdInc.h"
  2. #include <SDL.h>
  3. #include <SDL_video.h>
  4. #include <SDL_syswm.h>
  5. #include <SDL_opengl.h>
  6. #if __unix__
  7. # if !SDL_VIDEO_OPENGL_GLX
  8. # error SDL_VIDEO_OPENGL_GLX required
  9. # endif
  10. # include <GL/glx.h>
  11. # define _GLX 1
  12. #endif
  13. #include "GL2D.h"
  14. namespace GL2D
  15. {
  16. // Variables initialized by initVideo, updated by setScreenRes:
  17. static SDL_SysWMinfo wmInfo;
  18. #if _GLX
  19. static GLXContext glxCtx;
  20. #endif
  21. ui32 screenWidth = 0, screenHeight = 0; // Screen/Window size
  22. // OpenGL 1.3 functions pointers
  23. PFNGLACTIVETEXTUREPROC glActiveTexture;
  24. // OpenGL 2.0 functions pointers
  25. PFNGLCREATEPROGRAMPROC glCreateProgram;
  26. PFNGLCREATESHADERPROC glCreateShader;
  27. PFNGLDELETESHADERPROC glDeleteShader;
  28. PFNGLSHADERSOURCEPROC glShaderSource;
  29. PFNGLCOMPILESHADERPROC glCompileShader;
  30. PFNGLGETSHADERIVPROC glGetShaderiv;
  31. PFNGLATTACHSHADERPROC glAttachShader;
  32. PFNGLLINKPROGRAMPROC glLinkProgram;
  33. PFNGLGETPROGRAMIVPROC glGetProgramiv;
  34. PFNGLUSEPROGRAMPROC glUseProgram;
  35. PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
  36. PFNGLUNIFORM1IPROC glUniform1i;
  37. PFNGLUNIFORM2IPROC glUniform2i;
  38. PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
  39. PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
  40. // Shaders' sources
  41. static const char frag_palette_bitmap[] = (
  42. "#version 150\n"
  43. "uniform usampler2DRect bitmap;"
  44. "uniform sampler1D palette;"
  45. "uniform ivec2 coordOffs;"
  46. "layout (origin_upper_left) in vec4 gl_FragCoord;"
  47. "void main(){"
  48. "gl_FragColor = texelFetch(palette, int(texelFetch(bitmap, ivec2(gl_FragCoord.xy) - coordOffs).r), 0);"
  49. "}"
  50. );
  51. // Programs' handlers
  52. static GLuint currentProgram = 0;
  53. static GLuint colorizeProgram = 0;
  54. static GLuint paletteBitmapProgram = 0;
  55. // Uniforms handlers
  56. static GLint coord_uniform = -1;
  57. // Check for errors
  58. void checkErrors(const std::string & place)
  59. {
  60. GLenum err;
  61. while ((err = glGetError()) != GL_NO_ERROR)
  62. {
  63. tlog1 << "GL Error in " << place << "; code = " << err << std::endl;
  64. }
  65. }
  66. // Print out the information log for a shader object
  67. void printInfoLog(PFNGLGETPROGRAMINFOLOGPROC logFunc, GLuint object, GLint status)
  68. {
  69. if (logFunc != nullptr)
  70. {
  71. GLsizei infoLogLength = 0;
  72. GLchar infoLog[1024];
  73. logFunc(object, sizeof(infoLog)-1, &infoLogLength, infoLog);
  74. if (infoLogLength > 0) (status ? tlog0 : tlog1) << infoLog;
  75. }
  76. }
  77. GLuint makeShaderProgram(const char * frg_src)
  78. {
  79. // Creating a fragment shader object
  80. GLuint shader_object = glCreateShader(GL_FRAGMENT_SHADER);
  81. if (shader_object == 0) return 0;
  82. glShaderSource(shader_object, 1, &frg_src, nullptr); // assigning the shader source
  83. glCompileShader(shader_object); // Compiling the shader
  84. GLint ret;
  85. glGetShaderiv(shader_object, GL_COMPILE_STATUS, &ret);
  86. printInfoLog(glGetShaderInfoLog, shader_object, ret);
  87. if (ret == GL_TRUE)
  88. {
  89. GLuint program_object = glCreateProgram(); // Creating a program object
  90. glAttachShader(program_object, shader_object); // Attaching the shader into program
  91. // Link the shaders into a complete GLSL program.
  92. glLinkProgram(program_object);
  93. ret = GL_FALSE;
  94. glGetProgramiv(program_object, GL_LINK_STATUS, &ret);
  95. printInfoLog(glGetProgramInfoLog, program_object, ret);
  96. if (ret == GL_TRUE) return program_object;
  97. }
  98. glDeleteShader(shader_object);
  99. checkErrors("makeShaderProgram");
  100. return 0;
  101. }
  102. void initVideo(ui32 w, ui32 h, bool fullscreen)
  103. {
  104. SDL_VERSION(&wmInfo.version);
  105. SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  106. if (!setScreenRes(w, h, fullscreen))
  107. {
  108. throw std::runtime_error("Video mode setting failed\n");
  109. }
  110. glClearColor(0, 0, 0, 0); // Black Background
  111. glClear(GL_COLOR_BUFFER_BIT);
  112. SDL_GL_SwapBuffers();
  113. // Dynamic-linked OpenGL 1.3 and 2.0 functions - required to 2D rendeing
  114. if ( (glActiveTexture = (PFNGLACTIVETEXTUREPROC) SDL_GL_GetProcAddress("glActiveTexture")) == nullptr
  115. || (glCreateProgram = (PFNGLCREATEPROGRAMPROC) SDL_GL_GetProcAddress("glCreateProgram")) == nullptr
  116. || (glCreateShader = (PFNGLCREATESHADERPROC) SDL_GL_GetProcAddress("glCreateShader")) == nullptr
  117. || (glDeleteShader = (PFNGLDELETESHADERPROC) SDL_GL_GetProcAddress("glDeleteShader")) == nullptr
  118. || (glShaderSource = (PFNGLSHADERSOURCEPROC) SDL_GL_GetProcAddress("glShaderSource")) == nullptr
  119. || (glCompileShader = (PFNGLCOMPILESHADERPROC) SDL_GL_GetProcAddress("glCompileShader")) == nullptr
  120. || (glGetShaderiv = (PFNGLGETSHADERIVPROC) SDL_GL_GetProcAddress("glGetShaderiv")) == nullptr
  121. || (glAttachShader = (PFNGLATTACHSHADERPROC) SDL_GL_GetProcAddress("glAttachShader")) == nullptr
  122. || (glLinkProgram = (PFNGLLINKPROGRAMPROC) SDL_GL_GetProcAddress("glLinkProgram")) == nullptr
  123. || (glGetProgramiv = (PFNGLGETPROGRAMIVPROC) SDL_GL_GetProcAddress("glGetProgramiv")) == nullptr
  124. || (glUseProgram = (PFNGLUSEPROGRAMPROC) SDL_GL_GetProcAddress("glUseProgram")) == nullptr
  125. || (glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) SDL_GL_GetProcAddress("glGetUniformLocation")) == nullptr
  126. || (glUniform1i = (PFNGLUNIFORM1IPROC) SDL_GL_GetProcAddress("glUniform1i")) == nullptr
  127. || (glUniform2i = (PFNGLUNIFORM2IPROC) SDL_GL_GetProcAddress("glUniform2i")) == nullptr
  128. )
  129. {
  130. tlog1 << "GL2D Error: OpenGL2 Extenstions are not available\n";
  131. tlog1 << "SDL says: " << SDL_GetError() << std::endl;
  132. throw std::runtime_error("initVideo failed - OpenGL2 Extenstions are not available\n");
  133. }
  134. glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) SDL_GL_GetProcAddress("glGetProgramInfoLog"); // not required
  135. glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) SDL_GL_GetProcAddress("glGetShaderInfoLog"); // not required
  136. glDisable(GL_DEPTH_TEST);
  137. glDisable(GL_LIGHTING);
  138. glDisable(GL_DITHER);
  139. glDisable(GL_TEXTURE_2D);
  140. glEnable(GL_TEXTURE_RECTANGLE);
  141. glEnable(GL_BLEND);
  142. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  143. checkErrors("initVideo - early");
  144. paletteBitmapProgram = makeShaderProgram(frag_palette_bitmap);
  145. if (paletteBitmapProgram == 0)
  146. {
  147. throw std::runtime_error("initVideo failed - GL shader for palleted bitmaps isn't compiled\n");
  148. }
  149. GLint bitmap_uniform = glGetUniformLocation(paletteBitmapProgram, "bitmap");
  150. GLint palette_uniform = glGetUniformLocation(paletteBitmapProgram, "palette");
  151. coord_uniform = glGetUniformLocation(paletteBitmapProgram, "coordOffs");
  152. glUseProgram(currentProgram = paletteBitmapProgram);
  153. glUniform1i(bitmap_uniform, 0);
  154. glUniform1i(palette_uniform, 1);
  155. checkErrors("initVideo - late");
  156. // unhook OpenGL context from display context/window
  157. #ifdef _WIN32
  158. if (!wglMakeCurrent(NULL, NULL))
  159. {
  160. tlog1 << "GL2D Error: wglMakeCurrent failed while unhooking GL context\n";
  161. tlog1 << "WinAPI returns error code = " << GetLastError() << std::endl;
  162. throw std::runtime_error("initVideo failed - wglMakeCurrent\n");
  163. }
  164. #elif _GLX
  165. glxCtx = glXGetCurrentContext();
  166. if (glxCtx == nullptr)
  167. {
  168. tlog1 << "GL2D Error: glXGetCurrentContext returns NULL\n";
  169. throw std::runtime_error("initVideo failed - SDL didn't create GLX context?!\n");
  170. }
  171. if (!glXMakeCurrent(wmInfo.info.x11.display, None, nullptr))
  172. {
  173. tlog1 << "GL2D Error: glXMakeCurrent failed while unhooking GL context\n";
  174. throw std::runtime_error("initVideo failed - glXMakeCurrent\n");
  175. }
  176. #endif
  177. }
  178. void attachToCurrentThread()
  179. {
  180. #ifdef _WIN32
  181. HDC hdc = GetDC(wmInfo.window);
  182. if (!wglMakeCurrent(hdc, wmInfo.hglrc))
  183. {
  184. tlog1 << "GL2D Error: wglMakeCurrent failed while hooking GL context\n";
  185. tlog1 << "WinAPI returns error code = " << GetLastError() << std::endl;
  186. throw std::runtime_error("attachToCurrentThread: wglMakeCurrent failed\n");
  187. }
  188. #elif _GLX
  189. if (!glXMakeCurrent(wmInfo.info.x11.display, wmInfo.info.x11.window, glxCtx))
  190. {
  191. tlog1 << "GL2D Error: glXMakeCurrent failed while hooking GL context\n";
  192. throw std::runtime_error("attachToCurrentThread: glXMakeCurrent failed\n");
  193. }
  194. #endif
  195. }
  196. bool setScreenRes(ui32 w, ui32 h, bool fullscreen)
  197. {
  198. // Try to use the best screen depth for the display
  199. int suggestedBpp = SDL_VideoModeOK(w, h, 32, SDL_OPENGL | SDL_ANYFORMAT | (fullscreen?SDL_FULLSCREEN:0));
  200. if(suggestedBpp == 0)
  201. {
  202. tlog1 << "GL2D Error: SDL says that " << w << "x" << h << " resolution is not available!\n";
  203. return false;
  204. }
  205. if(suggestedBpp != 32)
  206. {
  207. tlog2 << "GL2D Warning: SDL suggests to use " << suggestedBpp << " bpp instead of 32 bpp\n";
  208. }
  209. if(SDL_SetVideoMode(w, h, suggestedBpp, SDL_OPENGL | SDL_ANYFORMAT | (fullscreen?SDL_FULLSCREEN:0)) == NULL)
  210. {
  211. tlog1 << "GL2D Error: Video mode setting failed (" << w << "x" << h << "x" << suggestedBpp << "bpp)\n";
  212. return false;
  213. }
  214. screenWidth = w; screenHeight = h;
  215. int getwm = SDL_GetWMInfo(&wmInfo);
  216. if (getwm != 1)
  217. {
  218. tlog1 << "GL2D Error: SDL_GetWMInfo returns " << getwm << std::endl;
  219. tlog1 << "SDL says: " << SDL_GetError() << std::endl;
  220. return false;
  221. }
  222. glViewport(0, 0, w, h);
  223. glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
  224. glLoadIdentity(); // Reset The Projection Matrix
  225. glOrtho(0, w, h, 0, 0, 1);
  226. glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
  227. glLoadIdentity(); // Reset The Modelview Matrix
  228. glTranslatef(0.375, 0.375, 0); // Displacement trick for exact pixelization
  229. return true;
  230. }
  231. void assignTexture(ui32 slot, ui32 type, ui32 handle)
  232. {
  233. glActiveTexture(slot);
  234. glBindTexture(type, handle);
  235. }
  236. void useNoShader()
  237. {
  238. if (currentProgram != 0) {
  239. glUseProgram(currentProgram = 0);
  240. }
  241. }
  242. void useColorizeShader(const float cm[3][3])
  243. {
  244. if (currentProgram != colorizeProgram) {
  245. glUseProgram(currentProgram = colorizeProgram);
  246. }
  247. }
  248. void usePaletteBitmapShader(si32 x, si32 y)
  249. {
  250. if (currentProgram != paletteBitmapProgram) {
  251. glUseProgram(currentProgram = paletteBitmapProgram);
  252. }
  253. glUniform2i(coord_uniform, x, y);
  254. }
  255. void usePaletteBitmapShader(si32 x, si32 y, const float cm[3][3])
  256. {
  257. if (currentProgram != paletteBitmapProgram) {
  258. glUseProgram(currentProgram = paletteBitmapProgram);
  259. }
  260. glUniform2i(coord_uniform, x, y);
  261. }
  262. }