SDL: Added SDL_CreateRendererWithProperties() and SDL_CreateTextureWithProperties()

From 1c64366b8097ff9e9361e878f6aff793dcde3bf1 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 13 Nov 2023 12:13:20 -0800
Subject: [PATCH] Added SDL_CreateRendererWithProperties() and
 SDL_CreateTextureWithProperties()

---
 include/SDL3/SDL_render.h                | 127 +++++++++++++++++------
 src/dynapi/SDL_dynapi.sym                |   2 +
 src/dynapi/SDL_dynapi_overrides.h        |   2 +
 src/dynapi/SDL_dynapi_procs.h            |   2 +
 src/render/SDL_render.c                  |  71 ++++++++++---
 src/render/SDL_sysrender.h               |   4 +-
 src/render/direct3d/SDL_render_d3d.c     |   8 +-
 src/render/direct3d11/SDL_render_d3d11.c |  75 ++++++++-----
 src/render/direct3d12/SDL_render_d3d12.c | 103 +++++++++++-------
 src/render/metal/SDL_render_metal.m      |   6 +-
 src/render/opengl/SDL_render_gl.c        |  65 +++++++++---
 src/render/opengles2/SDL_render_gles2.c  |  63 +++++++----
 src/render/ps2/SDL_render_ps2.c          |  12 ++-
 src/render/psp/SDL_render_psp.c          |  10 +-
 src/render/software/SDL_render_sw.c      |  10 +-
 src/render/vitagxm/SDL_render_vita_gxm.c |  11 +-
 16 files changed, 404 insertions(+), 167 deletions(-)

diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h
index 86a2abb1a050..b7313d9d09d2 100644
--- a/include/SDL3/SDL_render.h
+++ b/include/SDL3/SDL_render.h
@@ -195,7 +195,6 @@ extern DECLSPEC int SDLCALL SDL_GetNumRenderDrivers(void);
  */
 extern DECLSPEC const char *SDLCALL SDL_GetRenderDriver(int index);
 
-
 /**
  * Create a window and default renderer.
  *
@@ -215,14 +214,13 @@ extern DECLSPEC const char *SDLCALL SDL_GetRenderDriver(int index);
  */
 extern DECLSPEC int SDLCALL SDL_CreateWindowAndRenderer(int width, int height, Uint32 window_flags, SDL_Window **window, SDL_Renderer **renderer);
 
-
 /**
  * Create a 2D rendering context for a window.
  *
  * If you want a specific renderer, you can specify its name here. A list of
  * available renderers can be obtained by calling SDL_GetRenderDriver multiple
  * times, with indices from 0 to SDL_GetNumRenderDrivers()-1. If you don't
- * need a specific renderer, specify NULL and SDL will attempt to chooes the
+ * need a specific renderer, specify NULL and SDL will attempt to choose the
  * best option for you, based on what is available on the user's system.
  *
  * By default the rendering size matches the window size in pixels, but you
@@ -238,13 +236,37 @@ extern DECLSPEC int SDLCALL SDL_CreateWindowAndRenderer(int width, int height, U
  *
  * \since This function is available since SDL 3.0.0.
  *
+ * \sa SDL_CreateRendererWithProperties
  * \sa SDL_CreateSoftwareRenderer
  * \sa SDL_DestroyRenderer
  * \sa SDL_GetNumRenderDrivers
  * \sa SDL_GetRenderDriver
  * \sa SDL_GetRendererInfo
  */
