]> git.sesse.net Git - pistorm/blob - platforms/amiga/rtg/rtg-output-raylib.c
672eab730b9ba22dd7098f92c5c2283dc4ee3c9c
[pistorm] / platforms / amiga / rtg / rtg-output-raylib.c
1 // SPDX-License-Identifier: MIT
2
3 #include "config_file/config_file.h"
4 #include "emulator.h"
5 #include "rtg.h"
6
7 #include "raylib/raylib.h"
8
9 #include <pthread.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15
16 #define RTG_INIT_ERR(a) { printf(a); *data->running = 0; }
17
18 uint8_t busy = 0, rtg_on = 0, rtg_initialized = 0;
19 extern uint8_t *rtg_mem;
20 extern uint32_t framebuffer_addr;
21 extern uint32_t framebuffer_addr_adj;
22
23 extern uint16_t rtg_display_width, rtg_display_height;
24 extern uint16_t rtg_display_format;
25 extern uint16_t rtg_pitch, rtg_total_rows;
26 extern uint16_t rtg_offset_x, rtg_offset_y;
27
28 static pthread_t thread_id;
29 static uint8_t mouse_cursor_enabled = 0, cursor_image_updated = 0, updating_screen = 0, debug_palette = 0, show_fps = 0;
30 static uint8_t mouse_cursor_w = 16, mouse_cursor_h = 16;
31 static int16_t mouse_cursor_x = 0, mouse_cursor_y = 0;
32
33 struct rtg_shared_data {
34     uint16_t *width, *height;
35     uint16_t *format, *pitch;
36     uint16_t *offset_x, *offset_y;
37     uint8_t *memory;
38     uint32_t *addr;
39     uint8_t *running;
40 };
41
42 struct rtg_shared_data rtg_share_data;
43 static uint32_t palette[256];
44 static uint32_t cursor_palette[256];
45
46 uint32_t cursor_data[256 * 256];
47
48 void rtg_update_screen() {}
49
50 uint32_t rtg_to_raylib[RTGFMT_NUM] = {
51     PIXELFORMAT_UNCOMPRESSED_GRAYSCALE,
52     PIXELFORMAT_UNCOMPRESSED_R5G6B5,
53     PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
54     PIXELFORMAT_UNCOMPRESSED_R5G5B5A1,
55 };
56
57 uint32_t rtg_pixel_size[RTGFMT_NUM] = {
58     1,
59     2,
60     4,
61     2,
62 };
63
64 void *rtgThread(void *args) {
65
66     printf("RTG thread running\n");
67     fflush(stdout);
68
69     int reinit = 0;
70     rtg_on = 1;
71
72     uint32_t *indexed_buf = NULL;
73
74     rtg_share_data.format = &rtg_display_format;
75     rtg_share_data.width = &rtg_display_width;
76     rtg_share_data.height = &rtg_display_height;
77     rtg_share_data.pitch = &rtg_pitch;
78     rtg_share_data.offset_x = &rtg_offset_x;
79     rtg_share_data.offset_y = &rtg_offset_y;
80     rtg_share_data.memory = rtg_mem;
81     rtg_share_data.running = &rtg_on;
82     rtg_share_data.addr = &framebuffer_addr_adj;
83     struct rtg_shared_data *data = &rtg_share_data;
84
85     uint16_t width = rtg_display_width;
86     uint16_t height = rtg_display_height;
87     uint16_t format = rtg_display_format;
88     uint16_t pitch = rtg_pitch;
89
90     Texture raylib_texture, raylib_cursor_texture;
91     Texture raylib_clut_texture;
92     Image raylib_fb, raylib_cursor, raylib_clut;
93
94     InitWindow(GetScreenWidth(), GetScreenHeight(), "Pistorm RTG");
95     HideCursor();
96     SetTargetFPS(60);
97
98         Color bef = { 0, 64, 128, 255 };
99     float scale_x = 1.0f, scale_y = 1.0f;
100
101     Shader clut_shader = LoadShader(NULL, "platforms/amiga/rtg/clut.shader");
102     Shader swizzle_shader = LoadShader(NULL, "platforms/amiga/rtg/argbswizzle.shader");
103     int clut_loc = GetShaderLocation(clut_shader, "texture1");
104
105     raylib_clut.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
106     raylib_clut.width = 256;
107     raylib_clut.height = 1;
108     raylib_clut.mipmaps = 1;
109     raylib_clut.data = palette;
110
111     raylib_clut_texture = LoadTextureFromImage(raylib_clut);
112
113     raylib_cursor.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
114     raylib_cursor.width = 256;
115     raylib_cursor.height = 256;
116     raylib_cursor.mipmaps = 1;
117     raylib_cursor.data = cursor_data;
118     raylib_cursor_texture = LoadTextureFromImage(raylib_cursor);
119
120     Rectangle srcrect, dstscale;
121     Vector2 origin;
122
123 reinit_raylib:;
124     if (reinit) {
125         printf("Reinitializing raylib...\n");
126         width = rtg_display_width;
127         height = rtg_display_height;
128         format = rtg_display_format;
129         pitch = rtg_pitch;
130         if (indexed_buf) {
131             free(indexed_buf);
132             indexed_buf = NULL;
133         }
134         UnloadTexture(raylib_texture);
135         reinit = 0;
136     }
137
138     printf("Creating %dx%d raylib window...\n", width, height);
139
140     printf("Setting up raylib framebuffer image.\n");
141     raylib_fb.format = rtg_to_raylib[format];
142
143     switch (format) {
144         case RTGFMT_RBG565:
145             raylib_fb.width = width;
146             indexed_buf = calloc(1, width * height * 2);
147             break;
148         default:
149             raylib_fb.width = pitch / rtg_pixel_size[format];
150             break;
151     }
152     raylib_fb.height = height;
153         raylib_fb.mipmaps = 1;
154         raylib_fb.data = &data->memory[*data->addr];
155     printf("Width: %d\nPitch: %d\nBPP: %d\n", raylib_fb.width, pitch, rtg_pixel_size[format]);
156
157     raylib_texture = LoadTextureFromImage(raylib_fb);
158
159     srcrect.x = srcrect.y = 0;
160     srcrect.width = width;
161     srcrect.height = height;
162     dstscale.x = dstscale.y = 0;
163     dstscale.width = width;
164     dstscale.height = height;
165
166     if (dstscale.height * 2 <= GetScreenHeight()) {
167         if (width == 320) {
168             if (GetScreenHeight() == 720) {
169                 dstscale.width = 960;
170                 dstscale.height = 720;
171             } else if (GetScreenHeight() == 1080) {
172                 dstscale.width = 1440;
173                 dstscale.height = 1080;
174             }
175         } else {
176             while (dstscale.height + height <= GetScreenHeight()) {
177                 dstscale.height += height;
178                 dstscale.width += width;
179             }
180         }
181     } else if (dstscale.width > GetScreenWidth() || dstscale.height > GetScreenHeight()) {
182         if (dstscale.width > GetScreenWidth()) {
183             dstscale.height = dstscale.height * ((float)GetScreenWidth() / (float)width);
184             dstscale.width = GetScreenWidth();
185         }
186         if (dstscale.height > GetScreenHeight()) {
187             dstscale.width = dstscale.width * ((float)GetScreenHeight() / (float)height);
188             dstscale.height = GetScreenHeight();
189         }
190     }
191
192     scale_x = dstscale.width / (float)width;
193     scale_y = dstscale.height / (float)height;
194
195     origin.x = (dstscale.width - GetScreenWidth()) * 0.5;
196     origin.y = (dstscale.height - GetScreenHeight()) * 0.5;
197
198     while (1) {
199         if (rtg_on) {
200             BeginDrawing();
201             updating_screen = 1;
202
203             switch (format) {
204                 case RTGFMT_8BIT:
205                     UpdateTexture(raylib_clut_texture, palette);
206                     BeginShaderMode(clut_shader);
207                     SetShaderValueTexture(clut_shader, clut_loc, raylib_clut_texture);
208                     break;
209                 case RTGFMT_RGB32:
210                     BeginShaderMode(swizzle_shader);
211                     break;
212             }
213             
214             DrawTexturePro(raylib_texture, srcrect, dstscale, origin, 0.0f, RAYWHITE);
215
216             switch (format) {
217                 case RTGFMT_8BIT:
218                 case RTGFMT_RGB32:
219                     EndShaderMode();
220                     break;
221             }
222
223             if (mouse_cursor_enabled) {
224                 float mc_x = mouse_cursor_x - rtg_offset_x;
225                 float mc_y = mouse_cursor_y - rtg_offset_y;
226                 Rectangle cursor_srcrect = { 0, 0, mouse_cursor_w, mouse_cursor_h };
227                 Rectangle dstrect = { mc_x * scale_x, mc_y * scale_y, (float)mouse_cursor_w * scale_x, (float)mouse_cursor_h * scale_y };
228                 DrawTexturePro(raylib_cursor_texture, cursor_srcrect, dstrect, origin, 0.0f, RAYWHITE);
229             }
230
231             if (debug_palette) {
232                 if (format == RTGFMT_8BIT) {
233                     Rectangle srcrect = { 0, 0, 256, 1 };
234                     Rectangle dstrect = { 0, 0, 1024, 8 };
235                     DrawTexturePro(raylib_clut_texture, srcrect, dstrect, origin, 0.0f, RAYWHITE);
236                 }
237             }
238
239             if (show_fps) {
240                 DrawFPS(GetScreenWidth() - 128, 0);
241             }
242
243             EndDrawing();
244             if (format == RTGFMT_RBG565) {
245                 for (int y = 0; y < height; y++) {
246                     for (int x = 0; x < width; x++) {
247                         ((uint16_t *)indexed_buf)[x + (y * width)] = be16toh(((uint16_t *)data->memory)[(*data->addr / 2) + x + (y * (pitch / 2))]);
248                     }
249                 }
250                 UpdateTexture(raylib_texture, indexed_buf);
251             }
252             else {
253                 UpdateTexture(raylib_texture, &data->memory[*data->addr]);
254             }
255             if (cursor_image_updated) {
256                 UpdateTexture(raylib_cursor_texture, cursor_data);
257                 cursor_image_updated = 0;
258             }
259             updating_screen = 0;
260         } else {
261             BeginDrawing();
262             ClearBackground(bef);
263             DrawText("RTG is currently sleeping.", 16, 16, 12, RAYWHITE);
264             EndDrawing();
265         }
266         if (pitch != *data->pitch || height != *data->height || width != *data->width || format != *data->format) {
267             printf("Reinitializing due to something change.\n");
268             reinit = 1;
269             goto shutdown_raylib;
270         }
271         /*if (!rtg_on) {
272             goto shutdown_raylib;
273         }*/
274     }
275
276     rtg_initialized = 0;
277     printf("RTG thread shut down.\n");
278
279 shutdown_raylib:;
280
281     if (reinit)
282         goto reinit_raylib;
283
284     if (indexed_buf)
285         free(indexed_buf);
286
287     UnloadTexture(raylib_texture);
288     UnloadShader(clut_shader);
289
290     CloseWindow();
291
292     return args;
293 }
294
295 void rtg_set_clut_entry(uint8_t index, uint32_t xrgb) {
296     //palette[index] = xrgb;
297     unsigned char *src = (unsigned char *)&xrgb;
298     unsigned char *dst = (unsigned char *)&palette[index];
299     dst[0] = src[2];
300     dst[1] = src[1];
301     dst[2] = src[0];
302     dst[3] = 0xFF;
303 }
304
305 void rtg_init_display() {
306     int err;
307     rtg_on = 1;
308
309     if (!rtg_initialized) {
310         err = pthread_create(&thread_id, NULL, &rtgThread, (void *)&rtg_share_data);
311         if (err != 0) {
312             rtg_on = 0;
313             printf("can't create RTG thread :[%s]", strerror(err));
314         }
315         else {
316             rtg_initialized = 1;
317             pthread_setname_np(thread_id, "pistorm: rtg");
318             printf("RTG Thread created successfully\n");
319         }
320     }
321     printf("RTG display enabled.\n");
322 }
323
324 void rtg_shutdown_display() {
325     printf("RTG display disabled.\n");
326     rtg_on = 0;
327 }
328
329 void rtg_enable_mouse_cursor() {
330     mouse_cursor_enabled = 1;
331 }
332
333 void rtg_set_mouse_cursor_pos(int16_t x, int16_t y) {
334     mouse_cursor_x = x;
335     mouse_cursor_y = y;
336     //printf("Set mouse cursor pos to %d, %d.\n", x, y);
337 }
338
339 static uint8_t clut_cursor_data[256*256];
340
341 void update_mouse_cursor(uint8_t *src) {
342     if (src != NULL) {
343         memset(clut_cursor_data, 0x00, 256*256);
344         uint8_t cur_bit = 0x80;
345         uint8_t line_pitch = (mouse_cursor_w / 8) * 2;
346
347         for (uint8_t y = 0; y < mouse_cursor_h; y++) {
348             for (uint8_t x = 0; x < mouse_cursor_w; x++) {
349                 if (src[(x / 8) + (line_pitch * y)] & cur_bit)
350                     clut_cursor_data[x + (y * 256)] |= 0x01;
351                 if (src[(x / 8) + (line_pitch * y) + (mouse_cursor_w / 8)] & cur_bit)
352                     clut_cursor_data[x + (y * 256)] |= 0x02;
353                 cur_bit >>= 1;
354                 if (cur_bit == 0x00)
355                     cur_bit = 0x80;
356             }
357             cur_bit = 0x80;
358         }
359     }
360
361     for (int y = 0; y < mouse_cursor_h; y++) {
362         for (int x = 0; x < mouse_cursor_w; x++) {
363             cursor_data[x + (y * 256)] = cursor_palette[clut_cursor_data[x + (y * 256)]];
364         }
365     }
366
367     while (rtg_on && !updating_screen)
368         usleep(0);
369     cursor_image_updated = 1;
370 }
371
372 void rtg_set_cursor_clut_entry(uint8_t r, uint8_t g, uint8_t b, uint8_t idx) {
373     uint32_t color = 0;
374     unsigned char *dst = (unsigned char *)&color;
375
376     dst[0] = r;
377     dst[1] = g;
378     dst[2] = b;
379     dst[3] = 0xFF;
380     if (cursor_palette[idx + 1] != color) {
381         cursor_palette[0] = 0;
382         cursor_palette[idx + 1] = color;
383         update_mouse_cursor(NULL);
384     }
385 }
386
387 static uint8_t old_mouse_w, old_mouse_h;
388 static uint8_t old_mouse_data[256];
389
390 void rtg_set_mouse_cursor_image(uint8_t *src, uint8_t w, uint8_t h) {
391     uint8_t new_cursor_data = 0;
392
393     mouse_cursor_w = w;
394     mouse_cursor_h = h;
395
396     if (memcmp(src, old_mouse_data, (w / 8 * h)) != 0)
397         new_cursor_data = 1;
398
399     if (old_mouse_w != w || old_mouse_h != h || new_cursor_data) {
400         old_mouse_w = w;
401         old_mouse_h = h;
402         update_mouse_cursor(src);
403     }
404 }
405
406 void rtg_show_fps(uint8_t enable) {
407     show_fps = (enable != 0);
408 }
409
410 void rtg_palette_debug(uint8_t enable) {
411     debug_palette = (enable != 0);
412 }