Clean up any renderer in SDL_DestroyWindowSurface()

Also added an automated test to verify window surface functionality.

Fixes https://github.com/libsdl-org/SDL/issues/10133

(cherry picked from commit a522bfe3f1)
This commit is contained in:
Sam Lantinga 2024-06-29 02:34:17 -07:00
parent a0ec7c0906
commit a3f0c373d3
2 changed files with 143 additions and 64 deletions

View File

@ -1947,12 +1947,6 @@ int SDL_RecreateWindow(SDL_Window *window, Uint32 flags)
/* Tear down the old native window */
SDL_DestroyWindowSurface(window);
if (_this->checked_texture_framebuffer) { /* never checked? No framebuffer to destroy. Don't risk calling the wrong implementation. */
if (_this->DestroyWindowFramebuffer) {
_this->DestroyWindowFramebuffer(_this, window);
}
}
if (_this->DestroyWindow && !(flags & SDL_WINDOW_FOREIGN)) {
_this->DestroyWindow(_this, window);
}
@ -2667,55 +2661,57 @@ static SDL_Surface *SDL_CreateWindowFramebuffer(SDL_Window *window)
using a GPU texture through the 2D render API, if we think this would
be more efficient. This only checks once, on demand. */
if (!_this->checked_texture_framebuffer) {
SDL_bool attempt_texture_framebuffer = SDL_TRUE;
/* See if the user or application wants to specifically disable the framebuffer */
const char *hint = SDL_GetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION);
if (hint) {
if ((*hint == '0') || (SDL_strcasecmp(hint, "false") == 0) || (SDL_strcasecmp(hint, "software") == 0)) {
attempt_texture_framebuffer = SDL_FALSE;
}
}
SDL_bool attempt_texture_framebuffer;
if (_this->is_dummy) { /* dummy driver never has GPU support, of course. */
attempt_texture_framebuffer = SDL_FALSE;
}
} else {
/* See if the user or application wants to specifically disable the framebuffer */
const char *hint = SDL_GetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION);
if (hint && *hint) {
if ((*hint == '0') || (SDL_strcasecmp(hint, "false") == 0) || (SDL_strcasecmp(hint, "software") == 0)) {
attempt_texture_framebuffer = SDL_FALSE;
} else {
attempt_texture_framebuffer = SDL_TRUE;
}
} else {
attempt_texture_framebuffer = SDL_TRUE;
#if defined(__LINUX__)
/* On WSL, direct X11 is faster than using OpenGL for window framebuffers, so try to detect WSL and avoid texture framebuffer. */
else if ((_this->CreateWindowFramebuffer) && (SDL_strcmp(_this->name, "x11") == 0)) {
struct stat sb;
if ((stat("/proc/sys/fs/binfmt_misc/WSLInterop", &sb) == 0) || (stat("/run/WSL", &sb) == 0)) { /* if either of these exist, we're on WSL. */
attempt_texture_framebuffer = SDL_FALSE;
/* On WSL, direct X11 is faster than using OpenGL for window framebuffers, so try to detect WSL and avoid texture framebuffer. */
if ((_this->CreateWindowFramebuffer) && (SDL_strcmp(_this->name, "x11") == 0)) {
struct stat sb;
if ((stat("/proc/sys/fs/binfmt_misc/WSLInterop", &sb) == 0) || (stat("/run/WSL", &sb) == 0)) { /* if either of these exist, we're on WSL. */
attempt_texture_framebuffer = SDL_FALSE;
}
}
}
#endif
#if defined(__WIN32__) || defined(__WINGDK__) /* GDI BitBlt() is way faster than Direct3D dynamic textures right now. (!!! FIXME: is this still true?) */
else if ((_this->CreateWindowFramebuffer) && (SDL_strcmp(_this->name, "windows") == 0)) {
attempt_texture_framebuffer = SDL_FALSE;
}
if ((_this->CreateWindowFramebuffer) && (SDL_strcmp(_this->name, "windows") == 0)) {
attempt_texture_framebuffer = SDL_FALSE;
}
#endif
#if defined(__EMSCRIPTEN__)
else {
attempt_texture_framebuffer = SDL_FALSE;
}
#endif
}
if (attempt_texture_framebuffer) {
if (SDL_CreateWindowTexture(_this, window, &format, &pixels, &pitch) == -1) {
/* !!! FIXME: if this failed halfway (made renderer, failed to make texture, etc),
!!! FIXME: we probably need to clean this up so it doesn't interfere with
!!! FIXME: a software fallback at the system level (can we blit to an
!!! FIXME: OpenGL window? etc). */
} else {
/* future attempts will just try to use a texture framebuffer. */
/* !!! FIXME: maybe we shouldn't override these but check if we used a texture
!!! FIXME: framebuffer at the right places; is it feasible we could have an
!!! FIXME: accelerated OpenGL window and a second ends up in software? */
_this->CreateWindowFramebuffer = SDL_CreateWindowTexture;
_this->UpdateWindowFramebuffer = SDL_UpdateWindowTexture;
_this->DestroyWindowFramebuffer = SDL_DestroyWindowTexture;
created_framebuffer = SDL_TRUE;
if (attempt_texture_framebuffer) {
if (SDL_CreateWindowTexture(_this, window, &format, &pixels, &pitch) == -1) {
/* !!! FIXME: if this failed halfway (made renderer, failed to make texture, etc),
!!! FIXME: we probably need to clean this up so it doesn't interfere with
!!! FIXME: a software fallback at the system level (can we blit to an
!!! FIXME: OpenGL window? etc). */
} else {
/* future attempts will just try to use a texture framebuffer. */
/* !!! FIXME: maybe we shouldn't override these but check if we used a texture
!!! FIXME: framebuffer at the right places; is it feasible we could have an
!!! FIXME: accelerated OpenGL window and a second ends up in software? */
_this->CreateWindowFramebuffer = SDL_CreateWindowTexture;
_this->UpdateWindowFramebuffer = SDL_UpdateWindowTexture;
_this->DestroyWindowFramebuffer = SDL_DestroyWindowTexture;
created_framebuffer = SDL_TRUE;
}
}
}
@ -2724,6 +2720,7 @@ static SDL_Surface *SDL_CreateWindowFramebuffer(SDL_Window *window)
if (!created_framebuffer) {
if (!_this->CreateWindowFramebuffer || !_this->UpdateWindowFramebuffer) {
SDL_SetError("Window framebuffer support not available");
return NULL;
}
@ -2733,6 +2730,7 @@ static SDL_Surface *SDL_CreateWindowFramebuffer(SDL_Window *window)
}
if (window->surface) {
/* We may have gone recursive and already created the surface */
return window->surface;
}
@ -2755,7 +2753,12 @@ SDL_Surface *SDL_GetWindowSurface(SDL_Window *window)
CHECK_WINDOW_MAGIC(window, NULL);
if (!window->surface_valid) {
SDL_DestroyWindowSurface(window);
if (window->surface) {
window->surface->flags &= ~SDL_DONTFREE;
SDL_FreeSurface(window->surface);
window->surface = NULL;
}
window->surface = SDL_CreateWindowFramebuffer(window);
if (window->surface) {
window->surface_valid = SDL_TRUE;
@ -2792,6 +2795,25 @@ int SDL_UpdateWindowSurfaceRects(SDL_Window *window, const SDL_Rect *rects,
return _this->UpdateWindowFramebuffer(_this, window, rects, numrects);
}
int SDL_DestroyWindowSurface(SDL_Window *window)
{
CHECK_WINDOW_MAGIC(window, -1);
if (window->surface) {
window->surface->flags &= ~SDL_DONTFREE;
SDL_FreeSurface(window->surface);
window->surface = NULL;
window->surface_valid = SDL_FALSE;
}
if (_this->checked_texture_framebuffer) { /* never checked? No framebuffer to destroy. Don't risk calling the wrong implementation. */
if (_this->DestroyWindowFramebuffer) {
_this->DestroyWindowFramebuffer(_this, window);
}
}
return 0;
}
int SDL_SetWindowBrightness(SDL_Window * window, float brightness)
{
Uint16 ramp[256];
@ -2837,19 +2859,6 @@ int SDL_SetWindowOpacity(SDL_Window * window, float opacity)
return retval;
}
int SDL_DestroyWindowSurface(SDL_Window *window)
{
CHECK_WINDOW_MAGIC(window, -1);
if (window->surface) {
window->surface->flags &= ~SDL_DONTFREE;
SDL_FreeSurface(window->surface);
window->surface = NULL;
window->surface_valid = SDL_FALSE;
}
return 0;
}
int SDL_GetWindowOpacity(SDL_Window *window, float *out_opacity)
{
CHECK_WINDOW_MAGIC(window, -1);
@ -3288,19 +3297,15 @@ void SDL_DestroyWindow(SDL_Window *window)
SDL_SetMouseFocus(NULL);
}
/* make no context current if this is the current context window. */
SDL_DestroyWindowSurface(window);
/* Make no context current if this is the current context window */
if (window->flags & SDL_WINDOW_OPENGL) {
if (_this->current_glwin == window) {
SDL_GL_MakeCurrent(window, NULL);
}
}
SDL_DestroyWindowSurface(window);
if (_this->checked_texture_framebuffer) { /* never checked? No framebuffer to destroy. Don't risk calling the wrong implementation. */
if (_this->DestroyWindowFramebuffer) {
_this->DestroyWindowFramebuffer(_this, window);
}
}
if (_this->DestroyWindow) {
_this->DestroyWindow(_this, window);
}

View File

@ -2181,6 +2181,76 @@ int video_setWindowCenteredOnDisplay(void *arg)
return TEST_COMPLETED;
}
/**
* Tests window surface functionality
*/
static int video_getWindowSurface(void *arg)
{
const char *title = "video_getWindowSurface Test Window";
SDL_Window *window;
SDL_Surface *surface;
SDL_Renderer *renderer;
Uint32 renderer_flags = SDL_RENDERER_ACCELERATED;
int result;
if (SDL_strcmp(SDL_GetCurrentVideoDriver(), "dummy") == 0) {
renderer_flags = SDL_RENDERER_SOFTWARE;
}
/* Make sure we're testing interaction with an accelerated renderer */
SDL_SetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION, "1");
window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 320, 320, 0);
SDLTest_AssertPass("Call to SDL_CreateWindow('%s', SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 320, 320, 0)", title);
SDLTest_AssertCheck(window != NULL, "Validate that returned window is not NULL");
surface = SDL_GetWindowSurface(window);
SDLTest_AssertPass("Call to SDL_GetWindowSurface(window)");
SDLTest_AssertCheck(surface != NULL, "Validate that returned surface is not NULL");
SDLTest_AssertCheck(SDL_HasWindowSurface(window), "Validate that window has a surface");
result = SDL_UpdateWindowSurface(window);
SDLTest_AssertPass("Call to SDL_UpdateWindowSurface(window)");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
/* We shouldn't be able to create a renderer on a window with a surface */
renderer = SDL_CreateRenderer(window, -1, renderer_flags);
SDLTest_AssertPass("Call to SDL_CreateRenderer(window, -1, 0x%x)", renderer_flags);
SDLTest_AssertCheck(renderer == NULL, "Validate that returned renderer is NULL");
result = SDL_DestroyWindowSurface(window);
SDLTest_AssertPass("Call to SDL_DestroyWindowSurface(window)");
SDLTest_AssertCheck(result == 0, "Verify return value; expected: 0, got: %d", result);
SDLTest_AssertCheck(!SDL_HasWindowSurface(window), "Validate that window does not have a surface");
/* We should be able to create a renderer on the window now */
renderer = SDL_CreateRenderer(window, -1, renderer_flags);
SDLTest_AssertPass("Call to SDL_CreateRenderer(window, -1, 0x%x)", renderer_flags);
SDLTest_AssertCheck(renderer != NULL, "Validate that returned renderer is not NULL");
/* We should not be able to create a window surface now, unless it was created by the renderer */
if (!SDL_HasWindowSurface(window)) {
surface = SDL_GetWindowSurface(window);
SDLTest_AssertPass("Call to SDL_GetWindowSurface(window)");
SDLTest_AssertCheck(surface == NULL, "Validate that returned surface is NULL");
}
SDL_DestroyRenderer(renderer);
SDLTest_AssertPass("Call to SDL_DestroyRenderer(renderer)");
SDLTest_AssertCheck(!SDL_HasWindowSurface(window), "Validate that window does not have a surface");
/* We should be able to create a window surface again */
surface = SDL_GetWindowSurface(window);
SDLTest_AssertPass("Call to SDL_GetWindowSurface(window)");
SDLTest_AssertCheck(surface != NULL, "Validate that returned surface is not NULL");
SDLTest_AssertCheck(SDL_HasWindowSurface(window), "Validate that window has a surface");
/* Clean up */
SDL_DestroyWindow(window);
return TEST_COMPLETED;
}
/* ================= Test References ================== */
/* Video test cases */
@ -2265,13 +2335,17 @@ static const SDLTest_TestCaseReference videoTest23 =
static const SDLTest_TestCaseReference videoTest24 =
{ (SDLTest_TestCaseFp) video_setWindowCenteredOnDisplay, "video_setWindowCenteredOnDisplay", "Checks using SDL_WINDOWPOS_CENTERED_DISPLAY centers the window on a display", TEST_ENABLED };
static const SDLTest_TestCaseReference videoTest25 = {
(SDLTest_TestCaseFp)video_getWindowSurface, "video_getWindowSurface", "Checks window surface functionality", TEST_ENABLED
};
/* Sequence of Video test cases */
static const SDLTest_TestCaseReference *videoTests[] = {
&videoTest1, &videoTest2, &videoTest3, &videoTest4, &videoTest5, &videoTest6,
&videoTest7, &videoTest8, &videoTest9, &videoTest10, &videoTest11, &videoTest12,
&videoTest13, &videoTest14, &videoTest15, &videoTest16, &videoTest17,
&videoTest18, &videoTest19, &videoTest20, &videoTest21, &videoTest22,
&videoTest23, &videoTest24, NULL
&videoTest23, &videoTest24, &videoTest25, NULL
};
/* Video test suite (global) */