-extern DECLSPEC SDL_Renderer *SDLCALL SDL_CreateRenderer(SDL_Window *window, const char *name, Uint32 flags);
+extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window *window, const char *name, Uint32 flags);
+
+/**
+ * Create a 2D rendering context for a window, with the specified properties.
+ *
+ * These are the supported properties:
+ *
+ * - "window" (pointer) - the window where rendering is displayed
+ * - "surface" (pointer) - the surface where rendering is displayed, if you want a software renderer without a window
+ * - "name" (string) - the name of the rendering driver to use, if a specific one is desired
+ * - "present_vsync" (boolean) - true if you want present synchronized with the refresh rate
+ *
+ * \param props the properties to use
+ * \returns a valid rendering context or NULL if there was an error; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_CreateRenderer
+ * \sa SDL_CreateSoftwareRenderer
+ * \sa SDL_DestroyRenderer
+ * \sa SDL_GetRendererInfo
+ */
+extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRendererWithProperties(SDL_PropertiesID props);
 
 /**
  * Create a 2D software rendering context for a surface.
@@ -309,7 +331,7 @@ extern DECLSPEC int SDLCALL SDL_GetRendererInfo(SDL_Renderer *renderer, SDL_Rend
 /**
  * Get the properties associated with a renderer.
  *
- * The following properties are provided by SDL:
+ * The following read-only properties are provided by SDL:
  *
  * ```
  * "SDL.renderer.d3d9.device" (pointer) - the IDirect3DDevice9 associated with the renderer
@@ -386,6 +408,7 @@ extern DECLSPEC int SDLCALL SDL_GetCurrentRenderOutputSize(SDL_Renderer *rendere
  * \since This function is available since SDL 3.0.0.
  *
  * \sa SDL_CreateTextureFromSurface
+ * \sa SDL_CreateTextureWithProperties
  * \sa SDL_DestroyTexture
  * \sa SDL_QueryTexture
  * \sa SDL_UpdateTexture
@@ -413,50 +436,96 @@ extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTexture(SDL_Renderer *renderer, U
  * \since This function is available since SDL 3.0.0.
  *
  * \sa SDL_CreateTexture
+ * \sa SDL_CreateTextureWithProperties
  * \sa SDL_DestroyTexture
  * \sa SDL_QueryTexture
  */
 extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *surface);
 
