Emulator core geared towards emulating ZZT and Super ZZT.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

render_opengl.c 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. /**
  2. * Copyright (c) 2018, 2019 Adrian Siekierka
  3. *
  4. * This file is part of Zeta.
  5. *
  6. * Zeta is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Zeta is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Zeta. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include "../zzt.h"
  23. #include "../render_software.h"
  24. #include "frontend_sdl.h"
  25. #ifdef USE_OPENGL
  26. #ifdef DEBUG_OPENGL
  27. #define OGL_GUARD() oglguard()
  28. static void oglguard(void) {
  29. GLenum err;
  30. while ((err = glGetError()) != GL_NO_ERROR) {
  31. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "OpenGL error: %d", err);
  32. }
  33. }
  34. #else
  35. #define OGL_GUARD() {}
  36. #endif
  37. static SDL_Texture *chartex = NULL;
  38. static SDL_Renderer *renderer = NULL;
  39. static SDL_Window *window = NULL;
  40. static SDL_GLContext gl_context;
  41. static int charw, charh;
  42. static int force_update;
  43. static int last_blink_mode;
  44. static SDL_Texture *create_texture_from_array(SDL_Renderer *renderer, int access, unsigned char *data, int height) {
  45. SDL_Texture *texture;
  46. SDL_Rect rect;
  47. Uint32 texdata[8 * height];
  48. Uint32* tptr;
  49. int ch, cx, cy, ctmp;
  50. rect.w = 8;
  51. rect.h = height;
  52. texture = SDL_CreateTexture(renderer,
  53. SDL_PIXELFORMAT_RGBA32,
  54. access,
  55. 16*rect.w, 16*rect.h);
  56. for (ch = 0; ch < 256; ch++) {
  57. rect.x = (ch & 0x0F) * rect.w;
  58. rect.y = (ch >> 4) * rect.h;
  59. tptr = texdata;
  60. for (cy = 0; cy < height; cy++, data++) {
  61. ctmp = data[0];
  62. for (cx = 0; cx < 8; cx++, tptr++, ctmp <<= 1) {
  63. *tptr = ((ctmp >> 7) & 1) * 0xFFFFFFFF;
  64. }
  65. }
  66. SDL_UpdateTexture(texture, &rect, texdata, 32);
  67. }
  68. return texture;
  69. }
  70. static void prepare_render_opengl(void) {
  71. SDL_Rect rect;
  72. int w, h, scale;
  73. SDL_GL_GetDrawableSize(window, &w, &h);
  74. calc_render_area(&rect, w, h, &scale, AREA_WITHOUT_SCALE);
  75. glViewport(0, 0, scale * (rect.w + (rect.x * 2)), scale * (rect.h + (rect.y * 2)));
  76. glMatrixMode(GL_PROJECTION);
  77. glLoadIdentity();
  78. #ifdef USE_OPENGL_ES
  79. glOrthof(-(rect.x), rect.w + (rect.x), rect.h + (rect.y), -(rect.y), -1, 1);
  80. #else
  81. glOrtho(-(rect.x), rect.w + (rect.x), rect.h + (rect.y), -(rect.y), -1, 1);
  82. #endif
  83. glClearColor(0, 0, 0, 1);
  84. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  85. }
  86. #define GLVX(i,width) ((i)*charw*(80/width))
  87. #define GLVY(i) ((i)*charh)
  88. #define GLTX(chr,i) ( ( ((chr)&0xF)+(i) )/16.0*1.0 )
  89. #define GLTY(chr,i) ( ( ((chr)>>4)+(i) )/16.0*1.0 )
  90. static unsigned short *ogl_buf_pos;
  91. static unsigned short *ogl_buf_pos40;
  92. static unsigned char *ogl_buf_col;
  93. static float *ogl_buf_tex;
  94. static unsigned char *ogl_buf_colcache;
  95. static float *ogl_buf_texcache;
  96. #ifdef USE_OPENGL_ES
  97. #define GL_COMPONENT_POINTS 6
  98. #else
  99. #define GL_COMPONENT_POINTS 4
  100. #endif
  101. static void update_opengl_colcache(u32* pal) {
  102. int size = GL_COMPONENT_POINTS * 4;
  103. for (int i = 0; i < size; i++) {
  104. for (int bpos = i*size; bpos < (i+1)*size; bpos+=4) {
  105. ogl_buf_colcache[bpos] = (pal[i] >> 16) & 0xFF;
  106. ogl_buf_colcache[bpos + 1] = (pal[i] >> 8) & 0xFF;
  107. ogl_buf_colcache[bpos + 2] = (pal[i] >> 0) & 0xFF;
  108. ogl_buf_colcache[bpos + 3] = 0xFF;
  109. }
  110. }
  111. }
  112. static void update_opengl_tables(void) {
  113. #ifndef USE_OPENGL_ES
  114. for (int tpos = 0; tpos < 256 * 8; tpos += 8) {
  115. u8 chr = tpos >> 3;
  116. ogl_buf_texcache[tpos] = GLTX(chr,0);
  117. ogl_buf_texcache[tpos+1] = GLTY(chr,0);
  118. ogl_buf_texcache[tpos+2] = GLTX(chr,1);
  119. ogl_buf_texcache[tpos+3] = GLTY(chr,0);
  120. ogl_buf_texcache[tpos+4] = GLTX(chr,1);
  121. ogl_buf_texcache[tpos+5] = GLTY(chr,1);
  122. ogl_buf_texcache[tpos+6] = GLTX(chr,0);
  123. ogl_buf_texcache[tpos+7] = GLTY(chr,1);
  124. }
  125. for (int i = 0; i < 2000; i++) {
  126. int x = i % 80;
  127. int y = i / 80;
  128. ogl_buf_pos[i * 8 + 0] = GLVX(x,80);
  129. ogl_buf_pos[i * 8 + 1] = GLVY(y);
  130. ogl_buf_pos[i * 8 + 2] = GLVX(x+1,80);
  131. ogl_buf_pos[i * 8 + 3] = GLVY(y);
  132. ogl_buf_pos[i * 8 + 4] = GLVX(x+1,80);
  133. ogl_buf_pos[i * 8 + 5] = GLVY(y+1);
  134. ogl_buf_pos[i * 8 + 6] = GLVX(x,80);
  135. ogl_buf_pos[i * 8 + 7] = GLVY(y+1);
  136. }
  137. for (int i = 0; i < 1000; i++) {
  138. int x = i % 40;
  139. int y = i / 40;
  140. ogl_buf_pos40[i * 8 + 0] = GLVX(x,40);
  141. ogl_buf_pos40[i * 8 + 1] = GLVY(y);
  142. ogl_buf_pos40[i * 8 + 2] = GLVX(x+1,40);
  143. ogl_buf_pos40[i * 8 + 3] = GLVY(y);
  144. ogl_buf_pos40[i * 8 + 4] = GLVX(x+1,40);
  145. ogl_buf_pos40[i * 8 + 5] = GLVY(y+1);
  146. ogl_buf_pos40[i * 8 + 6] = GLVX(x,40);
  147. ogl_buf_pos40[i * 8 + 7] = GLVY(y+1);
  148. }
  149. #else
  150. for (int tpos = 0; tpos < 256 * 12; tpos += 12) {
  151. u8 chr = tpos / 12;
  152. ogl_buf_texcache[tpos] = GLTX(chr,0);
  153. ogl_buf_texcache[tpos+1] = GLTY(chr,0);
  154. ogl_buf_texcache[tpos+2] = GLTX(chr,1);
  155. ogl_buf_texcache[tpos+3] = GLTY(chr,0);
  156. ogl_buf_texcache[tpos+4] = GLTX(chr,1);
  157. ogl_buf_texcache[tpos+5] = GLTY(chr,1);
  158. ogl_buf_texcache[tpos+6] = GLTX(chr,0);
  159. ogl_buf_texcache[tpos+7] = GLTY(chr,0);
  160. ogl_buf_texcache[tpos+8] = GLTX(chr,1);
  161. ogl_buf_texcache[tpos+9] = GLTY(chr,1);
  162. ogl_buf_texcache[tpos+10] = GLTX(chr,0);
  163. ogl_buf_texcache[tpos+11] = GLTY(chr,1);
  164. }
  165. for (int i = 0; i < 2000; i++) {
  166. int x = i % 80;
  167. int y = i / 80;
  168. ogl_buf_pos[i * 12 + 0] = GLVX(x,80);
  169. ogl_buf_pos[i * 12 + 1] = GLVY(y);
  170. ogl_buf_pos[i * 12 + 2] = GLVX(x+1,80);
  171. ogl_buf_pos[i * 12 + 3] = GLVY(y);
  172. ogl_buf_pos[i * 12 + 4] = GLVX(x+1,80);
  173. ogl_buf_pos[i * 12 + 5] = GLVY(y+1);
  174. ogl_buf_pos[i * 12 + 6] = GLVX(x,80);
  175. ogl_buf_pos[i * 12 + 7] = GLVY(y);
  176. ogl_buf_pos[i * 12 + 8] = GLVX(x+1,80);
  177. ogl_buf_pos[i * 12 + 9] = GLVY(y+1);
  178. ogl_buf_pos[i * 12 + 10] = GLVX(x,80);
  179. ogl_buf_pos[i * 12 + 11] = GLVY(y+1);
  180. }
  181. for (int i = 0; i < 1000; i++) {
  182. int x = i % 40;
  183. int y = i / 40;
  184. ogl_buf_pos40[i * 12 + 0] = GLVX(x,40);
  185. ogl_buf_pos40[i * 12 + 1] = GLVY(y);
  186. ogl_buf_pos40[i * 12 + 2] = GLVX(x+1,40);
  187. ogl_buf_pos40[i * 12 + 3] = GLVY(y);
  188. ogl_buf_pos40[i * 12 + 4] = GLVX(x+1,40);
  189. ogl_buf_pos40[i * 12 + 5] = GLVY(y+1);
  190. ogl_buf_pos40[i * 12 + 6] = GLVX(x,40);
  191. ogl_buf_pos40[i * 12 + 7] = GLVY(y);
  192. ogl_buf_pos40[i * 12 + 8] = GLVX(x+1,40);
  193. ogl_buf_pos40[i * 12 + 9] = GLVY(y+1);
  194. ogl_buf_pos40[i * 12 + 10] = GLVX(x,40);
  195. ogl_buf_pos40[i * 12 + 11] = GLVY(y+1);
  196. }
  197. #endif
  198. }
  199. static void init_opengl_tables(void) {
  200. ogl_buf_pos = malloc((80 * 25) * GL_COMPONENT_POINTS * 2 * sizeof(short));
  201. ogl_buf_pos40 = malloc((40 * 25) * GL_COMPONENT_POINTS * 2 * sizeof(short));
  202. ogl_buf_col = malloc(2 * (80 * 25) * GL_COMPONENT_POINTS * 4 * sizeof(char));
  203. ogl_buf_tex = malloc((80 * 25) * GL_COMPONENT_POINTS * 2 * sizeof(float));
  204. ogl_buf_colcache = malloc(16 * 4 * GL_COMPONENT_POINTS * sizeof(char));
  205. ogl_buf_texcache = malloc(256 * 2 * GL_COMPONENT_POINTS * sizeof(float));
  206. memset(ogl_buf_colcache, 0, 16 * 4 * GL_COMPONENT_POINTS * sizeof(char));
  207. }
  208. static void free_opengl_tables(void) {
  209. free(ogl_buf_texcache);
  210. free(ogl_buf_colcache);
  211. free(ogl_buf_tex);
  212. free(ogl_buf_col);
  213. free(ogl_buf_pos40);
  214. free(ogl_buf_pos);
  215. }
  216. static void render_opengl(u8 *vram, int regen_visuals, int blink_mode) {
  217. float texw, texh;
  218. int width = (zzt_video_mode() & 2) ? 80 : 40;
  219. prepare_render_opengl();
  220. // generate visual data
  221. int vpos = 0;
  222. if (regen_visuals) for (int y = 0; y < 25; y++) {
  223. for (int x = 0; x < width; x++, vpos += 2) {
  224. u8 chr = vram[vpos];
  225. u8 col = vram[vpos+1];
  226. u8 bgcol = col >> 4;
  227. u8 fgcol = col & 0xF;
  228. if (blink_mode != BLINK_MODE_NONE) {
  229. if (bgcol >= 0x8) {
  230. bgcol &= 0x7;
  231. if (blink_mode == BLINK_MODE_2) fgcol = bgcol;
  232. }
  233. }
  234. int bpos_s = vpos * 2 * GL_COMPONENT_POINTS;
  235. memcpy(ogl_buf_col + bpos_s, ogl_buf_colcache + (4*GL_COMPONENT_POINTS*bgcol), 4*GL_COMPONENT_POINTS*sizeof(char));
  236. memcpy(ogl_buf_col + bpos_s + (8000 * GL_COMPONENT_POINTS), ogl_buf_colcache + (4*GL_COMPONENT_POINTS*fgcol), 4*GL_COMPONENT_POINTS*sizeof(char));
  237. int tpos_s = bpos_s >> 1;
  238. memcpy(ogl_buf_tex + tpos_s, ogl_buf_texcache + chr*2*GL_COMPONENT_POINTS, 2*GL_COMPONENT_POINTS*sizeof(float));
  239. }
  240. }
  241. // pass 1: background colors
  242. glDisable(GL_ALPHA_TEST);
  243. glDisable(GL_DEPTH_TEST);
  244. glDisable(GL_TEXTURE_2D);
  245. glDisable(GL_CULL_FACE);
  246. glEnableClientState(GL_VERTEX_ARRAY);
  247. glEnableClientState(GL_COLOR_ARRAY);
  248. glVertexPointer(2, GL_SHORT, 0, width == 40 ? ogl_buf_pos40 : ogl_buf_pos);
  249. glColorPointer(4, GL_UNSIGNED_BYTE, 0, ogl_buf_col);
  250. #ifdef USE_OPENGL_ES
  251. glDrawArrays(GL_TRIANGLES, 0, width * 25 * 6);
  252. #else
  253. glDrawArrays(GL_QUADS, 0, width * 25 * 4);
  254. #endif
  255. OGL_GUARD();
  256. // pass 2: foreground colors
  257. if (chartex != NULL) {
  258. if (SDL_GL_BindTexture(chartex, &texw, &texh)) {
  259. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Could not bind OpenGL texture! %s", SDL_GetError());
  260. }
  261. glAlphaFunc(GL_GREATER, 0.5);
  262. glEnable(GL_ALPHA_TEST);
  263. glEnable(GL_TEXTURE_2D);
  264. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  265. glColorPointer(4, GL_UNSIGNED_BYTE, 0, ogl_buf_col + (80 * 25 * 4 * GL_COMPONENT_POINTS * sizeof(char)));
  266. glTexCoordPointer(2, GL_FLOAT, 0, ogl_buf_tex);
  267. #ifdef USE_OPENGL_ES
  268. glDrawArrays(GL_TRIANGLES, 0, width * 25 * 6);
  269. #else
  270. glDrawArrays(GL_QUADS, 0, width * 25 * 4);
  271. #endif
  272. OGL_GUARD();
  273. glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  274. glDisableClientState(GL_VERTEX_ARRAY);
  275. glDisableClientState(GL_COLOR_ARRAY);
  276. SDL_GL_UnbindTexture(chartex);
  277. }
  278. }
  279. static int sdl_render_opengl_init(void) {
  280. init_opengl_tables();
  281. #ifdef USE_OPENGL_ES
  282. SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
  283. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
  284. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
  285. #else
  286. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
  287. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 4);
  288. #endif
  289. SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  290. SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
  291. window = SDL_CreateWindow("Zeta", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
  292. 640, 350, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
  293. if (window == NULL) {
  294. return -1;
  295. } else if ((gl_context = SDL_GL_CreateContext(window)) == NULL) {
  296. SDL_DestroyWindow(window);
  297. return -1;
  298. }
  299. SDL_GL_SetSwapInterval(1);
  300. SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
  301. SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
  302. renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
  303. if (renderer == NULL) {
  304. SDL_DestroyWindow(window);
  305. return -1;
  306. }
  307. return 0;
  308. }
  309. static void sdl_render_opengl_deinit(void) {
  310. if (chartex != NULL) {
  311. SDL_DestroyTexture(chartex);
  312. }
  313. SDL_DestroyRenderer(renderer);
  314. SDL_DestroyWindow(window);
  315. free_opengl_tables();
  316. }
  317. static void sdl_render_opengl_update_charset(int charw_arg, int charh_arg, u8 *data_arg) {
  318. charw = charw_arg;
  319. charh = charh_arg;
  320. if (chartex != NULL) SDL_DestroyTexture(chartex);
  321. chartex = create_texture_from_array(renderer, SDL_TEXTUREACCESS_STATIC, data_arg, charh_arg);
  322. SDL_SetTextureBlendMode(chartex, SDL_BLENDMODE_BLEND);
  323. update_opengl_tables();
  324. force_update = 1;
  325. }
  326. static void sdl_render_opengl_update_palette(u32 *data_arg) {
  327. update_opengl_colcache(data_arg);
  328. force_update = 1;
  329. }
  330. static void sdl_render_opengl_update_vram(u8 *vram) {
  331. force_update = 1;
  332. }
  333. static void sdl_render_opengl_draw(u8 *vram, int blink_mode) {
  334. // TODO: reimplement should_render flag
  335. int should_render = force_update || (blink_mode != last_blink_mode);
  336. render_opengl(vram, should_render, blink_mode);
  337. SDL_GL_SwapWindow(window);
  338. last_blink_mode = blink_mode;
  339. force_update = 0;
  340. }
  341. static SDL_Window *sdl_render_opengl_get_window(void) {
  342. return window;
  343. }
  344. sdl_renderer sdl_renderer_opengl = {
  345. sdl_render_opengl_init,
  346. sdl_render_opengl_deinit,
  347. sdl_render_opengl_update_charset,
  348. sdl_render_opengl_update_palette,
  349. sdl_render_opengl_update_vram,
  350. sdl_render_opengl_draw,
  351. sdl_render_opengl_get_window
  352. };
  353. #endif /* USE_OPENGL */