Emulator core geared towards emulating ZZT and Super ZZT.
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

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 */