+/**
+ * Create a texture for a rendering context with the specified properties.
+ *
+ * These are the supported properties:
+ *
+ * - "format" (number) - one of the enumerated values in SDL_PixelFormatEnum, defaults to the best RGBA format for the renderer
+ * - "access" (number) - one of the enumerated values in SDL_TextureAccess, defaults to SDL_TEXTUREACCESS_STATIC
+ * - "width" (number) - the width of the texture in pixels, required
+ * - "height" (number) - the height of the texture in pixels, required
+ *
+ * With the direct3d11 renderer:
+ *
+ * - "d3d11.texture" (pointer) - the ID3D11Texture2D associated with the texture, if you want to wrap an existing texture.
+ * - "d3d11.texture_u" (pointer) - the ID3D11Texture2D associated with the U plane of a YUV texture, if you want to wrap an existing texture.
+ * - "d3d11.texture_v" (pointer) - the ID3D11Texture2D associated with the V plane of a YUV texture, if you want to wrap an existing texture.
+ *
+ * With the direct3d12 renderer:
+ *
+ * - "d3d12.texture" (pointer) - the ID3D12Resource associated with the texture, if you want to wrap an existing texture.
+ * - "d3d12.texture_u" (pointer) - the ID3D12Resource associated with the U plane of a YUV texture, if you want to wrap an existing texture.
+ * - "d3d12.texture_v" (pointer) - the ID3D12Resource associated with the V plane of a YUV texture, if you want to wrap an existing texture.
+ *
+ * With the opengl renderer:
+ *
+ * - "opengl.texture" (number) - the GLuint texture associated with the texture, if you want to wrap an existing texture.
+ * - "opengl.texture_uv" (number) - the GLuint texture associated with the UV plane of an NV12 texture, if you want to wrap an existing texture.
+ * - "opengl.texture_u" (number) - the GLuint texture associated with the U plane of a YUV texture, if you want to wrap an existing texture.
+ * - "opengl.texture_v" (number) - the GLuint texture associated with the V plane of a YUV texture, if you want to wrap an existing texture.
+ *
+ * With the opengles2 renderer:
+ *
+ * - "opengles2.texture" (number) - the GLuint texture associated with the texture, if you want to wrap an existing texture.
+ * - "opengles2.texture_uv" (number) - the GLuint texture associated with the UV plane of an NV12 texture, if you want to wrap an existing texture.
+ * - "opengles2.texture_u" (number) - the GLuint texture associated with the U plane of a YUV texture, if you want to wrap an existing texture.
+ * - "opengles2.texture_v" (number) - the GLuint texture associated with the V plane of a YUV texture, if you want to wrap an existing texture.
+ *
+ * \param renderer the rendering context
+ * \param props the properties to use
+ * \returns a pointer to the created texture or NULL if no rendering context
+ *          was active, the format was unsupported, or the width or height
+ *          were out of range; call SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_CreateTextureFromSurface
+ * \sa SDL_CreateTexture
+ * \sa SDL_DestroyTexture
+ * \sa SDL_QueryTexture
+ * \sa SDL_UpdateTexture
+ */
+extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_PropertiesID props);
+
 /**
  * Get the properties associated with a texture.
  *
- * The following properties are provided by SDL:
+ * The following read-only properties are provided by SDL:
  *
  * With the direct3d11 renderer:
  *
- * ```
- * "SDL.texture.d3d11.texture" (pointer) - the ID3D11Texture2D associated with the texture
- * "SDL.texture.d3d11.texture_u" (pointer) - the ID3D11Texture2D associated with the U plane of a YUV texture
- * "SDL.texture.d3d11.texture_v" (pointer) - the ID3D11Texture2D associated with the V plane of a YUV texture
- * ```
+ * - "SDL.texture.d3d11.texture" (pointer) - the ID3D11Texture2D associated with the texture
+ * - "SDL.texture.d3d11.texture_u" (pointer) - the ID3D11Texture2D associated with the U plane of a YUV texture
+ * - "SDL.texture.d3d11.texture_v" (pointer) - the ID3D11Texture2D associated with the V plane of a YUV texture
  *
  * With the direct3d12 renderer:
  *
- * ```
- * "SDL.texture.d3d12.texture" (pointer) - the ID3D12Resource associated with the texture
- * "SDL.texture.d3d12.texture_u" (pointer) - the ID3D12Resource associated with the U plane of a YUV texture
- * "SDL.texture.d3d12.texture_v" (pointer) - the ID3D12Resource associated with the V plane of a YUV texture
- * ```
+ * - "SDL.texture.d3d12.texture" (pointer) - the ID3D12Resource associated with the texture
+ * - "SDL.texture.d3d12.texture_u" (pointer) - the ID3D12Resource associated with the U plane of a YUV texture
+ * - "SDL.texture.d3d12.texture_v" (pointer) - the ID3D12Resource associated with the V plane of a YUV texture
  *
  * With the opengl renderer:
  *
- * ```
- * "SDL.texture.opengl.texture" (number) - the GLuint texture associated with the texture
- * "SDL.texture.opengl.texture_u" (number) - the GLuint texture associated with the U plane of a YUV texture
- * "SDL.texture.opengl.texture_v" (number) - the GLuint texture associated with the V plane of a YUV texture
- * "SDL.texture.opengl.tex_w" (float) - the texture coordinate width of the texture (0.0 - 1.0)
- * "SDL.texture.opengl.tex_h" (float) - the texture coordinate height of the texture (0.0 - 1.0)
- * ```
+ * - "SDL.texture.opengl.texture" (number) - the GLuint texture associated with the texture
+ * - "SDL.texture.opengl.texture_uv" (number) - the GLuint texture associated with the UV plane of an NV12 texture
+ * - "SDL.texture.opengl.texture_u" (number) - the GLuint texture associated with the U plane of a YUV texture
+ * - "SDL.texture.opengl.texture_v" (number) - the GLuint texture associated with the V plane of a YUV texture
+ * - "SDL.texture.opengl.tex_w" (float) - the texture coordinate width of the texture (0.0 - 1.0)
+ * - "SDL.texture.opengl.tex_h" (float) - the texture coordinate height of the texture (0.0 - 1.0)
  *
  * With the opengles2 renderer:
  *
- * ```
- * "SDL.texture.opengles2.texture" (number) - the GLuint texture associated with the texture
- * "SDL.texture.opengles2.texture_uv" (number) - the GLuint texture associated with the UV plane of an NV12 texture
- * "SDL.texture.opengles2.texture_u" (number) - the GLuint texture associated with the U plane of a YUV texture
- * "SDL.texture.opengles2.texture_v" (number) - the GLuint texture associated with the V plane of a YUV texture
- * ```
+ * - "SDL.texture.opengles2.texture" (number) - the GLuint texture associated with the texture
+ * - "SDL.texture.opengles2.texture_uv" (number) - the GLuint texture associated with the UV plane of an NV12 texture
+ * - "SDL.texture.opengles2.texture_u" (number) - the GLuint texture associated with the U plane of a YUV texture
+ * - "SDL.texture.opengles2.texture_v" (number) - the GLuint texture associated with the V plane of a YUV texture
  *
  * \param texture the texture to query
  * \returns a valid property ID on success or 0 on failure; call
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index bb55f404c9a4..14bc32ae91ea 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -965,6 +965,8 @@ SDL3_0.0.0 {
     SDL_EnumerateProperties;
     SDL_SetBooleanProperty;
     SDL_GetBooleanProperty;
+    SDL_CreateTextureWithProperties;
+    SDL_CreateRendererWithProperties;
     # extra symbols go here (don't modify this line)
   local: *;
 };
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 57e1affe2342..83ec2ca332f7 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -990,3 +990,5 @@
 #define SDL_EnumerateProperties SDL_EnumerateProperties_REAL
 #define SDL_SetBooleanProperty SDL_SetBooleanProperty_REAL
 #define SDL_GetBooleanProperty SDL_GetBooleanProperty_REAL
+#define SDL_CreateTextureWithProperties SDL_CreateTextureWithProperties_REAL
+#define SDL_CreateRendererWithProperties SDL_CreateRendererWithProperties_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 3d8049d023a5..7ddba0e8c8c2 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -1015,3 +1015,5 @@ SDL_DYNAPI_PROC(float,SDL_GetFloatProperty,(SDL_PropertiesID a, const char *b, f
 SDL_DYNAPI_PROC(int,SDL_EnumerateProperties,(SDL_PropertiesID a, SDL_EnumeratePropertiesCallback b, void *c),(a,b,c),return)
 SDL_DYNAPI_PROC(int,SDL_SetBooleanProperty,(SDL_PropertiesID a, const char *b, SDL_bool c),(a,b,c),return)
 SDL_DYNAPI_PROC(SDL_bool,SDL_GetBooleanProperty,(SDL_PropertiesID a, const char *b, SDL_bool c),(a,b,c),return)
+SDL_DYNAPI_PROC(SDL_Texture*,SDL_CreateTextureWithProperties,(SDL_Renderer *a, SDL_PropertiesID b),(a,b),return)
+SDL_DYNAPI_PROC(SDL_Renderer*,SDL_CreateRendererWithProperties,(SDL_PropertiesID a),(a),return)
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index 8f987e84d0f7..aedde6af332c 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -804,15 +804,23 @@ static void SDL_CalculateSimulatedVSyncInterval(SDL_Renderer *renderer, SDL_Wind
 
 #endif /* !SDL_RENDER_DISABLED */
 
-SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, const char *name, Uint32 flags)
+
+SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
 {
 #ifndef SDL_RENDER_DISABLED
+    SDL_Window *window = SDL_GetProperty(props, "window", NULL);
+    SDL_Surface *surface = SDL_GetProperty(props, "surface", NULL);
+    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;
 
+    if (!window && surface) {
+        return SDL_CreateSoftwareRenderer(surface);
+    }
+
 #ifdef __ANDROID__
     Android_ActivityMutex_Lock_Running();
 #endif
@@ -834,11 +842,7 @@ SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, const char *name, Uint32 fl
 
     hint = SDL_GetHint(SDL_HINT_RENDER_VSYNC);
     if (hint && *hint) {
-        if (SDL_GetHintBoolean(SDL_HINT_RENDER_VSYNC, SDL_TRUE)) {
-            flags |= SDL_RENDERER_PRESENTVSYNC;
-        } else {
-            flags &= ~SDL_RENDERER_PRESENTVSYNC;
-        }
+        SDL_SetBooleanProperty(props, "present_vsync", SDL_GetHintBoolean(SDL_HINT_RENDER_VSYNC, SDL_TRUE));
     }
 
     if (!name) {
@@ -850,7 +854,7 @@ SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, const char *name, Uint32 fl
             const SDL_RenderDriver *driver = render_drivers[i];
             if (SDL_strcasecmp(name, driver->info.name) == 0) {
                 /* Create a new renderer instance */
-                renderer = driver->CreateRenderer(window, flags);
+                renderer = driver->CreateRenderer(window, props);
                 if (renderer) {
                     batching = SDL_FALSE;
                 }
@@ -860,13 +864,11 @@ SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, const char *name, Uint32 fl
     } else {
         for (i = 0; i < n; i++) {
             const SDL_RenderDriver *driver = render_drivers[i];
-            if ((driver->info.flags & flags) == flags) {
-                /* Create a new renderer instance */
-                renderer = driver->CreateRenderer(window, flags);
-                if (renderer) {
-                    /* Yay, we got one! */
-                    break;
-                }
+            /* Create a new renderer instance */
+            renderer = driver->CreateRenderer(window, props);
+            if (renderer) {
+                /* Yay, we got one! */
+                break;
             }
         }
     }
@@ -876,7 +878,7 @@ SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, const char *name, Uint32 fl
         goto error;
     }
 
-    if (flags & SDL_RENDERER_PRESENTVSYNC) {
+    if (SDL_GetBooleanProperty(props, "present_vsync", SDL_FALSE)) {
         renderer->wanted_vsync = SDL_TRUE;
 
         if (!(renderer->info.flags & SDL_RENDERER_PRESENTVSYNC)) {
@@ -956,6 +958,24 @@ SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, const char *name, Uint32 fl
 #endif
 }
 
+SDL_Renderer *SDL_CreateRenderer(SDL_Window *window, const char *name, Uint32 flags)
+{
+    SDL_Renderer *renderer;
+    SDL_PropertiesID props = SDL_CreateProperties();
+    SDL_SetProperty(props, "window", window);
+    if (flags & SDL_RENDERER_SOFTWARE) {
+        SDL_SetStringProperty(props, "name", "software");
+    } else {
+        SDL_SetStringProperty(props, "name", name);
+    }
+    if (flags & SDL_RENDERER_PRESENTVSYNC) {
+        SDL_SetBooleanProperty(props, "present_vsync", SDL_TRUE);
+    }
+    renderer = SDL_CreateRendererWithProperties(props);
+    SDL_DestroyProperties(props);
+    return renderer;
+}
+
 SDL_Renderer *SDL_CreateSoftwareRenderer(SDL_Surface *surface)
 {
 #if !defined(SDL_RENDER_DISABLED) && SDL_VIDEO_RENDER_SW
@@ -1116,9 +1136,13 @@ static SDL_ScaleMode SDL_GetScaleMode(void)
     }
 }
 
-SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, Uint32 format, int access, int w, int h)
+SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_PropertiesID props)
 {
     SDL_Texture *texture;
+    Uint32 format = (Uint32)SDL_GetNumberProperty(props, "format", SDL_PIXELFORMAT_UNKNOWN);
+    int access = (int)SDL_GetNumberProperty(props, "access", SDL_TEXTUREACCESS_STATIC);
+    int w = (int)SDL_GetNumberProperty(props, "width", 0);
+    int h = (int)SDL_GetNumberProperty(props, "height", 0);
     SDL_bool texture_is_fourcc_and_target;
 
     CHECK_RENDERER_MAGIC(renderer, NULL);
@@ -1177,7 +1201,7 @@ SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, Uint32 format, int access
     texture_is_fourcc_and_target = (access == SDL_TEXTUREACCESS_TARGET && SDL_ISPIXELFORMAT_FOURCC(texture->format));
 
     if (texture_is_fourcc_and_target == SDL_FALSE && IsSupportedFormat(renderer, format)) {
-        if (renderer->CreateTexture(renderer, texture) < 0) {
+        if (renderer->CreateTexture(renderer, texture, props) < 0) {
             SDL_DestroyTexture(texture);
             return NULL;
         }
@@ -1232,6 +1256,19 @@ SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, Uint32 format, int access
     return texture;
 }
 
+SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, Uint32 format, int access, int w, int h)
+{
+    SDL_Texture *texture;
+    SDL_PropertiesID props = SDL_CreateProperties();
+    SDL_SetNumberProperty(props, "format", format);
+    SDL_SetNumberProperty(props, "access", access);
+    SDL_SetNumberProperty(props, "width", w);
+    SDL_SetNumberProperty(props, "height", h);
+    texture = SDL_CreateTextureWithProperties(renderer, props);
+    SDL_DestroyProperties(props);
+    return texture;
+}
+
 SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *surface)
 {
     const SDL_PixelFormat *fmt;
diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h
index edba7e18e0c8..187073de1309 100644
--- a/src/render/SDL_sysrender.h
+++ b/src/render/SDL_sysrender.h
@@ -160,7 +160,7 @@ struct SDL_Renderer
     void (*WindowEvent)(SDL_Renderer *renderer, const SDL_WindowEvent *event);
     int (*GetOutputSize)(SDL_Renderer *renderer, int *w, int *h);
     SDL_bool (*SupportsBlendMode)(SDL_Renderer *renderer, SDL_BlendMode blendMode);
-    int (*CreateTexture)(SDL_Renderer *renderer, SDL_Texture *texture);
+    int (*CreateTexture)(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props);
     int (*QueueSetViewport)(SDL_Renderer *renderer, SDL_RenderCommand *cmd);
     int (*QueueSetDrawColor)(SDL_Renderer *renderer, SDL_RenderCommand *cmd);
     int (*QueueDrawPoints)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points,
@@ -280,7 +280,7 @@ struct SDL_Renderer
 /* Define the SDL render driver structure */
 struct SDL_RenderDriver
 {
-    SDL_Renderer *(*CreateRenderer)(SDL_Window *window, Uint32 flags);
+    SDL_Renderer *(*CreateRenderer)(SDL_Window *window, SDL_PropertiesID props);
 
     /* Info about the renderer capabilities */
     SDL_RendererInfo info;
diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c
index 7d6428932b3c..6a882ce0f101 100644
--- a/src/render/direct3d/SDL_render_d3d.c
+++ b/src/render/direct3d/SDL_render_d3d.c
@@ -517,7 +517,7 @@ static void D3D_DestroyTextureRep(D3D_TextureRep *texture)
     }
 }
 
-static int D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
+static int D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
     D3D_RenderData *data = (D3D_RenderData *)renderer->driverdata;
     D3D_TextureData *texturedata;
@@ -1493,7 +1493,7 @@ static int D3D_Reset(SDL_Renderer *renderer)
     /* Allocate application render targets */
     for (texture = renderer->textures; texture; texture = texture->next) {
         if (texture->access == SDL_TEXTUREACCESS_TARGET) {
-            D3D_CreateTexture(renderer, texture);
+            D3D_CreateTexture(renderer, texture, 0);
         }
     }
 
@@ -1536,7 +1536,7 @@ static int D3D_SetVSync(SDL_Renderer *renderer, const int vsync)
     return 0;
 }
 
