test/testgl2.c
author Cameron Gutman <aicommander@gmail.com>
Sun, 28 Apr 2019 17:37:49 -0700
changeset 12746 132a2af7edac
parent 12503 806492103856
permissions -rw-r--r--
Fix use-after-free when pumping the event loop after SDL_DestroyWindow()

Closing the window is asynchronous, but we free the window data immediately,
so we can get an updateLayer callback before the window is really destroyed which
will cause us to access the freed memory.

Clearing the content view will cause it to be immediately released, so no further
updateLayer callbacks will occur.
     1 /*
     2   Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
     3 
     4   This software is provided 'as-is', without any express or implied
     5   warranty.  In no event will the authors be held liable for any damages
     6   arising from the use of this software.
     7 
     8   Permission is granted to anyone to use this software for any purpose,
     9   including commercial applications, and to alter it and redistribute it
    10   freely.
    11 */
    12 #include <stdlib.h>
    13 #include <stdio.h>
    14 #include <string.h>
    15 #include <math.h>
    16 
    17 #include "SDL_test_common.h"
    18 
    19 #ifdef __MACOS__
    20 #define HAVE_OPENGL
    21 #endif
    22 
    23 #ifdef HAVE_OPENGL
    24 
    25 #include "SDL_opengl.h"
    26 
    27 typedef struct GL_Context
    28 {
    29 #define SDL_PROC(ret,func,params) ret (APIENTRY *func) params;
    30 #include "../src/render/opengl/SDL_glfuncs.h"
    31 #undef SDL_PROC
    32 } GL_Context;
    33 
    34 
    35 /* Undefine this if you want a flat cube instead of a rainbow cube */
    36 #define SHADED_CUBE
    37 
    38 static SDLTest_CommonState *state;
    39 static SDL_GLContext context;
    40 static GL_Context ctx;
    41 
    42 static int LoadContext(GL_Context * data)
    43 {
    44 #if SDL_VIDEO_DRIVER_UIKIT
    45 #define __SDL_NOGETPROCADDR__
    46 #elif SDL_VIDEO_DRIVER_ANDROID
    47 #define __SDL_NOGETPROCADDR__
    48 #elif SDL_VIDEO_DRIVER_PANDORA
    49 #define __SDL_NOGETPROCADDR__
    50 #endif
    51 
    52 #if defined __SDL_NOGETPROCADDR__
    53 #define SDL_PROC(ret,func,params) data->func=func;
    54 #else
    55 #define SDL_PROC(ret,func,params) \
    56     do { \
    57         data->func = SDL_GL_GetProcAddress(#func); \
    58         if ( ! data->func ) { \
    59             return SDL_SetError("Couldn't load GL function %s: %s", #func, SDL_GetError()); \
    60         } \
    61     } while ( 0 );
    62 #endif /* __SDL_NOGETPROCADDR__ */
    63 
    64 #include "../src/render/opengl/SDL_glfuncs.h"
    65 #undef SDL_PROC
    66     return 0;
    67 }
    68 
    69 
    70 /* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
    71 static void
    72 quit(int rc)
    73 {
    74     if (context) {
    75         /* SDL_GL_MakeCurrent(0, NULL); *//* doesn't do anything */
    76         SDL_GL_DeleteContext(context);
    77     }
    78     SDLTest_CommonQuit(state);
    79     exit(rc);
    80 }
    81 
    82 static void
    83 Render()
    84 {
    85     static float color[8][3] = {
    86         {1.0, 1.0, 0.0},
    87         {1.0, 0.0, 0.0},
    88         {0.0, 0.0, 0.0},
    89         {0.0, 1.0, 0.0},
    90         {0.0, 1.0, 1.0},
    91         {1.0, 1.0, 1.0},
    92         {1.0, 0.0, 1.0},
    93         {0.0, 0.0, 1.0}
    94     };
    95     static float cube[8][3] = {
    96         {0.5, 0.5, -0.5},
    97         {0.5, -0.5, -0.5},
    98         {-0.5, -0.5, -0.5},
    99         {-0.5, 0.5, -0.5},
   100         {-0.5, 0.5, 0.5},
   101         {0.5, 0.5, 0.5},
   102         {0.5, -0.5, 0.5},
   103         {-0.5, -0.5, 0.5}
   104     };
   105 
   106     /* Do our drawing, too. */
   107     ctx.glClearColor(0.0, 0.0, 0.0, 1.0);
   108     ctx.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   109 
   110     ctx.glBegin(GL_QUADS);
   111 
   112 #ifdef SHADED_CUBE
   113     ctx.glColor3fv(color[0]);
   114     ctx.glVertex3fv(cube[0]);
   115     ctx.glColor3fv(color[1]);
   116     ctx.glVertex3fv(cube[1]);
   117     ctx.glColor3fv(color[2]);
   118     ctx.glVertex3fv(cube[2]);
   119     ctx.glColor3fv(color[3]);
   120     ctx.glVertex3fv(cube[3]);
   121 
   122     ctx.glColor3fv(color[3]);
   123     ctx.glVertex3fv(cube[3]);
   124     ctx.glColor3fv(color[4]);
   125     ctx.glVertex3fv(cube[4]);
   126     ctx.glColor3fv(color[7]);
   127     ctx.glVertex3fv(cube[7]);
   128     ctx.glColor3fv(color[2]);
   129     ctx.glVertex3fv(cube[2]);
   130 
   131     ctx.glColor3fv(color[0]);
   132     ctx.glVertex3fv(cube[0]);
   133     ctx.glColor3fv(color[5]);
   134     ctx.glVertex3fv(cube[5]);
   135     ctx.glColor3fv(color[6]);
   136     ctx.glVertex3fv(cube[6]);
   137     ctx.glColor3fv(color[1]);
   138     ctx.glVertex3fv(cube[1]);
   139 
   140     ctx.glColor3fv(color[5]);
   141     ctx.glVertex3fv(cube[5]);
   142     ctx.glColor3fv(color[4]);
   143     ctx.glVertex3fv(cube[4]);
   144     ctx.glColor3fv(color[7]);
   145     ctx.glVertex3fv(cube[7]);
   146     ctx.glColor3fv(color[6]);
   147     ctx.glVertex3fv(cube[6]);
   148 
   149     ctx.glColor3fv(color[5]);
   150     ctx.glVertex3fv(cube[5]);
   151     ctx.glColor3fv(color[0]);
   152     ctx.glVertex3fv(cube[0]);
   153     ctx.glColor3fv(color[3]);
   154     ctx.glVertex3fv(cube[3]);
   155     ctx.glColor3fv(color[4]);
   156     ctx.glVertex3fv(cube[4]);
   157 
   158     ctx.glColor3fv(color[6]);
   159     ctx.glVertex3fv(cube[6]);
   160     ctx.glColor3fv(color[1]);
   161     ctx.glVertex3fv(cube[1]);
   162     ctx.glColor3fv(color[2]);
   163     ctx.glVertex3fv(cube[2]);
   164     ctx.glColor3fv(color[7]);
   165     ctx.glVertex3fv(cube[7]);
   166 #else /* flat cube */
   167     ctx.glColor3f(1.0, 0.0, 0.0);
   168     ctx.glVertex3fv(cube[0]);
   169     ctx.glVertex3fv(cube[1]);
   170     ctx.glVertex3fv(cube[2]);
   171     ctx.glVertex3fv(cube[3]);
   172 
   173     ctx.glColor3f(0.0, 1.0, 0.0);
   174     ctx.glVertex3fv(cube[3]);
   175     ctx.glVertex3fv(cube[4]);
   176     ctx.glVertex3fv(cube[7]);
   177     ctx.glVertex3fv(cube[2]);
   178 
   179     ctx.glColor3f(0.0, 0.0, 1.0);
   180     ctx.glVertex3fv(cube[0]);
   181     ctx.glVertex3fv(cube[5]);
   182     ctx.glVertex3fv(cube[6]);
   183     ctx.glVertex3fv(cube[1]);
   184 
   185     ctx.glColor3f(0.0, 1.0, 1.0);
   186     ctx.glVertex3fv(cube[5]);
   187     ctx.glVertex3fv(cube[4]);
   188     ctx.glVertex3fv(cube[7]);
   189     ctx.glVertex3fv(cube[6]);
   190 
   191     ctx.glColor3f(1.0, 1.0, 0.0);
   192     ctx.glVertex3fv(cube[5]);
   193     ctx.glVertex3fv(cube[0]);
   194     ctx.glVertex3fv(cube[3]);
   195     ctx.glVertex3fv(cube[4]);
   196 
   197     ctx.glColor3f(1.0, 0.0, 1.0);
   198     ctx.glVertex3fv(cube[6]);
   199     ctx.glVertex3fv(cube[1]);
   200     ctx.glVertex3fv(cube[2]);
   201     ctx.glVertex3fv(cube[7]);
   202 #endif /* SHADED_CUBE */
   203 
   204     ctx.glEnd();
   205 
   206     ctx.glMatrixMode(GL_MODELVIEW);
   207     ctx.glRotatef(5.0, 1.0, 1.0, 1.0);
   208 }
   209 
   210 int
   211 main(int argc, char *argv[])
   212 {
   213     int fsaa, accel;
   214     int value;
   215     int i, done;
   216     SDL_DisplayMode mode;
   217     SDL_Event event;
   218     Uint32 then, now, frames;
   219     int status;
   220     int dw, dh;
   221     int swap_interval = 0;
   222 
   223     /* Enable standard application logging */
   224     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
   225 
   226     /* Initialize parameters */
   227     fsaa = 0;
   228     accel = -1;
   229 
   230     /* Initialize test framework */
   231     state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO);
   232     if (!state) {
   233         return 1;
   234     }
   235     for (i = 1; i < argc;) {
   236         int consumed;
   237 
   238         consumed = SDLTest_CommonArg(state, i);
   239         if (consumed == 0) {
   240             if (SDL_strcasecmp(argv[i], "--fsaa") == 0 && i+1 < argc) {
   241                 fsaa = atoi(argv[i+1]);
   242                 consumed = 2;
   243             } else if (SDL_strcasecmp(argv[i], "--accel") == 0 && i+1 < argc) {
   244                 accel = atoi(argv[i+1]);
   245                 consumed = 2;
   246             } else {
   247                 consumed = -1;
   248             }
   249         }
   250         if (consumed < 0) {
   251             SDL_Log("Usage: %s %s [--fsaa n] [--accel n]\n", argv[0],
   252                     SDLTest_CommonUsage(state));
   253             quit(1);
   254         }
   255         i += consumed;
   256     }
   257 
   258     /* Set OpenGL parameters */
   259     state->window_flags |= SDL_WINDOW_OPENGL;
   260     state->gl_red_size = 5;
   261     state->gl_green_size = 5;
   262     state->gl_blue_size = 5;
   263     state->gl_depth_size = 16;
   264     state->gl_double_buffer = 1;
   265     if (fsaa) {
   266         state->gl_multisamplebuffers = 1;
   267         state->gl_multisamplesamples = fsaa;
   268     }
   269     if (accel >= 0) {
   270         state->gl_accelerated = accel;
   271     }
   272 
   273     if (!SDLTest_CommonInit(state)) {
   274         quit(2);
   275     }
   276 
   277     /* Create OpenGL context */
   278     context = SDL_GL_CreateContext(state->windows[0]);
   279     if (!context) {
   280         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GL_CreateContext(): %s\n", SDL_GetError());
   281         quit(2);
   282     }
   283     
   284     /* Important: call this *after* creating the context */
   285     if (LoadContext(&ctx) < 0) {
   286         SDL_Log("Could not load GL functions\n");
   287         quit(2);
   288         return 0;
   289     }
   290 
   291     if (state->render_flags & SDL_RENDERER_PRESENTVSYNC) {
   292         /* try late-swap-tearing first. If not supported, try normal vsync. */
   293         if (SDL_GL_SetSwapInterval(-1) == 0) {
   294             swap_interval = -1;
   295         } else {
   296             SDL_GL_SetSwapInterval(1);
   297             swap_interval = 1;
   298         }
   299     } else {
   300         SDL_GL_SetSwapInterval(0);  /* disable vsync. */
   301         swap_interval = 0;
   302     }
   303 
   304     SDL_GetCurrentDisplayMode(0, &mode);
   305     SDL_Log("Screen BPP    : %d\n", SDL_BITSPERPIXEL(mode.format));
   306     SDL_Log("Swap Interval : %d\n", SDL_GL_GetSwapInterval());
   307     SDL_GetWindowSize(state->windows[0], &dw, &dh);
   308     SDL_Log("Window Size   : %d,%d\n", dw, dh);
   309     SDL_GL_GetDrawableSize(state->windows[0], &dw, &dh);
   310     SDL_Log("Draw Size     : %d,%d\n", dw, dh);
   311     SDL_Log("\n");
   312     SDL_Log("Vendor        : %s\n", ctx.glGetString(GL_VENDOR));
   313     SDL_Log("Renderer      : %s\n", ctx.glGetString(GL_RENDERER));
   314     SDL_Log("Version       : %s\n", ctx.glGetString(GL_VERSION));
   315     SDL_Log("Extensions    : %s\n", ctx.glGetString(GL_EXTENSIONS));
   316     SDL_Log("\n");
   317 
   318     status = SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &value);
   319     if (!status) {
   320         SDL_Log("SDL_GL_RED_SIZE: requested %d, got %d\n", 5, value);
   321     } else {
   322         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get SDL_GL_RED_SIZE: %s\n", SDL_GetError());
   323     }
   324     status = SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &value);
   325     if (!status) {
   326         SDL_Log("SDL_GL_GREEN_SIZE: requested %d, got %d\n", 5, value);
   327     } else {
   328         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get SDL_GL_GREEN_SIZE: %s\n", SDL_GetError());
   329     }
   330     status = SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &value);
   331     if (!status) {
   332         SDL_Log("SDL_GL_BLUE_SIZE: requested %d, got %d\n", 5, value);
   333     } else {
   334         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get SDL_GL_BLUE_SIZE: %s\n", SDL_GetError());
   335     }
   336     status = SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &value);
   337     if (!status) {
   338         SDL_Log("SDL_GL_DEPTH_SIZE: requested %d, got %d\n", 16, value);
   339     } else {
   340         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get SDL_GL_DEPTH_SIZE: %s\n", SDL_GetError());
   341     }
   342     if (fsaa) {
   343         status = SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &value);
   344         if (!status) {
   345             SDL_Log("SDL_GL_MULTISAMPLEBUFFERS: requested 1, got %d\n", value);
   346         } else {
   347             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get SDL_GL_MULTISAMPLEBUFFERS: %s\n",
   348                    SDL_GetError());
   349         }
   350         status = SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &value);
   351         if (!status) {
   352             SDL_Log("SDL_GL_MULTISAMPLESAMPLES: requested %d, got %d\n", fsaa,
   353                    value);
   354         } else {
   355             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get SDL_GL_MULTISAMPLESAMPLES: %s\n",
   356                    SDL_GetError());
   357         }
   358     }
   359     if (accel >= 0) {
   360         status = SDL_GL_GetAttribute(SDL_GL_ACCELERATED_VISUAL, &value);
   361         if (!status) {
   362             SDL_Log("SDL_GL_ACCELERATED_VISUAL: requested %d, got %d\n", accel,
   363                    value);
   364         } else {
   365             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to get SDL_GL_ACCELERATED_VISUAL: %s\n",
   366                    SDL_GetError());
   367         }
   368     }
   369 
   370     /* Set rendering settings */
   371     ctx.glMatrixMode(GL_PROJECTION);
   372     ctx.glLoadIdentity();
   373     ctx.glOrtho(-2.0, 2.0, -2.0, 2.0, -20.0, 20.0);
   374     ctx.glMatrixMode(GL_MODELVIEW);
   375     ctx.glLoadIdentity();
   376     ctx.glEnable(GL_DEPTH_TEST);
   377     ctx.glDepthFunc(GL_LESS);
   378     ctx.glShadeModel(GL_SMOOTH);
   379     
   380     /* Main render loop */
   381     frames = 0;
   382     then = SDL_GetTicks();
   383     done = 0;
   384     while (!done) {
   385         SDL_bool update_swap_interval = SDL_FALSE;
   386 
   387         /* Check for events */
   388         ++frames;
   389         while (SDL_PollEvent(&event)) {
   390             SDLTest_CommonEvent(state, &event, &done);
   391             if (event.type == SDL_KEYDOWN) {
   392                 if (event.key.keysym.sym == SDLK_o) {
   393                     swap_interval--;
   394                     update_swap_interval = SDL_TRUE;
   395                 } else if (event.key.keysym.sym == SDLK_p) {
   396                     swap_interval++;
   397                     update_swap_interval = SDL_TRUE;
   398                 }
   399             }
   400         }
   401 
   402         if (update_swap_interval) {
   403             SDL_Log("Swap interval to be set to %d\n", swap_interval);
   404         }
   405 
   406         for (i = 0; i < state->num_windows; ++i) {
   407             int w, h;
   408             if (state->windows[i] == NULL)
   409                 continue;
   410             SDL_GL_MakeCurrent(state->windows[i], context);
   411             if (update_swap_interval) {
   412                 SDL_GL_SetSwapInterval(swap_interval);
   413             }
   414             SDL_GL_GetDrawableSize(state->windows[i], &w, &h);
   415             ctx.glViewport(0, 0, w, h);
   416             Render();
   417             SDL_GL_SwapWindow(state->windows[i]);
   418         }
   419     }
   420 
   421     /* Print out some timing information */
   422     now = SDL_GetTicks();
   423     if (now > then) {
   424         SDL_Log("%2.2f frames per second\n",
   425                ((double) frames * 1000) / (now - then));
   426     }
   427     quit(0);
   428     return 0;
   429 }
   430 
   431 #else /* HAVE_OPENGL */
   432 
   433 int
   434 main(int argc, char *argv[])
   435 {
   436     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No OpenGL support on this system\n");
   437     return 1;
   438 }
   439 
   440 #endif /* HAVE_OPENGL */