From b906de1125e50add32f6e08c4c13c03de715a85a Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 20 Nov 2023 20:26:12 -0500 Subject: [PATCH] render: Batching is always enabled now! Make sure your app uses SDL_RenderFlush() before it talks to D3D/OpenGL/etc! Fixes #8584. --- docs/README-migration.md | 8 +++++ include/SDL3/SDL_hints.h | 24 --------------- include/SDL3/SDL_render.h | 20 ++++-------- src/render/SDL_render.c | 47 +++++++---------------------- src/render/SDL_sysrender.h | 2 -- src/render/metal/SDL_render_metal.m | 2 -- src/test/SDL_test_common.c | 1 - 7 files changed, 25 insertions(+), 79 deletions(-) diff --git a/docs/README-migration.md b/docs/README-migration.md index 0601988fcfce2..4a6d5dade443c 100644 --- a/docs/README-migration.md +++ b/docs/README-migration.md @@ -559,6 +559,7 @@ The following hints have been removed: * SDL_HINT_IME_SUPPORT_EXTENDED_TEXT - the normal text editing event has extended text * SDL_HINT_MOUSE_RELATIVE_SCALING - mouse coordinates are no longer automatically scaled by the SDL renderer * SDL_HINT_RENDER_LOGICAL_SIZE_MODE - the logical size mode is explicitly set with SDL_SetRenderLogicalPresentation() +* SDL_HINT_RENDER_BATCHING - Render batching is always enabled, apps should call SDL_FlushRenderer() before calling into a lower-level graphics API. * SDL_HINT_VIDEO_FOREIGN_WINDOW_OPENGL - replaced with the "opengl" property in SDL_CreateWindowWithProperties() * SDL_HINT_VIDEO_FOREIGN_WINDOW_VULKAN - replaced with the "vulkan" property in SDL_CreateWindowWithProperties() * SDL_HINT_VIDEO_HIGHDPI_DISABLED - high DPI support is always enabled @@ -811,6 +812,13 @@ The following functions have been renamed: ## SDL_render.h +The 2D renderer API always uses batching in SDL3. There is no magic to turn +it on and off; it doesn't matter if you select a specific renderer or try to +use any hint. This means that all apps that use SDL3's 2D renderer and also +want to call directly into the platform's lower-layer graphics API _must_ call +SDL_RenderFlush() before doing so. This will make sure any pending rendering +work from SDL is done before the app starts directly drawing. + SDL_GetRenderDriverInfo() has been removed, since most of the information it reported were estimates and could not be accurate before creating a renderer. Often times this function was used to figure out the index of a driver, so one would call it in a for-loop, looking diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index d2c2b0d8a73a9..184dac3fb1e74 100644 --- a/include/SDL3/SDL_hints.h +++ b/include/SDL3/SDL_hints.h @@ -1387,30 +1387,6 @@ extern "C" { */ #define SDL_HINT_QTWAYLAND_WINDOW_FLAGS "SDL_QTWAYLAND_WINDOW_FLAGS" -/** - * A variable controlling whether the 2D render API is compatible or efficient. - * - * This variable can be set to the following values: - * - * "0" - Don't use batching to make rendering more efficient. - * "1" - Use batching, but might cause problems if app makes its own direct OpenGL calls. - * - * Up to SDL 2.0.9, the render API would draw immediately when requested. Now - * it batches up draw requests and sends them all to the GPU only when forced - * to (during SDL_RenderPresent, when changing render targets, by updating a - * texture that the batch needs, etc). This is significantly more efficient, - * but it can cause problems for apps that expect to render on top of the - * render API's output. As such, SDL will disable batching if a specific - * render backend is requested (since this might indicate that the app is - * planning to use the underlying graphics API directly). This hint can - * be used to explicitly request batching in this instance. It is a contract - * that you will either never use the underlying graphics API directly, or - * if you do, you will call SDL_RenderFlush() before you do so any current - * batch goes to the GPU before your work begins. Not following this contract - * will result in undefined behavior. - */ -#define SDL_HINT_RENDER_BATCHING "SDL_RENDER_BATCHING" - /** * A variable controlling how the 2D render API renders lines * diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index b7313d9d09d2e..caff6f87a672f 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -1559,20 +1559,12 @@ extern DECLSPEC void SDLCALL SDL_DestroyRenderer(SDL_Renderer *renderer); * are planning to call into OpenGL/Direct3D/Metal/whatever directly in * addition to using an SDL_Renderer. * - * This is for a very-specific case: if you are using SDL's render API, you - * asked for a specific renderer backend (OpenGL, Direct3D, etc), you set - * SDL_HINT_RENDER_BATCHING to "1", and you plan to make OpenGL/D3D/whatever - * calls in addition to SDL render API calls. If all of this applies, you - * should call SDL_RenderFlush() between calls to SDL's render API and the - * low-level API you're using in cooperation. - * - * In all other cases, you can ignore this function. This is only here to get - * maximum performance out of a specific situation. In all other cases, SDL - * will do the right thing, perhaps at a performance loss. - * - * This function is first available in SDL 2.0.10, and is not needed in 2.0.9 - * and earlier, as earlier versions did not queue rendering commands at all, - * instead flushing them to the OS immediately. + * This is for a very-specific case: if you are using SDL's render API, and + * you plan to make OpenGL/D3D/whatever calls in addition to SDL render API + * calls. If this applies, you should call SDL_RenderFlush() between calls to + * SDL's render API and the low-level API you're using in cooperation. + * + * In all other cases, you can ignore this function. * * \param renderer the rendering context * \returns 0 on success or a negative error code on failure; call diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index aedde6af332c8..533a2fe524c5d 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -265,11 +265,6 @@ static int FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture) return 0; } -static SDL_INLINE int FlushRenderCommandsIfNotBatching(SDL_Renderer *renderer) -{ - return renderer->batching ? 0 : FlushRenderCommands(renderer); -} - int SDL_RenderFlush(SDL_Renderer *renderer) { return FlushRenderCommands(renderer); @@ -813,7 +808,6 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props) const char *name = SDL_GetStringProperty(props, "name", NULL); SDL_Renderer *renderer = NULL; const int n = SDL_GetNumRenderDrivers(); - SDL_bool batching = SDL_TRUE; const char *hint; int i; @@ -855,9 +849,6 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props) if (SDL_strcasecmp(name, driver->info.name) == 0) { /* Create a new renderer instance */ renderer = driver->CreateRenderer(window, props); - if (renderer) { - batching = SDL_FALSE; - } break; } } @@ -890,14 +881,6 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props) VerifyDrawQueueFunctions(renderer); - /* let app/user override batching decisions. */ - if (renderer->always_batch) { - batching = SDL_TRUE; - } else if (SDL_GetHint(SDL_HINT_RENDER_BATCHING)) { - batching = SDL_GetHintBoolean(SDL_HINT_RENDER_BATCHING, SDL_TRUE); - } - - renderer->batching = batching; renderer->magic = &SDL_renderer_magic; renderer->window = window; renderer->target_mutex = SDL_CreateMutex(); @@ -2106,7 +2089,7 @@ static int SDL_SetRenderTargetInternal(SDL_Renderer *renderer, SDL_Texture *text } /* All set! */ - return FlushRenderCommandsIfNotBatching(renderer); + return 0; } int SDL_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) @@ -2506,7 +2489,6 @@ int SDL_ConvertEventToRenderCoordinates(SDL_Renderer *renderer, SDL_Event *event int SDL_SetRenderViewport(SDL_Renderer *renderer, const SDL_Rect *rect) { - int retval; CHECK_RENDERER_MAGIC(renderer, -1); if (rect) { @@ -2520,8 +2502,7 @@ int SDL_SetRenderViewport(SDL_Renderer *renderer, const SDL_Rect *rect) renderer->view->viewport.w = -1; renderer->view->viewport.h = -1; } - retval = QueueCmdSetViewport(renderer); - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return QueueCmdSetViewport(renderer); } int SDL_GetRenderViewport(SDL_Renderer *renderer, SDL_Rect *rect) @@ -2563,7 +2544,6 @@ static void GetRenderViewportSize(SDL_Renderer *renderer, SDL_FRect *rect) int SDL_SetRenderClipRect(SDL_Renderer *renderer, const SDL_Rect *rect) { - int retval; CHECK_RENDERER_MAGIC(renderer, -1) if (rect && rect->w >= 0 && rect->h >= 0) { @@ -2577,8 +2557,7 @@ int SDL_SetRenderClipRect(SDL_Renderer *renderer, const SDL_Rect *rect) SDL_zero(renderer->view->clip_rect); } - retval = QueueCmdSetClipRect(renderer); - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return QueueCmdSetClipRect(renderer); } int SDL_GetRenderClipRect(SDL_Renderer *renderer, SDL_Rect *rect) @@ -2617,7 +2596,7 @@ int SDL_SetRenderScale(SDL_Renderer *renderer, float scaleX, float scaleY) /* The scale affects the existing viewport and clip rectangle */ retval += QueueCmdSetViewport(renderer); retval += QueueCmdSetClipRect(renderer); - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } int SDL_GetRenderScale(SDL_Renderer *renderer, float *scaleX, float *scaleY) @@ -2687,7 +2666,7 @@ int SDL_RenderClear(SDL_Renderer *renderer) int retval; CHECK_RENDERER_MAGIC(renderer, -1); retval = QueueCmdClear(renderer); - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } int SDL_RenderPoint(SDL_Renderer *renderer, float x, float y) @@ -2753,7 +2732,7 @@ int SDL_RenderPoints(SDL_Renderer *renderer, const SDL_FPoint *points, int count } else { retval = QueueCmdDrawPoints(renderer, points, count); } - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } int SDL_RenderLine(SDL_Renderer *renderer, float x1, float y1, float x2, float y2) @@ -3072,7 +3051,7 @@ int SDL_RenderLines(SDL_Renderer *renderer, const SDL_FPoint *points, int count) retval = QueueCmdDrawLines(renderer, points, count); } - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } int SDL_RenderRect(SDL_Renderer *renderer, const SDL_FRect *rect) @@ -3181,7 +3160,7 @@ int SDL_RenderFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, int coun SDL_small_free(frects, isstack); - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } int SDL_RenderTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FRect *srcrect, const SDL_FRect *dstrect) @@ -3286,7 +3265,7 @@ int SDL_RenderTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_FR retval = QueueCmdCopy(renderer, texture, &real_srcrect, &real_dstrect); } - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } int SDL_RenderTextureRotated(SDL_Renderer *renderer, SDL_Texture *texture, @@ -3441,7 +3420,7 @@ int SDL_RenderTextureRotated(SDL_Renderer *renderer, SDL_Texture *texture, renderer->view->scale.x, renderer->view->scale.y); } - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } int SDL_RenderGeometry(SDL_Renderer *renderer, @@ -3798,8 +3777,6 @@ static int SDLCALL SDL_SW_RenderGeometryRaw(SDL_Renderer *renderer, renderer->view->scale.y); if (retval < 0) { goto end; - } else { - FlushRenderCommandsIfNotBatching(renderer); } } @@ -3821,8 +3798,6 @@ static int SDLCALL SDL_SW_RenderGeometryRaw(SDL_Renderer *renderer, renderer->view->scale.y); if (retval < 0) { goto end; - } else { - FlushRenderCommandsIfNotBatching(renderer); } } @@ -3944,7 +3919,7 @@ int SDL_RenderGeometryRaw(SDL_Renderer *renderer, renderer->view->scale.x, renderer->view->scale.y); - return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer); + return retval; } int SDL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect, Uint32 format, void *pixels, int pitch) diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 187073de13097..54d2d151484ec 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -254,8 +254,6 @@ struct SDL_Renderer SDL_Color color; /**< Color for drawing operations values */ SDL_BlendMode blendMode; /**< The drawing blend mode */ - SDL_bool always_batch; - SDL_bool batching; SDL_RenderCommand *render_commands; SDL_RenderCommand *render_commands_tail; SDL_RenderCommand *render_commands_pool; diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index 285d42018ec12..dc2b6124b6094 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -1917,8 +1917,6 @@ in case we want to use it later (recreating the renderer) renderer->info = METAL_RenderDriver.info; renderer->info.flags = SDL_RENDERER_ACCELERATED; - renderer->always_batch = SDL_TRUE; - #if (defined(__MACOS__) && defined(MAC_OS_X_VERSION_10_13)) || TARGET_OS_MACCATALYST if (@available(macOS 10.13, *)) { data.mtllayer.displaySyncEnabled = SDL_GetBooleanProperty(create_props, "present_vsync", SDL_FALSE); diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index 45928628df90d..80a7865889f8d 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -228,7 +228,6 @@ int SDLTest_CommonArg(SDLTest_CommonState *state, int index) } state->renderdriver = argv[index]; SDL_SetHint(SDL_HINT_RENDER_DRIVER, state->renderdriver); - SDL_SetHint(SDL_HINT_RENDER_BATCHING, "1"); return 2; } if (SDL_strcasecmp(argv[index], "--gldebug") == 0) {