-SDL_Renderer *D3D_CreateRenderer(SDL_Window *window, Uint32 flags)
+SDL_Renderer *D3D_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_props)
 {
     SDL_Renderer *renderer;
     D3D_RenderData *data;
@@ -1617,7 +1617,7 @@ SDL_Renderer *D3D_CreateRenderer(SDL_Window *window, Uint32 flags)
         pparams.BackBufferFormat = D3DFMT_UNKNOWN;
         pparams.FullScreen_RefreshRateInHz = 0;
     }
-    if (flags & SDL_RENDERER_PRESENTVSYNC) {
+    if (SDL_GetBooleanProperty(create_props, "present_vsync", SDL_FALSE)) {
         pparams.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
     } else {
         pparams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c
index 4561dfe300d5..5e4eba0f40d0 100644
--- a/src/render/direct3d11/SDL_render_d3d11.c
+++ b/src/render/direct3d11/SDL_render_d3d11.c
@@ -1062,7 +1062,19 @@ static SDL_bool D3D11_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode bl
     return SDL_TRUE;
 }
 
-static int D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
+static int GetTextureProperty(SDL_PropertiesID props, const char *name, ID3D11Texture2D **texture)
+{
+    IUnknown *unknown = SDL_GetProperty(props, name, NULL);
+    if (unknown) {
+        HRESULT result = IUnknown_QueryInterface(unknown, &SDL_IID_ID3D11Texture2D, (void **)texture);
+        if (FAILED(result)) {
+            return WIN_SetErrorFromHRESULT(name, result);
+        }
+    }
+    return 0;
+}
+
+static int D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
     D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->driverdata;
     D3D11_TextureData *textureData;
@@ -1109,13 +1121,18 @@ static int D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
         textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
     }
 
