# HG changeset patch # User Michael Pavone # Date 1713120032 25200 # Node ID ccee8dccd9cc00cdb95e63a99d5bf6c3efb761b6 # Parent 48ab1e3e5df5d6e174992ca7c18d18bd840fc4d9 Add support for rendering debug windows with OpenGL in perparation for allowing Nuklear UI in them diff -r 48ab1e3e5df5 -r ccee8dccd9cc nuklear_ui/blastem_nuklear.c --- a/nuklear_ui/blastem_nuklear.c Sun Mar 31 10:21:34 2024 -0700 +++ b/nuklear_ui/blastem_nuklear.c Sun Apr 14 11:40:32 2024 -0700 @@ -1879,6 +1879,10 @@ if (entries[i].is_dir) { continue; } + if (startswith(entries[i].name, "extra_window.")) { + //skip shader used for debug windows + continue; + } char *no_ext = basename_no_extension(entries[i].name); uint32_t len = strlen(no_ext); if (no_ext[len-1] == 'f' && no_ext[len-2] == '.') { diff -r 48ab1e3e5df5 -r ccee8dccd9cc render_sdl.c --- a/render_sdl.c Sun Mar 31 10:21:34 2024 -0700 +++ b/render_sdl.c Sun Apr 14 11:40:32 2024 -0700 @@ -29,13 +29,37 @@ #define MAX_EVENT_POLL_PER_FRAME 2 + +typedef struct { + SDL_Window *win; + SDL_Renderer *renderer; + SDL_Texture *sdl_texture; + window_close_handler on_close; + uint32_t width; + uint32_t height; +#ifndef DISABLE_OPENGL + SDL_GLContext *gl_context; + uint32_t *texture_buf; + uint32_t tex_width; + uint32_t tex_height; + GLuint gl_texture; + GLuint vshader; + GLuint fshader; + GLuint program; + GLuint gl_buffers[2]; + GLint un_texture; + GLint un_width; + GLint un_height; + GLint at_pos; +#endif +} extra_window; + static SDL_Window *main_window; -static SDL_Window **extra_windows; +static extra_window *extras; static SDL_Renderer *main_renderer; -static SDL_Renderer **extra_renderers; -static SDL_Texture **sdl_textures; +static SDL_Texture *sdl_textures[FRAMEBUFFER_UI + 1]; static window_close_handler *close_handlers; -static uint8_t num_textures; +static uint8_t num_extras; static SDL_Rect main_clip; static SDL_GLContext *main_context; @@ -480,10 +504,6 @@ if (texture_init) { return; } - if (!sdl_textures) { - sdl_textures= calloc(sizeof(SDL_Texture *), 3); - num_textures = 3; - } texture_init = 1; #ifndef DISABLE_OPENGL if (render_gl) { @@ -502,14 +522,12 @@ static void free_surfaces(void) { - for (int i = 0; i < num_textures; i++) + for (int i = 0; i <= FRAMEBUFFER_UI; i++) { if (sdl_textures[i]) { SDL_DestroyTexture(sdl_textures[i]); } } - free(sdl_textures); - sdl_textures = NULL; texture_init = 0; } @@ -943,11 +961,11 @@ if (main_window && SDL_GetWindowID(main_window) == event->window.windowID) { exit(0); } else { - for (int i = 0; i < num_textures - FRAMEBUFFER_USER_START; i++) + for (int i = 0; i < num_extras; i++) { - if (SDL_GetWindowID(extra_windows[i]) == event->window.windowID) { - if (close_handlers[i]) { - close_handlers[i](i + FRAMEBUFFER_USER_START); + if (SDL_GetWindowID(extras[i].win) == event->window.windowID) { + if (extras[i].on_close) { + extras[i].on_close(i + FRAMEBUFFER_USER_START); } break; } @@ -1293,8 +1311,7 @@ void render_config_updated(void) { - int n = num_textures < FRAMEBUFFER_USER_START ? num_textures : FRAMEBUFFER_USER_START; - for (int i = 0; i < n; i++) + for (int i = 0; i <= FRAMEBUFFER_UI; i++) { if (sdl_textures[i]) { SDL_DestroyTexture(sdl_textures[i]); @@ -1473,72 +1490,143 @@ free(path); } +void GLAPIENTRY gl_message_callback(GLenum source, GLenum type, GLenum id, GLenum severity, GLsizei length, const GLchar *message, const void *user) +{ + fprintf(stderr, "GL Message: %d, %d, %d - %s\n", source, type, severity, message); +} + uint8_t render_create_window(char *caption, uint32_t width, uint32_t height, window_close_handler close_handler) { uint8_t win_idx = 0xFF; - for (int i = 0; i < num_textures - FRAMEBUFFER_USER_START; i++) + for (int i = 0; i < num_extras; i++) { - if (!extra_windows[i]) { + if (!extras[i].win) { win_idx = i; break; } } if (win_idx == 0xFF) { - num_textures++; - sdl_textures = realloc(sdl_textures, num_textures * sizeof(*sdl_textures)); - extra_windows = realloc(extra_windows, (num_textures - FRAMEBUFFER_USER_START) * sizeof(*extra_windows)); - extra_renderers = realloc(extra_renderers, (num_textures - FRAMEBUFFER_USER_START) * sizeof(*extra_renderers)); - close_handlers = realloc(close_handlers, (num_textures - FRAMEBUFFER_USER_START) * sizeof(*close_handlers)); - win_idx = num_textures - FRAMEBUFFER_USER_START - 1; + num_extras++; + extras = realloc(extras, num_extras * sizeof(*extras)); + win_idx = num_extras - 1; } + memset(&extras[win_idx], 0, sizeof(extra_window)); + uint32_t flags = 0; +#ifndef DISABLE_OPENGL + if (render_gl) { + flags |= SDL_WINDOW_OPENGL; + } +#endif int x = SDL_WINDOWPOS_UNDEFINED; int y = SDL_WINDOWPOS_UNDEFINED; SDL_GetWindowPosition(main_window, &x, &y); if (x != SDL_WINDOWPOS_UNDEFINED) { x += main_width; } - extra_windows[win_idx] = SDL_CreateWindow(caption, x, y, width, height, 0); - if (!extra_windows[win_idx]) { + extras[win_idx].win = SDL_CreateWindow(caption, x, y, width, height, flags); + if (!extras[win_idx].win) { goto fail_window; } - extra_renderers[win_idx] = SDL_CreateRenderer(extra_windows[win_idx], -1, SDL_RENDERER_ACCELERATED); - if (!extra_renderers[win_idx]) { - goto fail_renderer; + extras[win_idx].width = width; + extras[win_idx].height = height; +#ifndef DISABLE_OPENGL + if (render_gl) { + extras[win_idx].gl_context = SDL_GL_CreateContext(extras[win_idx].win); + SDL_GL_MakeCurrent(extras[win_idx].win, extras[win_idx].gl_context); + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(gl_message_callback, NULL); + glGenTextures(1, &extras[win_idx].gl_texture); + glBindTexture(GL_TEXTURE_2D, extras[win_idx].gl_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + extras[win_idx].tex_width = width; + extras[win_idx].tex_height = height; + char *npot_textures = tern_find_path_default(config, "video\0npot_textures\0", (tern_val){.ptrval = "off"}, TVAL_PTR).ptrval; + if (strcmp(npot_textures, "on")) { + extras[win_idx].tex_width = nearest_pow2(width); + extras[win_idx].tex_height = nearest_pow2(height); + } + extras[win_idx].texture_buf = calloc(extras[win_idx].tex_width * extras[win_idx].tex_height, sizeof(uint32_t)); + glTexImage2D(GL_TEXTURE_2D, 0, INTERNAL_FORMAT, extras[win_idx].tex_width, extras[win_idx].tex_height, 0, SRC_FORMAT, GL_UNSIGNED_BYTE, extras[win_idx].texture_buf); + glGenBuffers(2, extras[win_idx].gl_buffers); + glBindBuffer(GL_ARRAY_BUFFER, extras[win_idx].gl_buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data_default), vertex_data_default, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, extras[win_idx].gl_buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(element_data), element_data, GL_STATIC_DRAW); + extras[win_idx].vshader = load_shader("extra_window.v.glsl", GL_VERTEX_SHADER); + extras[win_idx].fshader = load_shader("extra_window.f.glsl", GL_FRAGMENT_SHADER); + extras[win_idx].program = glCreateProgram(); + glAttachShader(program, extras[win_idx].vshader); + glAttachShader(program, extras[win_idx].fshader); + glLinkProgram(extras[win_idx].program); + GLint link_status; + glGetProgramiv(program, GL_LINK_STATUS, &link_status); + if (!link_status) { + fputs("Failed to link shader program\n", stderr); + //TODO: cleanup + goto sdl_renderer; + } + extras[win_idx].un_texture = glGetUniformLocation(program, "texture"); + extras[win_idx].un_width = glGetUniformLocation(program, "width"); + extras[win_idx].un_height = glGetUniformLocation(program, "height"); + extras[win_idx].at_pos = glGetAttribLocation(program, "pos"); + } else { +sdl_renderer: +#endif + extras[win_idx].renderer = SDL_CreateRenderer(extras[win_idx].win, -1, SDL_RENDERER_ACCELERATED); + if (!extras[win_idx].renderer) { + goto fail_renderer; + } + extras[win_idx].sdl_texture = SDL_CreateTexture(extras[win_idx].renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); + if (!extras[win_idx].sdl_texture) { + goto fail_texture; + } +#ifndef DISABLE_OPENGL } - uint8_t texture_idx = win_idx + FRAMEBUFFER_USER_START; - sdl_textures[texture_idx] = SDL_CreateTexture(extra_renderers[win_idx], SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, width, height); - if (!sdl_textures[texture_idx]) { - goto fail_texture; - } - close_handlers[win_idx] = close_handler; - return texture_idx; +#endif + extras[win_idx].on_close = close_handler; + return win_idx + FRAMEBUFFER_USER_START; fail_texture: - SDL_DestroyRenderer(extra_renderers[win_idx]); + SDL_DestroyRenderer(extras[win_idx].renderer); fail_renderer: - SDL_DestroyWindow(extra_windows[win_idx]); + SDL_DestroyWindow(extras[win_idx].win); fail_window: - num_textures--; return 0; } void render_destroy_window(uint8_t which) { uint8_t win_idx = which - FRAMEBUFFER_USER_START; - //Destroying the renderers also frees the textures - SDL_DestroyRenderer(extra_renderers[win_idx]); - SDL_DestroyWindow(extra_windows[win_idx]); + if (extras[win_idx].renderer) { + //Destroying the renderers also frees the textures + SDL_DestroyRenderer(extras[win_idx].renderer); + extras[win_idx].renderer = NULL; + } +#ifndef DISABLE_OPENGL + else { + SDL_GL_MakeCurrent(extras[win_idx].win, extras[win_idx].gl_context); + glDeleteProgram(extras[win_idx].program); + glDeleteShader(extras[win_idx].vshader); + glDeleteShader(extras[win_idx].fshader); + glDeleteBuffers(2, extras[win_idx].gl_buffers); + glDeleteTextures(1, &extras[win_idx].gl_texture); + SDL_GL_DeleteContext(extras[win_idx].gl_context); + } +#endif + SDL_DestroyWindow(extras[win_idx].win); - extra_renderers[win_idx] = NULL; - extra_windows[win_idx] = NULL; + extras[win_idx].win = NULL; } SDL_Texture **static_images; uint8_t num_static; uint8_t render_static_image(uint8_t window, uint8_t *buffer, uint32_t size) { - + window -= FRAMEBUFFER_USER_START; uint32_t width, height; uint32_t *pixels = load_png(buffer, size, &width, &height); if (!pixels) { @@ -1559,7 +1647,7 @@ num_static *= 2; static_images = realloc(static_images, num_static * sizeof(SDL_Texture*)); } - static_images[img_index] = SDL_CreateTexture(extra_renderers[window - FRAMEBUFFER_USER_START], SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, width, height); + static_images[img_index] = SDL_CreateTexture(extras[window].renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, width, height); SDL_UpdateTexture(static_images[img_index], NULL, pixels, sizeof(uint32_t) * width); free(pixels); return img_index; @@ -1567,36 +1655,70 @@ void render_draw_image(uint8_t window, uint8_t image, int x, int y, int width, int height) { - SDL_Rect dst = { - .x = x, - .y = y, - .w = width, - .h = height - }; - SDL_RenderCopy(extra_renderers[window - FRAMEBUFFER_USER_START], static_images[image], NULL, &dst); + uint8_t win_idx = window - FRAMEBUFFER_USER_START; + if (extras[win_idx].renderer) { + SDL_Rect dst = { + .x = x, + .y = y, + .w = width, + .h = height + }; + SDL_RenderCopy(extras[win_idx].renderer, static_images[image], NULL, &dst); + } +#ifndef DISABLE_OPENGL + else { + //TODO: implement me + } +#endif } void render_clear_window(uint8_t window, uint8_t r, uint8_t g, uint8_t b) { - SDL_SetRenderDrawColor(extra_renderers[window - FRAMEBUFFER_USER_START], r, g, b, 255); - SDL_RenderClear(extra_renderers[window - FRAMEBUFFER_USER_START]); + uint8_t win_idx = window - FRAMEBUFFER_USER_START; + if (extras[win_idx].renderer) { + SDL_SetRenderDrawColor(extras[win_idx].renderer, r, g, b, 255); + SDL_RenderClear(extras[win_idx].renderer); + } +#ifndef DISABLE_OPENGL + else { + SDL_GL_MakeCurrent(extras[win_idx].win, extras[win_idx].gl_context); + glClearColor(r / 255.0f, g / 255.0f, b / 255.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } +#endif } void render_fill_rect(uint8_t window, uint8_t r, uint8_t g, uint8_t b, int x, int y, int width, int height) { - SDL_SetRenderDrawColor(extra_renderers[window - FRAMEBUFFER_USER_START], r, g, b, 255); - SDL_Rect dst = { - .x = x, - .y = y, - .w = width, - .h = height - }; - SDL_RenderFillRect(extra_renderers[window - FRAMEBUFFER_USER_START], &dst); + uint8_t win_idx = window - FRAMEBUFFER_USER_START; + if (extras[win_idx].renderer) { + SDL_SetRenderDrawColor(extras[win_idx].renderer, r, g, b, 255); + SDL_Rect dst = { + .x = x, + .y = y, + .w = width, + .h = height + }; + SDL_RenderFillRect(extras[win_idx].renderer, &dst); + } +#ifndef DISABLE_OPENGL + else { + //TODO: implement me + } +#endif } void render_window_refresh(uint8_t window) { - SDL_RenderPresent(extra_renderers[window - FRAMEBUFFER_USER_START]); + uint8_t win_idx = window - FRAMEBUFFER_USER_START; + if (extras[win_idx].renderer) { + SDL_RenderPresent(extras[win_idx].renderer); + } +#ifndef DISABLE_OPENGL + else { + SDL_GL_SwapWindow(extras[win_idx].win); + } +#endif } uint32_t *locked_pixels; @@ -1620,17 +1742,28 @@ if (render_gl && which <= FRAMEBUFFER_EVEN) { *pitch = LINEBUF_SIZE * sizeof(uint32_t); return texture_buf; + } else if (render_gl && which >= FRAMEBUFFER_USER_START) { + uint8_t win_idx = which - FRAMEBUFFER_USER_START; + *pitch = extras[win_idx].width * sizeof(uint32_t); + return extras[win_idx].texture_buf; } else { #endif if (which == FRAMEBUFFER_UI && !sdl_textures[which]) { sdl_textures[which] = SDL_CreateTexture(main_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, main_width, main_height); } - if (which >= num_textures) { - warning("Request for invalid framebuffer number %d\n", which); - return NULL; + SDL_Texture *tex; + if (which >= FRAMEBUFFER_USER_START) { + uint8_t win_idx = which - FRAMEBUFFER_USER_START; + if (win_idx >= num_extras || !extras[win_idx].renderer) { + warning("Request for invalid framebuffer number %d\n", which); + return NULL; + } + tex = extras[win_idx].sdl_texture; + } else { + tex = sdl_textures[which]; } uint8_t *pixels; - if (SDL_LockTexture(sdl_textures[which], NULL, (void **)&pixels, pitch) < 0) { + if (SDL_LockTexture(tex, NULL, (void **)&pixels, pitch) < 0) { warning("Failed to lock texture: %s\n", SDL_GetError()); return NULL; } @@ -1684,28 +1817,30 @@ return; } - last_width = width; uint32_t height = which <= FRAMEBUFFER_EVEN ? (video_standard == VID_PAL ? 294 : 243) - (overscan_top[video_standard] + overscan_bot[video_standard]) : 240; FILE *screenshot_file = NULL; char *ext; - width -= overscan_left[video_standard] + overscan_right[video_standard]; - if (screenshot_path && which == FRAMEBUFFER_ODD) { - screenshot_file = fopen(screenshot_path, "wb"); - if (screenshot_file) { + if (which < FRAMEBUFFER_UI) { + last_width = width; + width -= overscan_left[video_standard] + overscan_right[video_standard]; + if (screenshot_path && which == FRAMEBUFFER_ODD) { + screenshot_file = fopen(screenshot_path, "wb"); + if (screenshot_file) { #ifndef DISABLE_ZLIB - ext = path_extension(screenshot_path); + ext = path_extension(screenshot_path); #endif - debug_message("Saving screenshot to %s\n", screenshot_path); - } else { - warning("Failed to open screenshot file %s for writing\n", screenshot_path); + debug_message("Saving screenshot to %s\n", screenshot_path); + } else { + warning("Failed to open screenshot file %s for writing\n", screenshot_path); + } + free(screenshot_path); + screenshot_path = NULL; } - free(screenshot_path); - screenshot_path = NULL; + interlaced = last_field != which; + buffer += overscan_left[video_standard] + LINEBUF_SIZE * overscan_top[video_standard]; } - interlaced = last_field != which; - buffer += overscan_left[video_standard] + LINEBUF_SIZE * overscan_top[video_standard]; #ifndef DISABLE_OPENGL if (render_gl && which <= FRAMEBUFFER_EVEN) { SDL_GL_MakeCurrent(main_window, main_context); @@ -1735,6 +1870,11 @@ save_png24_frame(apng_file, buffer, apng, width, height, LINEBUF_SIZE*sizeof(uint32_t)); } #endif + } else if (render_gl && which >= FRAMEBUFFER_USER_START) { + uint8_t win_idx = which - FRAMEBUFFER_USER_START; + SDL_GL_MakeCurrent(extras[win_idx].win, extras[win_idx].gl_context); + glBindTexture(GL_TEXTURE_2D, extras[win_idx].gl_texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, extras[win_idx].width, extras[win_idx].height, SRC_FORMAT, GL_UNSIGNED_BYTE, buffer); } else { #endif uint32_t shot_height = height; @@ -1778,8 +1918,8 @@ #ifndef DISABLE_OPENGL } #endif - last_height = height; if (which <= FRAMEBUFFER_EVEN) { + last_height = height; render_update_display(); } else if (which == FRAMEBUFFER_UI) { SDL_RenderCopy(main_renderer, sdl_textures[which], NULL, NULL); @@ -1792,8 +1932,34 @@ need_ui_fb_resize = 0; } } else { - SDL_RenderCopy(extra_renderers[which - FRAMEBUFFER_USER_START], sdl_textures[which], NULL, NULL); - SDL_RenderPresent(extra_renderers[which - FRAMEBUFFER_USER_START]); + uint8_t win_idx = which - FRAMEBUFFER_USER_START; + if (extras[win_idx].renderer) { + SDL_RenderCopy(extras[win_idx].renderer, extras[win_idx].sdl_texture, NULL, NULL); + SDL_RenderPresent(extras[win_idx].renderer); + } +#ifndef DISABLE_OPENGL + else { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glUseProgram(extras[win_idx].program); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, extras[win_idx].gl_texture); + glUniform1i(extras[win_idx].un_texture, 0); + + glUniform1f(extras[win_idx].un_width, (float)extras[win_idx].width / (float)extras[win_idx].tex_width); + glUniform1f(extras[win_idx].un_height, (float)extras[win_idx].height / (float)extras[win_idx].tex_height); + + glBindBuffer(GL_ARRAY_BUFFER, extras[win_idx].gl_buffers[0]); + glVertexAttribPointer(extras[win_idx].at_pos, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat[2]), (void *)0); + glEnableVertexAttribArray(extras[win_idx].at_pos); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, extras[win_idx].gl_buffers[1]); + glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, (void *)0); + + SDL_GL_SwapWindow(extras[win_idx].win); + } +#endif } if (screenshot_file) { fclose(screenshot_file); @@ -1920,7 +2086,7 @@ return; } //TODO: Maybe fixme for render API - process_framebuffer(texture_buf, which, width); + process_framebuffer(which < FRAMEBUFFER_USER_START ? texture_buf : extras[which - FRAMEBUFFER_USER_START].texture_buf, which, width); } void render_video_loop(void) @@ -2263,9 +2429,9 @@ if (SDL_GetWindowFlags(main_window) & SDL_WINDOW_INPUT_FOCUS) { return FRAMEBUFFER_ODD; } - for (int i = 0; i < num_textures - 2; i++) + for (int i = 0; i < num_extras; i++) { - if (extra_windows[i] && (SDL_GetWindowFlags(extra_windows[i]) & SDL_WINDOW_INPUT_FOCUS)) { + if (extras[i].win && (SDL_GetWindowFlags(extras[i].win) & SDL_WINDOW_INPUT_FOCUS)) { return FRAMEBUFFER_USER_START + i; } }