-    result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice,
-                                          &textureDesc,
-                                          NULL,
-                                          &textureData->mainTexture);
-    if (FAILED(result)) {
-        D3D11_DestroyTexture(renderer, texture);
-        return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D"), result);
+    if (GetTextureProperty(create_props, "d3d11.texture", &textureData->mainTexture) < 0) {
+        return -1;
+    }
+    if (!textureData->mainTexture) {
+        result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice,
+                                              &textureDesc,
+                                              NULL,
+                                              &textureData->mainTexture);
+        if (FAILED(result)) {
+            D3D11_DestroyTexture(renderer, texture);
+            return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D"), result);
+        }
     }
     SDL_SetProperty(SDL_GetTextureProperties(texture), "SDL.texture.d3d11.texture", textureData->mainTexture);
 #if SDL_HAVE_YUV
@@ -1126,23 +1143,33 @@ static int D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
         textureDesc.Width = (textureDesc.Width + 1) / 2;
         textureDesc.Height = (textureDesc.Height + 1) / 2;
 
-        result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice,
-                                              &textureDesc,
-                                              NULL,
-                                              &textureData->mainTextureU);
-        if (FAILED(result)) {
-            D3D11_DestroyTexture(renderer, texture);
-            return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D"), result);
+        if (GetTextureProperty(create_props, "d3d11.texture_u", &textureData->mainTextureU) < 0) {
+            return -1;
+        }
+        if (!textureData->mainTextureU) {
+            result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice,
+                                                  &textureDesc,
+                                                  NULL,
+                                                  &textureData->mainTextureU);
+            if (FAILED(result)) {
+                D3D11_DestroyTexture(renderer, texture);
+                return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D"), result);
+            }
         }
         SDL_SetProperty(SDL_GetTextureProperties(texture), "SDL.texture.d3d11.texture_u", textureData->mainTextureU);
 
-        result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice,
-                                              &textureDesc,
-                                              NULL,
-                                              &textureData->mainTextureV);
-        if (FAILED(result)) {
-            D3D11_DestroyTexture(renderer, texture);
-            return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D"), result);
+        if (GetTextureProperty(create_props, "d3d11.texture_v", &textureData->mainTextureV) < 0) {
+            return -1;
+        }
+        if (!textureData->mainTextureV) {
+            result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice,
+                                                  &textureDesc,
+                                                  NULL,
+                                                  &textureData->mainTextureV);
+            if (FAILED(result)) {
+                D3D11_DestroyTexture(renderer, texture);
+                return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D"), result);
+            }
         }
         SDL_SetProperty(SDL_GetTextureProperties(texture), "SDL.texture.d3d11.texture_v", textureData->mainTextureV);
     }
@@ -2384,7 +2411,7 @@ static int D3D11_SetVSync(SDL_Renderer *renderer, const int vsync)
 }
 #endif
 
-SDL_Renderer *D3D11_CreateRenderer(SDL_Window *window, Uint32 flags)
+SDL_Renderer *D3D11_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_props)
 {
     SDL_Renderer *renderer;
     D3D11_RenderData *data;
@@ -2445,7 +2472,7 @@ SDL_Renderer *D3D11_CreateRenderer(SDL_Window *window, Uint32 flags)
      */
     renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
 #else
-    if (flags & SDL_RENDERER_PRESENTVSYNC) {
+    if (SDL_GetBooleanProperty(create_props, "present_vsync", SDL_FALSE)) {
         renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC;
     }
     renderer->SetVSync = D3D11_SetVSync;
diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c
index 1442be58ee13..c241c3bc9cbc 100644
--- a/src/render/direct3d12/SDL_render_d3d12.c
+++ b/src/render/direct3d12/SDL_render_d3d12.c
@@ -1432,7 +1432,19 @@ static void D3D12_FreeSRVIndex(SDL_Renderer *renderer, SIZE_T index)
     rendererData->srvPoolHead = &rendererData->srvPoolNodes[index];
 }
 
-static int D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
+static int GetTextureProperty(SDL_PropertiesID props, const char *name, ID3D12Resource **texture)
+{
+    IUnknown *unknown = SDL_GetProperty(props, name, NULL);
+    if (unknown) {
+        HRESULT result = D3D_CALL(unknown, QueryInterface, D3D_GUID(SDL_IID_ID3D12Resource), (void **)texture);
+        if (FAILED(result)) {
+            return WIN_SetErrorFromHRESULT(name, result);
+        }
+    }
+    return 0;
+}
+
+static int D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
     D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->driverdata;
     D3D12_TextureData *textureData;
@@ -1476,28 +1488,10 @@ static int D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
     heapProps.CreationNodeMask = 1;
     heapProps.VisibleNodeMask = 1;
 
-    result = D3D_CALL(rendererData->d3dDevice, CreateCommittedResource,
-                      &heapProps,
-                      D3D12_HEAP_FLAG_NONE,
-                      &textureDesc,
-                      D3D12_RESOURCE_STATE_COPY_DEST,
-                      NULL,
-                      D3D_GUID(SDL_IID_ID3D12Resource),
-                      (void **)&textureData->mainTexture);
-    textureData->mainResourceState = D3D12_RESOURCE_STATE_COPY_DEST;
-    if (FAILED(result)) {
-        D3D12_DestroyTexture(renderer, texture);
-        return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateCommittedResource [texture]"), result);
+    if (GetTextureProperty(create_props, "d3d12.texture", &textureData->mainTexture) < 0) {
+        return -1;
     }
-    SDL_SetProperty(SDL_GetTextureProperties(texture), "SDL.texture.d3d12.texture", textureData->mainTexture);
-#if SDL_HAVE_YUV
-    if (texture->format == SDL_PIXELFORMAT_YV12 ||
-        texture->format == SDL_PIXELFORMAT_IYUV) {
-        textureData->yuv = SDL_TRUE;
-
-        textureDesc.Width = (textureDesc.Width + 1) / 2;
-        textureDesc.Height = (textureDesc.Height + 1) / 2;
-
+    if (!textureData->mainTexture) {
         result = D3D_CALL(rendererData->d3dDevice, CreateCommittedResource,
                           &heapProps,
                           D3D12_HEAP_FLAG_NONE,
@@ -1505,27 +1499,60 @@ static int D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
                           D3D12_RE

(Patch may be truncated, please check the link at the top of this post.)