From 631b05b211ea1df3fcc85152abb66c27caebf2ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Fri, 16 Feb 2024 00:19:13 +0100 Subject: [PATCH 001/220] Drop stray trailing whitespace for android-project --- android-project/app/build.gradle | 2 +- android-project/app/src/main/AndroidManifest.xml | 4 ++-- android-project/app/src/main/java/org/libsdl/app/SDL.java | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/android-project/app/build.gradle b/android-project/app/build.gradle index a158bbffa4c34..8dac6e00592a7 100644 --- a/android-project/app/build.gradle +++ b/android-project/app/build.gradle @@ -52,7 +52,7 @@ android { // path 'jni/CMakeLists.txt' // } } - + } lint { abortOnError false diff --git a/android-project/app/src/main/AndroidManifest.xml b/android-project/app/src/main/AndroidManifest.xml index dde60492b6475..8617dca9e8ed0 100644 --- a/android-project/app/src/main/AndroidManifest.xml +++ b/android-project/app/src/main/AndroidManifest.xml @@ -53,7 +53,7 @@ - + relinkClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker"); @@ -58,7 +58,7 @@ public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, Class contextClass = mContext.getClassLoader().loadClass("android.content.Context"); Class stringClass = mContext.getClassLoader().loadClass("java.lang.String"); - // Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if + // Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if // they've changed during updates. Method forceMethod = relinkClass.getDeclaredMethod("force"); Object relinkInstance = forceMethod.invoke(null); From cb3864949055aac24a464ebe561904328a088424 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 16 Feb 2024 17:36:11 -0800 Subject: [PATCH 002/220] Added SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT --- include/SDL3/SDL_video.h | 2 + src/video/SDL_sysvideo.h | 1 + src/video/SDL_video.c | 7 ++++ src/video/cocoa/SDL_cocoamodes.m | 1 + src/video/uikit/SDL_uikitmodes.m | 1 + src/video/windows/SDL_windowsmodes.c | 20 ++++----- test/testcolorspace.c | 63 ++++++++++++++++++++-------- 7 files changed, 64 insertions(+), 31 deletions(-) diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h index e3b995583170e..730f10fc43003 100644 --- a/include/SDL3/SDL_video.h +++ b/include/SDL3/SDL_video.h @@ -360,6 +360,7 @@ extern DECLSPEC SDL_DisplayID SDLCALL SDL_GetPrimaryDisplay(void); * - `SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT`: the luminance, in nits, that * SDR white is rendered on this display. If this value is not set or is * zero, the value 200 is a reasonable default when HDR is enabled. + * - `SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT`: the maximum luminance, in nits, of HDR content on this display. If this value is not set or is zero, the value 400 is a reasonable default when HDR is enabled. * * \param displayID the instance ID of the display to query * \returns a valid property ID on success or 0 on failure; call @@ -374,6 +375,7 @@ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetDisplayProperties(SDL_DisplayID #define SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN "SDL.display.HDR_enabled" #define SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT "SDL.display.SDR_white_level" +#define SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT "SDL.display.HDR_white_level" /** * Get the name of a display in UTF-8 encoding. diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 4eb861c3f37ff..ac112cbd31a99 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -122,6 +122,7 @@ typedef struct { SDL_bool enabled; float SDR_whitelevel; + float HDR_whitelevel; } SDL_HDRDisplayProperties; /* diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 8fe9c9a0bef59..3cfa955db8b1e 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -715,6 +715,9 @@ SDL_DisplayID SDL_AddVideoDisplay(const SDL_VideoDisplay *display, SDL_bool send if (display->HDR.SDR_whitelevel != 0.0f) { SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, display->HDR.SDR_whitelevel); } + if (display->HDR.HDR_whitelevel != 0.0f) { + SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT, display->HDR.HDR_whitelevel); + } return id; } @@ -992,6 +995,10 @@ void SDL_SetDisplayHDRProperties(SDL_VideoDisplay *display, const SDL_HDRDisplay SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, HDR->SDR_whitelevel); changed = SDL_TRUE; } + if (HDR->HDR_whitelevel != display->HDR.HDR_whitelevel) { + SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT, HDR->HDR_whitelevel); + changed = SDL_TRUE; + } SDL_copyp(&display->HDR, HDR); if (changed) { diff --git a/src/video/cocoa/SDL_cocoamodes.m b/src/video/cocoa/SDL_cocoamodes.m index fa6215e6a8136..66bb3a72e3f29 100644 --- a/src/video/cocoa/SDL_cocoamodes.m +++ b/src/video/cocoa/SDL_cocoamodes.m @@ -302,6 +302,7 @@ static void Cocoa_GetHDRProperties(CGDirectDisplayID displayID, SDL_HDRDisplayPr if (screen && screen.maximumPotentialExtendedDynamicRangeColorComponentValue > 1.0f) { HDR->enabled = SDL_TRUE; HDR->SDR_whitelevel = 80.0f; /* SDR content is always at scRGB 1.0 */ + HDR->HDR_whitelevel = HDR->SDR_whitelevel * screen.maximumExtendedDynamicRangeColorComponentValue; } } #endif diff --git a/src/video/uikit/SDL_uikitmodes.m b/src/video/uikit/SDL_uikitmodes.m index d9ca17f2c987e..e655c7c39346e 100644 --- a/src/video/uikit/SDL_uikitmodes.m +++ b/src/video/uikit/SDL_uikitmodes.m @@ -247,6 +247,7 @@ int UIKit_AddDisplay(UIScreen *uiscreen, SDL_bool send_event) if (uiscreen.potentialEDRHeadroom > 1.0f) { display.HDR.enabled = SDL_TRUE; display.HDR.SDR_whitelevel = 80.0f; /* SDR content is always at scRGB 1.0 */ + display.HDR.HDR_whitelevel = display.HDR.SDR_whitelevel * uiscreen.currentEDRHeadroom; } } #endif /* !SDL_PLATFORM_TVOS */ diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index 2ae5677664c68..c417420e9c180 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -339,7 +339,7 @@ static char *WIN_GetDisplayNameVista(const WCHAR *deviceName) static SDL_bool WIN_GetMonitorDESC1(HMONITOR hMonitor, DXGI_OUTPUT_DESC1 *desc) { - typedef HRESULT(WINAPI * PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory); + typedef HRESULT (WINAPI * PFN_CREATE_DXGI_FACTORY)(REFIID riid, void **ppFactory); PFN_CREATE_DXGI_FACTORY CreateDXGIFactoryFunc = NULL; void *hDXGIMod = NULL; SDL_bool found = SDL_FALSE; @@ -349,18 +349,18 @@ static SDL_bool WIN_GetMonitorDESC1(HMONITOR hMonitor, DXGI_OUTPUT_DESC1 *desc) #else hDXGIMod = SDL_LoadObject("dxgi.dll"); if (hDXGIMod) { - CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY)SDL_LoadFunction(hDXGIMod, "CreateDXGIFactory"); + CreateDXGIFactoryFunc = (PFN_CREATE_DXGI_FACTORY)SDL_LoadFunction(hDXGIMod, "CreateDXGIFactory1"); } #endif if (CreateDXGIFactoryFunc) { - static const GUID SDL_IID_IDXGIFactory2 = { 0x50c83a1c, 0xe072, 0x4c48, { 0x87, 0xb0, 0x36, 0x30, 0xfa, 0x36, 0xa6, 0xd0 } }; + static const GUID SDL_IID_IDXGIFactory1 = { 0x770aae78, 0xf26f, 0x4dba, { 0xa8, 0x29, 0x25, 0x3c, 0x83, 0xd1, 0xb3, 0x87 } }; static const GUID SDL_IID_IDXGIOutput6 = { 0x068346e8, 0xaaec, 0x4b84, { 0xad, 0xd7, 0x13, 0x7f, 0x51, 0x3f, 0x77, 0xa1 } }; - IDXGIFactory2 *dxgiFactory; + IDXGIFactory1 *dxgiFactory; - if (SUCCEEDED(CreateDXGIFactoryFunc(&SDL_IID_IDXGIFactory2, (void **)&dxgiFactory))) { + if (SUCCEEDED(CreateDXGIFactoryFunc(&SDL_IID_IDXGIFactory1, (void **)&dxgiFactory))) { IDXGIAdapter1 *dxgiAdapter; UINT adapter = 0; - while (!found && SUCCEEDED(IDXGIFactory2_EnumAdapters1(dxgiFactory, adapter, &dxgiAdapter))) { + while (!found && SUCCEEDED(IDXGIFactory1_EnumAdapters1(dxgiFactory, adapter, &dxgiAdapter))) { IDXGIOutput *dxgiOutput; UINT output = 0; while (!found && SUCCEEDED(IDXGIAdapter1_EnumOutputs(dxgiAdapter, output, &dxgiOutput))) { @@ -482,13 +482,7 @@ static void WIN_GetHDRProperties(SDL_VideoDevice *_this, HMONITOR hMonitor, SDL_ if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) { HDR->enabled = SDL_TRUE; HDR->SDR_whitelevel = WIN_GetSDRWhiteLevel(hMonitor); - - /* In theory you can get the maximum luminence from desc.MaxLuminance, but this value is 80 - * on my system regardless of whether HDR is enabled. Because the value isn't reliable games - * will typically have a calibration step where they show you a white image at high luminence - * and slowly lower the brightness until you can see it as distinct from the background and - * then use that as the calibrated maximum luminence. The value 400 is a reasonable default. - */ + HDR->HDR_whitelevel = desc.MaxLuminance; } } } diff --git a/test/testcolorspace.c b/test/testcolorspace.c index f3b6b3a9b1270..3b479ae09d977 100644 --- a/test/testcolorspace.c +++ b/test/testcolorspace.c @@ -50,8 +50,8 @@ static SDL_bool HDR_enabled = SDL_FALSE; static float SDR_white_level = SDR_DISPLAY_WHITE_LEVEL; static float SDR_color_scale = 1.0f; static SDL_FRect SDR_calibration_rect; -static float HDR_white_level = DEFAULT_HDR_WHITE_LEVEL; -static float HDR_color_scale = DEFAULT_HDR_WHITE_LEVEL / SDR_DISPLAY_WHITE_LEVEL; +static float HDR_white_level = SDR_DISPLAY_WHITE_LEVEL; +static float HDR_color_scale = 1.0f; static SDL_FRect HDR_calibration_rect; enum @@ -77,7 +77,10 @@ static float GetDisplaySDRWhiteLevel(void) { SDL_PropertiesID props; - HDR_enabled = SDL_FALSE; + if (!HDR_enabled) { + /* HDR is not enabled, use the SDR white level */ + return SDR_DISPLAY_WHITE_LEVEL; + } props = SDL_GetRendererProperties(renderer); if (SDL_GetNumberProperty(props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB) != SDL_COLORSPACE_SCRGB) { @@ -86,13 +89,6 @@ static float GetDisplaySDRWhiteLevel(void) } props = SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window)); - if (!SDL_GetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE)) { - /* HDR is not enabled on the display */ - return SDR_DISPLAY_WHITE_LEVEL; - } - - HDR_enabled = SDL_TRUE; - return SDL_GetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, DEFAULT_SDR_WHITE_LEVEL); } @@ -108,9 +104,23 @@ static void SetSDRWhiteLevel(float value) SDL_SetRenderColorScale(renderer, SDR_color_scale); } -static void UpdateSDRWhiteLevel(void) +static float GetDisplayHDRWhiteLevel(void) { - SetSDRWhiteLevel(GetDisplaySDRWhiteLevel()); + SDL_PropertiesID props; + + if (!HDR_enabled) { + /* HDR is not enabled, use the SDR white level */ + return SDR_DISPLAY_WHITE_LEVEL; + } + + props = SDL_GetRendererProperties(renderer); + if (SDL_GetNumberProperty(props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB) != SDL_COLORSPACE_SCRGB) { + /* We're not displaying in HDR, use the SDR white level */ + return SDR_DISPLAY_WHITE_LEVEL; + } + + props = SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window)); + return SDL_GetFloatProperty(props, SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT, DEFAULT_HDR_WHITE_LEVEL); } static void SetHDRWhiteLevel(float value) @@ -124,6 +134,26 @@ static void SetHDRWhiteLevel(float value) HDR_color_scale = HDR_white_level / SDR_DISPLAY_WHITE_LEVEL; } +static void UpdateHDRState(void) +{ + SDL_PropertiesID props; + + props = SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window)); + HDR_enabled = SDL_GetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE); + + SetHDRWhiteLevel(GetDisplayHDRWhiteLevel()); + SetSDRWhiteLevel(GetDisplaySDRWhiteLevel()); + + SDL_Log("HDR %s\n", HDR_enabled ? "enabled" : "disabled"); + + if (HDR_enabled) { + props = SDL_GetRendererProperties(renderer); + if (SDL_GetNumberProperty(props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB) != SDL_COLORSPACE_SCRGB) { + SDL_Log("Run with --colorspace scRGB to display HDR colors\n"); + } + } +} + static void CreateRenderer(void) { SDL_PropertiesID props; @@ -144,9 +174,7 @@ static void CreateRenderer(void) SDL_Log("Created renderer %s\n", info.name); renderer_name = info.name; - UpdateSDRWhiteLevel(); - - SDL_Log("HDR is %s\n", HDR_enabled ? "enabled" : "disabled"); + UpdateHDRState(); } static void NextRenderer( void ) @@ -661,7 +689,7 @@ static void RenderHDRCalibration(void) SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_SetRenderColorScale(renderer, MAXIMUM_HDR_WHITE_LEVEL / SDR_DISPLAY_WHITE_LEVEL); SDL_RenderFillRect(renderer, &HDR_calibration_rect); - SDL_SetRenderColorScale(renderer, HDR_color_scale); + SDL_SetRenderColorScale(renderer, HDR_color_scale * 0.90f); rect = HDR_calibration_rect; rect.h -= 4.0f; rect.w = 60.0f; @@ -747,8 +775,7 @@ static void loop(void) OnMouseHeld(event.button.x, event.button.y); } } else if (event.type == SDL_EVENT_DISPLAY_HDR_STATE_CHANGED) { - SDL_Log("HDR %s\n", event.display.data1 ? "enabled" : "disabled"); - UpdateSDRWhiteLevel(); + UpdateHDRState(); } else if (event.type == SDL_EVENT_QUIT) { done = 1; } From 317099b01fbca70a196650d46bed2cad78d8de69 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Sat, 17 Feb 2024 03:51:21 +0000 Subject: [PATCH 003/220] Sync SDL3 wiki -> header --- include/SDL3/SDL_video.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h index 730f10fc43003..6fbae05533f2a 100644 --- a/include/SDL3/SDL_video.h +++ b/include/SDL3/SDL_video.h @@ -360,7 +360,9 @@ extern DECLSPEC SDL_DisplayID SDLCALL SDL_GetPrimaryDisplay(void); * - `SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT`: the luminance, in nits, that * SDR white is rendered on this display. If this value is not set or is * zero, the value 200 is a reasonable default when HDR is enabled. - * - `SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT`: the maximum luminance, in nits, of HDR content on this display. If this value is not set or is zero, the value 400 is a reasonable default when HDR is enabled. + * - `SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT`: the maximum luminance, in nits, + * of HDR content on this display. If this value is not set or is zero, the + * value 400 is a reasonable default when HDR is enabled. * * \param displayID the instance ID of the display to query * \returns a valid property ID on success or 0 on failure; call From 202886f87392fc3e964aae5a3ec99eed1d99acf7 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 16 Feb 2024 20:05:11 -0800 Subject: [PATCH 004/220] Make use of the HDR light range when playing HDR video --- test/testffmpeg.c | 69 ++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/test/testffmpeg.c b/test/testffmpeg.c index d1b8e0901a81a..b5babd53862a4 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -99,28 +99,51 @@ static int done; /* This function isn't platform specific, but we haven't hooked up HDR video support on other platforms yet */ #if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_APPLE) -static void GetDisplayHDRProperties(SDL_bool *HDR_display, float *SDR_white_level) +static void GetDisplayHDRProperties(SDL_bool *HDR_display, float *SDR_white_level, float *HDR_white_level) { SDL_PropertiesID props; + *HDR_display = SDL_FALSE; + *SDR_white_level = SDR_DISPLAY_WHITE_LEVEL; + *HDR_white_level = SDR_DISPLAY_WHITE_LEVEL; + props = SDL_GetRendererProperties(renderer); if (SDL_GetNumberProperty(props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB) != SDL_COLORSPACE_SCRGB) { /* We're not displaying in HDR, use the SDR white level */ - *HDR_display = SDL_FALSE; - *SDR_white_level = SDR_DISPLAY_WHITE_LEVEL; return; } props = SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window)); if (!SDL_GetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE)) { /* HDR is not enabled on the display */ - *HDR_display = SDL_FALSE; - *SDR_white_level = SDR_DISPLAY_WHITE_LEVEL; return; } *HDR_display = SDL_TRUE; *SDR_white_level = SDL_GetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, DEFAULT_SDR_WHITE_LEVEL); + *HDR_white_level = SDL_GetFloatProperty(props, SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT, DEFAULT_HDR_WHITE_LEVEL); +} + +static void UpdateVideoColorScale(SDL_bool HDR_video) +{ + SDL_bool HDR_display = SDL_FALSE; + float SDR_white_level, HDR_white_level, video_white_level; + + GetDisplayHDRProperties(&HDR_display, &SDR_white_level, &HDR_white_level); + + if (HDR_video) { + video_white_level = DEFAULT_HDR_WHITE_LEVEL; + } else { + video_white_level = SDR_DISPLAY_WHITE_LEVEL; + } + + if (HDR_display && HDR_video) { + /* Scale the HDR range of the video to the HDR range of the display */ + SDL_SetRenderColorScale(renderer, HDR_white_level / video_white_level); + } else { + /* Scale the range of the video to the SDR range of the display */ + SDL_SetRenderColorScale(renderer, SDR_white_level / video_white_level); + } } #endif /* SDL_PLATFORM_WIN32 || SDL_PLATFORM_APPLE */ @@ -675,10 +698,8 @@ static SDL_bool GetTextureForD3D11Frame(AVFrame *frame, SDL_Texture **texture) SDL_QueryTexture(*texture, NULL, NULL, &texture_width, &texture_height); } if (!*texture || (UINT)texture_width != desc.Width || (UINT)texture_height != desc.Height) { - float SDR_white_level, video_white_level; - SDL_bool HDR_display = SDL_FALSE; - SDL_bool HDR_video = SDL_FALSE; Uint32 format; + SDL_bool HDR_video = SDL_FALSE; switch (desc.Format) { case DXGI_FORMAT_NV12: @@ -701,8 +722,6 @@ static SDL_bool GetTextureForD3D11Frame(AVFrame *frame, SDL_Texture **texture) SDL_DestroyTexture(*texture); } - GetDisplayHDRProperties(&HDR_display, &SDR_white_level); - SDL_PropertiesID props = SDL_CreateProperties(); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, GetFrameColorspace(frame)); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); @@ -715,16 +734,7 @@ static SDL_bool GetTextureForD3D11Frame(AVFrame *frame, SDL_Texture **texture) return SDL_FALSE; } - if (HDR_video != HDR_display) { - if (HDR_display) { - video_white_level = SDR_DISPLAY_WHITE_LEVEL; - } else { - video_white_level = DEFAULT_HDR_WHITE_LEVEL; - } - SDL_SetRenderColorScale(renderer, SDR_white_level / video_white_level); - } else { - SDL_SetRenderColorScale(renderer, 1.0f); - } + UpdateVideoColorScale(HDR_video); } ID3D11Resource *dx11_resource = SDL_GetProperty(SDL_GetTextureProperties(*texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_POINTER, NULL); @@ -749,8 +759,6 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex size_t nPixelBufferHeight = CVPixelBufferGetHeightOfPlane(pPixelBuffer, 0); SDL_PropertiesID props; Uint32 format; - float SDR_white_level, video_white_level; - SDL_bool HDR_display = SDL_FALSE; SDL_bool HDR_video = SDL_FALSE; switch (nPixelBufferType) { @@ -778,8 +786,6 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex SDL_DestroyTexture(*texture); } - GetDisplayHDRProperties(&HDR_display, &SDR_white_level); - props = SDL_CreateProperties(); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, GetFrameColorspace(frame)); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); @@ -793,19 +799,8 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex return SDL_FALSE; } - if (HDR_video != HDR_display) { - if (HDR_display) { - video_white_level = SDR_DISPLAY_WHITE_LEVEL; - } else { - video_white_level = DEFAULT_HDR_WHITE_LEVEL; - } - SDL_SetRenderColorScale(renderer, SDR_white_level / video_white_level); - } else if (HDR_display) { - /* Apple platforms already scale up the brightness of content so we need to scale it down by the same amount for HDR video */ - SDL_SetRenderColorScale(renderer, SDR_white_level / DEFAULT_SDR_WHITE_LEVEL); - } else { - SDL_SetRenderColorScale(renderer, 1.0f); - } + UpdateVideoColorScale(HDR_video); + return SDL_TRUE; #else return SDL_FALSE; From 8ce786d2b6c372dfca5374de20d15aae41b7ceb8 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 07:59:04 -0800 Subject: [PATCH 005/220] Property query functions don't set an error if they return the default value You can call SDL_HasProperty() if you want to check to see if a property exists. Fixes https://github.com/libsdl-org/SDL/issues/9067 --- include/SDL3/SDL_properties.h | 24 ++++++++++++++++-- src/SDL_properties.c | 41 ++++--------------------------- src/dynapi/SDL_dynapi.sym | 1 + src/dynapi/SDL_dynapi_overrides.h | 1 + src/dynapi/SDL_dynapi_procs.h | 1 + 5 files changed, 30 insertions(+), 38 deletions(-) diff --git a/include/SDL3/SDL_properties.h b/include/SDL3/SDL_properties.h index 80a01cc123e01..8061972373c39 100644 --- a/include/SDL3/SDL_properties.h +++ b/include/SDL3/SDL_properties.h @@ -248,6 +248,21 @@ extern DECLSPEC int SDLCALL SDL_SetFloatProperty(SDL_PropertiesID props, const c */ extern DECLSPEC int SDLCALL SDL_SetBooleanProperty(SDL_PropertiesID props, const char *name, SDL_bool value); +/** + * Return whether a property exists in a set of properties. + * + * \param props the properties to query + * \param name the name of the property to query + * \returns SDL_TRUE if the property exists, or SDL_FALSE if it doesn't. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_GetPropertyType + */ +extern DECLSPEC SDL_bool SDLCALL SDL_HasProperty(SDL_PropertiesID props, const char *name); + /** * Get the type of a property on a set of properties * @@ -259,6 +274,8 @@ extern DECLSPEC int SDLCALL SDL_SetBooleanProperty(SDL_PropertiesID props, const * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL 3.0.0. + * + * \sa SDL_HasProperty */ extern DECLSPEC SDL_PropertyType SDLCALL SDL_GetPropertyType(SDL_PropertiesID props, const char *name); @@ -285,6 +302,7 @@ extern DECLSPEC SDL_PropertyType SDLCALL SDL_GetPropertyType(SDL_PropertiesID pr * \since This function is available since SDL 3.0.0. * * \sa SDL_GetPropertyType + * \sa SDL_HasProperty * \sa SDL_SetProperty */ extern DECLSPEC void *SDLCALL SDL_GetProperty(SDL_PropertiesID props, const char *name, void *default_value); @@ -303,6 +321,7 @@ extern DECLSPEC void *SDLCALL SDL_GetProperty(SDL_PropertiesID props, const char * \since This function is available since SDL 3.0.0. * * \sa SDL_GetPropertyType + * \sa SDL_HasProperty * \sa SDL_SetStringProperty */ extern DECLSPEC const char *SDLCALL SDL_GetStringProperty(SDL_PropertiesID props, const char *name, const char *default_value); @@ -324,6 +343,7 @@ extern DECLSPEC const char *SDLCALL SDL_GetStringProperty(SDL_PropertiesID props * \since This function is available since SDL 3.0.0. * * \sa SDL_GetPropertyType + * \sa SDL_HasProperty * \sa SDL_SetNumberProperty */ extern DECLSPEC Sint64 SDLCALL SDL_GetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 default_value); @@ -345,6 +365,7 @@ extern DECLSPEC Sint64 SDLCALL SDL_GetNumberProperty(SDL_PropertiesID props, con * \since This function is available since SDL 3.0.0. * * \sa SDL_GetPropertyType + * \sa SDL_HasProperty * \sa SDL_SetFloatProperty */ extern DECLSPEC float SDLCALL SDL_GetFloatProperty(SDL_PropertiesID props, const char *name, float default_value); @@ -366,6 +387,7 @@ extern DECLSPEC float SDLCALL SDL_GetFloatProperty(SDL_PropertiesID props, const * \since This function is available since SDL 3.0.0. * * \sa SDL_GetPropertyType + * \sa SDL_HasProperty * \sa SDL_SetBooleanProperty */ extern DECLSPEC SDL_bool SDLCALL SDL_GetBooleanProperty(SDL_PropertiesID props, const char *name, SDL_bool default_value); @@ -381,8 +403,6 @@ extern DECLSPEC SDL_bool SDLCALL SDL_GetBooleanProperty(SDL_PropertiesID props, * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL 3.0.0. - * - * \sa SDL_GetProperty */ extern DECLSPEC int SDLCALL SDL_ClearProperty(SDL_PropertiesID props, const char *name); diff --git a/src/SDL_properties.c b/src/SDL_properties.c index e25d6f2d8dffc..8ee6b00ed6a28 100644 --- a/src/SDL_properties.c +++ b/src/SDL_properties.c @@ -451,17 +451,20 @@ int SDL_SetBooleanProperty(SDL_PropertiesID props, const char *name, SDL_bool va return SDL_PrivateSetProperty(props, name, property); } +SDL_bool SDL_HasProperty(SDL_PropertiesID props, const char *name) +{ + return (SDL_GetPropertyType(props, name) != SDL_PROPERTY_TYPE_INVALID); +} + SDL_PropertyType SDL_GetPropertyType(SDL_PropertiesID props, const char *name) { SDL_Properties *properties = NULL; SDL_PropertyType type = SDL_PROPERTY_TYPE_INVALID; if (!props) { - SDL_InvalidParamError("props"); return SDL_PROPERTY_TYPE_INVALID; } if (!name || !*name) { - SDL_InvalidParamError("name"); return SDL_PROPERTY_TYPE_INVALID; } @@ -470,7 +473,6 @@ SDL_PropertyType SDL_GetPropertyType(SDL_PropertiesID props, const char *name) SDL_UnlockMutex(SDL_properties_lock); if (!properties) { - SDL_InvalidParamError("props"); return SDL_PROPERTY_TYPE_INVALID; } @@ -479,8 +481,6 @@ SDL_PropertyType SDL_GetPropertyType(SDL_PropertiesID props, const char *name) SDL_Property *property = NULL; if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) { type = property->type; - } else { - SDL_SetError("Couldn't find property named %s", name); } } SDL_UnlockMutex(properties->lock); @@ -494,11 +494,9 @@ void *SDL_GetProperty(SDL_PropertiesID props, const char *name, void *default_va void *value = default_value; if (!props) { - SDL_InvalidParamError("props"); return value; } if (!name || !*name) { - SDL_InvalidParamError("name"); return value; } @@ -507,7 +505,6 @@ void *SDL_GetProperty(SDL_PropertiesID props, const char *name, void *default_va SDL_UnlockMutex(SDL_properties_lock); if (!properties) { - SDL_InvalidParamError("props"); return value; } @@ -521,11 +518,7 @@ void *SDL_GetProperty(SDL_PropertiesID props, const char *name, void *default_va if (SDL_FindInHashTable(properties->props, name, (const void **)&property)) { if (property->type == SDL_PROPERTY_TYPE_POINTER) { value = property->value.pointer_value; - } else { - SDL_SetError("Property %s isn't a pointer value", name); } - } else { - SDL_SetError("Couldn't find property named %s", name); } } SDL_UnlockMutex(properties->lock); @@ -539,11 +532,9 @@ const char *SDL_GetStringProperty(SDL_PropertiesID props, const char *name, cons const char *value = default_value; if (!props) { - SDL_InvalidParamError("props"); return value; } if (!name || !*name) { - SDL_InvalidParamError("name"); return value; } @@ -552,7 +543,6 @@ const char *SDL_GetStringProperty(SDL_PropertiesID props, const char *name, cons SDL_UnlockMutex(SDL_properties_lock); if (!properties) { - SDL_InvalidParamError("props"); return value; } @@ -594,11 +584,8 @@ const char *SDL_GetStringProperty(SDL_PropertiesID props, const char *name, cons value = property->value.boolean_value ? "true" : "false"; break; default: - SDL_SetError("Property %s isn't a string value", name); break; } - } else { - SDL_SetError("Couldn't find property named %s", name); } } SDL_UnlockMutex(properties->lock); @@ -612,11 +599,9 @@ Sint64 SDL_GetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 de Sint64 value = default_value; if (!props) { - SDL_InvalidParamError("props"); return value; } if (!name || !*name) { - SDL_InvalidParamError("name"); return value; } @@ -625,7 +610,6 @@ Sint64 SDL_GetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 de SDL_UnlockMutex(SDL_properties_lock); if (!properties) { - SDL_InvalidParamError("props"); return value; } @@ -647,11 +631,8 @@ Sint64 SDL_GetNumberProperty(SDL_PropertiesID props, const char *name, Sint64 de value = property->value.boolean_value; break; default: - SDL_SetError("Property %s isn't a number value", name); break; } - } else { - SDL_SetError("Couldn't find property named %s", name); } } SDL_UnlockMutex(properties->lock); @@ -665,11 +646,9 @@ float SDL_GetFloatProperty(SDL_PropertiesID props, const char *name, float defau float value = default_value; if (!props) { - SDL_InvalidParamError("props"); return value; } if (!name || !*name) { - SDL_InvalidParamError("name"); return value; } @@ -678,7 +657,6 @@ float SDL_GetFloatProperty(SDL_PropertiesID props, const char *name, float defau SDL_UnlockMutex(SDL_properties_lock); if (!properties) { - SDL_InvalidParamError("props"); return value; } @@ -700,11 +678,8 @@ float SDL_GetFloatProperty(SDL_PropertiesID props, const char *name, float defau value = (float)property->value.boolean_value; break; default: - SDL_SetError("Property %s isn't a float value", name); break; } - } else { - SDL_SetError("Couldn't find property named %s", name); } } SDL_UnlockMutex(properties->lock); @@ -718,11 +693,9 @@ SDL_bool SDL_GetBooleanProperty(SDL_PropertiesID props, const char *name, SDL_bo SDL_bool value = default_value; if (!props) { - SDL_InvalidParamError("props"); return value; } if (!name || !*name) { - SDL_InvalidParamError("name"); return value; } @@ -731,7 +704,6 @@ SDL_bool SDL_GetBooleanProperty(SDL_PropertiesID props, const char *name, SDL_bo SDL_UnlockMutex(SDL_properties_lock); if (!properties) { - SDL_InvalidParamError("props"); return value; } @@ -753,11 +725,8 @@ SDL_bool SDL_GetBooleanProperty(SDL_PropertiesID props, const char *name, SDL_bo value = property->value.boolean_value; break; default: - SDL_SetError("Property %s isn't a boolean value", name); break; } - } else { - SDL_SetError("Couldn't find property named %s", name); } } SDL_UnlockMutex(properties->lock); diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 2ba1f7be6a874..f545e9b3c909d 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -972,6 +972,7 @@ SDL3_0.0.0 { SDL_RenderGeometryRawFloat; SDL_SetWindowShape; SDL_RenderViewportSet; + SDL_HasProperty; # 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 b297a3026ecfc..b288bdcfa36e4 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -997,3 +997,4 @@ #define SDL_RenderGeometryRawFloat SDL_RenderGeometryRawFloat_REAL #define SDL_SetWindowShape SDL_SetWindowShape_REAL #define SDL_RenderViewportSet SDL_RenderViewportSet_REAL +#define SDL_HasProperty SDL_HasProperty_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 3969fda9c95ff..7ce6a43d8cb82 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1022,3 +1022,4 @@ SDL_DYNAPI_PROC(int,SDL_GetRenderColorScale,(SDL_Renderer *a, float *b),(a,b),re SDL_DYNAPI_PROC(int,SDL_RenderGeometryRawFloat,(SDL_Renderer *a, SDL_Texture *b, const float *c, int d, const SDL_FColor *e, int f, const float *g, int h, int i, const void *j, int k, int l),(a,b,c,d,e,f,g,h,i,j,k,l),return) SDL_DYNAPI_PROC(int,SDL_SetWindowShape,(SDL_Window *a, SDL_Surface *b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_RenderViewportSet,(SDL_Renderer *a),(a),return) +SDL_DYNAPI_PROC(SDL_bool,SDL_HasProperty,(SDL_PropertiesID a, const char *b),(a,b),return) From ff01d0b56853958c4e3d9cd0bef32407ec550947 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 08:12:10 -0800 Subject: [PATCH 006/220] Fixed building without HAVE_LIBC on Windows Fixes https://github.com/libsdl-org/SDL/issues/9064 --- src/stdlib/SDL_memcpy.c | 4 ++-- src/stdlib/SDL_memmove.c | 18 ++++++++++++++++++ src/stdlib/SDL_memset.c | 4 ++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/stdlib/SDL_memcpy.c b/src/stdlib/SDL_memcpy.c index 139eb420d4f01..d8cc81bdcfa37 100644 --- a/src/stdlib/SDL_memcpy.c +++ b/src/stdlib/SDL_memcpy.c @@ -81,7 +81,7 @@ void *SDL_memcpy(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void /* The optimizer on Visual Studio 2005 and later generates memcpy() and memset() calls. We will provide our own implementation if we're not building with a C runtime. */ -#if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(_MT) +#ifndef HAVE_LIBC /* NOLINTNEXTLINE(readability-redundant-declaration) */ extern void *memcpy(void *dst, const void *src, size_t len); #ifndef __INTEL_LLVM_COMPILER @@ -96,4 +96,4 @@ void *memcpy(void *dst, const void *src, size_t len) { return SDL_memcpy(dst, src, len); } -#endif /* (_MSC_VER >= 1400) && !defined(_MT) */ +#endif /* !HAVE_LIBC */ diff --git a/src/stdlib/SDL_memmove.c b/src/stdlib/SDL_memmove.c index 1daed2ffb4a2b..6758691058681 100644 --- a/src/stdlib/SDL_memmove.c +++ b/src/stdlib/SDL_memmove.c @@ -53,3 +53,21 @@ void *SDL_memmove(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void #endif /* HAVE_MEMMOVE */ } + +#ifndef HAVE_LIBC +/* NOLINTNEXTLINE(readability-redundant-declaration) */ +extern void *memmove(void *dst, const void *src, size_t len); +#ifndef __INTEL_LLVM_COMPILER +#pragma intrinsic(memmove) +#endif + +#ifndef __clang__ +#pragma function(memmove) +#endif +/* NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) */ +void *memmove(void *dst, const void *src, size_t len) +{ + return SDL_memmove(dst, src, len); +} +#endif /* !HAVE_LIBC */ + diff --git a/src/stdlib/SDL_memset.c b/src/stdlib/SDL_memset.c index 0c3579f86e2e7..61ad5e8347df8 100644 --- a/src/stdlib/SDL_memset.c +++ b/src/stdlib/SDL_memset.c @@ -118,7 +118,7 @@ void *SDL_memset4(void *dst, Uint32 val, size_t dwords) /* The optimizer on Visual Studio 2005 and later generates memcpy() and memset() calls. We will provide our own implementation if we're not building with a C runtime. */ -#if defined(_MSC_VER) && (_MSC_VER >= 1400) && !defined(_MT) +#ifndef HAVE_LIBC /* NOLINTNEXTLINE(readability-redundant-declaration) */ extern void *memset(void *dst, int c, size_t len); #ifndef __INTEL_LLVM_COMPILER @@ -133,5 +133,5 @@ void *memset(void *dst, int c, size_t len) { return SDL_memset(dst, c, len); } -#endif /* (_MSC_VER >= 1400) && !defined(_MT) */ +#endif /* !HAVE_LIBC */ From dc7baa415eb9e30287c8b663df9d7e4aa393aafe Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 09:21:30 -0800 Subject: [PATCH 007/220] Show the window after creating the renderer This hides any window recreation that might need to be done by the OpenGL renderers --- src/test/SDL_test_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index 9a3b872335047..4b2502e2b246d 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -1497,8 +1497,6 @@ SDL_bool SDLTest_CommonInit(SDLTest_CommonState *state) } } - SDL_ShowWindow(state->windows[i]); - if (!SDL_RectEmpty(&state->confine)) { SDL_SetWindowMouseRect(state->windows[i], &state->confine); } @@ -1530,6 +1528,8 @@ SDL_bool SDLTest_CommonInit(SDLTest_CommonState *state) SDLTest_PrintRenderer(&info); } } + + SDL_ShowWindow(state->windows[i]); } } From ed615e92b797220e24894e5f95f1f60c8fa8c587 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 09:30:50 -0800 Subject: [PATCH 008/220] Updated automated test now that getting an invalid property isn't an error --- test/testautomation_video.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/testautomation_video.c b/test/testautomation_video.c index 1d652b2716dab..c8e764fc418b5 100644 --- a/test/testautomation_video.c +++ b/test/testautomation_video.c @@ -1682,13 +1682,11 @@ static int video_getSetWindowData(void *arg) result = (char *)SDL_GetProperty(SDL_GetWindowProperties(window), NULL, NULL); SDLTest_AssertPass("Call to SDL_GetWindowData(name=NULL)"); SDLTest_AssertCheck(result == NULL, "Validate that result is NULL"); - checkInvalidParameterError(); /* Get data with empty name */ result = (char *)SDL_GetProperty(SDL_GetWindowProperties(window), "", NULL); SDLTest_AssertPass("Call to SDL_GetWindowData(name='')"); SDLTest_AssertCheck(result == NULL, "Validate that result is NULL"); - checkInvalidParameterError(); /* Clean up */ destroyVideoSuiteTestWindow(window); From 7ed1f3554d52f12797bff1860c5df52e11a10982 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 11:31:06 -0800 Subject: [PATCH 009/220] Define HAVE_LIBC for the platforms with a C library Allow the Visual Studio project to define HAVE_LIBC=0 to enable building without a C runtime on Windows entirely through Visual Studio project changes. --- include/build_config/SDL_build_config_android.h | 1 + include/build_config/SDL_build_config_emscripten.h | 1 + include/build_config/SDL_build_config_ios.h | 1 + include/build_config/SDL_build_config_macos.h | 1 + include/build_config/SDL_build_config_windows.h | 7 +++++-- include/build_config/SDL_build_config_wingdk.h | 8 +------- include/build_config/SDL_build_config_winrt.h | 1 + include/build_config/SDL_build_config_xbox.h | 8 +------- 8 files changed, 12 insertions(+), 16 deletions(-) diff --git a/include/build_config/SDL_build_config_android.h b/include/build_config/SDL_build_config_android.h index ad75cc1fbabc4..4e688d0d3f41e 100644 --- a/include/build_config/SDL_build_config_android.h +++ b/include/build_config/SDL_build_config_android.h @@ -52,6 +52,7 @@ #define HAVE_WCHAR_H 1 /* C library functions */ +#define HAVE_LIBC 1 #define HAVE_DLOPEN 1 #define HAVE_MALLOC 1 #define HAVE_CALLOC 1 diff --git a/include/build_config/SDL_build_config_emscripten.h b/include/build_config/SDL_build_config_emscripten.h index 7fb5294ded8d9..40876c3952fbb 100644 --- a/include/build_config/SDL_build_config_emscripten.h +++ b/include/build_config/SDL_build_config_emscripten.h @@ -55,6 +55,7 @@ #define HAVE_WCHAR_H 1 /* C library functions */ +#define HAVE_LIBC 1 #define HAVE_DLOPEN 1 #define HAVE_MALLOC 1 #define HAVE_CALLOC 1 diff --git a/include/build_config/SDL_build_config_ios.h b/include/build_config/SDL_build_config_ios.h index e79ca4ce20585..48da8852997c3 100644 --- a/include/build_config/SDL_build_config_ios.h +++ b/include/build_config/SDL_build_config_ios.h @@ -44,6 +44,7 @@ #define HAVE_WCHAR_H 1 /* C library functions */ +#define HAVE_LIBC 1 #define HAVE_DLOPEN 1 #define HAVE_MALLOC 1 #define HAVE_CALLOC 1 diff --git a/include/build_config/SDL_build_config_macos.h b/include/build_config/SDL_build_config_macos.h index 66bdb5ea42808..9eb5830cdd8b4 100644 --- a/include/build_config/SDL_build_config_macos.h +++ b/include/build_config/SDL_build_config_macos.h @@ -49,6 +49,7 @@ #define HAVE_WCHAR_H 1 /* C library functions */ +#define HAVE_LIBC 1 #define HAVE_DLOPEN 1 #define HAVE_MALLOC 1 #define HAVE_CALLOC 1 diff --git a/include/build_config/SDL_build_config_windows.h b/include/build_config/SDL_build_config_windows.h index d2846abc8e9bc..52c2df72593b4 100644 --- a/include/build_config/SDL_build_config_windows.h +++ b/include/build_config/SDL_build_config_windows.h @@ -112,8 +112,11 @@ typedef unsigned int uintptr_t; #endif /* This can be disabled to avoid C runtime dependencies and manifest requirements */ -#define HAVE_LIBC -#ifdef HAVE_LIBC +#ifndef HAVE_LIBC +#define HAVE_LIBC 1 +#endif + +#if HAVE_LIBC /* Useful headers */ #define HAVE_CTYPE_H 1 #define HAVE_FLOAT_H 1 diff --git a/include/build_config/SDL_build_config_wingdk.h b/include/build_config/SDL_build_config_wingdk.h index 3730221d919fa..2f25b597e5494 100644 --- a/include/build_config/SDL_build_config_wingdk.h +++ b/include/build_config/SDL_build_config_wingdk.h @@ -56,8 +56,6 @@ # define SDL_DISABLE_AVX 1 #endif -/* This is disabled by default to avoid C runtime dependencies and manifest requirements */ -#ifdef HAVE_LIBC /* Useful headers */ #define HAVE_CTYPE_H 1 #define HAVE_FLOAT_H 1 @@ -73,6 +71,7 @@ #define HAVE_WCHAR_H 1 /* C library functions */ +#define HAVE_LIBC 1 #define HAVE_MALLOC 1 #define HAVE_CALLOC 1 #define HAVE_REALLOC 1 @@ -156,11 +155,6 @@ #define HAVE_TRUNCF 1 #define HAVE__FSEEKI64 1 #endif /* _MSC_VER */ -#else -#define HAVE_STDARG_H 1 -#define HAVE_STDDEF_H 1 -#define HAVE_STDINT_H 1 -#endif /* Enable various audio drivers */ #if defined(HAVE_MMDEVICEAPI_H) && defined(HAVE_AUDIOCLIENT_H) diff --git a/include/build_config/SDL_build_config_winrt.h b/include/build_config/SDL_build_config_winrt.h index bb014efe93d85..08ec512b0708b 100644 --- a/include/build_config/SDL_build_config_winrt.h +++ b/include/build_config/SDL_build_config_winrt.h @@ -71,6 +71,7 @@ #define HAVE_WCHAR_H 1 /* C library functions */ +#define HAVE_LIBC 1 #define HAVE_MALLOC 1 #define HAVE_CALLOC 1 #define HAVE_REALLOC 1 diff --git a/include/build_config/SDL_build_config_xbox.h b/include/build_config/SDL_build_config_xbox.h index aecbd3a1222c1..2f0f090fe7728 100644 --- a/include/build_config/SDL_build_config_xbox.h +++ b/include/build_config/SDL_build_config_xbox.h @@ -56,8 +56,6 @@ # define SDL_DISABLE_AVX 1 #endif -/* This is disabled by default to avoid C runtime dependencies and manifest requirements */ -#ifdef HAVE_LIBC /* Useful headers */ #define HAVE_CTYPE_H 1 #define HAVE_FLOAT_H 1 @@ -73,6 +71,7 @@ #define HAVE_WCHAR_H 1 /* C library functions */ +#define HAVE_LIBC 1 #define HAVE_MALLOC 1 #define HAVE_CALLOC 1 #define HAVE_REALLOC 1 @@ -156,11 +155,6 @@ #define HAVE_TRUNCF 1 #define HAVE__FSEEKI64 1 #endif /* _MSC_VER */ -#else -#define HAVE_STDARG_H 1 -#define HAVE_STDDEF_H 1 -#define HAVE_STDINT_H 1 -#endif /* Enable various audio drivers */ #if defined(HAVE_MMDEVICEAPI_H) && defined(HAVE_AUDIOCLIENT_H) From 534f753e2025040f25b56524bbe96ae26cabe177 Mon Sep 17 00:00:00 2001 From: Nikita Krapivin Date: Thu, 25 Jan 2024 01:26:01 +0500 Subject: [PATCH 010/220] GameInput backend for SDL (Gamepad-only for now) --- VisualC-GDK/SDL/SDL.vcxproj | 7 +- .../build_config/SDL_build_config_wingdk.h | 10 + include/build_config/SDL_build_config_xbox.h | 12 +- src/core/windows/SDL_xinput.c | 4 + src/joystick/SDL_joystick.c | 3 + src/joystick/SDL_sysjoystick.h | 1 + src/joystick/gdk/SDL_gameinputjoystick.cpp | 582 ++++++++++++++++++ src/joystick/gdk/SDL_gameinputjoystick_c.h | 58 ++ 8 files changed, 674 insertions(+), 3 deletions(-) create mode 100644 src/joystick/gdk/SDL_gameinputjoystick.cpp create mode 100644 src/joystick/gdk/SDL_gameinputjoystick_c.h diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index e070c31ef76e7..cec3972f0f9c3 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj +++ b/VisualC-GDK/SDL/SDL.vcxproj @@ -438,6 +438,7 @@ + @@ -576,9 +577,7 @@ - - @@ -691,6 +690,10 @@ CompileAsCpp CompileAsCpp + + $(IntDir)$(TargetName)_cpp.pch + $(IntDir)$(TargetName)_cpp.pch + diff --git a/include/build_config/SDL_build_config_wingdk.h b/include/build_config/SDL_build_config_wingdk.h index 2f25b597e5494..d856d2323e08b 100644 --- a/include/build_config/SDL_build_config_wingdk.h +++ b/include/build_config/SDL_build_config_wingdk.h @@ -176,6 +176,16 @@ #define SDL_JOYSTICK_XINPUT 1 #define SDL_HAPTIC_DINPUT 1 +/* Native GameInput: */ +/*#define SDL_JOYSTICK_GAMEINPUT 1*/ +#if defined(SDL_JOYSTICK_GAMEINPUT) && (defined(SDL_JOYSTICK_XINPUT) || defined(SDL_JOYSTICK_DINPUT)) +#error "GameInput cannot co-exist, choose one." +#endif /* defined(SDL_JOYSTICK_GAMEINPUT) && (defined(SDL_JOYSTICK_XINPUT) || defined(SDL_JOYSTICK_DINPUT)) */ +#if defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT +/* TODO: Implement proper haptics for GameInput! */ +#define SDL_HAPTIC_DUMMY 1 +#endif /* defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT */ + /* Enable the sensor driver */ #ifdef HAVE_SENSORSAPI_H #define SDL_SENSOR_WINDOWS 1 diff --git a/include/build_config/SDL_build_config_xbox.h b/include/build_config/SDL_build_config_xbox.h index 2f0f090fe7728..c5198aeec7631 100644 --- a/include/build_config/SDL_build_config_xbox.h +++ b/include/build_config/SDL_build_config_xbox.h @@ -173,7 +173,17 @@ #ifdef HAVE_WINDOWS_GAMING_INPUT_H #define SDL_JOYSTICK_WGI 1 #endif -#define SDL_JOYSTICK_XINPUT 1 +/* This is XInputOnGameInput for GDK platforms: */ +/*#define SDL_JOYSTICK_XINPUT 1*/ +/* Native GameInput: */ +#define SDL_JOYSTICK_GAMEINPUT 1 +#if defined(SDL_JOYSTICK_GAMEINPUT) && (defined(SDL_JOYSTICK_XINPUT) || defined(SDL_JOYSTICK_DINPUT)) +#error "GameInput cannot co-exist, choose one." +#endif /* defined(SDL_JOYSTICK_GAMEINPUT) && (defined(SDL_JOYSTICK_XINPUT) || defined(SDL_JOYSTICK_DINPUT)) */ +#if defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT +/* TODO: Implement proper haptics for GameInput! */ +#define SDL_HAPTIC_DUMMY 1 +#endif /* defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT */ /*#define SDL_HAPTIC_DINPUT 1*/ /* Enable the sensor driver */ diff --git a/src/core/windows/SDL_xinput.c b/src/core/windows/SDL_xinput.c index 13c06bde4dbc6..fdeed70854499 100644 --- a/src/core/windows/SDL_xinput.c +++ b/src/core/windows/SDL_xinput.c @@ -20,6 +20,8 @@ */ #include "SDL_internal.h" +#ifndef SDL_JOYSTICK_GAMEINPUT + #include "SDL_xinput.h" /* Set up for C function definitions, even when using C++ */ @@ -142,3 +144,5 @@ void WIN_UnloadXInputDLL(void) #ifdef __cplusplus } #endif + +#endif /* !SDL_JOYSTICK_GAMEINPUT */ diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index bbc553b58b4cf..c1c1a3843cae0 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -55,6 +55,9 @@ static SDL_JoystickDriver *SDL_joystick_drivers[] = { #ifdef SDL_JOYSTICK_RAWINPUT /* Before WINDOWS_ driver, as WINDOWS wants to check if this driver is handling things */ &SDL_RAWINPUT_JoystickDriver, #endif +#ifdef SDL_JOYSTICK_GAMEINPUT /* Before WINDOWS_ driver, as GameInput takes priority over XInputOnGameInput for GDK platforms */ + &SDL_GAMEINPUT_JoystickDriver, +#endif #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) /* Before WGI driver, as WGI wants to check if this driver is handling things */ &SDL_WINDOWS_JoystickDriver, #endif diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h index 95544118da180..739d6973d1322 100644 --- a/src/joystick/SDL_sysjoystick.h +++ b/src/joystick/SDL_sysjoystick.h @@ -245,6 +245,7 @@ extern SDL_JoystickDriver SDL_PS2_JoystickDriver; extern SDL_JoystickDriver SDL_PSP_JoystickDriver; extern SDL_JoystickDriver SDL_VITA_JoystickDriver; extern SDL_JoystickDriver SDL_N3DS_JoystickDriver; +extern SDL_JoystickDriver SDL_GAMEINPUT_JoystickDriver; /* Ends C function definitions when using C++ */ #ifdef __cplusplus diff --git a/src/joystick/gdk/SDL_gameinputjoystick.cpp b/src/joystick/gdk/SDL_gameinputjoystick.cpp new file mode 100644 index 0000000000000..1b4223d6ea729 --- /dev/null +++ b/src/joystick/gdk/SDL_gameinputjoystick.cpp @@ -0,0 +1,582 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_gameinputjoystick_c.h" + +#if defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Public APIs: GAMEINPUT_Joystick... */ +/* Private APIs: GAMEINPUT_InternalJoystick... */ + +#include "../usb_ids.h" + +typedef struct GAMEINPUT_InternalDevice +{ + IGameInputDevice *device; + const char *deviceName; /* this is a constant string literal */ + SDL_JoystickGUID joystickGuid; /* generated by SDL. */ + SDL_JoystickID instanceId; /* generated by SDL. */ + int playerIndex; + Uint32 caps; + char devicePath[(APP_LOCAL_DEVICE_ID_SIZE * 2) + 1]; + bool isAdded, isDeleteRequested; +} GAMEINPUT_InternalDevice; + +typedef struct GAMEINPUT_InternalList +{ + GAMEINPUT_InternalDevice **devices; + int count; +} GAMEINPUT_InternalList; + +typedef struct joystick_hwdata +{ + GAMEINPUT_InternalDevice *devref; + GameInputRumbleParams rumbleParams; + Uint64 lastTimestamp; +} GAMEINPUT_InternalJoystickHwdata; + + +static GAMEINPUT_InternalList g_GameInputList = { NULL }; +static IGameInput *g_pGameInput = NULL; +static GameInputCallbackToken g_GameInputCallbackToken = GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE; + + +static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) +{ + GAMEINPUT_InternalDevice **devicelist = NULL; + GAMEINPUT_InternalDevice *elem = NULL; + const GameInputDeviceInfo *devinfo = NULL; + char tmpbuff[4]; + int idx = 0; + + if (!pDevice) { + return SDL_SetError("GAMEINPUT_InternalAddOrFind argument pDevice cannot be NULL"); + } + + devinfo = pDevice->GetDeviceInfo(); + if (!devinfo) { + return SDL_SetError("GAMEINPUT_InternalAddOrFind GetDeviceInfo returned NULL"); + } + + for (idx = 0; idx < g_GameInputList.count; ++idx) { + elem = g_GameInputList.devices[idx]; + if (elem && elem->device == pDevice) { + /* we're already added */ + return idx; + } + } + + elem = (GAMEINPUT_InternalDevice *)SDL_calloc(1, sizeof(*elem)); + if (!elem) { + return SDL_OutOfMemory(); + } + + /* generate a device name */ + for (idx = 0; idx < APP_LOCAL_DEVICE_ID_SIZE; ++idx) { + (void)SDL_snprintf(tmpbuff, SDL_arraysize(tmpbuff), "%02hhX", devinfo->deviceId.value[idx]); + (void)strncat_s(elem->devicePath, tmpbuff, SDL_arraysize(tmpbuff)); + } + + devicelist = (GAMEINPUT_InternalDevice **)SDL_realloc(g_GameInputList.devices, sizeof(elem) * (g_GameInputList.count + 1LL)); + if (!devicelist) { + SDL_free(elem); + return SDL_OutOfMemory(); + } + + g_GameInputList.devices = devicelist; + pDevice->AddRef(); + elem->device = pDevice; + elem->deviceName = "GameInput Gamepad"; + elem->caps = 0; + if (devinfo->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) { + elem->caps |= SDL_JOYSTICK_CAP_RUMBLE; + } + if (devinfo->supportedRumbleMotors & (GameInputRumbleLeftTrigger | GameInputRumbleRightTrigger)) { + elem->caps |= SDL_JOYSTICK_CAP_TRIGGER_RUMBLE; + } + elem->joystickGuid = SDL_CreateJoystickGUID( + SDL_HARDWARE_BUS_BLUETOOTH, + USB_VENDOR_MICROSOFT, + USB_PRODUCT_XBOX_SERIES_X_BLE, + 1, + "GameInput", + "Gamepad", + 'g', + 0 + ); + elem->instanceId = SDL_GetNextObjectID(); + g_GameInputList.devices[g_GameInputList.count] = elem; + + /* finally increment the count and return */ + return g_GameInputList.count++; +} + +static int GAMEINPUT_InternalRemoveByIndex(int idx) +{ + GAMEINPUT_InternalDevice **devicelist = NULL; + int bytes = 0; + + if (idx < 0 || idx >= g_GameInputList.count) { + return SDL_SetError("GAMEINPUT_InternalRemoveByIndex argument idx %d is out of range", idx); + } + + g_GameInputList.devices[idx]->device->Release(); + + if (g_GameInputList.devices[idx]) { + SDL_free(g_GameInputList.devices[idx]); + g_GameInputList.devices[idx] = NULL; + } + + if (g_GameInputList.count == 1) { + /* last element in the list, free the entire list then */ + SDL_free(g_GameInputList.devices); + g_GameInputList.devices = NULL; + } else { + if (idx != g_GameInputList.count - 1) { + bytes = sizeof(*devicelist) * (g_GameInputList.count - idx); + SDL_memmove(&g_GameInputList.devices[idx], &g_GameInputList.devices[idx + 1], bytes); + } + + devicelist = (GAMEINPUT_InternalDevice **)SDL_realloc(g_GameInputList.devices, sizeof(*devicelist) * (g_GameInputList.count - 1LL)); + if (!devicelist) { + return SDL_OutOfMemory(); + } + + g_GameInputList.devices = devicelist; + } + + /* decrement the count and return */ + return g_GameInputList.count--; +} + +static GAMEINPUT_InternalDevice *GAMEINPUT_InternalFindByIndex(int idx) +{ + if (idx < 0 || idx >= g_GameInputList.count) { + SDL_SetError("GAMEINPUT_InternalFindByIndex argument idx %d out of range", idx); + return NULL; + } + + return g_GameInputList.devices[idx]; +} + +static void CALLBACK GAMEINPUT_InternalJoystickDeviceCallback( + _In_ GameInputCallbackToken callbackToken, + _In_ void* context, + _In_ IGameInputDevice* device, + _In_ uint64_t timestamp, + _In_ GameInputDeviceStatus currentStatus, + _In_ GameInputDeviceStatus previousStatus) +{ + int idx = 0; + GAMEINPUT_InternalDevice *elem = NULL; + + if (currentStatus & GameInputDeviceConnected) { + GAMEINPUT_InternalAddOrFind(device); + } else { + for (idx = 0; idx < g_GameInputList.count; ++idx) { + elem = g_GameInputList.devices[idx]; + if (elem && elem->device == device) { + /* will be deleted on the next Detect call */ + elem->isDeleteRequested = true; + break; + } + } + } +} + +static void GAMEINPUT_JoystickDetect(void); + +static int GAMEINPUT_JoystickInit(void) +{ + HRESULT hR; + + if (!g_pGameInput) { + hR = GameInputCreate(&g_pGameInput); + if (FAILED(hR)) { + return SDL_SetError("GameInputCreate failure with HRESULT of %08X", hR); + } + } + + hR = g_pGameInput->RegisterDeviceCallback( + nullptr, + GameInputKindGamepad, + GameInputDeviceConnected, + GameInputBlockingEnumeration, + nullptr, + GAMEINPUT_InternalJoystickDeviceCallback, + &g_GameInputCallbackToken + ); + if (FAILED(hR)) { + return SDL_SetError("IGameInput::RegisterDeviceCallback failure with HRESULT of %08X", hR); + } + + GAMEINPUT_JoystickDetect(); + + /* no need to free IGameInput on failure. */ + return 0; +} + +static int GAMEINPUT_JoystickGetCount(void) +{ + return g_GameInputList.count; +} + +static void GAMEINPUT_JoystickDetect(void) +{ + int idx = 0; + GAMEINPUT_InternalDevice *elem = NULL; + + for (idx = 0; idx < g_GameInputList.count; ++idx) { + elem = g_GameInputList.devices[idx]; + if (!elem) { + continue; + } + + if (!elem->isAdded) { + SDL_PrivateJoystickAdded(elem->instanceId); + elem->isAdded = true; + } + + if (elem->isDeleteRequested || !(elem->device->GetDeviceStatus() & GameInputDeviceConnected)) { + SDL_PrivateJoystickRemoved(elem->instanceId); + GAMEINPUT_InternalRemoveByIndex(idx--); + } + } +} + +static const char *GAMEINPUT_JoystickGetDeviceName(int device_index) +{ + GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); + + if (!elem) { + return NULL; + } + + return elem->deviceName; +} + +static const char *GAMEINPUT_JoystickGetDevicePath(int device_index) +{ + GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); + + if (!elem) { + return NULL; + } + + /* APP_LOCAL_DEVICE_ID as a hex string, since it's required for some association callbacks */ + return elem->devicePath; +} + +static int GAMEINPUT_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) +{ + /* Steamworks API is not available in GDK */ + return -1; +} + +static int GAMEINPUT_JoystickGetDevicePlayerIndex(int device_index) +{ + /* + * Okay, so, while XInput technically has player indicies, + * GameInput does not. It just dispatches a callback whenever a device is found. + * So if you're using true native GameInput (which this backend IS) + * you're meant to assign some index to a player yourself. + * + * GameMaker, for example, seems to do this in the order of plugging in. + * + * Sorry for the trouble! + */ + GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); + + if (!elem) { + return -1; + } + + return elem->playerIndex; +} + +static void GAMEINPUT_JoystickSetDevicePlayerIndex(int device_index, int player_index) +{ + GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); + + if (!elem) { + return; + } + + elem->playerIndex = player_index; +} + +static SDL_JoystickGUID GAMEINPUT_JoystickGetDeviceGUID(int device_index) +{ + GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); + + if (!elem) { + /* empty guid */ + return { { 0 } }; + } + + return elem->joystickGuid; +} + +static SDL_JoystickID GAMEINPUT_JoystickGetDeviceInstanceID(int device_index) +{ + GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); + + if (!elem) { + return 0; + } + + return elem->instanceId; +} + +static int GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) +{ + GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); + GAMEINPUT_InternalJoystickHwdata *hwdata = NULL; + + if (!elem) { + return -1; + } + + hwdata = (GAMEINPUT_InternalJoystickHwdata *)SDL_calloc(1, sizeof(*hwdata)); + if (!hwdata) { + return SDL_OutOfMemory(); + } + + hwdata->devref = elem; + + joystick->hwdata = hwdata; + joystick->naxes = 6; + joystick->nbuttons = 11; + joystick->nhats = 1; + + return 0; +} + +static int GAMEINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +{ + /* don't check for caps here, since SetRumbleState doesn't return any result - we don't need to check it */ + GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata; + GameInputRumbleParams *params = &hwdata->rumbleParams; + params->lowFrequency = (float)low_frequency_rumble / (float)SDL_MAX_UINT16; + params->highFrequency = (float)high_frequency_rumble / (float)SDL_MAX_UINT16; + hwdata->devref->device->SetRumbleState(params); + return 0; +} + +static int GAMEINPUT_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) +{ + /* don't check for caps here, since SetRumbleState doesn't return any result - we don't need to check it */ + GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata; + GameInputRumbleParams *params = &hwdata->rumbleParams; + params->leftTrigger = (float)left_rumble / (float)SDL_MAX_UINT16; + params->rightTrigger = (float)right_rumble / (float)SDL_MAX_UINT16; + hwdata->devref->device->SetRumbleState(params); + return 0; +} + +static Uint32 GAMEINPUT_JoystickGetCapabilities(SDL_Joystick *joystick) +{ + return joystick->hwdata->devref->caps; +} + +static int GAMEINPUT_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) +{ + return SDL_Unsupported(); +} + +static int GAMEINPUT_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size) +{ + HRESULT hR = S_OK; + const GAMEINPUT_JoystickEffectData *effect = NULL; + GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata; + + if (!data || size != sizeof(GAMEINPUT_JoystickEffectData)) { + return SDL_SetError("GAMEINPUT_JoystickSendEffect invalid data or size"); + } + + effect = (const GAMEINPUT_JoystickEffectData *)data; + if (effect->type == GAMEINPUT_JoystickEffectDataType_HapticFeedback) { + hR = hwdata->devref->device->SetHapticMotorState( + effect->hapticFeedbackMotorIndex, + &effect->hapticFeedbackParams + ); + if (FAILED(hR)) { + return SDL_SetError("IGameInputDevice::SetHapticMotorState failure with HRESULT of %08X", hR); + } + } + + return 0; +} + +static int GAMEINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) +{ + /* I am not sure what is this even supposed to do in case of GameInput... */ + return 0; +} + +static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) +{ + static WORD s_XInputButtons[] = { + GameInputGamepadA, GameInputGamepadB, GameInputGamepadX, GameInputGamepadY, + GameInputGamepadLeftShoulder, GameInputGamepadRightShoulder, GameInputGamepadView, GameInputGamepadMenu, + GameInputGamepadLeftThumbstick, GameInputGamepadRightThumbstick, + 0 /* Guide button is not supported on Xbox so ignore that... */ + }; + Uint8 btnidx = 0, btnstate = 0, hat = 0; + GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata; + IGameInputDevice *device = hwdata->devref->device; + IGameInputReading *reading = NULL; + uint64_t ts = 0; + GameInputGamepadState state; + HRESULT hR = g_pGameInput->GetCurrentReading( + GameInputKindGamepad, + device, + &reading + ); + + if (FAILED(hR)) { + /* don't SetError here since there can be a legitimate case when there's no reading avail */ + return; + } + + /* GDKX private docs for GetTimestamp: "The microsecond timestamp describing when the input was made." */ + /* SDL expects a nanosecond timestamp, so I guess US_TO_NS should be used here? */ + ts = SDL_US_TO_NS(reading->GetTimestamp()); + + if (((!hwdata->lastTimestamp) || (ts != hwdata->lastTimestamp)) && reading->GetGamepadState(&state)) { + /* `state` is now valid */ + +#define tosint16(_TheValue) ((Sint16)(((_TheValue) < 0.0f) ? ((_TheValue) * 32768.0f) : ((_TheValue) * 32767.0f))) + SDL_SendJoystickAxis(ts, joystick, 0, tosint16(state.leftThumbstickX)); + SDL_SendJoystickAxis(ts, joystick, 1, tosint16(state.leftThumbstickY)); + SDL_SendJoystickAxis(ts, joystick, 2, tosint16(state.leftTrigger)); + SDL_SendJoystickAxis(ts, joystick, 3, tosint16(state.rightThumbstickX)); + SDL_SendJoystickAxis(ts, joystick, 4, tosint16(state.rightThumbstickY)); + SDL_SendJoystickAxis(ts, joystick, 5, tosint16(state.rightTrigger)); +#undef tosint16 + + for (btnidx = 0; btnidx < (Uint8)SDL_arraysize(s_XInputButtons); ++btnidx) { + if (s_XInputButtons[btnidx] == 0) { + btnstate = SDL_RELEASED; + } else { + btnstate = (state.buttons & s_XInputButtons[btnidx]) ? SDL_PRESSED : SDL_RELEASED; + } + + SDL_SendJoystickButton(ts, joystick, btnidx, btnstate); + } + + if (state.buttons & GameInputGamepadDPadUp) { + hat |= SDL_HAT_UP; + } + if (state.buttons & GameInputGamepadDPadDown) { + hat |= SDL_HAT_DOWN; + } + if (state.buttons & GameInputGamepadDPadLeft) { + hat |= SDL_HAT_LEFT; + } + if (state.buttons & GameInputGamepadDPadRight) { + hat |= SDL_HAT_RIGHT; + } + SDL_SendJoystickHat(ts, joystick, 0, hat); + + /* Xbox doesn't let you obtain the power level, pretend we're always full */ + SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL); + + hwdata->lastTimestamp = ts; + } + + reading->Release(); +} + +static void GAMEINPUT_JoystickClose(SDL_Joystick* joystick) +{ + SDL_free(joystick->hwdata); + joystick->hwdata = NULL; +} + +static void GAMEINPUT_JoystickQuit(void) +{ + int idx; + + if (!g_pGameInput) { + return; + } + + /* free the callback */ + g_pGameInput->UnregisterCallback(g_GameInputCallbackToken, /*timeoutInUs:*/ 10000); + g_GameInputCallbackToken = GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE; + + /* free the list */ + for (idx = 0; idx < g_GameInputList.count; ++idx) { + g_GameInputList.devices[idx]->device->Release(); + SDL_free(g_GameInputList.devices[idx]); + g_GameInputList.devices[idx] = NULL; + } + SDL_free(g_GameInputList.devices); + g_GameInputList.devices = NULL; + g_GameInputList.count = 0; + + g_pGameInput->Release(); + g_pGameInput = NULL; +} + +static SDL_bool GAMEINPUT_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) +{ + return SDL_FALSE; +} + + +SDL_JoystickDriver SDL_GAMEINPUT_JoystickDriver = +{ + GAMEINPUT_JoystickInit, + GAMEINPUT_JoystickGetCount, + GAMEINPUT_JoystickDetect, + GAMEINPUT_JoystickGetDeviceName, + GAMEINPUT_JoystickGetDevicePath, + GAMEINPUT_JoystickGetDeviceSteamVirtualGamepadSlot, + GAMEINPUT_JoystickGetDevicePlayerIndex, + GAMEINPUT_JoystickSetDevicePlayerIndex, + GAMEINPUT_JoystickGetDeviceGUID, + GAMEINPUT_JoystickGetDeviceInstanceID, + GAMEINPUT_JoystickOpen, + GAMEINPUT_JoystickRumble, + GAMEINPUT_JoystickRumbleTriggers, + GAMEINPUT_JoystickGetCapabilities, + GAMEINPUT_JoystickSetLED, + GAMEINPUT_JoystickSendEffect, + GAMEINPUT_JoystickSetSensorsEnabled, + GAMEINPUT_JoystickUpdate, + GAMEINPUT_JoystickClose, + GAMEINPUT_JoystickQuit, + GAMEINPUT_JoystickGetGamepadMapping +}; + + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif + +#endif /* defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT */ diff --git a/src/joystick/gdk/SDL_gameinputjoystick_c.h b/src/joystick/gdk/SDL_gameinputjoystick_c.h new file mode 100644 index 0000000000000..03bc1c059950d --- /dev/null +++ b/src/joystick/gdk/SDL_gameinputjoystick_c.h @@ -0,0 +1,58 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" +#include "../SDL_sysjoystick.h" + +#if defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT + +/* include this file in C++ */ +#include + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum GAMEINPUT_JoystickEffectDataType +{ + GAMEINPUT_JoystickEffectDataType_HapticFeedback +} GAMEINPUT_JoystickEffectDataType; + +typedef struct GAMEINPUT_JoystickEffectData +{ + GAMEINPUT_JoystickEffectDataType type; + + union + { + struct /* type == GAMEINPUT_JoystickEffectDataType_HapticFeedback */ + { + uint32_t hapticFeedbackMotorIndex; + GameInputHapticFeedbackParams hapticFeedbackParams; + }; + }; +} GAMEINPUT_JoystickEffectData; + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif + +#endif /* defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT */ From fee140bdfeb9971d625392310430347c8f4691df Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 14:07:42 -0800 Subject: [PATCH 011/220] Added the option for GameInput support to the Win32 SDL build GameInput is designed to be used by Win32 C applications, so no need to restrict it to the GDK build. --- VisualC-GDK/SDL/SDL.vcxproj | 7 +- VisualC-GDK/SDL/SDL.vcxproj.filters | 2 + VisualC/SDL/SDL.vcxproj | 2 + VisualC/SDL/SDL.vcxproj.filters | 9 ++ .../build_config/SDL_build_config_windows.h | 1 + src/core/windows/SDL_xinput.c | 4 - ...utjoystick.cpp => SDL_gameinputjoystick.c} | 130 ++++++++++-------- src/joystick/gdk/SDL_gameinputjoystick_c.h | 13 +- 8 files changed, 89 insertions(+), 79 deletions(-) rename src/joystick/gdk/{SDL_gameinputjoystick.cpp => SDL_gameinputjoystick.c} (82%) diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index cec3972f0f9c3..9d4ecf0da5f69 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj +++ b/VisualC-GDK/SDL/SDL.vcxproj @@ -425,6 +425,7 @@ + @@ -438,7 +439,6 @@ - @@ -646,6 +646,7 @@ + @@ -690,10 +691,6 @@ CompileAsCpp CompileAsCpp - - $(IntDir)$(TargetName)_cpp.pch - $(IntDir)$(TargetName)_cpp.pch - diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters index f5d648fe70cb1..c59af5d0a57bf 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj.filters +++ b/VisualC-GDK/SDL/SDL.vcxproj.filters @@ -52,6 +52,7 @@ + @@ -319,6 +320,7 @@ + diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index 45415212dcfd0..c5a508b827461 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -347,6 +347,7 @@ + @@ -520,6 +521,7 @@ + diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index 8073004813fd2..679a2bc552031 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -88,6 +88,9 @@ {d008487d-6ed0-4251-848b-79a68e3c1459} + + {c9e8273e-13ae-47dc-bef8-8ad8e64c9a3e} + {c9e8273e-13ae-47dc-bef8-8ad8e64c9a3d} @@ -546,6 +549,9 @@ haptic\windows + + joystick\gdk + joystick\hidapi @@ -1063,6 +1069,9 @@ joystick\dummy + + joystick\gdk + joystick\hidapi diff --git a/include/build_config/SDL_build_config_windows.h b/include/build_config/SDL_build_config_windows.h index 52c2df72593b4..68664c5ba9a36 100644 --- a/include/build_config/SDL_build_config_windows.h +++ b/include/build_config/SDL_build_config_windows.h @@ -238,6 +238,7 @@ typedef unsigned int uintptr_t; /* Enable various input drivers */ #define SDL_JOYSTICK_DINPUT 1 +/*#define SDL_JOYSTICK_GAMEINPUT 1*/ #define SDL_JOYSTICK_HIDAPI 1 #ifndef SDL_PLATFORM_WINRT #define SDL_JOYSTICK_RAWINPUT 1 diff --git a/src/core/windows/SDL_xinput.c b/src/core/windows/SDL_xinput.c index fdeed70854499..13c06bde4dbc6 100644 --- a/src/core/windows/SDL_xinput.c +++ b/src/core/windows/SDL_xinput.c @@ -20,8 +20,6 @@ */ #include "SDL_internal.h" -#ifndef SDL_JOYSTICK_GAMEINPUT - #include "SDL_xinput.h" /* Set up for C function definitions, even when using C++ */ @@ -144,5 +142,3 @@ void WIN_UnloadXInputDLL(void) #ifdef __cplusplus } #endif - -#endif /* !SDL_JOYSTICK_GAMEINPUT */ diff --git a/src/joystick/gdk/SDL_gameinputjoystick.cpp b/src/joystick/gdk/SDL_gameinputjoystick.c similarity index 82% rename from src/joystick/gdk/SDL_gameinputjoystick.cpp rename to src/joystick/gdk/SDL_gameinputjoystick.c index 1b4223d6ea729..230f5c96ff8de 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.cpp +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -18,6 +18,8 @@ misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ +#include "SDL_internal.h" + #include "SDL_gameinputjoystick_c.h" #if defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT @@ -39,9 +41,9 @@ typedef struct GAMEINPUT_InternalDevice SDL_JoystickGUID joystickGuid; /* generated by SDL. */ SDL_JoystickID instanceId; /* generated by SDL. */ int playerIndex; - Uint32 caps; + GameInputRumbleMotors supportedRumbleMotors; char devicePath[(APP_LOCAL_DEVICE_ID_SIZE * 2) + 1]; - bool isAdded, isDeleteRequested; + SDL_bool isAdded, isDeleteRequested; } GAMEINPUT_InternalDevice; typedef struct GAMEINPUT_InternalList @@ -59,6 +61,7 @@ typedef struct joystick_hwdata static GAMEINPUT_InternalList g_GameInputList = { NULL }; +static void *g_hGameInputDLL = NULL; static IGameInput *g_pGameInput = NULL; static GameInputCallbackToken g_GameInputCallbackToken = GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE; @@ -75,7 +78,7 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) return SDL_SetError("GAMEINPUT_InternalAddOrFind argument pDevice cannot be NULL"); } - devinfo = pDevice->GetDeviceInfo(); + devinfo = IGameInputDevice_GetDeviceInfo(pDevice); if (!devinfo) { return SDL_SetError("GAMEINPUT_InternalAddOrFind GetDeviceInfo returned NULL"); } @@ -95,8 +98,8 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) /* generate a device name */ for (idx = 0; idx < APP_LOCAL_DEVICE_ID_SIZE; ++idx) { - (void)SDL_snprintf(tmpbuff, SDL_arraysize(tmpbuff), "%02hhX", devinfo->deviceId.value[idx]); - (void)strncat_s(elem->devicePath, tmpbuff, SDL_arraysize(tmpbuff)); + SDL_snprintf(tmpbuff, SDL_arraysize(tmpbuff), "%02hhX", devinfo->deviceId.value[idx]); + SDL_strlcat(elem->devicePath, tmpbuff, SDL_arraysize(tmpbuff)); } devicelist = (GAMEINPUT_InternalDevice **)SDL_realloc(g_GameInputList.devices, sizeof(elem) * (g_GameInputList.count + 1LL)); @@ -106,16 +109,10 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) } g_GameInputList.devices = devicelist; - pDevice->AddRef(); + IGameInputDevice_AddRef(pDevice); elem->device = pDevice; elem->deviceName = "GameInput Gamepad"; - elem->caps = 0; - if (devinfo->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) { - elem->caps |= SDL_JOYSTICK_CAP_RUMBLE; - } - if (devinfo->supportedRumbleMotors & (GameInputRumbleLeftTrigger | GameInputRumbleRightTrigger)) { - elem->caps |= SDL_JOYSTICK_CAP_TRIGGER_RUMBLE; - } + elem->supportedRumbleMotors = devinfo->supportedRumbleMotors; elem->joystickGuid = SDL_CreateJoystickGUID( SDL_HARDWARE_BUS_BLUETOOTH, USB_VENDOR_MICROSOFT, @@ -142,7 +139,7 @@ static int GAMEINPUT_InternalRemoveByIndex(int idx) return SDL_SetError("GAMEINPUT_InternalRemoveByIndex argument idx %d is out of range", idx); } - g_GameInputList.devices[idx]->device->Release(); + IGameInputDevice_Release(g_GameInputList.devices[idx]->device); if (g_GameInputList.devices[idx]) { SDL_free(g_GameInputList.devices[idx]); @@ -199,7 +196,7 @@ static void CALLBACK GAMEINPUT_InternalJoystickDeviceCallback( elem = g_GameInputList.devices[idx]; if (elem && elem->device == device) { /* will be deleted on the next Detect call */ - elem->isDeleteRequested = true; + elem->isDeleteRequested = SDL_TRUE; break; } } @@ -212,29 +209,40 @@ static int GAMEINPUT_JoystickInit(void) { HRESULT hR; + if (!g_hGameInputDLL) { + g_hGameInputDLL = SDL_LoadObject("gameinput.dll"); + if (!g_hGameInputDLL) { + return -1; + } + } + if (!g_pGameInput) { - hR = GameInputCreate(&g_pGameInput); + typedef HRESULT (WINAPI *GameInputCreate_t)(IGameInput * *gameInput); + GameInputCreate_t GameInputCreateFunc = (GameInputCreate_t)SDL_LoadFunction(g_hGameInputDLL, "GameInputCreate"); + if (!GameInputCreateFunc) { + return -1; + } + + hR = GameInputCreateFunc(&g_pGameInput); if (FAILED(hR)) { return SDL_SetError("GameInputCreate failure with HRESULT of %08X", hR); } } - hR = g_pGameInput->RegisterDeviceCallback( - nullptr, - GameInputKindGamepad, - GameInputDeviceConnected, - GameInputBlockingEnumeration, - nullptr, - GAMEINPUT_InternalJoystickDeviceCallback, - &g_GameInputCallbackToken - ); + hR = IGameInput_RegisterDeviceCallback(g_pGameInput, + NULL, + GameInputKindGamepad, + GameInputDeviceConnected, + GameInputBlockingEnumeration, + NULL, + GAMEINPUT_InternalJoystickDeviceCallback, + &g_GameInputCallbackToken); if (FAILED(hR)) { return SDL_SetError("IGameInput::RegisterDeviceCallback failure with HRESULT of %08X", hR); } GAMEINPUT_JoystickDetect(); - /* no need to free IGameInput on failure. */ return 0; } @@ -256,10 +264,10 @@ static void GAMEINPUT_JoystickDetect(void) if (!elem->isAdded) { SDL_PrivateJoystickAdded(elem->instanceId); - elem->isAdded = true; + elem->isAdded = SDL_TRUE; } - if (elem->isDeleteRequested || !(elem->device->GetDeviceStatus() & GameInputDeviceConnected)) { + if (elem->isDeleteRequested || !(IGameInputDevice_GetDeviceStatus(elem->device) & GameInputDeviceConnected)) { SDL_PrivateJoystickRemoved(elem->instanceId); GAMEINPUT_InternalRemoveByIndex(idx--); } @@ -332,8 +340,8 @@ static SDL_JoystickGUID GAMEINPUT_JoystickGetDeviceGUID(int device_index) GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); if (!elem) { - /* empty guid */ - return { { 0 } }; + static SDL_JoystickGUID emptyGUID; + return emptyGUID; } return elem->joystickGuid; @@ -371,6 +379,13 @@ static int GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) joystick->nbuttons = 11; joystick->nhats = 1; + if (elem->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) { + SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, SDL_TRUE); + } + if (elem->supportedRumbleMotors & (GameInputRumbleLeftTrigger | GameInputRumbleRightTrigger)) { + SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, SDL_TRUE); + } + return 0; } @@ -381,7 +396,7 @@ static int GAMEINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency GameInputRumbleParams *params = &hwdata->rumbleParams; params->lowFrequency = (float)low_frequency_rumble / (float)SDL_MAX_UINT16; params->highFrequency = (float)high_frequency_rumble / (float)SDL_MAX_UINT16; - hwdata->devref->device->SetRumbleState(params); + IGameInputDevice_SetRumbleState(hwdata->devref->device, params); return 0; } @@ -392,15 +407,10 @@ static int GAMEINPUT_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_ GameInputRumbleParams *params = &hwdata->rumbleParams; params->leftTrigger = (float)left_rumble / (float)SDL_MAX_UINT16; params->rightTrigger = (float)right_rumble / (float)SDL_MAX_UINT16; - hwdata->devref->device->SetRumbleState(params); + IGameInputDevice_SetRumbleState(hwdata->devref->device, params); return 0; } -static Uint32 GAMEINPUT_JoystickGetCapabilities(SDL_Joystick *joystick) -{ - return joystick->hwdata->devref->caps; -} - static int GAMEINPUT_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) { return SDL_Unsupported(); @@ -418,7 +428,7 @@ static int GAMEINPUT_JoystickSendEffect(SDL_Joystick *joystick, const void *data effect = (const GAMEINPUT_JoystickEffectData *)data; if (effect->type == GAMEINPUT_JoystickEffectDataType_HapticFeedback) { - hR = hwdata->devref->device->SetHapticMotorState( + hR = IGameInputDevice_SetHapticMotorState(hwdata->devref->device, effect->hapticFeedbackMotorIndex, &effect->hapticFeedbackParams ); @@ -450,7 +460,7 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) IGameInputReading *reading = NULL; uint64_t ts = 0; GameInputGamepadState state; - HRESULT hR = g_pGameInput->GetCurrentReading( + HRESULT hR = IGameInput_GetCurrentReading(g_pGameInput, GameInputKindGamepad, device, &reading @@ -463,9 +473,9 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) /* GDKX private docs for GetTimestamp: "The microsecond timestamp describing when the input was made." */ /* SDL expects a nanosecond timestamp, so I guess US_TO_NS should be used here? */ - ts = SDL_US_TO_NS(reading->GetTimestamp()); + ts = SDL_US_TO_NS(IGameInputReading_GetTimestamp(reading)); - if (((!hwdata->lastTimestamp) || (ts != hwdata->lastTimestamp)) && reading->GetGamepadState(&state)) { + if (((!hwdata->lastTimestamp) || (ts != hwdata->lastTimestamp)) && IGameInputReading_GetGamepadState(reading, &state)) { /* `state` is now valid */ #define tosint16(_TheValue) ((Sint16)(((_TheValue) < 0.0f) ? ((_TheValue) * 32768.0f) : ((_TheValue) * 32767.0f))) @@ -507,7 +517,7 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) hwdata->lastTimestamp = ts; } - reading->Release(); + IGameInputReading_Release(reading); } static void GAMEINPUT_JoystickClose(SDL_Joystick* joystick) @@ -520,26 +530,29 @@ static void GAMEINPUT_JoystickQuit(void) { int idx; - if (!g_pGameInput) { - return; - } + if (g_pGameInput) { + /* free the callback */ + IGameInput_UnregisterCallback(g_pGameInput, g_GameInputCallbackToken, /*timeoutInUs:*/ 10000); + g_GameInputCallbackToken = GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE; - /* free the callback */ - g_pGameInput->UnregisterCallback(g_GameInputCallbackToken, /*timeoutInUs:*/ 10000); - g_GameInputCallbackToken = GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE; + /* free the list */ + for (idx = 0; idx < g_GameInputList.count; ++idx) { + IGameInputDevice_Release(g_GameInputList.devices[idx]->device); + SDL_free(g_GameInputList.devices[idx]); + g_GameInputList.devices[idx] = NULL; + } + SDL_free(g_GameInputList.devices); + g_GameInputList.devices = NULL; + g_GameInputList.count = 0; - /* free the list */ - for (idx = 0; idx < g_GameInputList.count; ++idx) { - g_GameInputList.devices[idx]->device->Release(); - SDL_free(g_GameInputList.devices[idx]); - g_GameInputList.devices[idx] = NULL; + IGameInput_Release(g_pGameInput); + g_pGameInput = NULL; } - SDL_free(g_GameInputList.devices); - g_GameInputList.devices = NULL; - g_GameInputList.count = 0; - g_pGameInput->Release(); - g_pGameInput = NULL; + if (g_hGameInputDLL) { + SDL_UnloadObject(g_hGameInputDLL); + g_hGameInputDLL = NULL; + } } static SDL_bool GAMEINPUT_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) @@ -563,7 +576,6 @@ SDL_JoystickDriver SDL_GAMEINPUT_JoystickDriver = GAMEINPUT_JoystickOpen, GAMEINPUT_JoystickRumble, GAMEINPUT_JoystickRumbleTriggers, - GAMEINPUT_JoystickGetCapabilities, GAMEINPUT_JoystickSetLED, GAMEINPUT_JoystickSendEffect, GAMEINPUT_JoystickSetSensorsEnabled, diff --git a/src/joystick/gdk/SDL_gameinputjoystick_c.h b/src/joystick/gdk/SDL_gameinputjoystick_c.h index 03bc1c059950d..0b3bc51a63abd 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick_c.h +++ b/src/joystick/gdk/SDL_gameinputjoystick_c.h @@ -23,14 +23,10 @@ #if defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT -/* include this file in C++ */ +#include +#define COBJMACROS #include -/* Set up for C function definitions, even when using C++ */ -#ifdef __cplusplus -extern "C" { -#endif - typedef enum GAMEINPUT_JoystickEffectDataType { GAMEINPUT_JoystickEffectDataType_HapticFeedback @@ -50,9 +46,4 @@ typedef struct GAMEINPUT_JoystickEffectData }; } GAMEINPUT_JoystickEffectData; -/* Ends C function definitions when using C++ */ -#ifdef __cplusplus -} -#endif - #endif /* defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT */ From 7f33464beddb48407ad7478bb77e42c9d9b490db Mon Sep 17 00:00:00 2001 From: Robert Edmonds Date: Sat, 17 Feb 2024 15:51:45 -0500 Subject: [PATCH 012/220] opengles2: Call glClearColor() with r,g,b,a, not r,g,g,a --- src/render/opengles2/SDL_render_gles2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index e51c7c85ac71b..3c7b94f77d938 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -1279,7 +1279,7 @@ static int GLES2_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, (g != data->drawstate.clear_color.g) || (b != data->drawstate.clear_color.b) || (a != data->drawstate.clear_color.a)) { - data->glClearColor(r, g, g, a); + data->glClearColor(r, g, b, a); data->drawstate.clear_color.r = r; data->drawstate.clear_color.g = g; data->drawstate.clear_color.b = b; From f35ede72810b8c5843752a776df9ae8869bf7a79 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 15:41:18 -0800 Subject: [PATCH 013/220] Generalized the idea of joystick driver priority Joystick drivers are sorted by priority in the driver list, and higher priority drivers report whether they are handling a device when lower priority drivers want to add it to their device list. This has been handled ad-hoc with the Windows and HIDAPI drivers, but this formalizes the idea and makes sure that GameInput has the highest priority of the Windows drivers. --- src/joystick/SDL_joystick.c | 35 ++++- src/joystick/SDL_joystick_c.h | 3 + src/joystick/SDL_sysjoystick.h | 3 + src/joystick/android/SDL_sysjoystick.c | 12 +- src/joystick/apple/SDL_mfijoystick.m | 7 + src/joystick/bsd/SDL_bsdjoystick.c | 18 +-- src/joystick/darwin/SDL_iokitjoystick.c | 16 ++- src/joystick/dummy/SDL_sysjoystick.c | 6 + src/joystick/emscripten/SDL_sysjoystick.c | 7 + src/joystick/gdk/SDL_gameinputjoystick.c | 64 +++++---- src/joystick/haiku/SDL_haikujoystick.cc | 7 + src/joystick/hidapi/SDL_hidapijoystick.c | 1 + src/joystick/linux/SDL_sysjoystick.c | 12 +- src/joystick/n3ds/SDL_sysjoystick.c | 47 ++++--- src/joystick/ps2/SDL_sysjoystick.c | 7 + src/joystick/psp/SDL_sysjoystick.c | 7 + src/joystick/virtual/SDL_virtualjoystick.c | 7 + src/joystick/vita/SDL_sysjoystick.c | 27 ++-- src/joystick/windows/SDL_dinputjoystick.c | 8 +- src/joystick/windows/SDL_rawinputjoystick.c | 78 +++++------ src/joystick/windows/SDL_rawinputjoystick_c.h | 3 - .../windows/SDL_windows_gaming_input.c | 131 ++---------------- src/joystick/windows/SDL_windowsjoystick.c | 12 ++ src/joystick/windows/SDL_xinputjoystick.c | 49 +++++-- src/joystick/windows/SDL_xinputjoystick_c.h | 1 + 25 files changed, 297 insertions(+), 271 deletions(-) diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index c1c1a3843cae0..1e0ca44665354 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -49,16 +49,16 @@ #endif static SDL_JoystickDriver *SDL_joystick_drivers[] = { -#ifdef SDL_JOYSTICK_HIDAPI /* Before WINDOWS_ driver, as WINDOWS wants to check if this driver is handling things */ +#ifdef SDL_JOYSTICK_HIDAPI /* Highest priority driver for supported devices */ &SDL_HIDAPI_JoystickDriver, #endif -#ifdef SDL_JOYSTICK_RAWINPUT /* Before WINDOWS_ driver, as WINDOWS wants to check if this driver is handling things */ - &SDL_RAWINPUT_JoystickDriver, -#endif -#ifdef SDL_JOYSTICK_GAMEINPUT /* Before WINDOWS_ driver, as GameInput takes priority over XInputOnGameInput for GDK platforms */ +#ifdef SDL_JOYSTICK_GAMEINPUT /* Higher priority than other Windows drivers */ &SDL_GAMEINPUT_JoystickDriver, #endif -#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) /* Before WGI driver, as WGI wants to check if this driver is handling things */ +#ifdef SDL_JOYSTICK_RAWINPUT + &SDL_RAWINPUT_JoystickDriver, +#endif +#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) &SDL_WINDOWS_JoystickDriver, #endif #ifdef SDL_JOYSTICK_WGI @@ -659,6 +659,29 @@ SDL_bool SDL_JoysticksOpened(void) return opened; } +SDL_bool SDL_JoystickHandledByAnotherDriver(struct SDL_JoystickDriver *driver, Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + int i; + SDL_bool result = SDL_FALSE; + + SDL_LockJoysticks(); + { + for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) { + if (driver == SDL_joystick_drivers[i]) { + /* Higher priority drivers do not have this device */ + break; + } + if (SDL_joystick_drivers[i]->IsDevicePresent(vendor_id, product_id, version, name)) { + result = SDL_TRUE; + break; + } + } + } + SDL_UnlockJoysticks(); + + return result; +} + SDL_JoystickID *SDL_GetJoysticks(int *count) { int i, num_joysticks, device_index; diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h index 3004bb1485a98..0f00d180b68b0 100644 --- a/src/joystick/SDL_joystick_c.h +++ b/src/joystick/SDL_joystick_c.h @@ -54,6 +54,9 @@ extern void SDL_AssertJoysticksLocked(void) SDL_ASSERT_CAPABILITY(SDL_joystick_l /* Function to return whether there are any joysticks opened by the application */ extern SDL_bool SDL_JoysticksOpened(void); +/* Function to determine whether a device is currently detected by this driver */ +extern SDL_bool SDL_JoystickHandledByAnotherDriver(struct SDL_JoystickDriver *driver, Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name); + /* Function to standardize the name for a controller This should be freed with SDL_free() when no longer needed */ diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h index 739d6973d1322..d3d8e71de5c58 100644 --- a/src/joystick/SDL_sysjoystick.h +++ b/src/joystick/SDL_sysjoystick.h @@ -158,6 +158,9 @@ typedef struct SDL_JoystickDriver /* Function to cause any queued joystick insertions to be processed */ void (*Detect)(void); + /* Function to determine whether a device is currently detected by this driver */ + SDL_bool (*IsDevicePresent)(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name); + /* Function to get the device-dependent name of a joystick */ const char *(*GetDeviceName)(int device_index); diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c index 0d2972f5ee833..fe085a749d764 100644 --- a/src/joystick/android/SDL_sysjoystick.c +++ b/src/joystick/android/SDL_sysjoystick.c @@ -321,12 +321,9 @@ int Android_AddJoystick(int device_id, const char *name, const char *desc, int v goto done; } -#ifdef SDL_JOYSTICK_HIDAPI - if (HIDAPI_IsDevicePresent(vendor_id, product_id, 0, name)) { - /* The HIDAPI driver is taking care of this device */ + if (SDL_JoystickHandledByAnotherDriver(&SDL_ANDROID_JoystickDriver, vendor_id, product_id, 0, name)) { goto done; } -#endif #ifdef DEBUG_JOYSTICK SDL_Log("Joystick: %s, descriptor %s, vendor = 0x%.4x, product = 0x%.4x, %d axes, %d hats\n", name, desc, vendor_id, product_id, naxes, nhats); @@ -482,6 +479,12 @@ static void ANDROID_JoystickDetect(void) } } +static SDL_bool ANDROID_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + /* We don't override any other drivers */ + return SDL_FALSE; +} + static SDL_joylist_item *GetJoystickByDevIndex(int device_index) { SDL_joylist_item *item = SDL_joylist; @@ -646,6 +649,7 @@ SDL_JoystickDriver SDL_ANDROID_JoystickDriver = { ANDROID_JoystickInit, ANDROID_JoystickGetCount, ANDROID_JoystickDetect, + ANDROID_JoystickIsDevicePresent, ANDROID_JoystickGetDeviceName, ANDROID_JoystickGetDevicePath, ANDROID_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/apple/SDL_mfijoystick.m b/src/joystick/apple/SDL_mfijoystick.m index 9f43a6e6398fe..c497fc606428a 100644 --- a/src/joystick/apple/SDL_mfijoystick.m +++ b/src/joystick/apple/SDL_mfijoystick.m @@ -879,6 +879,12 @@ static void IOS_JoystickDetect(void) { } +static SDL_bool IOS_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + /* We don't override any other drivers through this method */ + return SDL_FALSE; +} + static const char *IOS_JoystickGetDeviceName(int device_index) { SDL_JoystickDeviceItem *device = GetDeviceForIndex(device_index); @@ -2079,6 +2085,7 @@ static void GetAppleSFSymbolsNameForElement(GCControllerElement *element, char * IOS_JoystickInit, IOS_JoystickGetCount, IOS_JoystickDetect, + IOS_JoystickIsDevicePresent, IOS_JoystickGetDeviceName, IOS_JoystickGetDevicePath, IOS_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/bsd/SDL_bsdjoystick.c b/src/joystick/bsd/SDL_bsdjoystick.c index 647015c470b2f..c5c9245942432 100644 --- a/src/joystick/bsd/SDL_bsdjoystick.c +++ b/src/joystick/bsd/SDL_bsdjoystick.c @@ -427,15 +427,8 @@ static int MaybeAddDevice(const char *path) name = SDL_CreateJoystickName(di.udi_vendorNo, di.udi_productNo, di.udi_vendor, di.udi_product); guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_USB, di.udi_vendorNo, di.udi_productNo, di.udi_releaseNo, di.udi_vendor, di.udi_product, 0, 0); -#ifdef SDL_JOYSTICK_HIDAPI - if (HIDAPI_IsDevicePresent(di.udi_vendorNo, di.udi_productNo, di.udi_releaseNo, name)) { - /* The HIDAPI driver is taking care of this device */ - SDL_free(name); - FreeHwData(hw); - return -1; - } -#endif - if (SDL_ShouldIgnoreJoystick(name, guid)) { + if (SDL_ShouldIgnoreJoystick(name, guid) || + SDL_JoystickHandledByAnotherDriver(&SDL_BSD_JoystickDriver, di.udi_vendorNo, di.udi_productNo, di.udi_releaseNo, name)) { SDL_free(name); FreeHwData(hw); return -1; @@ -516,6 +509,12 @@ static void BSD_JoystickDetect(void) { } +static SDL_bool BSD_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + /* We don't override any other drivers */ + return SDL_FALSE; +} + static SDL_joylist_item *GetJoystickByDevIndex(int device_index) { SDL_joylist_item *item = SDL_joylist; @@ -848,6 +847,7 @@ SDL_JoystickDriver SDL_BSD_JoystickDriver = { BSD_JoystickInit, BSD_JoystickGetCount, BSD_JoystickDetect, + BSD_JoystickIsDevicePresent, BSD_JoystickGetDeviceName, BSD_JoystickGetDevicePath, BSD_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/darwin/SDL_iokitjoystick.c b/src/joystick/darwin/SDL_iokitjoystick.c index 9900ecf446806..99c9565fd2dc3 100644 --- a/src/joystick/darwin/SDL_iokitjoystick.c +++ b/src/joystick/darwin/SDL_iokitjoystick.c @@ -490,12 +490,9 @@ static SDL_bool GetDeviceInfo(IOHIDDeviceRef hidDevice, recDevice *pDevice) SDL_free(name); } -#ifdef SDL_JOYSTICK_HIDAPI - if (HIDAPI_IsDevicePresent(vendor, product, version, pDevice->product)) { - /* The HIDAPI driver is taking care of this device */ + if (SDL_JoystickHandledByAnotherDriver(&SDL_DARWIN_JoystickDriver, vendor, product, version, pDevice->product)) { return SDL_FALSE; } -#endif pDevice->guid = SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_USB, (Uint16)vendor, (Uint16)product, (Uint16)version, manufacturer_string, product_string, 0, 0); pDevice->steam_virtual_gamepad_slot = GetSteamVirtualGamepadSlot((Uint16)vendor, (Uint16)product, product_string); @@ -714,13 +711,19 @@ static void DARWIN_JoystickDetect(void) } } -const char *DARWIN_JoystickGetDeviceName(int device_index) +static SDL_bool DARWIN_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + /* We don't override any other drivers */ + return SDL_FALSE; +} + +static const char *DARWIN_JoystickGetDeviceName(int device_index) { recDevice *device = GetDeviceForIndex(device_index); return device ? device->product : "UNKNOWN"; } -const char *DARWIN_JoystickGetDevicePath(int device_index) +static const char *DARWIN_JoystickGetDevicePath(int device_index) { return NULL; } @@ -1063,6 +1066,7 @@ SDL_JoystickDriver SDL_DARWIN_JoystickDriver = { DARWIN_JoystickInit, DARWIN_JoystickGetCount, DARWIN_JoystickDetect, + DARWIN_JoystickIsDevicePresent, DARWIN_JoystickGetDeviceName, DARWIN_JoystickGetDevicePath, DARWIN_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/dummy/SDL_sysjoystick.c b/src/joystick/dummy/SDL_sysjoystick.c index 0d6a850d2c09f..9338757507ee2 100644 --- a/src/joystick/dummy/SDL_sysjoystick.c +++ b/src/joystick/dummy/SDL_sysjoystick.c @@ -41,6 +41,11 @@ static void DUMMY_JoystickDetect(void) { } +static SDL_bool DUMMY_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + return SDL_FALSE; +} + static const char *DUMMY_JoystickGetDeviceName(int device_index) { return NULL; @@ -128,6 +133,7 @@ SDL_JoystickDriver SDL_DUMMY_JoystickDriver = { DUMMY_JoystickInit, DUMMY_JoystickGetCount, DUMMY_JoystickDetect, + DUMMY_JoystickIsDevicePresent, DUMMY_JoystickGetDeviceName, DUMMY_JoystickGetDevicePath, DUMMY_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/emscripten/SDL_sysjoystick.c b/src/joystick/emscripten/SDL_sysjoystick.c index a9be05618f96a..e4a5603a9fc04 100644 --- a/src/joystick/emscripten/SDL_sysjoystick.c +++ b/src/joystick/emscripten/SDL_sysjoystick.c @@ -259,6 +259,12 @@ static void EMSCRIPTEN_JoystickDetect(void) { } +static SDL_bool EMSCRIPTEN_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + /* We don't override any other drivers */ + return SDL_FALSE; +} + static const char *EMSCRIPTEN_JoystickGetDeviceName(int device_index) { return JoystickByDeviceIndex(device_index)->name; @@ -414,6 +420,7 @@ SDL_JoystickDriver SDL_EMSCRIPTEN_JoystickDriver = { EMSCRIPTEN_JoystickInit, EMSCRIPTEN_JoystickGetCount, EMSCRIPTEN_JoystickDetect, + EMSCRIPTEN_JoystickIsDevicePresent, EMSCRIPTEN_JoystickGetDeviceName, EMSCRIPTEN_JoystickGetDevicePath, EMSCRIPTEN_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c index 230f5c96ff8de..c90635e6611f2 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.c +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -24,11 +24,6 @@ #if defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT -/* Set up for C function definitions, even when using C++ */ -#ifdef __cplusplus -extern "C" { -#endif - /* Public APIs: GAMEINPUT_Joystick... */ /* Private APIs: GAMEINPUT_InternalJoystick... */ @@ -38,6 +33,8 @@ typedef struct GAMEINPUT_InternalDevice { IGameInputDevice *device; const char *deviceName; /* this is a constant string literal */ + Uint16 vendor; + Uint16 product; SDL_JoystickGUID joystickGuid; /* generated by SDL. */ SDL_JoystickID instanceId; /* generated by SDL. */ int playerIndex; @@ -71,6 +68,10 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) GAMEINPUT_InternalDevice **devicelist = NULL; GAMEINPUT_InternalDevice *elem = NULL; const GameInputDeviceInfo *devinfo = NULL; + Uint16 bus = SDL_HARDWARE_BUS_USB; + Uint16 vendor = 0; + Uint16 product = 0; + Uint16 version = 0; char tmpbuff[4]; int idx = 0; @@ -96,33 +97,34 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) return SDL_OutOfMemory(); } + devicelist = (GAMEINPUT_InternalDevice **)SDL_realloc(g_GameInputList.devices, sizeof(elem) * (g_GameInputList.count + 1LL)); + if (!devicelist) { + SDL_free(elem); + return SDL_OutOfMemory(); + } + /* generate a device name */ for (idx = 0; idx < APP_LOCAL_DEVICE_ID_SIZE; ++idx) { SDL_snprintf(tmpbuff, SDL_arraysize(tmpbuff), "%02hhX", devinfo->deviceId.value[idx]); SDL_strlcat(elem->devicePath, tmpbuff, SDL_arraysize(tmpbuff)); } - - devicelist = (GAMEINPUT_InternalDevice **)SDL_realloc(g_GameInputList.devices, sizeof(elem) * (g_GameInputList.count + 1LL)); - if (!devicelist) { - SDL_free(elem); - return SDL_OutOfMemory(); + if (devinfo->capabilities & GameInputDeviceCapabilityWireless) { + bus = SDL_HARDWARE_BUS_BLUETOOTH; + } else { + bus = SDL_HARDWARE_BUS_USB; } + vendor = devinfo->vendorId; + product = devinfo->productId; + version = (devinfo->major << 8) | devinfo->minor; g_GameInputList.devices = devicelist; IGameInputDevice_AddRef(pDevice); elem->device = pDevice; elem->deviceName = "GameInput Gamepad"; + elem->vendor = vendor; + elem->product = product; elem->supportedRumbleMotors = devinfo->supportedRumbleMotors; - elem->joystickGuid = SDL_CreateJoystickGUID( - SDL_HARDWARE_BUS_BLUETOOTH, - USB_VENDOR_MICROSOFT, - USB_PRODUCT_XBOX_SERIES_X_BLE, - 1, - "GameInput", - "Gamepad", - 'g', - 0 - ); + elem->joystickGuid = SDL_CreateJoystickGUID(bus, vendor, product, version, "GameInput", "Gamepad", 'g', 0); elem->instanceId = SDL_GetNextObjectID(); g_GameInputList.devices[g_GameInputList.count] = elem; @@ -274,6 +276,20 @@ static void GAMEINPUT_JoystickDetect(void) } } +static SDL_bool GAMEINPUT_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + int idx = 0; + GAMEINPUT_InternalDevice *elem = NULL; + + for (idx = 0; idx < g_GameInputList.count; ++idx) { + elem = g_GameInputList.devices[idx]; + if (elem && vendor_id == elem->vendor && product_id == elem->product) { + return SDL_TRUE; + } + } + return SDL_FALSE; +} + static const char *GAMEINPUT_JoystickGetDeviceName(int device_index) { GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); @@ -312,7 +328,7 @@ static int GAMEINPUT_JoystickGetDevicePlayerIndex(int device_index) * you're meant to assign some index to a player yourself. * * GameMaker, for example, seems to do this in the order of plugging in. - * + * * Sorry for the trouble! */ GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); @@ -566,6 +582,7 @@ SDL_JoystickDriver SDL_GAMEINPUT_JoystickDriver = GAMEINPUT_JoystickInit, GAMEINPUT_JoystickGetCount, GAMEINPUT_JoystickDetect, + GAMEINPUT_JoystickIsDevicePresent, GAMEINPUT_JoystickGetDeviceName, GAMEINPUT_JoystickGetDevicePath, GAMEINPUT_JoystickGetDeviceSteamVirtualGamepadSlot, @@ -586,9 +603,4 @@ SDL_JoystickDriver SDL_GAMEINPUT_JoystickDriver = }; -/* Ends C function definitions when using C++ */ -#ifdef __cplusplus -} -#endif - #endif /* defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT */ diff --git a/src/joystick/haiku/SDL_haikujoystick.cc b/src/joystick/haiku/SDL_haikujoystick.cc index 77961036ecb30..db40918c047f6 100644 --- a/src/joystick/haiku/SDL_haikujoystick.cc +++ b/src/joystick/haiku/SDL_haikujoystick.cc @@ -91,6 +91,12 @@ extern "C" { } + static SDL_bool HAIKU_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) + { + /* We don't override any other drivers */ + return SDL_FALSE; + } + static const char *HAIKU_JoystickGetDeviceName(int device_index) { return SDL_joyname[device_index]; @@ -293,6 +299,7 @@ extern "C" HAIKU_JoystickInit, HAIKU_JoystickGetCount, HAIKU_JoystickDetect, + HAIKU_JoystickIsDevicePresent, HAIKU_JoystickGetDeviceName, HAIKU_JoystickGetDevicePath, HAIKU_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c index e0a9adb598589..0fadbe4aa0e31 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/src/joystick/hidapi/SDL_hidapijoystick.c @@ -1699,6 +1699,7 @@ SDL_JoystickDriver SDL_HIDAPI_JoystickDriver = { HIDAPI_JoystickInit, HIDAPI_JoystickGetCount, HIDAPI_JoystickDetect, + HIDAPI_IsDevicePresent, HIDAPI_JoystickGetDeviceName, HIDAPI_JoystickGetDevicePath, HIDAPI_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c index 0c8573a1a7329..17935745bb0cb 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/src/joystick/linux/SDL_sysjoystick.c @@ -310,14 +310,11 @@ static int IsJoystick(const char *path, int fd, char **name_return, Uint16 *vend return 0; } -#ifdef SDL_JOYSTICK_HIDAPI if (!IsVirtualJoystick(inpid.vendor, inpid.product, inpid.version, name) && - HIDAPI_IsDevicePresent(inpid.vendor, inpid.product, inpid.version, name)) { - /* The HIDAPI driver is taking care of this device */ + SDL_JoystickHandledByAnotherDriver(&SDL_LINUX_JoystickDriver, inpid.vendor, inpid.product, inpid.version, name)) { SDL_free(name); return 0; } -#endif FixupDeviceInfoForMapping(fd, &inpid); @@ -987,6 +984,12 @@ static void LINUX_JoystickDetect(void) SDL_UpdateSteamControllers(); } +static SDL_bool LINUX_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + /* We don't override any other drivers */ + return SDL_FALSE; +} + static int LINUX_JoystickInit(void) { const char *devices = SDL_GetHint(SDL_HINT_JOYSTICK_DEVICE); @@ -2686,6 +2689,7 @@ SDL_JoystickDriver SDL_LINUX_JoystickDriver = { LINUX_JoystickInit, LINUX_JoystickGetCount, LINUX_JoystickDetect, + LINUX_JoystickIsDevicePresent, LINUX_JoystickGetDeviceName, LINUX_JoystickGetDevicePath, LINUX_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/n3ds/SDL_sysjoystick.c b/src/joystick/n3ds/SDL_sysjoystick.c index 863a1c174782e..271b2627fdb8c 100644 --- a/src/joystick/n3ds/SDL_sysjoystick.c +++ b/src/joystick/n3ds/SDL_sysjoystick.c @@ -226,6 +226,12 @@ static void N3DS_JoystickDetect(void) { } +static SDL_bool N3DS_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + /* We don't override any other drivers */ + return SDL_FALSE; +} + static const char *N3DS_JoystickGetDevicePath(int device_index) { return NULL; @@ -266,26 +272,27 @@ static int N3DS_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int } SDL_JoystickDriver SDL_N3DS_JoystickDriver = { - .Init = N3DS_JoystickInit, - .GetCount = N3DS_JoystickGetCount, - .Detect = N3DS_JoystickDetect, - .GetDeviceName = N3DS_JoystickGetDeviceName, - .GetDevicePath = N3DS_JoystickGetDevicePath, - .GetDeviceSteamVirtualGamepadSlot = N3DS_JoystickGetDeviceSteamVirtualGamepadSlot, - .GetDevicePlayerIndex = N3DS_JoystickGetDevicePlayerIndex, - .SetDevicePlayerIndex = N3DS_JoystickSetDevicePlayerIndex, - .GetDeviceGUID = N3DS_JoystickGetDeviceGUID, - .GetDeviceInstanceID = N3DS_JoystickGetDeviceInstanceID, - .Open = N3DS_JoystickOpen, - .Rumble = N3DS_JoystickRumble, - .RumbleTriggers = N3DS_JoystickRumbleTriggers, - .SetLED = N3DS_JoystickSetLED, - .SendEffect = N3DS_JoystickSendEffect, - .SetSensorsEnabled = N3DS_JoystickSetSensorsEnabled, - .Update = N3DS_JoystickUpdate, - .Close = N3DS_JoystickClose, - .Quit = N3DS_JoystickQuit, - .GetGamepadMapping = N3DS_JoystickGetGamepadMapping + N3DS_JoystickInit, + N3DS_JoystickGetCount, + N3DS_JoystickDetect, + N3DS_JoystickIsDevicePresent, + N3DS_JoystickGetDeviceName, + N3DS_JoystickGetDevicePath, + N3DS_JoystickGetDeviceSteamVirtualGamepadSlot, + N3DS_JoystickGetDevicePlayerIndex, + N3DS_JoystickSetDevicePlayerIndex, + N3DS_JoystickGetDeviceGUID, + N3DS_JoystickGetDeviceInstanceID, + N3DS_JoystickOpen, + N3DS_JoystickRumble, + N3DS_JoystickRumbleTriggers, + N3DS_JoystickSetLED, + N3DS_JoystickSendEffect, + N3DS_JoystickSetSensorsEnabled, + N3DS_JoystickUpdate, + N3DS_JoystickClose, + N3DS_JoystickQuit, + N3DS_JoystickGetGamepadMapping }; #endif /* SDL_JOYSTICK_N3DS */ diff --git a/src/joystick/ps2/SDL_sysjoystick.c b/src/joystick/ps2/SDL_sysjoystick.c index ad264584e6c69..cf54852f15013 100644 --- a/src/joystick/ps2/SDL_sysjoystick.c +++ b/src/joystick/ps2/SDL_sysjoystick.c @@ -140,6 +140,12 @@ static void PS2_JoystickDetect() { } +static SDL_bool PS2_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + /* We don't override any other drivers */ + return SDL_FALSE; +} + /* Function to get the device-dependent name of a joystick */ static const char *PS2_JoystickGetDeviceName(int index) { @@ -341,6 +347,7 @@ SDL_JoystickDriver SDL_PS2_JoystickDriver = { PS2_JoystickInit, PS2_JoystickGetCount, PS2_JoystickDetect, + PS2_JoystickIsDevicePresent, PS2_JoystickGetDeviceName, PS2_JoystickGetDevicePath, PS2_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/psp/SDL_sysjoystick.c b/src/joystick/psp/SDL_sysjoystick.c index 2ae252865b99c..31cbb8a7129a4 100644 --- a/src/joystick/psp/SDL_sysjoystick.c +++ b/src/joystick/psp/SDL_sysjoystick.c @@ -105,6 +105,12 @@ static void PSP_JoystickDetect(void) { } +static SDL_bool PSP_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + /* We don't override any other drivers */ + return SDL_FALSE; +} + /* Function to get the device-dependent name of a joystick */ static const char *PSP_JoystickGetDeviceName(int device_index) { @@ -251,6 +257,7 @@ SDL_JoystickDriver SDL_PSP_JoystickDriver = { PSP_JoystickInit, PSP_JoystickGetCount, PSP_JoystickDetect, + PSP_JoystickIsDevicePresent, PSP_JoystickGetDeviceName, PSP_JoystickGetDevicePath, PSP_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/virtual/SDL_virtualjoystick.c b/src/joystick/virtual/SDL_virtualjoystick.c index 526ec24cc9bf0..6c014673f09be 100644 --- a/src/joystick/virtual/SDL_virtualjoystick.c +++ b/src/joystick/virtual/SDL_virtualjoystick.c @@ -353,6 +353,12 @@ static void VIRTUAL_JoystickDetect(void) { } +static SDL_bool VIRTUAL_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + /* We don't override any other drivers... or do we? */ + return SDL_FALSE; +} + static const char *VIRTUAL_JoystickGetDeviceName(int device_index) { joystick_hwdata *hwdata = VIRTUAL_HWDataForIndex(device_index); @@ -749,6 +755,7 @@ SDL_JoystickDriver SDL_VIRTUAL_JoystickDriver = { VIRTUAL_JoystickInit, VIRTUAL_JoystickGetCount, VIRTUAL_JoystickDetect, + VIRTUAL_JoystickIsDevicePresent, VIRTUAL_JoystickGetDeviceName, VIRTUAL_JoystickGetDevicePath, VIRTUAL_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/vita/SDL_sysjoystick.c b/src/joystick/vita/SDL_sysjoystick.c index 35ec7ebdd89ae..58fef17884191 100644 --- a/src/joystick/vita/SDL_sysjoystick.c +++ b/src/joystick/vita/SDL_sysjoystick.c @@ -101,7 +101,7 @@ static int calc_bezier_y(float t) * Joystick 0 should be the system default joystick. * It should return number of joysticks, or -1 on an unrecoverable fatal error. */ -int VITA_JoystickInit(void) +static int VITA_JoystickInit(void) { int i; SceCtrlPortInfo myPortInfo; @@ -139,22 +139,28 @@ int VITA_JoystickInit(void) return SDL_numjoysticks; } -int VITA_JoystickGetCount() +static int VITA_JoystickGetCount() { return SDL_numjoysticks; } -void VITA_JoystickDetect() +static void VITA_JoystickDetect() { } +static SDL_bool VITA_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + /* We don't override any other drivers */ + return SDL_FALSE; +} + /* Function to perform the mapping from device index to the instance id for this index */ -SDL_JoystickID VITA_JoystickGetDeviceInstanceID(int device_index) +static SDL_JoystickID VITA_JoystickGetDeviceInstanceID(int device_index) { return device_index + 1; } -const char *VITA_JoystickGetDeviceName(int index) +static const char *VITA_JoystickGetDeviceName(int index) { if (index == 0) { return "PSVita Controller"; @@ -176,7 +182,7 @@ const char *VITA_JoystickGetDeviceName(int index) return NULL; } -const char *VITA_JoystickGetDevicePath(int index) +static const char *VITA_JoystickGetDevicePath(int index) { return NULL; } @@ -200,7 +206,7 @@ static void VITA_JoystickSetDevicePlayerIndex(int device_index, int player_index This should fill the nbuttons and naxes fields of the joystick structure. It returns 0, or -1 if there is an error. */ -int VITA_JoystickOpen(SDL_Joystick *joystick, int device_index) +static int VITA_JoystickOpen(SDL_Joystick *joystick, int device_index) { joystick->nbuttons = SDL_arraysize(ext_button_map); joystick->naxes = 6; @@ -309,16 +315,16 @@ static void VITA_JoystickUpdate(SDL_Joystick *joystick) } /* Function to close a joystick after use */ -void VITA_JoystickClose(SDL_Joystick *joystick) +static void VITA_JoystickClose(SDL_Joystick *joystick) { } /* Function to perform any system-specific joystick related cleanup */ -void VITA_JoystickQuit(void) +static void VITA_JoystickQuit(void) { } -SDL_JoystickGUID VITA_JoystickGetDeviceGUID(int device_index) +static SDL_JoystickGUID VITA_JoystickGetDeviceGUID(int device_index) { /* the GUID is just the name for now */ const char *name = VITA_JoystickGetDeviceName(device_index); @@ -378,6 +384,7 @@ SDL_JoystickDriver SDL_VITA_JoystickDriver = { VITA_JoystickInit, VITA_JoystickGetCount, VITA_JoystickDetect, + VITA_JoystickIsDevicePresent, VITA_JoystickGetDeviceName, VITA_JoystickGetDevicePath, VITA_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/windows/SDL_dinputjoystick.c b/src/joystick/windows/SDL_dinputjoystick.c index 150f59be22e65..08d9fdebc134a 100644 --- a/src/joystick/windows/SDL_dinputjoystick.c +++ b/src/joystick/windows/SDL_dinputjoystick.c @@ -516,13 +516,7 @@ static BOOL CALLBACK EnumJoystickDetectCallback(LPCDIDEVICEINSTANCE pDeviceInsta CHECK(!SDL_ShouldIgnoreJoystick(pNewJoystick->joystickname, pNewJoystick->guid)); -#ifdef SDL_JOYSTICK_HIDAPI - CHECK(!HIDAPI_IsDevicePresent(vendor, product, version, pNewJoystick->joystickname)); -#endif - -#ifdef SDL_JOYSTICK_RAWINPUT - CHECK(!RAWINPUT_IsDevicePresent(vendor, product, version, pNewJoystick->joystickname)); -#endif + CHECK(!SDL_JoystickHandledByAnotherDriver(&SDL_WINDOWS_JoystickDriver, vendor, product, version, pNewJoystick->joystickname)); WINDOWS_AddJoystickDevice(pNewJoystick); pNewJoystick = NULL; diff --git a/src/joystick/windows/SDL_rawinputjoystick.c b/src/joystick/windows/SDL_rawinputjoystick.c index 671e3a376d39e..e59df1d9740fe 100644 --- a/src/joystick/windows/SDL_rawinputjoystick.c +++ b/src/joystick/windows/SDL_rawinputjoystick.c @@ -886,10 +886,7 @@ static void RAWINPUT_AddDevice(HANDLE hDevice) CHECK(GetRawInputDeviceInfoA(hDevice, RIDI_DEVICENAME, dev_name, &size) != (UINT)-1); /* Only take XInput-capable devices */ CHECK(SDL_strstr(dev_name, "IG_") != NULL); -#ifdef SDL_JOYSTICK_HIDAPI - /* Don't take devices handled by HIDAPI */ - CHECK(!HIDAPI_IsDevicePresent((Uint16)rdi.hid.dwVendorId, (Uint16)rdi.hid.dwProductId, (Uint16)rdi.hid.dwVersionNumber, "")); -#endif + CHECK(!SDL_JoystickHandledByAnotherDriver(&SDL_RAWINPUT_JoystickDriver, (Uint16)rdi.hid.dwVendorId, (Uint16)rdi.hid.dwProductId, (Uint16)rdi.hid.dwVersionNumber, "")); device = (SDL_RAWINPUT_Device *)SDL_calloc(1, sizeof(SDL_RAWINPUT_Device)); CHECK(device); device->hDevice = hDevice; @@ -1062,42 +1059,6 @@ SDL_bool RAWINPUT_IsEnabled() return SDL_RAWINPUT_inited && !SDL_RAWINPUT_remote_desktop; } -SDL_bool RAWINPUT_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) -{ - SDL_RAWINPUT_Device *device; - - /* If we're being asked about a device, that means another API just detected one, so rescan */ -#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT - xinput_device_change = SDL_TRUE; -#endif - - device = SDL_RAWINPUT_devices; - while (device) { - if (vendor_id == device->vendor_id && product_id == device->product_id) { - return SDL_TRUE; - } - - /* The Xbox 360 wireless controller shows up as product 0 in WGI. - Try to match it to a Raw Input device via name or known product ID. */ - if (vendor_id == device->vendor_id && product_id == 0 && - ((name && SDL_strstr(device->name, name) != NULL) || - (device->vendor_id == USB_VENDOR_MICROSOFT && - device->product_id == USB_PRODUCT_XBOX360_XUSB_CONTROLLER))) { - return SDL_TRUE; - } - - /* The Xbox One controller shows up as a hardcoded raw input VID/PID */ - if (name && SDL_strcmp(name, "Xbox One Game Controller") == 0 && - device->vendor_id == USB_VENDOR_MICROSOFT && - device->product_id == USB_PRODUCT_XBOX_ONE_XBOXGIP_CONTROLLER) { - return SDL_TRUE; - } - - device = device->next; - } - return SDL_FALSE; -} - static void RAWINPUT_PostUpdate(void) { #ifdef SDL_JOYSTICK_RAWINPUT_MATCHING @@ -1181,6 +1142,42 @@ static void RAWINPUT_JoystickDetect(void) RAWINPUT_PostUpdate(); } +static SDL_bool RAWINPUT_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + SDL_RAWINPUT_Device *device; + + /* If we're being asked about a device, that means another API just detected one, so rescan */ +#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT + xinput_device_change = SDL_TRUE; +#endif + + device = SDL_RAWINPUT_devices; + while (device) { + if (vendor_id == device->vendor_id && product_id == device->product_id) { + return SDL_TRUE; + } + + /* The Xbox 360 wireless controller shows up as product 0 in WGI. + Try to match it to a Raw Input device via name or known product ID. */ + if (vendor_id == device->vendor_id && product_id == 0 && + ((name && SDL_strstr(device->name, name) != NULL) || + (device->vendor_id == USB_VENDOR_MICROSOFT && + device->product_id == USB_PRODUCT_XBOX360_XUSB_CONTROLLER))) { + return SDL_TRUE; + } + + /* The Xbox One controller shows up as a hardcoded raw input VID/PID */ + if (name && SDL_strcmp(name, "Xbox One Game Controller") == 0 && + device->vendor_id == USB_VENDOR_MICROSOFT && + device->product_id == USB_PRODUCT_XBOX_ONE_XBOXGIP_CONTROLLER) { + return SDL_TRUE; + } + + device = device->next; + } + return SDL_FALSE; +} + static SDL_RAWINPUT_Device *RAWINPUT_GetDeviceByIndex(int device_index) { SDL_RAWINPUT_Device *device = SDL_RAWINPUT_devices; @@ -2206,6 +2203,7 @@ SDL_JoystickDriver SDL_RAWINPUT_JoystickDriver = { RAWINPUT_JoystickInit, RAWINPUT_JoystickGetCount, RAWINPUT_JoystickDetect, + RAWINPUT_JoystickIsDevicePresent, RAWINPUT_JoystickGetDeviceName, RAWINPUT_JoystickGetDevicePath, RAWINPUT_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/windows/SDL_rawinputjoystick_c.h b/src/joystick/windows/SDL_rawinputjoystick_c.h index d4f9e79fb6a4a..510bc8ae3a320 100644 --- a/src/joystick/windows/SDL_rawinputjoystick_c.h +++ b/src/joystick/windows/SDL_rawinputjoystick_c.h @@ -24,9 +24,6 @@ /* Return true if the RawInput driver is enabled */ extern SDL_bool RAWINPUT_IsEnabled(); -/* Return true if a RawInput device is present and supported as a joystick */ -extern SDL_bool RAWINPUT_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name); - /* Registers for input events */ extern int RAWINPUT_RegisterNotifications(HWND hWnd); extern int RAWINPUT_UnregisterNotifications(); diff --git a/src/joystick/windows/SDL_windows_gaming_input.c b/src/joystick/windows/SDL_windows_gaming_input.c index 61e8b2c417696..af788cd920df7 100644 --- a/src/joystick/windows/SDL_windows_gaming_input.c +++ b/src/joystick/windows/SDL_windows_gaming_input.c @@ -106,114 +106,8 @@ DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameController2, 0x43c0c035 DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics, 0xeb8d0792, 0xe95a, 0x4b19, 0xaf, 0xc7, 0x0a, 0x59, 0xf8, 0xbf, 0x75, 0x9e); extern SDL_bool SDL_XINPUT_Enabled(void); -extern SDL_bool SDL_DINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version); -static SDL_bool SDL_IsXInputDevice(Uint16 vendor, Uint16 product) -{ -#if defined(SDL_JOYSTICK_XINPUT) || defined(SDL_JOYSTICK_RAWINPUT) - PRAWINPUTDEVICELIST raw_devices = NULL; - UINT i, raw_device_count = 0; - LONG vidpid = MAKELONG(vendor, product); - - /* XInput and RawInput backends will pick up XInput-compatible devices */ - if (!SDL_XINPUT_Enabled() -#ifdef SDL_JOYSTICK_RAWINPUT - && !RAWINPUT_IsEnabled() -#endif - ) { - return SDL_FALSE; - } - - /* Go through RAWINPUT (WinXP and later) to find HID devices. */ - if ((GetRawInputDeviceList(NULL, &raw_device_count, sizeof(RAWINPUTDEVICELIST)) == -1) || (!raw_device_count)) { - return SDL_FALSE; /* oh well. */ - } - - raw_devices = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * raw_device_count); - if (!raw_devices) { - return SDL_FALSE; - } - - raw_device_count = GetRawInputDeviceList(raw_devices, &raw_device_count, sizeof(RAWINPUTDEVICELIST)); - if (raw_device_count == (UINT)-1) { - SDL_free(raw_devices); - raw_devices = NULL; - return SDL_FALSE; /* oh well. */ - } - - for (i = 0; i < raw_device_count; i++) { - RID_DEVICE_INFO rdi; - char devName[MAX_PATH] = { 0 }; - UINT rdiSize = sizeof(rdi); - UINT nameSize = SDL_arraysize(devName); - DEVINST devNode; - char devVidPidString[32]; - int j; - - rdi.cbSize = sizeof(rdi); - - if ((raw_devices[i].dwType != RIM_TYPEHID) || - (GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) == ((UINT)-1)) || - (GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) == ((UINT)-1)) || - (SDL_strstr(devName, "IG_") == NULL)) { - /* Skip non-XInput devices */ - continue; - } - - /* First check for a simple VID/PID match. This will work for Xbox 360 controllers. */ - if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == vidpid) { - SDL_free(raw_devices); - return SDL_TRUE; - } - - /* For Xbox One controllers, Microsoft doesn't propagate the VID/PID down to the HID stack. - * We'll have to walk the device tree upwards searching for a match for our VID/PID. */ - - /* Make sure the device interface string is something we know how to parse */ - /* Example: \\?\HID#VID_045E&PID_02FF&IG_00#9&2c203035&2&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} */ - if ((SDL_strstr(devName, "\\\\?\\") != devName) || (SDL_strstr(devName, "#{") == NULL)) { - continue; - } - - /* Unescape the backslashes in the string and terminate before the GUID portion */ - for (j = 0; devName[j] != '\0'; j++) { - if (devName[j] == '#') { - if (devName[j + 1] == '{') { - devName[j] = '\0'; - break; - } else { - devName[j] = '\\'; - } - } - } - - /* We'll be left with a string like this: \\?\HID\VID_045E&PID_02FF&IG_00\9&2c203035&2&0000 - * Simply skip the \\?\ prefix and we'll have a properly formed device instance ID */ - if (CM_Locate_DevNodeA(&devNode, &devName[4], CM_LOCATE_DEVNODE_NORMAL) != CR_SUCCESS) { - continue; - } - - (void)SDL_snprintf(devVidPidString, sizeof(devVidPidString), "VID_%04X&PID_%04X", vendor, product); - - while (CM_Get_Parent(&devNode, devNode, 0) == CR_SUCCESS) { - char deviceId[MAX_DEVICE_ID_LEN]; - - if ((CM_Get_Device_IDA(devNode, deviceId, SDL_arraysize(deviceId), 0) == CR_SUCCESS) && - (SDL_strstr(deviceId, devVidPidString) != NULL)) { - /* The VID/PID matched a parent device */ - SDL_free(raw_devices); - return SDL_TRUE; - } - } - } - - SDL_free(raw_devices); -#endif /* SDL_JOYSTICK_XINPUT || SDL_JOYSTICK_RAWINPUT */ - - return SDL_FALSE; -} - static void WGI_LoadRawGameControllerStatics() { HRESULT hr; @@ -448,23 +342,7 @@ static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdde name = SDL_strdup(""); } -#ifdef SDL_JOYSTICK_HIDAPI - if (!ignore_joystick && HIDAPI_IsDevicePresent(vendor, product, version, name)) { - ignore_joystick = SDL_TRUE; - } -#endif - -#ifdef SDL_JOYSTICK_RAWINPUT - if (!ignore_joystick && RAWINPUT_IsDevicePresent(vendor, product, version, name)) { - ignore_joystick = SDL_TRUE; - } -#endif - - if (!ignore_joystick && SDL_DINPUT_JoystickPresent(vendor, product, version)) { - ignore_joystick = SDL_TRUE; - } - - if (!ignore_joystick && SDL_IsXInputDevice(vendor, product)) { + if (!ignore_joystick && SDL_JoystickHandledByAnotherDriver(&SDL_WGI_JoystickDriver, vendor, product, version, name)) { ignore_joystick = SDL_TRUE; } @@ -684,6 +562,12 @@ static void WGI_JoystickDetect(void) { } +static SDL_bool WGI_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + /* We don't override any other drivers */ + return SDL_FALSE; +} + static const char *WGI_JoystickGetDeviceName(int device_index) { return wgi.controllers[device_index].name; @@ -1009,6 +893,7 @@ SDL_JoystickDriver SDL_WGI_JoystickDriver = { WGI_JoystickInit, WGI_JoystickGetCount, WGI_JoystickDetect, + WGI_JoystickIsDevicePresent, WGI_JoystickGetDeviceName, WGI_JoystickGetDevicePath, WGI_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/windows/SDL_windowsjoystick.c b/src/joystick/windows/SDL_windowsjoystick.c index 0aa33797abeb9..415f1d99bdc7d 100644 --- a/src/joystick/windows/SDL_windowsjoystick.c +++ b/src/joystick/windows/SDL_windowsjoystick.c @@ -579,6 +579,17 @@ void WINDOWS_JoystickDetect(void) } } +static SDL_bool WINDOWS_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) +{ + if (SDL_DINPUT_JoystickPresent(vendor_id, product_id, version)) { + return SDL_TRUE; + } + if (SDL_XINPUT_JoystickPresent(vendor_id, product_id, version)) { + return SDL_TRUE; + } + return SDL_FALSE; +} + static const char *WINDOWS_JoystickGetDeviceName(int device_index) { JoyStick_DeviceData *device = SYS_Joystick; @@ -788,6 +799,7 @@ SDL_JoystickDriver SDL_WINDOWS_JoystickDriver = { WINDOWS_JoystickInit, WINDOWS_JoystickGetCount, WINDOWS_JoystickDetect, + WINDOWS_JoystickIsDevicePresent, WINDOWS_JoystickGetDeviceName, WINDOWS_JoystickGetDevicePath, WINDOWS_JoystickGetDeviceSteamVirtualGamepadSlot, diff --git a/src/joystick/windows/SDL_xinputjoystick.c b/src/joystick/windows/SDL_xinputjoystick.c index d16c7803d7c9c..09739eb7c0957 100644 --- a/src/joystick/windows/SDL_xinputjoystick.c +++ b/src/joystick/windows/SDL_xinputjoystick.c @@ -97,6 +97,13 @@ static SDL_bool GetXInputDeviceInfo(Uint8 userid, Uint16 *pVID, Uint16 *pPID, Ui SDL_XINPUT_CAPABILITIES_EX capabilities; if (!XINPUTGETCAPABILITIESEX || XINPUTGETCAPABILITIESEX(1, userid, 0, &capabilities) != ERROR_SUCCESS) { + /* Use a generic VID/PID representing an XInput controller */ + if (pVID) { + *pVID = USB_VENDOR_MICROSOFT; + } + if (pPID) { + *pPID = USB_PRODUCT_XBOX360_XUSB_CONTROLLER; + } return SDL_FALSE; } @@ -199,22 +206,10 @@ static void AddXInputDevice(Uint8 userid, BYTE SubType, JoyStick_DeviceData **pC return; } -#ifdef SDL_JOYSTICK_HIDAPI - /* Since we're guessing about the VID/PID, use a hard-coded VID/PID to represent XInput */ - if (HIDAPI_IsDevicePresent(USB_VENDOR_MICROSOFT, USB_PRODUCT_XBOX360_XUSB_CONTROLLER, version, pNewJoystick->joystickname)) { - /* The HIDAPI driver is taking care of this device */ - SDL_free(pNewJoystick); - return; - } -#endif - -#ifdef SDL_JOYSTICK_RAWINPUT - if (RAWINPUT_IsDevicePresent(vendor, product, version, pNewJoystick->joystickname)) { - /* The RAWINPUT driver is taking care of this device */ + if (SDL_JoystickHandledByAnotherDriver(&SDL_WINDOWS_JoystickDriver, vendor, product, version, pNewJoystick->joystickname)) { SDL_free(pNewJoystick); return; } -#endif WINDOWS_AddJoystickDevice(pNewJoystick); } @@ -237,6 +232,29 @@ void SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext) } } +SDL_bool SDL_XINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version) +{ + int iuserid; + + if (!s_bXInputEnabled) { + return SDL_FALSE; + } + + /* iterate in reverse, so these are in the final list in ascending numeric order. */ + for (iuserid = 0; iuserid < XUSER_MAX_COUNT; ++iuserid) { + const Uint8 userid = (Uint8)iuserid; + Uint16 slot_vendor; + Uint16 slot_product; + Uint16 slot_version; + if (GetXInputDeviceInfo(userid, &slot_vendor, &slot_product, &slot_version)) { + if (vendor == slot_vendor && product == slot_product && version == slot_version) { + return SDL_TRUE; + } + } + } + return SDL_FALSE; +} + int SDL_XINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice) { const Uint8 userId = joystickdevice->XInputUserId; @@ -420,6 +438,11 @@ void SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext) { } +SDL_bool SDL_XINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version) +{ + return SDL_FALSE; +} + int SDL_XINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice) { return SDL_Unsupported(); diff --git a/src/joystick/windows/SDL_xinputjoystick_c.h b/src/joystick/windows/SDL_xinputjoystick_c.h index 8973f8b7c35a2..b244ced3b3300 100644 --- a/src/joystick/windows/SDL_xinputjoystick_c.h +++ b/src/joystick/windows/SDL_xinputjoystick_c.h @@ -30,6 +30,7 @@ extern "C" { extern SDL_bool SDL_XINPUT_Enabled(void); extern int SDL_XINPUT_JoystickInit(void); extern void SDL_XINPUT_JoystickDetect(JoyStick_DeviceData **pContext); +extern SDL_bool SDL_XINPUT_JoystickPresent(Uint16 vendor, Uint16 product, Uint16 version); extern int SDL_XINPUT_JoystickOpen(SDL_Joystick *joystick, JoyStick_DeviceData *joystickdevice); extern int SDL_XINPUT_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble); extern void SDL_XINPUT_JoystickUpdate(SDL_Joystick *joystick); From 698b7deaa269e3ad1751226b7d9ab4b112f30f1f Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 16:33:45 -0800 Subject: [PATCH 014/220] Removed GAMEINPUT_JoystickEffectDataType_HapticFeedback This refers to the HID Simple Haptics spec, which is currently only implemented by the Surface Dial accessory and WMR game controllers, which aren't currently exposed by the GameInput API. We can add support for other effects in the future, but for now we don't need this or the SDL_gameinputjoystick_c.h header. --- VisualC-GDK/SDL/SDL.vcxproj | 1 - VisualC-GDK/SDL/SDL.vcxproj.filters | 1 - VisualC/SDL/SDL.vcxproj | 1 - VisualC/SDL/SDL.vcxproj.filters | 3 -- src/joystick/gdk/SDL_gameinputjoystick.c | 35 ++++------------ src/joystick/gdk/SDL_gameinputjoystick_c.h | 49 ---------------------- 6 files changed, 8 insertions(+), 82 deletions(-) delete mode 100644 src/joystick/gdk/SDL_gameinputjoystick_c.h diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index 9d4ecf0da5f69..19f0a244f1e63 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj +++ b/VisualC-GDK/SDL/SDL.vcxproj @@ -425,7 +425,6 @@ - diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters index c59af5d0a57bf..c656f29be8e73 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj.filters +++ b/VisualC-GDK/SDL/SDL.vcxproj.filters @@ -320,7 +320,6 @@ - diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index c5a508b827461..52563669f0d35 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -347,7 +347,6 @@ - diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index 679a2bc552031..4b685e50a660f 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -549,9 +549,6 @@ haptic\windows - - joystick\gdk - joystick\hidapi diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c index c90635e6611f2..d1e605b993851 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.c +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -20,14 +20,14 @@ */ #include "SDL_internal.h" -#include "SDL_gameinputjoystick_c.h" +#ifdef SDL_JOYSTICK_GAMEINPUT -#if defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT +#include "../SDL_sysjoystick.h" -/* Public APIs: GAMEINPUT_Joystick... */ -/* Private APIs: GAMEINPUT_InternalJoystick... */ +#include +#define COBJMACROS +#include -#include "../usb_ids.h" typedef struct GAMEINPUT_InternalDevice { @@ -115,7 +115,7 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) } vendor = devinfo->vendorId; product = devinfo->productId; - version = (devinfo->major << 8) | devinfo->minor; + version = (devinfo->firmwareVersion.major << 8) | devinfo->firmwareVersion.minor; g_GameInputList.devices = devicelist; IGameInputDevice_AddRef(pDevice); @@ -434,26 +434,7 @@ static int GAMEINPUT_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 gre static int GAMEINPUT_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size) { - HRESULT hR = S_OK; - const GAMEINPUT_JoystickEffectData *effect = NULL; - GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata; - - if (!data || size != sizeof(GAMEINPUT_JoystickEffectData)) { - return SDL_SetError("GAMEINPUT_JoystickSendEffect invalid data or size"); - } - - effect = (const GAMEINPUT_JoystickEffectData *)data; - if (effect->type == GAMEINPUT_JoystickEffectDataType_HapticFeedback) { - hR = IGameInputDevice_SetHapticMotorState(hwdata->devref->device, - effect->hapticFeedbackMotorIndex, - &effect->hapticFeedbackParams - ); - if (FAILED(hR)) { - return SDL_SetError("IGameInputDevice::SetHapticMotorState failure with HRESULT of %08X", hR); - } - } - - return 0; + return SDL_Unsupported(); } static int GAMEINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) @@ -603,4 +584,4 @@ SDL_JoystickDriver SDL_GAMEINPUT_JoystickDriver = }; -#endif /* defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT */ +#endif /* SDL_JOYSTICK_GAMEINPUT */ diff --git a/src/joystick/gdk/SDL_gameinputjoystick_c.h b/src/joystick/gdk/SDL_gameinputjoystick_c.h deleted file mode 100644 index 0b3bc51a63abd..0000000000000 --- a/src/joystick/gdk/SDL_gameinputjoystick_c.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - Simple DirectMedia Layer - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. -*/ -#include "SDL_internal.h" -#include "../SDL_sysjoystick.h" - -#if defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT - -#include -#define COBJMACROS -#include - -typedef enum GAMEINPUT_JoystickEffectDataType -{ - GAMEINPUT_JoystickEffectDataType_HapticFeedback -} GAMEINPUT_JoystickEffectDataType; - -typedef struct GAMEINPUT_JoystickEffectData -{ - GAMEINPUT_JoystickEffectDataType type; - - union - { - struct /* type == GAMEINPUT_JoystickEffectDataType_HapticFeedback */ - { - uint32_t hapticFeedbackMotorIndex; - GameInputHapticFeedbackParams hapticFeedbackParams; - }; - }; -} GAMEINPUT_JoystickEffectData; - -#endif /* defined(SDL_JOYSTICK_GAMEINPUT) && SDL_JOYSTICK_GAMEINPUT */ From eb9a7d97f98d38f4645fe8c084dfc72d0ea814dd Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 16:43:07 -0800 Subject: [PATCH 015/220] The GameInput driver handles Xbox controllers Don't let the raw input driver handle them when GameInput is active --- src/joystick/gdk/SDL_gameinputjoystick.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c index d1e605b993851..64e444f659cab 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.c +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -23,6 +23,7 @@ #ifdef SDL_JOYSTICK_GAMEINPUT #include "../SDL_sysjoystick.h" +#include "../usb_ids.h" #include #define COBJMACROS @@ -281,6 +282,12 @@ static SDL_bool GAMEINPUT_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 produ int idx = 0; GAMEINPUT_InternalDevice *elem = NULL; + if (vendor_id == USB_VENDOR_MICROSOFT && + product_id == USB_PRODUCT_XBOX_ONE_XBOXGIP_CONTROLLER) { + /* The Xbox One controller shows up as a hardcoded raw input VID/PID, which we definitely handle */ + return SDL_TRUE; + } + for (idx = 0; idx < g_GameInputList.count; ++idx) { elem = g_GameInputList.devices[idx]; if (elem && vendor_id == elem->vendor && product_id == elem->product) { From 8f0f14c31227d6635eb01e901b6e26c9db276382 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 17:41:30 -0800 Subject: [PATCH 016/220] Added automatic gamepad mapping for the GameInput driver --- src/joystick/gdk/SDL_gameinputjoystick.c | 145 ++++++++++++++++------- 1 file changed, 105 insertions(+), 40 deletions(-) diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c index 64e444f659cab..b04d2447d41fb 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.c +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -54,7 +54,6 @@ typedef struct joystick_hwdata { GAMEINPUT_InternalDevice *devref; GameInputRumbleParams rumbleParams; - Uint64 lastTimestamp; } GAMEINPUT_InternalJoystickHwdata; @@ -453,52 +452,39 @@ static int GAMEINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) { static WORD s_XInputButtons[] = { - GameInputGamepadA, GameInputGamepadB, GameInputGamepadX, GameInputGamepadY, - GameInputGamepadLeftShoulder, GameInputGamepadRightShoulder, GameInputGamepadView, GameInputGamepadMenu, - GameInputGamepadLeftThumbstick, GameInputGamepadRightThumbstick, - 0 /* Guide button is not supported on Xbox so ignore that... */ + GameInputGamepadA, /* SDL_GAMEPAD_BUTTON_SOUTH */ + GameInputGamepadB, /* SDL_GAMEPAD_BUTTON_EAST */ + GameInputGamepadX, /* SDL_GAMEPAD_BUTTON_WEST */ + GameInputGamepadY, /* SDL_GAMEPAD_BUTTON_NORTH */ + GameInputGamepadView, /* SDL_GAMEPAD_BUTTON_BACK */ + 0, /* The guide button is not available */ + GameInputGamepadMenu, /* SDL_GAMEPAD_BUTTON_START */ + GameInputGamepadLeftThumbstick, /* SDL_GAMEPAD_BUTTON_LEFT_STICK */ + GameInputGamepadRightThumbstick, /* SDL_GAMEPAD_BUTTON_RIGHT_STICK */ + GameInputGamepadLeftShoulder, /* SDL_GAMEPAD_BUTTON_LEFT_SHOULDER */ + GameInputGamepadRightShoulder, /* SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER */ }; Uint8 btnidx = 0, btnstate = 0, hat = 0; GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata; IGameInputDevice *device = hwdata->devref->device; IGameInputReading *reading = NULL; - uint64_t ts = 0; + Uint64 timestamp = SDL_GetTicksNS(); GameInputGamepadState state; - HRESULT hR = IGameInput_GetCurrentReading(g_pGameInput, - GameInputKindGamepad, - device, - &reading - ); + HRESULT hR; + + hR = IGameInput_GetCurrentReading(g_pGameInput, GameInputKindGamepad, device, &reading); if (FAILED(hR)) { /* don't SetError here since there can be a legitimate case when there's no reading avail */ return; } - /* GDKX private docs for GetTimestamp: "The microsecond timestamp describing when the input was made." */ - /* SDL expects a nanosecond timestamp, so I guess US_TO_NS should be used here? */ - ts = SDL_US_TO_NS(IGameInputReading_GetTimestamp(reading)); - - if (((!hwdata->lastTimestamp) || (ts != hwdata->lastTimestamp)) && IGameInputReading_GetGamepadState(reading, &state)) { - /* `state` is now valid */ - -#define tosint16(_TheValue) ((Sint16)(((_TheValue) < 0.0f) ? ((_TheValue) * 32768.0f) : ((_TheValue) * 32767.0f))) - SDL_SendJoystickAxis(ts, joystick, 0, tosint16(state.leftThumbstickX)); - SDL_SendJoystickAxis(ts, joystick, 1, tosint16(state.leftThumbstickY)); - SDL_SendJoystickAxis(ts, joystick, 2, tosint16(state.leftTrigger)); - SDL_SendJoystickAxis(ts, joystick, 3, tosint16(state.rightThumbstickX)); - SDL_SendJoystickAxis(ts, joystick, 4, tosint16(state.rightThumbstickY)); - SDL_SendJoystickAxis(ts, joystick, 5, tosint16(state.rightTrigger)); -#undef tosint16 + /* FIXME: See if we can get the delta between the reading timestamp and current time and apply the offset to timestamp */ - for (btnidx = 0; btnidx < (Uint8)SDL_arraysize(s_XInputButtons); ++btnidx) { - if (s_XInputButtons[btnidx] == 0) { - btnstate = SDL_RELEASED; - } else { - btnstate = (state.buttons & s_XInputButtons[btnidx]) ? SDL_PRESSED : SDL_RELEASED; - } - - SDL_SendJoystickButton(ts, joystick, btnidx, btnstate); + if (IGameInputReading_GetGamepadState(reading, &state)) { + for (btnidx = 0; btnidx < SDL_arraysize(s_XInputButtons); ++btnidx) { + btnstate = (state.buttons & s_XInputButtons[btnidx]) ? SDL_PRESSED : SDL_RELEASED; + SDL_SendJoystickButton(timestamp, joystick, btnidx, btnstate); } if (state.buttons & GameInputGamepadDPadUp) { @@ -513,15 +499,32 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) if (state.buttons & GameInputGamepadDPadRight) { hat |= SDL_HAT_RIGHT; } - SDL_SendJoystickHat(ts, joystick, 0, hat); + SDL_SendJoystickHat(timestamp, joystick, 0, hat); - /* Xbox doesn't let you obtain the power level, pretend we're always full */ - SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL); - - hwdata->lastTimestamp = ts; +#define CONVERT_AXIS(v) (Sint16)(((v) < 0.0f) ? ((v)*32768.0f) : ((v)*32767.0f)) + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, CONVERT_AXIS(state.leftThumbstickX)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, CONVERT_AXIS(-state.leftThumbstickY)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, CONVERT_AXIS(state.rightThumbstickX)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, CONVERT_AXIS(-state.rightThumbstickY)); +#undef CONVERT_AXIS +#define CONVERT_TRIGGER(v) (Sint16)((v)*65535.0f - 32768.0f) + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, CONVERT_TRIGGER(state.leftTrigger)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, CONVERT_TRIGGER(state.rightTrigger)); +#undef CONVERT_TRIGGER } IGameInputReading_Release(reading); + +#if 0 + /* FIXME: We can poll this at a much lower rate */ + GameInputBatteryState battery_state; + SDL_zero(battery_state); + IGameInputDevice_GetBatteryState(device, &battery_state); + + + /* Xbox doesn't let you obtain the power level, pretend we're always full */ + SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL); +#endif } static void GAMEINPUT_JoystickClose(SDL_Joystick* joystick) @@ -561,7 +564,69 @@ static void GAMEINPUT_JoystickQuit(void) static SDL_bool GAMEINPUT_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) { - return SDL_FALSE; + out->a.kind = EMappingKind_Button; + out->a.target = SDL_GAMEPAD_BUTTON_SOUTH; + + out->b.kind = EMappingKind_Button; + out->b.target = SDL_GAMEPAD_BUTTON_EAST; + + out->x.kind = EMappingKind_Button; + out->x.target = SDL_GAMEPAD_BUTTON_WEST; + + out->y.kind = EMappingKind_Button; + out->y.target = SDL_GAMEPAD_BUTTON_NORTH; + + out->back.kind = EMappingKind_Button; + out->back.target = SDL_GAMEPAD_BUTTON_BACK; + + /* The guide button isn't available, so don't map it */ + + out->start.kind = EMappingKind_Button; + out->start.target = SDL_GAMEPAD_BUTTON_START; + + out->leftstick.kind = EMappingKind_Button; + out->leftstick.target = SDL_GAMEPAD_BUTTON_LEFT_STICK; + + out->rightstick.kind = EMappingKind_Button; + out->rightstick.target = SDL_GAMEPAD_BUTTON_RIGHT_STICK; + + out->leftshoulder.kind = EMappingKind_Button; + out->leftshoulder.target = SDL_GAMEPAD_BUTTON_LEFT_SHOULDER; + + out->rightshoulder.kind = EMappingKind_Button; + out->rightshoulder.target = SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER; + + out->dpup.kind = EMappingKind_Hat; + out->dpup.target = SDL_HAT_UP; + + out->dpdown.kind = EMappingKind_Hat; + out->dpdown.target = SDL_HAT_DOWN; + + out->dpleft.kind = EMappingKind_Hat; + out->dpleft.target = SDL_HAT_LEFT; + + out->dpright.kind = EMappingKind_Hat; + out->dpright.target = SDL_HAT_RIGHT; + + out->leftx.kind = EMappingKind_Axis; + out->leftx.target = SDL_GAMEPAD_AXIS_LEFTX; + + out->lefty.kind = EMappingKind_Axis; + out->lefty.target = SDL_GAMEPAD_AXIS_LEFTY; + + out->rightx.kind = EMappingKind_Axis; + out->rightx.target = SDL_GAMEPAD_AXIS_RIGHTX; + + out->righty.kind = EMappingKind_Axis; + out->righty.target = SDL_GAMEPAD_AXIS_RIGHTY; + + out->lefttrigger.kind = EMappingKind_Axis; + out->lefttrigger.target = SDL_GAMEPAD_AXIS_LEFT_TRIGGER; + + out->righttrigger.kind = EMappingKind_Axis; + out->righttrigger.target = SDL_GAMEPAD_AXIS_RIGHT_TRIGGER; + + return SDL_TRUE; } From fd9a4eff9f9b287a4e87dba4539f439086b0bc89 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 17:52:48 -0800 Subject: [PATCH 017/220] Updated GameInput device info to match other joystick drivers --- src/joystick/gdk/SDL_gameinputjoystick.c | 92 ++++++------------------ 1 file changed, 20 insertions(+), 72 deletions(-) diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c index b04d2447d41fb..c2692c442bf2a 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.c +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -33,15 +33,15 @@ typedef struct GAMEINPUT_InternalDevice { IGameInputDevice *device; - const char *deviceName; /* this is a constant string literal */ + char path[(APP_LOCAL_DEVICE_ID_SIZE * 2) + 1]; + const char *name; /* this is a constant string literal */ Uint16 vendor; Uint16 product; - SDL_JoystickGUID joystickGuid; /* generated by SDL. */ - SDL_JoystickID instanceId; /* generated by SDL. */ - int playerIndex; + SDL_JoystickGUID guid; /* generated by SDL */ + SDL_JoystickID device_instance; /* generated by SDL */ GameInputRumbleMotors supportedRumbleMotors; - char devicePath[(APP_LOCAL_DEVICE_ID_SIZE * 2) + 1]; - SDL_bool isAdded, isDeleteRequested; + SDL_bool isAdded; + SDL_bool isDeleteRequested; } GAMEINPUT_InternalDevice; typedef struct GAMEINPUT_InternalList @@ -72,7 +72,7 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) Uint16 vendor = 0; Uint16 product = 0; Uint16 version = 0; - char tmpbuff[4]; + char tmp[4]; int idx = 0; if (!pDevice) { @@ -105,8 +105,8 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) /* generate a device name */ for (idx = 0; idx < APP_LOCAL_DEVICE_ID_SIZE; ++idx) { - SDL_snprintf(tmpbuff, SDL_arraysize(tmpbuff), "%02hhX", devinfo->deviceId.value[idx]); - SDL_strlcat(elem->devicePath, tmpbuff, SDL_arraysize(tmpbuff)); + SDL_snprintf(tmp, SDL_arraysize(tmp), "%02hhX", devinfo->deviceId.value[idx]); + SDL_strlcat(elem->path, tmp, SDL_arraysize(tmp)); } if (devinfo->capabilities & GameInputDeviceCapabilityWireless) { bus = SDL_HARDWARE_BUS_BLUETOOTH; @@ -120,12 +120,12 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) g_GameInputList.devices = devicelist; IGameInputDevice_AddRef(pDevice); elem->device = pDevice; - elem->deviceName = "GameInput Gamepad"; + elem->name = "GameInput Gamepad"; elem->vendor = vendor; elem->product = product; + elem->guid = SDL_CreateJoystickGUID(bus, vendor, product, version, "GameInput", "Gamepad", 'g', 0); + elem->device_instance = SDL_GetNextObjectID(); elem->supportedRumbleMotors = devinfo->supportedRumbleMotors; - elem->joystickGuid = SDL_CreateJoystickGUID(bus, vendor, product, version, "GameInput", "Gamepad", 'g', 0); - elem->instanceId = SDL_GetNextObjectID(); g_GameInputList.devices[g_GameInputList.count] = elem; /* finally increment the count and return */ @@ -172,11 +172,7 @@ static int GAMEINPUT_InternalRemoveByIndex(int idx) static GAMEINPUT_InternalDevice *GAMEINPUT_InternalFindByIndex(int idx) { - if (idx < 0 || idx >= g_GameInputList.count) { - SDL_SetError("GAMEINPUT_InternalFindByIndex argument idx %d out of range", idx); - return NULL; - } - + /* We're guaranteed that the index is in range when this is called */ return g_GameInputList.devices[idx]; } @@ -265,12 +261,12 @@ static void GAMEINPUT_JoystickDetect(void) } if (!elem->isAdded) { - SDL_PrivateJoystickAdded(elem->instanceId); + SDL_PrivateJoystickAdded(elem->device_instance); elem->isAdded = SDL_TRUE; } if (elem->isDeleteRequested || !(IGameInputDevice_GetDeviceStatus(elem->device) & GameInputDeviceConnected)) { - SDL_PrivateJoystickRemoved(elem->instanceId); + SDL_PrivateJoystickRemoved(elem->device_instance); GAMEINPUT_InternalRemoveByIndex(idx--); } } @@ -298,25 +294,13 @@ static SDL_bool GAMEINPUT_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 produ static const char *GAMEINPUT_JoystickGetDeviceName(int device_index) { - GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); - - if (!elem) { - return NULL; - } - - return elem->deviceName; + return GAMEINPUT_InternalFindByIndex(device_index)->name; } static const char *GAMEINPUT_JoystickGetDevicePath(int device_index) { - GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); - - if (!elem) { - return NULL; - } - /* APP_LOCAL_DEVICE_ID as a hex string, since it's required for some association callbacks */ - return elem->devicePath; + return GAMEINPUT_InternalFindByIndex(device_index)->path; } static int GAMEINPUT_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) @@ -327,57 +311,21 @@ static int GAMEINPUT_JoystickGetDeviceSteamVirtualGamepadSlot(int device_index) static int GAMEINPUT_JoystickGetDevicePlayerIndex(int device_index) { - /* - * Okay, so, while XInput technically has player indicies, - * GameInput does not. It just dispatches a callback whenever a device is found. - * So if you're using true native GameInput (which this backend IS) - * you're meant to assign some index to a player yourself. - * - * GameMaker, for example, seems to do this in the order of plugging in. - * - * Sorry for the trouble! - */ - GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); - - if (!elem) { - return -1; - } - - return elem->playerIndex; + return -1; } static void GAMEINPUT_JoystickSetDevicePlayerIndex(int device_index, int player_index) { - GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); - - if (!elem) { - return; - } - - elem->playerIndex = player_index; } static SDL_JoystickGUID GAMEINPUT_JoystickGetDeviceGUID(int device_index) { - GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); - - if (!elem) { - static SDL_JoystickGUID emptyGUID; - return emptyGUID; - } - - return elem->joystickGuid; + return GAMEINPUT_InternalFindByIndex(device_index)->guid; } static SDL_JoystickID GAMEINPUT_JoystickGetDeviceInstanceID(int device_index) { - GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); - - if (!elem) { - return 0; - } - - return elem->instanceId; + return GAMEINPUT_InternalFindByIndex(device_index)->device_instance; } static int GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) From 4a59b17de2a1d44a7009def62ee22a0e7e55f0e2 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 19:09:29 -0800 Subject: [PATCH 018/220] Added infrastructure for querying battery status for GameInput --- src/joystick/gdk/SDL_gameinputjoystick.c | 34 +++++++++++++++++------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c index c2692c442bf2a..4b5489b4ad2d9 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.c +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -39,6 +39,7 @@ typedef struct GAMEINPUT_InternalDevice Uint16 product; SDL_JoystickGUID guid; /* generated by SDL */ SDL_JoystickID device_instance; /* generated by SDL */ + SDL_bool wireless; GameInputRumbleMotors supportedRumbleMotors; SDL_bool isAdded; SDL_bool isDeleteRequested; @@ -125,6 +126,7 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) elem->product = product; elem->guid = SDL_CreateJoystickGUID(bus, vendor, product, version, "GameInput", "Gamepad", 'g', 0); elem->device_instance = SDL_GetNextObjectID(); + elem->wireless = (devinfo->capabilities & GameInputDeviceCapabilityWireless); elem->supportedRumbleMotors = devinfo->supportedRumbleMotors; g_GameInputList.devices[g_GameInputList.count] = elem; @@ -328,6 +330,19 @@ static SDL_JoystickID GAMEINPUT_JoystickGetDeviceInstanceID(int device_index) return GAMEINPUT_InternalFindByIndex(device_index)->device_instance; } +static SDL_JoystickPowerLevel GAMEINPUT_InternalGetPowerLevel(IGameInputDevice *device) +{ + GameInputBatteryState battery_state; + + SDL_zero(battery_state); + IGameInputDevice_GetBatteryState(device, &battery_state); + + if (battery_state.status == GameInputBatteryDischarging) { + /* FIXME: What are the units for remainingCapacity? */ + } + return SDL_JOYSTICK_POWER_UNKNOWN; +} + static int GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) { GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); @@ -356,6 +371,11 @@ static int GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, SDL_TRUE); } + if (elem->wireless) { + joystick->epowerlevel = GAMEINPUT_InternalGetPowerLevel(elem->device); + } else { + joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; + } return 0; } @@ -463,16 +483,10 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) IGameInputReading_Release(reading); -#if 0 - /* FIXME: We can poll this at a much lower rate */ - GameInputBatteryState battery_state; - SDL_zero(battery_state); - IGameInputDevice_GetBatteryState(device, &battery_state); - - - /* Xbox doesn't let you obtain the power level, pretend we're always full */ - SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL); -#endif + if (joystick->epowerlevel != SDL_JOYSTICK_POWER_WIRED) { + /* FIXME: We can poll this at a much lower rate */ + SDL_SendJoystickBatteryLevel(joystick, GAMEINPUT_InternalGetPowerLevel(device)); + } } static void GAMEINPUT_JoystickClose(SDL_Joystick* joystick) From fbe4153214f546853d97d021b09c8824017194c2 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 19:13:04 -0800 Subject: [PATCH 019/220] The HIDAPI driver takes precedence over the GameInput driver The HIDAPI driver has more functionality for supported controllers. --- src/joystick/gdk/SDL_gameinputjoystick.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c index 4b5489b4ad2d9..bc1c01cbf0fc2 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.c +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -85,6 +85,19 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) return SDL_SetError("GAMEINPUT_InternalAddOrFind GetDeviceInfo returned NULL"); } + if (devinfo->capabilities & GameInputDeviceCapabilityWireless) { + bus = SDL_HARDWARE_BUS_BLUETOOTH; + } else { + bus = SDL_HARDWARE_BUS_USB; + } + vendor = devinfo->vendorId; + product = devinfo->productId; + version = (devinfo->firmwareVersion.major << 8) | devinfo->firmwareVersion.minor; + + if (SDL_JoystickHandledByAnotherDriver(&SDL_GAMEINPUT_JoystickDriver, vendor, product, version, "")) { + return 0; + } + for (idx = 0; idx < g_GameInputList.count; ++idx) { elem = g_GameInputList.devices[idx]; if (elem && elem->device == pDevice) { @@ -109,14 +122,6 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) SDL_snprintf(tmp, SDL_arraysize(tmp), "%02hhX", devinfo->deviceId.value[idx]); SDL_strlcat(elem->path, tmp, SDL_arraysize(tmp)); } - if (devinfo->capabilities & GameInputDeviceCapabilityWireless) { - bus = SDL_HARDWARE_BUS_BLUETOOTH; - } else { - bus = SDL_HARDWARE_BUS_USB; - } - vendor = devinfo->vendorId; - product = devinfo->productId; - version = (devinfo->firmwareVersion.major << 8) | devinfo->firmwareVersion.minor; g_GameInputList.devices = devicelist; IGameInputDevice_AddRef(pDevice); From 85ac0381b7a543542cb1164489a9088c0a6a772d Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 19:18:04 -0800 Subject: [PATCH 020/220] IGameInputDevice::GetDeviceInfo() can never return a null pointer The IGameInputDevice instance can't be internally instantiated without it, so it's always guaranteed to be present. --- src/joystick/gdk/SDL_gameinputjoystick.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c index bc1c01cbf0fc2..b0591d069324c 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.c +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -76,15 +76,7 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) char tmp[4]; int idx = 0; - if (!pDevice) { - return SDL_SetError("GAMEINPUT_InternalAddOrFind argument pDevice cannot be NULL"); - } - devinfo = IGameInputDevice_GetDeviceInfo(pDevice); - if (!devinfo) { - return SDL_SetError("GAMEINPUT_InternalAddOrFind GetDeviceInfo returned NULL"); - } - if (devinfo->capabilities & GameInputDeviceCapabilityWireless) { bus = SDL_HARDWARE_BUS_BLUETOOTH; } else { @@ -102,7 +94,7 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) elem = g_GameInputList.devices[idx]; if (elem && elem->device == pDevice) { /* we're already added */ - return idx; + return 0; } } @@ -123,7 +115,6 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) SDL_strlcat(elem->path, tmp, SDL_arraysize(tmp)); } - g_GameInputList.devices = devicelist; IGameInputDevice_AddRef(pDevice); elem->device = pDevice; elem->name = "GameInput Gamepad"; @@ -133,10 +124,11 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) elem->device_instance = SDL_GetNextObjectID(); elem->wireless = (devinfo->capabilities & GameInputDeviceCapabilityWireless); elem->supportedRumbleMotors = devinfo->supportedRumbleMotors; - g_GameInputList.devices[g_GameInputList.count] = elem; - /* finally increment the count and return */ - return g_GameInputList.count++; + g_GameInputList.devices = devicelist; + g_GameInputList.devices[g_GameInputList.count++] = elem; + + return 0; } static int GAMEINPUT_InternalRemoveByIndex(int idx) @@ -194,6 +186,11 @@ static void CALLBACK GAMEINPUT_InternalJoystickDeviceCallback( int idx = 0; GAMEINPUT_InternalDevice *elem = NULL; + if (!device) { + /* This should never happen, but ignore it if it does */ + return; + } + if (currentStatus & GameInputDeviceConnected) { GAMEINPUT_InternalAddOrFind(device); } else { From 419aebebda930f5da247e1d314a3663f39f0b41a Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 19:40:42 -0800 Subject: [PATCH 021/220] Added infrastructure for reporting GameInput sensors IGameInputReading::GetMotionState() isn't implemented yet, so we'll need to figure out how to interpret the motion data once it's available. --- src/joystick/gdk/SDL_gameinputjoystick.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c index b0591d069324c..5f744c1ec9d68 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.c +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -40,6 +40,7 @@ typedef struct GAMEINPUT_InternalDevice SDL_JoystickGUID guid; /* generated by SDL */ SDL_JoystickID device_instance; /* generated by SDL */ SDL_bool wireless; + SDL_bool sensors_supported; GameInputRumbleMotors supportedRumbleMotors; SDL_bool isAdded; SDL_bool isDeleteRequested; @@ -54,6 +55,7 @@ typedef struct GAMEINPUT_InternalList typedef struct joystick_hwdata { GAMEINPUT_InternalDevice *devref; + SDL_bool report_sensors; GameInputRumbleParams rumbleParams; } GAMEINPUT_InternalJoystickHwdata; @@ -123,6 +125,7 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) elem->guid = SDL_CreateJoystickGUID(bus, vendor, product, version, "GameInput", "Gamepad", 'g', 0); elem->device_instance = SDL_GetNextObjectID(); elem->wireless = (devinfo->capabilities & GameInputDeviceCapabilityWireless); + elem->sensors_supported = (devinfo->supportedInput & GameInputKindMotion); elem->supportedRumbleMotors = devinfo->supportedRumbleMotors; g_GameInputList.devices = devicelist; @@ -373,6 +376,12 @@ static int GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, SDL_TRUE); } + if (elem->sensors_supported) { + /* FIXME: What's the sensor update rate? */ + SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 250.0f); + SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 250.0f); + } + if (elem->wireless) { joystick->epowerlevel = GAMEINPUT_InternalGetPowerLevel(elem->device); } else { @@ -415,7 +424,7 @@ static int GAMEINPUT_JoystickSendEffect(SDL_Joystick *joystick, const void *data static int GAMEINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool enabled) { - /* I am not sure what is this even supposed to do in case of GameInput... */ + joystick->hwdata->report_sensors = enabled; return 0; } @@ -483,6 +492,14 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) #undef CONVERT_TRIGGER } + if (hwdata->report_sensors) { + GameInputMotionState motion_state; + + if (IGameInputReading_GetMotionState(reading, &motion_state)) { + /* FIXME: How do we interpret the motion data? */ + } + } + IGameInputReading_Release(reading); if (joystick->epowerlevel != SDL_JOYSTICK_POWER_WIRED) { From ae4aa2508288cab793818733668062fa02bc2ffb Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 20:10:41 -0800 Subject: [PATCH 022/220] Get the real GameInput device name if possible --- src/joystick/gdk/SDL_gameinputjoystick.c | 48 ++++++++++++------------ 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c index 5f744c1ec9d68..ee302305b5ea9 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.c +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -34,7 +34,7 @@ typedef struct GAMEINPUT_InternalDevice { IGameInputDevice *device; char path[(APP_LOCAL_DEVICE_ID_SIZE * 2) + 1]; - const char *name; /* this is a constant string literal */ + char *name; Uint16 vendor; Uint16 product; SDL_JoystickGUID guid; /* generated by SDL */ @@ -75,6 +75,8 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) Uint16 vendor = 0; Uint16 product = 0; Uint16 version = 0; + const char *manufacturer_string = NULL; + const char *product_string = NULL; char tmp[4]; int idx = 0; @@ -111,18 +113,26 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) return SDL_OutOfMemory(); } - /* generate a device name */ + /* Generate a device path */ for (idx = 0; idx < APP_LOCAL_DEVICE_ID_SIZE; ++idx) { SDL_snprintf(tmp, SDL_arraysize(tmp), "%02hhX", devinfo->deviceId.value[idx]); SDL_strlcat(elem->path, tmp, SDL_arraysize(tmp)); } + if (devinfo->deviceStrings) { + /* In theory we could get the manufacturer and product strings here, but they're NULL for all the controllers I've tested */ + } + + if (devinfo->displayName) { + /* This could give us a product string, but it's NULL for all the controllers I've tested */ + } + IGameInputDevice_AddRef(pDevice); elem->device = pDevice; - elem->name = "GameInput Gamepad"; + elem->name = SDL_CreateJoystickName(vendor, product, manufacturer_string, product_string); elem->vendor = vendor; elem->product = product; - elem->guid = SDL_CreateJoystickGUID(bus, vendor, product, version, "GameInput", "Gamepad", 'g', 0); + elem->guid = SDL_CreateJoystickGUID(bus, vendor, product, version, manufacturer_string, product_string, 'g', 0); elem->device_instance = SDL_GetNextObjectID(); elem->wireless = (devinfo->capabilities & GameInputDeviceCapabilityWireless); elem->sensors_supported = (devinfo->supportedInput & GameInputKindMotion); @@ -137,18 +147,20 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) static int GAMEINPUT_InternalRemoveByIndex(int idx) { GAMEINPUT_InternalDevice **devicelist = NULL; + GAMEINPUT_InternalDevice *elem; int bytes = 0; if (idx < 0 || idx >= g_GameInputList.count) { return SDL_SetError("GAMEINPUT_InternalRemoveByIndex argument idx %d is out of range", idx); } - IGameInputDevice_Release(g_GameInputList.devices[idx]->device); - - if (g_GameInputList.devices[idx]) { - SDL_free(g_GameInputList.devices[idx]); - g_GameInputList.devices[idx] = NULL; + elem = g_GameInputList.devices[idx]; + if (elem) { + IGameInputDevice_Release(elem->device); + SDL_free(elem->name); + SDL_free(elem); } + g_GameInputList.devices[idx] = NULL; if (g_GameInputList.count == 1) { /* last element in the list, free the entire list then */ @@ -159,13 +171,6 @@ static int GAMEINPUT_InternalRemoveByIndex(int idx) bytes = sizeof(*devicelist) * (g_GameInputList.count - idx); SDL_memmove(&g_GameInputList.devices[idx], &g_GameInputList.devices[idx + 1], bytes); } - - devicelist = (GAMEINPUT_InternalDevice **)SDL_realloc(g_GameInputList.devices, sizeof(*devicelist) * (g_GameInputList.count - 1LL)); - if (!devicelist) { - return SDL_OutOfMemory(); - } - - g_GameInputList.devices = devicelist; } /* decrement the count and return */ @@ -516,22 +521,15 @@ static void GAMEINPUT_JoystickClose(SDL_Joystick* joystick) static void GAMEINPUT_JoystickQuit(void) { - int idx; - if (g_pGameInput) { /* free the callback */ IGameInput_UnregisterCallback(g_pGameInput, g_GameInputCallbackToken, /*timeoutInUs:*/ 10000); g_GameInputCallbackToken = GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE; /* free the list */ - for (idx = 0; idx < g_GameInputList.count; ++idx) { - IGameInputDevice_Release(g_GameInputList.devices[idx]->device); - SDL_free(g_GameInputList.devices[idx]); - g_GameInputList.devices[idx] = NULL; + while (g_GameInputList.count > 0) { + GAMEINPUT_InternalRemoveByIndex(0); } - SDL_free(g_GameInputList.devices); - g_GameInputList.devices = NULL; - g_GameInputList.count = 0; IGameInput_Release(g_pGameInput); g_pGameInput = NULL; From ae8a9107813d4a8bbb6c82c5633c1f64b8a0bb3c Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 21:21:54 -0800 Subject: [PATCH 023/220] Added infrastructure for reporting GameInput touchpads PlayStation controllers don't seem to report touch info, so we'll need to figure out how to interpret the touch data once it's available. --- src/joystick/gdk/SDL_gameinputjoystick.c | 59 ++++++++++++++---------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c index ee302305b5ea9..f402b231e95e5 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.c +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -35,13 +35,9 @@ typedef struct GAMEINPUT_InternalDevice IGameInputDevice *device; char path[(APP_LOCAL_DEVICE_ID_SIZE * 2) + 1]; char *name; - Uint16 vendor; - Uint16 product; SDL_JoystickGUID guid; /* generated by SDL */ SDL_JoystickID device_instance; /* generated by SDL */ - SDL_bool wireless; - SDL_bool sensors_supported; - GameInputRumbleMotors supportedRumbleMotors; + const GameInputDeviceInfo *info; SDL_bool isAdded; SDL_bool isDeleteRequested; } GAMEINPUT_InternalDevice; @@ -70,7 +66,7 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) { GAMEINPUT_InternalDevice **devicelist = NULL; GAMEINPUT_InternalDevice *elem = NULL; - const GameInputDeviceInfo *devinfo = NULL; + const GameInputDeviceInfo *info = NULL; Uint16 bus = SDL_HARDWARE_BUS_USB; Uint16 vendor = 0; Uint16 product = 0; @@ -80,15 +76,15 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) char tmp[4]; int idx = 0; - devinfo = IGameInputDevice_GetDeviceInfo(pDevice); - if (devinfo->capabilities & GameInputDeviceCapabilityWireless) { + info = IGameInputDevice_GetDeviceInfo(pDevice); + if (info->capabilities & GameInputDeviceCapabilityWireless) { bus = SDL_HARDWARE_BUS_BLUETOOTH; } else { bus = SDL_HARDWARE_BUS_USB; } - vendor = devinfo->vendorId; - product = devinfo->productId; - version = (devinfo->firmwareVersion.major << 8) | devinfo->firmwareVersion.minor; + vendor = info->vendorId; + product = info->productId; + version = (info->firmwareVersion.major << 8) | info->firmwareVersion.minor; if (SDL_JoystickHandledByAnotherDriver(&SDL_GAMEINPUT_JoystickDriver, vendor, product, version, "")) { return 0; @@ -115,28 +111,24 @@ static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) /* Generate a device path */ for (idx = 0; idx < APP_LOCAL_DEVICE_ID_SIZE; ++idx) { - SDL_snprintf(tmp, SDL_arraysize(tmp), "%02hhX", devinfo->deviceId.value[idx]); + SDL_snprintf(tmp, SDL_arraysize(tmp), "%02hhX", info->deviceId.value[idx]); SDL_strlcat(elem->path, tmp, SDL_arraysize(tmp)); } - if (devinfo->deviceStrings) { + if (info->deviceStrings) { /* In theory we could get the manufacturer and product strings here, but they're NULL for all the controllers I've tested */ } - if (devinfo->displayName) { + if (info->displayName) { /* This could give us a product string, but it's NULL for all the controllers I've tested */ } IGameInputDevice_AddRef(pDevice); elem->device = pDevice; elem->name = SDL_CreateJoystickName(vendor, product, manufacturer_string, product_string); - elem->vendor = vendor; - elem->product = product; elem->guid = SDL_CreateJoystickGUID(bus, vendor, product, version, manufacturer_string, product_string, 'g', 0); elem->device_instance = SDL_GetNextObjectID(); - elem->wireless = (devinfo->capabilities & GameInputDeviceCapabilityWireless); - elem->sensors_supported = (devinfo->supportedInput & GameInputKindMotion); - elem->supportedRumbleMotors = devinfo->supportedRumbleMotors; + elem->info = info; g_GameInputList.devices = devicelist; g_GameInputList.devices[g_GameInputList.count++] = elem; @@ -297,7 +289,7 @@ static SDL_bool GAMEINPUT_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 produ for (idx = 0; idx < g_GameInputList.count; ++idx) { elem = g_GameInputList.devices[idx]; - if (elem && vendor_id == elem->vendor && product_id == elem->product) { + if (elem && vendor_id == elem->info->vendorId && product_id == elem->info->productId) { return SDL_TRUE; } } @@ -374,20 +366,24 @@ static int GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) joystick->nbuttons = 11; joystick->nhats = 1; - if (elem->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) { + if (elem->info->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) { SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, SDL_TRUE); } - if (elem->supportedRumbleMotors & (GameInputRumbleLeftTrigger | GameInputRumbleRightTrigger)) { + if (elem->info->supportedRumbleMotors & (GameInputRumbleLeftTrigger | GameInputRumbleRightTrigger)) { SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, SDL_TRUE); } - if (elem->sensors_supported) { + if (elem->info->supportedInput & GameInputKindTouch) { + SDL_PrivateJoystickAddTouchpad(joystick, elem->info->touchPointCount); + } + + if (elem->info->supportedInput & GameInputKindMotion) { /* FIXME: What's the sensor update rate? */ SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 250.0f); SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 250.0f); } - if (elem->wireless) { + if (elem->info->capabilities & GameInputDeviceCapabilityWireless) { joystick->epowerlevel = GAMEINPUT_InternalGetPowerLevel(elem->device); } else { joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; @@ -451,6 +447,7 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) Uint8 btnidx = 0, btnstate = 0, hat = 0; GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata; IGameInputDevice *device = hwdata->devref->device; + const GameInputDeviceInfo *info = hwdata->devref->info; IGameInputReading *reading = NULL; Uint64 timestamp = SDL_GetTicksNS(); GameInputGamepadState state; @@ -497,6 +494,20 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) #undef CONVERT_TRIGGER } + if (info->supportedInput & GameInputKindTouch) { + GameInputTouchState *touch_state = SDL_stack_alloc(GameInputTouchState, info->touchPointCount); + if (touch_state) { + uint32_t i; + uint32_t touch_count = IGameInputReading_GetTouchState(reading, info->touchPointCount, touch_state); + for (i = 0; i < touch_count; ++i) { + GameInputTouchState *touch = &touch_state[i]; + /* FIXME: We should use touch->touchId to track fingers instead of using i below */ + SDL_SendJoystickTouchpad(timestamp, joystick, 0, i, SDL_PRESSED, touch->positionX * info->touchSensorInfo[i].resolutionX, touch->positionY * info->touchSensorInfo[0].resolutionY, touch->pressure); + } + SDL_stack_free(touch_state); + } + } + if (hwdata->report_sensors) { GameInputMotionState motion_state; From f63f99bd2de20362f2ec2436590aee5a35eaa599 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 21:52:53 -0800 Subject: [PATCH 024/220] Setup to handle the guide button once we get an updated GameInput SDK --- src/joystick/gdk/SDL_gameinputjoystick.c | 32 ++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c index f402b231e95e5..3f3942815c4e1 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.c +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -53,6 +53,7 @@ typedef struct joystick_hwdata GAMEINPUT_InternalDevice *devref; SDL_bool report_sensors; GameInputRumbleParams rumbleParams; + GameInputCallbackToken guide_button_callback_token; } GAMEINPUT_InternalJoystickHwdata; @@ -345,6 +346,17 @@ static SDL_JoystickPowerLevel GAMEINPUT_InternalGetPowerLevel(IGameInputDevice * return SDL_JOYSTICK_POWER_UNKNOWN; } +#if 0 +static void CALLBACK GAMEINPUT_InternalGuideButtonCallback(GameInputCallbackToken callbackToken, void *context, IGameInputDevice *device, uint64_t timestamp, bool isPressed) +{ + SDL_Joystick *joystick = (SDL_Joystick *)context; + + SDL_LockJoysticks(); + SDL_SendJoystickButton(0, joystick, SDL_GAMEPAD_BUTTON_GUIDE, isPressed ? SDL_PRESSED : SDL_RELEASED); + SDL_UnlockJoysticks(); +} +#endif + static int GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) { GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); @@ -366,6 +378,12 @@ static int GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) joystick->nbuttons = 11; joystick->nhats = 1; + if (elem->info->supportedInput & GameInputKindGamepad) { +#if 0 /* The actual signature for this function is GameInputClient::RegisterSystemButtonCallback(struct IGameInputDevice *,enum GameInputSystemButtons,void *,void (*)(unsigned __int64,void *,struct IGameInputDevice *,unsigned __int64,enum GameInputSystemButtons,enum GameInputSystemButtons),unsigned __int64 *) */ + IGameInput_RegisterGuideButtonCallback(g_pGameInput, elem->device, joystick, GAMEINPUT_InternalGuideButtonCallback, &hwdata->guide_button_callback_token); +#endif + } + if (elem->info->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) { SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, SDL_TRUE); } @@ -464,7 +482,11 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) if (IGameInputReading_GetGamepadState(reading, &state)) { for (btnidx = 0; btnidx < SDL_arraysize(s_XInputButtons); ++btnidx) { - btnstate = (state.buttons & s_XInputButtons[btnidx]) ? SDL_PRESSED : SDL_RELEASED; + WORD button_mask = s_XInputButtons[btnidx]; + if (!button_mask) { + continue; + } + btnstate = (state.buttons & button_mask) ? SDL_PRESSED : SDL_RELEASED; SDL_SendJoystickButton(timestamp, joystick, btnidx, btnstate); } @@ -526,7 +548,13 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) static void GAMEINPUT_JoystickClose(SDL_Joystick* joystick) { - SDL_free(joystick->hwdata); + GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata; + + if (hwdata->guide_button_callback_token) { + IGameInput_UnregisterCallback(g_pGameInput, hwdata->guide_button_callback_token, 5000); + } + SDL_free(hwdata); + joystick->hwdata = NULL; } From bb4ec5250f550b81c12e3661169b3ab65ad699a6 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 17 Feb 2024 22:32:44 -0800 Subject: [PATCH 025/220] Added support for non-gamepad controllers to the GameInput driver --- src/joystick/gdk/SDL_gameinputjoystick.c | 154 +++++++++++++++-------- 1 file changed, 100 insertions(+), 54 deletions(-) diff --git a/src/joystick/gdk/SDL_gameinputjoystick.c b/src/joystick/gdk/SDL_gameinputjoystick.c index 3f3942815c4e1..c3ee5a2e776bf 100644 --- a/src/joystick/gdk/SDL_gameinputjoystick.c +++ b/src/joystick/gdk/SDL_gameinputjoystick.c @@ -63,6 +63,14 @@ static IGameInput *g_pGameInput = NULL; static GameInputCallbackToken g_GameInputCallbackToken = GAMEINPUT_INVALID_CALLBACK_TOKEN_VALUE; +static SDL_bool GAMEINPUT_InternalIsGamepad(const GameInputDeviceInfo *info) +{ + if (info->supportedInput & GameInputKindGamepad) { + return SDL_TRUE; + } + return SDL_FALSE; +} + static int GAMEINPUT_InternalAddOrFind(IGameInputDevice *pDevice) { GAMEINPUT_InternalDevice **devicelist = NULL; @@ -234,7 +242,7 @@ static int GAMEINPUT_JoystickInit(void) hR = IGameInput_RegisterDeviceCallback(g_pGameInput, NULL, - GameInputKindGamepad, + GameInputKindController, GameInputDeviceConnected, GameInputBlockingEnumeration, NULL, @@ -360,6 +368,7 @@ static void CALLBACK GAMEINPUT_InternalGuideButtonCallback(GameInputCallbackToke static int GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) { GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); + const GameInputDeviceInfo *info = elem->info; GAMEINPUT_InternalJoystickHwdata *hwdata = NULL; if (!elem) { @@ -374,34 +383,40 @@ static int GAMEINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index) hwdata->devref = elem; joystick->hwdata = hwdata; - joystick->naxes = 6; - joystick->nbuttons = 11; - joystick->nhats = 1; + if (GAMEINPUT_InternalIsGamepad(info)) { + joystick->naxes = 6; + joystick->nbuttons = 11; + joystick->nhats = 1; + } else { + joystick->naxes = info->controllerAxisCount; + joystick->nbuttons = info->controllerButtonCount; + joystick->nhats = info->controllerSwitchCount; + } - if (elem->info->supportedInput & GameInputKindGamepad) { + if (GAMEINPUT_InternalIsGamepad(info)) { #if 0 /* The actual signature for this function is GameInputClient::RegisterSystemButtonCallback(struct IGameInputDevice *,enum GameInputSystemButtons,void *,void (*)(unsigned __int64,void *,struct IGameInputDevice *,unsigned __int64,enum GameInputSystemButtons,enum GameInputSystemButtons),unsigned __int64 *) */ IGameInput_RegisterGuideButtonCallback(g_pGameInput, elem->device, joystick, GAMEINPUT_InternalGuideButtonCallback, &hwdata->guide_button_callback_token); #endif } - if (elem->info->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) { + if (info->supportedRumbleMotors & (GameInputRumbleLowFrequency | GameInputRumbleHighFrequency)) { SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_RUMBLE_BOOLEAN, SDL_TRUE); } - if (elem->info->supportedRumbleMotors & (GameInputRumbleLeftTrigger | GameInputRumbleRightTrigger)) { + if (info->supportedRumbleMotors & (GameInputRumbleLeftTrigger | GameInputRumbleRightTrigger)) { SDL_SetBooleanProperty(SDL_GetJoystickProperties(joystick), SDL_PROP_JOYSTICK_CAP_TRIGGER_RUMBLE_BOOLEAN, SDL_TRUE); } - if (elem->info->supportedInput & GameInputKindTouch) { - SDL_PrivateJoystickAddTouchpad(joystick, elem->info->touchPointCount); + if (info->supportedInput & GameInputKindTouch) { + SDL_PrivateJoystickAddTouchpad(joystick, info->touchPointCount); } - if (elem->info->supportedInput & GameInputKindMotion) { + if (info->supportedInput & GameInputKindMotion) { /* FIXME: What's the sensor update rate? */ SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 250.0f); SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 250.0f); } - if (elem->info->capabilities & GameInputDeviceCapabilityWireless) { + if (info->capabilities & GameInputDeviceCapabilityWireless) { joystick->epowerlevel = GAMEINPUT_InternalGetPowerLevel(elem->device); } else { joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; @@ -449,20 +464,6 @@ static int GAMEINPUT_JoystickSetSensorsEnabled(SDL_Joystick *joystick, SDL_bool static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) { - static WORD s_XInputButtons[] = { - GameInputGamepadA, /* SDL_GAMEPAD_BUTTON_SOUTH */ - GameInputGamepadB, /* SDL_GAMEPAD_BUTTON_EAST */ - GameInputGamepadX, /* SDL_GAMEPAD_BUTTON_WEST */ - GameInputGamepadY, /* SDL_GAMEPAD_BUTTON_NORTH */ - GameInputGamepadView, /* SDL_GAMEPAD_BUTTON_BACK */ - 0, /* The guide button is not available */ - GameInputGamepadMenu, /* SDL_GAMEPAD_BUTTON_START */ - GameInputGamepadLeftThumbstick, /* SDL_GAMEPAD_BUTTON_LEFT_STICK */ - GameInputGamepadRightThumbstick, /* SDL_GAMEPAD_BUTTON_RIGHT_STICK */ - GameInputGamepadLeftShoulder, /* SDL_GAMEPAD_BUTTON_LEFT_SHOULDER */ - GameInputGamepadRightShoulder, /* SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER */ - }; - Uint8 btnidx = 0, btnstate = 0, hat = 0; GAMEINPUT_InternalJoystickHwdata *hwdata = joystick->hwdata; IGameInputDevice *device = hwdata->devref->device; const GameInputDeviceInfo *info = hwdata->devref->info; @@ -471,8 +472,7 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) GameInputGamepadState state; HRESULT hR; - - hR = IGameInput_GetCurrentReading(g_pGameInput, GameInputKindGamepad, device, &reading); + hR = IGameInput_GetCurrentReading(g_pGameInput, info->supportedInput, device, &reading); if (FAILED(hR)) { /* don't SetError here since there can be a legitimate case when there's no reading avail */ return; @@ -480,40 +480,80 @@ static void GAMEINPUT_JoystickUpdate(SDL_Joystick *joystick) /* FIXME: See if we can get the delta between the reading timestamp and current time and apply the offset to timestamp */ - if (IGameInputReading_GetGamepadState(reading, &state)) { - for (btnidx = 0; btnidx < SDL_arraysize(s_XInputButtons); ++btnidx) { - WORD button_mask = s_XInputButtons[btnidx]; - if (!button_mask) { - continue; + if (GAMEINPUT_InternalIsGamepad(info)) { + static WORD s_XInputButtons[] = { + GameInputGamepadA, /* SDL_GAMEPAD_BUTTON_SOUTH */ + GameInputGamepadB, /* SDL_GAMEPAD_BUTTON_EAST */ + GameInputGamepadX, /* SDL_GAMEPAD_BUTTON_WEST */ + GameInputGamepadY, /* SDL_GAMEPAD_BUTTON_NORTH */ + GameInputGamepadView, /* SDL_GAMEPAD_BUTTON_BACK */ + 0, /* The guide button is not available */ + GameInputGamepadMenu, /* SDL_GAMEPAD_BUTTON_START */ + GameInputGamepadLeftThumbstick, /* SDL_GAMEPAD_BUTTON_LEFT_STICK */ + GameInputGamepadRightThumbstick, /* SDL_GAMEPAD_BUTTON_RIGHT_STICK */ + GameInputGamepadLeftShoulder, /* SDL_GAMEPAD_BUTTON_LEFT_SHOULDER */ + GameInputGamepadRightShoulder, /* SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER */ + }; + Uint8 btnidx = 0, btnstate = 0, hat = 0; + + if (IGameInputReading_GetGamepadState(reading, &state)) { + for (btnidx = 0; btnidx < SDL_arraysize(s_XInputButtons); ++btnidx) { + WORD button_mask = s_XInputButtons[btnidx]; + if (!button_mask) { + continue; + } + btnstate = (state.buttons & button_mask) ? SDL_PRESSED : SDL_RELEASED; + SDL_SendJoystickButton(timestamp, joystick, btnidx, btnstate); } - btnstate = (state.buttons & button_mask) ? SDL_PRESSED : SDL_RELEASED; - SDL_SendJoystickButton(timestamp, joystick, btnidx, btnstate); - } - if (state.buttons & GameInputGamepadDPadUp) { - hat |= SDL_HAT_UP; - } - if (state.buttons & GameInputGamepadDPadDown) { - hat |= SDL_HAT_DOWN; - } - if (state.buttons & GameInputGamepadDPadLeft) { - hat |= SDL_HAT_LEFT; - } - if (state.buttons & GameInputGamepadDPadRight) { - hat |= SDL_HAT_RIGHT; - } - SDL_SendJoystickHat(timestamp, joystick, 0, hat); + if (state.buttons & GameInputGamepadDPadUp) { + hat |= SDL_HAT_UP; + } + if (state.buttons & GameInputGamepadDPadDown) { + hat |= SDL_HAT_DOWN; + } + if (state.buttons & GameInputGamepadDPadLeft) { + hat |= SDL_HAT_LEFT; + } + if (state.buttons & GameInputGamepadDPadRight) { + hat |= SDL_HAT_RIGHT; + } + SDL_SendJoystickHat(timestamp, joystick, 0, hat); #define CONVERT_AXIS(v) (Sint16)(((v) < 0.0f) ? ((v)*32768.0f) : ((v)*32767.0f)) - SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, CONVERT_AXIS(state.leftThumbstickX)); - SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, CONVERT_AXIS(-state.leftThumbstickY)); - SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, CONVERT_AXIS(state.rightThumbstickX)); - SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, CONVERT_AXIS(-state.rightThumbstickY)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTX, CONVERT_AXIS(state.leftThumbstickX)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFTY, CONVERT_AXIS(-state.leftThumbstickY)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTX, CONVERT_AXIS(state.rightThumbstickX)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, CONVERT_AXIS(-state.rightThumbstickY)); #undef CONVERT_AXIS #define CONVERT_TRIGGER(v) (Sint16)((v)*65535.0f - 32768.0f) - SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, CONVERT_TRIGGER(state.leftTrigger)); - SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, CONVERT_TRIGGER(state.rightTrigger)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, CONVERT_TRIGGER(state.leftTrigger)); + SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, CONVERT_TRIGGER(state.rightTrigger)); #undef CONVERT_TRIGGER + } + } else { + bool *button_state = SDL_stack_alloc(bool, info->controllerButtonCount); + float *axis_state = SDL_stack_alloc(float, info->controllerAxisCount); + + if (button_state) { + uint32_t i; + uint32_t button_count = IGameInputReading_GetControllerButtonState(reading, info->controllerButtonCount, button_state); + for (i = 0; i < button_count; ++i) { + SDL_SendJoystickButton(timestamp, joystick, i, button_state[i]); + } + SDL_stack_free(button_state); + } + +#define CONVERT_AXIS(v) (Sint16)((v)*65535.0f - 32768.0f) + if (axis_state) { + uint32_t i; + uint32_t axis_count = IGameInputReading_GetControllerAxisState(reading, info->controllerAxisCount, axis_state); + for (i = 0; i < axis_count; ++i) { + SDL_SendJoystickAxis(timestamp, joystick, i, CONVERT_AXIS(axis_state[i])); + } + SDL_stack_free(axis_state); + } +#undef CONVERT_AXIS } if (info->supportedInput & GameInputKindTouch) { @@ -582,6 +622,12 @@ static void GAMEINPUT_JoystickQuit(void) static SDL_bool GAMEINPUT_JoystickGetGamepadMapping(int device_index, SDL_GamepadMapping *out) { + GAMEINPUT_InternalDevice *elem = GAMEINPUT_InternalFindByIndex(device_index); + + if (!GAMEINPUT_InternalIsGamepad(elem->info)) { + return SDL_FALSE; + } + out->a.kind = EMappingKind_Button; out->a.target = SDL_GAMEPAD_BUTTON_SOUTH; From cb3a1a82d530797402eef65a5cecac33ec50c63a Mon Sep 17 00:00:00 2001 From: Ozkan Sezer Date: Tue, 20 Feb 2024 01:23:18 +0300 Subject: [PATCH 026/220] SDL_memcpy.c, SDL_memmove.c, SDL_memset.c: don't use gcc builtins if !HAVE_LIBC __builtin_memcpy, as well as __builtin_memset and __builtin_memmove, needn't be inlined but emitted as a libc call, leading to infinitely recursive calls. Fixes https://github.com/libsdl-org/SDL/issues/9090 --- src/stdlib/SDL_memcpy.c | 4 ++-- src/stdlib/SDL_memmove.c | 2 +- src/stdlib/SDL_memset.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/stdlib/SDL_memcpy.c b/src/stdlib/SDL_memcpy.c index d8cc81bdcfa37..36a2bceaaaab3 100644 --- a/src/stdlib/SDL_memcpy.c +++ b/src/stdlib/SDL_memcpy.c @@ -29,7 +29,7 @@ #endif void *SDL_memcpy(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len) { -#ifdef __GNUC__ +#if defined(__GNUC__) && (defined(HAVE_LIBC) && HAVE_LIBC) /* Presumably this is well tuned for speed. On my machine this is twice as fast as the C code below. */ @@ -76,7 +76,7 @@ void *SDL_memcpy(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void } } return dst; -#endif /* __GNUC__ */ +#endif /* HAVE_MEMCPY */ } /* The optimizer on Visual Studio 2005 and later generates memcpy() and memset() calls. diff --git a/src/stdlib/SDL_memmove.c b/src/stdlib/SDL_memmove.c index 6758691058681..38a75e0f2aeff 100644 --- a/src/stdlib/SDL_memmove.c +++ b/src/stdlib/SDL_memmove.c @@ -29,7 +29,7 @@ #endif void *SDL_memmove(SDL_OUT_BYTECAP(len) void *dst, SDL_IN_BYTECAP(len) const void *src, size_t len) { -#ifdef __GNUC__ +#if defined(__GNUC__) && (defined(HAVE_LIBC) && HAVE_LIBC) /* Presumably this is well tuned for speed. */ return __builtin_memmove(dst, src, len); #elif defined(HAVE_MEMMOVE) diff --git a/src/stdlib/SDL_memset.c b/src/stdlib/SDL_memset.c index 61ad5e8347df8..90ecbeedd3872 100644 --- a/src/stdlib/SDL_memset.c +++ b/src/stdlib/SDL_memset.c @@ -29,7 +29,7 @@ #endif void *SDL_memset(SDL_OUT_BYTECAP(len) void *dst, int c, size_t len) { -#ifdef __GNUC__ +#if defined(__GNUC__) && (defined(HAVE_LIBC) && HAVE_LIBC) return __builtin_memset(dst, c, len); #elif defined(HAVE_MEMSET) return memset(dst, c, len); From 376ef4e418d9489f5ecdd5159020184370b4ba63 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 20 Feb 2024 06:19:20 -0800 Subject: [PATCH 027/220] Fixed the WGI driver picking up Xbox controllers handled by RAWINPUT The WGI driver will see them first, but the RAWINPUT driver has higher priority, so we'll defer to that when it's available. Fixes https://github.com/libsdl-org/SDL/issues/9091 --- .../windows/SDL_windows_gaming_input.c | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/src/joystick/windows/SDL_windows_gaming_input.c b/src/joystick/windows/SDL_windows_gaming_input.c index af788cd920df7..cb788986c949d 100644 --- a/src/joystick/windows/SDL_windows_gaming_input.c +++ b/src/joystick/windows/SDL_windows_gaming_input.c @@ -108,6 +108,111 @@ DEFINE_GUID(IID___x_ABI_CWindows_CGaming_CInput_CIRawGameControllerStatics, 0xeb extern SDL_bool SDL_XINPUT_Enabled(void); +static SDL_bool SDL_IsXInputDevice(Uint16 vendor, Uint16 product) +{ +#if defined(SDL_JOYSTICK_XINPUT) || defined(SDL_JOYSTICK_RAWINPUT) + PRAWINPUTDEVICELIST raw_devices = NULL; + UINT i, raw_device_count = 0; + LONG vidpid = MAKELONG(vendor, product); + + /* XInput and RawInput backends will pick up XInput-compatible devices */ + if (!SDL_XINPUT_Enabled() +#ifdef SDL_JOYSTICK_RAWINPUT + && !RAWINPUT_IsEnabled() +#endif + ) { + return SDL_FALSE; + } + + /* Go through RAWINPUT (WinXP and later) to find HID devices. */ + if ((GetRawInputDeviceList(NULL, &raw_device_count, sizeof(RAWINPUTDEVICELIST)) == -1) || (!raw_device_count)) { + return SDL_FALSE; /* oh well. */ + } + + raw_devices = (PRAWINPUTDEVICELIST)SDL_malloc(sizeof(RAWINPUTDEVICELIST) * raw_device_count); + if (!raw_devices) { + return SDL_FALSE; + } + + raw_device_count = GetRawInputDeviceList(raw_devices, &raw_device_count, sizeof(RAWINPUTDEVICELIST)); + if (raw_device_count == (UINT)-1) { + SDL_free(raw_devices); + raw_devices = NULL; + return SDL_FALSE; /* oh well. */ + } + + for (i = 0; i < raw_device_count; i++) { + RID_DEVICE_INFO rdi; + char devName[MAX_PATH] = { 0 }; + UINT rdiSize = sizeof(rdi); + UINT nameSize = SDL_arraysize(devName); + DEVINST devNode; + char devVidPidString[32]; + int j; + + rdi.cbSize = sizeof(rdi); + + if ((raw_devices[i].dwType != RIM_TYPEHID) || + (GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICEINFO, &rdi, &rdiSize) == ((UINT)-1)) || + (GetRawInputDeviceInfoA(raw_devices[i].hDevice, RIDI_DEVICENAME, devName, &nameSize) == ((UINT)-1)) || + (SDL_strstr(devName, "IG_") == NULL)) { + /* Skip non-XInput devices */ + continue; + } + + /* First check for a simple VID/PID match. This will work for Xbox 360 controllers. */ + if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) == vidpid) { + SDL_free(raw_devices); + return SDL_TRUE; + } + + /* For Xbox One controllers, Microsoft doesn't propagate the VID/PID down to the HID stack. + * We'll have to walk the device tree upwards searching for a match for our VID/PID. */ + + /* Make sure the device interface string is something we know how to parse */ + /* Example: \\?\HID#VID_045E&PID_02FF&IG_00#9&2c203035&2&0000#{4d1e55b2-f16f-11cf-88cb-001111000030} */ + if ((SDL_strstr(devName, "\\\\?\\") != devName) || (SDL_strstr(devName, "#{") == NULL)) { + continue; + } + + /* Unescape the backslashes in the string and terminate before the GUID portion */ + for (j = 0; devName[j] != '\0'; j++) { + if (devName[j] == '#') { + if (devName[j + 1] == '{') { + devName[j] = '\0'; + break; + } else { + devName[j] = '\\'; + } + } + } + + /* We'll be left with a string like this: \\?\HID\VID_045E&PID_02FF&IG_00\9&2c203035&2&0000 + * Simply skip the \\?\ prefix and we'll have a properly formed device instance ID */ + if (CM_Locate_DevNodeA(&devNode, &devName[4], CM_LOCATE_DEVNODE_NORMAL) != CR_SUCCESS) { + continue; + } + + (void)SDL_snprintf(devVidPidString, sizeof(devVidPidString), "VID_%04X&PID_%04X", vendor, product); + + while (CM_Get_Parent(&devNode, devNode, 0) == CR_SUCCESS) { + char deviceId[MAX_DEVICE_ID_LEN]; + + if ((CM_Get_Device_IDA(devNode, deviceId, SDL_arraysize(deviceId), 0) == CR_SUCCESS) && + (SDL_strstr(deviceId, devVidPidString) != NULL)) { + /* The VID/PID matched a parent device */ + SDL_free(raw_devices); + return SDL_TRUE; + } + } + } + + SDL_free(raw_devices); +#endif /* SDL_JOYSTICK_XINPUT || SDL_JOYSTICK_RAWINPUT */ + + return SDL_FALSE; +} + static void WGI_LoadRawGameControllerStatics() { HRESULT hr; @@ -346,6 +451,11 @@ static HRESULT STDMETHODCALLTYPE IEventHandler_CRawGameControllerVtbl_InvokeAdde ignore_joystick = SDL_TRUE; } + if (!ignore_joystick && SDL_IsXInputDevice(vendor, product)) { + /* This hasn't been detected by the RAWINPUT driver yet, but it will be picked up later. */ + ignore_joystick = SDL_TRUE; + } + if (!ignore_joystick) { if (game_controller) { type = GetGameControllerType(game_controller); From 8073f4aa1d24c9b974c3ff0ce90a9a9d20ed436f Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 20 Feb 2024 06:24:20 -0800 Subject: [PATCH 028/220] Pass the real error from D3D12_CreatePipelineState() back to the application --- src/render/direct3d12/SDL_render_d3d12.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index ccc17c98bc2b4..31018260c766f 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -2571,7 +2571,8 @@ static int D3D12_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *c } if (!rendererData->currentPipelineState) { - return SDL_SetError("[direct3d12] Unable to create required pipeline state"); + /* The error has been set inside D3D12_CreatePipelineState() */ + return -1; } D3D_CALL(rendererData->commandList, SetPipelineState, rendererData->currentPipelineState->pipelineState); From 8b6eae2d4fad188c6c82c9b41fd944b70fbe5d39 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Thu, 28 Dec 2023 13:04:41 -0500 Subject: [PATCH 029/220] cmake: Split and store the libdecor version as individual parts It is becoming necessary to enable additional features as libdecor continues to evolve, and checking against a single base version will no longer be adequate. Libdecor doesn't provide versioning defines in its headers, so split the version string into parts to allow for discrete version detection and feature enablement at build time. --- cmake/sdlchecks.cmake | 15 +++++++++++++-- include/build_config/SDL_build_config.h.cmake | 5 ++++- src/video/wayland/SDL_waylanddyn.h | 7 +++++++ src/video/wayland/SDL_waylandsym.h | 2 +- src/video/wayland/SDL_waylandwindow.c | 6 +++--- 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake index c91df3f395e22..55e939a8ca93f 100644 --- a/cmake/sdlchecks.cmake +++ b/cmake/sdlchecks.cmake @@ -557,9 +557,20 @@ macro(CheckWayland) set(LibDecor_PKG_CONFIG_SPEC libdecor-0) pkg_check_modules(PC_LIBDECOR IMPORTED_TARGET ${LibDecor_PKG_CONFIG_SPEC}) if(PC_LIBDECOR_FOUND) - # Version 0.2.0 or higher is needed for suspended window state and statically linked min/max getters. + + # Libdecor doesn't provide internal version defines, so generate them here. + if (PC_LIBDECOR_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)") + set(SDL_LIBDECOR_VERSION_MAJOR ${CMAKE_MATCH_1}) + set(SDL_LIBDECOR_VERSION_MINOR ${CMAKE_MATCH_2}) + set(SDL_LIBDECOR_VERSION_PATCH ${CMAKE_MATCH_3}) + else() + message(WARNING "Failed to parse libdecor version; defaulting to lowest supported (0.1.0)") + set(SDL_LIBDECOR_VERSION_MAJOR 0) + set(SDL_LIBDECOR_VERSION_MINOR 1) + set(SDL_LIBDECOR_VERSION_PATCH 0) + endif() + if(PC_LIBDECOR_VERSION VERSION_GREATER_EQUAL "0.2.0") - set(SDL_HAVE_LIBDECOR_VER_0_2_0 1) set(LibDecor_PKG_CONFIG_SPEC "libdecor-0>=0.2.0") endif() set(HAVE_WAYLAND_LIBDECOR TRUE) diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index 2b4df48157d52..e44f38ef6398b 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -492,7 +492,10 @@ #cmakedefine SDL_VIDEO_VITA_PVR @SDL_VIDEO_VITA_PVR@ #cmakedefine SDL_VIDEO_VITA_PVR_OGL @SDL_VIDEO_VITA_PVR_OGL@ -#cmakedefine SDL_HAVE_LIBDECOR_VER_0_2_0 @SDL_HAVE_LIBDECOR_VER_0_2_0@ +/* Libdecor version info */ +#define SDL_LIBDECOR_VERSION_MAJOR @SDL_LIBDECOR_VERSION_MAJOR@ +#define SDL_LIBDECOR_VERSION_MINOR @SDL_LIBDECOR_VERSION_MINOR@ +#define SDL_LIBDECOR_VERSION_PATCH @SDL_LIBDECOR_VERSION_PATCH@ #if !defined(HAVE_STDINT_H) && !defined(_STDINT_H_) /* Most everything except Visual Studio 2008 and earlier has stdint.h now */ diff --git a/src/video/wayland/SDL_waylanddyn.h b/src/video/wayland/SDL_waylanddyn.h index e674ab8a8b97b..29e4e0a49d3b2 100644 --- a/src/video/wayland/SDL_waylanddyn.h +++ b/src/video/wayland/SDL_waylanddyn.h @@ -60,6 +60,13 @@ enum libdecor_window_state; (WAYLAND_VERSION_MAJOR == x && WAYLAND_VERSION_MINOR > y) || \ (WAYLAND_VERSION_MAJOR == x && WAYLAND_VERSION_MINOR == y && WAYLAND_VERSION_MICRO >= z)) +#ifdef HAVE_LIBDECOR_H +#define SDL_LIBDECOR_CHECK_VERSION(x, y, z) \ + (SDL_LIBDECOR_VERSION_MAJOR > x || \ + (SDL_LIBDECOR_VERSION_MAJOR == x && SDL_LIBDECOR_VERSION_MINOR > y) || \ + (SDL_LIBDECOR_VERSION_MAJOR == x && SDL_LIBDECOR_VERSION_MINOR == y && SDL_LIBDECOR_VERSION_PATCH >= z)) +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/src/video/wayland/SDL_waylandsym.h b/src/video/wayland/SDL_waylandsym.h index 4d815c6343e1c..ac512a1458219 100644 --- a/src/video/wayland/SDL_waylandsym.h +++ b/src/video/wayland/SDL_waylandsym.h @@ -219,7 +219,7 @@ SDL_WAYLAND_SYM(bool, libdecor_configuration_get_window_state, (struct libdecor_ enum libdecor_window_state *)) SDL_WAYLAND_SYM(int, libdecor_dispatch, (struct libdecor *, int)) -#if defined(SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR) || defined(SDL_HAVE_LIBDECOR_VER_0_2_0) +#if defined(SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR) || SDL_LIBDECOR_CHECK_VERSION(0, 2, 0) /* Only found in libdecor 0.1.1 or higher, so failure to load them is not fatal. */ SDL_WAYLAND_SYM_OPT(void, libdecor_frame_get_min_content_size, (const struct libdecor_frame *,\ int *,\ diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index df6443870ba85..09f1963d11d59 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -945,7 +945,7 @@ static void OverrideLibdecorLimits(SDL_Window *window) if (!libdecor_frame_get_min_content_size) { libdecor_frame_set_min_content_size(window->driverdata->shell_surface.libdecor.frame, window->min_w, window->min_h); } -#elif !defined(SDL_HAVE_LIBDECOR_VER_0_2_0) +#elif !SDL_LIBDECOR_CHECK_VERSION(0, 2, 0) libdecor_frame_set_min_content_size(window->driverdata->shell_surface.libdecor.frame, window->min_w, window->min_h); #endif } @@ -964,7 +964,7 @@ static void LibdecorGetMinContentSize(struct libdecor_frame *frame, int *min_w, if (libdecor_frame_get_min_content_size != NULL) { libdecor_frame_get_min_content_size(frame, min_w, min_h); } -#elif defined(SDL_HAVE_LIBDECOR_VER_0_2_0) +#elif SDL_LIBDECOR_CHECK_VERSION(0, 2, 0) libdecor_frame_get_min_content_size(frame, min_w, min_h); #endif } @@ -997,7 +997,7 @@ static void decoration_frame_configure(struct libdecor_frame *frame, maximized = (window_state & LIBDECOR_WINDOW_STATE_MAXIMIZED) != 0; active = (window_state & LIBDECOR_WINDOW_STATE_ACTIVE) != 0; tiled = (window_state & tiled_states) != 0; -#ifdef SDL_HAVE_LIBDECOR_VER_0_2_0 +#if SDL_LIBDECOR_CHECK_VERSION(0, 2, 0) suspended = (window_state & LIBDECOR_WINDOW_STATE_SUSPENDED) != 0; #endif } From 751917cb6fed313820185d0f1c4c0ec68331b774 Mon Sep 17 00:00:00 2001 From: Mathieu Eyraud <70028899+meyraud705@users.noreply.github.com> Date: Tue, 20 Feb 2024 19:31:27 +0100 Subject: [PATCH 030/220] Fix null pointer dereference in SDL_BindAudioStreams() --- src/audio/SDL_audio.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index ab0872b16ec24..453d73702ba39 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -1792,15 +1792,11 @@ int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int if (retval != 0) { int j; - for (j = 0; j <= i; j++) { -#ifdef _MSC_VER /* Visual Studio analyzer can't tell that we've already verified streams[j] isn't NULL */ -#pragma warning(push) -#pragma warning(disable : 28182) -#endif + for (j = 0; j < i; j++) { SDL_UnlockMutex(streams[j]->lock); -#ifdef _MSC_VER -#pragma warning(pop) -#endif + } + if (streams[i]) { + SDL_UnlockMutex(streams[i]->lock); } break; } From 4d000ae3bd56decc999ef4ad62701ed07dbd56bf Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 20 Feb 2024 15:29:07 -0500 Subject: [PATCH 031/220] audio: Change references to `streams[i]` to previously-calculated `stream`. Reference PR #9096. --- src/audio/SDL_audio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c index 453d73702ba39..60fdde7043141 100644 --- a/src/audio/SDL_audio.c +++ b/src/audio/SDL_audio.c @@ -1795,8 +1795,8 @@ int SDL_BindAudioStreams(SDL_AudioDeviceID devid, SDL_AudioStream **streams, int for (j = 0; j < i; j++) { SDL_UnlockMutex(streams[j]->lock); } - if (streams[i]) { - SDL_UnlockMutex(streams[i]->lock); + if (stream) { + SDL_UnlockMutex(stream->lock); } break; } From f49ce4a15d00b989eb339bde5bde1c6f3d626cd7 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 27 Nov 2023 19:27:58 -0500 Subject: [PATCH 032/220] camera: Renamed "video_capture" files to "camera" and moved to own subdir. --- CMakeLists.txt | 3 ++- include/SDL3/SDL.h | 2 +- include/SDL3/{SDL_video_capture.h => SDL_camera.h} | 12 ++++++------ .../SDL_video_capture.c => camera/SDL_camera.c} | 8 ++++---- .../SDL_video_capture_c.h => camera/SDL_camera_c.h} | 2 +- .../SDL_sysvideocapture.h => camera/SDL_syscamera.h} | 0 .../android/SDL_camera_android.c} | 0 .../apple/SDL_camera_apple.m} | 0 .../v4l2/SDL_camera_v4l2.c} | 0 src/video/SDL_video.c | 2 +- test/{testvideocapture.c => testcamera.c} | 0 ...testvideocaptureminimal.c => testcameraminimal.c} | 2 +- 12 files changed, 16 insertions(+), 15 deletions(-) rename include/SDL3/{SDL_video_capture.h => SDL_camera.h} (98%) rename src/{video/SDL_video_capture.c => camera/SDL_camera.c} (99%) rename src/{video/SDL_video_capture_c.h => camera/SDL_camera_c.h} (96%) rename src/{video/SDL_sysvideocapture.h => camera/SDL_syscamera.h} (100%) rename src/{video/android/SDL_android_video_capture.c => camera/android/SDL_camera_android.c} (100%) rename src/{video/SDL_video_capture_apple.m => camera/apple/SDL_camera_apple.m} (100%) rename src/{video/SDL_video_capture_v4l2.c => camera/v4l2/SDL_camera_v4l2.c} (100%) rename test/{testvideocapture.c => testcamera.c} (100%) rename test/{testvideocaptureminimal.c => testcameraminimal.c} (99%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9fe68734439bc..c7686fd1ff77a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -453,6 +453,7 @@ sdl_glob_sources( "${SDL3_SOURCE_DIR}/src/*.c" "${SDL3_SOURCE_DIR}/src/atomic/*.c" "${SDL3_SOURCE_DIR}/src/audio/*.c" + "${SDL3_SOURCE_DIR}/src/camera/*.c" "${SDL3_SOURCE_DIR}/src/core/*.c" "${SDL3_SOURCE_DIR}/src/cpuinfo/*.c" "${SDL3_SOURCE_DIR}/src/dynapi/*.c" @@ -2019,7 +2020,7 @@ elseif(APPLE) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/file/cocoa/*.m") if(IOS OR TVOS OR MACOSX OR DARWIN) - sdl_sources("${SDL3_SOURCE_DIR}/src/video/SDL_video_capture_apple.m") + sdl_sources("${SDL3_SOURCE_DIR}/src/camera/apple/SDL_camera_apple.m") endif() if(IOS OR TVOS OR VISIONOS) diff --git a/include/SDL3/SDL.h b/include/SDL3/SDL.h index a0a77e539d9c5..d7716399de059 100644 --- a/include/SDL3/SDL.h +++ b/include/SDL3/SDL.h @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -76,7 +77,6 @@ #include #include #include -#include "SDL3/SDL_video_capture.h" #include #endif /* SDL_h_ */ diff --git a/include/SDL3/SDL_video_capture.h b/include/SDL3/SDL_camera.h similarity index 98% rename from include/SDL3/SDL_video_capture.h rename to include/SDL3/SDL_camera.h index 80a21605460a8..7325fb8ca6f63 100644 --- a/include/SDL3/SDL_video_capture.h +++ b/include/SDL3/SDL_camera.h @@ -20,13 +20,13 @@ */ /** - * \file SDL_video_capture.h + * \file SDL_camera.h * * Video Capture for the SDL library. */ -#ifndef SDL_video_capture_h_ -#define SDL_video_capture_h_ +#ifndef SDL_camera_h_ +#define SDL_camera_h_ #include "SDL3/SDL_video.h" @@ -356,8 +356,8 @@ extern DECLSPEC int SDLCALL SDL_ReleaseVideoCaptureFrame(SDL_VideoCaptureDevice extern DECLSPEC int SDLCALL SDL_StopVideoCapture(SDL_VideoCaptureDevice *device); /** - * Use this function to shut down video_capture processing and close the - * video_capture device. + * Use this function to shut down camera processing and close the + * camera device. * * \param device opened video capture device * @@ -374,4 +374,4 @@ extern DECLSPEC void SDLCALL SDL_CloseVideoCapture(SDL_VideoCaptureDevice *devic #endif #include -#endif /* SDL_video_capture_h_ */ +#endif /* SDL_camera_h_ */ diff --git a/src/video/SDL_video_capture.c b/src/camera/SDL_camera.c similarity index 99% rename from src/video/SDL_video_capture.c rename to src/camera/SDL_camera.c index 357240fbb07c2..f11601f0cd265 100644 --- a/src/video/SDL_video_capture.c +++ b/src/camera/SDL_camera.c @@ -21,10 +21,10 @@ #include "SDL_internal.h" #include "SDL3/SDL.h" -#include "SDL3/SDL_video_capture.h" -#include "SDL_sysvideocapture.h" -#include "SDL_video_capture_c.h" -#include "SDL_pixels_c.h" +#include "SDL3/SDL_camera.h" +#include "SDL_syscamera.h" +#include "SDL_camera_c.h" +#include "../video/SDL_pixels_c.h" #include "../thread/SDL_systhread.h" #define DEBUG_VIDEO_CAPTURE_CAPTURE 0 diff --git a/src/video/SDL_video_capture_c.h b/src/camera/SDL_camera_c.h similarity index 96% rename from src/video/SDL_video_capture_c.h rename to src/camera/SDL_camera_c.h index d7f1aa17d2e9d..dc4b342933d7e 100644 --- a/src/video/SDL_video_capture_c.h +++ b/src/camera/SDL_camera_c.h @@ -19,7 +19,7 @@ 3. This notice may not be removed or altered from any source distribution. */ #include "../SDL_internal.h" -#include "../../include/SDL3/SDL_video_capture.h" +#include "../../include/SDL3/SDL_camera.h" #ifndef SDL_video_capture_c_h_ #define SDL_video_capture_c_h_ diff --git a/src/video/SDL_sysvideocapture.h b/src/camera/SDL_syscamera.h similarity index 100% rename from src/video/SDL_sysvideocapture.h rename to src/camera/SDL_syscamera.h diff --git a/src/video/android/SDL_android_video_capture.c b/src/camera/android/SDL_camera_android.c similarity index 100% rename from src/video/android/SDL_android_video_capture.c rename to src/camera/android/SDL_camera_android.c diff --git a/src/video/SDL_video_capture_apple.m b/src/camera/apple/SDL_camera_apple.m similarity index 100% rename from src/video/SDL_video_capture_apple.m rename to src/camera/apple/SDL_camera_apple.m diff --git a/src/video/SDL_video_capture_v4l2.c b/src/camera/v4l2/SDL_camera_v4l2.c similarity index 100% rename from src/video/SDL_video_capture_v4l2.c rename to src/camera/v4l2/SDL_camera_v4l2.c diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 3cfa955db8b1e..7bcd62dd411c8 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -33,7 +33,7 @@ #include "../SDL_hints_c.h" #include "../SDL_properties_c.h" #include "../timer/SDL_timer_c.h" -#include "SDL_video_capture_c.h" +#include "../camera/SDL_camera_c.h" #ifdef SDL_VIDEO_OPENGL #include diff --git a/test/testvideocapture.c b/test/testcamera.c similarity index 100% rename from test/testvideocapture.c rename to test/testcamera.c diff --git a/test/testvideocaptureminimal.c b/test/testcameraminimal.c similarity index 99% rename from test/testvideocaptureminimal.c rename to test/testcameraminimal.c index 33396936d18e8..956b7e77a5ac3 100644 --- a/test/testvideocaptureminimal.c +++ b/test/testcameraminimal.c @@ -12,7 +12,7 @@ #include "SDL3/SDL_main.h" #include "SDL3/SDL.h" #include "SDL3/SDL_test.h" -#include "SDL3/SDL_video_capture.h" +#include "SDL3/SDL_camera.h" #ifdef SDL_PLATFORM_EMSCRIPTEN #include From 7ae955ce68318c5405bb5310e688270d344c5a3e Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 27 Nov 2023 23:05:54 -0500 Subject: [PATCH 033/220] camera: Renamed everything from "video capture" to "camera", wired to CMake. --- CMakeLists.txt | 23 +- include/SDL3/SDL_camera.h | 204 +++++----- include/build_config/SDL_build_config.h.cmake | 9 +- .../build_config/SDL_build_config_android.h | 3 + .../SDL_build_config_emscripten.h | 3 + include/build_config/SDL_build_config_ios.h | 3 + include/build_config/SDL_build_config_macos.h | 3 + .../build_config/SDL_build_config_minimal.h | 3 + include/build_config/SDL_build_config_ngage.h | 3 + .../build_config/SDL_build_config_windows.h | 3 + .../build_config/SDL_build_config_wingdk.h | 3 + include/build_config/SDL_build_config_winrt.h | 3 + include/build_config/SDL_build_config_xbox.h | 3 + src/camera/SDL_camera.c | 383 +++++------------- src/camera/SDL_camera_c.h | 15 +- src/camera/SDL_syscamera.h | 54 +-- src/camera/android/SDL_camera_android.c | 71 ++-- src/camera/apple/SDL_camera_apple.m | 95 ++--- src/camera/v4l2/SDL_camera_v4l2.c | 187 ++++----- src/dynapi/SDL_dynapi.sym | 32 +- src/dynapi/SDL_dynapi_overrides.h | 32 +- src/dynapi/SDL_dynapi_procs.h | 32 +- src/video/SDL_video.c | 12 +- test/CMakeLists.txt | 4 +- test/testcamera.c | 122 +++--- test/testcameraminimal.c | 32 +- 26 files changed, 588 insertions(+), 749 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c7686fd1ff77a..d3b09c6b3fe61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -331,7 +331,7 @@ set_option(SDL_METAL "Enable Metal support" ${APPLE}) set_option(SDL_KMSDRM "Use KMS DRM video driver" ${UNIX_SYS}) dep_option(SDL_KMSDRM_SHARED "Dynamically load KMS DRM support" ON "SDL_KMSDRM" OFF) set_option(SDL_OFFSCREEN "Use offscreen video driver" ON) -dep_option(SDL_VIDEO_CAPTURE "Enable video capturing" ON SDL_VIDEO OFF) +dep_option(SDL_CAMERA "Enable camera support" ON SDL_VIDEO OFF) option_string(SDL_BACKGROUNDING_SIGNAL "number to use for magic backgrounding signal or 'OFF'" OFF) option_string(SDL_FOREGROUNDING_SIGNAL "number to use for magic foregrounding signal or 'OFF'" OFF) dep_option(SDL_HIDAPI "Enable the HIDAPI subsystem" ON "NOT VISIONOS" OFF) @@ -1289,6 +1289,12 @@ if(ANDROID) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/sensor/android/*.c") endif() + if(SDL_CAMERA) + set(SDL_CAMERA_ANDROID 1) + set(HAVE_CAMERA TRUE) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/android/*.c") + endif() + if(SDL_VIDEO) set(SDL_VIDEO_DRIVER_ANDROID 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/video/android/*.c") @@ -1498,6 +1504,9 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) ioctl(0, KDGKBENT, &kbe); return 0; }" HAVE_INPUT_KD) + check_c_source_compiles(" + #include + int main(int argc, char** argv) { return 0; }" HAVE_LINUX_VIDEODEV2_H) elseif(FREEBSD) check_c_source_compiles(" #include @@ -1521,6 +1530,12 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) }" HAVE_INPUT_WSCONS) endif() + if(SDL_CAMERA AND HAVE_LINUX_VIDEODEV2_H) + set(SDL_CAMERA_V4L2 1) + set(HAVE_CAMERA TRUE) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/v4l2/*.c") + endif() + if(HAVE_LINUX_INPUT_H) set(SDL_INPUT_LINUXEV 1) endif() @@ -2020,6 +2035,8 @@ elseif(APPLE) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/file/cocoa/*.m") if(IOS OR TVOS OR MACOSX OR DARWIN) + set(SDL_CAMERA_APPLE TRUE) + set(HAVE_CAMERA TRUE) sdl_sources("${SDL3_SOURCE_DIR}/src/camera/apple/SDL_camera_apple.m") endif() @@ -2721,6 +2738,10 @@ if(NOT HAVE_SDL_MISC) set(SDL_MISC_DUMMY 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/misc/dummy/*.c") endif() +if(NOT HAVE_CAMERA) + set(SDL_CAMERA_DUMMY 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/dummy/*.c") +endif() # We always need to have threads and timers around if(NOT HAVE_SDL_THREADS) diff --git a/include/SDL3/SDL_camera.h b/include/SDL3/SDL_camera.h index 7325fb8ca6f63..c37499c54592d 100644 --- a/include/SDL3/SDL_camera.h +++ b/include/SDL3/SDL_camera.h @@ -37,182 +37,182 @@ extern "C" { #endif /** - * This is a unique ID for a video capture device for the time it is connected to the system, + * This is a unique ID for a camera device for the time it is connected to the system, * and is never reused for the lifetime of the application. If the device is * disconnected and reconnected, it will get a new ID. * * The ID value starts at 1 and increments from there. The value 0 is an invalid ID. * - * \sa SDL_GetVideoCaptureDevices + * \sa SDL_GetCameraDevices */ -typedef Uint32 SDL_VideoCaptureDeviceID; +typedef Uint32 SDL_CameraDeviceID; /** - * The structure used to identify an SDL video capture device + * The structure used to identify an SDL camera device */ -struct SDL_VideoCaptureDevice; -typedef struct SDL_VideoCaptureDevice SDL_VideoCaptureDevice; +struct SDL_CameraDevice; +typedef struct SDL_CameraDevice SDL_CameraDevice; -#define SDL_VIDEO_CAPTURE_ALLOW_ANY_CHANGE 1 +#define SDL_CAMERA_ALLOW_ANY_CHANGE 1 /** - * SDL_VideoCaptureSpec structure + * SDL_CameraSpec structure * * Only those field can be 'desired' when configuring the device: * - format * - width * - height * - * \sa SDL_GetVideoCaptureFormat - * \sa SDL_GetVideoCaptureFrameSize + * \sa SDL_GetCameraFormat + * \sa SDL_GetCameraFrameSize * */ -typedef struct SDL_VideoCaptureSpec +typedef struct SDL_CameraSpec { Uint32 format; /**< Frame SDL_PixelFormatEnum format */ int width; /**< Frame width */ int height; /**< Frame height */ -} SDL_VideoCaptureSpec; +} SDL_CameraSpec; /** - * SDL Video Capture Status + * SDL Camera Status * * Change states but calling the function in this order: * - * SDL_OpenVideoCapture() - * SDL_SetVideoCaptureSpec() -> Init - * SDL_StartVideoCapture() -> Playing - * SDL_StopVideoCapture() -> Stopped - * SDL_CloseVideoCapture() + * SDL_OpenCamera() + * SDL_SetCameraSpec() -> Init + * SDL_StartCamera() -> Playing + * SDL_StopCamera() -> Stopped + * SDL_CloseCamera() * */ typedef enum { - SDL_VIDEO_CAPTURE_FAIL = -1, /**< Failed */ - SDL_VIDEO_CAPTURE_INIT = 0, /**< Init, spec hasn't been set */ - SDL_VIDEO_CAPTURE_STOPPED, /**< Stopped */ - SDL_VIDEO_CAPTURE_PLAYING /**< Playing */ -} SDL_VideoCaptureStatus; + SDL_CAMERA_FAIL = -1, /**< Failed */ + SDL_CAMERA_INIT = 0, /**< Init, spec hasn't been set */ + SDL_CAMERA_STOPPED, /**< Stopped */ + SDL_CAMERA_PLAYING /**< Playing */ +} SDL_CameraStatus; /** * SDL Video Capture Status */ -typedef struct SDL_VideoCaptureFrame +typedef struct SDL_CameraFrame { Uint64 timestampNS; /**< Frame timestamp in nanoseconds when read from the driver */ int num_planes; /**< Number of planes */ Uint8 *data[3]; /**< Pointer to data of i-th plane */ int pitch[3]; /**< Pitch of i-th plane */ void *internal; /**< Private field */ -} SDL_VideoCaptureFrame; +} SDL_CameraFrame; /** - * Get a list of currently connected video capture devices. + * Get a list of currently connected camera devices. * - * \param count a pointer filled in with the number of video capture devices - * \returns a 0 terminated array of video capture instance IDs which should be + * \param count a pointer filled in with the number of camera devices + * \returns a 0 terminated array of camera instance IDs which should be * freed with SDL_free(), or NULL on error; call SDL_GetError() for * more details. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_OpenVideoCapture + * \sa SDL_OpenCamera */ -extern DECLSPEC SDL_VideoCaptureDeviceID *SDLCALL SDL_GetVideoCaptureDevices(int *count); +extern DECLSPEC SDL_CameraDeviceID *SDLCALL SDL_GetCameraDevices(int *count); /** * Open a Video Capture device * - * \param instance_id the video capture device instance ID + * \param instance_id the camera device instance ID * \returns device, or NULL on failure; call SDL_GetError() for more * information. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_GetVideoCaptureDeviceName - * \sa SDL_GetVideoCaptureDevices - * \sa SDL_OpenVideoCaptureWithSpec + * \sa SDL_GetCameraDeviceName + * \sa SDL_GetCameraDevices + * \sa SDL_OpenCameraWithSpec */ -extern DECLSPEC SDL_VideoCaptureDevice *SDLCALL SDL_OpenVideoCapture(SDL_VideoCaptureDeviceID instance_id); +extern DECLSPEC SDL_CameraDevice *SDLCALL SDL_OpenCamera(SDL_CameraDeviceID instance_id); /** * Set specification * - * \param device opened video capture device - * \param desired desired video capture spec - * \param obtained obtained video capture spec + * \param device opened camera device + * \param desired desired camera spec + * \param obtained obtained camera spec * \param allowed_changes allow changes or not * \returns 0 on success or a negative error code on failure; call * SDL_GetError() for more information. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_OpenVideoCapture - * \sa SDL_OpenVideoCaptureWithSpec - * \sa SDL_GetVideoCaptureSpec + * \sa SDL_OpenCamera + * \sa SDL_OpenCameraWithSpec + * \sa SDL_GetCameraSpec */ -extern DECLSPEC int SDLCALL SDL_SetVideoCaptureSpec(SDL_VideoCaptureDevice *device, - const SDL_VideoCaptureSpec *desired, - SDL_VideoCaptureSpec *obtained, +extern DECLSPEC int SDLCALL SDL_SetCameraSpec(SDL_CameraDevice *device, + const SDL_CameraSpec *desired, + SDL_CameraSpec *obtained, int allowed_changes); /** * Open a Video Capture device and set specification * - * \param instance_id the video capture device instance ID - * \param desired desired video capture spec - * \param obtained obtained video capture spec + * \param instance_id the camera device instance ID + * \param desired desired camera spec + * \param obtained obtained camera spec * \param allowed_changes allow changes or not * \returns device, or NULL on failure; call SDL_GetError() for more * information. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_OpenVideoCapture - * \sa SDL_SetVideoCaptureSpec - * \sa SDL_GetVideoCaptureSpec + * \sa SDL_OpenCamera + * \sa SDL_SetCameraSpec + * \sa SDL_GetCameraSpec */ -extern DECLSPEC SDL_VideoCaptureDevice *SDLCALL SDL_OpenVideoCaptureWithSpec(SDL_VideoCaptureDeviceID instance_id, - const SDL_VideoCaptureSpec *desired, - SDL_VideoCaptureSpec *obtained, +extern DECLSPEC SDL_CameraDevice *SDLCALL SDL_OpenCameraWithSpec(SDL_CameraDeviceID instance_id, + const SDL_CameraSpec *desired, + SDL_CameraSpec *obtained, int allowed_changes); /** * Get device name * - * \param instance_id the video capture device instance ID + * \param instance_id the camera device instance ID * \returns device name, shouldn't be freed * * \since This function is available since SDL 3.0.0. * - * \sa SDL_GetVideoCaptureDevices + * \sa SDL_GetCameraDevices */ -extern DECLSPEC const char * SDLCALL SDL_GetVideoCaptureDeviceName(SDL_VideoCaptureDeviceID instance_id); +extern DECLSPEC const char * SDLCALL SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id); /** - * Get the obtained video capture spec + * Get the obtained camera spec * - * \param device opened video capture device - * \param spec The SDL_VideoCaptureSpec to be initialized by this function. + * \param device opened camera device + * \param spec The SDL_CameraSpec to be initialized by this function. * \returns 0 on success or a negative error code on failure; call * SDL_GetError() for more information. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_SetVideoCaptureSpec - * \sa SDL_OpenVideoCaptureWithSpec + * \sa SDL_SetCameraSpec + * \sa SDL_OpenCameraWithSpec */ -extern DECLSPEC int SDLCALL SDL_GetVideoCaptureSpec(SDL_VideoCaptureDevice *device, SDL_VideoCaptureSpec *spec); +extern DECLSPEC int SDLCALL SDL_GetCameraSpec(SDL_CameraDevice *device, SDL_CameraSpec *spec); /** - * Get frame format of video capture device. + * Get frame format of camera device. * - * The value can be used to fill SDL_VideoCaptureSpec structure. + * The value can be used to fill SDL_CameraSpec structure. * - * \param device opened video capture device + * \param device opened camera device * \param index format between 0 and num -1 * \param format pointer output format (SDL_PixelFormatEnum) * \returns 0 on success or a negative error code on failure; call @@ -220,32 +220,32 @@ extern DECLSPEC int SDLCALL SDL_GetVideoCaptureSpec(SDL_VideoCaptureDevice *devi * * \since This function is available since SDL 3.0.0. * - * \sa SDL_GetNumVideoCaptureFormats + * \sa SDL_GetNumCameraFormats */ -extern DECLSPEC int SDLCALL SDL_GetVideoCaptureFormat(SDL_VideoCaptureDevice *device, +extern DECLSPEC int SDLCALL SDL_GetCameraFormat(SDL_CameraDevice *device, int index, Uint32 *format); /** * Number of available formats for the device * - * \param device opened video capture device + * \param device opened camera device * \returns number of formats or a negative error code on failure; call * SDL_GetError() for more information. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_GetVideoCaptureFormat - * \sa SDL_SetVideoCaptureSpec + * \sa SDL_GetCameraFormat + * \sa SDL_SetCameraSpec */ -extern DECLSPEC int SDLCALL SDL_GetNumVideoCaptureFormats(SDL_VideoCaptureDevice *device); +extern DECLSPEC int SDLCALL SDL_GetNumCameraFormats(SDL_CameraDevice *device); /** * Get frame sizes of the device and the specified input format. * - * The value can be used to fill SDL_VideoCaptureSpec structure. + * The value can be used to fill SDL_CameraSpec structure. * - * \param device opened video capture device + * \param device opened camera device * \param format a format that can be used by the device (SDL_PixelFormatEnum) * \param index framesize between 0 and num -1 * \param width output width @@ -255,51 +255,51 @@ extern DECLSPEC int SDLCALL SDL_GetNumVideoCaptureFormats(SDL_VideoCaptureDevice * * \since This function is available since SDL 3.0.0. * - * \sa SDL_GetNumVideoCaptureFrameSizes + * \sa SDL_GetNumCameraFrameSizes */ -extern DECLSPEC int SDLCALL SDL_GetVideoCaptureFrameSize(SDL_VideoCaptureDevice *device, Uint32 format, int index, int *width, int *height); +extern DECLSPEC int SDLCALL SDL_GetCameraFrameSize(SDL_CameraDevice *device, Uint32 format, int index, int *width, int *height); /** * Number of different framesizes available for the device and pixel format. * - * \param device opened video capture device + * \param device opened camera device * \param format frame pixel format (SDL_PixelFormatEnum) * \returns number of framesizes or a negative error code on failure; call * SDL_GetError() for more information. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_GetVideoCaptureFrameSize - * \sa SDL_SetVideoCaptureSpec + * \sa SDL_GetCameraFrameSize + * \sa SDL_SetCameraSpec */ -extern DECLSPEC int SDLCALL SDL_GetNumVideoCaptureFrameSizes(SDL_VideoCaptureDevice *device, Uint32 format); +extern DECLSPEC int SDLCALL SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format); /** - * Get video capture status + * Get camera status * - * \param device opened video capture device + * \param device opened camera device * \returns 0 on success or a negative error code on failure; call * SDL_GetError() for more information. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_VideoCaptureStatus + * \sa SDL_CameraStatus */ -extern DECLSPEC SDL_VideoCaptureStatus SDLCALL SDL_GetVideoCaptureStatus(SDL_VideoCaptureDevice *device); +extern DECLSPEC SDL_CameraStatus SDLCALL SDL_GetCameraStatus(SDL_CameraDevice *device); /** - * Start video capture + * Start camera * - * \param device opened video capture device + * \param device opened camera device * \returns 0 on success or a negative error code on failure; call * SDL_GetError() for more information. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_StopVideoCapture + * \sa SDL_StopCamera */ -extern DECLSPEC int SDLCALL SDL_StartVideoCapture(SDL_VideoCaptureDevice *device); +extern DECLSPEC int SDLCALL SDL_StartCamera(SDL_CameraDevice *device); /** * Acquire a frame. @@ -311,62 +311,62 @@ extern DECLSPEC int SDLCALL SDL_StartVideoCapture(SDL_VideoCaptureDevice *device * 0. If frame->num_planes is 0 and returned code is 0, there is no frame at * that time. * - * After used, the frame should be released with SDL_ReleaseVideoCaptureFrame + * After used, the frame should be released with SDL_ReleaseCameraFrame * - * \param device opened video capture device + * \param device opened camera device * \param frame pointer to get the frame * \returns 0 on success or a negative error code on failure; call * SDL_GetError() for more information. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_ReleaseVideoCaptureFrame + * \sa SDL_ReleaseCameraFrame */ -extern DECLSPEC int SDLCALL SDL_AcquireVideoCaptureFrame(SDL_VideoCaptureDevice *device, SDL_VideoCaptureFrame *frame); +extern DECLSPEC int SDLCALL SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame); /** * Release a frame. * - * Let the back-end re-use the internal buffer for video capture. + * Let the back-end re-use the internal buffer for camera. * * All acquired frames should be released before closing the device. * - * \param device opened video capture device + * \param device opened camera device * \param frame frame pointer. * \returns 0 on success or a negative error code on failure; call * SDL_GetError() for more information. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_AcquireVideoCaptureFrame + * \sa SDL_AcquireCameraFrame */ -extern DECLSPEC int SDLCALL SDL_ReleaseVideoCaptureFrame(SDL_VideoCaptureDevice *device, SDL_VideoCaptureFrame *frame); +extern DECLSPEC int SDLCALL SDL_ReleaseCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame); /** * Stop Video Capture * - * \param device opened video capture device + * \param device opened camera device * \returns 0 on success or a negative error code on failure; call * SDL_GetError() for more information. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_StartVideoCapture + * \sa SDL_StartCamera */ -extern DECLSPEC int SDLCALL SDL_StopVideoCapture(SDL_VideoCaptureDevice *device); +extern DECLSPEC int SDLCALL SDL_StopCamera(SDL_CameraDevice *device); /** * Use this function to shut down camera processing and close the * camera device. * - * \param device opened video capture device + * \param device opened camera device * * \since This function is available since SDL 3.0.0. * - * \sa SDL_OpenVideoCaptureWithSpec - * \sa SDL_OpenVideoCapture + * \sa SDL_OpenCameraWithSpec + * \sa SDL_OpenCamera */ -extern DECLSPEC void SDLCALL SDL_CloseVideoCapture(SDL_VideoCaptureDevice *device); +extern DECLSPEC void SDLCALL SDL_CloseCamera(SDL_CameraDevice *device); /* Ends C function definitions when using C++ */ #ifdef __cplusplus diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index e44f38ef6398b..076d3120f14ff 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -253,8 +253,6 @@ #cmakedefine SDL_DEFAULT_ASSERT_LEVEL @SDL_DEFAULT_ASSERT_LEVEL@ #endif -#cmakedefine SDL_VIDEO_CAPTURE - /* Allow disabling of major subsystems */ #cmakedefine SDL_AUDIO_DISABLED @SDL_AUDIO_DISABLED@ #cmakedefine SDL_JOYSTICK_DISABLED @SDL_JOYSTICK_DISABLED@ @@ -265,6 +263,7 @@ #cmakedefine SDL_THREADS_DISABLED @SDL_THREADS_DISABLED@ #cmakedefine SDL_VIDEO_DISABLED @SDL_VIDEO_DISABLED@ #cmakedefine SDL_POWER_DISABLED @SDL_POWER_DISABLED@ +#cmakedefine SDL_CAMERA_DISABLED @SDL_CAMERA_DISABLED@ /* Enable various audio drivers */ #cmakedefine SDL_AUDIO_DRIVER_ALSA @SDL_AUDIO_DRIVER_ALSA@ @@ -467,6 +466,12 @@ #cmakedefine SDL_FILESYSTEM_PS2 @SDL_FILESYSTEM_PS2@ #cmakedefine SDL_FILESYSTEM_N3DS @SDL_FILESYSTEM_N3DS@ +/* Enable camera subsystem */ +#cmakedefine SDL_CAMERA_DUMMY @SDL_CAMERA_DUMMY@ +#cmakedefine SDL_CAMERA_V4L2 @SDL_CAMERA_V4L2@ +#cmakedefine SDL_CAMERA_APPLE @SDL_CAMERA_APPLE@ +#cmakedefine SDL_CAMERA_ANDROID @SDL_CAMERA_ANDROID@ + /* Enable misc subsystem */ #cmakedefine SDL_MISC_DUMMY @SDL_MISC_DUMMY@ diff --git a/include/build_config/SDL_build_config_android.h b/include/build_config/SDL_build_config_android.h index 4e688d0d3f41e..c2eaf09859212 100644 --- a/include/build_config/SDL_build_config_android.h +++ b/include/build_config/SDL_build_config_android.h @@ -190,4 +190,7 @@ /* Enable the filesystem driver */ #define SDL_FILESYSTEM_ANDROID 1 +/* Enable the camera driver */ +#define SDL_CAMERA_ANDROID 1 + #endif /* SDL_build_config_android_h_ */ diff --git a/include/build_config/SDL_build_config_emscripten.h b/include/build_config/SDL_build_config_emscripten.h index 40876c3952fbb..3d7836678229e 100644 --- a/include/build_config/SDL_build_config_emscripten.h +++ b/include/build_config/SDL_build_config_emscripten.h @@ -209,4 +209,7 @@ /* Enable system filesystem support */ #define SDL_FILESYSTEM_EMSCRIPTEN 1 +/* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */ +#define SDL_CAMERA_DUMMY 1 + #endif /* SDL_build_config_emscripten_h */ diff --git a/include/build_config/SDL_build_config_ios.h b/include/build_config/SDL_build_config_ios.h index 48da8852997c3..a356f8920fab4 100644 --- a/include/build_config/SDL_build_config_ios.h +++ b/include/build_config/SDL_build_config_ios.h @@ -212,4 +212,7 @@ /* enable filesystem support */ #define SDL_FILESYSTEM_COCOA 1 +/* enable camera support */ +#define SDL_CAMERA_APPLE 1 + #endif /* SDL_build_config_ios_h_ */ diff --git a/include/build_config/SDL_build_config_macos.h b/include/build_config/SDL_build_config_macos.h index 9eb5830cdd8b4..d45a3980cd32f 100644 --- a/include/build_config/SDL_build_config_macos.h +++ b/include/build_config/SDL_build_config_macos.h @@ -269,6 +269,9 @@ /* enable filesystem support */ #define SDL_FILESYSTEM_COCOA 1 +/* enable camera support */ +#define SDL_CAMERA_APPLE 1 + /* Enable assembly routines */ #ifdef __ppc__ #define SDL_ALTIVEC_BLITTERS 1 diff --git a/include/build_config/SDL_build_config_minimal.h b/include/build_config/SDL_build_config_minimal.h index bb256fc3079d4..38e870160a77b 100644 --- a/include/build_config/SDL_build_config_minimal.h +++ b/include/build_config/SDL_build_config_minimal.h @@ -89,4 +89,7 @@ typedef unsigned int uintptr_t; /* Enable the dummy filesystem driver (src/filesystem/dummy/\*.c) */ #define SDL_FILESYSTEM_DUMMY 1 +/* Enable the camera driver (src/camera/dummy/\*.c) */ +#define SDL_CAMERA_DUMMY 1 + #endif /* SDL_build_config_minimal_h_ */ diff --git a/include/build_config/SDL_build_config_ngage.h b/include/build_config/SDL_build_config_ngage.h index 17872ef065818..a8719bd90905e 100644 --- a/include/build_config/SDL_build_config_ngage.h +++ b/include/build_config/SDL_build_config_ngage.h @@ -86,4 +86,7 @@ typedef unsigned long uintptr_t; /* Enable the dummy filesystem driver (src/filesystem/dummy/\*.c) */ #define SDL_FILESYSTEM_DUMMY 1 +/* Enable the camera driver (src/camera/dummy/\*.c) */ +#define SDL_CAMERA_DUMMY 1 + #endif /* SDL_build_config_ngage_h_ */ diff --git a/include/build_config/SDL_build_config_windows.h b/include/build_config/SDL_build_config_windows.h index 68664c5ba9a36..42682d8fcf3c5 100644 --- a/include/build_config/SDL_build_config_windows.h +++ b/include/build_config/SDL_build_config_windows.h @@ -311,4 +311,7 @@ typedef unsigned int uintptr_t; /* Enable filesystem support */ #define SDL_FILESYSTEM_WINDOWS 1 +/* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */ +#define SDL_CAMERA_DUMMY 1 + #endif /* SDL_build_config_windows_h_ */ diff --git a/include/build_config/SDL_build_config_wingdk.h b/include/build_config/SDL_build_config_wingdk.h index d856d2323e08b..0c60bea6f32ba 100644 --- a/include/build_config/SDL_build_config_wingdk.h +++ b/include/build_config/SDL_build_config_wingdk.h @@ -247,6 +247,9 @@ /* Enable filesystem support */ #define SDL_FILESYSTEM_WINDOWS 1 +/* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */ +#define SDL_CAMERA_DUMMY 1 + /* Use the (inferior) GDK text input method for GDK platforms */ /*#define SDL_GDK_TEXTINPUT 1*/ diff --git a/include/build_config/SDL_build_config_winrt.h b/include/build_config/SDL_build_config_winrt.h index 08ec512b0708b..4b3f865c48519 100644 --- a/include/build_config/SDL_build_config_winrt.h +++ b/include/build_config/SDL_build_config_winrt.h @@ -215,4 +215,7 @@ /* Enable system power support */ #define SDL_POWER_WINRT 1 +/* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */ +#define SDL_CAMERA_DUMMY 1 + #endif /* SDL_build_config_winrt_h_ */ diff --git a/include/build_config/SDL_build_config_xbox.h b/include/build_config/SDL_build_config_xbox.h index c5198aeec7631..11116f9a60274 100644 --- a/include/build_config/SDL_build_config_xbox.h +++ b/include/build_config/SDL_build_config_xbox.h @@ -236,4 +236,7 @@ /* Use the (inferior) GDK text input method for GDK platforms */ #define SDL_GDK_TEXTINPUT 1 +/* Enable the camera driver (src/camera/dummy/\*.c) */ +#define SDL_CAMERA_DUMMY 1 + #endif /* SDL_build_config_wingdk_h_ */ diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index f11601f0cd265..f10fc755fe7cc 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -27,20 +27,18 @@ #include "../video/SDL_pixels_c.h" #include "../thread/SDL_systhread.h" -#define DEBUG_VIDEO_CAPTURE_CAPTURE 0 +#define DEBUG_CAMERA 1 - -#ifdef SDL_VIDEO_CAPTURE /* list node entries to share frames between SDL and user app */ typedef struct entry_t { - SDL_VideoCaptureFrame frame; + SDL_CameraFrame frame; } entry_t; -static SDL_VideoCaptureDevice *open_devices[16]; +static SDL_CameraDevice *open_devices[16]; static void -close_device(SDL_VideoCaptureDevice *device) +close_device(SDL_CameraDevice *device) { if (!device) { return; @@ -73,7 +71,7 @@ close_device(SDL_VideoCaptureDevice *device) while (device->buffer_queue != NULL) { SDL_ListPop(&device->buffer_queue, (void**)&entry); if (entry) { - SDL_VideoCaptureFrame f = entry->frame; + SDL_CameraFrame f = entry->frame; /* Release frames not acquired, if any */ if (f.timestampNS) { ReleaseFrame(device, &f); @@ -109,7 +107,7 @@ SDL_bool check_device_playing(void) int i, n = SDL_arraysize(open_devices); for (i = 0; i < n; i++) { if (open_devices[i]) { - if (SDL_GetVideoCaptureStatus(open_devices[i]) == SDL_VIDEO_CAPTURE_PLAYING) { + if (SDL_GetCameraStatus(open_devices[i]) == SDL_CAMERA_PLAYING) { return SDL_TRUE; } } @@ -117,26 +115,20 @@ SDL_bool check_device_playing(void) return SDL_FALSE; } - -#endif /* SDL_VIDEO_CAPTURE */ - void -SDL_CloseVideoCapture(SDL_VideoCaptureDevice *device) +SDL_CloseCamera(SDL_CameraDevice *device) { -#ifdef SDL_VIDEO_CAPTURE if (!device) { SDL_InvalidParamError("device"); return; } close_device(device); -#endif } int -SDL_StartVideoCapture(SDL_VideoCaptureDevice *device) +SDL_StartCamera(SDL_CameraDevice *device) { -#ifdef SDL_VIDEO_CAPTURE - SDL_VideoCaptureStatus status; + SDL_CameraStatus status; int result; if (!device) { return SDL_InvalidParamError("device"); @@ -146,12 +138,12 @@ SDL_StartVideoCapture(SDL_VideoCaptureDevice *device) return SDL_SetError("no spec set"); } - status = SDL_GetVideoCaptureStatus(device); - if (status != SDL_VIDEO_CAPTURE_INIT) { + status = SDL_GetCameraStatus(device); + if (status != SDL_CAMERA_INIT) { return SDL_SetError("invalid state"); } - result = StartCapture(device); + result = StartCamera(device); if (result < 0) { return result; } @@ -159,15 +151,11 @@ SDL_StartVideoCapture(SDL_VideoCaptureDevice *device) SDL_AtomicSet(&device->enabled, 1); return 0; -#else - return SDL_Unsupported(); -#endif } int -SDL_GetVideoCaptureSpec(SDL_VideoCaptureDevice *device, SDL_VideoCaptureSpec *spec) +SDL_GetCameraSpec(SDL_CameraDevice *device, SDL_CameraSpec *spec) { -#ifdef SDL_VIDEO_CAPTURE if (!device) { return SDL_InvalidParamError("device"); } @@ -179,24 +167,20 @@ SDL_GetVideoCaptureSpec(SDL_VideoCaptureDevice *device, SDL_VideoCaptureSpec *sp SDL_zerop(spec); return GetDeviceSpec(device, spec); -#else - return SDL_Unsupported(); -#endif } int -SDL_StopVideoCapture(SDL_VideoCaptureDevice *device) +SDL_StopCamera(SDL_CameraDevice *device) { -#ifdef SDL_VIDEO_CAPTURE - SDL_VideoCaptureStatus status; + SDL_CameraStatus status; int ret; if (!device) { return SDL_InvalidParamError("device"); } - status = SDL_GetVideoCaptureStatus(device); + status = SDL_GetCameraStatus(device); - if (status != SDL_VIDEO_CAPTURE_PLAYING) { + if (status != SDL_CAMERA_PLAYING) { return SDL_SetError("invalid state"); } @@ -204,7 +188,7 @@ SDL_StopVideoCapture(SDL_VideoCaptureDevice *device) SDL_AtomicSet(&device->shutdown, 1); SDL_LockMutex(device->acquiring_lock); - ret = StopCapture(device); + ret = StopCamera(device); SDL_UnlockMutex(device->acquiring_lock); if (ret < 0) { @@ -212,25 +196,20 @@ SDL_StopVideoCapture(SDL_VideoCaptureDevice *device) } return 0; -#else - return SDL_Unsupported(); -#endif } -#ifdef SDL_VIDEO_CAPTURE - /* Check spec has valid format and frame size */ static int -prepare_video_capturespec(SDL_VideoCaptureDevice *device, const SDL_VideoCaptureSpec *desired, SDL_VideoCaptureSpec *obtained, int allowed_changes) +prepare_cameraspec(SDL_CameraDevice *device, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes) { /* Check format */ { - int i, num = SDL_GetNumVideoCaptureFormats(device); + int i, num = SDL_GetNumCameraFormats(device); int is_format_valid = 0; for (i = 0; i < num; i++) { Uint32 format; - if (SDL_GetVideoCaptureFormat(device, i, &format) == 0) { + if (SDL_GetCameraFormat(device, i, &format) == 0) { if (format == desired->format && format != SDL_PIXELFORMAT_UNKNOWN) { is_format_valid = 1; obtained->format = format; @@ -243,7 +222,7 @@ prepare_video_capturespec(SDL_VideoCaptureDevice *device, const SDL_VideoCapture if (allowed_changes) { for (i = 0; i < num; i++) { Uint32 format; - if (SDL_GetVideoCaptureFormat(device, i, &format) == 0) { + if (SDL_GetCameraFormat(device, i, &format) == 0) { if (format != SDL_PIXELFORMAT_UNKNOWN) { obtained->format = format; is_format_valid = 1; @@ -266,12 +245,12 @@ prepare_video_capturespec(SDL_VideoCaptureDevice *device, const SDL_VideoCapture /* Check frame size */ { - int i, num = SDL_GetNumVideoCaptureFrameSizes(device, obtained->format); + int i, num = SDL_GetNumCameraFrameSizes(device, obtained->format); int is_framesize_valid = 0; for (i = 0; i < num; i++) { int w, h; - if (SDL_GetVideoCaptureFrameSize(device, obtained->format, i, &w, &h) == 0) { + if (SDL_GetCameraFrameSize(device, obtained->format, i, &w, &h) == 0) { if (desired->width == w && desired->height == h) { is_framesize_valid = 1; obtained->width = w; @@ -284,7 +263,7 @@ prepare_video_capturespec(SDL_VideoCaptureDevice *device, const SDL_VideoCapture if (!is_framesize_valid) { if (allowed_changes) { int w, h; - if (SDL_GetVideoCaptureFrameSize(device, obtained->format, 0, &w, &h) == 0) { + if (SDL_GetCameraFrameSize(device, obtained->format, 0, &w, &h) == 0) { is_framesize_valid = 1; obtained->width = w; obtained->height = h; @@ -305,12 +284,9 @@ prepare_video_capturespec(SDL_VideoCaptureDevice *device, const SDL_VideoCapture return 0; } -#endif /* SDL_VIDEO_CAPTURE */ - const char * -SDL_GetVideoCaptureDeviceName(SDL_VideoCaptureDeviceID instance_id) +SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id) { -#ifdef SDL_VIDEO_CAPTURE static char buf[256]; buf[0] = 0; buf[255] = 0; @@ -320,26 +296,19 @@ SDL_GetVideoCaptureDeviceName(SDL_VideoCaptureDeviceID instance_id) return NULL; } - if (GetDeviceName(instance_id, buf, sizeof (buf)) < 0) { + if (GetCameraDeviceName(instance_id, buf, sizeof (buf)) < 0) { buf[0] = 0; } return buf; -#else - SDL_Unsupported(); - return NULL; -#endif } -SDL_VideoCaptureDeviceID * -SDL_GetVideoCaptureDevices(int *count) +SDL_CameraDeviceID * +SDL_GetCameraDevices(int *count) { int num = 0; - SDL_VideoCaptureDeviceID *ret = NULL; -#ifdef SDL_VIDEO_CAPTURE - ret = GetVideoCaptureDevices(&num); -#endif + SDL_CameraDeviceID *ret = GetCameraDevices(&num); if (ret) { if (count) { @@ -350,7 +319,7 @@ SDL_GetVideoCaptureDevices(int *count) /* return list of 0 ID, null terminated */ num = 0; - ret = (SDL_VideoCaptureDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); + ret = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); if (ret == NULL) { SDL_OutOfMemory(); @@ -368,17 +337,15 @@ SDL_GetVideoCaptureDevices(int *count) return ret; } -#ifdef SDL_VIDEO_CAPTURE - -/* Video capture thread function */ +/* Camera thread function */ static int SDLCALL -SDL_CaptureVideoThread(void *devicep) +SDL_CameraThread(void *devicep) { const int delay = 20; - SDL_VideoCaptureDevice *device = (SDL_VideoCaptureDevice *) devicep; + SDL_CameraDevice *device = (SDL_CameraDevice *) devicep; -#if DEBUG_VIDEO_CAPTURE_CAPTURE - SDL_Log("Start thread 'SDL_CaptureVideo'"); +#if DEBUG_CAMERA + SDL_Log("Start thread 'SDL_CameraThread'"); #endif @@ -387,11 +354,11 @@ SDL_CaptureVideoThread(void *devicep) /* { // Set thread priority to THREAD_PRIORITY_VIDEO - extern void Android_JNI_VideoCaptureSetThreadPriority(int, int); - Android_JNI_VideoCaptureSetThreadPriority(device->iscapture, device); + extern void Android_JNI_CameraSetThreadPriority(int, int); + Android_JNI_CameraSetThreadPriority(device->iscapture, device); }*/ #else - /* The video_capture mixing is always a high priority thread */ + /* The camera capture is always a high priority thread */ SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); #endif @@ -403,9 +370,9 @@ SDL_CaptureVideoThread(void *devicep) SDL_Delay(delay); } - /* Loop, filling the video_capture buffers */ + /* Loop, filling the camera buffers */ while (!SDL_AtomicGet(&device->shutdown)) { - SDL_VideoCaptureFrame f; + SDL_CameraFrame f; int ret; entry_t *entry; @@ -423,7 +390,7 @@ SDL_CaptureVideoThread(void *devicep) if (ret < 0) { /* Flag it as an error */ -#if DEBUG_VIDEO_CAPTURE_CAPTURE +#if DEBUG_CAMERA SDL_Log("dev[%p] error AcquireFrame: %d %s", (void *)device, ret, SDL_GetError()); #endif f.num_planes = 0; @@ -447,28 +414,26 @@ SDL_CaptureVideoThread(void *devicep) } } -#if DEBUG_VIDEO_CAPTURE_CAPTURE - SDL_Log("dev[%p] End thread 'SDL_CaptureVideo'", (void *)device); +#if DEBUG_CAMERA + SDL_Log("dev[%p] End thread 'SDL_CameraThread'", (void *)device); #endif return 0; error_mem: -#if DEBUG_VIDEO_CAPTURE_CAPTURE - SDL_Log("dev[%p] End thread 'SDL_CaptureVideo' with error: %s", (void *)device, SDL_GetError()); +#if DEBUG_CAMERA + SDL_Log("dev[%p] End thread 'SDL_CameraThread' with error: %s", (void *)device, SDL_GetError()); #endif SDL_AtomicSet(&device->shutdown, 1); SDL_OutOfMemory(); return 0; } -#endif -SDL_VideoCaptureDevice * -SDL_OpenVideoCapture(SDL_VideoCaptureDeviceID instance_id) +SDL_CameraDevice * +SDL_OpenCamera(SDL_CameraDeviceID instance_id) { -#ifdef SDL_VIDEO_CAPTURE int i, n = SDL_arraysize(open_devices); int id = -1; - SDL_VideoCaptureDevice *device = NULL; + SDL_CameraDevice *device = NULL; const char *device_name = NULL; if (!SDL_WasInit(SDL_INIT_VIDEO)) { @@ -486,19 +451,19 @@ SDL_OpenVideoCapture(SDL_VideoCaptureDeviceID instance_id) } if (id == -1) { - SDL_SetError("Too many open video capture devices"); + SDL_SetError("Too many open camera devices"); goto error; } if (instance_id != 0) { - device_name = SDL_GetVideoCaptureDeviceName(instance_id); + device_name = SDL_GetCameraDeviceName(instance_id); if (device_name == NULL) { goto error; } } else { - SDL_VideoCaptureDeviceID *devices = SDL_GetVideoCaptureDevices(NULL); + SDL_CameraDeviceID *devices = SDL_GetCameraDevices(NULL); if (devices && devices[0]) { - device_name = SDL_GetVideoCaptureDeviceName(devices[0]); + device_name = SDL_GetCameraDeviceName(devices[0]); SDL_free(devices); } } @@ -507,7 +472,7 @@ SDL_OpenVideoCapture(SDL_VideoCaptureDeviceID instance_id) // FIXME do we need this ? /* Let the user override. */ { - const char *dev = SDL_getenv("SDL_VIDEO_CAPTURE_DEVICE_NAME"); + const char *dev = SDL_getenv("SDL_CAMERA_DEVICE_NAME"); if (dev && dev[0]) { device_name = dev; } @@ -518,7 +483,7 @@ SDL_OpenVideoCapture(SDL_VideoCaptureDeviceID instance_id) goto error; } - device = (SDL_VideoCaptureDevice *) SDL_calloc(1, sizeof (SDL_VideoCaptureDevice)); + device = (SDL_CameraDevice *) SDL_calloc(1, sizeof (SDL_CameraDevice)); if (device == NULL) { SDL_OutOfMemory(); goto error; @@ -550,16 +515,16 @@ SDL_OpenVideoCapture(SDL_VideoCaptureDeviceID instance_id) open_devices[id] = device; /* add it to our list of open devices. */ - /* Start the video_capture thread */ + /* Start the camera thread */ { const size_t stacksize = 64 * 1024; char threadname[64]; - SDL_snprintf(threadname, sizeof (threadname), "SDLVideoC%d", id); - device->thread = SDL_CreateThreadInternal(SDL_CaptureVideoThread, threadname, stacksize, device); + SDL_snprintf(threadname, sizeof (threadname), "SDLCamera%d", id); + device->thread = SDL_CreateThreadInternal(SDL_CameraThread, threadname, stacksize, device); if (device->thread == NULL) { - SDL_SetError("Couldn't create video_capture thread"); + SDL_SetError("Couldn't create camera thread"); goto error; } } @@ -569,21 +534,16 @@ SDL_OpenVideoCapture(SDL_VideoCaptureDeviceID instance_id) error: close_device(device); return NULL; -#else - SDL_Unsupported(); - return NULL; -#endif /* SDL_VIDEO_CAPTURE */ } int -SDL_SetVideoCaptureSpec(SDL_VideoCaptureDevice *device, - const SDL_VideoCaptureSpec *desired, - SDL_VideoCaptureSpec *obtained, +SDL_SetCameraSpec(SDL_CameraDevice *device, + const SDL_CameraSpec *desired, + SDL_CameraSpec *obtained, int allowed_changes) { -#ifdef SDL_VIDEO_CAPTURE - SDL_VideoCaptureSpec _obtained; - SDL_VideoCaptureSpec _desired; + SDL_CameraSpec _obtained; + SDL_CameraSpec _desired; int result; if (!device) { @@ -597,7 +557,7 @@ SDL_SetVideoCaptureSpec(SDL_VideoCaptureDevice *device, if (!desired) { SDL_zero(_desired); desired = &_desired; - allowed_changes = SDL_VIDEO_CAPTURE_ALLOW_ANY_CHANGE; + allowed_changes = SDL_CAMERA_ALLOW_ANY_CHANGE; } else { /* in case desired == obtained */ _desired = *desired; @@ -610,7 +570,7 @@ SDL_SetVideoCaptureSpec(SDL_VideoCaptureDevice *device, SDL_zerop(obtained); - if (prepare_video_capturespec(device, desired, obtained, allowed_changes) < 0) { + if (prepare_cameraspec(device, desired, obtained, allowed_changes) < 0) { return -1; } @@ -626,16 +586,11 @@ SDL_SetVideoCaptureSpec(SDL_VideoCaptureDevice *device, device->is_spec_set = SDL_TRUE; return 0; -#else - SDL_zero(*obtained); - return SDL_Unsupported(); -#endif /* SDL_VIDEO_CAPTURE */ } int -SDL_AcquireVideoCaptureFrame(SDL_VideoCaptureDevice *device, SDL_VideoCaptureFrame *frame) +SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame) { -#ifdef SDL_VIDEO_CAPTURE if (!device) { return SDL_InvalidParamError("device"); } @@ -679,15 +634,11 @@ SDL_AcquireVideoCaptureFrame(SDL_VideoCaptureDevice *device, SDL_VideoCaptureFra } return 0; -#else - return SDL_Unsupported(); -#endif /* SDL_VIDEO_CAPTURE */ } int -SDL_ReleaseVideoCaptureFrame(SDL_VideoCaptureDevice *device, SDL_VideoCaptureFrame *frame) +SDL_ReleaseCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame) { -#ifdef SDL_VIDEO_CAPTURE if (!device) { return SDL_InvalidParamError("device"); } @@ -703,28 +654,20 @@ SDL_ReleaseVideoCaptureFrame(SDL_VideoCaptureDevice *device, SDL_VideoCaptureFra SDL_zerop(frame); return 0; -#else - return SDL_Unsupported(); -#endif /* SDL_VIDEO_CAPTURE */ } int -SDL_GetNumVideoCaptureFormats(SDL_VideoCaptureDevice *device) +SDL_GetNumCameraFormats(SDL_CameraDevice *device) { -#ifdef SDL_VIDEO_CAPTURE if (!device) { return SDL_InvalidParamError("device"); } return GetNumFormats(device); -#else - return 0; -#endif /* SDL_VIDEO_CAPTURE */ } int -SDL_GetVideoCaptureFormat(SDL_VideoCaptureDevice *device, int index, Uint32 *format) +SDL_GetCameraFormat(SDL_CameraDevice *device, int index, Uint32 *format) { -#ifdef SDL_VIDEO_CAPTURE if (!device) { return SDL_InvalidParamError("device"); } @@ -733,28 +676,20 @@ SDL_GetVideoCaptureFormat(SDL_VideoCaptureDevice *device, int index, Uint32 *for } *format = 0; return GetFormat(device, index, format); -#else - return SDL_Unsupported(); -#endif /* SDL_VIDEO_CAPTURE */ } int -SDL_GetNumVideoCaptureFrameSizes(SDL_VideoCaptureDevice *device, Uint32 format) +SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format) { -#ifdef SDL_VIDEO_CAPTURE if (!device) { return SDL_InvalidParamError("device"); } return GetNumFrameSizes(device, format); -#else - return 0; -#endif /* SDL_VIDEO_CAPTURE */ } int -SDL_GetVideoCaptureFrameSize(SDL_VideoCaptureDevice *device, Uint32 format, int index, int *width, int *height) +SDL_GetCameraFrameSize(SDL_CameraDevice *device, Uint32 format, int index, int *width, int *height) { -#ifdef SDL_VIDEO_CAPTURE if (!device) { return SDL_InvalidParamError("device"); } @@ -767,79 +702,61 @@ SDL_GetVideoCaptureFrameSize(SDL_VideoCaptureDevice *device, Uint32 format, int *width = 0; *height = 0; return GetFrameSize(device, format, index, width, height); -#else - return SDL_Unsupported(); -#endif } -SDL_VideoCaptureDevice * -SDL_OpenVideoCaptureWithSpec( - SDL_VideoCaptureDeviceID instance_id, - const SDL_VideoCaptureSpec *desired, - SDL_VideoCaptureSpec *obtained, +SDL_CameraDevice * +SDL_OpenCameraWithSpec( + SDL_CameraDeviceID instance_id, + const SDL_CameraSpec *desired, + SDL_CameraSpec *obtained, int allowed_changes) { -#ifdef SDL_VIDEO_CAPTURE - SDL_VideoCaptureDevice *device; + SDL_CameraDevice *device; - if ((device = SDL_OpenVideoCapture(instance_id)) == NULL) { + if ((device = SDL_OpenCamera(instance_id)) == NULL) { return NULL; } - if (SDL_SetVideoCaptureSpec(device, desired, obtained, allowed_changes) < 0) { - SDL_CloseVideoCapture(device); + if (SDL_SetCameraSpec(device, desired, obtained, allowed_changes) < 0) { + SDL_CloseCamera(device); return NULL; } return device; -#else - SDL_Unsupported(); - return NULL; -#endif } -SDL_VideoCaptureStatus -SDL_GetVideoCaptureStatus(SDL_VideoCaptureDevice *device) +SDL_CameraStatus +SDL_GetCameraStatus(SDL_CameraDevice *device) { -#ifdef SDL_VIDEO_CAPTURE if (device == NULL) { - return SDL_VIDEO_CAPTURE_INIT; + return SDL_CAMERA_INIT; } if (device->is_spec_set == SDL_FALSE) { - return SDL_VIDEO_CAPTURE_INIT; + return SDL_CAMERA_INIT; } if (SDL_AtomicGet(&device->shutdown)) { - return SDL_VIDEO_CAPTURE_STOPPED; + return SDL_CAMERA_STOPPED; } if (SDL_AtomicGet(&device->enabled)) { - return SDL_VIDEO_CAPTURE_PLAYING; + return SDL_CAMERA_PLAYING; } - return SDL_VIDEO_CAPTURE_INIT; -#else - SDL_Unsupported(); - return SDL_VIDEO_CAPTURE_FAIL; -#endif + return SDL_CAMERA_INIT; } int -SDL_VideoCaptureInit(void) +SDL_CameraInit(void) { -#ifdef SDL_VIDEO_CAPTURE SDL_zeroa(open_devices); - SDL_SYS_VideoCaptureInit(); - return 0; -#else + SDL_SYS_CameraInit(); return 0; -#endif } void -SDL_QuitVideoCapture(void) +SDL_QuitCamera(void) { -#ifdef SDL_VIDEO_CAPTURE int i, n = SDL_arraysize(open_devices); for (i = 0; i < n; i++) { close_device(open_devices[i]); @@ -847,122 +764,6 @@ SDL_QuitVideoCapture(void) SDL_zeroa(open_devices); - SDL_SYS_VideoCaptureQuit(); -#endif -} - -#ifdef SDL_VIDEO_CAPTURE - -#if defined(SDL_PLATFORM_LINUX) && !defined(SDL_PLATFORM_ANDROID) - -/* See SDL_video_capture_v4l2.c */ - -#elif defined(SDL_PLATFORM_ANDROID) && __ANDROID_API__ >= 24 - -/* See SDL_android_video_capture.c */ - -#elif defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_MACOS) - -/* See SDL_video_capture_apple.m */ -#else - -int SDL_SYS_VideoCaptureInit(void) -{ - return 0; + SDL_SYS_CameraQuit(); } -int SDL_SYS_VideoCaptureQuit(void) -{ - return 0; -} - -int -OpenDevice(SDL_VideoCaptureDevice *_this) -{ - return SDL_SetError("not implemented"); -} - -void -CloseDevice(SDL_VideoCaptureDevice *_this) -{ - return; -} - -int -InitDevice(SDL_VideoCaptureDevice *_this) -{ - size_t size, pitch; - SDL_CalculateSize(_this->spec.format, _this->spec.width, _this->spec.height, &size, &pitch, SDL_FALSE); - SDL_Log("Buffer size: %d x %d", _this->spec.width, _this->spec.height); - return -1; -} - -int -GetDeviceSpec(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureSpec *spec) -{ - return SDL_Unsupported(); -} - -int -StartCapture(SDL_VideoCaptureDevice *_this) -{ - return SDL_Unsupported(); -} - -int -StopCapture(SDL_VideoCaptureDevice *_this) -{ - return -1; -} - -int -AcquireFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) -{ - return -1; -} - -int -ReleaseFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) -{ - return -1; -} - -int -GetNumFormats(SDL_VideoCaptureDevice *_this) -{ - return -1; -} - -int -GetFormat(SDL_VideoCaptureDevice *_this, int index, Uint32 *format) -{ - return -1; -} - -int -GetNumFrameSizes(SDL_VideoCaptureDevice *_this, Uint32 format) -{ - return -1; -} - -int -GetFrameSize(SDL_VideoCaptureDevice *_this, Uint32 format, int index, int *width, int *height) -{ - return -1; -} - -int -GetDeviceName(SDL_VideoCaptureDeviceID instance_id, char *buf, int size) -{ - return -1; -} - -SDL_VideoCaptureDeviceID * -GetVideoCaptureDevices(int *count) -{ - return NULL; -} - -#endif - -#endif /* SDL_VIDEO_CAPTURE */ diff --git a/src/camera/SDL_camera_c.h b/src/camera/SDL_camera_c.h index dc4b342933d7e..1b3ea36e85dea 100644 --- a/src/camera/SDL_camera_c.h +++ b/src/camera/SDL_camera_c.h @@ -19,15 +19,14 @@ 3. This notice may not be removed or altered from any source distribution. */ #include "../SDL_internal.h" -#include "../../include/SDL3/SDL_camera.h" -#ifndef SDL_video_capture_c_h_ -#define SDL_video_capture_c_h_ +#ifndef SDL_camera_c_h_ +#define SDL_camera_c_h_ -/* Initialize the video_capture subsystem */ -int SDL_VideoCaptureInit(void); +/* Initialize the camera subsystem */ +int SDL_CameraInit(void); -/* Shutdown the video_capture subsystem */ -void SDL_QuitVideoCapture(void); +/* Shutdown the camera subsystem */ +void SDL_QuitCamera(void); -#endif /* SDL_video_capture_c_h_ */ +#endif /* SDL_camera_c_h_ */ diff --git a/src/camera/SDL_syscamera.h b/src/camera/SDL_syscamera.h index 9db178e600996..cb0ce2c6f2a51 100644 --- a/src/camera/SDL_syscamera.h +++ b/src/camera/SDL_syscamera.h @@ -20,22 +20,22 @@ */ #include "../SDL_internal.h" -#ifndef SDL_sysvideocapture_h_ -#define SDL_sysvideocapture_h_ +#ifndef SDL_syscamera_h_ +#define SDL_syscamera_h_ #include "../SDL_list.h" -/* The SDL video_capture driver */ -typedef struct SDL_VideoCaptureDevice SDL_VideoCaptureDevice; +/* The SDL camera driver */ +typedef struct SDL_CameraDevice SDL_CameraDevice; -/* Define the SDL video_capture driver structure */ -struct SDL_VideoCaptureDevice +/* Define the SDL camera driver structure */ +struct SDL_CameraDevice { /* * * */ /* Data common to all devices */ - /* The device's current video_capture specification */ - SDL_VideoCaptureSpec spec; + /* The device's current camera specification */ + SDL_CameraSpec spec; /* Device name */ char *dev_name; @@ -49,7 +49,7 @@ struct SDL_VideoCaptureDevice SDL_Mutex *device_lock; SDL_Mutex *acquiring_lock; - /* A thread to feed the video_capture device */ + /* A thread to feed the camera device */ SDL_Thread *thread; SDL_ThreadID threadid; @@ -58,35 +58,35 @@ struct SDL_VideoCaptureDevice /* * * */ /* Data private to this driver */ - struct SDL_PrivateVideoCaptureData *hidden; + struct SDL_PrivateCameraData *hidden; }; -extern int SDL_SYS_VideoCaptureInit(void); -extern int SDL_SYS_VideoCaptureQuit(void); +extern int SDL_SYS_CameraInit(void); +extern int SDL_SYS_CameraQuit(void); -extern int OpenDevice(SDL_VideoCaptureDevice *_this); -extern void CloseDevice(SDL_VideoCaptureDevice *_this); +extern int OpenDevice(SDL_CameraDevice *_this); +extern void CloseDevice(SDL_CameraDevice *_this); -extern int InitDevice(SDL_VideoCaptureDevice *_this); +extern int InitDevice(SDL_CameraDevice *_this); -extern int GetDeviceSpec(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureSpec *spec); +extern int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec); -extern int StartCapture(SDL_VideoCaptureDevice *_this); -extern int StopCapture(SDL_VideoCaptureDevice *_this); +extern int StartCamera(SDL_CameraDevice *_this); +extern int StopCamera(SDL_CameraDevice *_this); -extern int AcquireFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame); -extern int ReleaseFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame); +extern int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame); +extern int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame); -extern int GetNumFormats(SDL_VideoCaptureDevice *_this); -extern int GetFormat(SDL_VideoCaptureDevice *_this, int index, Uint32 *format); +extern int GetNumFormats(SDL_CameraDevice *_this); +extern int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format); -extern int GetNumFrameSizes(SDL_VideoCaptureDevice *_this, Uint32 format); -extern int GetFrameSize(SDL_VideoCaptureDevice *_this, Uint32 format, int index, int *width, int *height); +extern int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format); +extern int GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height); -extern int GetDeviceName(SDL_VideoCaptureDeviceID instance_id, char *buf, int size); -extern SDL_VideoCaptureDeviceID *GetVideoCaptureDevices(int *count); +extern int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size); +extern SDL_CameraDeviceID *GetCameraDevices(int *count); extern SDL_bool check_all_device_closed(void); extern SDL_bool check_device_playing(void); -#endif /* SDL_sysvideocapture_h_ */ +#endif /* SDL_syscamera_h_ */ diff --git a/src/camera/android/SDL_camera_android.c b/src/camera/android/SDL_camera_android.c index ff157cbd41c93..b00e183345023 100644 --- a/src/camera/android/SDL_camera_android.c +++ b/src/camera/android/SDL_camera_android.c @@ -20,16 +20,14 @@ */ #include "SDL_internal.h" -#include "SDL3/SDL.h" -#include "SDL3/SDL_video_capture.h" -#include "../SDL_sysvideocapture.h" -#include "../SDL_video_capture_c.h" -#include "../SDL_pixels_c.h" +#include "../SDL_syscamera.h" +#include "../SDL_camera_c.h" +#include "../../video/SDL_pixels_c.h" #include "../../thread/SDL_systhread.h" -#define DEBUG_VIDEO_CAPTURE_CAPTURE 0 +#define DEBUG_CAMERA 1 -#if defined(SDL_PLATFORM_ANDROID) && __ANDROID_API__ >= 24 +#if defined(SDL_CAMERA_ANDROID) && __ANDROID_API__ >= 24 /* * APP_PLATFORM=android-24 @@ -42,7 +40,7 @@ * * * - * Add: #define SDL_VIDEO_CAPTURE 1 + * Add: #define SDL_CAMERA 1 * in: include/build_config/SDL_build_config_android.h * * @@ -97,7 +95,7 @@ delete_cameraMgr(void) } } -struct SDL_PrivateVideoCaptureData +struct SDL_PrivateCameraData { ACameraDevice *device; ACameraCaptureSession *session; @@ -186,14 +184,14 @@ format_sdl_2_android(Uint32 fmt) static void onDisconnected(void *context, ACameraDevice *device) { - // SDL_VideoCaptureDevice *_this = (SDL_VideoCaptureDevice *) context; + // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; SDL_Log("CB onDisconnected"); } static void onError(void *context, ACameraDevice *device, int error) { - // SDL_VideoCaptureDevice *_this = (SDL_VideoCaptureDevice *) context; + // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; SDL_Log("CB onError"); } @@ -201,26 +199,26 @@ onError(void *context, ACameraDevice *device, int error) static void onClosed(void* context, ACameraCaptureSession *session) { - // SDL_VideoCaptureDevice *_this = (SDL_VideoCaptureDevice *) context; + // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; SDL_Log("CB onClosed"); } static void onReady(void* context, ACameraCaptureSession *session) { - // SDL_VideoCaptureDevice *_this = (SDL_VideoCaptureDevice *) context; + // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; SDL_Log("CB onReady"); } static void onActive(void* context, ACameraCaptureSession *session) { - // SDL_VideoCaptureDevice *_this = (SDL_VideoCaptureDevice *) context; + // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; SDL_Log("CB onActive"); } int -OpenDevice(SDL_VideoCaptureDevice *_this) +OpenDevice(SDL_CameraDevice *_this) { camera_status_t res; @@ -236,7 +234,7 @@ OpenDevice(SDL_VideoCaptureDevice *_this) return SDL_SetError("A camera is already playing"); } - _this->hidden = (struct SDL_PrivateVideoCaptureData *) SDL_calloc(1, sizeof (struct SDL_PrivateVideoCaptureData)); + _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); if (_this->hidden == NULL) { return SDL_OutOfMemory(); } @@ -256,7 +254,7 @@ OpenDevice(SDL_VideoCaptureDevice *_this) } void -CloseDevice(SDL_VideoCaptureDevice *_this) +CloseDevice(SDL_CameraDevice *_this) { if (_this && _this->hidden) { if (_this->hidden->session) { @@ -286,7 +284,7 @@ CloseDevice(SDL_VideoCaptureDevice *_this) } int -InitDevice(SDL_VideoCaptureDevice *_this) +InitDevice(SDL_CameraDevice *_this) { size_t size, pitch; SDL_CalculateSize(_this->spec.format, _this->spec.width, _this->spec.height, &size, &pitch, SDL_FALSE); @@ -295,7 +293,7 @@ InitDevice(SDL_VideoCaptureDevice *_this) } int -GetDeviceSpec(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureSpec *spec) +GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) { if (spec) { *spec = _this->spec; @@ -305,7 +303,7 @@ GetDeviceSpec(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureSpec *spec) } int -StartCapture(SDL_VideoCaptureDevice *_this) +StartCamera(SDL_CameraDevice *_this) { camera_status_t res; media_status_t res2; @@ -391,7 +389,7 @@ StartCapture(SDL_VideoCaptureDevice *_this) } int -StopCapture(SDL_VideoCaptureDevice *_this) +StopCamera(SDL_CameraDevice *_this) { ACameraCaptureSession_close(_this->hidden->session); _this->hidden->session = NULL; @@ -399,7 +397,7 @@ StopCapture(SDL_VideoCaptureDevice *_this) } int -AcquireFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) +AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { media_status_t res; AImage *image; @@ -410,7 +408,7 @@ AcquireFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) if (res == AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE ) { SDL_Delay(20); // TODO fix some delay -#if DEBUG_VIDEO_CAPTURE_CAPTURE +#if DEBUG_CAMERA // SDL_Log("AImageReader_acquireNextImage: AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE"); #endif return 0; @@ -455,7 +453,7 @@ AcquireFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) } int -ReleaseFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) +ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { if (frame->internal){ AImage_delete((AImage *)frame->internal); @@ -464,7 +462,7 @@ ReleaseFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) } int -GetNumFormats(SDL_VideoCaptureDevice *_this) +GetNumFormats(SDL_CameraDevice *_this) { camera_status_t res; int i; @@ -500,7 +498,7 @@ GetNumFormats(SDL_VideoCaptureDevice *_this) fmt = format_android_2_sdl(format); _this->hidden->count_formats[format_2_id(fmt)] += 1; -#if DEBUG_VIDEO_CAPTURE_CAPTURE +#if DEBUG_CAMERA if (fmt != SDL_PIXELFORMAT_UNKNOWN) { int w = entry.data.i32[i + 1]; int h = entry.data.i32[i + 2]; @@ -511,7 +509,7 @@ GetNumFormats(SDL_VideoCaptureDevice *_this) #endif } -#if DEBUG_VIDEO_CAPTURE_CAPTURE +#if DEBUG_CAMERA if (unknown) { SDL_Log("Got unknown android"); } @@ -529,7 +527,7 @@ GetNumFormats(SDL_VideoCaptureDevice *_this) } int -GetFormat(SDL_VideoCaptureDevice *_this, int index, Uint32 *format) +GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) { int i; int i2 = 0; @@ -558,7 +556,7 @@ GetFormat(SDL_VideoCaptureDevice *_this, int index, Uint32 *format) } int -GetNumFrameSizes(SDL_VideoCaptureDevice *_this, Uint32 format) +GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) { int i, i2 = 0, index; if (_this->hidden->num_formats == 0) { @@ -584,7 +582,7 @@ GetNumFrameSizes(SDL_VideoCaptureDevice *_this, Uint32 format) } int -GetFrameSize(SDL_VideoCaptureDevice *_this, Uint32 format, int index, int *width, int *height) +GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) { camera_status_t res; int i, i2 = 0; @@ -636,7 +634,7 @@ GetFrameSize(SDL_VideoCaptureDevice *_this, Uint32 format, int index, int *width static int GetNumDevices(void); int -GetDeviceName(SDL_VideoCaptureDeviceID instance_id, char *buf, int size) +GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) { int index = instance_id - 1; create_cameraMgr(); @@ -676,14 +674,14 @@ GetNumDevices(void) return -1; } -SDL_VideoCaptureDeviceID *GetVideoCaptureDevices(int *count) +SDL_CameraDeviceID *GetCameraDevices(int *count) { /* hard-coded list of ID */ int i; int num = GetNumDevices(); - SDL_VideoCaptureDeviceID *ret; + SDL_CameraDeviceID *ret; - ret = (SDL_VideoCaptureDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); + ret = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); if (ret == NULL) { SDL_OutOfMemory(); @@ -699,15 +697,14 @@ SDL_VideoCaptureDeviceID *GetVideoCaptureDevices(int *count) return ret; } -int SDL_SYS_VideoCaptureInit(void) { +int SDL_SYS_CameraInit(void) { return 0; } -int SDL_SYS_VideoCaptureQuit(void) { +int SDL_SYS_CameraQuit(void) { return 0; } - #endif diff --git a/src/camera/apple/SDL_camera_apple.m b/src/camera/apple/SDL_camera_apple.m index 40561263b9438..b0a187349c2b2 100644 --- a/src/camera/apple/SDL_camera_apple.m +++ b/src/camera/apple/SDL_camera_apple.m @@ -20,12 +20,10 @@ */ #include "SDL_internal.h" -#ifdef SDL_VIDEO_CAPTURE +#ifdef SDL_CAMERA_APPLE -#include "SDL3/SDL.h" -#include "SDL3/SDL_video_capture.h" -#include "SDL_sysvideocapture.h" -#include "SDL_video_capture_c.h" +#include "../SDL_syscamera.h" +#include "../SDL_camera_c.h" #include "../thread/SDL_systhread.h" #if defined(HAVE_COREMEDIA) && defined(SDL_PLATFORM_MACOS) && (__MAC_OS_X_VERSION_MAX_ALLOWED < 101500) @@ -37,52 +35,52 @@ #undef HAVE_COREMEDIA #endif -#ifndef HAVE_COREMEDIA -int InitDevice(SDL_VideoCaptureDevice *_this) { +#ifndef HAVE_COREMEDIA /* !!! FIXME: use the dummy driver. */ +int InitDevice(SDL_CameraDevice *_this) { return -1; } -int OpenDevice(SDL_VideoCaptureDevice *_this) { +int OpenDevice(SDL_CameraDevice *_this) { return -1; } -int AcquireFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) { +int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { return -1; } -void CloseDevice(SDL_VideoCaptureDevice *_this) { +void CloseDevice(SDL_CameraDevice *_this) { } -int GetDeviceName(SDL_VideoCaptureDeviceID instance_id, char *buf, int size) { +int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) { return -1; } -int GetDeviceSpec(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureSpec *spec) { +int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) { return -1; } -int GetFormat(SDL_VideoCaptureDevice *_this, int index, Uint32 *format) { +int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) { return -1; } -int GetFrameSize(SDL_VideoCaptureDevice *_this, Uint32 format, int index, int *width, int *height) { +int GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) { return -1; } -SDL_VideoCaptureDeviceID *GetVideoCaptureDevices(int *count) { +SDL_CameraDeviceID *GetCameraDevices(int *count) { return NULL; } -int GetNumFormats(SDL_VideoCaptureDevice *_this) { +int GetNumFormats(SDL_CameraDevice *_this) { return 0; } -int GetNumFrameSizes(SDL_VideoCaptureDevice *_this, Uint32 format) { +int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) { return 0; } -int ReleaseFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) { +int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { return 0; } -int StartCapture(SDL_VideoCaptureDevice *_this) { +int StartCapture(SDL_CameraDevice *_this) { return 0; } -int StopCapture(SDL_VideoCaptureDevice *_this) { +int StopCapture(SDL_CameraDevice *_this) { return 0; } -int SDL_SYS_VideoCaptureInit(void) { +int SDL_SYS_CameraInit(void) { return 0; } -int SDL_SYS_VideoCaptureQuit(void) { +int SDL_SYS_CameraQuit(void) { return 0; } @@ -107,13 +105,13 @@ int SDL_SYS_VideoCaptureQuit(void) { * IOS: * * - Need to link with:: CoreMedia CoreVideo - * - Add #define SDL_VIDEO_CAPTURE 1 + * - Add #define SDL_CAMERA 1 * to SDL_build_config_ios.h */ @class MySampleBufferDelegate; -struct SDL_PrivateVideoCaptureData +struct SDL_PrivateCameraData { dispatch_queue_t queue; MySampleBufferDelegate *delegate; @@ -216,13 +214,13 @@ int SDL_SYS_VideoCaptureQuit(void) { @interface MySampleBufferDelegate : NSObject - @property struct SDL_PrivateVideoCaptureData *hidden; - - (void) set: (struct SDL_PrivateVideoCaptureData *) val; + @property struct SDL_PrivateCameraData *hidden; + - (void) set: (struct SDL_PrivateCameraData *) val; @end @implementation MySampleBufferDelegate - - (void) set: (struct SDL_PrivateVideoCaptureData *) val { + - (void) set: (struct SDL_PrivateCameraData *) val { _hidden = val; } @@ -241,9 +239,9 @@ - (void)captureOutput:(AVCaptureOutput *)output @end int -OpenDevice(SDL_VideoCaptureDevice *_this) +OpenDevice(SDL_CameraDevice *_this) { - _this->hidden = (struct SDL_PrivateVideoCaptureData *) SDL_calloc(1, sizeof (struct SDL_PrivateVideoCaptureData)); + _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); if (_this->hidden == NULL) { SDL_OutOfMemory(); goto error; @@ -256,7 +254,7 @@ - (void)captureOutput:(AVCaptureOutput *)output } void -CloseDevice(SDL_VideoCaptureDevice *_this) +CloseDevice(SDL_CameraDevice *_this) { if (!_this) { return; @@ -285,7 +283,7 @@ - (void)captureOutput:(AVCaptureOutput *)output } int -InitDevice(SDL_VideoCaptureDevice *_this) +InitDevice(SDL_CameraDevice *_this) { NSString *fmt = sdlformat_to_nsfourcc(_this->spec.format); int w = _this->spec.width; @@ -405,7 +403,7 @@ - (void)captureOutput:(AVCaptureOutput *)output } int -GetDeviceSpec(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureSpec *spec) +GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) { if (spec) { *spec = _this->spec; @@ -415,21 +413,21 @@ - (void)captureOutput:(AVCaptureOutput *)output } int -StartCapture(SDL_VideoCaptureDevice *_this) +StartCamera(SDL_CameraDevice *_this) { [_this->hidden->session startRunning]; return 0; } int -StopCapture(SDL_VideoCaptureDevice *_this) +StopCamera(SDL_CameraDevice *_this) { [_this->hidden->session stopRunning]; return 0; } int -AcquireFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) +AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { if (CMSimpleQueueGetCount(_this->hidden->frame_queue) > 0) { int i, numPlanes, planar; @@ -482,7 +480,7 @@ - (void)captureOutput:(AVCaptureOutput *)output } int -ReleaseFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) +ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { if (frame->internal){ CMSampleBufferRef sampleBuffer = (CMSampleBufferRef) frame->internal; @@ -496,7 +494,7 @@ - (void)captureOutput:(AVCaptureOutput *)output } int -GetNumFormats(SDL_VideoCaptureDevice *_this) +GetNumFormats(SDL_CameraDevice *_this) { AVCaptureDevice *device = get_device_by_name(_this->dev_name); if (device) { @@ -517,7 +515,7 @@ - (void)captureOutput:(AVCaptureOutput *)output } int -GetFormat(SDL_VideoCaptureDevice *_this, int index, Uint32 *format) +GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) { AVCaptureDevice *device = get_device_by_name(_this->dev_name); if (device) { @@ -545,7 +543,7 @@ - (void)captureOutput:(AVCaptureOutput *)output } int -GetNumFrameSizes(SDL_VideoCaptureDevice *_this, Uint32 format) +GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) { AVCaptureDevice *device = get_device_by_name(_this->dev_name); if (device) { @@ -568,7 +566,7 @@ - (void)captureOutput:(AVCaptureOutput *)output } int -GetFrameSize(SDL_VideoCaptureDevice *_this, Uint32 format, int index, int *width, int *height) +GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) { AVCaptureDevice *device = get_device_by_name(_this->dev_name); if (device) { @@ -596,7 +594,7 @@ - (void)captureOutput:(AVCaptureOutput *)output } int -GetDeviceName(SDL_VideoCaptureDeviceID instance_id, char *buf, int size) +GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) { int index = instance_id - 1; NSArray *devices = discover_devices(); @@ -617,14 +615,14 @@ - (void)captureOutput:(AVCaptureOutput *)output return [devices count]; } -SDL_VideoCaptureDeviceID *GetVideoCaptureDevices(int *count) +SDL_CameraDeviceID *GetCameraDevices(int *count) { /* hard-coded list of ID */ int i; int num = GetNumDevices(); - SDL_VideoCaptureDeviceID *ret; + SDL_CameraDeviceID *ret; - ret = (SDL_VideoCaptureDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); + ret = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); if (ret == NULL) { SDL_OutOfMemory(); @@ -640,20 +638,17 @@ - (void)captureOutput:(AVCaptureOutput *)output return ret; } -int SDL_SYS_VideoCaptureInit(void) +int SDL_SYS_CameraInit(void) { return 0; } -int SDL_SYS_VideoCaptureQuit(void) +int SDL_SYS_CameraQuit(void) { return 0; } - - - #endif /* HAVE_COREMEDIA */ -#endif /* SDL_VIDEO_CAPTURE */ +#endif /* SDL_CAMERA_APPLE */ diff --git a/src/camera/v4l2/SDL_camera_v4l2.c b/src/camera/v4l2/SDL_camera_v4l2.c index 01e46d709fa64..a5440ca4b8316 100644 --- a/src/camera/v4l2/SDL_camera_v4l2.c +++ b/src/camera/v4l2/SDL_camera_v4l2.c @@ -20,46 +20,41 @@ */ #include "SDL_internal.h" -#ifdef SDL_VIDEO_CAPTURE - -#include "SDL3/SDL.h" -#include "SDL3/SDL_video_capture.h" -#include "SDL_sysvideocapture.h" -#include "SDL_video_capture_c.h" -#include "SDL_pixels_c.h" -#include "../thread/SDL_systhread.h" +#ifdef SDL_CAMERA_V4L2 + +#include "../SDL_syscamera.h" +#include "../SDL_camera_c.h" +#include "../../video/SDL_pixels_c.h" +#include "../../thread/SDL_systhread.h" #include "../../core/linux/SDL_evdev_capabilities.h" #include "../../core/linux/SDL_udev.h" #include /* INT_MAX */ -#define DEBUG_VIDEO_CAPTURE_CAPTURE 0 - -#if defined(SDL_PLATFORM_LINUX) && !defined(SDL_PLATFORM_ANDROID) +#define DEBUG_CAMERA 1 - -#define MAX_CAPTURE_DEVICES 128 /* It's doubtful someone has more than that */ +#define MAX_CAMERA_DEVICES 128 /* It's doubtful someone has more than that */ static int MaybeAddDevice(const char *path); #ifdef SDL_USE_LIBUDEV static int MaybeRemoveDevice(const char *path); -static void capture_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath); +static void camera_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath); #endif /* SDL_USE_LIBUDEV */ /* - * List of available capture devices. + * List of available camera devices. */ -typedef struct SDL_capturelist_item +typedef struct SDL_cameralist_item { char *fname; /* Dev path name (like /dev/video0) */ char *bus_info; /* don't add two paths with same bus_info (eg /dev/video0 and /dev/video1 */ - SDL_VideoCaptureDeviceID instance_id; - SDL_VideoCaptureDevice *device; /* Associated device */ - struct SDL_capturelist_item *next; -} SDL_capturelist_item; + SDL_CameraDeviceID instance_id; + SDL_CameraDevice *device; /* Associated device */ + struct SDL_cameralist_item *next; +} SDL_cameralist_item; -static SDL_capturelist_item *SDL_capturelist = NULL; -static SDL_capturelist_item *SDL_capturelist_tail = NULL; -static int num_video_captures = 0; +static SDL_cameralist_item *SDL_cameralist = NULL; +static SDL_cameralist_item *SDL_cameralist_tail = NULL; +static int num_cameras = 0; @@ -75,7 +70,7 @@ struct buffer { int available; /* Is available in userspace */ }; -struct SDL_PrivateVideoCaptureData +struct SDL_PrivateCameraData { int fd; enum io_method io; @@ -107,7 +102,7 @@ xioctl(int fh, int request, void *arg) /* -1:error 1:frame 0:no frame*/ static int -acquire_frame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) +acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { struct v4l2_buffer buf; int i; @@ -168,7 +163,7 @@ acquire_frame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) frame->pitch[0] = _this->hidden->driver_pitch; _this->hidden->buffers[buf.index].available = 1; -#if DEBUG_VIDEO_CAPTURE_CAPTURE +#if DEBUG_CAMERA SDL_Log("debug mmap: image %d/%d num_planes:%d data[0]=%p", buf.index, _this->hidden->nb_buffers, frame->num_planes, (void*)frame->data[0]); #endif break; @@ -208,7 +203,7 @@ acquire_frame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) frame->data[0] = (void*)buf.m.userptr; frame->pitch[0] = _this->hidden->driver_pitch; _this->hidden->buffers[i].available = 1; -#if DEBUG_VIDEO_CAPTURE_CAPTURE +#if DEBUG_CAMERA SDL_Log("debug userptr: image %d/%d num_planes:%d data[0]=%p", buf.index, _this->hidden->nb_buffers, frame->num_planes, (void*)frame->data[0]); #endif break; @@ -219,7 +214,7 @@ acquire_frame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) int -ReleaseFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) +ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { struct v4l2_buffer buf; int i; @@ -274,7 +269,7 @@ ReleaseFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) int -AcquireFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) +AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { fd_set fds; struct timeval tv; @@ -293,7 +288,7 @@ AcquireFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) if (ret == -1) { if (errno == EINTR) { -#if DEBUG_VIDEO_CAPTURE_CAPTURE +#if DEBUG_CAMERA SDL_Log("continue .."); #endif return 0; @@ -315,7 +310,7 @@ AcquireFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) if (ret == 1){ frame->timestampNS = SDL_GetTicksNS(); } else if (ret == 0) { -#if DEBUG_VIDEO_CAPTURE_CAPTURE +#if DEBUG_CAMERA SDL_Log("No frame continue: %s", SDL_GetError()); #endif } @@ -326,7 +321,7 @@ AcquireFrame(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureFrame *frame) int -StopCapture(SDL_VideoCaptureDevice *_this) +StopCamera(SDL_CameraDevice *_this) { enum v4l2_buf_type type; int fd = _this->hidden->fd; @@ -349,7 +344,7 @@ StopCapture(SDL_VideoCaptureDevice *_this) } static int -enqueue_buffers(SDL_VideoCaptureDevice *_this) +enqueue_buffers(SDL_CameraDevice *_this) { int i; int fd = _this->hidden->fd; @@ -398,7 +393,7 @@ enqueue_buffers(SDL_VideoCaptureDevice *_this) } static int -pre_enqueue_buffers(SDL_VideoCaptureDevice *_this) +pre_enqueue_buffers(SDL_CameraDevice *_this) { struct v4l2_requestbuffers req; int fd = _this->hidden->fd; @@ -452,7 +447,7 @@ pre_enqueue_buffers(SDL_VideoCaptureDevice *_this) } int -StartCapture(SDL_VideoCaptureDevice *_this) +StartCamera(SDL_CameraDevice *_this) { enum v4l2_buf_type type; @@ -498,7 +493,7 @@ StartCapture(SDL_VideoCaptureDevice *_this) return 0; } -static int alloc_buffer_read(SDL_VideoCaptureDevice *_this, size_t buffer_size) +static int alloc_buffer_read(SDL_CameraDevice *_this, size_t buffer_size) { _this->hidden->buffers[0].length = buffer_size; _this->hidden->buffers[0].start = SDL_calloc(1, buffer_size); @@ -510,7 +505,7 @@ static int alloc_buffer_read(SDL_VideoCaptureDevice *_this, size_t buffer_size) } static int -alloc_buffer_mmap(SDL_VideoCaptureDevice *_this) +alloc_buffer_mmap(SDL_CameraDevice *_this) { int fd = _this->hidden->fd; int i; @@ -543,7 +538,7 @@ alloc_buffer_mmap(SDL_VideoCaptureDevice *_this) } static int -alloc_buffer_userp(SDL_VideoCaptureDevice *_this, size_t buffer_size) +alloc_buffer_userp(SDL_CameraDevice *_this, size_t buffer_size) { int i; for (i = 0; i < _this->hidden->nb_buffers; ++i) { @@ -585,7 +580,7 @@ format_sdl_2_v4l2(Uint32 fmt) } int -GetNumFormats(SDL_VideoCaptureDevice *_this) +GetNumFormats(SDL_CameraDevice *_this) { int fd = _this->hidden->fd; int i = 0; @@ -601,7 +596,7 @@ GetNumFormats(SDL_VideoCaptureDevice *_this) } int -GetFormat(SDL_VideoCaptureDevice *_this, int index, Uint32 *format) +GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) { int fd = _this->hidden->fd; struct v4l2_fmtdesc fmtdesc; @@ -612,7 +607,7 @@ GetFormat(SDL_VideoCaptureDevice *_this, int index, Uint32 *format) if (ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) == 0) { *format = format_v4l2_2_sdl(fmtdesc.pixelformat); -#if DEBUG_VIDEO_CAPTURE_CAPTURE +#if DEBUG_CAMERA if (fmtdesc.flags & V4L2_FMT_FLAG_EMULATED) { SDL_Log("%s format emulated", SDL_GetPixelFormatName(*format)); } @@ -627,7 +622,7 @@ GetFormat(SDL_VideoCaptureDevice *_this, int index, Uint32 *format) } int -GetNumFrameSizes(SDL_VideoCaptureDevice *_this, Uint32 format) +GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) { int fd = _this->hidden->fd; int i = 0; @@ -651,7 +646,7 @@ GetNumFrameSizes(SDL_VideoCaptureDevice *_this, Uint32 format) } int -GetFrameSize(SDL_VideoCaptureDevice *_this, Uint32 format, int index, int *width, int *height) +GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) { int fd = _this->hidden->fd; struct v4l2_frmsizeenum frmsizeenum; @@ -704,7 +699,7 @@ dbg_v4l2_pixelformat(const char *str, int f) { #endif int -GetDeviceSpec(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureSpec *spec) +GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) { struct v4l2_format fmt; int fd = _this->hidden->fd; @@ -737,7 +732,7 @@ GetDeviceSpec(SDL_VideoCaptureDevice *_this, SDL_VideoCaptureSpec *spec) } int -InitDevice(SDL_VideoCaptureDevice *_this) +InitDevice(SDL_CameraDevice *_this) { struct v4l2_cropcap cropcap; struct v4l2_crop crop; @@ -783,7 +778,7 @@ InitDevice(SDL_VideoCaptureDevice *_this) // fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; fmt.fmt.pix.field = V4L2_FIELD_ANY; -#if DEBUG_VIDEO_CAPTURE_CAPTURE +#if DEBUG_CAMERA SDL_Log("set SDL format %s", SDL_GetPixelFormatName(_this->spec.format)); dbg_v4l2_pixelformat("set format", fmt.fmt.pix.pixelformat); #endif @@ -833,7 +828,7 @@ InitDevice(SDL_VideoCaptureDevice *_this) } void -CloseDevice(SDL_VideoCaptureDevice *_this) +CloseDevice(SDL_CameraDevice *_this) { if (!_this) { return; @@ -869,7 +864,7 @@ CloseDevice(SDL_VideoCaptureDevice *_this) if (_this->hidden->fd != -1) { if (close(_this->hidden->fd)) { - SDL_SetError("close video capture device"); + SDL_SetError("close camera device"); } } SDL_free(_this->hidden); @@ -880,14 +875,14 @@ CloseDevice(SDL_VideoCaptureDevice *_this) int -OpenDevice(SDL_VideoCaptureDevice *_this) +OpenDevice(SDL_CameraDevice *_this) { struct stat st; struct v4l2_capability cap; int fd; enum io_method io; - _this->hidden = (struct SDL_PrivateVideoCaptureData *) SDL_calloc(1, sizeof (struct SDL_PrivateVideoCaptureData)); + _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); if (_this->hidden == NULL) { SDL_OutOfMemory(); return -1; @@ -966,10 +961,10 @@ OpenDevice(SDL_VideoCaptureDevice *_this) } int -GetDeviceName(SDL_VideoCaptureDeviceID instance_id, char *buf, int size) +GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) { - SDL_capturelist_item *item; - for (item = SDL_capturelist; item; item = item->next) { + SDL_cameralist_item *item; + for (item = SDL_cameralist; item; item = item->next) { if (item->instance_id == instance_id) { SDL_snprintf(buf, size, "%s", item->fname); return 0; @@ -981,15 +976,15 @@ GetDeviceName(SDL_VideoCaptureDeviceID instance_id, char *buf, int size) } -SDL_VideoCaptureDeviceID *GetVideoCaptureDevices(int *count) +SDL_CameraDeviceID *GetCameraDevices(int *count) { /* real list of ID */ int i = 0; - int num = num_video_captures; - SDL_VideoCaptureDeviceID *ret; - SDL_capturelist_item *item; + int num = num_cameras; + SDL_CameraDeviceID *ret; + SDL_cameralist_item *item; - ret = (SDL_VideoCaptureDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); + ret = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); if (ret == NULL) { SDL_OutOfMemory(); @@ -997,7 +992,7 @@ SDL_VideoCaptureDeviceID *GetVideoCaptureDevices(int *count) return NULL; } - for (item = SDL_capturelist; item; item = item->next) { + for (item = SDL_cameralist; item; item = item->next) { ret[i] = item->instance_id; i++; } @@ -1011,18 +1006,18 @@ SDL_VideoCaptureDeviceID *GetVideoCaptureDevices(int *count) /* * Initializes the subsystem by finding available devices. */ -int SDL_SYS_VideoCaptureInit(void) +int SDL_SYS_CameraInit(void) { const char pattern[] = "/dev/video%d"; char path[PATH_MAX]; int i, j; /* - * Limit amount of checks to MAX_CAPTURE_DEVICES since we may or may not have + * Limit amount of checks to MAX_CAMERA_DEVICES since we may or may not have * permission to some or all devices. */ i = 0; - for (j = 0; j < MAX_CAPTURE_DEVICES; ++j) { + for (j = 0; j < MAX_CAMERA_DEVICES; ++j) { (void)SDL_snprintf(path, PATH_MAX, pattern, i++); if (MaybeAddDevice(path) == -2) { break; @@ -1034,7 +1029,7 @@ int SDL_SYS_VideoCaptureInit(void) return SDL_SetError("Could not initialize UDEV"); } - if (SDL_UDEV_AddCallback(capture_udev_callback) < 0) { + if (SDL_UDEV_AddCallback(camera_udev_callback) < 0) { SDL_UDEV_Quit(); return SDL_SetError("Could not setup Video Capture <-> udev callback"); } @@ -1043,15 +1038,15 @@ int SDL_SYS_VideoCaptureInit(void) SDL_UDEV_Scan(); #endif /* SDL_USE_LIBUDEV */ - return num_video_captures; + return num_cameras; } -int SDL_SYS_VideoCaptureQuit(void) +int SDL_SYS_CameraQuit(void) { - SDL_capturelist_item *item; - for (item = SDL_capturelist; item; ) { - SDL_capturelist_item *tmp = item->next; + SDL_cameralist_item *item; + for (item = SDL_cameralist; item; ) { + SDL_cameralist_item *tmp = item->next; SDL_free(item->fname); SDL_free(item->bus_info); @@ -1059,15 +1054,15 @@ int SDL_SYS_VideoCaptureQuit(void) item = tmp; } - num_video_captures = 0; - SDL_capturelist = NULL; - SDL_capturelist_tail = NULL; + num_cameras = 0; + SDL_cameralist = NULL; + SDL_cameralist_tail = NULL; return SDL_FALSE; } #ifdef SDL_USE_LIBUDEV -static void capture_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath) +static void camera_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath) { if (!devpath || !(udev_class & SDL_UDEV_DEVICE_VIDEO_CAPTURE)) { return; @@ -1089,9 +1084,9 @@ static void capture_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class #endif /* SDL_USE_LIBUDEV */ static SDL_bool DeviceExists(const char *path, const char *bus_info) { - SDL_capturelist_item *item; + SDL_cameralist_item *item; - for (item = SDL_capturelist; item; item = item->next) { + for (item = SDL_cameralist; item; item = item->next) { /* found same dev name */ if (SDL_strcmp(path, item->fname) == 0) { return SDL_TRUE; @@ -1110,7 +1105,7 @@ static int MaybeAddDevice(const char *path) struct v4l2_capability vcap; int err; int fd; - SDL_capturelist_item *item; + SDL_cameralist_item *item; if (!path) { return -1; @@ -1135,7 +1130,7 @@ static int MaybeAddDevice(const char *path) /* Add new item */ - item = (SDL_capturelist_item *)SDL_calloc(1, sizeof(SDL_capturelist_item)); + item = (SDL_cameralist_item *)SDL_calloc(1, sizeof(SDL_cameralist_item)); if (!item) { SDL_free(bus_info); return -1; @@ -1153,18 +1148,18 @@ static int MaybeAddDevice(const char *path) item->instance_id = SDL_GetNextObjectID(); - if (!SDL_capturelist_tail) { - SDL_capturelist = SDL_capturelist_tail = item; + if (!SDL_cameralist_tail) { + SDL_cameralist = SDL_cameralist_tail = item; } else { - SDL_capturelist_tail->next = item; - SDL_capturelist_tail = item; + SDL_cameralist_tail->next = item; + SDL_cameralist_tail = item; } - ++num_video_captures; + ++num_cameras; /* !!! TODO: Send a add event? */ -#if DEBUG_VIDEO_CAPTURE_CAPTURE - SDL_Log("Added video capture ID: %d %s (%s) (total: %d)", item->instance_id, path, bus_info, num_video_captures); +#if DEBUG_CAMERA + SDL_Log("Added video camera ID: %d %s (%s) (total: %d)", item->instance_id, path, bus_info, num_cameras); #endif return 0; } @@ -1173,30 +1168,30 @@ static int MaybeAddDevice(const char *path) static int MaybeRemoveDevice(const char *path) { - SDL_capturelist_item *item; - SDL_capturelist_item *prev = NULL; -#if DEBUG_VIDEO_CAPTURE_CAPTURE - SDL_Log("Remove video capture %s", path); + SDL_cameralist_item *item; + SDL_cameralist_item *prev = NULL; +#if DEBUG_CAMERA + SDL_Log("Remove video camera %s", path); #endif if (!path) { return -1; } - for (item = SDL_capturelist; item; item = item->next) { + for (item = SDL_cameralist; item; item = item->next) { /* found it, remove it. */ if (SDL_strcmp(path, item->fname) == 0) { if (prev) { prev->next = item->next; } else { - SDL_assert(SDL_capturelist == item); - SDL_capturelist = item->next; + SDL_assert(SDL_cameralist == item); + SDL_cameralist = item->next; } - if (item == SDL_capturelist_tail) { - SDL_capturelist_tail = prev; + if (item == SDL_cameralist_tail) { + SDL_cameralist_tail = prev; } /* Need to decrement the count */ - --num_video_captures; + --num_cameras; /* !!! TODO: Send a remove event? */ SDL_free(item->fname); @@ -1210,8 +1205,4 @@ static int MaybeRemoveDevice(const char *path) } #endif /* SDL_USE_LIBUDEV */ - - -#endif - -#endif /* SDL_VIDEO_CAPTURE */ +#endif /* SDL_CAMERA_V4L2 */ diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index f545e9b3c909d..b9ebf221e6ba3 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -895,22 +895,6 @@ SDL3_0.0.0 { SDL_SetPropertyWithCleanup; SDL_SetX11EventHook; SDL_GetGlobalProperties; - SDL_OpenVideoCapture; - SDL_SetVideoCaptureSpec; - SDL_OpenVideoCaptureWithSpec; - SDL_GetVideoCaptureDeviceName; - SDL_GetVideoCaptureSpec; - SDL_GetVideoCaptureFormat; - SDL_GetNumVideoCaptureFormats; - SDL_GetVideoCaptureFrameSize; - SDL_GetNumVideoCaptureFrameSizes; - SDL_GetVideoCaptureStatus; - SDL_StartVideoCapture; - SDL_AcquireVideoCaptureFrame; - SDL_ReleaseVideoCaptureFrame; - SDL_StopVideoCapture; - SDL_CloseVideoCapture; - SDL_GetVideoCaptureDevices; SDL_GetGamepadButtonLabelForType; SDL_GetGamepadButtonLabel; SDL_GetPens; @@ -973,6 +957,22 @@ SDL3_0.0.0 { SDL_SetWindowShape; SDL_RenderViewportSet; SDL_HasProperty; + SDL_GetCameraDevices; + SDL_OpenCamera; + SDL_SetCameraSpec; + SDL_OpenCameraWithSpec; + SDL_GetCameraDeviceName; + SDL_GetCameraSpec; + SDL_GetCameraFormat; + SDL_GetNumCameraFormats; + SDL_GetCameraFrameSize; + SDL_GetNumCameraFrameSizes; + SDL_GetCameraStatus; + SDL_StartCamera; + SDL_AcquireCameraFrame; + SDL_ReleaseCameraFrame; + SDL_StopCamera; + SDL_CloseCamera; # 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 b288bdcfa36e4..79206a4932fae 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -920,22 +920,6 @@ #define SDL_SetPropertyWithCleanup SDL_SetPropertyWithCleanup_REAL #define SDL_SetX11EventHook SDL_SetX11EventHook_REAL #define SDL_GetGlobalProperties SDL_GetGlobalProperties_REAL -#define SDL_OpenVideoCapture SDL_OpenVideoCapture_REAL -#define SDL_SetVideoCaptureSpec SDL_SetVideoCaptureSpec_REAL -#define SDL_OpenVideoCaptureWithSpec SDL_OpenVideoCaptureWithSpec_REAL -#define SDL_GetVideoCaptureDeviceName SDL_GetVideoCaptureDeviceName_REAL -#define SDL_GetVideoCaptureSpec SDL_GetVideoCaptureSpec_REAL -#define SDL_GetVideoCaptureFormat SDL_GetVideoCaptureFormat_REAL -#define SDL_GetNumVideoCaptureFormats SDL_GetNumVideoCaptureFormats_REAL -#define SDL_GetVideoCaptureFrameSize SDL_GetVideoCaptureFrameSize_REAL -#define SDL_GetNumVideoCaptureFrameSizes SDL_GetNumVideoCaptureFrameSizes_REAL -#define SDL_GetVideoCaptureStatus SDL_GetVideoCaptureStatus_REAL -#define SDL_StartVideoCapture SDL_StartVideoCapture_REAL -#define SDL_AcquireVideoCaptureFrame SDL_AcquireVideoCaptureFrame_REAL -#define SDL_ReleaseVideoCaptureFrame SDL_ReleaseVideoCaptureFrame_REAL -#define SDL_StopVideoCapture SDL_StopVideoCapture_REAL -#define SDL_CloseVideoCapture SDL_CloseVideoCapture_REAL -#define SDL_GetVideoCaptureDevices SDL_GetVideoCaptureDevices_REAL #define SDL_GetGamepadButtonLabelForType SDL_GetGamepadButtonLabelForType_REAL #define SDL_GetGamepadButtonLabel SDL_GetGamepadButtonLabel_REAL #define SDL_GetPens SDL_GetPens_REAL @@ -998,3 +982,19 @@ #define SDL_SetWindowShape SDL_SetWindowShape_REAL #define SDL_RenderViewportSet SDL_RenderViewportSet_REAL #define SDL_HasProperty SDL_HasProperty_REAL +#define SDL_GetCameraDevices SDL_GetCameraDevices_REAL +#define SDL_OpenCamera SDL_OpenCamera_REAL +#define SDL_SetCameraSpec SDL_SetCameraSpec_REAL +#define SDL_OpenCameraWithSpec SDL_OpenCameraWithSpec_REAL +#define SDL_GetCameraDeviceName SDL_GetCameraDeviceName_REAL +#define SDL_GetCameraSpec SDL_GetCameraSpec_REAL +#define SDL_GetCameraFormat SDL_GetCameraFormat_REAL +#define SDL_GetNumCameraFormats SDL_GetNumCameraFormats_REAL +#define SDL_GetCameraFrameSize SDL_GetCameraFrameSize_REAL +#define SDL_GetNumCameraFrameSizes SDL_GetNumCameraFrameSizes_REAL +#define SDL_GetCameraStatus SDL_GetCameraStatus_REAL +#define SDL_StartCamera SDL_StartCamera_REAL +#define SDL_AcquireCameraFrame SDL_AcquireCameraFrame_REAL +#define SDL_ReleaseCameraFrame SDL_ReleaseCameraFrame_REAL +#define SDL_StopCamera SDL_StopCamera_REAL +#define SDL_CloseCamera SDL_CloseCamera_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 7ce6a43d8cb82..4dd7e019e05af 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -953,22 +953,6 @@ SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetDisplayProperties,(SDL_DisplayID a),(a), SDL_DYNAPI_PROC(int,SDL_SetPropertyWithCleanup,(SDL_PropertiesID a, const char *b, void *c, void (SDLCALL *d)(void *userdata, void *value), void *e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(void,SDL_SetX11EventHook,(SDL_X11EventHook a, void *b),(a,b),) SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetGlobalProperties,(void),(),return) -SDL_DYNAPI_PROC(SDL_VideoCaptureDevice*,SDL_OpenVideoCapture,(SDL_VideoCaptureDeviceID a),(a),return) -SDL_DYNAPI_PROC(int,SDL_SetVideoCaptureSpec,(SDL_VideoCaptureDevice *a, const SDL_VideoCaptureSpec *b, SDL_VideoCaptureSpec *c, int d),(a,b,c,d),return) -SDL_DYNAPI_PROC(SDL_VideoCaptureDevice*,SDL_OpenVideoCaptureWithSpec,(SDL_VideoCaptureDeviceID a, const SDL_VideoCaptureSpec *b, SDL_VideoCaptureSpec *c, int d),(a,b,c,d),return) -SDL_DYNAPI_PROC(const char*,SDL_GetVideoCaptureDeviceName,(SDL_VideoCaptureDeviceID a),(a),return) -SDL_DYNAPI_PROC(int,SDL_GetVideoCaptureSpec,(SDL_VideoCaptureDevice *a, SDL_VideoCaptureSpec *b),(a,b),return) -SDL_DYNAPI_PROC(int,SDL_GetVideoCaptureFormat,(SDL_VideoCaptureDevice *a, int b, Uint32 *c),(a,b,c),return) -SDL_DYNAPI_PROC(int,SDL_GetNumVideoCaptureFormats,(SDL_VideoCaptureDevice *a),(a),return) -SDL_DYNAPI_PROC(int,SDL_GetVideoCaptureFrameSize,(SDL_VideoCaptureDevice *a, Uint32 b, int c, int *d, int *e),(a,b,c,d,e),return) -SDL_DYNAPI_PROC(int,SDL_GetNumVideoCaptureFrameSizes,(SDL_VideoCaptureDevice *a, Uint32 b),(a,b),return) -SDL_DYNAPI_PROC(SDL_VideoCaptureStatus,SDL_GetVideoCaptureStatus,(SDL_VideoCaptureDevice *a),(a),return) -SDL_DYNAPI_PROC(int,SDL_StartVideoCapture,(SDL_VideoCaptureDevice *a),(a),return) -SDL_DYNAPI_PROC(int,SDL_AcquireVideoCaptureFrame,(SDL_VideoCaptureDevice *a, SDL_VideoCaptureFrame *b),(a,b),return) -SDL_DYNAPI_PROC(int,SDL_ReleaseVideoCaptureFrame,(SDL_VideoCaptureDevice *a, SDL_VideoCaptureFrame *b),(a,b),return) -SDL_DYNAPI_PROC(int,SDL_StopVideoCapture,(SDL_VideoCaptureDevice *a),(a),return) -SDL_DYNAPI_PROC(void,SDL_CloseVideoCapture,(SDL_VideoCaptureDevice *a),(a),) -SDL_DYNAPI_PROC(SDL_VideoCaptureDeviceID*,SDL_GetVideoCaptureDevices,(int *a),(a),return) SDL_DYNAPI_PROC(SDL_GamepadButtonLabel,SDL_GetGamepadButtonLabelForType,(SDL_GamepadType a, SDL_GamepadButton b),(a,b),return) SDL_DYNAPI_PROC(SDL_GamepadButtonLabel,SDL_GetGamepadButtonLabel,(SDL_Gamepad *a, SDL_GamepadButton b),(a,b),return) SDL_DYNAPI_PROC(SDL_PenID*,SDL_GetPens,(int *a),(a),return) @@ -1023,3 +1007,19 @@ SDL_DYNAPI_PROC(int,SDL_RenderGeometryRawFloat,(SDL_Renderer *a, SDL_Texture *b, SDL_DYNAPI_PROC(int,SDL_SetWindowShape,(SDL_Window *a, SDL_Surface *b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_RenderViewportSet,(SDL_Renderer *a),(a),return) SDL_DYNAPI_PROC(SDL_bool,SDL_HasProperty,(SDL_PropertiesID a, const char *b),(a,b),return) +SDL_DYNAPI_PROC(SDL_CameraDeviceID*,SDL_GetCameraDevices,(int *a),(a),return) +SDL_DYNAPI_PROC(SDL_CameraDevice*,SDL_OpenCamera,(SDL_CameraDeviceID a),(a),return) +SDL_DYNAPI_PROC(int,SDL_SetCameraSpec,(SDL_CameraDevice *a, const SDL_CameraSpec *b, SDL_CameraSpec *c, int d),(a,b,c,d),return) +SDL_DYNAPI_PROC(SDL_CameraDevice*,SDL_OpenCameraWithSpec,(SDL_CameraDeviceID a, const SDL_CameraSpec *b, SDL_CameraSpec *c, int d),(a,b,c,d),return) +SDL_DYNAPI_PROC(const char*,SDL_GetCameraDeviceName,(SDL_CameraDeviceID a),(a),return) +SDL_DYNAPI_PROC(int,SDL_GetCameraSpec,(SDL_CameraDevice *a, SDL_CameraSpec *b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_GetCameraFormat,(SDL_CameraDevice *a, int b, Uint32 *c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_GetNumCameraFormats,(SDL_CameraDevice *a),(a),return) +SDL_DYNAPI_PROC(int,SDL_GetCameraFrameSize,(SDL_CameraDevice *a, Uint32 b, int c, int *d, int *e),(a,b,c,d,e),return) +SDL_DYNAPI_PROC(int,SDL_GetNumCameraFrameSizes,(SDL_CameraDevice *a, Uint32 b),(a,b),return) +SDL_DYNAPI_PROC(SDL_CameraStatus,SDL_GetCameraStatus,(SDL_CameraDevice *a),(a),return) +SDL_DYNAPI_PROC(int,SDL_StartCamera,(SDL_CameraDevice *a),(a),return) +SDL_DYNAPI_PROC(int,SDL_AcquireCameraFrame,(SDL_CameraDevice *a, SDL_CameraFrame *b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_ReleaseCameraFrame,(SDL_CameraDevice *a, SDL_CameraFrame *b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_StopCamera,(SDL_CameraDevice *a),(a),return) +SDL_DYNAPI_PROC(void,SDL_CloseCamera,(SDL_CameraDevice *a),(a),) diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 7bcd62dd411c8..beb7e40f8f4a9 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -458,7 +458,7 @@ int SDL_VideoInit(const char *driver_name) SDL_bool init_keyboard = SDL_FALSE; SDL_bool init_mouse = SDL_FALSE; SDL_bool init_touch = SDL_FALSE; - SDL_bool init_video_capture = SDL_FALSE; + SDL_bool init_camera = SDL_FALSE; int i = 0; /* Check to make sure we don't overwrite '_this' */ @@ -485,10 +485,10 @@ int SDL_VideoInit(const char *driver_name) goto pre_driver_error; } init_touch = SDL_TRUE; - if (SDL_VideoCaptureInit() < 0) { + if (SDL_CameraInit() < 0) { goto pre_driver_error; } - init_video_capture = SDL_TRUE; + init_camera = SDL_TRUE; /* Select the proper video driver */ video = NULL; @@ -590,8 +590,8 @@ int SDL_VideoInit(const char *driver_name) pre_driver_error: SDL_assert(_this == NULL); - if (init_video_capture) { - SDL_QuitVideoCapture(); + if (init_camera) { + SDL_QuitCamera(); } if (init_touch) { SDL_QuitTouch(); @@ -3784,7 +3784,7 @@ void SDL_VideoQuit(void) SDL_ClearClipboardData(); /* Halt event processing before doing anything else */ - SDL_QuitVideoCapture(); + SDL_QuitCamera(); SDL_QuitTouch(); SDL_QuitMouse(); SDL_QuitKeyboard(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b7ef7ce0d55d0..38e72e4161292 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -394,8 +394,8 @@ add_sdl_test_executable(teststreaming NEEDS_RESOURCES TESTUTILS SOURCES teststre add_sdl_test_executable(testtimer NONINTERACTIVE NONINTERACTIVE_ARGS --no-interactive NONINTERACTIVE_TIMEOUT 60 SOURCES testtimer.c) add_sdl_test_executable(testurl SOURCES testurl.c) add_sdl_test_executable(testver NONINTERACTIVE SOURCES testver.c) -add_sdl_test_executable(testvideocapture SOURCES testvideocapture.c) -add_sdl_test_executable(testvideocaptureminimal SOURCES testvideocaptureminimal.c) +add_sdl_test_executable(testcamera SOURCES testcamera.c) +add_sdl_test_executable(testcameraminimal SOURCES testcameraminimal.c) add_sdl_test_executable(testviewport NEEDS_RESOURCES TESTUTILS SOURCES testviewport.c) add_sdl_test_executable(testwm SOURCES testwm.c) add_sdl_test_executable(testyuv NONINTERACTIVE NONINTERACTIVE_ARGS "--automated" NEEDS_RESOURCES TESTUTILS SOURCES testyuv.c testyuv_cvt.c) diff --git a/test/testcamera.c b/test/testcamera.c index a6eb8d43d3f3b..255dcf4ea2e46 100644 --- a/test/testcamera.c +++ b/test/testcamera.c @@ -12,7 +12,7 @@ #include "SDL3/SDL_main.h" #include "SDL3/SDL.h" #include "SDL3/SDL_test.h" -#include "SDL3/SDL_video_capture.h" +#include "SDL3/SDL_camera.h" #ifdef SDL_PLATFORM_EMSCRIPTEN #include @@ -25,8 +25,8 @@ static const char *usage = "\ =========================================================================\n\ \n\ Use keyboards:\n\ - o: open first video capture device. (close previously opened)\n\ - l: switch to, and list video capture devices\n\ + o: open first camera device. (close previously opened)\n\ + l: switch to, and list camera devices\n\ i: information about status (Init, Playing, Stopped)\n\ f: formats and resolutions available\n\ s: start / stop capture\n\ @@ -81,10 +81,10 @@ static void load_average(float *val) struct data_capture_t { - SDL_VideoCaptureDevice *device; - SDL_VideoCaptureSpec obtained; + SDL_CameraDevice *device; + SDL_CameraSpec obtained; int stopped; - SDL_VideoCaptureFrame frame_current; + SDL_CameraFrame frame_current; measure_fps_t fps_capture; SDL_Texture *texture; int texture_updated; @@ -113,11 +113,11 @@ struct data_capture_t { -static SDL_VideoCaptureDeviceID get_instance_id(int index) { +static SDL_CameraDeviceID get_instance_id(int index) { int ret = 0; int num = 0; - SDL_VideoCaptureDeviceID *devices; - devices = SDL_GetVideoCaptureDevices(&num); + SDL_CameraDeviceID *devices; + devices = SDL_GetCameraDevices(&num); if (devices) { if (index >= 0 && index < num) { ret = devices[index]; @@ -155,10 +155,10 @@ int main(int argc, char **argv) SDL_FRect r_format = { 50 + (120 + 50) * 3, 50, 120, 50 }; SDL_FRect r_listdev = { 50 + (120 + 50) * 4, 50, 120, 50 }; - SDL_VideoCaptureDevice *device; - SDL_VideoCaptureSpec obtained; + SDL_CameraDevice *device; + SDL_CameraSpec obtained; int stopped = 0; - SDL_VideoCaptureFrame frame_current; + SDL_CameraFrame frame_current; measure_fps_t fps_capture; SDL_Texture *texture = NULL; int texture_updated = 0; @@ -234,24 +234,24 @@ int main(int argc, char **argv) SDL_LogSetAllPriority(SDL_LOG_PRIORITY_INFO); - device = SDL_OpenVideoCapture(0); + device = SDL_OpenCamera(0); if (!device) { - SDL_Log("Error SDL_OpenVideoCapture: %s", SDL_GetError()); + SDL_Log("Error SDL_OpenCamera: %s", SDL_GetError()); } { /* List formats */ - int i, num = SDL_GetNumVideoCaptureFormats(device); + int i, num = SDL_GetNumCameraFormats(device); for (i = 0; i < num; i++) { Uint32 format; - SDL_GetVideoCaptureFormat(device, i, &format); + SDL_GetCameraFormat(device, i, &format); SDL_Log("format %d/%d: %s", i, num, SDL_GetPixelFormatName(format)); { int w, h; - int j, num2 = SDL_GetNumVideoCaptureFrameSizes(device, format); + int j, num2 = SDL_GetNumCameraFrameSizes(device, format); for (j = 0; j < num2; j++) { - SDL_GetVideoCaptureFrameSize(device, format, j, &w, &h); + SDL_GetCameraFrameSize(device, format, j, &w, &h); SDL_Log(" framesizes %d/%d : %d x %d", j, num2, w, h); } } @@ -262,24 +262,24 @@ int main(int argc, char **argv) { int ret; /* forced_format */ - SDL_VideoCaptureSpec desired; + SDL_CameraSpec desired; SDL_zero(desired); desired.width = 640 * 2; desired.height = 360 * 2; desired.format = SDL_PIXELFORMAT_NV12; - ret = SDL_SetVideoCaptureSpec(device, &desired, &obtained, SDL_VIDEO_CAPTURE_ALLOW_ANY_CHANGE); + ret = SDL_SetCameraSpec(device, &desired, &obtained, SDL_CAMERA_ALLOW_ANY_CHANGE); if (ret < 0) { - SDL_SetVideoCaptureSpec(device, NULL, &obtained, 0); + SDL_SetCameraSpec(device, NULL, &obtained, 0); } } - SDL_Log("Open capture video device. Obtained spec: size=%d x %d format=%s", + SDL_Log("Open camera device. Obtained spec: size=%d x %d format=%s", obtained.width, obtained.height, SDL_GetPixelFormatName(obtained.format)); { - SDL_VideoCaptureSpec spec; - if (SDL_GetVideoCaptureSpec(device, &spec) == 0) { + SDL_CameraSpec spec; + if (SDL_GetCameraSpec(device, &spec) == 0) { SDL_Log("Read spec: size=%d x %d format=%s", spec.width, spec.height, SDL_GetPixelFormatName(spec.format)); } else { @@ -287,8 +287,8 @@ int main(int argc, char **argv) } } - if (SDL_StartVideoCapture(device) < 0) { - SDL_Log("error SDL_StartVideoCapture(): %s", SDL_GetError()); + if (SDL_StartCamera(device) < 0) { + SDL_Log("error SDL_StartCamera(): %s", SDL_GetError()); } while (!quit) { @@ -389,9 +389,9 @@ int main(int argc, char **argv) if (sym == SDLK_c) { if (frame_current.num_planes) { - SDL_ReleaseVideoCaptureFrame(device, &frame_current); + SDL_ReleaseCameraFrame(device, &frame_current); } - SDL_CloseVideoCapture(device); + SDL_CloseCamera(device); device = NULL; SDL_Log("Close"); } @@ -400,20 +400,20 @@ int main(int argc, char **argv) if (device) { SDL_Log("Close previous .."); if (frame_current.num_planes) { - SDL_ReleaseVideoCaptureFrame(device, &frame_current); + SDL_ReleaseCameraFrame(device, &frame_current); } - SDL_CloseVideoCapture(device); + SDL_CloseCamera(device); } texture_updated = 0; SDL_ClearError(); - SDL_Log("Try to open:%s", SDL_GetVideoCaptureDeviceName(get_instance_id(current_dev))); + SDL_Log("Try to open:%s", SDL_GetCameraDeviceName(get_instance_id(current_dev))); obtained.width = 640 * 2; obtained.height = 360 * 2; - device = SDL_OpenVideoCaptureWithSpec(get_instance_id(current_dev), &obtained, &obtained, SDL_VIDEO_CAPTURE_ALLOW_ANY_CHANGE); + device = SDL_OpenCameraWithSpec(get_instance_id(current_dev), &obtained, &obtained, SDL_CAMERA_ALLOW_ANY_CHANGE); /* spec may have changed because of re-open */ if (texture) { @@ -427,13 +427,13 @@ int main(int argc, char **argv) if (sym == SDLK_l) { int num = 0; - SDL_VideoCaptureDeviceID *devices; + SDL_CameraDeviceID *devices; int i; - devices = SDL_GetVideoCaptureDevices(&num); + devices = SDL_GetCameraDevices(&num); SDL_Log("Num devices : %d", num); for (i = 0; i < num; i++) { - SDL_Log("Device %d/%d : %s", i, num, SDL_GetVideoCaptureDeviceName(devices[i])); + SDL_Log("Device %d/%d : %s", i, num, SDL_GetCameraDeviceName(devices[i])); } SDL_free(devices); @@ -449,19 +449,19 @@ int main(int argc, char **argv) } if (sym == SDLK_i) { - SDL_VideoCaptureStatus status = SDL_GetVideoCaptureStatus(device); - if (status == SDL_VIDEO_CAPTURE_STOPPED) { SDL_Log("STOPPED"); } - if (status == SDL_VIDEO_CAPTURE_PLAYING) { SDL_Log("PLAYING"); } - if (status == SDL_VIDEO_CAPTURE_INIT) { SDL_Log("INIT"); } + SDL_CameraStatus status = SDL_GetCameraStatus(device); + if (status == SDL_CAMERA_STOPPED) { SDL_Log("STOPPED"); } + if (status == SDL_CAMERA_PLAYING) { SDL_Log("PLAYING"); } + if (status == SDL_CAMERA_INIT) { SDL_Log("INIT"); } } if (sym == SDLK_s) { if (stopped) { SDL_Log("Stop"); - SDL_StopVideoCapture(device); + SDL_StopCamera(device); } else { SDL_Log("Start"); - SDL_StartVideoCapture(device); + SDL_StartCamera(device); } stopped = !stopped; } @@ -470,21 +470,21 @@ int main(int argc, char **argv) SDL_Log("List formats"); if (!device) { - device = SDL_OpenVideoCapture(get_instance_id(current_dev)); + device = SDL_OpenCamera(get_instance_id(current_dev)); } /* List formats */ { - int i, num = SDL_GetNumVideoCaptureFormats(device); + int i, num = SDL_GetNumCameraFormats(device); for (i = 0; i < num; i++) { Uint32 format; - SDL_GetVideoCaptureFormat(device, i, &format); + SDL_GetCameraFormat(device, i, &format); SDL_Log("format %d/%d : %s", i, num, SDL_GetPixelFormatName(format)); { int w, h; - int j, num2 = SDL_GetNumVideoCaptureFrameSizes(device, format); + int j, num2 = SDL_GetNumCameraFrameSizes(device, format); for (j = 0; j < num2; j++) { - SDL_GetVideoCaptureFrameSize(device, format, j, &w, &h); + SDL_GetCameraFrameSize(device, format, j, &w, &h); SDL_Log(" framesizes %d/%d : %d x %d", j, num2, w, h); } } @@ -515,12 +515,12 @@ int main(int argc, char **argv) texture_updated = 0; } else { int ret; - SDL_VideoCaptureFrame frame_next; + SDL_CameraFrame frame_next; SDL_zero(frame_next); - ret = SDL_AcquireVideoCaptureFrame(device, &frame_next); + ret = SDL_AcquireCameraFrame(device, &frame_next); if (ret < 0) { - SDL_Log("dev[%d] err SDL_AcquireVideoCaptureFrame: %s", i, SDL_GetError()); + SDL_Log("dev[%d] err SDL_AcquireCameraFrame: %s", i, SDL_GetError()); } #if 1 if (frame_next.num_planes) { @@ -533,9 +533,9 @@ int main(int argc, char **argv) update_fps(&fps_capture); if (frame_current.num_planes) { - ret = SDL_ReleaseVideoCaptureFrame(device, &frame_current); + ret = SDL_ReleaseCameraFrame(device, &frame_current); if (ret < 0) { - SDL_Log("dev[%d] err SDL_ReleaseVideoCaptureFrame: %s", i, SDL_GetError()); + SDL_Log("dev[%d] err SDL_ReleaseCameraFrame: %s", i, SDL_GetError()); } } frame_current = frame_next; @@ -670,7 +670,7 @@ int main(int argc, char **argv) const float x_offset = 0; #endif char buf[256]; - SDL_snprintf(buf, 256, "Device %d (%s) is not opened", current_dev, SDL_GetVideoCaptureDeviceName(get_instance_id(current_dev))); + SDL_snprintf(buf, 256, "Device %d (%s) is not opened", current_dev, SDL_GetCameraDeviceName(get_instance_id(current_dev))); SDLTest_DrawString(renderer, x_offset + 10, 10, buf); } else { #ifdef SDL_PLATFORM_IOS @@ -682,14 +682,14 @@ int main(int argc, char **argv) char buf[256]; if (device) { - SDL_VideoCaptureStatus s = SDL_GetVideoCaptureStatus(device); - if (s == SDL_VIDEO_CAPTURE_INIT) { + SDL_CameraStatus s = SDL_GetCameraStatus(device); + if (s == SDL_CAMERA_INIT) { status = "init"; - } else if (s == SDL_VIDEO_CAPTURE_PLAYING) { + } else if (s == SDL_CAMERA_PLAYING) { status = "playing"; - } else if (s == SDL_VIDEO_CAPTURE_STOPPED) { + } else if (s == SDL_CAMERA_STOPPED) { status = "stopped"; - } else if (s == SDL_VIDEO_CAPTURE_FAIL) { + } else if (s == SDL_CAMERA_FAIL) { status = "failed"; } @@ -745,13 +745,13 @@ int main(int argc, char **argv) RESTORE_CAPTURE_STATE(i); if (device) { - if (SDL_StopVideoCapture(device) < 0) { - SDL_Log("error SDL_StopVideoCapture(): %s", SDL_GetError()); + if (SDL_StopCamera(device) < 0) { + SDL_Log("error SDL_StopCamera(): %s", SDL_GetError()); } if (frame_current.num_planes) { - SDL_ReleaseVideoCaptureFrame(device, &frame_current); + SDL_ReleaseCameraFrame(device, &frame_current); } - SDL_CloseVideoCapture(device); + SDL_CloseCamera(device); } if (texture) { diff --git a/test/testcameraminimal.c b/test/testcameraminimal.c index 956b7e77a5ac3..060b8ed50077f 100644 --- a/test/testcameraminimal.c +++ b/test/testcameraminimal.c @@ -28,10 +28,10 @@ int main(int argc, char **argv) int quit = 0; SDLTest_CommonState *state = NULL; - SDL_VideoCaptureDevice *device = NULL; - SDL_VideoCaptureSpec obtained; + SDL_CameraDevice *device = NULL; + SDL_CameraSpec obtained; - SDL_VideoCaptureFrame frame_current; + SDL_CameraFrame frame_current; SDL_Texture *texture = NULL; int texture_updated = 0; @@ -73,14 +73,14 @@ int main(int argc, char **argv) return 1; } - device = SDL_OpenVideoCaptureWithSpec(0, NULL, &obtained, SDL_VIDEO_CAPTURE_ALLOW_ANY_CHANGE); + device = SDL_OpenCameraWithSpec(0, NULL, &obtained, SDL_CAMERA_ALLOW_ANY_CHANGE); if (!device) { - SDL_Log("No video capture? %s", SDL_GetError()); + SDL_Log("No camera? %s", SDL_GetError()); return 1; } - if (SDL_StartVideoCapture(device) < 0) { - SDL_Log("error SDL_StartVideoCapture(): %s", SDL_GetError()); + if (SDL_StartCamera(device) < 0) { + SDL_Log("error SDL_StartCamera(): %s", SDL_GetError()); return 1; } @@ -118,11 +118,11 @@ int main(int argc, char **argv) } { - SDL_VideoCaptureFrame frame_next; + SDL_CameraFrame frame_next; SDL_zero(frame_next); - if (SDL_AcquireVideoCaptureFrame(device, &frame_next) < 0) { - SDL_Log("err SDL_AcquireVideoCaptureFrame: %s", SDL_GetError()); + if (SDL_AcquireCameraFrame(device, &frame_next) < 0) { + SDL_Log("err SDL_AcquireCameraFrame: %s", SDL_GetError()); } #if 0 if (frame_next.num_planes) { @@ -132,8 +132,8 @@ int main(int argc, char **argv) if (frame_next.num_planes) { if (frame_current.num_planes) { - if (SDL_ReleaseVideoCaptureFrame(device, &frame_current) < 0) { - SDL_Log("err SDL_ReleaseVideoCaptureFrame: %s", SDL_GetError()); + if (SDL_ReleaseCameraFrame(device, &frame_current) < 0) { + SDL_Log("err SDL_ReleaseCameraFrame: %s", SDL_GetError()); } } @@ -186,13 +186,13 @@ int main(int argc, char **argv) SDL_RenderPresent(renderer); } - if (SDL_StopVideoCapture(device) < 0) { - SDL_Log("error SDL_StopVideoCapture(): %s", SDL_GetError()); + if (SDL_StopCamera(device) < 0) { + SDL_Log("error SDL_StopCamera(): %s", SDL_GetError()); } if (frame_current.num_planes) { - SDL_ReleaseVideoCaptureFrame(device, &frame_current); + SDL_ReleaseCameraFrame(device, &frame_current); } - SDL_CloseVideoCapture(device); + SDL_CloseCamera(device); if (texture) { SDL_DestroyTexture(texture); From 2ad44bd1621a3e037ef8c524e169d54370ffdece Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 28 Nov 2023 23:03:19 -0500 Subject: [PATCH 034/220] camera: Made a pass over all the sources, cleaning up for SDL3 style, etc. --- src/camera/SDL_camera.c | 423 +++++++++--------------- src/camera/SDL_camera_c.h | 6 +- src/camera/SDL_syscamera.h | 30 +- src/camera/android/SDL_camera_android.c | 264 +++++++-------- src/camera/apple/SDL_camera_apple.m | 243 ++++++-------- src/camera/v4l2/SDL_camera_v4l2.c | 332 ++++++++----------- 6 files changed, 537 insertions(+), 761 deletions(-) diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index f10fc755fe7cc..d62854c9c3528 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -20,8 +20,6 @@ */ #include "SDL_internal.h" -#include "SDL3/SDL.h" -#include "SDL3/SDL_camera.h" #include "SDL_syscamera.h" #include "SDL_camera_c.h" #include "../video/SDL_pixels_c.h" @@ -29,16 +27,16 @@ #define DEBUG_CAMERA 1 -/* list node entries to share frames between SDL and user app */ +// list node entries to share frames between SDL and user app +// !!! FIXME: do we need this struct? typedef struct entry_t { SDL_CameraFrame frame; } entry_t; -static SDL_CameraDevice *open_devices[16]; +static SDL_CameraDevice *open_devices[16]; // !!! FIXME: remove limit -static void -close_device(SDL_CameraDevice *device) +static void CloseCameraDevice(SDL_CameraDevice *device) { if (!device) { return; @@ -57,27 +55,23 @@ close_device(SDL_CameraDevice *device) SDL_DestroyMutex(device->acquiring_lock); } - { - int i, n = SDL_arraysize(open_devices); - for (i = 0; i < n; i++) { - if (open_devices[i] == device) { - open_devices[i] = NULL; - } + const int n = SDL_arraysize(open_devices); + for (int i = 0; i < n; i++) { + if (open_devices[i] == device) { + open_devices[i] = NULL; } } - { - entry_t *entry = NULL; - while (device->buffer_queue != NULL) { - SDL_ListPop(&device->buffer_queue, (void**)&entry); - if (entry) { - SDL_CameraFrame f = entry->frame; - /* Release frames not acquired, if any */ - if (f.timestampNS) { - ReleaseFrame(device, &f); - } - SDL_free(entry); + entry_t *entry = NULL; + while (device->buffer_queue != NULL) { + SDL_ListPop(&device->buffer_queue, (void**)&entry); + if (entry) { + SDL_CameraFrame f = entry->frame; + // Release frames not acquired, if any + if (f.timestampNS) { + ReleaseFrame(device, &f); } + SDL_free(entry); } } @@ -87,12 +81,12 @@ close_device(SDL_CameraDevice *device) SDL_free(device); } -/* Tell if all device are closed */ -SDL_bool check_all_device_closed(void) +// Tell if all devices are closed +SDL_bool CheckAllDeviceClosed(void) { - int i, n = SDL_arraysize(open_devices); + const int n = SDL_arraysize(open_devices); int all_closed = SDL_TRUE; - for (i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { if (open_devices[i]) { all_closed = SDL_FALSE; break; @@ -101,11 +95,11 @@ SDL_bool check_all_device_closed(void) return all_closed; } -/* Tell if at least one device is in playing state */ -SDL_bool check_device_playing(void) +// Tell if at least one device is in playing state +SDL_bool CheckDevicePlaying(void) { - int i, n = SDL_arraysize(open_devices); - for (i = 0; i < n; i++) { + const int n = SDL_arraysize(open_devices); + for (int i = 0; i < n; i++) { if (open_devices[i]) { if (SDL_GetCameraStatus(open_devices[i]) == SDL_CAMERA_PLAYING) { return SDL_TRUE; @@ -115,35 +109,26 @@ SDL_bool check_device_playing(void) return SDL_FALSE; } -void -SDL_CloseCamera(SDL_CameraDevice *device) +void SDL_CloseCamera(SDL_CameraDevice *device) { if (!device) { SDL_InvalidParamError("device"); - return; + } else { + CloseCameraDevice(device); } - close_device(device); } -int -SDL_StartCamera(SDL_CameraDevice *device) +int SDL_StartCamera(SDL_CameraDevice *device) { - SDL_CameraStatus status; - int result; if (!device) { return SDL_InvalidParamError("device"); - } - - if (device->is_spec_set == SDL_FALSE) { + } else if (device->is_spec_set == SDL_FALSE) { return SDL_SetError("no spec set"); - } - - status = SDL_GetCameraStatus(device); - if (status != SDL_CAMERA_INIT) { + } else if (SDL_GetCameraStatus(device) != SDL_CAMERA_INIT) { return SDL_SetError("invalid state"); } - result = StartCamera(device); + const int result = StartCamera(device); if (result < 0) { return result; } @@ -153,34 +138,23 @@ SDL_StartCamera(SDL_CameraDevice *device) return 0; } -int -SDL_GetCameraSpec(SDL_CameraDevice *device, SDL_CameraSpec *spec) +int SDL_GetCameraSpec(SDL_CameraDevice *device, SDL_CameraSpec *spec) { if (!device) { return SDL_InvalidParamError("device"); - } - - if (!spec) { + } else if (!spec) { return SDL_InvalidParamError("spec"); } SDL_zerop(spec); - return GetDeviceSpec(device, spec); } -int -SDL_StopCamera(SDL_CameraDevice *device) +int SDL_StopCamera(SDL_CameraDevice *device) { - SDL_CameraStatus status; - int ret; if (!device) { return SDL_InvalidParamError("device"); - } - - status = SDL_GetCameraStatus(device); - - if (status != SDL_CAMERA_PLAYING) { + } else if (SDL_GetCameraStatus(device) != SDL_CAMERA_PLAYING) { return SDL_SetError("invalid state"); } @@ -188,104 +162,88 @@ SDL_StopCamera(SDL_CameraDevice *device) SDL_AtomicSet(&device->shutdown, 1); SDL_LockMutex(device->acquiring_lock); - ret = StopCamera(device); + const int retval = StopCamera(device); SDL_UnlockMutex(device->acquiring_lock); - if (ret < 0) { - return -1; - } - - return 0; + return (retval < 0) ? -1 : 0; } -/* Check spec has valid format and frame size */ -static int -prepare_cameraspec(SDL_CameraDevice *device, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes) +// Check spec has valid format and frame size +static int prepare_cameraspec(SDL_CameraDevice *device, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes) { - /* Check format */ - { - int i, num = SDL_GetNumCameraFormats(device); - int is_format_valid = 0; - - for (i = 0; i < num; i++) { - Uint32 format; - if (SDL_GetCameraFormat(device, i, &format) == 0) { - if (format == desired->format && format != SDL_PIXELFORMAT_UNKNOWN) { - is_format_valid = 1; - obtained->format = format; - break; - } + // Check format + const int numfmts = SDL_GetNumCameraFormats(device); + SDL_bool is_format_valid = SDL_FALSE; + + for (int i = 0; i < numfmts; i++) { + Uint32 format; + if (SDL_GetCameraFormat(device, i, &format) == 0) { + if (format == desired->format && format != SDL_PIXELFORMAT_UNKNOWN) { + is_format_valid = SDL_TRUE; + obtained->format = format; + break; } } + } - if (!is_format_valid) { - if (allowed_changes) { - for (i = 0; i < num; i++) { - Uint32 format; - if (SDL_GetCameraFormat(device, i, &format) == 0) { - if (format != SDL_PIXELFORMAT_UNKNOWN) { - obtained->format = format; - is_format_valid = 1; - break; - } + if (!is_format_valid) { + if (allowed_changes) { + for (int i = 0; i < numfmts; i++) { + Uint32 format; + if (SDL_GetCameraFormat(device, i, &format) == 0) { + if (format != SDL_PIXELFORMAT_UNKNOWN) { + obtained->format = format; + is_format_valid = SDL_TRUE; + break; } } - - } else { - SDL_SetError("Not allowed to change the format"); - return -1; } + } else { + return SDL_SetError("Not allowed to change the format"); } + } - if (!is_format_valid) { - SDL_SetError("Invalid format"); - return -1; - } + if (!is_format_valid) { + return SDL_SetError("Invalid format"); } - /* Check frame size */ - { - int i, num = SDL_GetNumCameraFrameSizes(device, obtained->format); - int is_framesize_valid = 0; + // Check frame size + const int numsizes = SDL_GetNumCameraFrameSizes(device, obtained->format); + SDL_bool is_framesize_valid = SDL_FALSE; - for (i = 0; i < num; i++) { - int w, h; - if (SDL_GetCameraFrameSize(device, obtained->format, i, &w, &h) == 0) { - if (desired->width == w && desired->height == h) { - is_framesize_valid = 1; - obtained->width = w; - obtained->height = h; - break; - } + for (int i = 0; i < numsizes; i++) { + int w, h; + if (SDL_GetCameraFrameSize(device, obtained->format, i, &w, &h) == 0) { + if (desired->width == w && desired->height == h) { + is_framesize_valid = SDL_TRUE; + obtained->width = w; + obtained->height = h; + break; } } + } - if (!is_framesize_valid) { - if (allowed_changes) { - int w, h; - if (SDL_GetCameraFrameSize(device, obtained->format, 0, &w, &h) == 0) { - is_framesize_valid = 1; - obtained->width = w; - obtained->height = h; - } - } else { - SDL_SetError("Not allowed to change the frame size"); - return -1; + if (!is_framesize_valid) { + if (allowed_changes) { + int w, h; + if (SDL_GetCameraFrameSize(device, obtained->format, 0, &w, &h) == 0) { + is_framesize_valid = SDL_TRUE; + obtained->width = w; + obtained->height = h; } + } else { + return SDL_SetError("Not allowed to change the frame size"); } + } - if (!is_framesize_valid) { - SDL_SetError("Invalid frame size"); - return -1; - } - + if (!is_framesize_valid) { + return SDL_SetError("Invalid frame size"); } return 0; } -const char * -SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id) +const char *SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id) { static char buf[256]; buf[0] = 0; @@ -299,47 +257,41 @@ SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id) if (GetCameraDeviceName(instance_id, buf, sizeof (buf)) < 0) { buf[0] = 0; } + return buf; } -SDL_CameraDeviceID * -SDL_GetCameraDevices(int *count) +SDL_CameraDeviceID *SDL_GetCameraDevices(int *count) { + int dummycount = 0; + if (!count) { + count = &dummycount; + } int num = 0; - SDL_CameraDeviceID *ret = GetCameraDevices(&num); - - if (ret) { - if (count) { - *count = num; - } - return ret; + SDL_CameraDeviceID *retval = GetCameraDevices(&num); + if (retval) { + *count = num; + return retval; } - /* return list of 0 ID, null terminated */ - num = 0; - ret = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); - - if (ret == NULL) { + // return list of 0 ID, null terminated + retval = (SDL_CameraDeviceID *)SDL_calloc(1, sizeof(*retval)); + if (retval == NULL) { SDL_OutOfMemory(); - if (count) { - *count = 0; - } + *count = 0; return NULL; } - ret[num] = 0; - if (count) { - *count = num; - } + retval[0] = 0; + *count = 0; - return ret; + return retval; } -/* Camera thread function */ -static int SDLCALL -SDL_CameraThread(void *devicep) +// Camera thread function +static int SDLCALL SDL_CameraThread(void *devicep) { const int delay = 20; SDL_CameraDevice *device = (SDL_CameraDevice *) devicep; @@ -358,23 +310,23 @@ SDL_CameraThread(void *devicep) Android_JNI_CameraSetThreadPriority(device->iscapture, device); }*/ #else - /* The camera capture is always a high priority thread */ + // The camera capture is always a high priority thread SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); #endif - /* Perform any thread setup */ + // Perform any thread setup device->threadid = SDL_GetCurrentThreadID(); - /* Init state */ + // Init state + // !!! FIXME: use a semaphore or something while (!SDL_AtomicGet(&device->enabled)) { SDL_Delay(delay); } - /* Loop, filling the camera buffers */ + // Loop, filling the camera buffers while (!SDL_AtomicGet(&device->shutdown)) { SDL_CameraFrame f; int ret; - entry_t *entry; SDL_zero(f); @@ -389,7 +341,7 @@ SDL_CameraThread(void *devicep) } if (ret < 0) { - /* Flag it as an error */ + // Flag it as an error #if DEBUG_CAMERA SDL_Log("dev[%p] error AcquireFrame: %d %s", (void *)device, ret, SDL_GetError()); #endif @@ -397,7 +349,7 @@ SDL_CameraThread(void *devicep) } - entry = SDL_malloc(sizeof (entry_t)); + entry_t *entry = SDL_malloc(sizeof (entry_t)); if (entry == NULL) { goto error_mem; } @@ -424,26 +376,25 @@ SDL_CameraThread(void *devicep) SDL_Log("dev[%p] End thread 'SDL_CameraThread' with error: %s", (void *)device, SDL_GetError()); #endif SDL_AtomicSet(&device->shutdown, 1); - SDL_OutOfMemory(); + SDL_OutOfMemory(); // !!! FIXME: this error isn't accessible since the thread is about to terminate return 0; } -SDL_CameraDevice * -SDL_OpenCamera(SDL_CameraDeviceID instance_id) +SDL_CameraDevice *SDL_OpenCamera(SDL_CameraDeviceID instance_id) { - int i, n = SDL_arraysize(open_devices); - int id = -1; + const int n = SDL_arraysize(open_devices); SDL_CameraDevice *device = NULL; const char *device_name = NULL; + int id = -1; if (!SDL_WasInit(SDL_INIT_VIDEO)) { SDL_SetError("Video subsystem is not initialized"); goto error; } - /* !!! FIXME: there is a race condition here if two devices open from two threads at once. */ - /* Find an available device ID... */ - for (i = 0; i < n; i++) { + // !!! FIXME: there is a race condition here if two devices open from two threads at once. + // Find an available device ID... + for (int i = 0; i < n; i++) { if (open_devices[i] == NULL) { id = i; break; @@ -470,7 +421,7 @@ SDL_OpenCamera(SDL_CameraDeviceID instance_id) #if 0 // FIXME do we need this ? - /* Let the user override. */ + // Let the user override. { const char *dev = SDL_getenv("SDL_CAMERA_DEVICE_NAME"); if (dev && dev[0]) { @@ -490,7 +441,6 @@ SDL_OpenCamera(SDL_CameraDeviceID instance_id) } device->dev_name = SDL_strdup(device_name); - SDL_AtomicSet(&device->shutdown, 0); SDL_AtomicSet(&device->enabled, 0); @@ -510,37 +460,28 @@ SDL_OpenCamera(SDL_CameraDeviceID instance_id) goto error; } - /* empty */ + // empty device->buffer_queue = NULL; - open_devices[id] = device; /* add it to our list of open devices. */ - - - /* Start the camera thread */ - { - const size_t stacksize = 64 * 1024; - char threadname[64]; + open_devices[id] = device; // add it to our list of open devices. - SDL_snprintf(threadname, sizeof (threadname), "SDLCamera%d", id); - device->thread = SDL_CreateThreadInternal(SDL_CameraThread, threadname, stacksize, device); - if (device->thread == NULL) { - SDL_SetError("Couldn't create camera thread"); - goto error; - } + // Start the camera thread + char threadname[64]; + SDL_snprintf(threadname, sizeof (threadname), "SDLCamera%d", id); + device->thread = SDL_CreateThreadInternal(SDL_CameraThread, threadname, 0, device); + if (device->thread == NULL) { + SDL_SetError("Couldn't create camera thread"); + goto error; } return device; error: - close_device(device); + CloseCameraDevice(device); return NULL; } -int -SDL_SetCameraSpec(SDL_CameraDevice *device, - const SDL_CameraSpec *desired, - SDL_CameraSpec *obtained, - int allowed_changes) +int SDL_SetCameraSpec(SDL_CameraDevice *device, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes) { SDL_CameraSpec _obtained; SDL_CameraSpec _desired; @@ -548,9 +489,7 @@ SDL_SetCameraSpec(SDL_CameraDevice *device, if (!device) { return SDL_InvalidParamError("device"); - } - - if (device->is_spec_set == SDL_TRUE) { + } else if (device->is_spec_set == SDL_TRUE) { return SDL_SetError("already configured"); } @@ -559,7 +498,7 @@ SDL_SetCameraSpec(SDL_CameraDevice *device, desired = &_desired; allowed_changes = SDL_CAMERA_ALLOW_ANY_CHANGE; } else { - /* in case desired == obtained */ + // in case desired == obtained _desired = *desired; desired = &_desired; } @@ -588,14 +527,11 @@ SDL_SetCameraSpec(SDL_CameraDevice *device, return 0; } -int -SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame) +int SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame) { if (!device) { return SDL_InvalidParamError("device"); - } - - if (!frame) { + } else if (!frame) { return SDL_InvalidParamError("frame"); } @@ -604,7 +540,7 @@ SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame) if (device->thread == NULL) { int ret; - /* Wait for a frame */ + // Wait for a frame while ((ret = AcquireFrame(device, frame)) == 0) { if (frame->num_planes) { return 0; @@ -622,42 +558,33 @@ SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame) *frame = entry->frame; SDL_free(entry); - /* Error from thread */ + // Error from thread if (frame->num_planes == 0 && frame->timestampNS == 0) { return SDL_SetError("error from acquisition thread"); } - - } else { - /* Queue is empty. Not an error. */ + // Queue is empty. Not an error. } } return 0; } -int -SDL_ReleaseCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame) +int SDL_ReleaseCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame) { if (!device) { return SDL_InvalidParamError("device"); - } - - if (frame == NULL) { + } else if (frame == NULL) { return SDL_InvalidParamError("frame"); - } - - if (ReleaseFrame(device, frame) < 0) { + } else if (ReleaseFrame(device, frame) < 0) { return -1; } SDL_zerop(frame); - return 0; } -int -SDL_GetNumCameraFormats(SDL_CameraDevice *device) +int SDL_GetNumCameraFormats(SDL_CameraDevice *device) { if (!device) { return SDL_InvalidParamError("device"); @@ -665,21 +592,18 @@ SDL_GetNumCameraFormats(SDL_CameraDevice *device) return GetNumFormats(device); } -int -SDL_GetCameraFormat(SDL_CameraDevice *device, int index, Uint32 *format) +int SDL_GetCameraFormat(SDL_CameraDevice *device, int index, Uint32 *format) { if (!device) { return SDL_InvalidParamError("device"); - } - if (!format) { + } else if (!format) { return SDL_InvalidParamError("format"); } *format = 0; return GetFormat(device, index, format); } -int -SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format) +int SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format) { if (!device) { return SDL_InvalidParamError("device"); @@ -687,29 +611,20 @@ SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format) return GetNumFrameSizes(device, format); } -int -SDL_GetCameraFrameSize(SDL_CameraDevice *device, Uint32 format, int index, int *width, int *height) +int SDL_GetCameraFrameSize(SDL_CameraDevice *device, Uint32 format, int index, int *width, int *height) { if (!device) { return SDL_InvalidParamError("device"); - } - if (!width) { + } else if (!width) { return SDL_InvalidParamError("width"); - } - if (!height) { + } else if (!height) { return SDL_InvalidParamError("height"); } - *width = 0; - *height = 0; + *width = *height = 0; return GetFrameSize(device, format, index, width, height); } -SDL_CameraDevice * -SDL_OpenCameraWithSpec( - SDL_CameraDeviceID instance_id, - const SDL_CameraSpec *desired, - SDL_CameraSpec *obtained, - int allowed_changes) +SDL_CameraDevice *SDL_OpenCameraWithSpec(SDL_CameraDeviceID instance_id, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes) { SDL_CameraDevice *device; @@ -724,42 +639,32 @@ SDL_OpenCameraWithSpec( return device; } -SDL_CameraStatus -SDL_GetCameraStatus(SDL_CameraDevice *device) +SDL_CameraStatus SDL_GetCameraStatus(SDL_CameraDevice *device) { if (device == NULL) { return SDL_CAMERA_INIT; - } - - if (device->is_spec_set == SDL_FALSE) { + } else if (device->is_spec_set == SDL_FALSE) { return SDL_CAMERA_INIT; - } - - if (SDL_AtomicGet(&device->shutdown)) { + } else if (SDL_AtomicGet(&device->shutdown)) { return SDL_CAMERA_STOPPED; - } - - if (SDL_AtomicGet(&device->enabled)) { + } else if (SDL_AtomicGet(&device->enabled)) { return SDL_CAMERA_PLAYING; } return SDL_CAMERA_INIT; } -int -SDL_CameraInit(void) +int SDL_CameraInit(void) { SDL_zeroa(open_devices); - SDL_SYS_CameraInit(); return 0; } -void -SDL_QuitCamera(void) +void SDL_QuitCamera(void) { - int i, n = SDL_arraysize(open_devices); - for (i = 0; i < n; i++) { - close_device(open_devices[i]); + const int n = SDL_arraysize(open_devices); + for (int i = 0; i < n; i++) { + CloseCameraDevice(open_devices[i]); } SDL_zeroa(open_devices); diff --git a/src/camera/SDL_camera_c.h b/src/camera/SDL_camera_c.h index 1b3ea36e85dea..787b5f2db5fce 100644 --- a/src/camera/SDL_camera_c.h +++ b/src/camera/SDL_camera_c.h @@ -23,10 +23,10 @@ #ifndef SDL_camera_c_h_ #define SDL_camera_c_h_ -/* Initialize the camera subsystem */ +// Initialize the camera subsystem int SDL_CameraInit(void); -/* Shutdown the camera subsystem */ +// Shutdown the camera subsystem void SDL_QuitCamera(void); -#endif /* SDL_camera_c_h_ */ +#endif // SDL_camera_c_h_ diff --git a/src/camera/SDL_syscamera.h b/src/camera/SDL_syscamera.h index cb0ce2c6f2a51..990272f8d0a82 100644 --- a/src/camera/SDL_syscamera.h +++ b/src/camera/SDL_syscamera.h @@ -25,45 +25,43 @@ #include "../SDL_list.h" -/* The SDL camera driver */ +// The SDL camera driver typedef struct SDL_CameraDevice SDL_CameraDevice; -/* Define the SDL camera driver structure */ +// Define the SDL camera driver structure struct SDL_CameraDevice { - /* * * */ - /* Data common to all devices */ - - /* The device's current camera specification */ + // The device's current camera specification SDL_CameraSpec spec; - /* Device name */ + // Device name char *dev_name; - /* Current state flags */ + // Current state flags SDL_AtomicInt shutdown; SDL_AtomicInt enabled; SDL_bool is_spec_set; - /* A mutex for locking the queue buffers */ + // A mutex for locking the queue buffers SDL_Mutex *device_lock; SDL_Mutex *acquiring_lock; - /* A thread to feed the camera device */ + // A thread to feed the camera device SDL_Thread *thread; SDL_ThreadID threadid; - /* Queued buffers (if app not using callback). */ + // Queued buffers (if app not using callback). SDL_ListNode *buffer_queue; - /* * * */ - /* Data private to this driver */ + // Data private to this driver struct SDL_PrivateCameraData *hidden; }; extern int SDL_SYS_CameraInit(void); extern int SDL_SYS_CameraQuit(void); +// !!! FIXME: These names need to be made camera-specific. + extern int OpenDevice(SDL_CameraDevice *_this); extern void CloseDevice(SDL_CameraDevice *_this); @@ -86,7 +84,7 @@ extern int GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int * extern int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size); extern SDL_CameraDeviceID *GetCameraDevices(int *count); -extern SDL_bool check_all_device_closed(void); -extern SDL_bool check_device_playing(void); +extern SDL_bool CheckAllDeviceClosed(void); +extern SDL_bool CheckDevicePlaying(void); -#endif /* SDL_syscamera_h_ */ +#endif // SDL_syscamera_h_ diff --git a/src/camera/android/SDL_camera_android.c b/src/camera/android/SDL_camera_android.c index b00e183345023..51d2446b6709a 100644 --- a/src/camera/android/SDL_camera_android.c +++ b/src/camera/android/SDL_camera_android.c @@ -62,8 +62,7 @@ static ACameraManager *cameraMgr = NULL; static ACameraIdList *cameraIdList = NULL; -static void -create_cameraMgr(void) +static void create_cameraMgr(void) { if (cameraMgr == NULL) { #if 0 // !!! FIXME: this is getting replaced in a different branch. @@ -81,8 +80,7 @@ create_cameraMgr(void) } } -static void -delete_cameraMgr(void) +static void delete_cameraMgr(void) { if (cameraIdList) { ACameraManager_deleteCameraIdList(cameraIdList); @@ -104,50 +102,46 @@ struct SDL_PrivateCameraData ACaptureSessionOutputContainer *sessionOutputContainer; AImageReader *reader; int num_formats; - int count_formats[6]; // see format_2_id + int count_formats[6]; // see format_to_id }; -/**/ #define FORMAT_SDL SDL_PIXELFORMAT_NV12 -static int -format_2_id(int fmt) { +static int format_to_id(int fmt) { switch (fmt) { -#define CASE(x, y) case x: return y + #define CASE(x, y) case x: return y CASE(FORMAT_SDL, 0); CASE(SDL_PIXELFORMAT_RGB565, 1); CASE(SDL_PIXELFORMAT_XRGB8888, 2); CASE(SDL_PIXELFORMAT_RGBA8888, 3); CASE(SDL_PIXELFORMAT_RGBX8888, 4); CASE(SDL_PIXELFORMAT_UNKNOWN, 5); -#undef CASE + #undef CASE default: - return 5; + return 5; } } -static int -id_2_format(int fmt) { +static int id_to_format(int fmt) { switch (fmt) { -#define CASE(x, y) case y: return x + #define CASE(x, y) case y: return x CASE(FORMAT_SDL, 0); CASE(SDL_PIXELFORMAT_RGB565, 1); CASE(SDL_PIXELFORMAT_XRGB8888, 2); CASE(SDL_PIXELFORMAT_RGBA8888, 3); CASE(SDL_PIXELFORMAT_RGBX8888, 4); CASE(SDL_PIXELFORMAT_UNKNOWN, 5); -#undef CASE + #undef CASE default: return SDL_PIXELFORMAT_UNKNOWN; } } -static Uint32 -format_android_2_sdl(Uint32 fmt) +static Uint32 format_android_to_sdl(Uint32 fmt) { switch (fmt) { -#define CASE(x, y) case x: return y + #define CASE(x, y) case x: return y CASE(AIMAGE_FORMAT_YUV_420_888, FORMAT_SDL); CASE(AIMAGE_FORMAT_RGB_565, SDL_PIXELFORMAT_RGB565); CASE(AIMAGE_FORMAT_RGB_888, SDL_PIXELFORMAT_XRGB8888); @@ -157,71 +151,72 @@ format_android_2_sdl(Uint32 fmt) CASE(AIMAGE_FORMAT_RGBA_FP16, SDL_PIXELFORMAT_UNKNOWN); // 64bits CASE(AIMAGE_FORMAT_RAW_PRIVATE, SDL_PIXELFORMAT_UNKNOWN); CASE(AIMAGE_FORMAT_JPEG, SDL_PIXELFORMAT_UNKNOWN); -#undef CASE + #undef CASE default: SDL_Log("Unknown format AIMAGE_FORMAT '%d'", fmt); return SDL_PIXELFORMAT_UNKNOWN; } } -static Uint32 -format_sdl_2_android(Uint32 fmt) +static Uint32 format_sdl_to_android(Uint32 fmt) { switch (fmt) { -#define CASE(x, y) case y: return x + #define CASE(x, y) case y: return x CASE(AIMAGE_FORMAT_YUV_420_888, FORMAT_SDL); CASE(AIMAGE_FORMAT_RGB_565, SDL_PIXELFORMAT_RGB565); CASE(AIMAGE_FORMAT_RGB_888, SDL_PIXELFORMAT_XRGB8888); CASE(AIMAGE_FORMAT_RGBA_8888, SDL_PIXELFORMAT_RGBA8888); CASE(AIMAGE_FORMAT_RGBX_8888, SDL_PIXELFORMAT_RGBX8888); -#undef CASE + #undef CASE default: return 0; } } -static void -onDisconnected(void *context, ACameraDevice *device) +static void onDisconnected(void *context, ACameraDevice *device) { // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; + #if DEBUG_CAMERA SDL_Log("CB onDisconnected"); + #endif } -static void -onError(void *context, ACameraDevice *device, int error) +static void onError(void *context, ACameraDevice *device, int error) { // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; + #if DEBUG_CAMERA SDL_Log("CB onError"); + #endif } -static void -onClosed(void* context, ACameraCaptureSession *session) +static void onClosed(void* context, ACameraCaptureSession *session) { // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; + #if DEBUG_CAMERA SDL_Log("CB onClosed"); + #endif } -static void -onReady(void* context, ACameraCaptureSession *session) +static void onReady(void* context, ACameraCaptureSession *session) { // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; + #if DEBUG_CAMERA SDL_Log("CB onReady"); + #endif } -static void -onActive(void* context, ACameraCaptureSession *session) +static void onActive(void* context, ACameraCaptureSession *session) { // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; + #if DEBUG_CAMERA SDL_Log("CB onActive"); + #endif } -int -OpenDevice(SDL_CameraDevice *_this) +int OpenDevice(SDL_CameraDevice *_this) { - camera_status_t res; - /* Cannot open a second camera, while the first one is opened. * If you want to play several camera, they must all be opened first, then played. * @@ -230,7 +225,7 @@ OpenDevice(SDL_CameraDevice *_this) * before configuring sessions on any of the camera devices. * " * */ - if (check_device_playing()) { + if (CheckDevicePlaying()) { return SDL_SetError("A camera is already playing"); } @@ -245,7 +240,7 @@ OpenDevice(SDL_CameraDevice *_this) _this->hidden->dev_callbacks.onDisconnected = onDisconnected; _this->hidden->dev_callbacks.onError = onError; - res = ACameraManager_openCamera(cameraMgr, _this->dev_name, &_this->hidden->dev_callbacks, &_this->hidden->device); + camera_status_t res = ACameraManager_openCamera(cameraMgr, _this->dev_name, &_this->hidden->dev_callbacks, &_this->hidden->device); if (res != ACAMERA_OK) { return SDL_SetError("Failed to open camera"); } @@ -253,8 +248,7 @@ OpenDevice(SDL_CameraDevice *_this) return 0; } -void -CloseDevice(SDL_CameraDevice *_this) +void CloseDevice(SDL_CameraDevice *_this) { if (_this && _this->hidden) { if (_this->hidden->session) { @@ -278,13 +272,13 @@ CloseDevice(SDL_CameraDevice *_this) _this->hidden = NULL; } - if (check_all_device_closed()) { + // !!! FIXME: just refcount this? + if (CheckAllDeviceClosed()) { delete_cameraMgr(); } } -int -InitDevice(SDL_CameraDevice *_this) +int InitDevice(SDL_CameraDevice *_this) { size_t size, pitch; SDL_CalculateSize(_this->spec.format, _this->spec.width, _this->spec.height, &size, &pitch, SDL_FALSE); @@ -292,19 +286,19 @@ InitDevice(SDL_CameraDevice *_this) return 0; } -int -GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) +int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) { + // !!! FIXME: catch NULLs at higher level if (spec) { - *spec = _this->spec; + SDL_copyp(spec, &_this->spec); return 0; } return -1; } -int -StartCamera(SDL_CameraDevice *_this) +int StartCamera(SDL_CameraDevice *_this) { + // !!! FIXME: maybe log the error code in SDL_SetError camera_status_t res; media_status_t res2; ANativeWindow *window = NULL; @@ -312,7 +306,7 @@ StartCamera(SDL_CameraDevice *_this) ACameraOutputTarget *outputTarget; ACaptureRequest *request; - res2 = AImageReader_new(_this->spec.width, _this->spec.height, format_sdl_2_android(_this->spec.format), 10 /* nb buffers */, &_this->hidden->reader); + res2 = AImageReader_new(_this->spec.width, _this->spec.height, format_sdl_to_android(_this->spec.format), 10 /* nb buffers */, &_this->hidden->reader); if (res2 != AMEDIA_OK) { SDL_SetError("Error AImageReader_new"); goto error; @@ -321,10 +315,8 @@ StartCamera(SDL_CameraDevice *_this) if (res2 != AMEDIA_OK) { SDL_SetError("Error AImageReader_new"); goto error; - } - res = ACaptureSessionOutput_create(window, &sessionOutput); if (res != ACAMERA_OK) { SDL_SetError("Error ACaptureSessionOutput_create"); @@ -341,14 +333,12 @@ StartCamera(SDL_CameraDevice *_this) goto error; } - res = ACameraOutputTarget_create(window, &outputTarget); if (res != ACAMERA_OK) { SDL_SetError("Error ACameraOutputTarget_create"); goto error; } - res = ACameraDevice_createCaptureRequest(_this->hidden->device, TEMPLATE_RECORD, &request); if (res != ACAMERA_OK) { SDL_SetError("Error ACameraDevice_createCaptureRequest"); @@ -361,7 +351,6 @@ StartCamera(SDL_CameraDevice *_this) goto error; } - _this->hidden->capture_callbacks.context = (void *) _this; _this->hidden->capture_callbacks.onClosed = onClosed; _this->hidden->capture_callbacks.onReady = onReady; @@ -388,16 +377,14 @@ StartCamera(SDL_CameraDevice *_this) return -1; } -int -StopCamera(SDL_CameraDevice *_this) +int StopCamera(SDL_CameraDevice *_this) { ACameraCaptureSession_close(_this->hidden->session); _this->hidden->session = NULL; return 0; } -int -AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { media_status_t res; AImage *image; @@ -406,20 +393,17 @@ AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) res = AImageReader_acquireLatestImage(_this->hidden->reader, &image); */ if (res == AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE ) { - SDL_Delay(20); // TODO fix some delay -#if DEBUG_CAMERA -// SDL_Log("AImageReader_acquireNextImage: AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE"); -#endif - return 0; + #if DEBUG_CAMERA + //SDL_Log("AImageReader_acquireNextImage: AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE"); + #endif } else if (res == AMEDIA_OK ) { - int i = 0; int32_t numPlanes = 0; AImage_getNumberOfPlanes(image, &numPlanes); frame->timestampNS = SDL_GetTicksNS(); - for (i = 0; i < numPlanes && i < 3; i++) { + for (int i = 0; i < numPlanes && i < 3; i++) { int dataLength = 0; int rowStride = 0; uint8_t *data = NULL; @@ -442,18 +426,16 @@ AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) } frame->internal = (void*)image; - return 0; } else if (res == AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED) { - SDL_SetError("AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED"); + return SDL_SetError("AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED"); } else { - SDL_SetError("AImageReader_acquireNextImage: %d", res); + return SDL_SetError("AImageReader_acquireNextImage: %d", res); } - return -1; + return 0; } -int -ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { if (frame->internal){ AImage_delete((AImage *)frame->internal); @@ -461,12 +443,10 @@ ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) return 0; } -int -GetNumFormats(SDL_CameraDevice *_this) +int GetNumFormats(SDL_CameraDevice *_this) { camera_status_t res; - int i; - int unknown = 0; + SDL_bool unknown = SDL_FALSE; ACameraMetadata *metadata; ACameraMetadata_const_entry entry; @@ -486,34 +466,33 @@ GetNumFormats(SDL_CameraDevice *_this) SDL_Log("got entry ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS"); - for (i = 0; i < entry.count; i += 4) { - int32_t format = entry.data.i32[i + 0]; - int32_t type = entry.data.i32[i + 3]; - Uint32 fmt; + for (int i = 0; i < entry.count; i += 4) { + const int32_t format = entry.data.i32[i + 0]; + const int32_t type = entry.data.i32[i + 3]; if (type == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) { continue; } - fmt = format_android_2_sdl(format); - _this->hidden->count_formats[format_2_id(fmt)] += 1; + const Uint32 fmt = format_android_to_sdl(format); + _this->hidden->count_formats[format_to_id(fmt)] += 1; -#if DEBUG_CAMERA + #if DEBUG_CAMERA if (fmt != SDL_PIXELFORMAT_UNKNOWN) { int w = entry.data.i32[i + 1]; int h = entry.data.i32[i + 2]; SDL_Log("Got format android 0x%08x -> %s %d x %d", format, SDL_GetPixelFormatName(fmt), w, h); } else { - unknown += 1; + unknown = SDL_TRUE; } -#endif + #endif } -#if DEBUG_CAMERA - if (unknown) { - SDL_Log("Got unknown android"); - } -#endif + #if DEBUG_CAMERA + if (unknown) { + SDL_Log("Got unknown android"); + } + #endif if ( _this->hidden->count_formats[0]) _this->hidden->num_formats += 1; @@ -526,10 +505,8 @@ GetNumFormats(SDL_CameraDevice *_this) return _this->hidden->num_formats; } -int -GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) +int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) { - int i; int i2 = 0; if (_this->hidden->num_formats == 0) { @@ -537,16 +514,17 @@ GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) } if (index < 0 || index >= _this->hidden->num_formats) { + // !!! FIXME: call SDL_SetError()? return -1; } - for (i = 0; i < SDL_arraysize(_this->hidden->count_formats); i++) { + for (int i = 0; i < SDL_arraysize(_this->hidden->count_formats); i++) { if (_this->hidden->count_formats[i] == 0) { continue; } if (i2 == index) { - *format = id_2_format(i); + *format = id_to_format(i); } i2++; @@ -555,17 +533,17 @@ GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) return 0; } -int -GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) +int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) { - int i, i2 = 0, index; + // !!! FIXME: call SDL_SetError()? if (_this->hidden->num_formats == 0) { GetNumFormats(_this); } - index = format_2_id(format); + const int index = format_to_id(format); - for (i = 0; i < SDL_arraysize(_this->hidden->count_formats); i++) { + int i2 = 0; + for (int i = 0; i < SDL_arraysize(_this->hidden->count_formats); i++) { if (_this->hidden->count_formats[i] == 0) { continue; } @@ -581,11 +559,10 @@ GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) return -1; } -int -GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) +int GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) { + // !!! FIXME: call SDL_SetError()? camera_status_t res; - int i, i2 = 0; ACameraMetadata *metadata; ACameraMetadata_const_entry entry; @@ -603,19 +580,18 @@ GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int return -1; } - for (i = 0; i < entry.count; i += 4) { + int i2 = 0; + for (int i = 0; i < entry.count; i += 4) { int32_t f = entry.data.i32[i + 0]; - int w = entry.data.i32[i + 1]; - int h = entry.data.i32[i + 2]; + const int w = entry.data.i32[i + 1]; + const int h = entry.data.i32[i + 2]; int32_t type = entry.data.i32[i + 3]; - Uint32 fmt; if (type == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) { continue; } - - fmt = format_android_2_sdl(f); + Uint32 fmt = format_android_to_sdl(f); if (fmt != format) { continue; } @@ -628,33 +604,11 @@ GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int i2++; } - return -1; -} - -static int GetNumDevices(void); - -int -GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) -{ - int index = instance_id - 1; - create_cameraMgr(); - - if (cameraIdList == NULL) { - GetNumDevices(); - } - - if (cameraIdList) { - if (index >= 0 && index < cameraIdList->numCameras) { - SDL_snprintf(buf, size, "%s", cameraIdList->cameraIds[index]); - return 0; - } - } return -1; } -static int -GetNumDevices(void) +static int GetNumDevices(void) { camera_status_t res; create_cameraMgr(); @@ -674,37 +628,55 @@ GetNumDevices(void) return -1; } -SDL_CameraDeviceID *GetCameraDevices(int *count) +int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) { - /* hard-coded list of ID */ - int i; - int num = GetNumDevices(); - SDL_CameraDeviceID *ret; + // !!! FIXME: call SDL_SetError()? + int index = instance_id - 1; + create_cameraMgr(); - ret = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); + if (cameraIdList == NULL) { + GetNumDevices(); + } - if (ret == NULL) { + if (cameraIdList) { + if (index >= 0 && index < cameraIdList->numCameras) { + SDL_snprintf(buf, size, "%s", cameraIdList->cameraIds[index]); + return 0; + } + } + + return -1; +} + +SDL_CameraDeviceID *GetCameraDevices(int *count) +{ + // hard-coded list of ID + const int num = GetNumDevices(); + SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); + + if (retval == NULL) { SDL_OutOfMemory(); *count = 0; return NULL; } - for (i = 0; i < num; i++) { - ret[i] = i + 1; + for (int i = 0; i < num; i++) { + retval[i] = i + 1; } - ret[num] = 0; + retval[num] = 0; *count = num; - return ret; + return retval; } -int SDL_SYS_CameraInit(void) { +int SDL_SYS_CameraInit(void) +{ return 0; } -int SDL_SYS_CameraQuit(void) { +int SDL_SYS_CameraQuit(void) +{ return 0; } #endif - diff --git a/src/camera/apple/SDL_camera_apple.m b/src/camera/apple/SDL_camera_apple.m index b0a187349c2b2..ae44fe69cbc54 100644 --- a/src/camera/apple/SDL_camera_apple.m +++ b/src/camera/apple/SDL_camera_apple.m @@ -1,6 +1,6 @@ /* Simple DirectMedia Layer - Copyright (C) 2021 Valve Corporation + Copyright (C) 1997-2023 Sam Lantinga This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -27,7 +27,7 @@ #include "../thread/SDL_systhread.h" #if defined(HAVE_COREMEDIA) && defined(SDL_PLATFORM_MACOS) && (__MAC_OS_X_VERSION_MAX_ALLOWED < 101500) -/* AVCaptureDeviceTypeBuiltInWideAngleCamera requires macOS SDK 10.15 */ +// AVCaptureDeviceTypeBuiltInWideAngleCamera requires macOS SDK 10.15 #undef HAVE_COREMEDIA #endif @@ -35,7 +35,9 @@ #undef HAVE_COREMEDIA #endif -#ifndef HAVE_COREMEDIA /* !!! FIXME: use the dummy driver. */ +// !!! FIXME: use the dummy driver +// !!! FIXME: actually, move everything over to backend callbacks instead. +#ifndef HAVE_COREMEDIA int InitDevice(SDL_CameraDevice *_this) { return -1; } @@ -119,16 +121,14 @@ int SDL_SYS_CameraQuit(void) { CMSimpleQueueRef frame_queue; }; -static NSString * -fourcc_to_nstring(Uint32 code) +static NSString *fourcc_to_nstring(Uint32 code) { Uint8 buf[4]; *(Uint32 *)buf = code; return [NSString stringWithFormat:@"%c%c%c%c", buf[3], buf[2], buf[1], buf[0]]; } -static NSArray * -discover_devices() +static NSArray *DiscoverCameraDevices() { NSArray *deviceType = @[AVCaptureDeviceTypeBuiltInWideAngleCamera]; @@ -154,10 +154,9 @@ int SDL_SYS_CameraQuit(void) { return devices; } -static AVCaptureDevice * -get_device_by_name(const char *dev_name) +static AVCaptureDevice *GetCameraDeviceByName(const char *dev_name) { - NSArray *devices = discover_devices(); + NSArray *devices = DiscoverCameraDevices(); for (AVCaptureDevice *device in devices) { char buf[1024]; @@ -171,45 +170,41 @@ int SDL_SYS_CameraQuit(void) { return nil; } -static Uint32 -nsfourcc_to_sdlformat(NSString *nsfourcc) +static Uint32 nsfourcc_to_sdlformat(NSString *nsfourcc) { - const char *str = [nsfourcc UTF8String]; + const char *str = [nsfourcc UTF8String]; - /* FIXME - * on IOS this mode gives 2 planes, and it's NV12 - * on macos, 1 plane/ YVYU - * - */ -#ifdef SDL_PLATFORM_MACOS - if (SDL_strcmp("420v", str) == 0) return SDL_PIXELFORMAT_YVYU; -#else - if (SDL_strcmp("420v", str) == 0) return SDL_PIXELFORMAT_NV12; -#endif - if (SDL_strcmp("yuvs", str) == 0) return SDL_PIXELFORMAT_UYVY; - if (SDL_strcmp("420f", str) == 0) return SDL_PIXELFORMAT_UNKNOWN; + /* FIXME + * on IOS this mode gives 2 planes, and it's NV12 + * on macos, 1 plane/ YVYU + */ + #ifdef SDL_PLATFORM_MACOS + if (SDL_strcmp("420v", str) == 0) return SDL_PIXELFORMAT_YVYU; + #else + if (SDL_strcmp("420v", str) == 0) return SDL_PIXELFORMAT_NV12; + #endif - SDL_Log("Unknown format '%s'", str); + if (SDL_strcmp("yuvs", str) == 0) return SDL_PIXELFORMAT_UYVY; + if (SDL_strcmp("420f", str) == 0) return SDL_PIXELFORMAT_UNKNOWN; - return SDL_PIXELFORMAT_UNKNOWN; + SDL_Log("Unknown format '%s'", str); + + return SDL_PIXELFORMAT_UNKNOWN; } -static NSString * -sdlformat_to_nsfourcc(Uint32 fmt) +static NSString *sdlformat_to_nsfourcc(Uint32 fmt) { - const char *str = ""; - NSString *result; + const char *str = ""; + NSString *result; #ifdef SDL_PLATFORM_MACOS - if (fmt == SDL_PIXELFORMAT_YVYU) str = "420v"; + if (fmt == SDL_PIXELFORMAT_YVYU) str = "420v"; #else - if (fmt == SDL_PIXELFORMAT_NV12) str = "420v"; + if (fmt == SDL_PIXELFORMAT_NV12) str = "420v"; #endif - if (fmt == SDL_PIXELFORMAT_UYVY) str = "yuvs"; - - result = [[NSString alloc] initWithUTF8String: str]; + if (fmt == SDL_PIXELFORMAT_UYVY) str = "yuvs"; - return result; + return [[NSString alloc] initWithUTF8String: str]; } @@ -234,27 +229,21 @@ - (void) captureOutput:(AVCaptureOutput *)output - (void)captureOutput:(AVCaptureOutput *)output didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { + // !!! FIXME #if DEBUG_CAMERA SDL_Log("Drop frame.."); } @end -int -OpenDevice(SDL_CameraDevice *_this) +int OpenDevice(SDL_CameraDevice *_this) { _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); if (_this->hidden == NULL) { - SDL_OutOfMemory(); - goto error; + return SDL_OutOfMemory(); } - return 0; - -error: - return -1; } -void -CloseDevice(SDL_CameraDevice *_this) +void CloseDevice(SDL_CameraDevice *_this) { if (!_this) { return; @@ -282,9 +271,9 @@ - (void)captureOutput:(AVCaptureOutput *)output } } -int -InitDevice(SDL_CameraDevice *_this) +int InitDevice(SDL_CameraDevice *_this) { + // !!! FIXME: autorelease pool? NSString *fmt = sdlformat_to_nsfourcc(_this->spec.format); int w = _this->spec.width; int h = _this->spec.height; @@ -298,13 +287,13 @@ - (void)captureOutput:(AVCaptureOutput *)output #ifdef SDL_PLATFORM_MACOS if (@available(macOS 10.15, *)) { - /* good. */ + // good. } else { return -1; } #endif - device = get_device_by_name(_this->dev_name); + device = GetCameraDeviceByName(_this->dev_name); if (!device) { goto error; } @@ -317,14 +306,13 @@ - (void)captureOutput:(AVCaptureOutput *)output [_this->hidden->session setSessionPreset:AVCaptureSessionPresetHigh]; // Pick format that matches the spec - { - NSArray *formats = [device formats]; - for (AVCaptureDeviceFormat *format in formats) { - CMFormatDescriptionRef formatDescription = [format formatDescription]; - FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription); - NSString *str = fourcc_to_nstring(mediaSubType); - if (str == fmt) { - CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDescription); + NSArray *formats = [device formats]; + for (AVCaptureDeviceFormat *format in formats) { + CMFormatDescriptionRef formatDescription = [format formatDescription]; + FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription); + NSString *str = fourcc_to_nstring(mediaSubType); + if ([str isEqualToString:fmt]) { + CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDescription); if (dim.width == w && dim.height == h) { spec_format = format; break; @@ -334,8 +322,7 @@ - (void)captureOutput:(AVCaptureOutput *)output } if (spec_format == nil) { - SDL_SetError("format not found"); - goto error; + return SDL_SetError("format not found"); } // Set format @@ -343,15 +330,13 @@ - (void)captureOutput:(AVCaptureOutput *)output device.activeFormat = spec_format; [device unlockForConfiguration]; } else { - SDL_SetError("Cannot lockForConfiguration"); - goto error; + return SDL_SetError("Cannot lockForConfiguration"); } // Input input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; if (!input) { - SDL_SetError("Cannot create AVCaptureDeviceInput"); - goto error; + return SDL_SetError("Cannot create AVCaptureDeviceInput"); } // Output @@ -373,105 +358,85 @@ - (void)captureOutput:(AVCaptureOutput *)output CMSimpleQueueCreate(kCFAllocatorDefault, 30 /* buffers */, &_this->hidden->frame_queue); if (_this->hidden->frame_queue == nil) { - goto error; + return SDL_SetError("CMSimpleQueueCreate() failed"); } _this->hidden->queue = dispatch_queue_create("my_queue", NULL); [output setSampleBufferDelegate:_this->hidden->delegate queue:_this->hidden->queue]; - if ([_this->hidden->session canAddInput:input] ){ [_this->hidden->session addInput:input]; } else { - SDL_SetError("Cannot add AVCaptureDeviceInput"); - goto error; + return SDL_SetError("Cannot add AVCaptureDeviceInput"); } if ([_this->hidden->session canAddOutput:output] ){ [_this->hidden->session addOutput:output]; } else { - SDL_SetError("Cannot add AVCaptureVideoDataOutput"); - goto error; + return SDL_SetError("Cannot add AVCaptureVideoDataOutput"); } [_this->hidden->session commitConfiguration]; return 0; - -error: - return -1; } -int -GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) +int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) { + // !!! FIXME: make sure higher level checks spec != NULL if (spec) { - *spec = _this->spec; + SDL_copyp(spec, &_this->spec); return 0; } return -1; } -int -StartCamera(SDL_CameraDevice *_this) +int StartCamera(SDL_CameraDevice *_this) { [_this->hidden->session startRunning]; return 0; } -int -StopCamera(SDL_CameraDevice *_this) +int StopCamera(SDL_CameraDevice *_this) { [_this->hidden->session stopRunning]; return 0; } -int -AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { if (CMSimpleQueueGetCount(_this->hidden->frame_queue) > 0) { - int i, numPlanes, planar; - CMSampleBufferRef sampleBuffer; - CVImageBufferRef image; - - sampleBuffer = (CMSampleBufferRef)CMSimpleQueueDequeue(_this->hidden->frame_queue); + CMSampleBufferRef sampleBuffer = (CMSampleBufferRef)CMSimpleQueueDequeue(_this->hidden->frame_queue); frame->internal = (void *) sampleBuffer; frame->timestampNS = SDL_GetTicksNS(); - i = 0; - image = CMSampleBufferGetImageBuffer(sampleBuffer); - numPlanes = CVPixelBufferGetPlaneCount(image); - planar = CVPixelBufferIsPlanar(image); + CVImageBufferRef image = CMSampleBufferGetImageBuffer(sampleBuffer); + const int numPlanes = CVPixelBufferGetPlaneCount(image); + const int planar = CVPixelBufferIsPlanar(image); #if 0 - int w = CVPixelBufferGetWidth(image); - int h = CVPixelBufferGetHeight(image); - int sz = CVPixelBufferGetDataSize(image); - int pitch = CVPixelBufferGetBytesPerRow(image); + const int w = CVPixelBufferGetWidth(image); + const int h = CVPixelBufferGetHeight(image); + const int sz = CVPixelBufferGetDataSize(image); + const int pitch = CVPixelBufferGetBytesPerRow(image); SDL_Log("buffer planar=%d count:%d %d x %d sz=%d pitch=%d", planar, numPlanes, w, h, sz, pitch); #endif CVPixelBufferLockBaseAddress(image, 0); - if (planar == 0 && numPlanes == 0) { + if ((planar == 0) && (numPlanes == 0)) { frame->pitch[0] = CVPixelBufferGetBytesPerRow(image); frame->data[0] = CVPixelBufferGetBaseAddress(image); frame->num_planes = 1; } else { - for (i = 0; i < numPlanes && i < 3; i++) { - int rowStride = 0; - uint8_t *data = NULL; + for (int i = 0; (i < numPlanes) && (i < 3); i++) { frame->num_planes += 1; - - rowStride = CVPixelBufferGetBytesPerRowOfPlane(image, i); - data = CVPixelBufferGetBaseAddressOfPlane(image, i); - frame->data[i] = data; - frame->pitch[i] = rowStride; + frame->data[i] = CVPixelBufferGetBaseAddressOfPlane(image, i); + frame->pitch[i] = CVPixelBufferGetBytesPerRowOfPlane(image, i); } } - /* Unlocked when frame is released */ - + // Unlocked when frame is released } else { // no frame SDL_Delay(20); // TODO fix some delay @@ -479,24 +444,21 @@ - (void)captureOutput:(AVCaptureOutput *)output return 0; } -int -ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { - if (frame->internal){ + if (frame->internal) { CMSampleBufferRef sampleBuffer = (CMSampleBufferRef) frame->internal; - CVImageBufferRef image = CMSampleBufferGetImageBuffer(sampleBuffer); CVPixelBufferUnlockBaseAddress(image, 0); - CFRelease(sampleBuffer); } + return 0; } -int -GetNumFormats(SDL_CameraDevice *_this) +int GetNumFormats(SDL_CameraDevice *_this) { - AVCaptureDevice *device = get_device_by_name(_this->dev_name); + AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name); if (device) { // LIST FORMATS NSMutableOrderedSet *array_formats = [NSMutableOrderedSet new]; @@ -514,10 +476,9 @@ - (void)captureOutput:(AVCaptureOutput *)output return 0; } -int -GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) +int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) { - AVCaptureDevice *device = get_device_by_name(_this->dev_name); + AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name); if (device) { // LIST FORMATS NSMutableOrderedSet *array_formats = [NSMutableOrderedSet new]; @@ -542,10 +503,9 @@ - (void)captureOutput:(AVCaptureOutput *)output return -1; } -int -GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) +int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) { - AVCaptureDevice *device = get_device_by_name(_this->dev_name); + AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name); if (device) { NSString *fmt = sdlformat_to_nsfourcc(format); int count = 0; @@ -556,8 +516,8 @@ - (void)captureOutput:(AVCaptureOutput *)output FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription); NSString *str = fourcc_to_nstring(mediaSubType); - if (str == fmt) { - count += 1; + if ([str isEqualToString:fmt]) { + count++; } } return count; @@ -568,7 +528,7 @@ - (void)captureOutput:(AVCaptureOutput *)output int GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) { - AVCaptureDevice *device = get_device_by_name(_this->dev_name); + AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name); if (device) { NSString *fmt = sdlformat_to_nsfourcc(format); int count = 0; @@ -579,25 +539,24 @@ - (void)captureOutput:(AVCaptureOutput *)output FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription); NSString *str = fourcc_to_nstring(mediaSubType); - if (str == fmt) { + if ([str isEqualToString:fmt]) { if (index == count) { CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDescription); *width = dim.width; *height = dim.height; return 0; } - count += 1; + count++; } } } return -1; } -int -GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) +int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) { int index = instance_id - 1; - NSArray *devices = discover_devices(); + NSArray *devices = DiscoverCameraDevices(); if (index < [devices count]) { AVCaptureDevice *device = devices[index]; NSString *cameraID = [device localizedName]; @@ -608,32 +567,28 @@ - (void)captureOutput:(AVCaptureOutput *)output return -1; } -static int -GetNumDevices(void) +static int GetNumDevices(void) { - NSArray *devices = discover_devices(); + NSArray *devices = DiscoverCameraDevices(); return [devices count]; } SDL_CameraDeviceID *GetCameraDevices(int *count) { - /* hard-coded list of ID */ - int i; - int num = GetNumDevices(); - SDL_CameraDeviceID *ret; - - ret = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); + // hard-coded list of ID + const int num = GetNumDevices(); + SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_calloc((num + 1), sizeof(*ret)); - if (ret == NULL) { + if (retval == NULL) { SDL_OutOfMemory(); *count = 0; return NULL; } - for (i = 0; i < num; i++) { - ret[i] = i + 1; + for (int i = 0; i < num; i++) { + retval[i] = i + 1; } - ret[num] = 0; + retval[num] = 0; *count = num; return ret; } @@ -648,7 +603,7 @@ int SDL_SYS_CameraQuit(void) return 0; } -#endif /* HAVE_COREMEDIA */ +#endif // HAVE_COREMEDIA -#endif /* SDL_CAMERA_APPLE */ +#endif // SDL_CAMERA_APPLE diff --git a/src/camera/v4l2/SDL_camera_v4l2.c b/src/camera/v4l2/SDL_camera_v4l2.c index a5440ca4b8316..223d8a053cf78 100644 --- a/src/camera/v4l2/SDL_camera_v4l2.c +++ b/src/camera/v4l2/SDL_camera_v4l2.c @@ -28,27 +28,25 @@ #include "../../thread/SDL_systhread.h" #include "../../core/linux/SDL_evdev_capabilities.h" #include "../../core/linux/SDL_udev.h" -#include /* INT_MAX */ +#include // INT_MAX #define DEBUG_CAMERA 1 -#define MAX_CAMERA_DEVICES 128 /* It's doubtful someone has more than that */ +#define MAX_CAMERA_DEVICES 128 // It's doubtful someone has more than that static int MaybeAddDevice(const char *path); #ifdef SDL_USE_LIBUDEV static int MaybeRemoveDevice(const char *path); -static void camera_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath); -#endif /* SDL_USE_LIBUDEV */ +static void CameraUdevCallback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath); +#endif // SDL_USE_LIBUDEV -/* - * List of available camera devices. - */ +// List of available camera devices. typedef struct SDL_cameralist_item { - char *fname; /* Dev path name (like /dev/video0) */ - char *bus_info; /* don't add two paths with same bus_info (eg /dev/video0 and /dev/video1 */ + char *fname; // Dev path name (like /dev/video0) + char *bus_info; // don't add two paths with same bus_info (eg /dev/video0 and /dev/video1 SDL_CameraDeviceID instance_id; - SDL_CameraDevice *device; /* Associated device */ + SDL_CameraDevice *device; // Associated device struct SDL_cameralist_item *next; } SDL_cameralist_item; @@ -67,7 +65,7 @@ enum io_method { struct buffer { void *start; size_t length; - int available; /* Is available in userspace */ + int available; // Is available in userspace }; struct SDL_PrivateCameraData @@ -82,34 +80,30 @@ struct SDL_PrivateCameraData #include #include -#include /* low-level i/o */ +#include // low-level i/o #include #include #include #include -static int -xioctl(int fh, int request, void *arg) +static int xioctl(int fh, int request, void *arg) { int r; do { r = ioctl(fh, request, arg); - } while (r == -1 && errno == EINTR); + } while ((r == -1) && (errno == EINTR)); return r; } -/* -1:error 1:frame 0:no frame*/ -static int -acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +// -1:error 1:frame 0:no frame +static int acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { - struct v4l2_buffer buf; - int i; - - int fd = _this->hidden->fd; + const int fd = _this->hidden->fd; enum io_method io = _this->hidden->io; size_t size = _this->hidden->buffers[0].length; + struct v4l2_buffer buf; switch (io) { case IO_METHOD_READ: @@ -119,9 +113,8 @@ acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) return 0; case EIO: - /* Could ignore EIO, see spec. */ - - /* fall through */ + // Could ignore EIO, see spec. + // fall through default: return SDL_SetError("read"); @@ -145,9 +138,8 @@ acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) return 0; case EIO: - /* Could ignore EIO, see spec. */ - - /* fall through */ + // Could ignore EIO, see spec. + // fall through default: return SDL_SetError("VIDIOC_DQBUF: %d", errno); @@ -180,15 +172,16 @@ acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) return 0; case EIO: - /* Could ignore EIO, see spec. */ + // Could ignore EIO, see spec. - /* fall through */ + // fall through default: return SDL_SetError("VIDIOC_DQBUF"); } } + int i; for (i = 0; i < _this->hidden->nb_buffers; ++i) { if (buf.m.userptr == (unsigned long)_this->hidden->buffers[i].start && buf.length == size) { break; @@ -213,13 +206,12 @@ acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) } -int -ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { struct v4l2_buffer buf; - int i; - int fd = _this->hidden->fd; + const int fd = _this->hidden->fd; enum io_method io = _this->hidden->io; + int i; for (i = 0; i < _this->hidden->nb_buffers; ++i) { if (frame->num_planes && frame->data[0] == _this->hidden->buffers[i].start) { @@ -268,25 +260,23 @@ ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) } -int -AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { fd_set fds; struct timeval tv; - int ret; - int fd = _this->hidden->fd; + const int fd = _this->hidden->fd; FD_ZERO(&fds); FD_SET(fd, &fds); - /* Timeout. */ + // Timeout. tv.tv_sec = 0; tv.tv_usec = 300 * 1000; - ret = select(fd + 1, &fds, NULL, NULL, &tv); + int retval = select(fd + 1, &fds, NULL, NULL, &tv); - if (ret == -1) { + if (retval == -1) { if (errno == EINTR) { #if DEBUG_CAMERA SDL_Log("continue .."); @@ -296,35 +286,34 @@ AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) return SDL_SetError("select"); } - if (ret == 0) { - /* Timeout. Not an error */ + if (retval == 0) { + // Timeout. Not an error SDL_SetError("timeout select"); return 0; } - ret = acquire_frame(_this, frame); - if (ret < 0) { + retval = acquire_frame(_this, frame); + if (retval < 0) { return -1; } - if (ret == 1){ + if (retval == 1){ frame->timestampNS = SDL_GetTicksNS(); - } else if (ret == 0) { + } else if (retval == 0) { #if DEBUG_CAMERA SDL_Log("No frame continue: %s", SDL_GetError()); #endif } - /* EAGAIN - continue select loop. */ + // EAGAIN - continue select loop. return 0; } -int -StopCamera(SDL_CameraDevice *_this) +int StopCamera(SDL_CameraDevice *_this) { enum v4l2_buf_type type; - int fd = _this->hidden->fd; + const int fd = _this->hidden->fd; enum io_method io = _this->hidden->io; switch (io) { @@ -343,18 +332,16 @@ StopCamera(SDL_CameraDevice *_this) return 0; } -static int -enqueue_buffers(SDL_CameraDevice *_this) +static int EnqueueBuffers(SDL_CameraDevice *_this) { - int i; - int fd = _this->hidden->fd; + const int fd = _this->hidden->fd; enum io_method io = _this->hidden->io; switch (io) { case IO_METHOD_READ: break; case IO_METHOD_MMAP: - for (i = 0; i < _this->hidden->nb_buffers; ++i) { + for (int i = 0; i < _this->hidden->nb_buffers; ++i) { if (_this->hidden->buffers[i].available == 0) { struct v4l2_buffer buf; @@ -371,7 +358,7 @@ enqueue_buffers(SDL_CameraDevice *_this) break; case IO_METHOD_USERPTR: - for (i = 0; i < _this->hidden->nb_buffers; ++i) { + for (int i = 0; i < _this->hidden->nb_buffers; ++i) { if (_this->hidden->buffers[i].available == 0) { struct v4l2_buffer buf; @@ -392,11 +379,10 @@ enqueue_buffers(SDL_CameraDevice *_this) return 0; } -static int -pre_enqueue_buffers(SDL_CameraDevice *_this) +static int PreEnqueueBuffers(SDL_CameraDevice *_this) { struct v4l2_requestbuffers req; - int fd = _this->hidden->fd; + const int fd = _this->hidden->fd; enum io_method io = _this->hidden->io; switch (io) { @@ -404,41 +390,37 @@ pre_enqueue_buffers(SDL_CameraDevice *_this) break; case IO_METHOD_MMAP: - { - SDL_zero(req); - req.count = _this->hidden->nb_buffers; - req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req.memory = V4L2_MEMORY_MMAP; - - if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) { - if (errno == EINVAL) { - return SDL_SetError("Does not support memory mapping"); - } else { - return SDL_SetError("VIDIOC_REQBUFS"); - } - } - - if (req.count < 2) { - return SDL_SetError("Insufficient buffer memory"); + SDL_zero(req); + req.count = _this->hidden->nb_buffers; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + + if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) { + if (errno == EINVAL) { + return SDL_SetError("Does not support memory mapping"); + } else { + return SDL_SetError("VIDIOC_REQBUFS"); } + } - _this->hidden->nb_buffers = req.count; + if (req.count < 2) { + return SDL_SetError("Insufficient buffer memory"); } + + _this->hidden->nb_buffers = req.count; break; case IO_METHOD_USERPTR: - { - SDL_zero(req); - req.count = _this->hidden->nb_buffers; - req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req.memory = V4L2_MEMORY_USERPTR; - - if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) { - if (errno == EINVAL) { - return SDL_SetError("Does not support user pointer i/o"); - } else { - return SDL_SetError("VIDIOC_REQBUFS"); - } + SDL_zero(req); + req.count = _this->hidden->nb_buffers; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_USERPTR; + + if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) { + if (errno == EINVAL) { + return SDL_SetError("Does not support user pointer i/o"); + } else { + return SDL_SetError("VIDIOC_REQBUFS"); } } break; @@ -446,34 +428,31 @@ pre_enqueue_buffers(SDL_CameraDevice *_this) return 0; } -int -StartCamera(SDL_CameraDevice *_this) +int StartCamera(SDL_CameraDevice *_this) { enum v4l2_buf_type type; - int fd = _this->hidden->fd; + const int fd = _this->hidden->fd; enum io_method io = _this->hidden->io; if (_this->hidden->first_start == 0) { _this->hidden->first_start = 1; } else { - int old = _this->hidden->nb_buffers; + const int old = _this->hidden->nb_buffers; // TODO mmap; doesn't work with stop->start #if 1 - /* Can change nb_buffers for mmap */ - if (pre_enqueue_buffers(_this) < 0) { - return -1; - } - if (old != _this->hidden->nb_buffers) { - SDL_SetError("different nb of buffers requested"); + // Can change nb_buffers for mmap + if (PreEnqueueBuffers(_this) < 0) { return -1; + } else if (old != _this->hidden->nb_buffers) { + return SDL_SetError("different nb of buffers requested"); } #endif _this->hidden->first_start = 1; } - if (enqueue_buffers(_this) < 0) { + if (EnqueueBuffers(_this) < 0) { return -1; } @@ -493,7 +472,7 @@ StartCamera(SDL_CameraDevice *_this) return 0; } -static int alloc_buffer_read(SDL_CameraDevice *_this, size_t buffer_size) +static int AllocBufferRead(SDL_CameraDevice *_this, size_t buffer_size) { _this->hidden->buffers[0].length = buffer_size; _this->hidden->buffers[0].start = SDL_calloc(1, buffer_size); @@ -505,7 +484,7 @@ static int alloc_buffer_read(SDL_CameraDevice *_this, size_t buffer_size) } static int -alloc_buffer_mmap(SDL_CameraDevice *_this) +AllocBufferMmap(SDL_CameraDevice *_this) { int fd = _this->hidden->fd; int i; @@ -538,7 +517,7 @@ alloc_buffer_mmap(SDL_CameraDevice *_this) } static int -alloc_buffer_userp(SDL_CameraDevice *_this, size_t buffer_size) +AllocBufferUserPtr(SDL_CameraDevice *_this, size_t buffer_size) { int i; for (i = 0; i < _this->hidden->nb_buffers; ++i) { @@ -708,12 +687,12 @@ GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) SDL_zero(fmt); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - /* Preserve original settings as set by v4l2-ctl for example */ + // Preserve original settings as set by v4l2-ctl for example if (xioctl(fd, VIDIOC_G_FMT, &fmt) == -1) { return SDL_SetError("Error VIDIOC_G_FMT"); } - /* Buggy driver paranoia. */ + // Buggy driver paranoia. min = fmt.fmt.pix.width * 2; if (fmt.fmt.pix.bytesperline < min) { fmt.fmt.pix.bytesperline = min; @@ -739,29 +718,29 @@ InitDevice(SDL_CameraDevice *_this) int fd = _this->hidden->fd; enum io_method io = _this->hidden->io; - int ret = -1; + int retval = -1; - /* Select video input, video standard and tune here. */ + // Select video input, video standard and tune here. SDL_zero(cropcap); cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (xioctl(fd, VIDIOC_CROPCAP, &cropcap) == 0) { crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - crop.c = cropcap.defrect; /* reset to default */ + crop.c = cropcap.defrect; // reset to default if (xioctl(fd, VIDIOC_S_CROP, &crop) == -1) { switch (errno) { case EINVAL: - /* Cropping not supported. */ + // Cropping not supported. break; default: - /* Errors ignored. */ + // Errors ignored. break; } } } else { - /* Errors ignored. */ + // Errors ignored. } @@ -790,7 +769,7 @@ InitDevice(SDL_CameraDevice *_this) GetDeviceSpec(_this, &_this->spec); - if (pre_enqueue_buffers(_this) < 0) { + if (PreEnqueueBuffers(_this) < 0) { return -1; } @@ -807,28 +786,23 @@ InitDevice(SDL_CameraDevice *_this) switch (io) { case IO_METHOD_READ: - ret = alloc_buffer_read(_this, size); + retval = AllocBufferRead(_this, size); break; case IO_METHOD_MMAP: - ret = alloc_buffer_mmap(_this); + retval = AllocBufferMmap(_this); break; case IO_METHOD_USERPTR: - ret = alloc_buffer_userp(_this, size); + retval = AllocBufferUserPtr(_this, size); break; } } - if (ret < 0) { - return -1; - } - - return 0; + return (retval < 0) ? -1 : 0; } -void -CloseDevice(SDL_CameraDevice *_this) +void CloseDevice(SDL_CameraDevice *_this) { if (!_this) { return; @@ -836,7 +810,6 @@ CloseDevice(SDL_CameraDevice *_this) if (_this->hidden) { if (_this->hidden->buffers) { - int i; enum io_method io = _this->hidden->io; switch (io) { @@ -845,7 +818,7 @@ CloseDevice(SDL_CameraDevice *_this) break; case IO_METHOD_MMAP: - for (i = 0; i < _this->hidden->nb_buffers; ++i) { + for (int i = 0; i < _this->hidden->nb_buffers; ++i) { if (munmap(_this->hidden->buffers[i].start, _this->hidden->buffers[i].length) == -1) { SDL_SetError("munmap"); } @@ -853,7 +826,7 @@ CloseDevice(SDL_CameraDevice *_this) break; case IO_METHOD_USERPTR: - for (i = 0; i < _this->hidden->nb_buffers; ++i) { + for (int i = 0; i < _this->hidden->nb_buffers; ++i) { SDL_free(_this->hidden->buffers[i].start); } break; @@ -864,7 +837,7 @@ CloseDevice(SDL_CameraDevice *_this) if (_this->hidden->fd != -1) { if (close(_this->hidden->fd)) { - SDL_SetError("close camera device"); + SDL_SetError("close camera device"); // !!! FIXME: we probably won't ever see this error } } SDL_free(_this->hidden); @@ -874,13 +847,12 @@ CloseDevice(SDL_CameraDevice *_this) } -int -OpenDevice(SDL_CameraDevice *_this) +int OpenDevice(SDL_CameraDevice *_this) { struct stat st; struct v4l2_capability cap; - int fd; enum io_method io; + int fd; _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); if (_this->hidden == NULL) { @@ -891,19 +863,11 @@ OpenDevice(SDL_CameraDevice *_this) _this->hidden->fd = -1; if (stat(_this->dev_name, &st) == -1) { - SDL_SetError("Cannot identify '%s': %d, %s", _this->dev_name, errno, strerror(errno)); - return -1; - } - - if (!S_ISCHR(st.st_mode)) { - SDL_SetError("%s is no device", _this->dev_name); - return -1; - } - - fd = open(_this->dev_name, O_RDWR /* required */ | O_NONBLOCK, 0); - if (fd == -1) { - SDL_SetError("Cannot open '%s': %d, %s", _this->dev_name, errno, strerror(errno)); - return -1; + return SDL_SetError("Cannot identify '%s': %d, %s", _this->dev_name, errno, strerror(errno)); + } else if (!S_ISCHR(st.st_mode)) { + return SDL_SetError("%s is no device", _this->dev_name); + } else if ((fd = open(_this->dev_name, O_RDWR /* required */ | O_NONBLOCK, 0)) == -1) { + return SDL_SetError("Cannot open '%s': %d, %s", _this->dev_name, errno, strerror(errno)); } _this->hidden->fd = fd; @@ -914,7 +878,7 @@ OpenDevice(SDL_CameraDevice *_this) if (_this->hidden->io == IO_METHOD_READ) { _this->hidden->nb_buffers = 1; } else { - _this->hidden->nb_buffers = 8; /* Number of image as internal buffer, */ + _this->hidden->nb_buffers = 8; // Number of image as internal buffer, } io = _this->hidden->io; @@ -954,14 +918,10 @@ OpenDevice(SDL_CameraDevice *_this) break; } - - - return 0; } -int -GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) +int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) { SDL_cameralist_item *item; for (item = SDL_cameralist; item; item = item->next) { @@ -971,54 +931,46 @@ GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) } } - /* unknown instance_id */ + // unknown instance_id return -1; } SDL_CameraDeviceID *GetCameraDevices(int *count) { - /* real list of ID */ - int i = 0; - int num = num_cameras; - SDL_CameraDeviceID *ret; - SDL_cameralist_item *item; + // real list of ID + const int num = num_cameras; + SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*retval)); - ret = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); - - if (ret == NULL) { + if (retval == NULL) { SDL_OutOfMemory(); *count = 0; return NULL; } - for (item = SDL_cameralist; item; item = item->next) { - ret[i] = item->instance_id; - i++; + int i = 0; + for (SDL_cameralist_item *item = SDL_cameralist; item; item = item->next) { + retval[i++] = item->instance_id; } - ret[num] = 0; + retval[num] = 0; *count = num; - return ret; + return retval; } -/* - * Initializes the subsystem by finding available devices. - */ +// Initializes the subsystem by finding available devices. int SDL_SYS_CameraInit(void) { const char pattern[] = "/dev/video%d"; char path[PATH_MAX]; - int i, j; /* * Limit amount of checks to MAX_CAMERA_DEVICES since we may or may not have * permission to some or all devices. */ - i = 0; - for (j = 0; j < MAX_CAMERA_DEVICES; ++j) { - (void)SDL_snprintf(path, PATH_MAX, pattern, i++); + for (int i = 0; i < MAX_CAMERA_DEVICES; i++) { + (void)SDL_snprintf(path, PATH_MAX, pattern, i); if (MaybeAddDevice(path) == -2) { break; } @@ -1027,27 +979,22 @@ int SDL_SYS_CameraInit(void) #ifdef SDL_USE_LIBUDEV if (SDL_UDEV_Init() < 0) { return SDL_SetError("Could not initialize UDEV"); - } - - if (SDL_UDEV_AddCallback(camera_udev_callback) < 0) { + } else if (SDL_UDEV_AddCallback(CameraUdevCallback) < 0) { SDL_UDEV_Quit(); return SDL_SetError("Could not setup Video Capture <-> udev callback"); } - /* Force a scan to build the initial device list */ + // Force a scan to build the initial device list SDL_UDEV_Scan(); -#endif /* SDL_USE_LIBUDEV */ +#endif // SDL_USE_LIBUDEV return num_cameras; } - int SDL_SYS_CameraQuit(void) { - SDL_cameralist_item *item; - for (item = SDL_cameralist; item; ) { + for (SDL_cameralist_item *item = SDL_cameralist; item; ) { SDL_cameralist_item *tmp = item->next; - SDL_free(item->fname); SDL_free(item->bus_info); SDL_free(item); @@ -1062,7 +1009,7 @@ int SDL_SYS_CameraQuit(void) } #ifdef SDL_USE_LIBUDEV -static void camera_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath) +static void CameraUdevCallback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath) { if (!devpath || !(udev_class & SDL_UDEV_DEVICE_VIDEO_CAPTURE)) { return; @@ -1081,17 +1028,15 @@ static void camera_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, break; } } -#endif /* SDL_USE_LIBUDEV */ +#endif // SDL_USE_LIBUDEV static SDL_bool DeviceExists(const char *path, const char *bus_info) { - SDL_cameralist_item *item; - - for (item = SDL_cameralist; item; item = item->next) { - /* found same dev name */ + for (SDL_cameralist_item *item = SDL_cameralist; item; item = item->next) { + // found same dev name if (SDL_strcmp(path, item->fname) == 0) { return SDL_TRUE; } - /* found same bus_info */ + // found same bus_info if (SDL_strcmp(bus_info, item->bus_info) == 0) { return SDL_TRUE; } @@ -1113,7 +1058,7 @@ static int MaybeAddDevice(const char *path) fd = open(path, O_RDWR); if (fd < 0) { - return -2; /* stop iterating /dev/video%d */ + return -2; // stop iterating /dev/video%d } err = ioctl(fd, VIDIOC_QUERYCAP, &vcap); close(fd); @@ -1129,7 +1074,7 @@ static int MaybeAddDevice(const char *path) } - /* Add new item */ + // Add new item item = (SDL_cameralist_item *)SDL_calloc(1, sizeof(SDL_cameralist_item)); if (!item) { SDL_free(bus_info); @@ -1157,7 +1102,7 @@ static int MaybeAddDevice(const char *path) ++num_cameras; - /* !!! TODO: Send a add event? */ + // !!! TODO: Send a add event? #if DEBUG_CAMERA SDL_Log("Added video camera ID: %d %s (%s) (total: %d)", item->instance_id, path, bus_info, num_cameras); #endif @@ -1178,7 +1123,7 @@ static int MaybeRemoveDevice(const char *path) } for (item = SDL_cameralist; item; item = item->next) { - /* found it, remove it. */ + // found it, remove it. if (SDL_strcmp(path, item->fname) == 0) { if (prev) { prev->next = item->next; @@ -1190,9 +1135,9 @@ static int MaybeRemoveDevice(const char *path) SDL_cameralist_tail = prev; } - /* Need to decrement the count */ + // Need to decrement the count --num_cameras; - /* !!! TODO: Send a remove event? */ + // !!! TODO: Send a remove event? SDL_free(item->fname); SDL_free(item->bus_info); @@ -1203,6 +1148,7 @@ static int MaybeRemoveDevice(const char *path) } return 0; } -#endif /* SDL_USE_LIBUDEV */ +#endif // SDL_USE_LIBUDEV + +#endif // SDL_CAMERA_V4L2 -#endif /* SDL_CAMERA_V4L2 */ From cb10c80aafb42d1dfb6d8bcc34aa765e83dfe619 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 1 Dec 2023 10:59:13 -0500 Subject: [PATCH 035/220] camera: Reworked to operate with a driver interface, like other subsystems. --- CMakeLists.txt | 28 +- include/SDL3/SDL_camera.h | 65 +++++ include/SDL3/SDL_hints.h | 17 +- include/SDL3/SDL_init.h | 3 +- include/build_config/SDL_build_config.h.cmake | 9 +- .../build_config/SDL_build_config_android.h | 2 +- .../SDL_build_config_emscripten.h | 2 +- include/build_config/SDL_build_config_ios.h | 2 +- include/build_config/SDL_build_config_macos.h | 3 +- .../build_config/SDL_build_config_minimal.h | 2 +- include/build_config/SDL_build_config_ngage.h | 2 +- .../build_config/SDL_build_config_windows.h | 2 +- .../build_config/SDL_build_config_wingdk.h | 2 +- include/build_config/SDL_build_config_winrt.h | 2 +- include/build_config/SDL_build_config_xbox.h | 2 +- src/SDL.c | 37 +++ src/camera/SDL_camera.c | 239 ++++++++++++++---- src/camera/SDL_camera_c.h | 2 +- src/camera/SDL_syscamera.h | 69 +++-- src/camera/android/SDL_camera_android.c | 96 ++++--- .../SDL_camera_coremedia.m} | 130 ++++------ src/camera/v4l2/SDL_camera_v4l2.c | 215 ++++++++-------- src/video/SDL_video.c | 9 - test/testcamera.c | 2 +- test/testcameraminimal.c | 2 +- 25 files changed, 610 insertions(+), 334 deletions(-) rename src/camera/{apple/SDL_camera_apple.m => coremedia/SDL_camera_coremedia.m} (87%) diff --git a/CMakeLists.txt b/CMakeLists.txt index d3b09c6b3fe61..b6eefb29650dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -332,6 +332,7 @@ set_option(SDL_KMSDRM "Use KMS DRM video driver" ${UNIX_SYS}) dep_option(SDL_KMSDRM_SHARED "Dynamically load KMS DRM support" ON "SDL_KMSDRM" OFF) set_option(SDL_OFFSCREEN "Use offscreen video driver" ON) dep_option(SDL_CAMERA "Enable camera support" ON SDL_VIDEO OFF) +set_option(SDL_DUMMYCAMERA "Support the dummy camera driver" ON) option_string(SDL_BACKGROUNDING_SIGNAL "number to use for magic backgrounding signal or 'OFF'" OFF) option_string(SDL_FOREGROUNDING_SIGNAL "number to use for magic foregrounding signal or 'OFF'" OFF) dep_option(SDL_HIDAPI "Enable the HIDAPI subsystem" ON "NOT VISIONOS" OFF) @@ -1172,6 +1173,23 @@ if(SDL_AUDIO) endif() endif() +if(SDL_CAMERA) + # CheckDummyCamera/CheckDiskCamera - valid for all platforms + if(SDL_DUMMYCAMERA) + set(SDL_CAMERA_DRIVER_DUMMY 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/dummy/*.c") + set(HAVE_DUMMYCAMERA TRUE) + set(HAVE_SDL_CAMERA TRUE) + endif() + # !!! FIXME: for later. + #if(SDL_DISKCAMERA) + # set(SDL_CAMERA_DRIVER_DISK 1) + # sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/disk/*.c") + # set(HAVE_DISKCAMERA TRUE) + # set(HAVE_SDL_CAMERA TRUE) + #endif() +endif() + if(UNIX OR APPLE) # Relevant for Unix/Darwin only set(DYNAPI_NEEDS_DLOPEN 1) @@ -1290,7 +1308,7 @@ if(ANDROID) endif() if(SDL_CAMERA) - set(SDL_CAMERA_ANDROID 1) + set(SDL_CAMERA_DRIVER_ANDROID 1) set(HAVE_CAMERA TRUE) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/android/*.c") endif() @@ -1531,7 +1549,7 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) endif() if(SDL_CAMERA AND HAVE_LINUX_VIDEODEV2_H) - set(SDL_CAMERA_V4L2 1) + set(SDL_CAMERA_DRIVER_V4L2 1) set(HAVE_CAMERA TRUE) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/v4l2/*.c") endif() @@ -2035,9 +2053,9 @@ elseif(APPLE) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/file/cocoa/*.m") if(IOS OR TVOS OR MACOSX OR DARWIN) - set(SDL_CAMERA_APPLE TRUE) + set(SDL_CAMERA_DRIVER_COREMEDIA 1) set(HAVE_CAMERA TRUE) - sdl_sources("${SDL3_SOURCE_DIR}/src/camera/apple/SDL_camera_apple.m") + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/coremedia/*.m") endif() if(IOS OR TVOS OR VISIONOS) @@ -2739,7 +2757,7 @@ if(NOT HAVE_SDL_MISC) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/misc/dummy/*.c") endif() if(NOT HAVE_CAMERA) - set(SDL_CAMERA_DUMMY 1) + set(SDL_CAMERA_DRIVER_DUMMY 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/dummy/*.c") endif() diff --git a/include/SDL3/SDL_camera.h b/include/SDL3/SDL_camera.h index c37499c54592d..e2b13cfe82dd4 100644 --- a/include/SDL3/SDL_camera.h +++ b/include/SDL3/SDL_camera.h @@ -108,6 +108,71 @@ typedef struct SDL_CameraFrame } SDL_CameraFrame; +/** + * Use this function to get the number of built-in camera drivers. + * + * This function returns a hardcoded number. This never returns a negative + * value; if there are no drivers compiled into this build of SDL, this + * function returns zero. The presence of a driver in this list does not mean + * it will function, it just means SDL is capable of interacting with that + * interface. For example, a build of SDL might have v4l2 support, but if + * there's no kernel support available, SDL's v4l2 driver would fail if used. + * + * By default, SDL tries all drivers, in its preferred order, until one is + * found to be usable. + * + * \returns the number of built-in camera drivers. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_GetCameraDriver + */ +extern DECLSPEC int SDLCALL SDL_GetNumCameraDrivers(void); + +/** + * Use this function to get the name of a built in camera driver. + * + * The list of camera drivers is given in the order that they are normally + * initialized by default; the drivers that seem more reasonable to choose + * first (as far as the SDL developers believe) are earlier in the list. + * + * The names of drivers are all simple, low-ASCII identifiers, like "v4l2", + * "coremedia" or "android". These never have Unicode characters, and are not + * meant to be proper names. + * + * \param index the index of the camera driver; the value ranges from 0 to + * SDL_GetNumCameraDrivers() - 1 + * \returns the name of the camera driver at the requested index, or NULL if an + * invalid index was specified. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_GetNumCameraDrivers + */ +extern DECLSPEC const char *SDLCALL SDL_GetCameraDriver(int index); + +/** + * Get the name of the current camera driver. + * + * The returned string points to internal static memory and thus never becomes + * invalid, even if you quit the camera subsystem and initialize a new driver + * (although such a case would return a different static string from another + * call to this function, of course). As such, you should not modify or free + * the returned string. + * + * \returns the name of the current camera driver or NULL if no driver has been + * initialized. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC const char *SDLCALL SDL_GetCurrentCameraDriver(void); + /** * Get a list of currently connected camera devices. * diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index acbbd84ea223c..7c30a707dc8a8 100644 --- a/include/SDL3/SDL_hints.h +++ b/include/SDL3/SDL_hints.h @@ -355,6 +355,22 @@ extern "C" { */ #define SDL_HINT_BMP_SAVE_LEGACY_FORMAT "SDL_BMP_SAVE_LEGACY_FORMAT" +/** + * A variable that decides what camera backend to use. + * + * By default, SDL will try all available camera backends in a reasonable + * order until it finds one that can work, but this hint allows the app + * or user to force a specific target, such as "directshow" if, say, you are + * on Windows Media Foundations but want to try DirectShow instead. + * + * The default value is unset, in which case SDL will try to figure out + * the best camera backend on your behalf. This hint needs to be set + * before SDL_Init() is called to be useful. + * + * This hint is available since SDL 3.0.0. + */ +#define SDL_HINT_CAMERA_DRIVER "SDL_CAMERA_DRIVER" + /** * A variable controlling whether DirectInput should be used for controllers * @@ -2478,7 +2494,6 @@ extern "C" { */ #define SDL_HINT_XINPUT_ENABLED "SDL_XINPUT_ENABLED" - /** * An enumeration of hint priorities */ diff --git a/include/SDL3/SDL_init.h b/include/SDL3/SDL_init.h index c208b5f7d3814..8459bf9ebbb2f 100644 --- a/include/SDL3/SDL_init.h +++ b/include/SDL3/SDL_init.h @@ -59,7 +59,8 @@ typedef enum SDL_INIT_HAPTIC = 0x00001000, SDL_INIT_GAMEPAD = 0x00002000, /**< `SDL_INIT_GAMEPAD` implies `SDL_INIT_JOYSTICK` */ SDL_INIT_EVENTS = 0x00004000, - SDL_INIT_SENSOR = 0x00008000 + SDL_INIT_SENSOR = 0x00008000, + SDL_INIT_CAMERA = 0x00010000 /**< `SDL_INIT_CAMERA` implies `SDL_INIT_EVENTS` */ } SDL_InitFlags; /** diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index 076d3120f14ff..7985065dcbfc4 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -467,10 +467,11 @@ #cmakedefine SDL_FILESYSTEM_N3DS @SDL_FILESYSTEM_N3DS@ /* Enable camera subsystem */ -#cmakedefine SDL_CAMERA_DUMMY @SDL_CAMERA_DUMMY@ -#cmakedefine SDL_CAMERA_V4L2 @SDL_CAMERA_V4L2@ -#cmakedefine SDL_CAMERA_APPLE @SDL_CAMERA_APPLE@ -#cmakedefine SDL_CAMERA_ANDROID @SDL_CAMERA_ANDROID@ +#cmakedefine SDL_CAMERA_DRIVER_DUMMY @SDL_CAMERA_DRIVER_DUMMY@ +/* !!! FIXME: for later cmakedefine SDL_CAMERA_DRIVER_DISK @SDL_CAMERA_DRIVER_DISK@ */ +#cmakedefine SDL_CAMERA_DRIVER_V4L2 @SDL_CAMERA_DRIVER_V4L2@ +#cmakedefine SDL_CAMERA_DRIVER_COREMEDIA @SDL_CAMERA_DRIVER_COREMEDIA@ +#cmakedefine SDL_CAMERA_DRIVER_ANDROID @SDL_CAMERA_DRIVER_ANDROID@ /* Enable misc subsystem */ #cmakedefine SDL_MISC_DUMMY @SDL_MISC_DUMMY@ diff --git a/include/build_config/SDL_build_config_android.h b/include/build_config/SDL_build_config_android.h index c2eaf09859212..9ae2ca4d95bbe 100644 --- a/include/build_config/SDL_build_config_android.h +++ b/include/build_config/SDL_build_config_android.h @@ -191,6 +191,6 @@ #define SDL_FILESYSTEM_ANDROID 1 /* Enable the camera driver */ -#define SDL_CAMERA_ANDROID 1 +#define SDL_CAMERA_DRIVER_ANDROID 1 #endif /* SDL_build_config_android_h_ */ diff --git a/include/build_config/SDL_build_config_emscripten.h b/include/build_config/SDL_build_config_emscripten.h index 3d7836678229e..07a94d615dce4 100644 --- a/include/build_config/SDL_build_config_emscripten.h +++ b/include/build_config/SDL_build_config_emscripten.h @@ -210,6 +210,6 @@ #define SDL_FILESYSTEM_EMSCRIPTEN 1 /* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */ -#define SDL_CAMERA_DUMMY 1 +#define SDL_CAMERA_DRIVER_DUMMY 1 #endif /* SDL_build_config_emscripten_h */ diff --git a/include/build_config/SDL_build_config_ios.h b/include/build_config/SDL_build_config_ios.h index a356f8920fab4..649ff4e86690d 100644 --- a/include/build_config/SDL_build_config_ios.h +++ b/include/build_config/SDL_build_config_ios.h @@ -213,6 +213,6 @@ #define SDL_FILESYSTEM_COCOA 1 /* enable camera support */ -#define SDL_CAMERA_APPLE 1 +#define SDL_CAMERA_DRIVER_COREMEDIA 1 #endif /* SDL_build_config_ios_h_ */ diff --git a/include/build_config/SDL_build_config_macos.h b/include/build_config/SDL_build_config_macos.h index d45a3980cd32f..a8bed484d94d3 100644 --- a/include/build_config/SDL_build_config_macos.h +++ b/include/build_config/SDL_build_config_macos.h @@ -270,7 +270,8 @@ #define SDL_FILESYSTEM_COCOA 1 /* enable camera support */ -#define SDL_CAMERA_APPLE 1 +#define SDL_CAMERA_DRIVER_COREMEDIA 1 +#define SDL_CAMERA_DRIVER_DUMMY 1 /* Enable assembly routines */ #ifdef __ppc__ diff --git a/include/build_config/SDL_build_config_minimal.h b/include/build_config/SDL_build_config_minimal.h index 38e870160a77b..06d02557ea223 100644 --- a/include/build_config/SDL_build_config_minimal.h +++ b/include/build_config/SDL_build_config_minimal.h @@ -90,6 +90,6 @@ typedef unsigned int uintptr_t; #define SDL_FILESYSTEM_DUMMY 1 /* Enable the camera driver (src/camera/dummy/\*.c) */ -#define SDL_CAMERA_DUMMY 1 +#define SDL_CAMERA_DRIVER_DUMMY 1 #endif /* SDL_build_config_minimal_h_ */ diff --git a/include/build_config/SDL_build_config_ngage.h b/include/build_config/SDL_build_config_ngage.h index a8719bd90905e..3449627b02e8a 100644 --- a/include/build_config/SDL_build_config_ngage.h +++ b/include/build_config/SDL_build_config_ngage.h @@ -87,6 +87,6 @@ typedef unsigned long uintptr_t; #define SDL_FILESYSTEM_DUMMY 1 /* Enable the camera driver (src/camera/dummy/\*.c) */ -#define SDL_CAMERA_DUMMY 1 +#define SDL_CAMERA_DRIVER_DUMMY 1 #endif /* SDL_build_config_ngage_h_ */ diff --git a/include/build_config/SDL_build_config_windows.h b/include/build_config/SDL_build_config_windows.h index 42682d8fcf3c5..066888fbd7bc7 100644 --- a/include/build_config/SDL_build_config_windows.h +++ b/include/build_config/SDL_build_config_windows.h @@ -312,6 +312,6 @@ typedef unsigned int uintptr_t; #define SDL_FILESYSTEM_WINDOWS 1 /* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */ -#define SDL_CAMERA_DUMMY 1 +#define SDL_CAMERA_DRIVER_DUMMY 1 #endif /* SDL_build_config_windows_h_ */ diff --git a/include/build_config/SDL_build_config_wingdk.h b/include/build_config/SDL_build_config_wingdk.h index 0c60bea6f32ba..a636694a0e775 100644 --- a/include/build_config/SDL_build_config_wingdk.h +++ b/include/build_config/SDL_build_config_wingdk.h @@ -248,7 +248,7 @@ #define SDL_FILESYSTEM_WINDOWS 1 /* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */ -#define SDL_CAMERA_DUMMY 1 +#define SDL_CAMERA_DRIVER_DUMMY 1 /* Use the (inferior) GDK text input method for GDK platforms */ /*#define SDL_GDK_TEXTINPUT 1*/ diff --git a/include/build_config/SDL_build_config_winrt.h b/include/build_config/SDL_build_config_winrt.h index 4b3f865c48519..07c656ef34e6c 100644 --- a/include/build_config/SDL_build_config_winrt.h +++ b/include/build_config/SDL_build_config_winrt.h @@ -216,6 +216,6 @@ #define SDL_POWER_WINRT 1 /* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */ -#define SDL_CAMERA_DUMMY 1 +#define SDL_CAMERA_DRIVER_DUMMY 1 #endif /* SDL_build_config_winrt_h_ */ diff --git a/include/build_config/SDL_build_config_xbox.h b/include/build_config/SDL_build_config_xbox.h index 11116f9a60274..ea050da7d2831 100644 --- a/include/build_config/SDL_build_config_xbox.h +++ b/include/build_config/SDL_build_config_xbox.h @@ -237,6 +237,6 @@ #define SDL_GDK_TEXTINPUT 1 /* Enable the camera driver (src/camera/dummy/\*.c) */ -#define SDL_CAMERA_DUMMY 1 +#define SDL_CAMERA_DRIVER_DUMMY 1 #endif /* SDL_build_config_wingdk_h_ */ diff --git a/src/SDL.c b/src/SDL.c index e5c43fee134b0..1553f80bcfbff 100644 --- a/src/SDL.c +++ b/src/SDL.c @@ -46,6 +46,7 @@ #include "joystick/SDL_gamepad_c.h" #include "joystick/SDL_joystick_c.h" #include "sensor/SDL_sensor_c.h" +#include "camera/SDL_camera_c.h" #define SDL_INIT_EVERYTHING ~0U @@ -365,6 +366,30 @@ int SDL_InitSubSystem(Uint32 flags) #endif } + /* Initialize the camera subsystem */ + if (flags & SDL_INIT_CAMERA) { +#ifndef SDL_CAMERA_DISABLED + if (SDL_ShouldInitSubsystem(SDL_INIT_CAMERA)) { + /* camera implies events */ + if (!SDL_InitOrIncrementSubsystem(SDL_INIT_EVENTS)) { + goto quit_and_error; + } + + SDL_IncrementSubsystemRefCount(SDL_INIT_CAMERA); + if (SDL_CameraInit(NULL) < 0) { + SDL_DecrementSubsystemRefCount(SDL_INIT_CAMERA); + goto quit_and_error; + } + } else { + SDL_IncrementSubsystemRefCount(SDL_INIT_CAMERA); + } + flags_initialized |= SDL_INIT_CAMERA; +#else + SDL_SetError("SDL not built with camera support"); + goto quit_and_error; +#endif + } + (void)flags_initialized; /* make static analysis happy, since this only gets used in error cases. */ return 0; @@ -382,6 +407,18 @@ int SDL_Init(Uint32 flags) void SDL_QuitSubSystem(Uint32 flags) { /* Shut down requested initialized subsystems */ + +#ifndef SDL_CAMERA_DISABLED + if (flags & SDL_INIT_CAMERA) { + if (SDL_ShouldQuitSubsystem(SDL_INIT_CAMERA)) { + SDL_QuitCamera(); + /* camera implies events */ + SDL_QuitSubSystem(SDL_INIT_EVENTS); + } + SDL_DecrementSubsystemRefCount(SDL_INIT_CAMERA); + } +#endif + #ifndef SDL_SENSOR_DISABLED if (flags & SDL_INIT_SENSOR) { if (SDL_ShouldQuitSubsystem(SDL_INIT_SENSOR)) { diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index d62854c9c3528..5a8c08f8c31df 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -25,7 +25,25 @@ #include "../video/SDL_pixels_c.h" #include "../thread/SDL_systhread.h" -#define DEBUG_CAMERA 1 +// Available camera drivers +static const CameraBootStrap *const bootstrap[] = { +#ifdef SDL_CAMERA_DRIVER_V4L2 + &V4L2_bootstrap, +#endif +#ifdef SDL_CAMERA_DRIVER_COREMEDIA + &COREMEDIA_bootstrap, +#endif +#ifdef SDL_CAMERA_DRIVER_ANDROID + &ANDROIDCAMERA_bootstrap, +#endif +#ifdef SDL_CAMERA_DRIVER_DUMMY + &DUMMYCAMERA_bootstrap, +#endif + NULL +}; + +static SDL_CameraDriver camera_driver; + // list node entries to share frames between SDL and user app // !!! FIXME: do we need this struct? @@ -36,6 +54,25 @@ typedef struct entry_t static SDL_CameraDevice *open_devices[16]; // !!! FIXME: remove limit +int SDL_GetNumCameraDrivers(void) +{ + return SDL_arraysize(bootstrap) - 1; +} + +const char *SDL_GetCameraDriver(int index) +{ + if (index >= 0 && index < SDL_GetNumCameraDrivers()) { + return bootstrap[index]->name; + } + return NULL; +} + +const char *SDL_GetCurrentCameraDriver(void) +{ + return camera_driver.name; +} + + static void CloseCameraDevice(SDL_CameraDevice *device) { if (!device) { @@ -69,46 +106,18 @@ static void CloseCameraDevice(SDL_CameraDevice *device) SDL_CameraFrame f = entry->frame; // Release frames not acquired, if any if (f.timestampNS) { - ReleaseFrame(device, &f); + camera_driver.impl.ReleaseFrame(device, &f); } SDL_free(entry); } } - CloseDevice(device); + camera_driver.impl.CloseDevice(device); SDL_free(device->dev_name); SDL_free(device); } -// Tell if all devices are closed -SDL_bool CheckAllDeviceClosed(void) -{ - const int n = SDL_arraysize(open_devices); - int all_closed = SDL_TRUE; - for (int i = 0; i < n; i++) { - if (open_devices[i]) { - all_closed = SDL_FALSE; - break; - } - } - return all_closed; -} - -// Tell if at least one device is in playing state -SDL_bool CheckDevicePlaying(void) -{ - const int n = SDL_arraysize(open_devices); - for (int i = 0; i < n; i++) { - if (open_devices[i]) { - if (SDL_GetCameraStatus(open_devices[i]) == SDL_CAMERA_PLAYING) { - return SDL_TRUE; - } - } - } - return SDL_FALSE; -} - void SDL_CloseCamera(SDL_CameraDevice *device) { if (!device) { @@ -128,7 +137,7 @@ int SDL_StartCamera(SDL_CameraDevice *device) return SDL_SetError("invalid state"); } - const int result = StartCamera(device); + const int result = camera_driver.impl.StartCamera(device); if (result < 0) { return result; } @@ -147,7 +156,7 @@ int SDL_GetCameraSpec(SDL_CameraDevice *device, SDL_CameraSpec *spec) } SDL_zerop(spec); - return GetDeviceSpec(device, spec); + return camera_driver.impl.GetDeviceSpec(device, spec); } int SDL_StopCamera(SDL_CameraDevice *device) @@ -162,7 +171,7 @@ int SDL_StopCamera(SDL_CameraDevice *device) SDL_AtomicSet(&device->shutdown, 1); SDL_LockMutex(device->acquiring_lock); - const int retval = StopCamera(device); + const int retval = camera_driver.impl.StopCamera(device); SDL_UnlockMutex(device->acquiring_lock); return (retval < 0) ? -1 : 0; @@ -254,14 +263,13 @@ const char *SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id) return NULL; } - if (GetCameraDeviceName(instance_id, buf, sizeof (buf)) < 0) { + if (camera_driver.impl.GetDeviceName(instance_id, buf, sizeof (buf)) < 0) { buf[0] = 0; } return buf; } - SDL_CameraDeviceID *SDL_GetCameraDevices(int *count) { int dummycount = 0; @@ -270,7 +278,7 @@ SDL_CameraDeviceID *SDL_GetCameraDevices(int *count) } int num = 0; - SDL_CameraDeviceID *retval = GetCameraDevices(&num); + SDL_CameraDeviceID *retval = camera_driver.impl.GetDevices(&num); if (retval) { *count = num; return retval; @@ -279,7 +287,6 @@ SDL_CameraDeviceID *SDL_GetCameraDevices(int *count) // return list of 0 ID, null terminated retval = (SDL_CameraDeviceID *)SDL_calloc(1, sizeof(*retval)); if (retval == NULL) { - SDL_OutOfMemory(); *count = 0; return NULL; } @@ -331,7 +338,7 @@ static int SDLCALL SDL_CameraThread(void *devicep) SDL_zero(f); SDL_LockMutex(device->acquiring_lock); - ret = AcquireFrame(device, &f); + ret = camera_driver.impl.AcquireFrame(device, &f); SDL_UnlockMutex(device->acquiring_lock); if (ret == 0) { @@ -376,7 +383,6 @@ static int SDLCALL SDL_CameraThread(void *devicep) SDL_Log("dev[%p] End thread 'SDL_CameraThread' with error: %s", (void *)device, SDL_GetError()); #endif SDL_AtomicSet(&device->shutdown, 1); - SDL_OutOfMemory(); // !!! FIXME: this error isn't accessible since the thread is about to terminate return 0; } @@ -436,7 +442,6 @@ SDL_CameraDevice *SDL_OpenCamera(SDL_CameraDeviceID instance_id) device = (SDL_CameraDevice *) SDL_calloc(1, sizeof (SDL_CameraDevice)); if (device == NULL) { - SDL_OutOfMemory(); goto error; } device->dev_name = SDL_strdup(device_name); @@ -456,7 +461,7 @@ SDL_CameraDevice *SDL_OpenCamera(SDL_CameraDeviceID instance_id) goto error; } - if (OpenDevice(device) < 0) { + if (camera_driver.impl.OpenDevice(device) < 0) { goto error; } @@ -515,7 +520,7 @@ int SDL_SetCameraSpec(SDL_CameraDevice *device, const SDL_CameraSpec *desired, S device->spec = *obtained; - result = InitDevice(device); + result = camera_driver.impl.InitDevice(device); if (result < 0) { return result; } @@ -541,7 +546,7 @@ int SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame) int ret; // Wait for a frame - while ((ret = AcquireFrame(device, frame)) == 0) { + while ((ret = camera_driver.impl.AcquireFrame(device, frame)) == 0) { if (frame->num_planes) { return 0; } @@ -576,7 +581,7 @@ int SDL_ReleaseCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame) return SDL_InvalidParamError("device"); } else if (frame == NULL) { return SDL_InvalidParamError("frame"); - } else if (ReleaseFrame(device, frame) < 0) { + } else if (camera_driver.impl.ReleaseFrame(device, frame) < 0) { return -1; } @@ -589,7 +594,7 @@ int SDL_GetNumCameraFormats(SDL_CameraDevice *device) if (!device) { return SDL_InvalidParamError("device"); } - return GetNumFormats(device); + return camera_driver.impl.GetNumFormats(device); } int SDL_GetCameraFormat(SDL_CameraDevice *device, int index, Uint32 *format) @@ -600,7 +605,7 @@ int SDL_GetCameraFormat(SDL_CameraDevice *device, int index, Uint32 *format) return SDL_InvalidParamError("format"); } *format = 0; - return GetFormat(device, index, format); + return camera_driver.impl.GetFormat(device, index, format); } int SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format) @@ -608,7 +613,7 @@ int SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format) if (!device) { return SDL_InvalidParamError("device"); } - return GetNumFrameSizes(device, format); + return camera_driver.impl.GetNumFrameSizes(device, format); } int SDL_GetCameraFrameSize(SDL_CameraDevice *device, Uint32 format, int index, int *width, int *height) @@ -621,7 +626,7 @@ int SDL_GetCameraFrameSize(SDL_CameraDevice *device, Uint32 format, int index, i return SDL_InvalidParamError("height"); } *width = *height = 0; - return GetFrameSize(device, format, index, width, height); + return camera_driver.impl.GetFrameSize(device, format, index, width, height); } SDL_CameraDevice *SDL_OpenCameraWithSpec(SDL_CameraDeviceID instance_id, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes) @@ -653,15 +658,35 @@ SDL_CameraStatus SDL_GetCameraStatus(SDL_CameraDevice *device) return SDL_CAMERA_INIT; } -int SDL_CameraInit(void) +static void CompleteCameraEntryPoints(void) { - SDL_zeroa(open_devices); - SDL_SYS_CameraInit(); - return 0; + // this doesn't currently fill in stub implementations, it just asserts the backend filled them all in. + #define FILL_STUB(x) SDL_assert(camera_driver.impl.x != NULL) + FILL_STUB(DetectDevices); + FILL_STUB(OpenDevice); + FILL_STUB(CloseDevice); + FILL_STUB(InitDevice); + FILL_STUB(GetDeviceSpec); + FILL_STUB(StartCamera); + FILL_STUB(StopCamera); + FILL_STUB(AcquireFrame); + FILL_STUB(ReleaseFrame); + FILL_STUB(GetNumFormats); + FILL_STUB(GetFormat); + FILL_STUB(GetNumFrameSizes); + FILL_STUB(GetFrameSize); + FILL_STUB(GetDeviceName); + FILL_STUB(GetDevices); + FILL_STUB(Deinitialize); + #undef FILL_STUB } void SDL_QuitCamera(void) { + if (!camera_driver.name) { // not initialized?! + return; + } + const int n = SDL_arraysize(open_devices); for (int i = 0; i < n; i++) { CloseCameraDevice(open_devices[i]); @@ -669,6 +694,112 @@ void SDL_QuitCamera(void) SDL_zeroa(open_devices); - SDL_SYS_CameraQuit(); +#if 0 // !!! FIXME + SDL_PendingCameraDeviceEvent *pending_events = camera_driver.pending_events.next; + camera_driver.pending_events.next = NULL; + + SDL_PendingCameraDeviceEvent *pending_next = NULL; + for (SDL_PendingCameraDeviceEvent *i = pending_events; i; i = pending_next) { + pending_next = i->next; + SDL_free(i); + } +#endif + + // Free the driver data + camera_driver.impl.Deinitialize(); + + SDL_zero(camera_driver); +} + +// this is 90% the same code as the audio subsystem uses. +int SDL_CameraInit(const char *driver_name) +{ + if (SDL_GetCurrentCameraDriver()) { + SDL_QuitCamera(); // shutdown driver if already running. + } + + SDL_zeroa(open_devices); + + // Select the proper camera driver + if (!driver_name) { + driver_name = SDL_GetHint(SDL_HINT_CAMERA_DRIVER); + } + + SDL_bool initialized = SDL_FALSE; + SDL_bool tried_to_init = SDL_FALSE; + + if (driver_name && (*driver_name != 0)) { + char *driver_name_copy = SDL_strdup(driver_name); + const char *driver_attempt = driver_name_copy; + + if (!driver_name_copy) { + return -1; + } + + while (driver_attempt && (*driver_attempt != 0) && !initialized) { + char *driver_attempt_end = SDL_strchr(driver_attempt, ','); + if (driver_attempt_end) { + *driver_attempt_end = '\0'; + } + + for (int i = 0; bootstrap[i]; i++) { + if (SDL_strcasecmp(bootstrap[i]->name, driver_attempt) == 0) { + tried_to_init = SDL_TRUE; + SDL_zero(camera_driver); + #if 0 // !!! FIXME + camera_driver.pending_events_tail = &camera_driver.pending_events; + #endif + if (bootstrap[i]->init(&camera_driver.impl)) { + camera_driver.name = bootstrap[i]->name; + camera_driver.desc = bootstrap[i]->desc; + initialized = SDL_TRUE; + } + break; + } + } + + driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL; + } + + SDL_free(driver_name_copy); + } else { + for (int i = 0; !initialized && bootstrap[i]; i++) { + if (bootstrap[i]->demand_only) { + continue; + } + + tried_to_init = SDL_TRUE; + SDL_zero(camera_driver); + #if 0 // !!! FIXME + camera_driver.pending_events_tail = &camera_driver.pending_events; + #endif + if (bootstrap[i]->init(&camera_driver.impl)) { + camera_driver.name = bootstrap[i]->name; + camera_driver.desc = bootstrap[i]->desc; + initialized = SDL_TRUE; + } + } + } + + if (!initialized) { + // specific drivers will set the error message if they fail, but otherwise we do it here. + if (!tried_to_init) { + if (driver_name) { + SDL_SetError("Camera driver '%s' not available", driver_name); + } else { + SDL_SetError("No available camera driver"); + } + } + + SDL_zero(camera_driver); + return -1; // No driver was available, so fail. + } + + CompleteCameraEntryPoints(); + + // Make sure we have a list of devices available at startup... + camera_driver.impl.DetectDevices(); + + return 0; } diff --git a/src/camera/SDL_camera_c.h b/src/camera/SDL_camera_c.h index 787b5f2db5fce..56921ab8a0b25 100644 --- a/src/camera/SDL_camera_c.h +++ b/src/camera/SDL_camera_c.h @@ -24,7 +24,7 @@ #define SDL_camera_c_h_ // Initialize the camera subsystem -int SDL_CameraInit(void); +int SDL_CameraInit(const char *driver_name); // Shutdown the camera subsystem void SDL_QuitCamera(void); diff --git a/src/camera/SDL_syscamera.h b/src/camera/SDL_syscamera.h index 990272f8d0a82..6a7c56356760b 100644 --- a/src/camera/SDL_syscamera.h +++ b/src/camera/SDL_syscamera.h @@ -25,6 +25,8 @@ #include "../SDL_list.h" +#define DEBUG_CAMERA 1 + // The SDL camera driver typedef struct SDL_CameraDevice SDL_CameraDevice; @@ -57,34 +59,45 @@ struct SDL_CameraDevice struct SDL_PrivateCameraData *hidden; }; -extern int SDL_SYS_CameraInit(void); -extern int SDL_SYS_CameraQuit(void); - -// !!! FIXME: These names need to be made camera-specific. - -extern int OpenDevice(SDL_CameraDevice *_this); -extern void CloseDevice(SDL_CameraDevice *_this); - -extern int InitDevice(SDL_CameraDevice *_this); - -extern int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec); - -extern int StartCamera(SDL_CameraDevice *_this); -extern int StopCamera(SDL_CameraDevice *_this); - -extern int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame); -extern int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame); - -extern int GetNumFormats(SDL_CameraDevice *_this); -extern int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format); - -extern int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format); -extern int GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height); - -extern int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size); -extern SDL_CameraDeviceID *GetCameraDevices(int *count); +typedef struct SDL_CameraDriverImpl +{ + void (*DetectDevices)(void); + int (*OpenDevice)(SDL_CameraDevice *_this); + void (*CloseDevice)(SDL_CameraDevice *_this); + int (*InitDevice)(SDL_CameraDevice *_this); + int (*GetDeviceSpec)(SDL_CameraDevice *_this, SDL_CameraSpec *spec); + int (*StartCamera)(SDL_CameraDevice *_this); + int (*StopCamera)(SDL_CameraDevice *_this); + int (*AcquireFrame)(SDL_CameraDevice *_this, SDL_CameraFrame *frame); + int (*ReleaseFrame)(SDL_CameraDevice *_this, SDL_CameraFrame *frame); + int (*GetNumFormats)(SDL_CameraDevice *_this); + int (*GetFormat)(SDL_CameraDevice *_this, int index, Uint32 *format); + int (*GetNumFrameSizes)(SDL_CameraDevice *_this, Uint32 format); + int (*GetFrameSize)(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height); + int (*GetDeviceName)(SDL_CameraDeviceID instance_id, char *buf, int size); + SDL_CameraDeviceID *(*GetDevices)(int *count); + void (*Deinitialize)(void); +} SDL_CameraDriverImpl; + +typedef struct SDL_CameraDriver +{ + const char *name; // The name of this camera driver + const char *desc; // The description of this camera driver + SDL_CameraDriverImpl impl; // the backend's interface +} SDL_CameraDriver; -extern SDL_bool CheckAllDeviceClosed(void); -extern SDL_bool CheckDevicePlaying(void); +typedef struct CameraBootStrap +{ + const char *name; + const char *desc; + SDL_bool (*init)(SDL_CameraDriverImpl *impl); + SDL_bool demand_only; // if SDL_TRUE: request explicitly, or it won't be available. +} CameraBootStrap; + +// Not all of these are available in a given build. Use #ifdefs, etc. +extern CameraBootStrap DUMMYCAMERA_bootstrap; +extern CameraBootStrap V4L2_bootstrap; +extern CameraBootStrap COREMEDIA_bootstrap; +extern CameraBootStrap ANDROIDCAMERA_bootstrap; #endif // SDL_syscamera_h_ diff --git a/src/camera/android/SDL_camera_android.c b/src/camera/android/SDL_camera_android.c index 51d2446b6709a..20aeb06f106bd 100644 --- a/src/camera/android/SDL_camera_android.c +++ b/src/camera/android/SDL_camera_android.c @@ -25,9 +25,9 @@ #include "../../video/SDL_pixels_c.h" #include "../../thread/SDL_systhread.h" -#define DEBUG_CAMERA 1 +#if defined(SDL_CAMERA_DRIVER_ANDROID) -#if defined(SDL_CAMERA_ANDROID) && __ANDROID_API__ >= 24 +#if __ANDROID_API__ >= 24 /* * APP_PLATFORM=android-24 @@ -62,7 +62,7 @@ static ACameraManager *cameraMgr = NULL; static ACameraIdList *cameraIdList = NULL; -static void create_cameraMgr(void) +static int CreateCameraManager(void) { if (cameraMgr == NULL) { #if 0 // !!! FIXME: this is getting replaced in a different branch. @@ -78,9 +78,13 @@ static void create_cameraMgr(void) SDL_Log("Create ACameraManager"); } } + + cameraMgr = ACameraManager_create(); + + return cameraMgr ? 0 : SDL_SetError("Error creating ACameraManager"); } -static void delete_cameraMgr(void) +static void DestroyCameraManager(void) { if (cameraIdList) { ACameraManager_deleteCameraIdList(cameraIdList); @@ -215,7 +219,7 @@ static void onActive(void* context, ACameraCaptureSession *session) #endif } -int OpenDevice(SDL_CameraDevice *_this) +static int ANDROIDCAMERA_OpenDevice(SDL_CameraDevice *_this) { /* Cannot open a second camera, while the first one is opened. * If you want to play several camera, they must all be opened first, then played. @@ -231,10 +235,10 @@ int OpenDevice(SDL_CameraDevice *_this) _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); if (_this->hidden == NULL) { - return SDL_OutOfMemory(); + return -1; } - create_cameraMgr(); + CreateCameraManager(); _this->hidden->dev_callbacks.context = (void *) _this; _this->hidden->dev_callbacks.onDisconnected = onDisconnected; @@ -248,7 +252,7 @@ int OpenDevice(SDL_CameraDevice *_this) return 0; } -void CloseDevice(SDL_CameraDevice *_this) +static void ANDROIDCAMERA_CloseDevice(SDL_CameraDevice *_this) { if (_this && _this->hidden) { if (_this->hidden->session) { @@ -271,14 +275,9 @@ void CloseDevice(SDL_CameraDevice *_this) _this->hidden = NULL; } - - // !!! FIXME: just refcount this? - if (CheckAllDeviceClosed()) { - delete_cameraMgr(); - } } -int InitDevice(SDL_CameraDevice *_this) +static int ANDROIDCAMERA_InitDevice(SDL_CameraDevice *_this) { size_t size, pitch; SDL_CalculateSize(_this->spec.format, _this->spec.width, _this->spec.height, &size, &pitch, SDL_FALSE); @@ -286,7 +285,7 @@ int InitDevice(SDL_CameraDevice *_this) return 0; } -int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) +static int ANDROIDCAMERA_GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) { // !!! FIXME: catch NULLs at higher level if (spec) { @@ -296,7 +295,7 @@ int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) return -1; } -int StartCamera(SDL_CameraDevice *_this) +static int ANDROIDCAMERA_StartCamera(SDL_CameraDevice *_this) { // !!! FIXME: maybe log the error code in SDL_SetError camera_status_t res; @@ -377,14 +376,14 @@ int StartCamera(SDL_CameraDevice *_this) return -1; } -int StopCamera(SDL_CameraDevice *_this) +static int ANDROIDCAMERA_StopCamera(SDL_CameraDevice *_this) { ACameraCaptureSession_close(_this->hidden->session); _this->hidden->session = NULL; return 0; } -int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +static int ANDROIDCAMERA_AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { media_status_t res; AImage *image; @@ -435,7 +434,7 @@ int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) return 0; } -int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +static int ANDROIDCAMERA_ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { if (frame->internal){ AImage_delete((AImage *)frame->internal); @@ -443,7 +442,7 @@ int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) return 0; } -int GetNumFormats(SDL_CameraDevice *_this) +static int ANDROIDCAMERA_GetNumFormats(SDL_CameraDevice *_this) { camera_status_t res; SDL_bool unknown = SDL_FALSE; @@ -505,7 +504,7 @@ int GetNumFormats(SDL_CameraDevice *_this) return _this->hidden->num_formats; } -int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) +static int ANDROIDCAMERA_GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) { int i2 = 0; @@ -533,7 +532,7 @@ int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) return 0; } -int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) +static int ANDROIDCAMERA_GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) { // !!! FIXME: call SDL_SetError()? if (_this->hidden->num_formats == 0) { @@ -559,7 +558,7 @@ int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) return -1; } -int GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) +static int ANDROIDCAMERA_GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) { // !!! FIXME: call SDL_SetError()? camera_status_t res; @@ -608,10 +607,10 @@ int GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, return -1; } -static int GetNumDevices(void) +static int ANDROIDCAMERA_GetNumDevices(void) { camera_status_t res; - create_cameraMgr(); + CreateCameraManager(); if (cameraIdList) { ACameraManager_deleteCameraIdList(cameraIdList); @@ -628,11 +627,11 @@ static int GetNumDevices(void) return -1; } -int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) +static int ANDROIDCAMERA_GetDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) { // !!! FIXME: call SDL_SetError()? int index = instance_id - 1; - create_cameraMgr(); + CreateCameraManager(); if (cameraIdList == NULL) { GetNumDevices(); @@ -648,14 +647,13 @@ int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) return -1; } -SDL_CameraDeviceID *GetCameraDevices(int *count) +static SDL_CameraDeviceID *ANDROIDCAMERA_GetDevices(int *count) { // hard-coded list of ID const int num = GetNumDevices(); SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); if (retval == NULL) { - SDL_OutOfMemory(); *count = 0; return NULL; } @@ -668,15 +666,47 @@ SDL_CameraDeviceID *GetCameraDevices(int *count) return retval; } -int SDL_SYS_CameraInit(void) +static void ANDROIDCAMERA_Deinitialize(void) { - return 0; + DestroyCameraManager(); } -int SDL_SYS_CameraQuit(void) +#endif // __ANDROID_API__ >= 24 + + +static SDL_bool ANDROIDCAMERA_Init(SDL_CameraDriverImpl *impl) { - return 0; +#if __ANDROID_API__ < 24 + return SDL_FALSE; +#else + if (CreateCameraManager() < 0) { + return SDL_FALSE; + } + + impl->DetectDevices = ANDROIDCAMERA_DetectDevices; + impl->OpenDevice = ANDROIDCAMERA_OpenDevice; + impl->CloseDevice = ANDROIDCAMERA_CloseDevice; + impl->InitDevice = ANDROIDCAMERA_InitDevice; + impl->GetDeviceSpec = ANDROIDCAMERA_GetDeviceSpec; + impl->StartCamera = ANDROIDCAMERA_StartCamera; + impl->StopCamera = ANDROIDCAMERA_StopCamera; + impl->AcquireFrame = ANDROIDCAMERA_AcquireFrame; + impl->ReleaseFrame = ANDROIDCAMERA_ReleaseFrame; + impl->GetNumFormats = ANDROIDCAMERA_GetNumFormats; + impl->GetFormat = ANDROIDCAMERA_GetFormat; + impl->GetNumFrameSizes = ANDROIDCAMERA_GetNumFrameSizes; + impl->GetFrameSize = ANDROIDCAMERA_GetFrameSize; + impl->GetDeviceName = ANDROIDCAMERA_GetDeviceName; + impl->GetDevices = ANDROIDCAMERA_GetDevices; + impl->Deinitialize = ANDROIDCAMERA_Deinitialize; + + return SDL_TRUE; +#endif } +CameraBootStrap ANDROIDCAMERA_bootstrap = { + "android", "SDL Android camera driver", ANDROIDCAMERA_Init, SDL_FALSE +}; + #endif diff --git a/src/camera/apple/SDL_camera_apple.m b/src/camera/coremedia/SDL_camera_coremedia.m similarity index 87% rename from src/camera/apple/SDL_camera_apple.m rename to src/camera/coremedia/SDL_camera_coremedia.m index ae44fe69cbc54..f5ade8ca0d58c 100644 --- a/src/camera/apple/SDL_camera_apple.m +++ b/src/camera/coremedia/SDL_camera_coremedia.m @@ -20,7 +20,7 @@ */ #include "SDL_internal.h" -#ifdef SDL_CAMERA_APPLE +#ifdef SDL_CAMERA_DRIVER_COREMEDIA #include "../SDL_syscamera.h" #include "../SDL_camera_c.h" @@ -35,59 +35,7 @@ #undef HAVE_COREMEDIA #endif -// !!! FIXME: use the dummy driver -// !!! FIXME: actually, move everything over to backend callbacks instead. -#ifndef HAVE_COREMEDIA -int InitDevice(SDL_CameraDevice *_this) { - return -1; -} -int OpenDevice(SDL_CameraDevice *_this) { - return -1; -} -int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { - return -1; -} -void CloseDevice(SDL_CameraDevice *_this) { -} -int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) { - return -1; -} -int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) { - return -1; -} -int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) { - return -1; -} -int GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) { - return -1; -} -SDL_CameraDeviceID *GetCameraDevices(int *count) { - return NULL; -} -int GetNumFormats(SDL_CameraDevice *_this) { - return 0; -} -int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) { - return 0; -} -int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { - return 0; -} -int StartCapture(SDL_CameraDevice *_this) { - return 0; -} -int StopCapture(SDL_CameraDevice *_this) { - return 0; -} -int SDL_SYS_CameraInit(void) { - return 0; -} -int SDL_SYS_CameraQuit(void) { - return 0; -} - - -#else +#ifdef HAVE_COREMEDIA #import #import @@ -234,16 +182,16 @@ - (void)captureOutput:(AVCaptureOutput *)output } @end -int OpenDevice(SDL_CameraDevice *_this) +static int COREMEDIA_OpenDevice(SDL_CameraDevice *_this) { _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); if (_this->hidden == NULL) { - return SDL_OutOfMemory(); + return -1; } return 0; } -void CloseDevice(SDL_CameraDevice *_this) +static void COREMEDIA_CloseDevice(SDL_CameraDevice *_this) { if (!_this) { return; @@ -271,7 +219,7 @@ void CloseDevice(SDL_CameraDevice *_this) } } -int InitDevice(SDL_CameraDevice *_this) +static int COREMEDIA_InitDevice(SDL_CameraDevice *_this) { // !!! FIXME: autorelease pool? NSString *fmt = sdlformat_to_nsfourcc(_this->spec.format); @@ -381,7 +329,7 @@ int InitDevice(SDL_CameraDevice *_this) return 0; } -int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) +static int COREMEDIA_GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) { // !!! FIXME: make sure higher level checks spec != NULL if (spec) { @@ -391,19 +339,19 @@ int GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) return -1; } -int StartCamera(SDL_CameraDevice *_this) +static int COREMEDIA_StartCamera(SDL_CameraDevice *_this) { [_this->hidden->session startRunning]; return 0; } -int StopCamera(SDL_CameraDevice *_this) +static int COREMEDIA_StopCamera(SDL_CameraDevice *_this) { [_this->hidden->session stopRunning]; return 0; } -int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +static int COREMEDIA_AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { if (CMSimpleQueueGetCount(_this->hidden->frame_queue) > 0) { CMSampleBufferRef sampleBuffer = (CMSampleBufferRef)CMSimpleQueueDequeue(_this->hidden->frame_queue); @@ -444,7 +392,7 @@ int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) return 0; } -int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +static int COREMEDIA_ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { if (frame->internal) { CMSampleBufferRef sampleBuffer = (CMSampleBufferRef) frame->internal; @@ -456,7 +404,7 @@ int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) return 0; } -int GetNumFormats(SDL_CameraDevice *_this) +static int COREMEDIA_GetNumFormats(SDL_CameraDevice *_this) { AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name); if (device) { @@ -476,7 +424,7 @@ int GetNumFormats(SDL_CameraDevice *_this) return 0; } -int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) +static int COREMEDIA_GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) { AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name); if (device) { @@ -503,7 +451,7 @@ int GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) return -1; } -int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) +static int COREMEDIA_GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) { AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name); if (device) { @@ -525,8 +473,7 @@ int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) return 0; } -int -GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) +static int COREMEDIA_GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) { AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name); if (device) { @@ -553,7 +500,7 @@ int GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) return -1; } -int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) +static int COREMEDIA_GetDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) { int index = instance_id - 1; NSArray *devices = DiscoverCameraDevices(); @@ -567,20 +514,19 @@ int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) return -1; } -static int GetNumDevices(void) +static int GetNumCameraDevices(void) { NSArray *devices = DiscoverCameraDevices(); return [devices count]; } -SDL_CameraDeviceID *GetCameraDevices(int *count) +static SDL_CameraDeviceID *COREMEDIA_GetDevices(int *count) { // hard-coded list of ID - const int num = GetNumDevices(); + const int num = GetNumCameraDevices(); SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_calloc((num + 1), sizeof(*ret)); if (retval == NULL) { - SDL_OutOfMemory(); *count = 0; return NULL; } @@ -593,17 +539,45 @@ static int GetNumDevices(void) return ret; } -int SDL_SYS_CameraInit(void) +static void COREMEDIA_DetectDevices(void) { - return 0; } -int SDL_SYS_CameraQuit(void) +static void COREMEDIA_Deinitialize(void) { - return 0; } +static SDL_bool COREMEDIA_Init(SDL_CameraDriverImpl *impl) +{ +#ifndef HAVE_COREMEDIA + return SDL_FALSE; +#else + impl->DetectDevices = COREMEDIA_DetectDevices; + impl->OpenDevice = COREMEDIA_OpenDevice; + impl->CloseDevice = COREMEDIA_CloseDevice; + impl->InitDevice = COREMEDIA_InitDevice; + impl->GetDeviceSpec = COREMEDIA_GetDeviceSpec; + impl->StartCamera = COREMEDIA_StartCamera; + impl->StopCamera = COREMEDIA_StopCamera; + impl->AcquireFrame = COREMEDIA_AcquireFrame; + impl->ReleaseFrame = COREMEDIA_ReleaseFrame; + impl->GetNumFormats = COREMEDIA_GetNumFormats; + impl->GetFormat = COREMEDIA_GetFormat; + impl->GetNumFrameSizes = COREMEDIA_GetNumFrameSizes; + impl->GetFrameSize = COREMEDIA_GetFrameSize; + impl->GetDeviceName = COREMEDIA_GetDeviceName; + impl->GetDevices = COREMEDIA_GetDevices; + impl->Deinitialize = COREMEDIA_Deinitialize; + + return SDL_TRUE; +#endif +} + +CameraBootStrap COREMEDIA_bootstrap = { + "coremedia", "SDL Apple CoreMedia camera driver", COREMEDIA_Init, SDL_FALSE +}; + #endif // HAVE_COREMEDIA -#endif // SDL_CAMERA_APPLE +#endif // SDL_CAMERA_COREMEDIA diff --git a/src/camera/v4l2/SDL_camera_v4l2.c b/src/camera/v4l2/SDL_camera_v4l2.c index 223d8a053cf78..f4f484666eb0b 100644 --- a/src/camera/v4l2/SDL_camera_v4l2.c +++ b/src/camera/v4l2/SDL_camera_v4l2.c @@ -20,7 +20,7 @@ */ #include "SDL_internal.h" -#ifdef SDL_CAMERA_V4L2 +#ifdef SDL_CAMERA_DRIVER_V4L2 #include "../SDL_syscamera.h" #include "../SDL_camera_c.h" @@ -30,8 +30,6 @@ #include "../../core/linux/SDL_udev.h" #include // INT_MAX -#define DEBUG_CAMERA 1 - #define MAX_CAMERA_DEVICES 128 // It's doubtful someone has more than that static int MaybeAddDevice(const char *path); @@ -206,7 +204,7 @@ static int acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) } -int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +static int V4L2_ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { struct v4l2_buffer buf; const int fd = _this->hidden->fd; @@ -259,8 +257,7 @@ int ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) return 0; } - -int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +static int V4L2_AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) { fd_set fds; struct timeval tv; @@ -310,7 +307,7 @@ int AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) } -int StopCamera(SDL_CameraDevice *_this) +static int V4L2_StopCamera(SDL_CameraDevice *_this) { enum v4l2_buf_type type; const int fd = _this->hidden->fd; @@ -428,7 +425,7 @@ static int PreEnqueueBuffers(SDL_CameraDevice *_this) return 0; } -int StartCamera(SDL_CameraDevice *_this) +static int V4L2_StartCamera(SDL_CameraDevice *_this) { enum v4l2_buf_type type; @@ -476,15 +473,10 @@ static int AllocBufferRead(SDL_CameraDevice *_this, size_t buffer_size) { _this->hidden->buffers[0].length = buffer_size; _this->hidden->buffers[0].start = SDL_calloc(1, buffer_size); - - if (!_this->hidden->buffers[0].start) { - return SDL_OutOfMemory(); - } - return 0; + return _this->hidden->buffers[0].start ? 0 : -1; } -static int -AllocBufferMmap(SDL_CameraDevice *_this) +static int AllocBufferMmap(SDL_CameraDevice *_this) { int fd = _this->hidden->fd; int i; @@ -516,8 +508,7 @@ AllocBufferMmap(SDL_CameraDevice *_this) return 0; } -static int -AllocBufferUserPtr(SDL_CameraDevice *_this, size_t buffer_size) +static int AllocBufferUserPtr(SDL_CameraDevice *_this, size_t buffer_size) { int i; for (i = 0; i < _this->hidden->nb_buffers; ++i) { @@ -525,41 +516,38 @@ AllocBufferUserPtr(SDL_CameraDevice *_this, size_t buffer_size) _this->hidden->buffers[i].start = SDL_calloc(1, buffer_size); if (!_this->hidden->buffers[i].start) { - return SDL_OutOfMemory(); + return -1; } } return 0; } -static Uint32 -format_v4l2_2_sdl(Uint32 fmt) +static Uint32 format_v4l2_to_sdl(Uint32 fmt) { switch (fmt) { -#define CASE(x, y) case x: return y + #define CASE(x, y) case x: return y CASE(V4L2_PIX_FMT_YUYV, SDL_PIXELFORMAT_YUY2); CASE(V4L2_PIX_FMT_MJPEG, SDL_PIXELFORMAT_UNKNOWN); -#undef CASE + #undef CASE default: SDL_Log("Unknown format V4L2_PIX_FORMAT '%d'", fmt); return SDL_PIXELFORMAT_UNKNOWN; } } -static Uint32 -format_sdl_2_v4l2(Uint32 fmt) +static Uint32 format_sdl_to_v4l2(Uint32 fmt) { switch (fmt) { -#define CASE(y, x) case x: return y + #define CASE(y, x) case x: return y CASE(V4L2_PIX_FMT_YUYV, SDL_PIXELFORMAT_YUY2); CASE(V4L2_PIX_FMT_MJPEG, SDL_PIXELFORMAT_UNKNOWN); -#undef CASE + #undef CASE default: return 0; } } -int -GetNumFormats(SDL_CameraDevice *_this) +static int V4L2_GetNumFormats(SDL_CameraDevice *_this) { int fd = _this->hidden->fd; int i = 0; @@ -574,8 +562,7 @@ GetNumFormats(SDL_CameraDevice *_this) return i; } -int -GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) +static int V4L2_GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) { int fd = _this->hidden->fd; struct v4l2_fmtdesc fmtdesc; @@ -584,7 +571,7 @@ GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) fmtdesc.index = index; fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) == 0) { - *format = format_v4l2_2_sdl(fmtdesc.pixelformat); + *format = format_v4l2_to_sdl(fmtdesc.pixelformat); #if DEBUG_CAMERA if (fmtdesc.flags & V4L2_FMT_FLAG_EMULATED) { @@ -600,8 +587,7 @@ GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) return -1; } -int -GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) +static int V4L2_GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) { int fd = _this->hidden->fd; int i = 0; @@ -609,7 +595,7 @@ GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) SDL_zero(frmsizeenum); frmsizeenum.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - frmsizeenum.pixel_format = format_sdl_2_v4l2(format); + frmsizeenum.pixel_format = format_sdl_to_v4l2(format); while (ioctl(fd,VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == 0) { frmsizeenum.index++; if (frmsizeenum.type == V4L2_FRMSIZE_TYPE_DISCRETE) { @@ -624,8 +610,7 @@ GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) return i; } -int -GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) +static int V4L2_GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) { int fd = _this->hidden->fd; struct v4l2_frmsizeenum frmsizeenum; @@ -633,7 +618,7 @@ GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int SDL_zero(frmsizeenum); frmsizeenum.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - frmsizeenum.pixel_format = format_sdl_2_v4l2(format); + frmsizeenum.pixel_format = format_sdl_to_v4l2(format); while (ioctl(fd,VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == 0) { frmsizeenum.index++; @@ -663,12 +648,8 @@ GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int return -1; } - - - -#if DEBUG_VIDEO_CAPTURE_CAPTURE -static void -dbg_v4l2_pixelformat(const char *str, int f) { +static void dbg_v4l2_pixelformat(const char *str, int f) +{ SDL_Log("%s V4L2_format=%d %c%c%c%c", str, f, (f >> 0) & 0xff, (f >> 8) & 0xff, @@ -677,8 +658,7 @@ dbg_v4l2_pixelformat(const char *str, int f) { } #endif -int -GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) +static int V4L2_GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) { struct v4l2_format fmt; int fd = _this->hidden->fd; @@ -705,13 +685,12 @@ GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) //spec->width = fmt.fmt.pix.width; //spec->height = fmt.fmt.pix.height; _this->hidden->driver_pitch = fmt.fmt.pix.bytesperline; - //spec->format = format_v4l2_2_sdl(fmt.fmt.pix.pixelformat); + //spec->format = format_v4l2_to_sdl(fmt.fmt.pix.pixelformat); return 0; } -int -InitDevice(SDL_CameraDevice *_this) +static int V4L2_InitDevice(SDL_CameraDevice *_this) { struct v4l2_cropcap cropcap; struct v4l2_crop crop; @@ -753,7 +732,7 @@ InitDevice(SDL_CameraDevice *_this) fmt.fmt.pix.height = _this->spec.height; - fmt.fmt.pix.pixelformat = format_sdl_2_v4l2(_this->spec.format); + fmt.fmt.pix.pixelformat = format_sdl_to_v4l2(_this->spec.format); // fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; fmt.fmt.pix.field = V4L2_FIELD_ANY; @@ -767,7 +746,7 @@ InitDevice(SDL_CameraDevice *_this) } } - GetDeviceSpec(_this, &_this->spec); + V4L2_GetDeviceSpec(_this, &_this->spec); if (PreEnqueueBuffers(_this) < 0) { return -1; @@ -776,7 +755,7 @@ InitDevice(SDL_CameraDevice *_this) { _this->hidden->buffers = SDL_calloc(_this->hidden->nb_buffers, sizeof(*_this->hidden->buffers)); if (!_this->hidden->buffers) { - return SDL_OutOfMemory(); + return -1; } } @@ -802,7 +781,7 @@ InitDevice(SDL_CameraDevice *_this) return (retval < 0) ? -1 : 0; } -void CloseDevice(SDL_CameraDevice *_this) +static void V4L2_CloseDevice(SDL_CameraDevice *_this) { if (!_this) { return; @@ -846,8 +825,7 @@ void CloseDevice(SDL_CameraDevice *_this) } } - -int OpenDevice(SDL_CameraDevice *_this) +static int V4L2_OpenDevice(SDL_CameraDevice *_this) { struct stat st; struct v4l2_capability cap; @@ -856,7 +834,6 @@ int OpenDevice(SDL_CameraDevice *_this) _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); if (_this->hidden == NULL) { - SDL_OutOfMemory(); return -1; } @@ -921,7 +898,7 @@ int OpenDevice(SDL_CameraDevice *_this) return 0; } -int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) +static int V4L2_GetDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) { SDL_cameralist_item *item; for (item = SDL_cameralist; item; item = item->next) { @@ -935,15 +912,13 @@ int GetCameraDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) return -1; } - -SDL_CameraDeviceID *GetCameraDevices(int *count) +static SDL_CameraDeviceID *V4L2_GetDevices(int *count) { // real list of ID const int num = num_cameras; SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*retval)); if (retval == NULL) { - SDL_OutOfMemory(); *count = 0; return NULL; } @@ -959,55 +934,6 @@ SDL_CameraDeviceID *GetCameraDevices(int *count) } -// Initializes the subsystem by finding available devices. -int SDL_SYS_CameraInit(void) -{ - const char pattern[] = "/dev/video%d"; - char path[PATH_MAX]; - - /* - * Limit amount of checks to MAX_CAMERA_DEVICES since we may or may not have - * permission to some or all devices. - */ - for (int i = 0; i < MAX_CAMERA_DEVICES; i++) { - (void)SDL_snprintf(path, PATH_MAX, pattern, i); - if (MaybeAddDevice(path) == -2) { - break; - } - } - -#ifdef SDL_USE_LIBUDEV - if (SDL_UDEV_Init() < 0) { - return SDL_SetError("Could not initialize UDEV"); - } else if (SDL_UDEV_AddCallback(CameraUdevCallback) < 0) { - SDL_UDEV_Quit(); - return SDL_SetError("Could not setup Video Capture <-> udev callback"); - } - - // Force a scan to build the initial device list - SDL_UDEV_Scan(); -#endif // SDL_USE_LIBUDEV - - return num_cameras; -} - -int SDL_SYS_CameraQuit(void) -{ - for (SDL_cameralist_item *item = SDL_cameralist; item; ) { - SDL_cameralist_item *tmp = item->next; - SDL_free(item->fname); - SDL_free(item->bus_info); - SDL_free(item); - item = tmp; - } - - num_cameras = 0; - SDL_cameralist = NULL; - SDL_cameralist_tail = NULL; - - return SDL_FALSE; -} - #ifdef SDL_USE_LIBUDEV static void CameraUdevCallback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath) { @@ -1150,5 +1076,78 @@ static int MaybeRemoveDevice(const char *path) } #endif // SDL_USE_LIBUDEV + +static void V4L2_Deinitialize(void) +{ + for (SDL_cameralist_item *item = SDL_cameralist; item; ) { + SDL_cameralist_item *tmp = item->next; + SDL_free(item->fname); + SDL_free(item->bus_info); + SDL_free(item); + item = tmp; + } + + num_cameras = 0; + SDL_cameralist = NULL; + SDL_cameralist_tail = NULL; +} + +static void V4L2_DetectDevices(void) +{ +} + +static SDL_bool V4L2_Init(SDL_CameraDriverImpl *impl) +{ + // !!! FIXME: move to DetectDevices + const char pattern[] = "/dev/video%d"; + char path[PATH_MAX]; + + /* + * Limit amount of checks to MAX_CAMERA_DEVICES since we may or may not have + * permission to some or all devices. + */ + for (int i = 0; i < MAX_CAMERA_DEVICES; i++) { + (void)SDL_snprintf(path, PATH_MAX, pattern, i); + if (MaybeAddDevice(path) == -2) { + break; + } + } + +#ifdef SDL_USE_LIBUDEV + if (SDL_UDEV_Init() < 0) { + return SDL_SetError("Could not initialize UDEV"); + } else if (SDL_UDEV_AddCallback(CameraUdevCallback) < 0) { + SDL_UDEV_Quit(); + return SDL_SetError("Could not setup Video Capture <-> udev callback"); + } + + // Force a scan to build the initial device list + SDL_UDEV_Scan(); +#endif // SDL_USE_LIBUDEV + + impl->DetectDevices = V4L2_DetectDevices; + impl->OpenDevice = V4L2_OpenDevice; + impl->CloseDevice = V4L2_CloseDevice; + impl->InitDevice = V4L2_InitDevice; + impl->GetDeviceSpec = V4L2_GetDeviceSpec; + impl->StartCamera = V4L2_StartCamera; + impl->StopCamera = V4L2_StopCamera; + impl->AcquireFrame = V4L2_AcquireFrame; + impl->ReleaseFrame = V4L2_ReleaseFrame; + impl->GetNumFormats = V4L2_GetNumFormats; + impl->GetFormat = V4L2_GetFormat; + impl->GetNumFrameSizes = V4L2_GetNumFrameSizes; + impl->GetFrameSize = V4L2_GetFrameSize; + impl->GetDeviceName = V4L2_GetDeviceName; + impl->GetDevices = V4L2_GetDevices; + impl->Deinitialize = V4L2_Deinitialize; + + return SDL_TRUE; +} + +CameraBootStrap V4L2_bootstrap = { + "v4l2", "SDL Video4Linux2 camera driver", V4L2_Init, SDL_FALSE +}; + #endif // SDL_CAMERA_V4L2 diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index beb7e40f8f4a9..cedc7556be499 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -458,7 +458,6 @@ int SDL_VideoInit(const char *driver_name) SDL_bool init_keyboard = SDL_FALSE; SDL_bool init_mouse = SDL_FALSE; SDL_bool init_touch = SDL_FALSE; - SDL_bool init_camera = SDL_FALSE; int i = 0; /* Check to make sure we don't overwrite '_this' */ @@ -485,10 +484,6 @@ int SDL_VideoInit(const char *driver_name) goto pre_driver_error; } init_touch = SDL_TRUE; - if (SDL_CameraInit() < 0) { - goto pre_driver_error; - } - init_camera = SDL_TRUE; /* Select the proper video driver */ video = NULL; @@ -590,9 +585,6 @@ int SDL_VideoInit(const char *driver_name) pre_driver_error: SDL_assert(_this == NULL); - if (init_camera) { - SDL_QuitCamera(); - } if (init_touch) { SDL_QuitTouch(); } @@ -3784,7 +3776,6 @@ void SDL_VideoQuit(void) SDL_ClearClipboardData(); /* Halt event processing before doing anything else */ - SDL_QuitCamera(); SDL_QuitTouch(); SDL_QuitMouse(); SDL_QuitKeyboard(); diff --git a/test/testcamera.c b/test/testcamera.c index 255dcf4ea2e46..f96b728661518 100644 --- a/test/testcamera.c +++ b/test/testcamera.c @@ -213,7 +213,7 @@ int main(int argc, char **argv) SDL_Log("%s", usage); /* Load the SDL library */ - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { /* FIXME: SDL_INIT_JOYSTICK needed for add/removing devices at runtime */ + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError()); return 1; } diff --git a/test/testcameraminimal.c b/test/testcameraminimal.c index 060b8ed50077f..ce2d3ec20ee54 100644 --- a/test/testcameraminimal.c +++ b/test/testcameraminimal.c @@ -54,7 +54,7 @@ int main(int argc, char **argv) SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); /* Load the SDL library */ - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { /* FIXME: SDL_INIT_JOYSTICK needed for add/removing devices at runtime */ + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError()); return 1; } From 8e1758260cca4ad8bff8c1d05e818957602b508c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 15 Dec 2023 10:57:54 -0500 Subject: [PATCH 036/220] surface: Fixed a typo in a comment. --- src/video/SDL_surface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index c4c9073f3a8df..6111dbe4c7637 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -199,7 +199,7 @@ SDL_Surface *SDL_CreateSurface(int width, int height, Uint32 format) } /* - * Create an RGB surface from an existing memory buffer using the given given + * Create an RGB surface from an existing memory buffer using the given * enum SDL_PIXELFORMAT_* format */ SDL_Surface *SDL_CreateSurfaceFrom(void *pixels, int width, int height, int pitch, Uint32 format) From 3d2d5d18f3bd667eb1a87409fa033911c8713e67 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 15 Dec 2023 11:11:24 -0500 Subject: [PATCH 037/220] pixels: Packed-pixel YUV formats can legit report bits-per-pixel. This makes the existing SDL_SoftStretch code work with them, at least for nearest-neighbor scaling; otherwise, it'll mangle the data trying to scale it as 8bpp data without warning. --- src/video/SDL_pixels.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/video/SDL_pixels.c b/src/video/SDL_pixels.c index 41f32e389c826..88b13ccbdf7b0 100644 --- a/src/video/SDL_pixels.c +++ b/src/video/SDL_pixels.c @@ -147,8 +147,17 @@ SDL_bool SDL_GetMasksForPixelFormatEnum(Uint32 format, int *bpp, Uint32 *Rmask, /* Partial support for SDL_Surface with FOURCC */ if (SDL_ISPIXELFORMAT_FOURCC(format)) { /* Not a format that uses masks */ - *bpp = 0; *Rmask = *Gmask = *Bmask = *Amask = 0; + // however, some of these are packed formats, and can legit declare bits-per-pixel! + switch (format) { + case SDL_PIXELFORMAT_YUY2: + case SDL_PIXELFORMAT_UYVY: + case SDL_PIXELFORMAT_YVYU: + *bpp = 32; + break; + default: + *bpp = 0; // oh well. + } return SDL_TRUE; } #else From d3e6ef3cc6e04139204302f07b59a08b3d41cbd1 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 15 Dec 2023 11:45:11 -0500 Subject: [PATCH 038/220] camera: Massive code reworking. - Simplified public API, simplified backend interface. - Camera device hotplug events. - Thread code is split up so it backends that provide own threads can use it. - Added "dummy" backend. Note that CoreMedia (Apple) and Android backends need to be updated, as does the testcamera app (testcameraminimal works). --- include/SDL3/SDL_camera.h | 315 ++--- include/SDL3/SDL_events.h | 17 + src/camera/SDL_camera.c | 1218 ++++++++++++------- src/camera/SDL_camera_c.h | 3 + src/camera/SDL_syscamera.h | 132 +- src/camera/coremedia/SDL_camera_coremedia.m | 15 +- src/camera/dummy/SDL_camera_dummy.c | 80 ++ src/camera/v4l2/SDL_camera_v4l2.c | 1170 +++++++----------- src/dynapi/SDL_dynapi.sym | 17 +- src/dynapi/SDL_dynapi_overrides.h | 17 +- src/dynapi/SDL_dynapi_procs.h | 27 +- src/events/SDL_events.c | 14 + test/testcamera.c | 10 +- test/testcameraminimal.c | 94 +- 14 files changed, 1657 insertions(+), 1472 deletions(-) create mode 100644 src/camera/dummy/SDL_camera_dummy.c diff --git a/include/SDL3/SDL_camera.h b/include/SDL3/SDL_camera.h index e2b13cfe82dd4..eb6a9f2d82950 100644 --- a/include/SDL3/SDL_camera.h +++ b/include/SDL3/SDL_camera.h @@ -49,23 +49,16 @@ typedef Uint32 SDL_CameraDeviceID; /** - * The structure used to identify an SDL camera device + * The structure used to identify an opened SDL camera */ -struct SDL_CameraDevice; -typedef struct SDL_CameraDevice SDL_CameraDevice; - -#define SDL_CAMERA_ALLOW_ANY_CHANGE 1 +struct SDL_Camera; +typedef struct SDL_Camera SDL_Camera; /** * SDL_CameraSpec structure * - * Only those field can be 'desired' when configuring the device: - * - format - * - width - * - height - * - * \sa SDL_GetCameraFormat - * \sa SDL_GetCameraFrameSize + * \sa SDL_GetCameraDeviceSupportedSpecs + * \sa SDL_GetCameraSpec * */ typedef struct SDL_CameraSpec @@ -75,39 +68,6 @@ typedef struct SDL_CameraSpec int height; /**< Frame height */ } SDL_CameraSpec; -/** - * SDL Camera Status - * - * Change states but calling the function in this order: - * - * SDL_OpenCamera() - * SDL_SetCameraSpec() -> Init - * SDL_StartCamera() -> Playing - * SDL_StopCamera() -> Stopped - * SDL_CloseCamera() - * - */ -typedef enum -{ - SDL_CAMERA_FAIL = -1, /**< Failed */ - SDL_CAMERA_INIT = 0, /**< Init, spec hasn't been set */ - SDL_CAMERA_STOPPED, /**< Stopped */ - SDL_CAMERA_PLAYING /**< Playing */ -} SDL_CameraStatus; - -/** - * SDL Video Capture Status - */ -typedef struct SDL_CameraFrame -{ - Uint64 timestampNS; /**< Frame timestamp in nanoseconds when read from the driver */ - int num_planes; /**< Number of planes */ - Uint8 *data[3]; /**< Pointer to data of i-th plane */ - int pitch[3]; /**< Pitch of i-th plane */ - void *internal; /**< Private field */ -} SDL_CameraFrame; - - /** * Use this function to get the number of built-in camera drivers. * @@ -176,11 +136,13 @@ extern DECLSPEC const char *SDLCALL SDL_GetCurrentCameraDriver(void); /** * Get a list of currently connected camera devices. * - * \param count a pointer filled in with the number of camera devices + * \param count a pointer filled in with the number of camera devices. Can be NULL. * \returns a 0 terminated array of camera instance IDs which should be * freed with SDL_free(), or NULL on error; call SDL_GetError() for * more details. * + * \threadsafety It is safe to call this function from any thread. + * * \since This function is available since SDL 3.0.0. * * \sa SDL_OpenCamera @@ -188,237 +150,202 @@ extern DECLSPEC const char *SDLCALL SDL_GetCurrentCameraDriver(void); extern DECLSPEC SDL_CameraDeviceID *SDLCALL SDL_GetCameraDevices(int *count); /** - * Open a Video Capture device + * Get the list of native formats/sizes a camera supports. * - * \param instance_id the camera device instance ID - * \returns device, or NULL on failure; call SDL_GetError() for more - * information. + * This returns a list of all formats and frame sizes that a specific + * camera can offer. This is useful if your app can accept a variety + * of image formats and sizes and so want to find the optimal spec + * that doesn't require conversion. * - * \since This function is available since SDL 3.0.0. + * This function isn't strictly required; if you call SDL_OpenCameraDevice + * with a NULL spec, SDL will choose a native format for you, and if you + * instead specify a desired format, it will transparently convert to the + * requested format on your behalf. * - * \sa SDL_GetCameraDeviceName - * \sa SDL_GetCameraDevices - * \sa SDL_OpenCameraWithSpec - */ -extern DECLSPEC SDL_CameraDevice *SDLCALL SDL_OpenCamera(SDL_CameraDeviceID instance_id); - -/** - * Set specification + * If `count` is not NULL, it will be filled with the number of elements + * in the returned array. Additionally, the last element of the array + * has all fields set to zero (this element is not included in `count`). * - * \param device opened camera device - * \param desired desired camera spec - * \param obtained obtained camera spec - * \param allowed_changes allow changes or not - * \returns 0 on success or a negative error code on failure; call - * SDL_GetError() for more information. + * The returned list is owned by the caller, and should be released with + * SDL_free() when no longer needed. * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_OpenCamera - * \sa SDL_OpenCameraWithSpec - * \sa SDL_GetCameraSpec - */ -extern DECLSPEC int SDLCALL SDL_SetCameraSpec(SDL_CameraDevice *device, - const SDL_CameraSpec *desired, - SDL_CameraSpec *obtained, - int allowed_changes); - -/** - * Open a Video Capture device and set specification + * \param devid the camera device instance ID to query. + * \param count a pointer filled in with the number of elements in the list. Can be NULL. + * \returns a 0 terminated array of SDL_CameraSpecs, which should be + * freed with SDL_free(), or NULL on error; call SDL_GetError() for + * more details. * - * \param instance_id the camera device instance ID - * \param desired desired camera spec - * \param obtained obtained camera spec - * \param allowed_changes allow changes or not - * \returns device, or NULL on failure; call SDL_GetError() for more - * information. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_OpenCamera - * \sa SDL_SetCameraSpec - * \sa SDL_GetCameraSpec + * \sa SDL_GetCameraDevices + * \sa SDL_OpenCameraDevice */ -extern DECLSPEC SDL_CameraDevice *SDLCALL SDL_OpenCameraWithSpec(SDL_CameraDeviceID instance_id, - const SDL_CameraSpec *desired, - SDL_CameraSpec *obtained, - int allowed_changes); +extern DECLSPEC SDL_CameraSpec *SDLCALL SDL_GetCameraDeviceSupportedSpecs(SDL_CameraDeviceID devid, int *count); /** - * Get device name + * Get human-readable device name for a camera. * - * \param instance_id the camera device instance ID - * \returns device name, shouldn't be freed + * The returned string is owned by the caller; please release it with + * SDL_free() when done with it. * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_GetCameraDevices - */ -extern DECLSPEC const char * SDLCALL SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id); - -/** - * Get the obtained camera spec + * \param instance_id the camera device instance ID + * \returns Human-readable device name, or NULL on error; call SDL_GetError() for more information. * - * \param device opened camera device - * \param spec The SDL_CameraSpec to be initialized by this function. - * \returns 0 on success or a negative error code on failure; call - * SDL_GetError() for more information. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_SetCameraSpec - * \sa SDL_OpenCameraWithSpec + * \sa SDL_GetCameraDevices */ -extern DECLSPEC int SDLCALL SDL_GetCameraSpec(SDL_CameraDevice *device, SDL_CameraSpec *spec); - +extern DECLSPEC char * SDLCALL SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id); /** - * Get frame format of camera device. + * Open a video capture device (a "camera"). * - * The value can be used to fill SDL_CameraSpec structure. + * You can open the device with any reasonable spec, and if the hardware can't + * directly support it, it will convert data seamlessly to the requested + * format. This might incur overhead, including scaling of image data. * - * \param device opened camera device - * \param index format between 0 and num -1 - * \param format pointer output format (SDL_PixelFormatEnum) - * \returns 0 on success or a negative error code on failure; call - * SDL_GetError() for more information. + * If you would rather accept whatever format the device offers, you can + * pass a NULL spec here and it will choose one for you (and you can use + * SDL_Surface's conversion/scaling functions directly if necessary). * - * \since This function is available since SDL 3.0.0. + * You can call SDL_GetCameraSpec() to get the actual data format if + * passing a NULL spec here. You can see the exact specs a device can + * support without conversion with SDL_GetCameraSupportedSpecs(). * - * \sa SDL_GetNumCameraFormats - */ -extern DECLSPEC int SDLCALL SDL_GetCameraFormat(SDL_CameraDevice *device, - int index, - Uint32 *format); - -/** - * Number of available formats for the device + * \param instance_id the camera device instance ID + * \param spec The desired format for data the device will provide. Can be NULL. + * \returns device, or NULL on failure; call SDL_GetError() for more + * information. * - * \param device opened camera device - * \returns number of formats or a negative error code on failure; call - * SDL_GetError() for more information. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_GetCameraFormat - * \sa SDL_SetCameraSpec + * \sa SDL_GetCameraDeviceName + * \sa SDL_GetCameraDevices + * \sa SDL_OpenCameraWithSpec */ -extern DECLSPEC int SDLCALL SDL_GetNumCameraFormats(SDL_CameraDevice *device); +extern DECLSPEC SDL_Camera *SDLCALL SDL_OpenCameraDevice(SDL_CameraDeviceID instance_id, const SDL_CameraSpec *spec); /** - * Get frame sizes of the device and the specified input format. + * Get the instance ID of an opened camera. * - * The value can be used to fill SDL_CameraSpec structure. + * \param device an SDL_Camera to query + * \returns the instance ID of the specified camera on success or 0 on + * failure; call SDL_GetError() for more information. * - * \param device opened camera device - * \param format a format that can be used by the device (SDL_PixelFormatEnum) - * \param index framesize between 0 and num -1 - * \param width output width - * \param height output height - * \returns 0 on success or a negative error code on failure; call - * SDL_GetError() for more information. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_GetNumCameraFrameSizes + * \sa SDL_OpenCameraDevice */ -extern DECLSPEC int SDLCALL SDL_GetCameraFrameSize(SDL_CameraDevice *device, Uint32 format, int index, int *width, int *height); +extern DECLSPEC SDL_CameraDeviceID SDLCALL SDL_GetCameraInstanceID(SDL_Camera *camera); /** - * Number of different framesizes available for the device and pixel format. + * Get the properties associated with an opened camera. * - * \param device opened camera device - * \param format frame pixel format (SDL_PixelFormatEnum) - * \returns number of framesizes or a negative error code on failure; call + * \param device the SDL_Camera obtained from SDL_OpenCameraDevice() + * \returns a valid property ID on success or 0 on failure; call * SDL_GetError() for more information. * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_GetCameraFrameSize - * \sa SDL_SetCameraSpec - */ -extern DECLSPEC int SDLCALL SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format); - - -/** - * Get camera status - * - * \param device opened camera device - * \returns 0 on success or a negative error code on failure; call - * SDL_GetError() for more information. + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL 3.0.0. * - * \sa SDL_CameraStatus + * \sa SDL_GetProperty + * \sa SDL_SetProperty */ -extern DECLSPEC SDL_CameraStatus SDLCALL SDL_GetCameraStatus(SDL_CameraDevice *device); +extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetCameraProperties(SDL_Camera *camera); /** - * Start camera + * Get the spec that a camera is using when generating images. + * + * Note that this might not be the native format of the hardware, as SDL + * might be converting to this format behind the scenes. * * \param device opened camera device + * \param spec The SDL_CameraSpec to be initialized by this function. * \returns 0 on success or a negative error code on failure; call * SDL_GetError() for more information. * + * \threadsafety It is safe to call this function from any thread. + * * \since This function is available since SDL 3.0.0. * - * \sa SDL_StopCamera + * \sa SDL_OpenCameraDevice */ -extern DECLSPEC int SDLCALL SDL_StartCamera(SDL_CameraDevice *device); +extern DECLSPEC int SDLCALL SDL_GetCameraSpec(SDL_Camera *camera, SDL_CameraSpec *spec); /** * Acquire a frame. * * The frame is a memory pointer to the image data, whose size and format are - * given by the the obtained spec. + * given by the spec requested when opening the device. + * + * This is a non blocking API. If there is a frame available, a non-NULL surface is + * returned, and timestampNS will be filled with a non-zero value. + * + * Note that an error case can also return NULL, but a NULL by itself is normal + * and just signifies that a new frame is not yet available. Note that even if a + * camera device fails outright (a USB camera is unplugged while in use, etc), SDL + * will send an event separately to notify the app, but continue to provide blank + * frames at ongoing intervals until SDL_CloseCamera() is called, so real + * failure here is almost always an out of memory condition. * - * Non blocking API. If there is a frame available, frame->num_planes is non - * 0. If frame->num_planes is 0 and returned code is 0, there is no frame at - * that time. + * After use, the frame should be released with SDL_ReleaseCameraFrame(). If you + * don't do this, the system may stop providing more video! If the hardware is + * using DMA to write directly into memory, frames held too long may be overwritten + * with new data. * - * After used, the frame should be released with SDL_ReleaseCameraFrame + * Do not call SDL_FreeSurface() on the returned surface! It must be given back + * to the camera subsystem with SDL_ReleaseCameraFrame! * * \param device opened camera device - * \param frame pointer to get the frame - * \returns 0 on success or a negative error code on failure; call - * SDL_GetError() for more information. + * \param timestampNS a pointer filled in with the frame's timestamp, or 0 on error. Can be NULL. + * \returns A new frame of video on success, NULL if none is currently available. + * + * \threadsafety It is safe to call this function from any thread. * * \since This function is available since SDL 3.0.0. * * \sa SDL_ReleaseCameraFrame */ -extern DECLSPEC int SDLCALL SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame); +extern DECLSPEC SDL_Surface * SDLCALL SDL_AcquireCameraFrame(SDL_Camera *camera, Uint64 *timestampNS); /** - * Release a frame. + * Release a frame of video acquired from a camera. * * Let the back-end re-use the internal buffer for camera. * - * All acquired frames should be released before closing the device. + * This function _must_ be called only on surface objects returned by + * SDL_AcquireCameraFrame(). This function should be called as quickly as + * possible after acquisition, as SDL keeps a small FIFO queue of surfaces + * for video frames; if surfaces aren't released in a timely manner, SDL + * may drop upcoming video frames from the camera. * - * \param device opened camera device - * \param frame frame pointer. - * \returns 0 on success or a negative error code on failure; call - * SDL_GetError() for more information. + * If the app needs to keep the surface for a significant time, they should + * make a copy of it and release the original. * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_AcquireCameraFrame - */ -extern DECLSPEC int SDLCALL SDL_ReleaseCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame); - -/** - * Stop Video Capture + * The app should not use the surface again after calling this function; + * assume the surface is freed and the pointer is invalid. * * \param device opened camera device + * \param frame The video frame surface to release. * \returns 0 on success or a negative error code on failure; call * SDL_GetError() for more information. * + * \threadsafety It is safe to call this function from any thread. + * * \since This function is available since SDL 3.0.0. * - * \sa SDL_StartCamera + * \sa SDL_AcquireCameraFrame */ -extern DECLSPEC int SDLCALL SDL_StopCamera(SDL_CameraDevice *device); +extern DECLSPEC int SDLCALL SDL_ReleaseCameraFrame(SDL_Camera *camera, SDL_Surface *frame); /** * Use this function to shut down camera processing and close the @@ -426,12 +353,16 @@ extern DECLSPEC int SDLCALL SDL_StopCamera(SDL_CameraDevice *device); * * \param device opened camera device * + * \threadsafety It is safe to call this function from any thread, but + * no thread may reference `device` once this function + * is called. + * * \since This function is available since SDL 3.0.0. * * \sa SDL_OpenCameraWithSpec * \sa SDL_OpenCamera */ -extern DECLSPEC void SDLCALL SDL_CloseCamera(SDL_CameraDevice *device); +extern DECLSPEC void SDLCALL SDL_CloseCamera(SDL_Camera *camera); /* Ends C function definitions when using C++ */ #ifdef __cplusplus diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index d209801ede518..74a39267c1881 100644 --- a/include/SDL3/SDL_events.h +++ b/include/SDL3/SDL_events.h @@ -205,6 +205,10 @@ typedef enum SDL_EVENT_PEN_BUTTON_DOWN, /**< Pressure-sensitive pen button pressed */ SDL_EVENT_PEN_BUTTON_UP, /**< Pressure-sensitive pen button released */ + /* Camera hotplug events */ + SDL_EVENT_CAMERA_DEVICE_ADDED = 0x1400, /**< A new camera device is available */ + SDL_EVENT_CAMERA_DEVICE_REMOVED, /**< A camera device has been removed. */ + /* Render events */ SDL_EVENT_RENDER_TARGETS_RESET = 0x2000, /**< The render targets have been reset and their contents need to be updated */ SDL_EVENT_RENDER_DEVICE_RESET, /**< The device has been reset and all textures need to be recreated */ @@ -526,6 +530,18 @@ typedef struct SDL_AudioDeviceEvent Uint8 padding3; } SDL_AudioDeviceEvent; +/** + * Camera device event structure (event.cdevice.*) + */ +typedef struct SDL_CameraDeviceEvent +{ + Uint32 type; /**< ::SDL_EVENT_CAMERA_DEVICE_ADDED, or ::SDL_EVENT_CAMERA_DEVICE_REMOVED */ + Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ + SDL_CameraDeviceID which; /**< SDL_CameraDeviceID for the device being added or removed or changing */ + Uint8 padding1; + Uint8 padding2; + Uint8 padding3; +} SDL_CameraDeviceEvent; /** * Touch finger event structure (event.tfinger.*) @@ -699,6 +715,7 @@ typedef union SDL_Event SDL_GamepadTouchpadEvent gtouchpad; /**< Gamepad touchpad event data */ SDL_GamepadSensorEvent gsensor; /**< Gamepad sensor event data */ SDL_AudioDeviceEvent adevice; /**< Audio device event data */ + SDL_CameraDeviceEvent cdevice; /**< Camera device event data */ SDL_SensorEvent sensor; /**< Sensor event data */ SDL_QuitEvent quit; /**< Quit request event data */ SDL_UserEvent user; /**< Custom event data */ diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index 5a8c08f8c31df..d9c581b04a16b 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -25,6 +25,10 @@ #include "../video/SDL_pixels_c.h" #include "../thread/SDL_systhread.h" + +// A lot of this is a simplified version of SDL_audio.c; if fixing stuff here, +// maybe check that file, too. + // Available camera drivers static const CameraBootStrap *const bootstrap[] = { #ifdef SDL_CAMERA_DRIVER_V4L2 @@ -45,15 +49,6 @@ static const CameraBootStrap *const bootstrap[] = { static SDL_CameraDriver camera_driver; -// list node entries to share frames between SDL and user app -// !!! FIXME: do we need this struct? -typedef struct entry_t -{ - SDL_CameraFrame frame; -} entry_t; - -static SDL_CameraDevice *open_devices[16]; // !!! FIXME: remove limit - int SDL_GetNumCameraDrivers(void) { return SDL_arraysize(bootstrap) - 1; @@ -72,242 +67,435 @@ const char *SDL_GetCurrentCameraDriver(void) return camera_driver.name; } - -static void CloseCameraDevice(SDL_CameraDevice *device) +static void ClosePhysicalCameraDevice(SDL_CameraDevice *device) { if (!device) { return; } SDL_AtomicSet(&device->shutdown, 1); - SDL_AtomicSet(&device->enabled, 1); + +// !!! FIXME: the close_cond stuff from audio might help the race condition here. if (device->thread != NULL) { SDL_WaitThread(device->thread, NULL); + device->thread = NULL; } - if (device->device_lock != NULL) { - SDL_DestroyMutex(device->device_lock); + + // release frames that are queued up somewhere... + if (!device->needs_conversion && !device->needs_scaling) { + for (SurfaceList *i = device->filled_output_surfaces.next; i != NULL; i = i->next) { + camera_driver.impl.ReleaseFrame(device, i->surface); + } + for (SurfaceList *i = device->app_held_output_surfaces.next; i != NULL; i = i->next) { + camera_driver.impl.ReleaseFrame(device, i->surface); + } } - if (device->acquiring_lock != NULL) { - SDL_DestroyMutex(device->acquiring_lock); + + camera_driver.impl.CloseDevice(device); + + SDL_DestroyProperties(device->props); + + SDL_DestroySurface(device->acquire_surface); + device->acquire_surface = NULL; + SDL_DestroySurface(device->conversion_surface); + device->conversion_surface = NULL; + + for (int i = 0; i < SDL_arraysize(device->output_surfaces); i++) { + SDL_DestroySurface(device->output_surfaces[i].surface); } + SDL_zeroa(device->output_surfaces); - const int n = SDL_arraysize(open_devices); - for (int i = 0; i < n; i++) { - if (open_devices[i] == device) { - open_devices[i] = NULL; - } + device->filled_output_surfaces.next = NULL; + device->empty_output_surfaces.next = NULL; + device->app_held_output_surfaces.next = NULL; +} + +// this must not be called while `device` is still in a device list, or while a device's camera thread is still running. +static void DestroyPhysicalCameraDevice(SDL_CameraDevice *device) +{ + if (device) { + // Destroy any logical devices that still exist... + ClosePhysicalCameraDevice(device); + camera_driver.impl.FreeDeviceHandle(device); + SDL_DestroyMutex(device->lock); + SDL_free(device->all_specs); + SDL_free(device->name); + SDL_free(device); } +} - entry_t *entry = NULL; - while (device->buffer_queue != NULL) { - SDL_ListPop(&device->buffer_queue, (void**)&entry); - if (entry) { - SDL_CameraFrame f = entry->frame; - // Release frames not acquired, if any - if (f.timestampNS) { - camera_driver.impl.ReleaseFrame(device, &f); - } - SDL_free(entry); + +// Don't hold the device lock when calling this, as we may destroy the device! +void UnrefPhysicalCameraDevice(SDL_CameraDevice *device) +{ + if (SDL_AtomicDecRef(&device->refcount)) { + // take it out of the device list. + SDL_LockRWLockForWriting(camera_driver.device_hash_lock); + if (SDL_RemoveFromHashTable(camera_driver.device_hash, (const void *) (uintptr_t) device->instance_id)) { + SDL_AtomicAdd(&camera_driver.device_count, -1); } + SDL_UnlockRWLock(camera_driver.device_hash_lock); + DestroyPhysicalCameraDevice(device); // ...and nuke it. } +} - camera_driver.impl.CloseDevice(device); +void RefPhysicalCameraDevice(SDL_CameraDevice *device) +{ + SDL_AtomicIncRef(&device->refcount); +} - SDL_free(device->dev_name); - SDL_free(device); +static void ObtainPhysicalCameraDeviceObj(SDL_CameraDevice *device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXMEL SDL_ACQUIRE +{ + if (device) { + RefPhysicalCameraDevice(device); + SDL_LockMutex(device->lock); + } } -void SDL_CloseCamera(SDL_CameraDevice *device) +static SDL_CameraDevice *ObtainPhysicalCameraDevice(SDL_CameraDeviceID devid) // !!! FIXME: SDL_ACQUIRE { + if (!SDL_GetCurrentCameraDriver()) { + SDL_SetError("Camera subsystem is not initialized"); + return NULL; + } + + SDL_CameraDevice *device = NULL; + SDL_LockRWLockForReading(camera_driver.device_hash_lock); + SDL_FindInHashTable(camera_driver.device_hash, (const void *) (uintptr_t) devid, (const void **) &device); + SDL_UnlockRWLock(camera_driver.device_hash_lock); if (!device) { - SDL_InvalidParamError("device"); + SDL_SetError("Invalid camera device instance ID"); } else { - CloseCameraDevice(device); + ObtainPhysicalCameraDeviceObj(device); } + return device; } -int SDL_StartCamera(SDL_CameraDevice *device) +static void ReleaseCameraDevice(SDL_CameraDevice *device) SDL_NO_THREAD_SAFETY_ANALYSIS // !!! FIXME: SDL_RELEASE { - if (!device) { - return SDL_InvalidParamError("device"); - } else if (device->is_spec_set == SDL_FALSE) { - return SDL_SetError("no spec set"); - } else if (SDL_GetCameraStatus(device) != SDL_CAMERA_INIT) { - return SDL_SetError("invalid state"); + if (device) { + SDL_UnlockMutex(device->lock); + UnrefPhysicalCameraDevice(device); } +} - const int result = camera_driver.impl.StartCamera(device); - if (result < 0) { - return result; - } +// we want these sorted by format first, so you can find a block of all +// resolutions that are supported for a format. The formats are sorted in +// "best" order, but that's subjective: right now, we prefer planar +// formats, since they're likely what the cameras prefer to produce +// anyhow, and they basically send the same information in less space +// than an RGB-style format. After that, sort by bits-per-pixel. - SDL_AtomicSet(&device->enabled, 1); +// we want specs sorted largest to smallest dimensions, larger width taking precedence over larger height. +static int SDLCALL CameraSpecCmp(const void *vpa, const void *vpb) +{ + const SDL_CameraSpec *a = (const SDL_CameraSpec *) vpa; + const SDL_CameraSpec *b = (const SDL_CameraSpec *) vpb; + + // driver shouldn't send specs like this, check here since we're eventually going to sniff the whole array anyhow. + SDL_assert(a->format != SDL_PIXELFORMAT_UNKNOWN); + SDL_assert(a->width > 0); + SDL_assert(a->height > 0); + SDL_assert(b->format != SDL_PIXELFORMAT_UNKNOWN); + SDL_assert(b->width > 0); + SDL_assert(b->height > 0); + + const Uint32 afmt = a->format; + const Uint32 bfmt = b->format; + if (SDL_ISPIXELFORMAT_FOURCC(afmt) && !SDL_ISPIXELFORMAT_FOURCC(bfmt)) { + return -1; + } else if (!SDL_ISPIXELFORMAT_FOURCC(afmt) && SDL_ISPIXELFORMAT_FOURCC(bfmt)) { + return 1; + } else if (SDL_BITSPERPIXEL(afmt) > SDL_BITSPERPIXEL(bfmt)) { + return -1; + } else if (SDL_BITSPERPIXEL(bfmt) > SDL_BITSPERPIXEL(afmt)) { + return 1; + } else if (a->width > b->width) { + return -1; + } else if (b->width > a->width) { + return 1; + } else if (a->height > b->height) { + return -1; + } else if (b->height > a->height) { + return 1; + } - return 0; + return 0; // apparently, they're equal. } -int SDL_GetCameraSpec(SDL_CameraDevice *device, SDL_CameraSpec *spec) + +// The camera backends call this when a new device is plugged in. +SDL_CameraDevice *SDL_AddCameraDevice(const char *name, int num_specs, const SDL_CameraSpec *specs, void *handle) { + SDL_assert(name != NULL); + SDL_assert(num_specs > 0); + SDL_assert(specs != NULL); + SDL_assert(handle != NULL); + + SDL_LockRWLockForReading(camera_driver.device_hash_lock); + const int shutting_down = SDL_AtomicGet(&camera_driver.shutting_down); + SDL_UnlockRWLock(camera_driver.device_hash_lock); + if (shutting_down) { + return NULL; // we're shutting down, don't add any devices that are hotplugged at the last possible moment. + } + + SDL_CameraDevice *device = (SDL_CameraDevice *)SDL_calloc(1, sizeof(SDL_CameraDevice)); if (!device) { - return SDL_InvalidParamError("device"); - } else if (!spec) { - return SDL_InvalidParamError("spec"); + return NULL; } - SDL_zerop(spec); - return camera_driver.impl.GetDeviceSpec(device, spec); -} + device->name = SDL_strdup(name); + if (!device->name) { + SDL_free(device); + return NULL; + } -int SDL_StopCamera(SDL_CameraDevice *device) -{ - if (!device) { - return SDL_InvalidParamError("device"); - } else if (SDL_GetCameraStatus(device) != SDL_CAMERA_PLAYING) { - return SDL_SetError("invalid state"); + device->lock = SDL_CreateMutex(); + if (!device->lock) { + SDL_free(device->name); + SDL_free(device); + return NULL; } - SDL_AtomicSet(&device->enabled, 0); - SDL_AtomicSet(&device->shutdown, 1); + device->all_specs = SDL_calloc(num_specs + 1, sizeof (*specs)); + if (!device->all_specs) { + SDL_DestroyMutex(device->lock); + SDL_free(device->name); + SDL_free(device); + return NULL; + } + + SDL_memcpy(device->all_specs, specs, sizeof (*specs) * num_specs); + SDL_qsort(device->all_specs, num_specs, sizeof (*specs), CameraSpecCmp); + + // weed out duplicates, just in case. + for (int i = 0; i < num_specs; i++) { + SDL_CameraSpec *a = &device->all_specs[i]; + SDL_CameraSpec *b = &device->all_specs[i + 1]; + if ((a->format == b->format) && (a->width == b->width) && (a->height == b->height)) { + SDL_memmove(a, b, sizeof (*specs) * (num_specs - i)); + i--; + num_specs--; + } + } + + device->num_specs = num_specs; + device->handle = handle; + device->instance_id = SDL_GetNextObjectID(); + SDL_AtomicSet(&device->shutdown, 0); + SDL_AtomicSet(&device->zombie, 0); + RefPhysicalCameraDevice(device); + + SDL_LockRWLockForWriting(camera_driver.device_hash_lock); + if (SDL_InsertIntoHashTable(camera_driver.device_hash, (const void *) (uintptr_t) device->instance_id, device)) { + SDL_AtomicAdd(&camera_driver.device_count, 1); + } else { + SDL_DestroyMutex(device->lock); + SDL_free(device->all_specs); + SDL_free(device->name); + SDL_free(device); + device = NULL; + } - SDL_LockMutex(device->acquiring_lock); - const int retval = camera_driver.impl.StopCamera(device); - SDL_UnlockMutex(device->acquiring_lock); + // Add a device add event to the pending list, to be pushed when the event queue is pumped (away from any of our internal threads). + if (device) { + SDL_PendingCameraDeviceEvent *p = (SDL_PendingCameraDeviceEvent *) SDL_malloc(sizeof (SDL_PendingCameraDeviceEvent)); + if (p) { // if allocation fails, you won't get an event, but we can't help that. + p->type = SDL_EVENT_CAMERA_DEVICE_ADDED; + p->devid = device->instance_id; + p->next = NULL; + SDL_assert(camera_driver.pending_events_tail != NULL); + SDL_assert(camera_driver.pending_events_tail->next == NULL); + camera_driver.pending_events_tail->next = p; + camera_driver.pending_events_tail = p; + } + } + SDL_UnlockRWLock(camera_driver.device_hash_lock); - return (retval < 0) ? -1 : 0; + return device; } -// Check spec has valid format and frame size -static int prepare_cameraspec(SDL_CameraDevice *device, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes) +// Called when a device is removed from the system, or it fails unexpectedly, from any thread, possibly even the camera device's thread. +void SDL_CameraDeviceDisconnected(SDL_CameraDevice *device) { - // Check format - const int numfmts = SDL_GetNumCameraFormats(device); - SDL_bool is_format_valid = SDL_FALSE; + if (!device) { + return; + } - for (int i = 0; i < numfmts; i++) { - Uint32 format; - if (SDL_GetCameraFormat(device, i, &format) == 0) { - if (format == desired->format && format != SDL_PIXELFORMAT_UNKNOWN) { - is_format_valid = SDL_TRUE; - obtained->format = format; - break; - } + // Save off removal info in a list so we can send events for each, next + // time the event queue pumps, in case something tries to close a device + // from an event filter, as this would risk deadlocks and other disasters + // if done from the device thread. + SDL_PendingCameraDeviceEvent pending; + pending.next = NULL; + SDL_PendingCameraDeviceEvent *pending_tail = &pending; + + ObtainPhysicalCameraDeviceObj(device); + + const SDL_bool first_disconnect = SDL_AtomicCAS(&device->zombie, 0, 1); + if (first_disconnect) { // if already disconnected this device, don't do it twice. + // Swap in "Zombie" versions of the usual platform interfaces, so the device will keep + // making progress until the app closes it. +#if 0 // !!! FIXME +sdfsdf + device->WaitDevice = ZombieWaitDevice; + device->GetDeviceBuf = ZombieGetDeviceBuf; + device->PlayDevice = ZombiePlayDevice; + device->WaitCaptureDevice = ZombieWaitDevice; + device->CaptureFromDevice = ZombieCaptureFromDevice; + device->FlushCapture = ZombieFlushCapture; +sdfsdf +#endif + + SDL_PendingCameraDeviceEvent *p = (SDL_PendingCameraDeviceEvent *) SDL_malloc(sizeof (SDL_PendingCameraDeviceEvent)); + if (p) { // if this failed, no event for you, but you have deeper problems anyhow. + p->type = SDL_EVENT_CAMERA_DEVICE_REMOVED; + p->devid = device->instance_id; + p->next = NULL; + pending_tail->next = p; + pending_tail = p; } } - if (!is_format_valid) { - if (allowed_changes) { - for (int i = 0; i < numfmts; i++) { - Uint32 format; - if (SDL_GetCameraFormat(device, i, &format) == 0) { - if (format != SDL_PIXELFORMAT_UNKNOWN) { - obtained->format = format; - is_format_valid = SDL_TRUE; - break; - } - } - } - } else { - return SDL_SetError("Not allowed to change the format"); + ReleaseCameraDevice(device); + + if (first_disconnect) { + if (pending.next) { // NULL if event is disabled or disaster struck. + SDL_LockRWLockForWriting(camera_driver.device_hash_lock); + SDL_assert(camera_driver.pending_events_tail != NULL); + SDL_assert(camera_driver.pending_events_tail->next == NULL); + camera_driver.pending_events_tail->next = pending.next; + camera_driver.pending_events_tail = pending_tail; + SDL_UnlockRWLock(camera_driver.device_hash_lock); } } +} - if (!is_format_valid) { - return SDL_SetError("Invalid format"); +SDL_CameraDevice *SDL_FindPhysicalCameraDeviceByCallback(SDL_bool (*callback)(SDL_CameraDevice *device, void *userdata), void *userdata) +{ + if (!SDL_GetCurrentCameraDriver()) { + SDL_SetError("Camera subsystem is not initialized"); + return NULL; } - // Check frame size - const int numsizes = SDL_GetNumCameraFrameSizes(device, obtained->format); - SDL_bool is_framesize_valid = SDL_FALSE; + const void *key; + const void *value; + void *iter = NULL; - for (int i = 0; i < numsizes; i++) { - int w, h; - if (SDL_GetCameraFrameSize(device, obtained->format, i, &w, &h) == 0) { - if (desired->width == w && desired->height == h) { - is_framesize_valid = SDL_TRUE; - obtained->width = w; - obtained->height = h; - break; - } + SDL_LockRWLockForReading(camera_driver.device_hash_lock); + while (SDL_IterateHashTable(camera_driver.device_hash, &key, &value, &iter)) { + SDL_CameraDevice *device = (SDL_CameraDevice *) value; + if (callback(device, userdata)) { // found it? + SDL_UnlockRWLock(camera_driver.device_hash_lock); + return device; } } - if (!is_framesize_valid) { - if (allowed_changes) { - int w, h; - if (SDL_GetCameraFrameSize(device, obtained->format, 0, &w, &h) == 0) { - is_framesize_valid = SDL_TRUE; - obtained->width = w; - obtained->height = h; - } - } else { - return SDL_SetError("Not allowed to change the frame size"); - } - } + SDL_UnlockRWLock(camera_driver.device_hash_lock); - if (!is_framesize_valid) { - return SDL_SetError("Invalid frame size"); + SDL_SetError("Device not found"); + return NULL; +} + +void SDL_CloseCamera(SDL_Camera *camera) +{ + SDL_CameraDevice *device = (SDL_CameraDevice *) camera; // currently there's no separation between physical and logical device. + ClosePhysicalCameraDevice(device); +} + +int SDL_GetCameraSpec(SDL_Camera *camera, SDL_CameraSpec *spec) +{ + if (!camera) { + return SDL_InvalidParamError("camera"); + } else if (!spec) { + return SDL_InvalidParamError("spec"); } + SDL_CameraDevice *device = (SDL_CameraDevice *) camera; // currently there's no separation between physical and logical device. + SDL_copyp(spec, &device->spec); return 0; } -const char *SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id) +char *SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id) { - static char buf[256]; - buf[0] = 0; - buf[255] = 0; + char *retval = NULL; + SDL_CameraDevice *device = ObtainPhysicalCameraDevice(instance_id); + if (device) { + retval = SDL_strdup(device->name); + ReleaseCameraDevice(device); + } + return retval; +} - if (instance_id == 0) { - SDL_InvalidParamError("instance_id"); +SDL_CameraDeviceID *SDL_GetCameraDevices(int *count) +{ + if (!SDL_GetCurrentCameraDriver()) { + SDL_SetError("Camera subsystem is not initialized"); return NULL; } - if (camera_driver.impl.GetDeviceName(instance_id, buf, sizeof (buf)) < 0) { - buf[0] = 0; + SDL_CameraDeviceID *retval = NULL; + + SDL_LockRWLockForReading(camera_driver.device_hash_lock); + int num_devices = SDL_AtomicGet(&camera_driver.device_count); + if (num_devices > 0) { + retval = (SDL_CameraDeviceID *) SDL_malloc((num_devices + 1) * sizeof (SDL_CameraDeviceID)); + if (!retval) { + num_devices = 0; + } else { + int devs_seen = 0; + const void *key; + const void *value; + void *iter = NULL; + while (SDL_IterateHashTable(camera_driver.device_hash, &key, &value, &iter)) { + retval[devs_seen++] = (SDL_CameraDeviceID) (uintptr_t) key; + } + + SDL_assert(devs_seen == num_devices); + retval[devs_seen] = 0; // null-terminated. + } } + SDL_UnlockRWLock(camera_driver.device_hash_lock); + + if (count) { + *count = num_devices; + } + + return retval; - return buf; } -SDL_CameraDeviceID *SDL_GetCameraDevices(int *count) +SDL_CameraSpec *SDL_GetCameraDeviceSupportedSpecs(SDL_CameraDeviceID instance_id, int *count) { - int dummycount = 0; - if (!count) { - count = &dummycount; + if (count) { + *count = 0; } - int num = 0; - SDL_CameraDeviceID *retval = camera_driver.impl.GetDevices(&num); - if (retval) { - *count = num; - return retval; + SDL_CameraDevice *device = ObtainPhysicalCameraDevice(instance_id); + if (!device) { + return NULL; } - // return list of 0 ID, null terminated - retval = (SDL_CameraDeviceID *)SDL_calloc(1, sizeof(*retval)); - if (retval == NULL) { - *count = 0; - return NULL; + SDL_CameraSpec *retval = (SDL_CameraSpec *) SDL_calloc(device->num_specs + 1, sizeof (SDL_CameraSpec)); + if (retval) { + SDL_memcpy(retval, device->all_specs, sizeof (SDL_CameraSpec) * device->num_specs); + if (count) { + *count = device->num_specs; + } } - retval[0] = 0; - *count = 0; + ReleaseCameraDevice(device); return retval; } -// Camera thread function -static int SDLCALL SDL_CameraThread(void *devicep) -{ - const int delay = 20; - SDL_CameraDevice *device = (SDL_CameraDevice *) devicep; - -#if DEBUG_CAMERA - SDL_Log("Start thread 'SDL_CameraThread'"); -#endif +// Camera device thread. This is split into chunks, so drivers that need to control this directly can use the pieces they need without duplicating effort. +void SDL_CameraThreadSetup(SDL_CameraDevice *device) +{ + //camera_driver.impl.ThreadInit(device); #ifdef SDL_VIDEO_DRIVER_ANDROID // TODO /* @@ -320,342 +508,464 @@ static int SDLCALL SDL_CameraThread(void *devicep) // The camera capture is always a high priority thread SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); #endif +} - // Perform any thread setup - device->threadid = SDL_GetCurrentThreadID(); +SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device) +{ + SDL_LockMutex(device->lock); - // Init state - // !!! FIXME: use a semaphore or something - while (!SDL_AtomicGet(&device->enabled)) { - SDL_Delay(delay); + if (SDL_AtomicGet(&device->shutdown)) { + SDL_UnlockMutex(device->lock); + return SDL_FALSE; // we're done, shut it down. } - // Loop, filling the camera buffers - while (!SDL_AtomicGet(&device->shutdown)) { - SDL_CameraFrame f; - int ret; + // !!! FIXME: this should block elsewhere without holding the lock until a frame is available, like the audio subsystem does. - SDL_zero(f); + SDL_bool failed = SDL_FALSE; // set to true if disaster worthy of treating the device as lost has happened. + SDL_Surface *acquired = NULL; + SDL_Surface *output_surface = NULL; + SurfaceList *slist = NULL; + Uint64 timestampNS = 0; - SDL_LockMutex(device->acquiring_lock); - ret = camera_driver.impl.AcquireFrame(device, &f); - SDL_UnlockMutex(device->acquiring_lock); + // AcquireFrame SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitDevice instead! + const int rc = camera_driver.impl.AcquireFrame(device, device->acquire_surface, ×tampNS); - if (ret == 0) { - if (f.num_planes == 0) { - continue; - } - } + if (rc == 1) { // new frame acquired! + #if DEBUG_CAMERA + SDL_Log("CAMERA: New frame available!"); + #endif - if (ret < 0) { - // Flag it as an error -#if DEBUG_CAMERA - SDL_Log("dev[%p] error AcquireFrame: %d %s", (void *)device, ret, SDL_GetError()); -#endif - f.num_planes = 0; + if (device->empty_output_surfaces.next == NULL) { + // uhoh, no output frames available! Either the app is slow, or it forgot to release frames when done with them. Drop this new frame. + #if DEBUG_CAMERA + SDL_Log("CAMERA: No empty output surfaces! Dropping frame!"); + #endif + camera_driver.impl.ReleaseFrame(device, device->acquire_surface); + device->acquire_surface->pixels = NULL; + device->acquire_surface->pitch = 0; + } else { + slist = device->empty_output_surfaces.next; + output_surface = slist->surface; + device->empty_output_surfaces.next = slist->next; + acquired = device->acquire_surface; + slist->timestampNS = timestampNS; } + } else if (rc == 0) { // no frame available yet; not an error. + #if 0 //DEBUG_CAMERA + SDL_Log("CAMERA: No frame available yet."); + #endif + } else { // fatal error! + SDL_assert(rc == -1); + #if DEBUG_CAMERA + SDL_Log("CAMERA: dev[%p] error AcquireFrame: %s", device, SDL_GetError()); + #endif + failed = SDL_TRUE; + } + // we can let go of the lock once we've tried to grab a frame of video and maybe moved the output frame from the empty to the filled list. + // this lets us chew up the CPU for conversion and scaling without blocking other threads. + SDL_UnlockMutex(device->lock); + + if (failed) { + SDL_assert(slist == NULL); + SDL_assert(acquired == NULL); + SDL_CameraDeviceDisconnected(device); // doh. + } else if (acquired) { // we have a new frame, scale/convert if necessary and queue it for the app! + SDL_assert(slist != NULL); + if (!device->needs_scaling && !device->needs_conversion) { // no conversion needed? Just move the pointer/pitch into the output surface. + output_surface->pixels = acquired->pixels; + output_surface->pitch = acquired->pitch; + } else { // convert/scale into a different surface. + SDL_Surface *srcsurf = acquired; + if (device->needs_scaling == -1) { // downscaling? Do it first. -1: downscale, 0: no scaling, 1: upscale + SDL_Surface *dstsurf = device->needs_conversion ? device->conversion_surface : output_surface; + SDL_SoftStretch(srcsurf, NULL, dstsurf, NULL); // !!! FIXME: linear scale? letterboxing? + srcsurf = dstsurf; + } + if (device->needs_conversion) { + SDL_Surface *dstsurf = (device->needs_scaling == 1) ? device->conversion_surface : output_surface; + SDL_ConvertPixels(srcsurf->w, srcsurf->h, + srcsurf->format->format, srcsurf->pixels, srcsurf->pitch, + dstsurf->format->format, dstsurf->pixels, dstsurf->pitch); + srcsurf = dstsurf; + } + if (device->needs_scaling == 1) { // upscaling? Do it last. -1: downscale, 0: no scaling, 1: upscale + SDL_SoftStretch(srcsurf, NULL, output_surface, NULL); // !!! FIXME: linear scale? letterboxing? + } - entry_t *entry = SDL_malloc(sizeof (entry_t)); - if (entry == NULL) { - goto error_mem; + // we made a copy, so we can give the driver back its resources. + camera_driver.impl.ReleaseFrame(device, acquired); } - entry->frame = f; + // we either released these already after we copied the data, or the pointer was migrated to output_surface. + acquired->pixels = NULL; + acquired->pitch = 0; - SDL_LockMutex(device->device_lock); - ret = SDL_ListAdd(&device->buffer_queue, entry); - SDL_UnlockMutex(device->device_lock); - - if (ret < 0) { - SDL_free(entry); - goto error_mem; - } + // make the filled output surface available to the app. + SDL_LockMutex(device->lock); + slist->next = device->filled_output_surfaces.next; + device->filled_output_surfaces.next = slist; + SDL_UnlockMutex(device->lock); } -#if DEBUG_CAMERA - SDL_Log("dev[%p] End thread 'SDL_CameraThread'", (void *)device); -#endif - return 0; + return SDL_TRUE; // always go on if not shutting down, even if device failed. +} -error_mem: -#if DEBUG_CAMERA - SDL_Log("dev[%p] End thread 'SDL_CameraThread' with error: %s", (void *)device, SDL_GetError()); -#endif - SDL_AtomicSet(&device->shutdown, 1); - return 0; +void SDL_CameraThreadShutdown(SDL_CameraDevice *device) +{ + //device->FlushCapture(device); + //camera_driver.impl.ThreadDeinit(device); + //SDL_CameraThreadFinalize(device); } -SDL_CameraDevice *SDL_OpenCamera(SDL_CameraDeviceID instance_id) +// Actual thread entry point, if driver didn't handle this itself. +static int SDLCALL CameraThread(void *devicep) { - const int n = SDL_arraysize(open_devices); - SDL_CameraDevice *device = NULL; - const char *device_name = NULL; - int id = -1; + SDL_CameraDevice *device = (SDL_CameraDevice *) devicep; - if (!SDL_WasInit(SDL_INIT_VIDEO)) { - SDL_SetError("Video subsystem is not initialized"); - goto error; - } + #if DEBUG_CAMERA + SDL_Log("CAMERA: Start thread 'SDL_CameraThread'"); + #endif - // !!! FIXME: there is a race condition here if two devices open from two threads at once. - // Find an available device ID... - for (int i = 0; i < n; i++) { - if (open_devices[i] == NULL) { - id = i; - break; + SDL_assert(device != NULL); + SDL_CameraThreadSetup(device); + + do { + if (camera_driver.impl.WaitDevice(device) < 0) { + SDL_CameraDeviceDisconnected(device); // doh. (but don't break out of the loop, just be a zombie for now!) } - } + } while (SDL_CameraThreadIterate(device)); - if (id == -1) { - SDL_SetError("Too many open camera devices"); - goto error; - } + SDL_CameraThreadShutdown(device); - if (instance_id != 0) { - device_name = SDL_GetCameraDeviceName(instance_id); - if (device_name == NULL) { - goto error; + #if DEBUG_CAMERA + SDL_Log("CAMERA: dev[%p] End thread 'SDL_CameraThread'", (void *)device); + #endif + + return 0; +} + +static void ChooseBestCameraSpec(SDL_CameraDevice *device, const SDL_CameraSpec *spec, SDL_CameraSpec *closest) +{ + // Find the closest available native format/size... + // + // We want the exact size if possible, even if we have + // to convert formats, because we can _probably_ do that + // conversion losslessly at less expense verses scaling. + // + // Failing that, we want the size that's closest to the + // requested aspect ratio, then the closest size within + // that. + + SDL_zerop(closest); + SDL_assert(((Uint32) SDL_PIXELFORMAT_UNKNOWN) == 0); // since we SDL_zerop'd to this value. + + if (!spec) { // nothing specifically requested, get the best format we can... + // we sorted this into the "best" format order when adding the camera. + SDL_assert(device->num_specs > 0); + SDL_copyp(closest, &device->all_specs[0]); + } else { // specific thing requested, try to get as close to that as possible... + const int num_specs = device->num_specs; + int wantw = spec->width; + int wanth = spec->height; + + // Find the sizes with the closest aspect ratio and then find the best fit of those. + const float wantaspect = ((float)wantw) / ((float) wanth); + const float epsilon = 1e-6f; + float closestaspect = -9999999.0f; + float closestdiff = 999999.0f; + int closestdiffw = 9999999; + + for (int i = 0; i < num_specs; i++) { + const SDL_CameraSpec *thisspec = &device->all_specs[i]; + const int thisw = thisspec->width; + const int thish = thisspec->height; + const float thisaspect = ((float)thisw) / ((float) thish); + const float aspectdiff = SDL_fabs(wantaspect - thisaspect); + const float diff = SDL_fabs(closestaspect - thisaspect); + const int diffw = SDL_abs(thisw - wantw); + if (diff < epsilon) { // matches current closestaspect? See if resolution is closer in size. + if (diffw < closestdiffw) { + closestdiffw = diffw; + closest->width = thisw; + closest->height = thish; + } + } else if (aspectdiff < closestdiff) { // this is a closer aspect ratio? Take it, reset resolution checks. + closestdiff = aspectdiff; + closestaspect = thisaspect; + closestdiffw = diffw; + closest->width = thisw; + closest->height = thish; + } } - } else { - SDL_CameraDeviceID *devices = SDL_GetCameraDevices(NULL); - if (devices && devices[0]) { - device_name = SDL_GetCameraDeviceName(devices[0]); - SDL_free(devices); + + SDL_assert(closest->width > 0); + SDL_assert(closest->height > 0); + + // okay, we have what we think is the best resolution, now we just need the best format that supports it... + const Uint32 wantfmt = spec->format; + Uint32 bestfmt = SDL_PIXELFORMAT_UNKNOWN; + for (int i = 0; i < num_specs; i++) { + const SDL_CameraSpec *thisspec = &device->all_specs[i]; + if ((thisspec->width == closest->width) && (thisspec->height == closest->height)) { + if (bestfmt == SDL_PIXELFORMAT_UNKNOWN) { + bestfmt = thisspec->format; // spec list is sorted by what we consider "best" format, so unless we find an exact match later, first size match is the one! + } + if (thisspec->format == wantfmt) { + bestfmt = thisspec->format; + break; // exact match, stop looking. + } + } } + + SDL_assert(bestfmt != SDL_PIXELFORMAT_UNKNOWN); + closest->format = bestfmt; } -#if 0 - // FIXME do we need this ? - // Let the user override. - { - const char *dev = SDL_getenv("SDL_CAMERA_DEVICE_NAME"); - if (dev && dev[0]) { - device_name = dev; + SDL_assert(closest->width > 0); + SDL_assert(closest->height > 0); + SDL_assert(closest->format != SDL_PIXELFORMAT_UNKNOWN); +} + +SDL_Camera *SDL_OpenCameraDevice(SDL_CameraDeviceID instance_id, const SDL_CameraSpec *spec) +{ + if (spec) { + if ((spec->width <= 0) || (spec->height <= 0)) { + SDL_SetError("Requested spec frame size is invalid"); + return NULL; + } else if (spec->format == SDL_PIXELFORMAT_UNKNOWN) { + SDL_SetError("Requested spec format is invalid"); + return NULL; } } -#endif - if (device_name == NULL) { - goto error; + SDL_CameraDevice *device = ObtainPhysicalCameraDevice(instance_id); + if (!device) { + return NULL; } - device = (SDL_CameraDevice *) SDL_calloc(1, sizeof (SDL_CameraDevice)); - if (device == NULL) { - goto error; + if (device->hidden != NULL) { + ReleaseCameraDevice(device); + SDL_SetError("Camera already opened"); // we may remove this limitation at some point. + return NULL; } - device->dev_name = SDL_strdup(device_name); SDL_AtomicSet(&device->shutdown, 0); - SDL_AtomicSet(&device->enabled, 0); - device->device_lock = SDL_CreateMutex(); - if (device->device_lock == NULL) { - SDL_SetError("Couldn't create acquiring_lock"); - goto error; - } + SDL_CameraSpec closest; + ChooseBestCameraSpec(device, spec, &closest); - device->acquiring_lock = SDL_CreateMutex(); - if (device->acquiring_lock == NULL) { - SDL_SetError("Couldn't create acquiring_lock"); - goto error; - } + #if DEBUG_CAMERA + SDL_Log("CAMERA: App wanted [(%dx%d) fmt=%s], chose [(%dx%d) fmt=%s]", spec ? spec->width : -1, spec ? spec->height : -1, spec ? SDL_GetPixelFormatName(spec->format) : "(null)", closest.width, closest.height, SDL_GetPixelFormatName(closest.format)); + #endif - if (camera_driver.impl.OpenDevice(device) < 0) { - goto error; + if (camera_driver.impl.OpenDevice(device, &closest) < 0) { + ClosePhysicalCameraDevice(device); // in case anything is half-initialized. + ReleaseCameraDevice(device); + return NULL; } - // empty - device->buffer_queue = NULL; - open_devices[id] = device; // add it to our list of open devices. - - - // Start the camera thread - char threadname[64]; - SDL_snprintf(threadname, sizeof (threadname), "SDLCamera%d", id); - device->thread = SDL_CreateThreadInternal(SDL_CameraThread, threadname, 0, device); - if (device->thread == NULL) { - SDL_SetError("Couldn't create camera thread"); - goto error; + if (!spec) { + SDL_copyp(&device->spec, &closest); + } else { + SDL_copyp(&device->spec, spec); } - return device; + SDL_copyp(&device->actual_spec, &closest); -error: - CloseCameraDevice(device); - return NULL; -} - -int SDL_SetCameraSpec(SDL_CameraDevice *device, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes) -{ - SDL_CameraSpec _obtained; - SDL_CameraSpec _desired; - int result; - - if (!device) { - return SDL_InvalidParamError("device"); - } else if (device->is_spec_set == SDL_TRUE) { - return SDL_SetError("already configured"); + if ((closest.width == device->spec.width) && (closest.height == device->spec.height)) { + device->needs_scaling = 0; + } else { + const Uint64 srcarea = ((Uint64) closest.width) * ((Uint64) closest.height); + const Uint64 dstarea = ((Uint64) device->spec.width) * ((Uint64) device->spec.height); + if (dstarea <= srcarea) { + device->needs_scaling = -1; // downscaling (or changing to new aspect ratio with same area) + } else { + device->needs_scaling = 1; // upscaling + } } - if (!desired) { - SDL_zero(_desired); - desired = &_desired; - allowed_changes = SDL_CAMERA_ALLOW_ANY_CHANGE; - } else { - // in case desired == obtained - _desired = *desired; - desired = &_desired; + device->needs_conversion = (closest.format != device->spec.format); + + device->acquire_surface = SDL_CreateSurfaceFrom(NULL, closest.width, closest.height, 0, closest.format); + if (!device->acquire_surface) { + ClosePhysicalCameraDevice(device); + ReleaseCameraDevice(device); + return NULL; } - if (!obtained) { - obtained = &_obtained; + // if we have to scale _and_ convert, we need a middleman surface, since we can't do both changes at once. + if (device->needs_scaling && device->needs_conversion) { + const SDL_bool downsampling_first = (device->needs_scaling < 0); + const SDL_CameraSpec *s = downsampling_first ? &device->spec : &closest; + const Uint32 fmt = downsampling_first ? closest.format : device->spec.format; + device->conversion_surface = SDL_CreateSurface(s->width, s->height, fmt); } - SDL_zerop(obtained); + // output surfaces are in the app-requested format. If no conversion is necessary, we'll just use the pointers + // the backend fills into acquired_surface, and you can get all the way from DMA access in the camera hardware + // to the app without a single copy. Otherwise, these will be full surfaces that hold converted/scaled copies. - if (prepare_cameraspec(device, desired, obtained, allowed_changes) < 0) { - return -1; + for (int i = 0; i < (SDL_arraysize(device->output_surfaces) - 1); i++) { + device->output_surfaces[i].next = &device->output_surfaces[i + 1]; } + device->empty_output_surfaces.next = device->output_surfaces; - device->spec = *obtained; + for (int i = 0; i < SDL_arraysize(device->output_surfaces); i++) { + SDL_Surface *surf; + if (device->needs_scaling || device->needs_conversion) { + surf = SDL_CreateSurface(device->spec.width, device->spec.height, device->spec.format); + } else { + surf = SDL_CreateSurfaceFrom(NULL, device->spec.width, device->spec.height, 0, device->spec.format); + } + + if (!surf) { + ClosePhysicalCameraDevice(device); + ReleaseCameraDevice(device); + return NULL; + } - result = camera_driver.impl.InitDevice(device); - if (result < 0) { - return result; + device->output_surfaces[i].surface = surf; } - *obtained = device->spec; + // Start the camera thread if necessary + if (!camera_driver.impl.ProvidesOwnCallbackThread) { + char threadname[64]; + SDL_snprintf(threadname, sizeof (threadname), "SDLCamera%d", instance_id); + device->thread = SDL_CreateThreadInternal(CameraThread, threadname, 0, device); + if (!device->thread) { + ClosePhysicalCameraDevice(device); + ReleaseCameraDevice(device); + SDL_SetError("Couldn't create camera thread"); + return NULL; + } + } - device->is_spec_set = SDL_TRUE; + ReleaseCameraDevice(device); // unlock, we're good to go! - return 0; + return (SDL_Camera *) device; // currently there's no separation between physical and logical device. } -int SDL_AcquireCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame) +SDL_Surface *SDL_AcquireCameraFrame(SDL_Camera *camera, Uint64 *timestampNS) { - if (!device) { - return SDL_InvalidParamError("device"); - } else if (!frame) { - return SDL_InvalidParamError("frame"); + if (timestampNS) { + *timestampNS = 0; } - SDL_zerop(frame); + if (!camera) { + SDL_InvalidParamError("camera"); + return NULL; + } - if (device->thread == NULL) { - int ret; + SDL_CameraDevice *device = (SDL_CameraDevice *) camera; // currently there's no separation between physical and logical device. - // Wait for a frame - while ((ret = camera_driver.impl.AcquireFrame(device, frame)) == 0) { - if (frame->num_planes) { - return 0; - } - } - return -1; - } else { - entry_t *entry = NULL; + ObtainPhysicalCameraDeviceObj(device); - SDL_LockMutex(device->device_lock); - SDL_ListPop(&device->buffer_queue, (void**)&entry); - SDL_UnlockMutex(device->device_lock); + SDL_Surface *retval = NULL; - if (entry) { - *frame = entry->frame; - SDL_free(entry); + // frames are in this list from newest to oldest, so find the end of the list... + SurfaceList *slistprev = &device->filled_output_surfaces; + SurfaceList *slist = slistprev; + while (slist->next) { + slistprev = slist; + slist = slist->next; + } - // Error from thread - if (frame->num_planes == 0 && frame->timestampNS == 0) { - return SDL_SetError("error from acquisition thread"); - } - } else { - // Queue is empty. Not an error. + const SDL_bool list_is_empty = (slist == slistprev); + if (!list_is_empty) { // report the oldest frame. + if (timestampNS) { + *timestampNS = slist->timestampNS; } + retval = slist->surface; + slistprev->next = slist->next; // remove from filled list. + slist->next = device->app_held_output_surfaces.next; // add to app_held list. + device->app_held_output_surfaces.next = slist; } - return 0; + ReleaseCameraDevice(device); + + return retval; } -int SDL_ReleaseCameraFrame(SDL_CameraDevice *device, SDL_CameraFrame *frame) +int SDL_ReleaseCameraFrame(SDL_Camera *camera, SDL_Surface *frame) { - if (!device) { - return SDL_InvalidParamError("device"); + if (!camera) { + return SDL_InvalidParamError("camera"); } else if (frame == NULL) { return SDL_InvalidParamError("frame"); - } else if (camera_driver.impl.ReleaseFrame(device, frame) < 0) { - return -1; } - SDL_zerop(frame); - return 0; -} + SDL_CameraDevice *device = (SDL_CameraDevice *) camera; // currently there's no separation between physical and logical device. + ObtainPhysicalCameraDeviceObj(device); -int SDL_GetNumCameraFormats(SDL_CameraDevice *device) -{ - if (!device) { - return SDL_InvalidParamError("device"); + SurfaceList *slistprev = &device->app_held_output_surfaces; + SurfaceList *slist; + for (slist = slistprev->next; slist != NULL; slist = slist->next) { + if (slist->surface == frame) { + break; + } + slistprev = slist; } - return camera_driver.impl.GetNumFormats(device); -} -int SDL_GetCameraFormat(SDL_CameraDevice *device, int index, Uint32 *format) -{ - if (!device) { - return SDL_InvalidParamError("device"); - } else if (!format) { - return SDL_InvalidParamError("format"); + if (!slist) { + ReleaseCameraDevice(device); + return SDL_SetError("Surface was not acquired from this camera, or was already released"); } - *format = 0; - return camera_driver.impl.GetFormat(device, index, format); -} -int SDL_GetNumCameraFrameSizes(SDL_CameraDevice *device, Uint32 format) -{ - if (!device) { - return SDL_InvalidParamError("device"); + // this pointer was owned by the backend (DMA memory or whatever), clear it out. + if (!device->needs_conversion && !device->needs_scaling) { + camera_driver.impl.ReleaseFrame(device, frame); + frame->pixels = NULL; + frame->pitch = 0; } - return camera_driver.impl.GetNumFrameSizes(device, format); -} -int SDL_GetCameraFrameSize(SDL_CameraDevice *device, Uint32 format, int index, int *width, int *height) -{ - if (!device) { - return SDL_InvalidParamError("device"); - } else if (!width) { - return SDL_InvalidParamError("width"); - } else if (!height) { - return SDL_InvalidParamError("height"); - } - *width = *height = 0; - return camera_driver.impl.GetFrameSize(device, format, index, width, height); + slist->timestampNS = 0; + + // remove from app_held list... + slistprev->next = slist->next; + + // insert at front of empty list (and we'll use it first when we need to fill a new frame). + slist->next = device->empty_output_surfaces.next; + device->empty_output_surfaces.next = slist; + + ReleaseCameraDevice(device); + + return 0; } -SDL_CameraDevice *SDL_OpenCameraWithSpec(SDL_CameraDeviceID instance_id, const SDL_CameraSpec *desired, SDL_CameraSpec *obtained, int allowed_changes) -{ - SDL_CameraDevice *device; +// !!! FIXME: add a way to "pause" camera output. - if ((device = SDL_OpenCamera(instance_id)) == NULL) { - return NULL; +SDL_CameraDeviceID SDL_GetCameraInstanceID(SDL_Camera *camera) +{ + SDL_CameraDeviceID retval = 0; + if (!camera) { + SDL_InvalidParamError("camera"); + } else { + SDL_CameraDevice *device = (SDL_CameraDevice *) camera; // currently there's no separation between physical and logical device. + ObtainPhysicalCameraDeviceObj(device); + retval = device->instance_id; + ReleaseCameraDevice(device); } - if (SDL_SetCameraSpec(device, desired, obtained, allowed_changes) < 0) { - SDL_CloseCamera(device); - return NULL; - } - return device; + return retval; } -SDL_CameraStatus SDL_GetCameraStatus(SDL_CameraDevice *device) +SDL_PropertiesID SDL_GetCameraProperties(SDL_Camera *camera) { - if (device == NULL) { - return SDL_CAMERA_INIT; - } else if (device->is_spec_set == SDL_FALSE) { - return SDL_CAMERA_INIT; - } else if (SDL_AtomicGet(&device->shutdown)) { - return SDL_CAMERA_STOPPED; - } else if (SDL_AtomicGet(&device->enabled)) { - return SDL_CAMERA_PLAYING; + SDL_PropertiesID retval = 0; + if (!camera) { + SDL_InvalidParamError("camera"); + } else { + SDL_CameraDevice *device = (SDL_CameraDevice *) camera; // currently there's no separation between physical and logical device. + ObtainPhysicalCameraDeviceObj(device); + if (device->props == 0) { + device->props = SDL_CreateProperties(); + } + retval = device->props; + ReleaseCameraDevice(device); } - return SDL_CAMERA_INIT; + + return retval; } static void CompleteCameraEntryPoints(void) @@ -665,18 +975,9 @@ static void CompleteCameraEntryPoints(void) FILL_STUB(DetectDevices); FILL_STUB(OpenDevice); FILL_STUB(CloseDevice); - FILL_STUB(InitDevice); - FILL_STUB(GetDeviceSpec); - FILL_STUB(StartCamera); - FILL_STUB(StopCamera); FILL_STUB(AcquireFrame); FILL_STUB(ReleaseFrame); - FILL_STUB(GetNumFormats); - FILL_STUB(GetFormat); - FILL_STUB(GetNumFrameSizes); - FILL_STUB(GetFrameSize); - FILL_STUB(GetDeviceName); - FILL_STUB(GetDevices); + FILL_STUB(FreeDeviceHandle); FILL_STUB(Deinitialize); #undef FILL_STUB } @@ -687,38 +988,70 @@ void SDL_QuitCamera(void) return; } - const int n = SDL_arraysize(open_devices); - for (int i = 0; i < n; i++) { - CloseCameraDevice(open_devices[i]); - } - - SDL_zeroa(open_devices); - -#if 0 // !!! FIXME + SDL_LockRWLockForWriting(camera_driver.device_hash_lock); + SDL_AtomicSet(&camera_driver.shutting_down, 1); + SDL_HashTable *device_hash = camera_driver.device_hash; + camera_driver.device_hash = NULL; SDL_PendingCameraDeviceEvent *pending_events = camera_driver.pending_events.next; camera_driver.pending_events.next = NULL; + SDL_AtomicSet(&camera_driver.device_count, 0); + SDL_UnlockRWLock(camera_driver.device_hash_lock); SDL_PendingCameraDeviceEvent *pending_next = NULL; for (SDL_PendingCameraDeviceEvent *i = pending_events; i; i = pending_next) { pending_next = i->next; SDL_free(i); } -#endif + + const void *key; + const void *value; + void *iter = NULL; + while (SDL_IterateHashTable(device_hash, &key, &value, &iter)) { + DestroyPhysicalCameraDevice((SDL_CameraDevice *) value); + } // Free the driver data camera_driver.impl.Deinitialize(); + SDL_DestroyRWLock(camera_driver.device_hash_lock); + SDL_DestroyHashTable(device_hash); + SDL_zero(camera_driver); } -// this is 90% the same code as the audio subsystem uses. + +static Uint32 HashCameraDeviceID(const void *key, void *data) +{ + // The values are unique incrementing integers, starting at 1, so just return minus 1 to start with bucket zero. + return ((Uint32) ((uintptr_t) key)) - 1; +} + +static SDL_bool MatchCameraDeviceID(const void *a, const void *b, void *data) +{ + return (a == b); // simple integers, just compare them as pointer values. +} + +static void NukeCameraDeviceHashItem(const void *key, const void *value, void *data) +{ + // no-op, keys and values in this hashtable are treated as Plain Old Data and don't get freed here. +} + int SDL_CameraInit(const char *driver_name) { if (SDL_GetCurrentCameraDriver()) { SDL_QuitCamera(); // shutdown driver if already running. } - SDL_zeroa(open_devices); + SDL_RWLock *device_hash_lock = SDL_CreateRWLock(); // create this early, so if it fails we don't have to tear down the whole camera subsystem. + if (!device_hash_lock) { + return -1; + } + + SDL_HashTable *device_hash = SDL_CreateHashTable(NULL, 8, HashCameraDeviceID, MatchCameraDeviceID, NukeCameraDeviceHashItem, SDL_FALSE); + if (!device_hash) { + SDL_DestroyRWLock(device_hash_lock); + return -1; + } // Select the proper camera driver if (!driver_name) { @@ -733,6 +1066,8 @@ int SDL_CameraInit(const char *driver_name) const char *driver_attempt = driver_name_copy; if (!driver_name_copy) { + SDL_DestroyRWLock(device_hash_lock); + SDL_DestroyHashTable(device_hash); return -1; } @@ -746,9 +1081,9 @@ int SDL_CameraInit(const char *driver_name) if (SDL_strcasecmp(bootstrap[i]->name, driver_attempt) == 0) { tried_to_init = SDL_TRUE; SDL_zero(camera_driver); - #if 0 // !!! FIXME camera_driver.pending_events_tail = &camera_driver.pending_events; - #endif + camera_driver.device_hash_lock = device_hash_lock; + camera_driver.device_hash = device_hash; if (bootstrap[i]->init(&camera_driver.impl)) { camera_driver.name = bootstrap[i]->name; camera_driver.desc = bootstrap[i]->desc; @@ -770,9 +1105,9 @@ int SDL_CameraInit(const char *driver_name) tried_to_init = SDL_TRUE; SDL_zero(camera_driver); - #if 0 // !!! FIXME camera_driver.pending_events_tail = &camera_driver.pending_events; - #endif + camera_driver.device_hash_lock = device_hash_lock; + camera_driver.device_hash = device_hash; if (bootstrap[i]->init(&camera_driver.impl)) { camera_driver.name = bootstrap[i]->name; camera_driver.desc = bootstrap[i]->desc; @@ -792,6 +1127,8 @@ int SDL_CameraInit(const char *driver_name) } SDL_zero(camera_driver); + SDL_DestroyRWLock(device_hash_lock); + SDL_DestroyHashTable(device_hash); return -1; // No driver was available, so fail. } @@ -803,3 +1140,36 @@ int SDL_CameraInit(const char *driver_name) return 0; } +// This is an internal function, so SDL_PumpEvents() can check for pending camera device events. +// ("UpdateSubsystem" is the same naming that the other things that hook into PumpEvents use.) +void SDL_UpdateCamera(void) +{ + SDL_LockRWLockForReading(camera_driver.device_hash_lock); + SDL_PendingCameraDeviceEvent *pending_events = camera_driver.pending_events.next; + SDL_UnlockRWLock(camera_driver.device_hash_lock); + + if (!pending_events) { + return; // nothing to do, check next time. + } + + // okay, let's take this whole list of events so we can dump the lock, and new ones can queue up for a later update. + SDL_LockRWLockForWriting(camera_driver.device_hash_lock); + pending_events = camera_driver.pending_events.next; // in case this changed... + camera_driver.pending_events.next = NULL; + camera_driver.pending_events_tail = &camera_driver.pending_events; + SDL_UnlockRWLock(camera_driver.device_hash_lock); + + SDL_PendingCameraDeviceEvent *pending_next = NULL; + for (SDL_PendingCameraDeviceEvent *i = pending_events; i; i = pending_next) { + pending_next = i->next; + if (SDL_EventEnabled(i->type)) { + SDL_Event event; + SDL_zero(event); + event.type = i->type; + event.adevice.which = (Uint32) i->devid; + SDL_PushEvent(&event); + } + SDL_free(i); + } +} + diff --git a/src/camera/SDL_camera_c.h b/src/camera/SDL_camera_c.h index 56921ab8a0b25..6fae3101df737 100644 --- a/src/camera/SDL_camera_c.h +++ b/src/camera/SDL_camera_c.h @@ -29,4 +29,7 @@ int SDL_CameraInit(const char *driver_name); // Shutdown the camera subsystem void SDL_QuitCamera(void); +// "Pump" the event queue. +extern void SDL_UpdateCamera(void); + #endif // SDL_camera_c_h_ diff --git a/src/camera/SDL_syscamera.h b/src/camera/SDL_syscamera.h index 6a7c56356760b..a48a1529830d7 100644 --- a/src/camera/SDL_syscamera.h +++ b/src/camera/SDL_syscamera.h @@ -23,67 +23,141 @@ #ifndef SDL_syscamera_h_ #define SDL_syscamera_h_ -#include "../SDL_list.h" +#include "../SDL_hashtable.h" #define DEBUG_CAMERA 1 -// The SDL camera driver + +// !!! FIXME: update these drivers! +#ifdef SDL_CAMERA_DRIVER_COREMEDIA +#undef SDL_CAMERA_DRIVER_COREMEDIA +#endif +#ifdef SDL_CAMERA_DRIVER_ANDROID +#undef SDL_CAMERA_DRIVER_ANDROID +#endif + typedef struct SDL_CameraDevice SDL_CameraDevice; +/* Backends should call this as devices are added to the system (such as + a USB camera being plugged in), and should also be called for + for every device found during DetectDevices(). */ +extern SDL_CameraDevice *SDL_AddCameraDevice(const char *name, int num_specs, const SDL_CameraSpec *specs, void *handle); + +/* Backends should call this if an opened camera device is lost. + This can happen due to i/o errors, or a device being unplugged, etc. */ +extern void SDL_CameraDeviceDisconnected(SDL_CameraDevice *device); + +// Find an SDL_CameraDevice, selected by a callback. NULL if not found. DOES NOT LOCK THE DEVICE. +extern SDL_CameraDevice *SDL_FindPhysicalCameraDeviceByCallback(SDL_bool (*callback)(SDL_CameraDevice *device, void *userdata), void *userdata); + +// These functions are the heart of the camera threads. Backends can call them directly if they aren't using the SDL-provided thread. +extern void SDL_CameraThreadSetup(SDL_CameraDevice *device); +extern SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device); +extern void SDL_CameraThreadShutdown(SDL_CameraDevice *device); + +typedef struct SurfaceList +{ + SDL_Surface *surface; + Uint64 timestampNS; + struct SurfaceList *next; +} SurfaceList; + // Define the SDL camera driver structure struct SDL_CameraDevice { - // The device's current camera specification + // A mutex for locking + SDL_Mutex *lock; + + // Human-readable device name. + char *name; + + // When refcount hits zero, we destroy the device object. + SDL_AtomicInt refcount; + + // All supported formats/dimensions for this device. + SDL_CameraSpec *all_specs; + + // Elements in all_specs. + int num_specs; + + // The device's actual specification that the camera is outputting, before conversion. + SDL_CameraSpec actual_spec; + + // The device's current camera specification, after conversions. SDL_CameraSpec spec; - // Device name - char *dev_name; + // Unique value assigned at creation time. + SDL_CameraDeviceID instance_id; + + // Driver-specific hardware data on how to open device (`hidden` is driver-specific data _when opened_). + void *handle; + + // Pixel data flows from the driver into these, then gets converted for the app if necessary. + SDL_Surface *acquire_surface; + + // acquire_surface converts or scales to this surface before landing in output_surfaces, if necessary. + SDL_Surface *conversion_surface; + + // A queue of surfaces that buffer converted/scaled frames of video until the app claims them. + SurfaceList output_surfaces[8]; + SurfaceList filled_output_surfaces; // this is FIFO + SurfaceList empty_output_surfaces; // this is LIFO + SurfaceList app_held_output_surfaces; + + // non-zero if acquire_surface needs to be scaled for final output. + int needs_scaling; // -1: downscale, 0: no scaling, 1: upscale + + // SDL_TRUE if acquire_surface needs to be converted for final output. + SDL_bool needs_conversion; // Current state flags SDL_AtomicInt shutdown; - SDL_AtomicInt enabled; - SDL_bool is_spec_set; - - // A mutex for locking the queue buffers - SDL_Mutex *device_lock; - SDL_Mutex *acquiring_lock; + SDL_AtomicInt zombie; // A thread to feed the camera device SDL_Thread *thread; - SDL_ThreadID threadid; - // Queued buffers (if app not using callback). - SDL_ListNode *buffer_queue; + // Optional properties. + SDL_PropertiesID props; - // Data private to this driver + // Data private to this driver, used when device is opened and running. struct SDL_PrivateCameraData *hidden; }; typedef struct SDL_CameraDriverImpl { void (*DetectDevices)(void); - int (*OpenDevice)(SDL_CameraDevice *_this); - void (*CloseDevice)(SDL_CameraDevice *_this); - int (*InitDevice)(SDL_CameraDevice *_this); - int (*GetDeviceSpec)(SDL_CameraDevice *_this, SDL_CameraSpec *spec); - int (*StartCamera)(SDL_CameraDevice *_this); - int (*StopCamera)(SDL_CameraDevice *_this); - int (*AcquireFrame)(SDL_CameraDevice *_this, SDL_CameraFrame *frame); - int (*ReleaseFrame)(SDL_CameraDevice *_this, SDL_CameraFrame *frame); - int (*GetNumFormats)(SDL_CameraDevice *_this); - int (*GetFormat)(SDL_CameraDevice *_this, int index, Uint32 *format); - int (*GetNumFrameSizes)(SDL_CameraDevice *_this, Uint32 format); - int (*GetFrameSize)(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height); - int (*GetDeviceName)(SDL_CameraDeviceID instance_id, char *buf, int size); - SDL_CameraDeviceID *(*GetDevices)(int *count); + int (*OpenDevice)(SDL_CameraDevice *device, const SDL_CameraSpec *spec); + void (*CloseDevice)(SDL_CameraDevice *device); + int (*WaitDevice)(SDL_CameraDevice *device); + int (*AcquireFrame)(SDL_CameraDevice *device, SDL_Surface *frame, Uint64 *timestampNS); // set frame->pixels, frame->pitch, and *timestampNS! + void (*ReleaseFrame)(SDL_CameraDevice *device, SDL_Surface *frame); // Reclaim frame->pixels and frame->pitch! + void (*FreeDeviceHandle)(SDL_CameraDevice *device); // SDL is done with this device; free the handle from SDL_AddCameraDevice() void (*Deinitialize)(void); + + SDL_bool ProvidesOwnCallbackThread; } SDL_CameraDriverImpl; +typedef struct SDL_PendingCameraDeviceEvent +{ + Uint32 type; + SDL_CameraDeviceID devid; + struct SDL_PendingCameraDeviceEvent *next; +} SDL_PendingCameraDeviceEvent; + typedef struct SDL_CameraDriver { const char *name; // The name of this camera driver const char *desc; // The description of this camera driver SDL_CameraDriverImpl impl; // the backend's interface + + SDL_RWLock *device_hash_lock; // A rwlock that protects `device_hash` + SDL_HashTable *device_hash; // the collection of currently-available camera devices + SDL_PendingCameraDeviceEvent pending_events; + SDL_PendingCameraDeviceEvent *pending_events_tail; + + SDL_AtomicInt device_count; + SDL_AtomicInt shutting_down; // non-zero during SDL_Quit, so we known not to accept any last-minute device hotplugs. } SDL_CameraDriver; typedef struct CameraBootStrap diff --git a/src/camera/coremedia/SDL_camera_coremedia.m b/src/camera/coremedia/SDL_camera_coremedia.m index f5ade8ca0d58c..69f2815ed3535 100644 --- a/src/camera/coremedia/SDL_camera_coremedia.m +++ b/src/camera/coremedia/SDL_camera_coremedia.m @@ -135,7 +135,9 @@ static Uint32 nsfourcc_to_sdlformat(NSString *nsfourcc) if (SDL_strcmp("yuvs", str) == 0) return SDL_PIXELFORMAT_UYVY; if (SDL_strcmp("420f", str) == 0) return SDL_PIXELFORMAT_UNKNOWN; - SDL_Log("Unknown format '%s'", str); + #if DEBUG_CAMERA + SDL_Log("CAMERA: Unknown format '%s'", str); + #endif return SDL_PIXELFORMAT_UNKNOWN; } @@ -177,8 +179,9 @@ - (void) captureOutput:(AVCaptureOutput *)output - (void)captureOutput:(AVCaptureOutput *)output didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { - // !!! FIXME #if DEBUG_CAMERA - SDL_Log("Drop frame.."); + #if DEBUG_CAMERA + SDL_Log("CAMERA: Drop frame.."); + #endif } @end @@ -362,13 +365,13 @@ static int COREMEDIA_AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *fram const int numPlanes = CVPixelBufferGetPlaneCount(image); const int planar = CVPixelBufferIsPlanar(image); -#if 0 + #if DEBUG_CAMERA const int w = CVPixelBufferGetWidth(image); const int h = CVPixelBufferGetHeight(image); const int sz = CVPixelBufferGetDataSize(image); const int pitch = CVPixelBufferGetBytesPerRow(image); - SDL_Log("buffer planar=%d count:%d %d x %d sz=%d pitch=%d", planar, numPlanes, w, h, sz, pitch); -#endif + SDL_Log("CAMERA: buffer planar=%d count:%d %d x %d sz=%d pitch=%d", planar, numPlanes, w, h, sz, pitch); + #endif CVPixelBufferLockBaseAddress(image, 0); diff --git a/src/camera/dummy/SDL_camera_dummy.c b/src/camera/dummy/SDL_camera_dummy.c new file mode 100644 index 0000000000000..1dcdd295858b3 --- /dev/null +++ b/src/camera/dummy/SDL_camera_dummy.c @@ -0,0 +1,80 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_CAMERA_DRIVER_DUMMY + +#include "../SDL_syscamera.h" + +static int DUMMYCAMERA_OpenDevice(SDL_CameraDevice *device, const SDL_CameraSpec *spec) +{ + return SDL_Unsupported(); +} + +static void DUMMYCAMERA_CloseDevice(SDL_CameraDevice *device) +{ +} + +static int DUMMYCAMERA_WaitDevice(SDL_CameraDevice *device) +{ + return SDL_Unsupported(); +} + +static int DUMMYCAMERA_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame, Uint64 *timestampNS) +{ + return SDL_Unsupported(); +} + +static void DUMMYCAMERA_ReleaseFrame(SDL_CameraDevice *device, SDL_Surface *frame) +{ +} + +static void DUMMYCAMERA_DetectDevices(void) +{ +} + +static void DUMMYCAMERA_FreeDeviceHandle(SDL_CameraDevice *device) +{ +} + +static void DUMMYCAMERA_Deinitialize(void) +{ +} + +static SDL_bool DUMMYCAMERA_Init(SDL_CameraDriverImpl *impl) +{ + impl->DetectDevices = DUMMYCAMERA_DetectDevices; + impl->OpenDevice = DUMMYCAMERA_OpenDevice; + impl->CloseDevice = DUMMYCAMERA_CloseDevice; + impl->WaitDevice = DUMMYCAMERA_WaitDevice; + impl->AcquireFrame = DUMMYCAMERA_AcquireFrame; + impl->ReleaseFrame = DUMMYCAMERA_ReleaseFrame; + impl->FreeDeviceHandle = DUMMYCAMERA_FreeDeviceHandle; + impl->Deinitialize = DUMMYCAMERA_Deinitialize; + + return SDL_TRUE; +} + +CameraBootStrap DUMMYCAMERA_bootstrap = { + "dummy", "SDL dummy camera driver", DUMMYCAMERA_Init, SDL_TRUE +}; + +#endif diff --git a/src/camera/v4l2/SDL_camera_v4l2.c b/src/camera/v4l2/SDL_camera_v4l2.c index f4f484666eb0b..0eebc71960af4 100644 --- a/src/camera/v4l2/SDL_camera_v4l2.c +++ b/src/camera/v4l2/SDL_camera_v4l2.c @@ -22,43 +22,38 @@ #ifdef SDL_CAMERA_DRIVER_V4L2 +#include +#include +#include // low-level i/o +#include +#include +#include +#include + #include "../SDL_syscamera.h" #include "../SDL_camera_c.h" #include "../../video/SDL_pixels_c.h" #include "../../thread/SDL_systhread.h" #include "../../core/linux/SDL_evdev_capabilities.h" #include "../../core/linux/SDL_udev.h" -#include // INT_MAX - -#define MAX_CAMERA_DEVICES 128 // It's doubtful someone has more than that -static int MaybeAddDevice(const char *path); -#ifdef SDL_USE_LIBUDEV -static int MaybeRemoveDevice(const char *path); -static void CameraUdevCallback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath); -#endif // SDL_USE_LIBUDEV +#ifndef SDL_USE_LIBUDEV +#include +#endif -// List of available camera devices. -typedef struct SDL_cameralist_item +typedef struct V4L2DeviceHandle { - char *fname; // Dev path name (like /dev/video0) - char *bus_info; // don't add two paths with same bus_info (eg /dev/video0 and /dev/video1 - SDL_CameraDeviceID instance_id; - SDL_CameraDevice *device; // Associated device - struct SDL_cameralist_item *next; -} SDL_cameralist_item; + char *bus_info; + char *path; +} V4L2DeviceHandle; -static SDL_cameralist_item *SDL_cameralist = NULL; -static SDL_cameralist_item *SDL_cameralist_tail = NULL; -static int num_cameras = 0; - - -enum io_method { +typedef enum io_method { + IO_METHOD_INVALID, IO_METHOD_READ, IO_METHOD_MMAP, IO_METHOD_USERPTR -}; +} io_method; struct buffer { void *start; @@ -69,21 +64,13 @@ struct buffer { struct SDL_PrivateCameraData { int fd; - enum io_method io; + io_method io; int nb_buffers; struct buffer *buffers; int first_start; int driver_pitch; }; -#include -#include -#include // low-level i/o -#include -#include -#include -#include - static int xioctl(int fh, int request, void *arg) { int r; @@ -95,17 +82,40 @@ static int xioctl(int fh, int request, void *arg) return r; } -// -1:error 1:frame 0:no frame -static int acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +static int V4L2_WaitDevice(SDL_CameraDevice *device) { - const int fd = _this->hidden->fd; - enum io_method io = _this->hidden->io; - size_t size = _this->hidden->buffers[0].length; + const int fd = device->hidden->fd; + + int retval; + + do { + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100 * 1000; + + retval = select(fd + 1, &fds, NULL, NULL, &tv); + if ((retval == -1) && (errno == EINTR)) { + retval = 0; // pretend it was a timeout, keep looping. + } + } while (retval == 0); + + return retval; +} + +static int V4L2_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame, Uint64 *timestampNS) +{ + const int fd = device->hidden->fd; + const io_method io = device->hidden->io; + size_t size = device->hidden->buffers[0].length; struct v4l2_buffer buf; switch (io) { case IO_METHOD_READ: - if (read(fd, _this->hidden->buffers[0].start, size) == -1) { + if (read(fd, device->hidden->buffers[0].start, size) == -1) { switch (errno) { case EAGAIN: return 0; @@ -119,9 +129,8 @@ static int acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) } } - frame->num_planes = 1; - frame->data[0] = _this->hidden->buffers[0].start; - frame->pitch[0] = _this->hidden->driver_pitch; + frame->pixels = device->hidden->buffers[0].start; + frame->pitch = device->hidden->driver_pitch; break; case IO_METHOD_MMAP: @@ -144,18 +153,17 @@ static int acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) } } - if ((int)buf.index < 0 || (int)buf.index >= _this->hidden->nb_buffers) { + if ((int)buf.index < 0 || (int)buf.index >= device->hidden->nb_buffers) { return SDL_SetError("invalid buffer index"); } - frame->num_planes = 1; - frame->data[0] = _this->hidden->buffers[buf.index].start; - frame->pitch[0] = _this->hidden->driver_pitch; - _this->hidden->buffers[buf.index].available = 1; + frame->pixels = device->hidden->buffers[buf.index].start; + frame->pitch = device->hidden->driver_pitch; + device->hidden->buffers[buf.index].available = 1; -#if DEBUG_CAMERA - SDL_Log("debug mmap: image %d/%d num_planes:%d data[0]=%p", buf.index, _this->hidden->nb_buffers, frame->num_planes, (void*)frame->data[0]); -#endif + #if DEBUG_CAMERA + SDL_Log("CAMERA: debug mmap: image %d/%d data[0]=%p", buf.index, device->hidden->nb_buffers, (void*)frame->pixels); + #endif break; case IO_METHOD_USERPTR: @@ -180,45 +188,49 @@ static int acquire_frame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) } int i; - for (i = 0; i < _this->hidden->nb_buffers; ++i) { - if (buf.m.userptr == (unsigned long)_this->hidden->buffers[i].start && buf.length == size) { + for (i = 0; i < device->hidden->nb_buffers; ++i) { + if (buf.m.userptr == (unsigned long)device->hidden->buffers[i].start && buf.length == size) { break; } } - if (i >= _this->hidden->nb_buffers) { + if (i >= device->hidden->nb_buffers) { return SDL_SetError("invalid buffer index"); } - frame->num_planes = 1; - frame->data[0] = (void*)buf.m.userptr; - frame->pitch[0] = _this->hidden->driver_pitch; - _this->hidden->buffers[i].available = 1; -#if DEBUG_CAMERA - SDL_Log("debug userptr: image %d/%d num_planes:%d data[0]=%p", buf.index, _this->hidden->nb_buffers, frame->num_planes, (void*)frame->data[0]); -#endif + frame->pixels = (void*)buf.m.userptr; + frame->pitch = device->hidden->driver_pitch; + device->hidden->buffers[i].available = 1; + + #if DEBUG_CAMERA + SDL_Log("CAMERA: debug userptr: image %d/%d data[0]=%p", buf.index, device->hidden->nb_buffers, (void*)frame->pixels); + #endif + break; + + case IO_METHOD_INVALID: + SDL_assert(!"Shouldn't have hit this"); break; } + *timestampNS = SDL_GetTicksNS(); // !!! FIXME: can we get this info more accurately from v4l2? return 1; } - -static int V4L2_ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +static void V4L2_ReleaseFrame(SDL_CameraDevice *device, SDL_Surface *frame) { struct v4l2_buffer buf; - const int fd = _this->hidden->fd; - enum io_method io = _this->hidden->io; + const int fd = device->hidden->fd; + const io_method io = device->hidden->io; int i; - for (i = 0; i < _this->hidden->nb_buffers; ++i) { - if (frame->num_planes && frame->data[0] == _this->hidden->buffers[i].start) { + for (i = 0; i < device->hidden->nb_buffers; ++i) { + if (frame->pixels == device->hidden->buffers[i].start) { break; } } - if (i >= _this->hidden->nb_buffers) { - return SDL_SetError("invalid buffer index"); + if (i >= device->hidden->nb_buffers) { + return; // oh well, we didn't own this. } switch (io) { @@ -233,9 +245,10 @@ static int V4L2_ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) buf.index = i; if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) { - return SDL_SetError("VIDIOC_QBUF"); + // !!! FIXME: disconnect the device. + return; //SDL_SetError("VIDIOC_QBUF"); } - _this->hidden->buffers[i].available = 0; + device->hidden->buffers[i].available = 0; break; case IO_METHOD_USERPTR: @@ -244,102 +257,33 @@ static int V4L2_ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_USERPTR; buf.index = i; - buf.m.userptr = (unsigned long)frame->data[0]; - buf.length = (int) _this->hidden->buffers[i].length; + buf.m.userptr = (unsigned long)frame->pixels; + buf.length = (int) device->hidden->buffers[i].length; if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) { - return SDL_SetError("VIDIOC_QBUF"); + // !!! FIXME: disconnect the device. + return; //SDL_SetError("VIDIOC_QBUF"); } - _this->hidden->buffers[i].available = 0; - break; - } - - return 0; -} - -static int V4L2_AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) -{ - fd_set fds; - struct timeval tv; - - const int fd = _this->hidden->fd; - - FD_ZERO(&fds); - FD_SET(fd, &fds); - - // Timeout. - tv.tv_sec = 0; - tv.tv_usec = 300 * 1000; - - int retval = select(fd + 1, &fds, NULL, NULL, &tv); - - if (retval == -1) { - if (errno == EINTR) { -#if DEBUG_CAMERA - SDL_Log("continue .."); -#endif - return 0; - } - return SDL_SetError("select"); - } - - if (retval == 0) { - // Timeout. Not an error - SDL_SetError("timeout select"); - return 0; - } - - retval = acquire_frame(_this, frame); - if (retval < 0) { - return -1; - } - - if (retval == 1){ - frame->timestampNS = SDL_GetTicksNS(); - } else if (retval == 0) { -#if DEBUG_CAMERA - SDL_Log("No frame continue: %s", SDL_GetError()); -#endif - } - - // EAGAIN - continue select loop. - return 0; -} - - -static int V4L2_StopCamera(SDL_CameraDevice *_this) -{ - enum v4l2_buf_type type; - const int fd = _this->hidden->fd; - enum io_method io = _this->hidden->io; - - switch (io) { - case IO_METHOD_READ: + device->hidden->buffers[i].available = 0; break; - case IO_METHOD_MMAP: - case IO_METHOD_USERPTR: - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (xioctl(fd, VIDIOC_STREAMOFF, &type) == -1) { - return SDL_SetError("VIDIOC_STREAMOFF"); - } + case IO_METHOD_INVALID: + SDL_assert(!"Shouldn't have hit this"); break; } - - return 0; } -static int EnqueueBuffers(SDL_CameraDevice *_this) +static int EnqueueBuffers(SDL_CameraDevice *device) { - const int fd = _this->hidden->fd; - enum io_method io = _this->hidden->io; + const int fd = device->hidden->fd; + const io_method io = device->hidden->io; switch (io) { case IO_METHOD_READ: break; case IO_METHOD_MMAP: - for (int i = 0; i < _this->hidden->nb_buffers; ++i) { - if (_this->hidden->buffers[i].available == 0) { + for (int i = 0; i < device->hidden->nb_buffers; ++i) { + if (device->hidden->buffers[i].available == 0) { struct v4l2_buffer buf; SDL_zero(buf); @@ -355,16 +299,16 @@ static int EnqueueBuffers(SDL_CameraDevice *_this) break; case IO_METHOD_USERPTR: - for (int i = 0; i < _this->hidden->nb_buffers; ++i) { - if (_this->hidden->buffers[i].available == 0) { + for (int i = 0; i < device->hidden->nb_buffers; ++i) { + if (device->hidden->buffers[i].available == 0) { struct v4l2_buffer buf; SDL_zero(buf); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_USERPTR; buf.index = i; - buf.m.userptr = (unsigned long)_this->hidden->buffers[i].start; - buf.length = (int) _this->hidden->buffers[i].length; + buf.m.userptr = (unsigned long)device->hidden->buffers[i].start; + buf.length = (int) device->hidden->buffers[i].length; if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) { return SDL_SetError("VIDIOC_QBUF"); @@ -372,115 +316,24 @@ static int EnqueueBuffers(SDL_CameraDevice *_this) } } break; - } - return 0; -} - -static int PreEnqueueBuffers(SDL_CameraDevice *_this) -{ - struct v4l2_requestbuffers req; - const int fd = _this->hidden->fd; - enum io_method io = _this->hidden->io; - - switch (io) { - case IO_METHOD_READ: - break; - - case IO_METHOD_MMAP: - SDL_zero(req); - req.count = _this->hidden->nb_buffers; - req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req.memory = V4L2_MEMORY_MMAP; - - if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) { - if (errno == EINVAL) { - return SDL_SetError("Does not support memory mapping"); - } else { - return SDL_SetError("VIDIOC_REQBUFS"); - } - } - if (req.count < 2) { - return SDL_SetError("Insufficient buffer memory"); - } - - _this->hidden->nb_buffers = req.count; - break; - - case IO_METHOD_USERPTR: - SDL_zero(req); - req.count = _this->hidden->nb_buffers; - req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req.memory = V4L2_MEMORY_USERPTR; - - if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) { - if (errno == EINVAL) { - return SDL_SetError("Does not support user pointer i/o"); - } else { - return SDL_SetError("VIDIOC_REQBUFS"); - } - } - break; + case IO_METHOD_INVALID: SDL_assert(!"Shouldn't have hit this"); break; } return 0; } -static int V4L2_StartCamera(SDL_CameraDevice *_this) +static int AllocBufferRead(SDL_CameraDevice *device, size_t buffer_size) { - enum v4l2_buf_type type; - - const int fd = _this->hidden->fd; - enum io_method io = _this->hidden->io; - - - if (_this->hidden->first_start == 0) { - _this->hidden->first_start = 1; - } else { - const int old = _this->hidden->nb_buffers; - // TODO mmap; doesn't work with stop->start -#if 1 - // Can change nb_buffers for mmap - if (PreEnqueueBuffers(_this) < 0) { - return -1; - } else if (old != _this->hidden->nb_buffers) { - return SDL_SetError("different nb of buffers requested"); - } -#endif - _this->hidden->first_start = 1; - } - - if (EnqueueBuffers(_this) < 0) { - return -1; - } - - switch (io) { - case IO_METHOD_READ: - break; - - case IO_METHOD_MMAP: - case IO_METHOD_USERPTR: - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (xioctl(fd, VIDIOC_STREAMON, &type) == -1) { - return SDL_SetError("VIDIOC_STREAMON"); - } - break; - } - - return 0; -} - -static int AllocBufferRead(SDL_CameraDevice *_this, size_t buffer_size) -{ - _this->hidden->buffers[0].length = buffer_size; - _this->hidden->buffers[0].start = SDL_calloc(1, buffer_size); - return _this->hidden->buffers[0].start ? 0 : -1; + device->hidden->buffers[0].length = buffer_size; + device->hidden->buffers[0].start = SDL_calloc(1, buffer_size); + return device->hidden->buffers[0].start ? 0 : -1; } -static int AllocBufferMmap(SDL_CameraDevice *_this) +static int AllocBufferMmap(SDL_CameraDevice *device) { - int fd = _this->hidden->fd; + const int fd = device->hidden->fd; int i; - for (i = 0; i < _this->hidden->nb_buffers; ++i) { + for (i = 0; i < device->hidden->nb_buffers; ++i) { struct v4l2_buffer buf; SDL_zero(buf); @@ -493,29 +346,29 @@ static int AllocBufferMmap(SDL_CameraDevice *_this) return SDL_SetError("VIDIOC_QUERYBUF"); } - _this->hidden->buffers[i].length = buf.length; - _this->hidden->buffers[i].start = + device->hidden->buffers[i].length = buf.length; + device->hidden->buffers[i].start = mmap(NULL /* start anywhere */, buf.length, PROT_READ | PROT_WRITE /* required */, MAP_SHARED /* recommended */, fd, buf.m.offset); - if (MAP_FAILED == _this->hidden->buffers[i].start) { + if (MAP_FAILED == device->hidden->buffers[i].start) { return SDL_SetError("mmap"); } } return 0; } -static int AllocBufferUserPtr(SDL_CameraDevice *_this, size_t buffer_size) +static int AllocBufferUserPtr(SDL_CameraDevice *device, size_t buffer_size) { int i; - for (i = 0; i < _this->hidden->nb_buffers; ++i) { - _this->hidden->buffers[i].length = buffer_size; - _this->hidden->buffers[i].start = SDL_calloc(1, buffer_size); + for (i = 0; i < device->hidden->nb_buffers; ++i) { + device->hidden->buffers[i].length = buffer_size; + device->hidden->buffers[i].start = SDL_calloc(1, buffer_size); - if (!_this->hidden->buffers[i].start) { + if (!device->hidden->buffers[i].start) { return -1; } } @@ -530,7 +383,9 @@ static Uint32 format_v4l2_to_sdl(Uint32 fmt) CASE(V4L2_PIX_FMT_MJPEG, SDL_PIXELFORMAT_UNKNOWN); #undef CASE default: - SDL_Log("Unknown format V4L2_PIX_FORMAT '%d'", fmt); + #if DEBUG_CAMERA + SDL_Log("CAMERA: Unknown format V4L2_PIX_FORMAT '%d'", fmt); + #endif return SDL_PIXELFORMAT_UNKNOWN; } } @@ -547,599 +402,442 @@ static Uint32 format_sdl_to_v4l2(Uint32 fmt) } } -static int V4L2_GetNumFormats(SDL_CameraDevice *_this) +static void V4L2_CloseDevice(SDL_CameraDevice *device) { - int fd = _this->hidden->fd; - int i = 0; - struct v4l2_fmtdesc fmtdesc; - - SDL_zero(fmtdesc); - fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - while (ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) == 0) { - fmtdesc.index++; - i++; - } - return i; -} - -static int V4L2_GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) -{ - int fd = _this->hidden->fd; - struct v4l2_fmtdesc fmtdesc; - - SDL_zero(fmtdesc); - fmtdesc.index = index; - fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc) == 0) { - *format = format_v4l2_to_sdl(fmtdesc.pixelformat); - -#if DEBUG_CAMERA - if (fmtdesc.flags & V4L2_FMT_FLAG_EMULATED) { - SDL_Log("%s format emulated", SDL_GetPixelFormatName(*format)); - } - if (fmtdesc.flags & V4L2_FMT_FLAG_COMPRESSED) { - SDL_Log("%s format compressed", SDL_GetPixelFormatName(*format)); - } -#endif - return 0; + if (!device) { + return; } - return -1; -} + if (device->hidden) { + const io_method io = device->hidden->io; + const int fd = device->hidden->fd; -static int V4L2_GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) -{ - int fd = _this->hidden->fd; - int i = 0; - struct v4l2_frmsizeenum frmsizeenum; - - SDL_zero(frmsizeenum); - frmsizeenum.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - frmsizeenum.pixel_format = format_sdl_to_v4l2(format); - while (ioctl(fd,VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == 0) { - frmsizeenum.index++; - if (frmsizeenum.type == V4L2_FRMSIZE_TYPE_DISCRETE) { - i++; - } else if (frmsizeenum.type == V4L2_FRMSIZE_TYPE_STEPWISE) { - i += (1 + (frmsizeenum.stepwise.max_width - frmsizeenum.stepwise.min_width) / frmsizeenum.stepwise.step_width) - * (1 + (frmsizeenum.stepwise.max_height - frmsizeenum.stepwise.min_height) / frmsizeenum.stepwise.step_height); - } else if (frmsizeenum.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) { - SDL_SetError("V4L2_FRMSIZE_TYPE_CONTINUOUS not handled"); + if ((io == IO_METHOD_MMAP) || (io == IO_METHOD_USERPTR)) { + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + xioctl(fd, VIDIOC_STREAMOFF, &type); } - } - return i; -} - -static int V4L2_GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) -{ - int fd = _this->hidden->fd; - struct v4l2_frmsizeenum frmsizeenum; - int i = 0; - - SDL_zero(frmsizeenum); - frmsizeenum.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - frmsizeenum.pixel_format = format_sdl_to_v4l2(format); - while (ioctl(fd,VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == 0) { - frmsizeenum.index++; - - if (frmsizeenum.type == V4L2_FRMSIZE_TYPE_DISCRETE) { - if (i == index) { - *width = frmsizeenum.discrete.width; - *height = frmsizeenum.discrete.height; - return 0; - } - i++; - } else if (frmsizeenum.type == V4L2_FRMSIZE_TYPE_STEPWISE) { - unsigned int w; - for (w = frmsizeenum.stepwise.min_width; w <= frmsizeenum.stepwise.max_width; w += frmsizeenum.stepwise.step_width) { - unsigned int h; - for (h = frmsizeenum.stepwise.min_height; h <= frmsizeenum.stepwise.max_height; h += frmsizeenum.stepwise.step_height) { - if (i == index) { - *width = h; - *height = w; - return 0; - } - i++; - } - } - } else if (frmsizeenum.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) { - } - } - - return -1; -} -static void dbg_v4l2_pixelformat(const char *str, int f) -{ - SDL_Log("%s V4L2_format=%d %c%c%c%c", str, f, - (f >> 0) & 0xff, - (f >> 8) & 0xff, - (f >> 16) & 0xff, - (f >> 24) & 0xff); -} -#endif - -static int V4L2_GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) -{ - struct v4l2_format fmt; - int fd = _this->hidden->fd; - unsigned int min; - - SDL_zero(fmt); - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - // Preserve original settings as set by v4l2-ctl for example - if (xioctl(fd, VIDIOC_G_FMT, &fmt) == -1) { - return SDL_SetError("Error VIDIOC_G_FMT"); - } - // Buggy driver paranoia. - min = fmt.fmt.pix.width * 2; - if (fmt.fmt.pix.bytesperline < min) { - fmt.fmt.pix.bytesperline = min; - } - min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; - if (fmt.fmt.pix.sizeimage < min) { - fmt.fmt.pix.sizeimage = min; - } - - //spec->width = fmt.fmt.pix.width; - //spec->height = fmt.fmt.pix.height; - _this->hidden->driver_pitch = fmt.fmt.pix.bytesperline; - //spec->format = format_v4l2_to_sdl(fmt.fmt.pix.pixelformat); - - return 0; -} - -static int V4L2_InitDevice(SDL_CameraDevice *_this) -{ - struct v4l2_cropcap cropcap; - struct v4l2_crop crop; - - int fd = _this->hidden->fd; - enum io_method io = _this->hidden->io; - int retval = -1; - - // Select video input, video standard and tune here. - SDL_zero(cropcap); - - cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if (xioctl(fd, VIDIOC_CROPCAP, &cropcap) == 0) { - crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - crop.c = cropcap.defrect; // reset to default - - if (xioctl(fd, VIDIOC_S_CROP, &crop) == -1) { - switch (errno) { - case EINVAL: - // Cropping not supported. - break; - default: - // Errors ignored. + if (device->hidden->buffers) { + switch (io) { + case IO_METHOD_INVALID: break; - } - } - } else { - // Errors ignored. - } - - - { - struct v4l2_format fmt; - SDL_zero(fmt); - - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt.fmt.pix.width = _this->spec.width; - fmt.fmt.pix.height = _this->spec.height; - - - fmt.fmt.pix.pixelformat = format_sdl_to_v4l2(_this->spec.format); - // fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; - fmt.fmt.pix.field = V4L2_FIELD_ANY; - -#if DEBUG_CAMERA - SDL_Log("set SDL format %s", SDL_GetPixelFormatName(_this->spec.format)); - dbg_v4l2_pixelformat("set format", fmt.fmt.pix.pixelformat); -#endif - - if (xioctl(fd, VIDIOC_S_FMT, &fmt) == -1) { - return SDL_SetError("Error VIDIOC_S_FMT"); - } - } - - V4L2_GetDeviceSpec(_this, &_this->spec); - - if (PreEnqueueBuffers(_this) < 0) { - return -1; - } - - { - _this->hidden->buffers = SDL_calloc(_this->hidden->nb_buffers, sizeof(*_this->hidden->buffers)); - if (!_this->hidden->buffers) { - return -1; - } - } - - { - size_t size, pitch; - SDL_CalculateSize(_this->spec.format, _this->spec.width, _this->spec.height, &size, &pitch, SDL_FALSE); - - switch (io) { - case IO_METHOD_READ: - retval = AllocBufferRead(_this, size); - break; - - case IO_METHOD_MMAP: - retval = AllocBufferMmap(_this); - break; - - case IO_METHOD_USERPTR: - retval = AllocBufferUserPtr(_this, size); - break; - } - } - - return (retval < 0) ? -1 : 0; -} - -static void V4L2_CloseDevice(SDL_CameraDevice *_this) -{ - if (!_this) { - return; - } - - if (_this->hidden) { - if (_this->hidden->buffers) { - enum io_method io = _this->hidden->io; - switch (io) { case IO_METHOD_READ: - SDL_free(_this->hidden->buffers[0].start); + SDL_free(device->hidden->buffers[0].start); break; case IO_METHOD_MMAP: - for (int i = 0; i < _this->hidden->nb_buffers; ++i) { - if (munmap(_this->hidden->buffers[i].start, _this->hidden->buffers[i].length) == -1) { + for (int i = 0; i < device->hidden->nb_buffers; ++i) { + if (munmap(device->hidden->buffers[i].start, device->hidden->buffers[i].length) == -1) { SDL_SetError("munmap"); } } break; case IO_METHOD_USERPTR: - for (int i = 0; i < _this->hidden->nb_buffers; ++i) { - SDL_free(_this->hidden->buffers[i].start); + for (int i = 0; i < device->hidden->nb_buffers; ++i) { + SDL_free(device->hidden->buffers[i].start); } break; } - SDL_free(_this->hidden->buffers); + SDL_free(device->hidden->buffers); } - if (_this->hidden->fd != -1) { - if (close(_this->hidden->fd)) { - SDL_SetError("close camera device"); // !!! FIXME: we probably won't ever see this error - } + if (fd != -1) { + close(fd); } - SDL_free(_this->hidden); + SDL_free(device->hidden); - _this->hidden = NULL; + device->hidden = NULL; } } -static int V4L2_OpenDevice(SDL_CameraDevice *_this) +static int V4L2_OpenDevice(SDL_CameraDevice *device, const SDL_CameraSpec *spec) { + const V4L2DeviceHandle *handle = (const V4L2DeviceHandle *) device->handle; struct stat st; struct v4l2_capability cap; - enum io_method io; - int fd; + const int fd = open(handle->path, O_RDWR /* required */ | O_NONBLOCK, 0); + + // most of this probably shouldn't fail unless the filesystem node changed out from under us since MaybeAddDevice(). + if (fd == -1) { + return SDL_SetError("Cannot open '%s': %d, %s", handle->path, errno, strerror(errno)); + } else if (fstat(fd, &st) == -1) { + close(fd); + return SDL_SetError("Cannot identify '%s': %d, %s", handle->path, errno, strerror(errno)); + } else if (!S_ISCHR(st.st_mode)) { + close(fd); + return SDL_SetError("%s is not a character device", handle->path); + } else if (xioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) { + const int err = errno; + close(fd); + if (err == EINVAL) { + return SDL_SetError("%s is unexpectedly not a V4L2 device", handle->path); + } + return SDL_SetError("Error VIDIOC_QUERYCAP errno=%d device%s is no V4L2 device", err, handle->path); + } else if ((cap.device_caps & V4L2_CAP_VIDEO_CAPTURE) == 0) { + close(fd); + return SDL_SetError("%s is unexpectedly not a video capture device", handle->path); + } - _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); - if (_this->hidden == NULL) { + device->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); + if (device->hidden == NULL) { + close(fd); return -1; } - _this->hidden->fd = -1; + device->hidden->fd = fd; + device->hidden->io = IO_METHOD_INVALID; - if (stat(_this->dev_name, &st) == -1) { - return SDL_SetError("Cannot identify '%s': %d, %s", _this->dev_name, errno, strerror(errno)); - } else if (!S_ISCHR(st.st_mode)) { - return SDL_SetError("%s is no device", _this->dev_name); - } else if ((fd = open(_this->dev_name, O_RDWR /* required */ | O_NONBLOCK, 0)) == -1) { - return SDL_SetError("Cannot open '%s': %d, %s", _this->dev_name, errno, strerror(errno)); + // Select video input, video standard and tune here. + // errors in the crop code are not fatal. + struct v4l2_cropcap cropcap; + SDL_zero(cropcap); + cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (xioctl(fd, VIDIOC_CROPCAP, &cropcap) == 0) { + struct v4l2_crop crop; + SDL_zero(crop); + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop.c = cropcap.defrect; // reset to default + xioctl(fd, VIDIOC_S_CROP, &crop); } - _this->hidden->fd = fd; - _this->hidden->io = IO_METHOD_MMAP; -// _this->hidden->io = IO_METHOD_USERPTR; -// _this->hidden->io = IO_METHOD_READ; -// - if (_this->hidden->io == IO_METHOD_READ) { - _this->hidden->nb_buffers = 1; - } else { - _this->hidden->nb_buffers = 8; // Number of image as internal buffer, + struct v4l2_format fmt; + SDL_zero(fmt); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = spec->width; + fmt.fmt.pix.height = spec->height; + fmt.fmt.pix.pixelformat = format_sdl_to_v4l2(spec->format); + //fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; + fmt.fmt.pix.field = V4L2_FIELD_ANY; + + #if DEBUG_CAMERA + SDL_Log("CAMERA: set SDL format %s", SDL_GetPixelFormatName(spec->format)); + { const Uint32 f = fmt.fmt.pix.pixelformat; SDL_Log("CAMERA: set format V4L2_format=%d %c%c%c%c", f, (f >> 0) & 0xff, (f >> 8) & 0xff, (f >> 16) & 0xff, (f >> 24) & 0xff); } + #endif + + if (xioctl(fd, VIDIOC_S_FMT, &fmt) == -1) { + return SDL_SetError("Error VIDIOC_S_FMT"); } - io = _this->hidden->io; - if (xioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) { - if (errno == EINVAL) { - return SDL_SetError("%s is no V4L2 device", _this->dev_name); - } else { - return SDL_SetError("Error VIDIOC_QUERYCAP errno=%d device%s is no V4L2 device", errno, _this->dev_name); + SDL_zero(fmt); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (xioctl(fd, VIDIOC_G_FMT, &fmt) == -1) { + return SDL_SetError("Error VIDIOC_G_FMT"); + } + device->hidden->driver_pitch = fmt.fmt.pix.bytesperline; + + io_method io = IO_METHOD_INVALID; + if ((io == IO_METHOD_INVALID) && (cap.device_caps & V4L2_CAP_STREAMING)) { + struct v4l2_requestbuffers req; + SDL_zero(req); + req.count = 8; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + if ((xioctl(fd, VIDIOC_REQBUFS, &req) == 0) && (req.count >= 2)) { + io = IO_METHOD_MMAP; + device->hidden->nb_buffers = req.count; + } else { // mmap didn't work out? Try USERPTR. + SDL_zero(req); + req.count = 8; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_USERPTR; + if (xioctl(fd, VIDIOC_REQBUFS, &req) == 0) { + io = IO_METHOD_USERPTR; + device->hidden->nb_buffers = 8; + } } } - if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { - return SDL_SetError("%s is no video capture device", _this->dev_name); + if ((io == IO_METHOD_INVALID) && (cap.device_caps & V4L2_CAP_READWRITE)) { + io = IO_METHOD_READ; + device->hidden->nb_buffers = 1; } -#if 0 - if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { - SDL_Log("%s is video capture device - single plane", _this->dev_name); + if (io == IO_METHOD_INVALID) { + return SDL_SetError("Don't have a way to talk to this device"); } - if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE)) { - SDL_Log("%s is video capture device - multiple planes", _this->dev_name); + + device->hidden->io = io; + + device->hidden->buffers = SDL_calloc(device->hidden->nb_buffers, sizeof(*device->hidden->buffers)); + if (!device->hidden->buffers) { + return -1; } -#endif + size_t size, pitch; + SDL_CalculateSize(device->spec.format, device->spec.width, device->spec.height, &size, &pitch, SDL_FALSE); + + int rc = 0; switch (io) { case IO_METHOD_READ: - if (!(cap.capabilities & V4L2_CAP_READWRITE)) { - return SDL_SetError("%s does not support read i/o", _this->dev_name); - } + rc = AllocBufferRead(device, size); break; case IO_METHOD_MMAP: + rc = AllocBufferMmap(device); + break; + case IO_METHOD_USERPTR: - if (!(cap.capabilities & V4L2_CAP_STREAMING)) { - return SDL_SetError("%s does not support streaming i/o", _this->dev_name); - } + rc = AllocBufferUserPtr(device, size); break; - } - return 0; -} + case IO_METHOD_INVALID: + SDL_assert(!"Shouldn't have hit this"); + break; + } -static int V4L2_GetDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) -{ - SDL_cameralist_item *item; - for (item = SDL_cameralist; item; item = item->next) { - if (item->instance_id == instance_id) { - SDL_snprintf(buf, size, "%s", item->fname); - return 0; + if (rc < 0) { + return -1; + } else if (EnqueueBuffers(device) < 0) { + return -1; + } else if (io != IO_METHOD_READ) { + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (xioctl(fd, VIDIOC_STREAMON, &type) == -1) { + return SDL_SetError("VIDIOC_STREAMON"); } } - // unknown instance_id - return -1; + return 0; } -static SDL_CameraDeviceID *V4L2_GetDevices(int *count) +static SDL_bool FindV4L2CameraDeviceByBusInfoCallback(SDL_CameraDevice *device, void *userdata) { - // real list of ID - const int num = num_cameras; - SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*retval)); - - if (retval == NULL) { - *count = 0; - return NULL; - } - - int i = 0; - for (SDL_cameralist_item *item = SDL_cameralist; item; item = item->next) { - retval[i++] = item->instance_id; - } - - retval[num] = 0; - *count = num; - return retval; + const V4L2DeviceHandle *handle = (const V4L2DeviceHandle *) device->handle; + return (SDL_strcmp(handle->bus_info, (const char *) userdata) == 0); } +typedef struct SpecAddData +{ + SDL_CameraSpec *specs; + int num_specs; + int allocated_specs; +} SpecAddData; -#ifdef SDL_USE_LIBUDEV -static void CameraUdevCallback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath) +static int AddCameraSpec(SpecAddData *data, Uint32 fmt, int w, int h) { - if (!devpath || !(udev_class & SDL_UDEV_DEVICE_VIDEO_CAPTURE)) { - return; + SDL_assert(data != NULL); + if (data->allocated_specs <= data->num_specs) { + const int newalloc = data->allocated_specs ? (data->allocated_specs * 2) : 16; + void *ptr = SDL_realloc(data->specs, sizeof (SDL_CameraSpec) * newalloc); + if (!ptr) { + return -1; + } + data->specs = (SDL_CameraSpec *) ptr; + data->allocated_specs = newalloc; } - switch (udev_type) { - case SDL_UDEV_DEVICEADDED: - MaybeAddDevice(devpath); - break; - - case SDL_UDEV_DEVICEREMOVED: - MaybeRemoveDevice(devpath); - break; + SDL_CameraSpec *spec = &data->specs[data->num_specs]; + spec->format = fmt; + spec->width = w; + spec->height = h; - default: - break; - } -} -#endif // SDL_USE_LIBUDEV + data->num_specs++; -static SDL_bool DeviceExists(const char *path, const char *bus_info) { - for (SDL_cameralist_item *item = SDL_cameralist; item; item = item->next) { - // found same dev name - if (SDL_strcmp(path, item->fname) == 0) { - return SDL_TRUE; - } - // found same bus_info - if (SDL_strcmp(bus_info, item->bus_info) == 0) { - return SDL_TRUE; - } - } - return SDL_FALSE; + return 0; } -static int MaybeAddDevice(const char *path) +static void MaybeAddDevice(const char *path) { - char *bus_info = NULL; - struct v4l2_capability vcap; - int err; - int fd; - SDL_cameralist_item *item; - if (!path) { - return -1; + return; } - fd = open(path, O_RDWR); - if (fd < 0) { - return -2; // stop iterating /dev/video%d + struct stat st; + const int fd = open(path, O_RDWR /* required */ | O_NONBLOCK, 0); + if (fd == -1) { + return; // can't open it? skip it. + } else if (fstat(fd, &st) == -1) { + close(fd); + return; // can't stat it? skip it. + } else if (!S_ISCHR(st.st_mode)) { + close(fd); + return; // not a character device. } - err = ioctl(fd, VIDIOC_QUERYCAP, &vcap); - close(fd); - if (err) { - return -1; + + struct v4l2_capability vcap; + const int rc = ioctl(fd, VIDIOC_QUERYCAP, &vcap); + if (rc != 0) { + close(fd); + return; // probably not a v4l2 device at all. + } else if ((vcap.device_caps & V4L2_CAP_VIDEO_CAPTURE) == 0) { + close(fd); + return; // not a video capture device. + } else if (SDL_FindPhysicalCameraDeviceByCallback(FindV4L2CameraDeviceByBusInfoCallback, vcap.bus_info)) { + close(fd); + return; // already have it. } - bus_info = SDL_strdup((char *)vcap.bus_info); + #if DEBUG_CAMERA + SDL_Log("CAMERA: V4L2 camera path='%s' bus_info='%s' name='%s'", path, (const char *) vcap.bus_info, vcap.card); + #endif - if (DeviceExists(path, bus_info)) { - SDL_free(bus_info); - return 0; - } + SpecAddData add_data; + SDL_zero(add_data); + struct v4l2_fmtdesc fmtdesc; + SDL_zero(fmtdesc); + fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + while (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0) { + const Uint32 sdlfmt = format_v4l2_to_sdl(fmtdesc.pixelformat); - // Add new item - item = (SDL_cameralist_item *)SDL_calloc(1, sizeof(SDL_cameralist_item)); - if (!item) { - SDL_free(bus_info); - return -1; - } + #if DEBUG_CAMERA + SDL_Log("CAMERA: - Has format '%s'%s%s", SDL_GetPixelFormatName(sdlfmt), + (fmtdesc.flags & V4L2_FMT_FLAG_EMULATED) ? " [EMULATED]" : "", + (fmtdesc.flags & V4L2_FMT_FLAG_COMPRESSED) ? " [COMPRESSED]" : ""); + #endif - item->fname = SDL_strdup(path); - if (!item->fname) { - SDL_free(item); - SDL_free(bus_info); - return -1; - } + fmtdesc.index++; // prepare for next iteration. - item->fname = SDL_strdup(path); - item->bus_info = bus_info; - item->instance_id = SDL_GetNextObjectID(); + if (sdlfmt == SDL_PIXELFORMAT_UNKNOWN) { + continue; // unsupported by SDL atm. + } + struct v4l2_frmsizeenum frmsizeenum; + SDL_zero(frmsizeenum); + frmsizeenum.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + frmsizeenum.pixel_format = fmtdesc.pixelformat; + + while (ioctl(fd,VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == 0) { + if (frmsizeenum.type == V4L2_FRMSIZE_TYPE_DISCRETE) { + const int w = (int) frmsizeenum.discrete.width; + const int h = (int) frmsizeenum.discrete.height; + #if DEBUG_CAMERA + SDL_Log("CAMERA: * Has discrete size %dx%d", w, h); + #endif + if (AddCameraSpec(&add_data, sdlfmt, w, h) == -1) { + break; // Probably out of memory; we'll go with what we have, if anything. + } + frmsizeenum.index++; // set up for the next one. + } else if ((frmsizeenum.type == V4L2_FRMSIZE_TYPE_STEPWISE) || (frmsizeenum.type == V4L2_FRMSIZE_TYPE_CONTINUOUS)) { + const int minw = (int) frmsizeenum.stepwise.min_width; + const int minh = (int) frmsizeenum.stepwise.min_height; + const int maxw = (int) frmsizeenum.stepwise.max_width; + const int maxh = (int) frmsizeenum.stepwise.max_height; + const int stepw = (int) frmsizeenum.stepwise.step_width; + const int steph = (int) frmsizeenum.stepwise.step_height; + for (int w = minw; w <= maxw; w += stepw) { + for (int h = minh; w <= maxh; w += steph) { + #if DEBUG_CAMERA + SDL_Log("CAMERA: * Has %s size %dx%d", (frmsizeenum.type == V4L2_FRMSIZE_TYPE_STEPWISE) ? "stepwise" : "continuous", w, h); + #endif + if (AddCameraSpec(&add_data, sdlfmt, w, h) == -1) { + break; // Probably out of memory; we'll go with what we have, if anything. + } - if (!SDL_cameralist_tail) { - SDL_cameralist = SDL_cameralist_tail = item; - } else { - SDL_cameralist_tail->next = item; - SDL_cameralist_tail = item; + } + } + break; + } + } } - ++num_cameras; + close(fd); - // !!! TODO: Send a add event? -#if DEBUG_CAMERA - SDL_Log("Added video camera ID: %d %s (%s) (total: %d)", item->instance_id, path, bus_info, num_cameras); -#endif - return 0; + #if DEBUG_CAMERA + SDL_Log("CAMERA: (total specs: %d)", add_data.num_specs); + #endif + + if (add_data.num_specs > 0) { + V4L2DeviceHandle *handle = (V4L2DeviceHandle *) SDL_calloc(1, sizeof (V4L2DeviceHandle)); + if (handle) { + handle->path = SDL_strdup(path); + if (handle->path) { + handle->bus_info = SDL_strdup((char *)vcap.bus_info); + if (handle->bus_info) { + if (SDL_AddCameraDevice((const char *) vcap.card, add_data.num_specs, add_data.specs, handle)) { + SDL_free(add_data.specs); + return; // good to go. + } + SDL_free(handle->bus_info); + } + SDL_free(handle->path); + } + SDL_free(handle); + } + } + SDL_free(add_data.specs); } -#ifdef SDL_USE_LIBUDEV -static int MaybeRemoveDevice(const char *path) +static void V4L2_FreeDeviceHandle(SDL_CameraDevice *device) { - - SDL_cameralist_item *item; - SDL_cameralist_item *prev = NULL; -#if DEBUG_CAMERA - SDL_Log("Remove video camera %s", path); -#endif - if (!path) { - return -1; + if (device) { + V4L2DeviceHandle *handle = (V4L2DeviceHandle *) device->handle; + SDL_free(handle->path); + SDL_free(handle->bus_info); + SDL_free(handle); } +} - for (item = SDL_cameralist; item; item = item->next) { - // found it, remove it. - if (SDL_strcmp(path, item->fname) == 0) { - if (prev) { - prev->next = item->next; - } else { - SDL_assert(SDL_cameralist == item); - SDL_cameralist = item->next; - } - if (item == SDL_cameralist_tail) { - SDL_cameralist_tail = prev; - } +#ifdef SDL_USE_LIBUDEV +static SDL_bool FindV4L2CameraDeviceByPathCallback(SDL_CameraDevice *device, void *userdata) +{ + const V4L2DeviceHandle *handle = (const V4L2DeviceHandle *) device->handle; + return (SDL_strcmp(handle->path, (const char *) userdata) == 0); +} - // Need to decrement the count - --num_cameras; - // !!! TODO: Send a remove event? +static void MaybeRemoveDevice(const char *path) +{ + if (path) { + SDL_CameraDeviceDisconnected(SDL_FindPhysicalCameraDeviceByCallback(FindV4L2CameraDeviceByPathCallback, (void *) path)); + } +} - SDL_free(item->fname); - SDL_free(item->bus_info); - SDL_free(item); - return 0; +static void CameraUdevCallback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath) +{ + if (devpath && (udev_class & SDL_UDEV_DEVICE_VIDEO_CAPTURE)) { + if (udev_type == SDL_UDEV_DEVICEADDED) { + MaybeAddDevice(devpath); + } else if (udev_type == SDL_UDEV_DEVICEREMOVED) { + MaybeRemoveDevice(devpath); } - prev = item; } - return 0; } #endif // SDL_USE_LIBUDEV - static void V4L2_Deinitialize(void) { - for (SDL_cameralist_item *item = SDL_cameralist; item; ) { - SDL_cameralist_item *tmp = item->next; - SDL_free(item->fname); - SDL_free(item->bus_info); - SDL_free(item); - item = tmp; - } - - num_cameras = 0; - SDL_cameralist = NULL; - SDL_cameralist_tail = NULL; +#ifdef SDL_USE_LIBUDEV + SDL_UDEV_DelCallback(CameraUdevCallback); + SDL_UDEV_Quit(); +#endif // SDL_USE_LIBUDEV } static void V4L2_DetectDevices(void) { -} - -static SDL_bool V4L2_Init(SDL_CameraDriverImpl *impl) -{ - // !!! FIXME: move to DetectDevices - const char pattern[] = "/dev/video%d"; - char path[PATH_MAX]; - - /* - * Limit amount of checks to MAX_CAMERA_DEVICES since we may or may not have - * permission to some or all devices. - */ - for (int i = 0; i < MAX_CAMERA_DEVICES; i++) { - (void)SDL_snprintf(path, PATH_MAX, pattern, i); - if (MaybeAddDevice(path) == -2) { - break; +#ifdef SDL_USE_LIBUDEV + if (SDL_UDEV_Init() == 0) { + if (SDL_UDEV_AddCallback(CameraUdevCallback) >= 0) { // !!! FIXME: this should return 0 on success, it currently returns 1. + SDL_UDEV_Scan(); // Force a scan to build the initial device list } } - -#ifdef SDL_USE_LIBUDEV - if (SDL_UDEV_Init() < 0) { - return SDL_SetError("Could not initialize UDEV"); - } else if (SDL_UDEV_AddCallback(CameraUdevCallback) < 0) { - SDL_UDEV_Quit(); - return SDL_SetError("Could not setup Video Capture <-> udev callback"); +#else + DIR *dirp = opendir("/dev"); + if (dirp) { + struct dirent *dent; + while ((dent = readdir(dirp)) != NULL) { + int num = 0; + if (SDL_sscanf(dent->d_name, "video%d", &num) == 1) { + char fullpath[64]; + SDL_snprintf(fullpath, sizeof (fullpath), "/dev/video%d", num); + MaybeAddDevice(fullpath); + } + } + closedir(dirp); } - - // Force a scan to build the initial device list - SDL_UDEV_Scan(); #endif // SDL_USE_LIBUDEV +} +static SDL_bool V4L2_Init(SDL_CameraDriverImpl *impl) +{ impl->DetectDevices = V4L2_DetectDevices; impl->OpenDevice = V4L2_OpenDevice; impl->CloseDevice = V4L2_CloseDevice; - impl->InitDevice = V4L2_InitDevice; - impl->GetDeviceSpec = V4L2_GetDeviceSpec; - impl->StartCamera = V4L2_StartCamera; - impl->StopCamera = V4L2_StopCamera; + impl->WaitDevice = V4L2_WaitDevice; impl->AcquireFrame = V4L2_AcquireFrame; impl->ReleaseFrame = V4L2_ReleaseFrame; - impl->GetNumFormats = V4L2_GetNumFormats; - impl->GetFormat = V4L2_GetFormat; - impl->GetNumFrameSizes = V4L2_GetNumFrameSizes; - impl->GetFrameSize = V4L2_GetFrameSize; - impl->GetDeviceName = V4L2_GetDeviceName; - impl->GetDevices = V4L2_GetDevices; + impl->FreeDeviceHandle = V4L2_FreeDeviceHandle; impl->Deinitialize = V4L2_Deinitialize; return SDL_TRUE; diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index b9ebf221e6ba3..9943d9d0f8f6e 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -957,21 +957,18 @@ SDL3_0.0.0 { SDL_SetWindowShape; SDL_RenderViewportSet; SDL_HasProperty; + SDL_GetNumCameraDrivers; + SDL_GetCameraDriver; + SDL_GetCurrentCameraDriver; SDL_GetCameraDevices; - SDL_OpenCamera; - SDL_SetCameraSpec; - SDL_OpenCameraWithSpec; + SDL_GetCameraDeviceSupportedSpecs; SDL_GetCameraDeviceName; + SDL_OpenCameraDevice; + SDL_GetCameraInstanceID; + SDL_GetCameraProperties; SDL_GetCameraSpec; - SDL_GetCameraFormat; - SDL_GetNumCameraFormats; - SDL_GetCameraFrameSize; - SDL_GetNumCameraFrameSizes; - SDL_GetCameraStatus; - SDL_StartCamera; SDL_AcquireCameraFrame; SDL_ReleaseCameraFrame; - SDL_StopCamera; SDL_CloseCamera; # 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 79206a4932fae..e7f050c0358f8 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -982,19 +982,16 @@ #define SDL_SetWindowShape SDL_SetWindowShape_REAL #define SDL_RenderViewportSet SDL_RenderViewportSet_REAL #define SDL_HasProperty SDL_HasProperty_REAL +#define SDL_GetNumCameraDrivers SDL_GetNumCameraDrivers_REAL +#define SDL_GetCameraDriver SDL_GetCameraDriver_REAL +#define SDL_GetCurrentCameraDriver SDL_GetCurrentCameraDriver_REAL #define SDL_GetCameraDevices SDL_GetCameraDevices_REAL -#define SDL_OpenCamera SDL_OpenCamera_REAL -#define SDL_SetCameraSpec SDL_SetCameraSpec_REAL -#define SDL_OpenCameraWithSpec SDL_OpenCameraWithSpec_REAL +#define SDL_GetCameraDeviceSupportedSpecs SDL_GetCameraDeviceSupportedSpecs_REAL #define SDL_GetCameraDeviceName SDL_GetCameraDeviceName_REAL +#define SDL_OpenCameraDevice SDL_OpenCameraDevice_REAL +#define SDL_GetCameraInstanceID SDL_GetCameraInstanceID_REAL +#define SDL_GetCameraProperties SDL_GetCameraProperties_REAL #define SDL_GetCameraSpec SDL_GetCameraSpec_REAL -#define SDL_GetCameraFormat SDL_GetCameraFormat_REAL -#define SDL_GetNumCameraFormats SDL_GetNumCameraFormats_REAL -#define SDL_GetCameraFrameSize SDL_GetCameraFrameSize_REAL -#define SDL_GetNumCameraFrameSizes SDL_GetNumCameraFrameSizes_REAL -#define SDL_GetCameraStatus SDL_GetCameraStatus_REAL -#define SDL_StartCamera SDL_StartCamera_REAL #define SDL_AcquireCameraFrame SDL_AcquireCameraFrame_REAL #define SDL_ReleaseCameraFrame SDL_ReleaseCameraFrame_REAL -#define SDL_StopCamera SDL_StopCamera_REAL #define SDL_CloseCamera SDL_CloseCamera_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 4dd7e019e05af..9f1f735891d99 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1007,19 +1007,16 @@ SDL_DYNAPI_PROC(int,SDL_RenderGeometryRawFloat,(SDL_Renderer *a, SDL_Texture *b, SDL_DYNAPI_PROC(int,SDL_SetWindowShape,(SDL_Window *a, SDL_Surface *b),(a,b),return) SDL_DYNAPI_PROC(SDL_bool,SDL_RenderViewportSet,(SDL_Renderer *a),(a),return) SDL_DYNAPI_PROC(SDL_bool,SDL_HasProperty,(SDL_PropertiesID a, const char *b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_GetNumCameraDrivers,(void),(),return) +SDL_DYNAPI_PROC(const char*,SDL_GetCameraDriver,(int a),(a),return) +SDL_DYNAPI_PROC(const char*,SDL_GetCurrentCameraDriver,(void),(),return) SDL_DYNAPI_PROC(SDL_CameraDeviceID*,SDL_GetCameraDevices,(int *a),(a),return) -SDL_DYNAPI_PROC(SDL_CameraDevice*,SDL_OpenCamera,(SDL_CameraDeviceID a),(a),return) -SDL_DYNAPI_PROC(int,SDL_SetCameraSpec,(SDL_CameraDevice *a, const SDL_CameraSpec *b, SDL_CameraSpec *c, int d),(a,b,c,d),return) -SDL_DYNAPI_PROC(SDL_CameraDevice*,SDL_OpenCameraWithSpec,(SDL_CameraDeviceID a, const SDL_CameraSpec *b, SDL_CameraSpec *c, int d),(a,b,c,d),return) -SDL_DYNAPI_PROC(const char*,SDL_GetCameraDeviceName,(SDL_CameraDeviceID a),(a),return) -SDL_DYNAPI_PROC(int,SDL_GetCameraSpec,(SDL_CameraDevice *a, SDL_CameraSpec *b),(a,b),return) -SDL_DYNAPI_PROC(int,SDL_GetCameraFormat,(SDL_CameraDevice *a, int b, Uint32 *c),(a,b,c),return) -SDL_DYNAPI_PROC(int,SDL_GetNumCameraFormats,(SDL_CameraDevice *a),(a),return) -SDL_DYNAPI_PROC(int,SDL_GetCameraFrameSize,(SDL_CameraDevice *a, Uint32 b, int c, int *d, int *e),(a,b,c,d,e),return) -SDL_DYNAPI_PROC(int,SDL_GetNumCameraFrameSizes,(SDL_CameraDevice *a, Uint32 b),(a,b),return) -SDL_DYNAPI_PROC(SDL_CameraStatus,SDL_GetCameraStatus,(SDL_CameraDevice *a),(a),return) -SDL_DYNAPI_PROC(int,SDL_StartCamera,(SDL_CameraDevice *a),(a),return) -SDL_DYNAPI_PROC(int,SDL_AcquireCameraFrame,(SDL_CameraDevice *a, SDL_CameraFrame *b),(a,b),return) -SDL_DYNAPI_PROC(int,SDL_ReleaseCameraFrame,(SDL_CameraDevice *a, SDL_CameraFrame *b),(a,b),return) -SDL_DYNAPI_PROC(int,SDL_StopCamera,(SDL_CameraDevice *a),(a),return) -SDL_DYNAPI_PROC(void,SDL_CloseCamera,(SDL_CameraDevice *a),(a),) +SDL_DYNAPI_PROC(SDL_CameraSpec*,SDL_GetCameraDeviceSupportedSpecs,(SDL_CameraDeviceID a, int *b),(a,b),return) +SDL_DYNAPI_PROC(char*,SDL_GetCameraDeviceName,(SDL_CameraDeviceID a),(a),return) +SDL_DYNAPI_PROC(SDL_Camera*,SDL_OpenCameraDevice,(SDL_CameraDeviceID a, const SDL_CameraSpec *b),(a,b),return) +SDL_DYNAPI_PROC(SDL_CameraDeviceID,SDL_GetCameraInstanceID,(SDL_Camera *a),(a),return) +SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetCameraProperties,(SDL_Camera *a),(a),return) +SDL_DYNAPI_PROC(int,SDL_GetCameraSpec,(SDL_Camera *a, SDL_CameraSpec *b),(a,b),return) +SDL_DYNAPI_PROC(SDL_Surface*,SDL_AcquireCameraFrame,(SDL_Camera *a, Uint64 *b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_ReleaseCameraFrame,(SDL_Camera *a, SDL_Surface *b),(a,b),return) +SDL_DYNAPI_PROC(void,SDL_CloseCamera,(SDL_Camera *a),(a),) diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index 02600437c49d9..04a54953abf14 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -25,6 +25,7 @@ #include "SDL_events_c.h" #include "../SDL_hints_c.h" #include "../audio/SDL_audio_c.h" +#include "../camera/SDL_camera_c.h" #include "../timer/SDL_timer_c.h" #ifndef SDL_JOYSTICK_DISABLED #include "../joystick/SDL_joystick_c.h" @@ -554,6 +555,15 @@ static void SDL_LogEvent(const SDL_Event *event) break; #undef PRINT_AUDIODEV_EVENT +#define PRINT_CAMERADEV_EVENT(event) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%u)", (uint)event->cdevice.timestamp, (uint)event->cdevice.which) + SDL_EVENT_CASE(SDL_EVENT_CAMERA_DEVICE_ADDED) + PRINT_CAMERADEV_EVENT(event); + break; + SDL_EVENT_CASE(SDL_EVENT_CAMERA_DEVICE_REMOVED) + PRINT_CAMERADEV_EVENT(event); + break; +#undef PRINT_CAMERADEV_EVENT + SDL_EVENT_CASE(SDL_EVENT_SENSOR_UPDATE) (void)SDL_snprintf(details, sizeof(details), " (timestamp=%u which=%d data[0]=%f data[1]=%f data[2]=%f data[3]=%f data[4]=%f data[5]=%f)", (uint)event->sensor.timestamp, (int)event->sensor.which, @@ -942,6 +952,10 @@ static void SDL_PumpEventsInternal(SDL_bool push_sentinel) SDL_UpdateAudio(); #endif +#ifndef SDL_CAMERA_DISABLED + SDL_UpdateCamera(); +#endif + #ifndef SDL_SENSOR_DISABLED /* Check for sensor state change */ if (SDL_update_sensors) { diff --git a/test/testcamera.c b/test/testcamera.c index f96b728661518..c524a13fae46f 100644 --- a/test/testcamera.c +++ b/test/testcamera.c @@ -18,8 +18,13 @@ #include #endif -#include - +#if 1 +int main(int argc, char **argv) +{ + SDL_Log("FIXME: update me"); + return 0; +} +#else static const char *usage = "\ \n\ =========================================================================\n\ @@ -769,3 +774,4 @@ int main(int argc, char **argv) return 0; } +#endif \ No newline at end of file diff --git a/test/testcameraminimal.c b/test/testcameraminimal.c index ce2d3ec20ee54..57ce1830da6f1 100644 --- a/test/testcameraminimal.c +++ b/test/testcameraminimal.c @@ -28,21 +28,14 @@ int main(int argc, char **argv) int quit = 0; SDLTest_CommonState *state = NULL; - SDL_CameraDevice *device = NULL; - SDL_CameraSpec obtained; - - SDL_CameraFrame frame_current; + SDL_Camera *camera = NULL; + SDL_CameraSpec spec; SDL_Texture *texture = NULL; int texture_updated = 0; + SDL_Surface *frame_current = NULL; SDL_zero(evt); - SDL_zero(obtained); - SDL_zero(frame_current); - - /* Set 0 to disable TouchEvent to be duplicated as MouseEvent with SDL_TOUCH_MOUSEID */ - SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); - /* Set 0 to disable MouseEvent to be duplicated as TouchEvent with SDL_MOUSE_TOUCHID */ - SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0"); + SDL_zero(spec); /* Initialize test framework */ state = SDLTest_CommonCreateState(argv, 0); @@ -73,20 +66,42 @@ int main(int argc, char **argv) return 1; } - device = SDL_OpenCameraWithSpec(0, NULL, &obtained, SDL_CAMERA_ALLOW_ANY_CHANGE); - if (!device) { - SDL_Log("No camera? %s", SDL_GetError()); + SDL_CameraDeviceID *devices = SDL_GetCameraDevices(NULL); + if (!devices) { + SDL_Log("SDL_GetCameraDevices failed: %s", SDL_GetError()); + return 1; + } + + const SDL_CameraDeviceID devid = devices[0]; /* just take the first one. */ + SDL_free(devices); + + if (!devid) { + SDL_Log("No cameras available? %s", SDL_GetError()); + return 1; + } + + SDL_CameraSpec *pspec = NULL; + #if 0 /* just for edge-case testing purposes, ignore. */ + pspec = &spec; + spec.width = 100 /*1280 * 2*/; + spec.height = 100 /*720 * 2*/; + spec.format = SDL_PIXELFORMAT_YUY2 /*SDL_PIXELFORMAT_RGBA8888*/; + #endif + + camera = SDL_OpenCameraDevice(devid, pspec); + if (!camera) { + SDL_Log("Failed to open camera device: %s", SDL_GetError()); return 1; } - if (SDL_StartCamera(device) < 0) { - SDL_Log("error SDL_StartCamera(): %s", SDL_GetError()); + if (SDL_GetCameraSpec(camera, &spec) < 0) { + SDL_Log("Couldn't get camera spec: %s", SDL_GetError()); return 1; } /* Create texture with appropriate format */ if (texture == NULL) { - texture = SDL_CreateTexture(renderer, obtained.format, SDL_TEXTUREACCESS_STATIC, obtained.width, obtained.height); + texture = SDL_CreateTexture(renderer, spec.format, SDL_TEXTUREACCESS_STATIC, spec.width, spec.height); if (texture == NULL) { SDL_Log("Couldn't create texture: %s", SDL_GetError()); return 1; @@ -118,21 +133,18 @@ int main(int argc, char **argv) } { - SDL_CameraFrame frame_next; - SDL_zero(frame_next); + Uint64 timestampNS = 0; + SDL_Surface *frame_next = SDL_AcquireCameraFrame(camera, ×tampNS); - if (SDL_AcquireCameraFrame(device, &frame_next) < 0) { - SDL_Log("err SDL_AcquireCameraFrame: %s", SDL_GetError()); - } #if 0 - if (frame_next.num_planes) { - SDL_Log("frame: %p at %" SDL_PRIu64, (void*)frame_next.data[0], frame_next.timestampNS); + if (frame_next) { + SDL_Log("frame: %p at %" SDL_PRIu64, (void*)frame_next->pixels, timestampNS); } #endif - if (frame_next.num_planes) { - if (frame_current.num_planes) { - if (SDL_ReleaseCameraFrame(device, &frame_current) < 0) { + if (frame_next) { + if (frame_current) { + if (SDL_ReleaseCameraFrame(camera, frame_current) < 0) { SDL_Log("err SDL_ReleaseCameraFrame: %s", SDL_GetError()); } } @@ -146,20 +158,8 @@ int main(int argc, char **argv) } /* Update SDL_Texture with last video frame (only once per new frame) */ - if (frame_current.num_planes && texture_updated == 0) { - /* Use software data */ - if (frame_current.num_planes == 1) { - SDL_UpdateTexture(texture, NULL, - frame_current.data[0], frame_current.pitch[0]); - } else if (frame_current.num_planes == 2) { - SDL_UpdateNVTexture(texture, NULL, - frame_current.data[0], frame_current.pitch[0], - frame_current.data[1], frame_current.pitch[1]); - } else if (frame_current.num_planes == 3) { - SDL_UpdateYUVTexture(texture, NULL, frame_current.data[0], frame_current.pitch[0], - frame_current.data[1], frame_current.pitch[1], - frame_current.data[2], frame_current.pitch[2]); - } + if (frame_current && texture_updated == 0) { + SDL_UpdateTexture(texture, NULL, frame_current->pixels, frame_current->pitch); texture_updated = 1; } @@ -177,7 +177,7 @@ int main(int argc, char **argv) th = (int)((float) th * scale); } d.x = (float)(10 ); - d.y = (float)(win_h - th); + d.y = ((float)(win_h - th)) / 2.0f; d.w = (float)tw; d.h = (float)(th - 10); SDL_RenderTexture(renderer, texture, NULL, &d); @@ -186,13 +186,10 @@ int main(int argc, char **argv) SDL_RenderPresent(renderer); } - if (SDL_StopCamera(device) < 0) { - SDL_Log("error SDL_StopCamera(): %s", SDL_GetError()); - } - if (frame_current.num_planes) { - SDL_ReleaseCameraFrame(device, &frame_current); + if (frame_current) { + SDL_ReleaseCameraFrame(camera, frame_current); } - SDL_CloseCamera(device); + SDL_CloseCamera(camera); if (texture) { SDL_DestroyTexture(texture); @@ -205,3 +202,4 @@ int main(int argc, char **argv) return 0; } + From f87d5362291dbe80bdfd9ba6ecbf827f033800b2 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 16 Dec 2023 16:00:15 -0500 Subject: [PATCH 039/220] camera: Added more accurate timestamps. --- src/camera/SDL_camera.c | 18 +++++++++++++++++- src/camera/SDL_syscamera.h | 11 ++++++++++- src/camera/v4l2/SDL_camera_v4l2.c | 7 +++++-- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index d9c581b04a16b..6cbd453628b0d 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -109,6 +109,9 @@ static void ClosePhysicalCameraDevice(SDL_CameraDevice *device) device->filled_output_surfaces.next = NULL; device->empty_output_surfaces.next = NULL; device->app_held_output_surfaces.next = NULL; + + device->base_timestamp = 0; + device->adjust_timestamp = 0; } // this must not be called while `device` is still in a device list, or while a device's camera thread is still running. @@ -535,7 +538,12 @@ SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device) SDL_Log("CAMERA: New frame available!"); #endif - if (device->empty_output_surfaces.next == NULL) { + if (device->drop_frames > 0) { + device->drop_frames--; + camera_driver.impl.ReleaseFrame(device, device->acquire_surface); + device->acquire_surface->pixels = NULL; + device->acquire_surface->pitch = 0; + } else if (device->empty_output_surfaces.next == NULL) { // uhoh, no output frames available! Either the app is slow, or it forgot to release frames when done with them. Drop this new frame. #if DEBUG_CAMERA SDL_Log("CAMERA: No empty output surfaces! Dropping frame!"); @@ -544,6 +552,12 @@ SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device) device->acquire_surface->pixels = NULL; device->acquire_surface->pitch = 0; } else { + if (!device->adjust_timestamp) { + device->adjust_timestamp = SDL_GetTicksNS(); + device->base_timestamp = timestampNS; + } + timestampNS = (timestampNS - device->base_timestamp) + device->adjust_timestamp; + slist = device->empty_output_surfaces.next; output_surface = slist->surface; device->empty_output_surfaces.next = slist->next; @@ -828,6 +842,8 @@ SDL_Camera *SDL_OpenCameraDevice(SDL_CameraDeviceID instance_id, const SDL_Camer device->output_surfaces[i].surface = surf; } + device->drop_frames = 1; + // Start the camera thread if necessary if (!camera_driver.impl.ProvidesOwnCallbackThread) { char threadname[64]; diff --git a/src/camera/SDL_syscamera.h b/src/camera/SDL_syscamera.h index a48a1529830d7..27672890b54cf 100644 --- a/src/camera/SDL_syscamera.h +++ b/src/camera/SDL_syscamera.h @@ -25,7 +25,7 @@ #include "../SDL_hashtable.h" -#define DEBUG_CAMERA 1 +#define DEBUG_CAMERA 0 // !!! FIXME: update these drivers! @@ -92,6 +92,15 @@ struct SDL_CameraDevice // Driver-specific hardware data on how to open device (`hidden` is driver-specific data _when opened_). void *handle; + // Dropping the first frame(s) after open seems to help timing on some platforms. + int drop_frames; + + // Backend timestamp of first acquired frame, so we can keep these meaningful regardless of epoch. + Uint64 base_timestamp; + + // SDL timestamp of first acquired frame, so we can roughly convert to SDL ticks. + Uint64 adjust_timestamp; + // Pixel data flows from the driver into these, then gets converted for the app if necessary. SDL_Surface *acquire_surface; diff --git a/src/camera/v4l2/SDL_camera_v4l2.c b/src/camera/v4l2/SDL_camera_v4l2.c index 0eebc71960af4..c43762a466b33 100644 --- a/src/camera/v4l2/SDL_camera_v4l2.c +++ b/src/camera/v4l2/SDL_camera_v4l2.c @@ -67,7 +67,6 @@ struct SDL_PrivateCameraData io_method io; int nb_buffers; struct buffer *buffers; - int first_start; int driver_pitch; }; @@ -129,6 +128,7 @@ static int V4L2_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame, Uint6 } } + *timestampNS = SDL_GetTicksNS(); // oh well, close enough. frame->pixels = device->hidden->buffers[0].start; frame->pitch = device->hidden->driver_pitch; break; @@ -161,6 +161,8 @@ static int V4L2_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame, Uint6 frame->pitch = device->hidden->driver_pitch; device->hidden->buffers[buf.index].available = 1; + *timestampNS = (((Uint64) buf.timestamp.tv_sec) * SDL_NS_PER_SECOND) + SDL_US_TO_NS(buf.timestamp.tv_usec); + #if DEBUG_CAMERA SDL_Log("CAMERA: debug mmap: image %d/%d data[0]=%p", buf.index, device->hidden->nb_buffers, (void*)frame->pixels); #endif @@ -202,6 +204,8 @@ static int V4L2_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame, Uint6 frame->pitch = device->hidden->driver_pitch; device->hidden->buffers[i].available = 1; + *timestampNS = (((Uint64) buf.timestamp.tv_sec) * SDL_NS_PER_SECOND) + SDL_US_TO_NS(buf.timestamp.tv_usec); + #if DEBUG_CAMERA SDL_Log("CAMERA: debug userptr: image %d/%d data[0]=%p", buf.index, device->hidden->nb_buffers, (void*)frame->pixels); #endif @@ -212,7 +216,6 @@ static int V4L2_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame, Uint6 break; } - *timestampNS = SDL_GetTicksNS(); // !!! FIXME: can we get this info more accurately from v4l2? return 1; } From 87e7046fcaa281119df25df8e5b63af398a6ed98 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 16 Dec 2023 16:12:19 -0500 Subject: [PATCH 040/220] camera: Public API functions should say "Format" not "Spec" to match audio. --- include/SDL3/SDL_camera.h | 10 +++++----- src/camera/SDL_camera.c | 4 ++-- src/camera/v4l2/SDL_camera_v4l2.c | 12 ++++++------ src/dynapi/SDL_dynapi.sym | 4 ++-- src/dynapi/SDL_dynapi_overrides.h | 4 ++-- src/dynapi/SDL_dynapi_procs.h | 4 ++-- test/testcamera.c | 2 +- test/testcameraminimal.c | 2 +- 8 files changed, 21 insertions(+), 21 deletions(-) diff --git a/include/SDL3/SDL_camera.h b/include/SDL3/SDL_camera.h index eb6a9f2d82950..d31e045508264 100644 --- a/include/SDL3/SDL_camera.h +++ b/include/SDL3/SDL_camera.h @@ -57,8 +57,8 @@ typedef struct SDL_Camera SDL_Camera; /** * SDL_CameraSpec structure * - * \sa SDL_GetCameraDeviceSupportedSpecs - * \sa SDL_GetCameraSpec + * \sa SDL_GetCameraDeviceSupportedFormats + * \sa SDL_GetCameraFormat * */ typedef struct SDL_CameraSpec @@ -182,7 +182,7 @@ extern DECLSPEC SDL_CameraDeviceID *SDLCALL SDL_GetCameraDevices(int *count); * \sa SDL_GetCameraDevices * \sa SDL_OpenCameraDevice */ -extern DECLSPEC SDL_CameraSpec *SDLCALL SDL_GetCameraDeviceSupportedSpecs(SDL_CameraDeviceID devid, int *count); +extern DECLSPEC SDL_CameraSpec *SDLCALL SDL_GetCameraDeviceSupportedFormats(SDL_CameraDeviceID devid, int *count); /** * Get human-readable device name for a camera. @@ -212,7 +212,7 @@ extern DECLSPEC char * SDLCALL SDL_GetCameraDeviceName(SDL_CameraDeviceID instan * pass a NULL spec here and it will choose one for you (and you can use * SDL_Surface's conversion/scaling functions directly if necessary). * - * You can call SDL_GetCameraSpec() to get the actual data format if + * You can call SDL_GetCameraFormat() to get the actual data format if * passing a NULL spec here. You can see the exact specs a device can * support without conversion with SDL_GetCameraSupportedSpecs(). * @@ -279,7 +279,7 @@ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetCameraProperties(SDL_Camera *cam * * \sa SDL_OpenCameraDevice */ -extern DECLSPEC int SDLCALL SDL_GetCameraSpec(SDL_Camera *camera, SDL_CameraSpec *spec); +extern DECLSPEC int SDLCALL SDL_GetCameraFormat(SDL_Camera *camera, SDL_CameraSpec *spec); /** * Acquire a frame. diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index 6cbd453628b0d..18efa3f282e70 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -407,7 +407,7 @@ void SDL_CloseCamera(SDL_Camera *camera) ClosePhysicalCameraDevice(device); } -int SDL_GetCameraSpec(SDL_Camera *camera, SDL_CameraSpec *spec) +int SDL_GetCameraFormat(SDL_Camera *camera, SDL_CameraSpec *spec) { if (!camera) { return SDL_InvalidParamError("camera"); @@ -469,7 +469,7 @@ SDL_CameraDeviceID *SDL_GetCameraDevices(int *count) } -SDL_CameraSpec *SDL_GetCameraDeviceSupportedSpecs(SDL_CameraDeviceID instance_id, int *count) +SDL_CameraSpec *SDL_GetCameraDeviceSupportedFormats(SDL_CameraDeviceID instance_id, int *count) { if (count) { *count = 0; diff --git a/src/camera/v4l2/SDL_camera_v4l2.c b/src/camera/v4l2/SDL_camera_v4l2.c index c43762a466b33..7b68378fd442f 100644 --- a/src/camera/v4l2/SDL_camera_v4l2.c +++ b/src/camera/v4l2/SDL_camera_v4l2.c @@ -611,14 +611,14 @@ static SDL_bool FindV4L2CameraDeviceByBusInfoCallback(SDL_CameraDevice *device, return (SDL_strcmp(handle->bus_info, (const char *) userdata) == 0); } -typedef struct SpecAddData +typedef struct FormatAddData { SDL_CameraSpec *specs; int num_specs; int allocated_specs; -} SpecAddData; +} FormatAddData; -static int AddCameraSpec(SpecAddData *data, Uint32 fmt, int w, int h) +static int AddCameraFormat(FormatAddData *data, Uint32 fmt, int w, int h) { SDL_assert(data != NULL); if (data->allocated_specs <= data->num_specs) { @@ -676,7 +676,7 @@ static void MaybeAddDevice(const char *path) SDL_Log("CAMERA: V4L2 camera path='%s' bus_info='%s' name='%s'", path, (const char *) vcap.bus_info, vcap.card); #endif - SpecAddData add_data; + FormatAddData add_data; SDL_zero(add_data); struct v4l2_fmtdesc fmtdesc; @@ -709,7 +709,7 @@ static void MaybeAddDevice(const char *path) #if DEBUG_CAMERA SDL_Log("CAMERA: * Has discrete size %dx%d", w, h); #endif - if (AddCameraSpec(&add_data, sdlfmt, w, h) == -1) { + if (AddCameraFormat(&add_data, sdlfmt, w, h) == -1) { break; // Probably out of memory; we'll go with what we have, if anything. } frmsizeenum.index++; // set up for the next one. @@ -725,7 +725,7 @@ static void MaybeAddDevice(const char *path) #if DEBUG_CAMERA SDL_Log("CAMERA: * Has %s size %dx%d", (frmsizeenum.type == V4L2_FRMSIZE_TYPE_STEPWISE) ? "stepwise" : "continuous", w, h); #endif - if (AddCameraSpec(&add_data, sdlfmt, w, h) == -1) { + if (AddCameraFormat(&add_data, sdlfmt, w, h) == -1) { break; // Probably out of memory; we'll go with what we have, if anything. } diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 9943d9d0f8f6e..a098f270972d3 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -961,12 +961,12 @@ SDL3_0.0.0 { SDL_GetCameraDriver; SDL_GetCurrentCameraDriver; SDL_GetCameraDevices; - SDL_GetCameraDeviceSupportedSpecs; + SDL_GetCameraDeviceSupportedFormats; SDL_GetCameraDeviceName; SDL_OpenCameraDevice; SDL_GetCameraInstanceID; SDL_GetCameraProperties; - SDL_GetCameraSpec; + SDL_GetCameraFormat; SDL_AcquireCameraFrame; SDL_ReleaseCameraFrame; SDL_CloseCamera; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index e7f050c0358f8..db16355d7927f 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -986,12 +986,12 @@ #define SDL_GetCameraDriver SDL_GetCameraDriver_REAL #define SDL_GetCurrentCameraDriver SDL_GetCurrentCameraDriver_REAL #define SDL_GetCameraDevices SDL_GetCameraDevices_REAL -#define SDL_GetCameraDeviceSupportedSpecs SDL_GetCameraDeviceSupportedSpecs_REAL +#define SDL_GetCameraDeviceSupportedFormats SDL_GetCameraDeviceSupportedFormats_REAL #define SDL_GetCameraDeviceName SDL_GetCameraDeviceName_REAL #define SDL_OpenCameraDevice SDL_OpenCameraDevice_REAL #define SDL_GetCameraInstanceID SDL_GetCameraInstanceID_REAL #define SDL_GetCameraProperties SDL_GetCameraProperties_REAL -#define SDL_GetCameraSpec SDL_GetCameraSpec_REAL +#define SDL_GetCameraFormat SDL_GetCameraFormat_REAL #define SDL_AcquireCameraFrame SDL_AcquireCameraFrame_REAL #define SDL_ReleaseCameraFrame SDL_ReleaseCameraFrame_REAL #define SDL_CloseCamera SDL_CloseCamera_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 9f1f735891d99..49d5e1a7532cd 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1011,12 +1011,12 @@ SDL_DYNAPI_PROC(int,SDL_GetNumCameraDrivers,(void),(),return) SDL_DYNAPI_PROC(const char*,SDL_GetCameraDriver,(int a),(a),return) SDL_DYNAPI_PROC(const char*,SDL_GetCurrentCameraDriver,(void),(),return) SDL_DYNAPI_PROC(SDL_CameraDeviceID*,SDL_GetCameraDevices,(int *a),(a),return) -SDL_DYNAPI_PROC(SDL_CameraSpec*,SDL_GetCameraDeviceSupportedSpecs,(SDL_CameraDeviceID a, int *b),(a,b),return) +SDL_DYNAPI_PROC(SDL_CameraSpec*,SDL_GetCameraDeviceSupportedFormats,(SDL_CameraDeviceID a, int *b),(a,b),return) SDL_DYNAPI_PROC(char*,SDL_GetCameraDeviceName,(SDL_CameraDeviceID a),(a),return) SDL_DYNAPI_PROC(SDL_Camera*,SDL_OpenCameraDevice,(SDL_CameraDeviceID a, const SDL_CameraSpec *b),(a,b),return) SDL_DYNAPI_PROC(SDL_CameraDeviceID,SDL_GetCameraInstanceID,(SDL_Camera *a),(a),return) SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetCameraProperties,(SDL_Camera *a),(a),return) -SDL_DYNAPI_PROC(int,SDL_GetCameraSpec,(SDL_Camera *a, SDL_CameraSpec *b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_GetCameraFormat,(SDL_Camera *a, SDL_CameraSpec *b),(a,b),return) SDL_DYNAPI_PROC(SDL_Surface*,SDL_AcquireCameraFrame,(SDL_Camera *a, Uint64 *b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_ReleaseCameraFrame,(SDL_Camera *a, SDL_Surface *b),(a,b),return) SDL_DYNAPI_PROC(void,SDL_CloseCamera,(SDL_Camera *a),(a),) diff --git a/test/testcamera.c b/test/testcamera.c index c524a13fae46f..e5389d218fc25 100644 --- a/test/testcamera.c +++ b/test/testcamera.c @@ -284,7 +284,7 @@ int main(int argc, char **argv) { SDL_CameraSpec spec; - if (SDL_GetCameraSpec(device, &spec) == 0) { + if (SDL_GetCameraFormat(device, &spec) == 0) { SDL_Log("Read spec: size=%d x %d format=%s", spec.width, spec.height, SDL_GetPixelFormatName(spec.format)); } else { diff --git a/test/testcameraminimal.c b/test/testcameraminimal.c index 57ce1830da6f1..df8a08b175852 100644 --- a/test/testcameraminimal.c +++ b/test/testcameraminimal.c @@ -94,7 +94,7 @@ int main(int argc, char **argv) return 1; } - if (SDL_GetCameraSpec(camera, &spec) < 0) { + if (SDL_GetCameraFormat(camera, &spec) < 0) { SDL_Log("Couldn't get camera spec: %s", SDL_GetError()); return 1; } From 9ae39d52de860e02fda2e6e1e18b4337c5d32a19 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 16 Dec 2023 21:36:04 -0500 Subject: [PATCH 041/220] camera: Add sources to Xcode and Visual Studio projects. This still needs updates to actually compile on macOS/iOS, though! --- VisualC-GDK/SDL/SDL.vcxproj | 5 ++ VisualC-WinRT/SDL-UWP.vcxproj | 7 ++- VisualC-WinRT/SDL-UWP.vcxproj.filters | 25 ++++++-- VisualC/SDL/SDL.vcxproj | 6 +- VisualC/SDL/SDL.vcxproj.filters | 24 +++++++- Xcode/SDL/SDL.xcodeproj/project.pbxproj | 66 +++++++++++++-------- src/camera/coremedia/SDL_camera_coremedia.m | 2 +- 7 files changed, 99 insertions(+), 36 deletions(-) diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index 19f0a244f1e63..76ea691d79100 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj +++ b/VisualC-GDK/SDL/SDL.vcxproj @@ -314,6 +314,7 @@ + @@ -398,6 +399,8 @@ + + @@ -498,6 +501,8 @@ $(IntDir)$(TargetName)_cpp.pch $(IntDir)$(TargetName)_cpp.pch + + diff --git a/VisualC-WinRT/SDL-UWP.vcxproj b/VisualC-WinRT/SDL-UWP.vcxproj index 37188a009c2c5..2f8cc4ace9bd0 100644 --- a/VisualC-WinRT/SDL-UWP.vcxproj +++ b/VisualC-WinRT/SDL-UWP.vcxproj @@ -36,6 +36,7 @@ + @@ -89,7 +90,6 @@ - @@ -99,6 +99,8 @@ + + @@ -259,6 +261,8 @@ true true + + @@ -532,7 +536,6 @@ - diff --git a/VisualC-WinRT/SDL-UWP.vcxproj.filters b/VisualC-WinRT/SDL-UWP.vcxproj.filters index 784f5961baa01..2d93536feed7a 100644 --- a/VisualC-WinRT/SDL-UWP.vcxproj.filters +++ b/VisualC-WinRT/SDL-UWP.vcxproj.filters @@ -13,11 +13,20 @@ {0000318d975e0a2867ab1d5727bf0000} + + {00009e5236c2ac679fe0bc30beb90000} + + + {000031d805439b865ff4550d2f620000} + Header Files + + API Headers + Header Files @@ -165,8 +174,11 @@ Header Files - - Header Files + + camera + + + camera Header Files @@ -507,6 +519,12 @@ Source Files + + camera\dummy + + + camera + Source Files @@ -822,9 +840,6 @@ Source Files - - Source Files - Source Files diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index 52563669f0d35..2ddcdb1a6bb2c 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -236,6 +236,7 @@ + @@ -320,6 +321,8 @@ + + @@ -393,6 +396,8 @@ Create Create + + @@ -658,7 +663,6 @@ - diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index 4b685e50a660f..6673e9e816e2b 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -178,11 +178,20 @@ {0000ddc7911820dbe64274d3654f0000} + + {0000de1b75e1a954834693f1c81e0000} + + + {0000fc2700d453b3c8d79fe81e1c0000} + API Headers + + API Headers + API Headers @@ -396,6 +405,12 @@ API Headers + + camera + + + camera + main @@ -838,6 +853,12 @@ + + camera\dummy + + + camera + main\generic @@ -1195,9 +1216,6 @@ video - - video - video diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj index e7925013650cb..e5623fe149a7f 100644 --- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -33,8 +33,11 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 00001B2471F503DD3C1B0000 /* SDL_camera_dummy.c in Sources */ = {isa = PBXBuildFile; fileRef = 00005BD74B46358B33A20000 /* SDL_camera_dummy.c */; }; 000028F8113A53F4333E0000 /* SDL_main_callbacks.c in Sources */ = {isa = PBXBuildFile; fileRef = 00009366FB9FBBD54C390000 /* SDL_main_callbacks.c */; }; + 00002B20A48E055EB0350000 /* SDL_camera_coremedia.m in Sources */ = {isa = PBXBuildFile; fileRef = 00008B79BF08CBCEAC460000 /* SDL_camera_coremedia.m */; }; 000040E76FDC6AE48CBF0000 /* SDL_hashtable.c in Sources */ = {isa = PBXBuildFile; fileRef = 000078E1881E857EBB6C0000 /* SDL_hashtable.c */; }; + 000098E9DAA43EF6FF7F0000 /* SDL_camera.c in Sources */ = {isa = PBXBuildFile; fileRef = 0000035D38C3899C7EFD0000 /* SDL_camera.c */; }; 0000A4DA2F45A31DC4F00000 /* SDL_sysmain_callbacks.m in Sources */ = {isa = PBXBuildFile; fileRef = 0000BB287BA0A0178C1A0000 /* SDL_sysmain_callbacks.m */; platformFilters = (ios, maccatalyst, macos, tvos, watchos, ); }; 007317A40858DECD00B2BC32 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0073179D0858DECD00B2BC32 /* Cocoa.framework */; platformFilters = (macos, ); }; 007317A60858DECD00B2BC32 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0073179F0858DECD00B2BC32 /* IOKit.framework */; platformFilters = (ios, maccatalyst, macos, ); }; @@ -421,14 +424,8 @@ F3B38CDF296E2E52005DA6D3 /* SDL_intrin.h in Headers */ = {isa = PBXBuildFile; fileRef = F3B38CCE296E2E52005DA6D3 /* SDL_intrin.h */; settings = {ATTRIBUTES = (Public, ); }; }; F3D60A8328C16A1900788A3A /* SDL_hidapi_wii.c in Sources */ = {isa = PBXBuildFile; fileRef = F3D60A8228C16A1800788A3A /* SDL_hidapi_wii.c */; }; F3DDCC562AFD42B600B0842B /* SDL_clipboard_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC4D2AFD42B500B0842B /* SDL_clipboard_c.h */; }; - F3DDCC582AFD42B600B0842B /* SDL_video_capture.c in Sources */ = {isa = PBXBuildFile; fileRef = F3DDCC4F2AFD42B500B0842B /* SDL_video_capture.c */; }; - F3DDCC592AFD42B600B0842B /* SDL_video_capture_apple.m in Sources */ = {isa = PBXBuildFile; fileRef = F3DDCC502AFD42B500B0842B /* SDL_video_capture_apple.m */; }; - F3DDCC5A2AFD42B600B0842B /* SDL_video_capture_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC512AFD42B500B0842B /* SDL_video_capture_c.h */; }; F3DDCC5B2AFD42B600B0842B /* SDL_video_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC522AFD42B600B0842B /* SDL_video_c.h */; }; - F3DDCC5C2AFD42B600B0842B /* SDL_sysvideocapture.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC532AFD42B600B0842B /* SDL_sysvideocapture.h */; }; F3DDCC5D2AFD42B600B0842B /* SDL_rect_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC542AFD42B600B0842B /* SDL_rect_impl.h */; }; - F3DDCC5E2AFD42B600B0842B /* SDL_video_capture_v4l2.c in Sources */ = {isa = PBXBuildFile; fileRef = F3DDCC552AFD42B600B0842B /* SDL_video_capture_v4l2.c */; }; - F3DDCC602AFD432500B0842B /* SDL_video_capture.h in Headers */ = {isa = PBXBuildFile; fileRef = F3DDCC5F2AFD432500B0842B /* SDL_video_capture.h */; settings = {ATTRIBUTES = (Public, ); }; }; F3E5A6EB2AD5E0E600293D83 /* SDL_properties.c in Sources */ = {isa = PBXBuildFile; fileRef = F3E5A6EA2AD5E0E600293D83 /* SDL_properties.c */; }; F3E5A6ED2AD5E10800293D83 /* SDL_properties.h in Headers */ = {isa = PBXBuildFile; fileRef = F3E5A6EC2AD5E10800293D83 /* SDL_properties.h */; settings = {ATTRIBUTES = (Public, ); }; }; F3F07D5A269640160074468B /* SDL_hidapi_luna.c in Sources */ = {isa = PBXBuildFile; fileRef = F3F07D59269640160074468B /* SDL_hidapi_luna.c */; }; @@ -528,8 +525,14 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0000035D38C3899C7EFD0000 /* SDL_camera.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_camera.c; sourceTree = ""; }; 00003260407E1002EAC10000 /* SDL_main_callbacks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_main_callbacks.h; sourceTree = ""; }; + 00005BD74B46358B33A20000 /* SDL_camera_dummy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_camera_dummy.c; sourceTree = ""; }; + 00005D3EB902478835E20000 /* SDL_syscamera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_syscamera.h; sourceTree = ""; }; 000078E1881E857EBB6C0000 /* SDL_hashtable.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hashtable.c; sourceTree = ""; }; + 000084ED0A56E3ED52F90000 /* SDL_camera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_camera.h; path = SDL3/SDL_camera.h; sourceTree = ""; }; + 00008B79BF08CBCEAC460000 /* SDL_camera_coremedia.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_camera_coremedia.m; sourceTree = ""; }; + 00009003C7148E1126CA0000 /* SDL_camera_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_camera_c.h; sourceTree = ""; }; 00009366FB9FBBD54C390000 /* SDL_main_callbacks.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_main_callbacks.c; sourceTree = ""; }; 0000B6ADCD88CAD6610F0000 /* SDL_hashtable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_hashtable.h; sourceTree = ""; }; 0000BB287BA0A0178C1A0000 /* SDL_sysmain_callbacks.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_sysmain_callbacks.m; sourceTree = ""; }; @@ -946,14 +949,8 @@ F3B38CCE296E2E52005DA6D3 /* SDL_intrin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_intrin.h; path = SDL3/SDL_intrin.h; sourceTree = ""; }; F3D60A8228C16A1800788A3A /* SDL_hidapi_wii.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_wii.c; sourceTree = ""; }; F3DDCC4D2AFD42B500B0842B /* SDL_clipboard_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_clipboard_c.h; sourceTree = ""; }; - F3DDCC4F2AFD42B500B0842B /* SDL_video_capture.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_video_capture.c; sourceTree = ""; }; - F3DDCC502AFD42B500B0842B /* SDL_video_capture_apple.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDL_video_capture_apple.m; sourceTree = ""; }; - F3DDCC512AFD42B500B0842B /* SDL_video_capture_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_video_capture_c.h; sourceTree = ""; }; F3DDCC522AFD42B600B0842B /* SDL_video_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_video_c.h; sourceTree = ""; }; - F3DDCC532AFD42B600B0842B /* SDL_sysvideocapture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sysvideocapture.h; sourceTree = ""; }; F3DDCC542AFD42B600B0842B /* SDL_rect_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_rect_impl.h; sourceTree = ""; }; - F3DDCC552AFD42B600B0842B /* SDL_video_capture_v4l2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_video_capture_v4l2.c; sourceTree = ""; }; - F3DDCC5F2AFD432500B0842B /* SDL_video_capture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_video_capture.h; path = SDL3/SDL_video_capture.h; sourceTree = ""; }; F3E5A6EA2AD5E0E600293D83 /* SDL_properties.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_properties.c; sourceTree = ""; }; F3E5A6EC2AD5E10800293D83 /* SDL_properties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_properties.h; path = SDL3/SDL_properties.h; sourceTree = ""; }; F3F07D59269640160074468B /* SDL_hidapi_luna.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_luna.c; sourceTree = ""; }; @@ -1055,6 +1052,26 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 000023E01FD84242AF850000 /* dummy */ = { + isa = PBXGroup; + children = ( + 00005BD74B46358B33A20000 /* SDL_camera_dummy.c */, + ); + path = dummy; + sourceTree = ""; + }; + 00002EC7DF7A0A31B32A0000 /* camera */ = { + isa = PBXGroup; + children = ( + 0000DBB4B95F4CC5CAE80000 /* coremedia */, + 000023E01FD84242AF850000 /* dummy */, + 0000035D38C3899C7EFD0000 /* SDL_camera.c */, + 00009003C7148E1126CA0000 /* SDL_camera_c.h */, + 00005D3EB902478835E20000 /* SDL_syscamera.h */, + ); + path = camera; + sourceTree = ""; + }; 000082EF09C89B62BD840000 /* main */ = { isa = PBXGroup; children = ( @@ -1074,6 +1091,14 @@ path = ios; sourceTree = ""; }; + 0000DBB4B95F4CC5CAE80000 /* coremedia */ = { + isa = PBXGroup; + children = ( + 00008B79BF08CBCEAC460000 /* SDL_camera_coremedia.m */, + ); + path = coremedia; + sourceTree = ""; + }; 0153844A006D81B07F000001 /* Public Headers */ = { isa = PBXGroup; children = ( @@ -1141,10 +1166,10 @@ F3F7D8B22933074900816151 /* SDL_timer.h */, F3F7D8AF2933074900816151 /* SDL_touch.h */, F3F7D8E42933074D00816151 /* SDL_version.h */, - F3DDCC5F2AFD432500B0842B /* SDL_video_capture.h */, F3F7D8C52933074B00816151 /* SDL_video.h */, F3F7D8D42933074C00816151 /* SDL_vulkan.h */, F3F7D8CF2933074C00816151 /* SDL.h */, + 000084ED0A56E3ED52F90000 /* SDL_camera.h */, ); name = "Public Headers"; path = ../../include; @@ -1182,6 +1207,7 @@ children = ( A7D8A57223E2513D00DCD162 /* atomic */, A7D8A86423E2513F00DCD162 /* audio */, + 00002EC7DF7A0A31B32A0000 /* camera */, F36C7ACF294B9F5E004D61C3 /* core */, A7D8A77423E2513E00DCD162 /* cpuinfo */, A7D8A5D723E2513D00DCD162 /* dynapi */, @@ -1462,12 +1488,7 @@ A7D8A60323E2513D00DCD162 /* SDL_stretch.c */, A7D8A61423E2513D00DCD162 /* SDL_surface.c */, A7D8A61723E2513D00DCD162 /* SDL_sysvideo.h */, - F3DDCC532AFD42B600B0842B /* SDL_sysvideocapture.h */, F3DDCC522AFD42B600B0842B /* SDL_video_c.h */, - F3DDCC502AFD42B500B0842B /* SDL_video_capture_apple.m */, - F3DDCC512AFD42B500B0842B /* SDL_video_capture_c.h */, - F3DDCC552AFD42B600B0842B /* SDL_video_capture_v4l2.c */, - F3DDCC4F2AFD42B500B0842B /* SDL_video_capture.c */, E4F7981F2AD8D87F00669F54 /* SDL_video_unsupported.c */, A7D8A60E23E2513D00DCD162 /* SDL_video.c */, A7D8A63E23E2513D00DCD162 /* SDL_vulkan_internal.h */, @@ -2167,10 +2188,8 @@ A7D8B61723E2514300DCD162 /* SDL_assert_c.h in Headers */, F3F7D9292933074E00816151 /* SDL_atomic.h in Headers */, F3F7D8ED2933074E00816151 /* SDL_audio.h in Headers */, - F3DDCC602AFD432500B0842B /* SDL_video_capture.h in Headers */, A7D8B7A023E2514400DCD162 /* SDL_audio_c.h in Headers */, F3681E812B7AA6240002C6FD /* SDL_cocoashape.h in Headers */, - F3DDCC5A2AFD42B600B0842B /* SDL_video_capture_c.h in Headers */, A7D8B7B223E2514400DCD162 /* SDL_audiodev_c.h in Headers */, F3F7D9E12933074E00816151 /* SDL_begin_code.h in Headers */, F3F7D9A52933074E00816151 /* SDL_bits.h in Headers */, @@ -2186,7 +2205,6 @@ A7D8BB6F23E2514500DCD162 /* SDL_clipboardevents_c.h in Headers */, F3F7D9D92933074E00816151 /* SDL_close_code.h in Headers */, A7D8AECA23E2514100DCD162 /* SDL_cocoaclipboard.h in Headers */, - F3DDCC5C2AFD42B600B0842B /* SDL_sysvideocapture.h in Headers */, A7D8AF1223E2514100DCD162 /* SDL_cocoaevents.h in Headers */, A7D8AE8E23E2514100DCD162 /* SDL_cocoakeyboard.h in Headers */, A7D8AF0623E2514100DCD162 /* SDL_cocoamessagebox.h in Headers */, @@ -2549,7 +2567,6 @@ A7D8AEC423E2514100DCD162 /* SDL_cocoaevents.m in Sources */, A7D8B86623E2514400DCD162 /* SDL_audiocvt.c in Sources */, A7D8B9F523E2514400DCD162 /* SDL_rotate.c in Sources */, - F3DDCC5E2AFD42B600B0842B /* SDL_video_capture_v4l2.c in Sources */, A7D8BBE323E2574800DCD162 /* SDL_uikitvideo.m in Sources */, 5616CA4E252BB2A6005D5928 /* SDL_sysurl.m in Sources */, A7D8A97523E2514000DCD162 /* SDL_coremotionsensor.m in Sources */, @@ -2566,7 +2583,6 @@ A7D8AE8823E2514100DCD162 /* SDL_cocoaopengl.m in Sources */, A7D8AB7323E2514100DCD162 /* SDL_offscreenframebuffer.c in Sources */, A7D8B43423E2514300DCD162 /* SDL_systhread.c in Sources */, - F3DDCC592AFD42B600B0842B /* SDL_video_capture_apple.m in Sources */, A7D8BB3323E2514500DCD162 /* SDL_windowevents.c in Sources */, A7D8BABB23E2514400DCD162 /* s_scalbn.c in Sources */, F3973FAB28A59BDD00B84553 /* SDL_crc16.c in Sources */, @@ -2602,7 +2618,6 @@ A7D8B86023E2514400DCD162 /* SDL_audiotypecvt.c in Sources */, A7D8BBC523E2561500DCD162 /* SDL_steamcontroller.c in Sources */, A7D8AD3223E2514100DCD162 /* SDL_blit_N.c in Sources */, - F3DDCC582AFD42B600B0842B /* SDL_video_capture.c in Sources */, A7D8BB7B23E2514500DCD162 /* SDL_dropevents.c in Sources */, A7D8BACD23E2514500DCD162 /* e_atan2.c in Sources */, A7D8BA8B23E2514400DCD162 /* s_sin.c in Sources */, @@ -2736,6 +2751,9 @@ 000040E76FDC6AE48CBF0000 /* SDL_hashtable.c in Sources */, 0000A4DA2F45A31DC4F00000 /* SDL_sysmain_callbacks.m in Sources */, 000028F8113A53F4333E0000 /* SDL_main_callbacks.c in Sources */, + 000098E9DAA43EF6FF7F0000 /* SDL_camera.c in Sources */, + 00001B2471F503DD3C1B0000 /* SDL_camera_dummy.c in Sources */, + 00002B20A48E055EB0350000 /* SDL_camera_coremedia.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/camera/coremedia/SDL_camera_coremedia.m b/src/camera/coremedia/SDL_camera_coremedia.m index 69f2815ed3535..c37962026c205 100644 --- a/src/camera/coremedia/SDL_camera_coremedia.m +++ b/src/camera/coremedia/SDL_camera_coremedia.m @@ -24,7 +24,7 @@ #include "../SDL_syscamera.h" #include "../SDL_camera_c.h" -#include "../thread/SDL_systhread.h" +#include "../../thread/SDL_systhread.h" #if defined(HAVE_COREMEDIA) && defined(SDL_PLATFORM_MACOS) && (__MAC_OS_X_VERSION_MAX_ALLOWED < 101500) // AVCaptureDeviceTypeBuiltInWideAngleCamera requires macOS SDK 10.15 From 0b5875825e42eb56623847a7c812fc01d03182c4 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 17 Dec 2023 13:38:36 -0500 Subject: [PATCH 042/220] camera: framerate support. --- include/SDL3/SDL_camera.h | 11 ++++- src/camera/SDL_camera.c | 51 +++++++++++++++++++++- src/camera/v4l2/SDL_camera_v4l2.c | 70 ++++++++++++++++++++++++++++--- 3 files changed, 122 insertions(+), 10 deletions(-) diff --git a/include/SDL3/SDL_camera.h b/include/SDL3/SDL_camera.h index d31e045508264..c19e50db9e9a4 100644 --- a/include/SDL3/SDL_camera.h +++ b/include/SDL3/SDL_camera.h @@ -66,6 +66,8 @@ typedef struct SDL_CameraSpec Uint32 format; /**< Frame SDL_PixelFormatEnum format */ int width; /**< Frame width */ int height; /**< Frame height */ + int interval_numerator; /**< Frame rate numerator ((dom / num) == fps) */ + int interval_denominator; /**< Frame rate demoninator ((dom / num) == fps)*/ } SDL_CameraSpec; /** @@ -216,6 +218,12 @@ extern DECLSPEC char * SDLCALL SDL_GetCameraDeviceName(SDL_CameraDeviceID instan * passing a NULL spec here. You can see the exact specs a device can * support without conversion with SDL_GetCameraSupportedSpecs(). * + * SDL will not attempt to emulate framerate; it will try to set the + * hardware to the rate closest to the requested speed, but it won't + * attempt to limit or duplicate frames artificially; call + * SDL_GetCameraFormat() to see the actual framerate of the opened the device, + * and check your timestamps if this is crucial to your app! + * * \param instance_id the camera device instance ID * \param spec The desired format for data the device will provide. Can be NULL. * \returns device, or NULL on failure; call SDL_GetError() for more @@ -225,9 +233,8 @@ extern DECLSPEC char * SDLCALL SDL_GetCameraDeviceName(SDL_CameraDeviceID instan * * \since This function is available since SDL 3.0.0. * - * \sa SDL_GetCameraDeviceName * \sa SDL_GetCameraDevices - * \sa SDL_OpenCameraWithSpec + * \sa SDL_GetCameraFormat */ extern DECLSPEC SDL_Camera *SDLCALL SDL_OpenCameraDevice(SDL_CameraDeviceID instance_id, const SDL_CameraSpec *spec); diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index 18efa3f282e70..c8af9f0e2e28b 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -224,6 +224,21 @@ static int SDLCALL CameraSpecCmp(const void *vpa, const void *vpb) return 1; } + // still here? We care about framerate less than format or size, but faster is better than slow. + if (a->interval_numerator && !b->interval_numerator) { + return -1; + } else if (!a->interval_numerator && b->interval_numerator) { + return 1; + } + + const float fpsa = ((float) a->interval_denominator)/ ((float) a->interval_numerator); + const float fpsb = ((float) b->interval_denominator)/ ((float) b->interval_numerator); + if (fpsa > fpsb) { + return -1; + } else if (fpsb > fpsa) { + return 1; + } + return 0; // apparently, they're equal. } @@ -276,13 +291,21 @@ SDL_CameraDevice *SDL_AddCameraDevice(const char *name, int num_specs, const SDL for (int i = 0; i < num_specs; i++) { SDL_CameraSpec *a = &device->all_specs[i]; SDL_CameraSpec *b = &device->all_specs[i + 1]; - if ((a->format == b->format) && (a->width == b->width) && (a->height == b->height)) { + if (SDL_memcmp(a, b, sizeof (*a)) == 0) { SDL_memmove(a, b, sizeof (*specs) * (num_specs - i)); i--; num_specs--; } } + #if DEBUG_CAMERA + SDL_Log("CAMERA: Adding device ('%s') with %d spec%s:", name, num_specs, (num_specs == 1) ? "" : "s"); + for (int i = 0; i < num_specs; i++) { + const SDL_CameraSpec *spec = &device->all_specs[i]; + SDL_Log("CAMERA: - fmt=%s, w=%d, h=%d, numerator=%d, denominator=%d", SDL_GetPixelFormatName(spec->format), spec->width, spec->height, spec->interval_numerator, spec->interval_denominator); + } + #endif + device->num_specs = num_specs; device->handle = handle; device->instance_id = SDL_GetNextObjectID(); @@ -734,6 +757,28 @@ static void ChooseBestCameraSpec(SDL_CameraDevice *device, const SDL_CameraSpec SDL_assert(bestfmt != SDL_PIXELFORMAT_UNKNOWN); closest->format = bestfmt; + + // We have a resolution and a format, find the closest framerate... + const float wantfps = spec->interval_numerator ? (spec->interval_denominator / spec->interval_numerator) : 0.0f; + float closestfps = 9999999.0f; + for (int i = 0; i < num_specs; i++) { + const SDL_CameraSpec *thisspec = &device->all_specs[i]; + if ((thisspec->format == closest->format) && (thisspec->width == closest->width) && (thisspec->height == closest->height)) { + if ((thisspec->interval_numerator == spec->interval_numerator) && (thisspec->interval_denominator == spec->interval_denominator)) { + closest->interval_numerator = thisspec->interval_numerator; + closest->interval_denominator = thisspec->interval_denominator; + break; // exact match, stop looking. + } + + const float thisfps = thisspec->interval_numerator ? (thisspec->interval_denominator / thisspec->interval_numerator) : 0.0f; + const float fpsdiff = SDL_fabs(wantfps - thisfps); + if (fpsdiff < closestfps) { // this is a closest FPS? Take it until something closer arrives. + closestfps = fpsdiff; + closest->interval_numerator = thisspec->interval_numerator; + closest->interval_denominator = thisspec->interval_denominator; + } + } + } } SDL_assert(closest->width > 0); @@ -770,7 +815,9 @@ SDL_Camera *SDL_OpenCameraDevice(SDL_CameraDeviceID instance_id, const SDL_Camer ChooseBestCameraSpec(device, spec, &closest); #if DEBUG_CAMERA - SDL_Log("CAMERA: App wanted [(%dx%d) fmt=%s], chose [(%dx%d) fmt=%s]", spec ? spec->width : -1, spec ? spec->height : -1, spec ? SDL_GetPixelFormatName(spec->format) : "(null)", closest.width, closest.height, SDL_GetPixelFormatName(closest.format)); + SDL_Log("CAMERA: App wanted [(%dx%d) fmt=%s interval=%d/%d], chose [(%dx%d) fmt=%s interval=%d/%d]", + spec ? spec->width : -1, spec ? spec->height : -1, spec ? SDL_GetPixelFormatName(spec->format) : "(null)", spec ? spec->interval_numerator : -1, spec ? spec->interval_denominator : -1, + closest.width, closest.height, SDL_GetPixelFormatName(closest.format), closest.interval_numerator, closest.interval_denominator); #endif if (camera_driver.impl.OpenDevice(device, &closest) < 0) { diff --git a/src/camera/v4l2/SDL_camera_v4l2.c b/src/camera/v4l2/SDL_camera_v4l2.c index 7b68378fd442f..6e5cadd7ce919 100644 --- a/src/camera/v4l2/SDL_camera_v4l2.c +++ b/src/camera/v4l2/SDL_camera_v4l2.c @@ -524,6 +524,23 @@ static int V4L2_OpenDevice(SDL_CameraDevice *device, const SDL_CameraSpec *spec) return SDL_SetError("Error VIDIOC_S_FMT"); } + if (spec->interval_numerator && spec->interval_denominator) { + struct v4l2_streamparm setfps; + SDL_zero(setfps); + setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (xioctl(fd, VIDIOC_G_PARM, &setfps) == 0) { + if ( (setfps.parm.capture.timeperframe.numerator != spec->interval_numerator) || + (setfps.parm.capture.timeperframe.denominator = spec->interval_denominator) ) { + setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + setfps.parm.capture.timeperframe.numerator = spec->interval_numerator; + setfps.parm.capture.timeperframe.denominator = spec->interval_denominator; + if (xioctl(fd, VIDIOC_S_PARM, &setfps) == -1) { + return SDL_SetError("Error VIDIOC_S_PARM"); + } + } + } + } + SDL_zero(fmt); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (xioctl(fd, VIDIOC_G_FMT, &fmt) == -1) { @@ -618,7 +635,7 @@ typedef struct FormatAddData int allocated_specs; } FormatAddData; -static int AddCameraFormat(FormatAddData *data, Uint32 fmt, int w, int h) +static int AddCameraCompleteFormat(FormatAddData *data, Uint32 fmt, int w, int h, int interval_numerator, int interval_denominator) { SDL_assert(data != NULL); if (data->allocated_specs <= data->num_specs) { @@ -635,12 +652,55 @@ static int AddCameraFormat(FormatAddData *data, Uint32 fmt, int w, int h) spec->format = fmt; spec->width = w; spec->height = h; + spec->interval_numerator = interval_numerator; + spec->interval_denominator = interval_denominator; data->num_specs++; return 0; } +static int AddCameraFormat(const int fd, FormatAddData *data, Uint32 sdlfmt, Uint32 v4l2fmt, int w, int h) +{ + struct v4l2_frmivalenum frmivalenum; + SDL_zero(frmivalenum); + frmivalenum.pixel_format = v4l2fmt; + frmivalenum.width = (Uint32) w; + frmivalenum.height = (Uint32) h; + + while (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmivalenum) == 0) { + if (frmivalenum.type == V4L2_FRMIVAL_TYPE_DISCRETE) { + const int numerator = (int) frmivalenum.discrete.numerator; + const int denominator = (int) frmivalenum.discrete.denominator; + #if DEBUG_CAMERA + const float fps = (float) denominator / (float) numerator; + SDL_Log("CAMERA: * Has discrete frame interval (%d / %d), fps=%f", numerator, denominator, fps); + #endif + if (AddCameraCompleteFormat(data, sdlfmt, w, h, numerator, denominator) == -1) { + return -1; // Probably out of memory; we'll go with what we have, if anything. + } + frmivalenum.index++; // set up for the next one. + } else if ((frmivalenum.type == V4L2_FRMIVAL_TYPE_STEPWISE) || (frmivalenum.type == V4L2_FRMIVAL_TYPE_CONTINUOUS)) { + int d = frmivalenum.stepwise.min.denominator; + // !!! FIXME: should we step by the numerator...? + for (int n = (int) frmivalenum.stepwise.min.numerator; n <= (int) frmivalenum.stepwise.max.numerator; n += (int) frmivalenum.stepwise.step.numerator) { + #if DEBUG_CAMERA + const float fps = (float) d / (float) n; + SDL_Log("CAMERA: * Has %s frame interval (%d / %d), fps=%f", (frmivalenum.type == V4L2_FRMIVAL_TYPE_STEPWISE) ? "stepwise" : "continuous", n, d, fps); + #endif + if (AddCameraCompleteFormat(data, sdlfmt, w, h, n, d) == -1) { + return -1; // Probably out of memory; we'll go with what we have, if anything. + } + d += (int) frmivalenum.stepwise.step.denominator; + } + break; + } + } + + return 0; +} + + static void MaybeAddDevice(const char *path) { if (!path) { @@ -699,17 +759,16 @@ static void MaybeAddDevice(const char *path) struct v4l2_frmsizeenum frmsizeenum; SDL_zero(frmsizeenum); - frmsizeenum.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; frmsizeenum.pixel_format = fmtdesc.pixelformat; - while (ioctl(fd,VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == 0) { + while (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == 0) { if (frmsizeenum.type == V4L2_FRMSIZE_TYPE_DISCRETE) { const int w = (int) frmsizeenum.discrete.width; const int h = (int) frmsizeenum.discrete.height; #if DEBUG_CAMERA SDL_Log("CAMERA: * Has discrete size %dx%d", w, h); #endif - if (AddCameraFormat(&add_data, sdlfmt, w, h) == -1) { + if (AddCameraFormat(fd, &add_data, sdlfmt, fmtdesc.pixelformat, w, h) == -1) { break; // Probably out of memory; we'll go with what we have, if anything. } frmsizeenum.index++; // set up for the next one. @@ -725,10 +784,9 @@ static void MaybeAddDevice(const char *path) #if DEBUG_CAMERA SDL_Log("CAMERA: * Has %s size %dx%d", (frmsizeenum.type == V4L2_FRMSIZE_TYPE_STEPWISE) ? "stepwise" : "continuous", w, h); #endif - if (AddCameraFormat(&add_data, sdlfmt, w, h) == -1) { + if (AddCameraFormat(fd, &add_data, sdlfmt, fmtdesc.pixelformat, w, h) == -1) { break; // Probably out of memory; we'll go with what we have, if anything. } - } } break; From 2cdff93578cf5ad6d3c480b47b6697b18c99802b Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 17 Dec 2023 15:45:13 -0500 Subject: [PATCH 043/220] v4l2: Corrected SDL_UDEV_AddCallback return check after #8694. --- src/camera/v4l2/SDL_camera_v4l2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/camera/v4l2/SDL_camera_v4l2.c b/src/camera/v4l2/SDL_camera_v4l2.c index 6e5cadd7ce919..fd76ad4f675ab 100644 --- a/src/camera/v4l2/SDL_camera_v4l2.c +++ b/src/camera/v4l2/SDL_camera_v4l2.c @@ -869,7 +869,7 @@ static void V4L2_DetectDevices(void) { #ifdef SDL_USE_LIBUDEV if (SDL_UDEV_Init() == 0) { - if (SDL_UDEV_AddCallback(CameraUdevCallback) >= 0) { // !!! FIXME: this should return 0 on success, it currently returns 1. + if (SDL_UDEV_AddCallback(CameraUdevCallback) == 0) { SDL_UDEV_Scan(); // Force a scan to build the initial device list } } From 182f7072847e4ae41f5144480ad3a34d8c1a0c7c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 17 Dec 2023 19:28:32 -0500 Subject: [PATCH 044/220] include: Fixed doxygen comments on new camera APIs. --- include/SDL3/SDL_camera.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/SDL3/SDL_camera.h b/include/SDL3/SDL_camera.h index c19e50db9e9a4..0204fe9bdca55 100644 --- a/include/SDL3/SDL_camera.h +++ b/include/SDL3/SDL_camera.h @@ -241,7 +241,7 @@ extern DECLSPEC SDL_Camera *SDLCALL SDL_OpenCameraDevice(SDL_CameraDeviceID inst /** * Get the instance ID of an opened camera. * - * \param device an SDL_Camera to query + * \param camera an SDL_Camera to query * \returns the instance ID of the specified camera on success or 0 on * failure; call SDL_GetError() for more information. * @@ -256,7 +256,7 @@ extern DECLSPEC SDL_CameraDeviceID SDLCALL SDL_GetCameraInstanceID(SDL_Camera *c /** * Get the properties associated with an opened camera. * - * \param device the SDL_Camera obtained from SDL_OpenCameraDevice() + * \param camera the SDL_Camera obtained from SDL_OpenCameraDevice() * \returns a valid property ID on success or 0 on failure; call * SDL_GetError() for more information. * @@ -275,7 +275,7 @@ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetCameraProperties(SDL_Camera *cam * Note that this might not be the native format of the hardware, as SDL * might be converting to this format behind the scenes. * - * \param device opened camera device + * \param camera opened camera device * \param spec The SDL_CameraSpec to be initialized by this function. * \returns 0 on success or a negative error code on failure; call * SDL_GetError() for more information. @@ -312,7 +312,7 @@ extern DECLSPEC int SDLCALL SDL_GetCameraFormat(SDL_Camera *camera, SDL_CameraSp * Do not call SDL_FreeSurface() on the returned surface! It must be given back * to the camera subsystem with SDL_ReleaseCameraFrame! * - * \param device opened camera device + * \param camera opened camera device * \param timestampNS a pointer filled in with the frame's timestamp, or 0 on error. Can be NULL. * \returns A new frame of video on success, NULL if none is currently available. * @@ -341,7 +341,7 @@ extern DECLSPEC SDL_Surface * SDLCALL SDL_AcquireCameraFrame(SDL_Camera *camera, * The app should not use the surface again after calling this function; * assume the surface is freed and the pointer is invalid. * - * \param device opened camera device + * \param camera opened camera device * \param frame The video frame surface to release. * \returns 0 on success or a negative error code on failure; call * SDL_GetError() for more information. @@ -358,7 +358,7 @@ extern DECLSPEC int SDLCALL SDL_ReleaseCameraFrame(SDL_Camera *camera, SDL_Surfa * Use this function to shut down camera processing and close the * camera device. * - * \param device opened camera device + * \param camera opened camera device * * \threadsafety It is safe to call this function from any thread, but * no thread may reference `device` once this function From 67708f91100e901c309a62beb4a39c8f6d434c9a Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 22 Dec 2023 01:23:49 -0500 Subject: [PATCH 045/220] camera: Emscripten support! This also adds code to deal with waiting for the user to approve camera access, reworks testcameraminimal to use main callbacks, etc. --- CMakeLists.txt | 6 + include/SDL3/SDL_camera.h | 65 ++++- include/SDL3/SDL_events.h | 4 +- include/build_config/SDL_build_config.h.cmake | 1 + .../SDL_build_config_emscripten.h | 4 +- src/camera/SDL_camera.c | 118 ++++++-- src/camera/SDL_syscamera.h | 7 + src/camera/emscripten/SDL_camera_emscripten.c | 269 ++++++++++++++++++ src/camera/v4l2/SDL_camera_v4l2.c | 3 + src/dynapi/SDL_dynapi.sym | 1 + src/dynapi/SDL_dynapi_overrides.h | 1 + src/dynapi/SDL_dynapi_procs.h | 1 + src/events/SDL_events.c | 6 + test/testcameraminimal.c | 214 +++++++------- 14 files changed, 557 insertions(+), 143 deletions(-) create mode 100644 src/camera/emscripten/SDL_camera_emscripten.c diff --git a/CMakeLists.txt b/CMakeLists.txt index b6eefb29650dc..74ed3a10de2da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1423,6 +1423,12 @@ elseif(EMSCRIPTEN) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/filesystem/emscripten/*.c") set(HAVE_SDL_FILESYSTEM TRUE) + if(SDL_CAMERA) + set(SDL_CAMERA_DRIVER_EMSCRIPTEN 1) + set(HAVE_CAMERA TRUE) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/camera/emscripten/*.c") + endif() + if(SDL_JOYSTICK) set(SDL_JOYSTICK_EMSCRIPTEN 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/joystick/emscripten/*.c") diff --git a/include/SDL3/SDL_camera.h b/include/SDL3/SDL_camera.h index 0204fe9bdca55..a57a66a069967 100644 --- a/include/SDL3/SDL_camera.h +++ b/include/SDL3/SDL_camera.h @@ -171,6 +171,13 @@ extern DECLSPEC SDL_CameraDeviceID *SDLCALL SDL_GetCameraDevices(int *count); * The returned list is owned by the caller, and should be released with * SDL_free() when no longer needed. * + * Note that it's legal for a camera to supply a list with only the zeroed + * final element and `*count` set to zero; this is what will happen on + * Emscripten builds, since that platform won't tell _anything_ about + * available cameras until you've opened one, and won't even tell if there + * _is_ a camera until the user has given you permission to check through + * a scary warning popup. + * * \param devid the camera device instance ID to query. * \param count a pointer filled in with the number of elements in the list. Can be NULL. * \returns a 0 terminated array of SDL_CameraSpecs, which should be @@ -224,6 +231,16 @@ extern DECLSPEC char * SDLCALL SDL_GetCameraDeviceName(SDL_CameraDeviceID instan * SDL_GetCameraFormat() to see the actual framerate of the opened the device, * and check your timestamps if this is crucial to your app! * + * Note that the camera is not usable until the user approves its use! On + * some platforms, the operating system will prompt the user to permit access + * to the camera, and they can choose Yes or No at that point. Until they do, + * the camera will not be usable. The app should either wait for an + * SDL_EVENT_CAMERA_DEVICE_APPROVED (or SDL_EVENT_CAMERA_DEVICE_DENIED) event, + * or poll SDL_IsCameraApproved() occasionally until it returns non-zero. On + * platforms that don't require explicit user approval (and perhaps in places + * where the user previously permitted access), the approval event might come + * immediately, but it might come seconds, minutes, or hours later! + * * \param instance_id the camera device instance ID * \param spec The desired format for data the device will provide. Can be NULL. * \returns device, or NULL on failure; call SDL_GetError() for more @@ -238,6 +255,38 @@ extern DECLSPEC char * SDLCALL SDL_GetCameraDeviceName(SDL_CameraDeviceID instan */ extern DECLSPEC SDL_Camera *SDLCALL SDL_OpenCameraDevice(SDL_CameraDeviceID instance_id, const SDL_CameraSpec *spec); +/** + * Query if camera access has been approved by the user. + * + * Cameras will not function between when the device is opened by the app + * and when the user permits access to the hardware. On some platforms, this + * presents as a popup dialog where the user has to explicitly approve access; + * on others the approval might be implicit and not alert the user at all. + * + * This function can be used to check the status of that approval. It will + * return 0 if still waiting for user response, 1 if the camera is approved + * for use, and -1 if the user denied access. + * + * Instead of polling with this function, you can wait for a + * SDL_EVENT_CAMERA_DEVICE_APPROVED (or SDL_EVENT_CAMERA_DEVICE_DENIED) event + * in the standard SDL event loop, which is guaranteed to be sent once when + * permission to use the camera is decided. + * + * If a camera is declined, there's nothing to be done but call + * SDL_CloseCamera() to dispose of it. + * + * \param camera the opened camera device to query + * \returns -1 if user denied access to the camera, 1 if user approved access, 0 if no decision has been made yet. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_OpenCameraDevice + * \sa SDL_CloseCamera + */ +extern DECLSPEC int SDLCALL SDL_GetCameraPermissionState(SDL_Camera *camera); + /** * Get the instance ID of an opened camera. * @@ -275,6 +324,12 @@ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetCameraProperties(SDL_Camera *cam * Note that this might not be the native format of the hardware, as SDL * might be converting to this format behind the scenes. * + * If the system is waiting for the user to approve access to the camera, as + * some platforms require, this will return -1, but this isn't necessarily a + * fatal error; you should either wait for an SDL_EVENT_CAMERA_DEVICE_APPROVED + * (or SDL_EVENT_CAMERA_DEVICE_DENIED) event, or poll SDL_IsCameraApproved() + * occasionally until it returns non-zero. + * * \param camera opened camera device * \param spec The SDL_CameraSpec to be initialized by this function. * \returns 0 on success or a negative error code on failure; call @@ -305,13 +360,17 @@ extern DECLSPEC int SDLCALL SDL_GetCameraFormat(SDL_Camera *camera, SDL_CameraSp * failure here is almost always an out of memory condition. * * After use, the frame should be released with SDL_ReleaseCameraFrame(). If you - * don't do this, the system may stop providing more video! If the hardware is - * using DMA to write directly into memory, frames held too long may be overwritten - * with new data. + * don't do this, the system may stop providing more video! * * Do not call SDL_FreeSurface() on the returned surface! It must be given back * to the camera subsystem with SDL_ReleaseCameraFrame! * + * If the system is waiting for the user to approve access to the camera, as + * some platforms require, this will return NULL (no frames available); you should + * either wait for an SDL_EVENT_CAMERA_DEVICE_APPROVED (or + * SDL_EVENT_CAMERA_DEVICE_DENIED) event, or poll SDL_IsCameraApproved() + * occasionally until it returns non-zero. + * * \param camera opened camera device * \param timestampNS a pointer filled in with the frame's timestamp, or 0 on error. Can be NULL. * \returns A new frame of video on success, NULL if none is currently available. diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index 74a39267c1881..cbd1f2a8af6df 100644 --- a/include/SDL3/SDL_events.h +++ b/include/SDL3/SDL_events.h @@ -208,6 +208,8 @@ typedef enum /* Camera hotplug events */ SDL_EVENT_CAMERA_DEVICE_ADDED = 0x1400, /**< A new camera device is available */ SDL_EVENT_CAMERA_DEVICE_REMOVED, /**< A camera device has been removed. */ + SDL_EVENT_CAMERA_DEVICE_APPROVED, /**< A camera device has been approved for use by the user. */ + SDL_EVENT_CAMERA_DEVICE_DENIED, /**< A camera device has been denied for use by the user. */ /* Render events */ SDL_EVENT_RENDER_TARGETS_RESET = 0x2000, /**< The render targets have been reset and their contents need to be updated */ @@ -535,7 +537,7 @@ typedef struct SDL_AudioDeviceEvent */ typedef struct SDL_CameraDeviceEvent { - Uint32 type; /**< ::SDL_EVENT_CAMERA_DEVICE_ADDED, or ::SDL_EVENT_CAMERA_DEVICE_REMOVED */ + Uint32 type; /**< ::SDL_EVENT_CAMERA_DEVICE_ADDED, ::SDL_EVENT_CAMERA_DEVICE_REMOVED, ::SDL_EVENT_CAMERA_DEVICE_APPROVED, ::SDL_EVENT_CAMERA_DEVICE_DENIED */ Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ SDL_CameraDeviceID which; /**< SDL_CameraDeviceID for the device being added or removed or changing */ Uint8 padding1; diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index 7985065dcbfc4..9f4302a79a5f2 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -472,6 +472,7 @@ #cmakedefine SDL_CAMERA_DRIVER_V4L2 @SDL_CAMERA_DRIVER_V4L2@ #cmakedefine SDL_CAMERA_DRIVER_COREMEDIA @SDL_CAMERA_DRIVER_COREMEDIA@ #cmakedefine SDL_CAMERA_DRIVER_ANDROID @SDL_CAMERA_DRIVER_ANDROID@ +#cmakedefine SDL_CAMERA_DRIVER_EMSCRIPTEN @SDL_CAMERA_DRIVER_EMSCRIPTEND@ /* Enable misc subsystem */ #cmakedefine SDL_MISC_DUMMY @SDL_MISC_DUMMY@ diff --git a/include/build_config/SDL_build_config_emscripten.h b/include/build_config/SDL_build_config_emscripten.h index 07a94d615dce4..9e4ad6aa25ee5 100644 --- a/include/build_config/SDL_build_config_emscripten.h +++ b/include/build_config/SDL_build_config_emscripten.h @@ -209,7 +209,7 @@ /* Enable system filesystem support */ #define SDL_FILESYSTEM_EMSCRIPTEN 1 -/* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */ -#define SDL_CAMERA_DRIVER_DUMMY 1 +/* Enable the camera driver */ +#define SDL_CAMERA_DRIVER_EMSCRIPTEN 1 #endif /* SDL_build_config_emscripten_h */ diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index c8af9f0e2e28b..01179dc44f97b 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -40,6 +40,9 @@ static const CameraBootStrap *const bootstrap[] = { #ifdef SDL_CAMERA_DRIVER_ANDROID &ANDROIDCAMERA_bootstrap, #endif +#ifdef SDL_CAMERA_DRIVER_EMSCRIPTEN + &EMSCRIPTENCAMERA_bootstrap, +#endif #ifdef SDL_CAMERA_DRIVER_DUMMY &DUMMYCAMERA_bootstrap, #endif @@ -247,8 +250,8 @@ static int SDLCALL CameraSpecCmp(const void *vpa, const void *vpb) SDL_CameraDevice *SDL_AddCameraDevice(const char *name, int num_specs, const SDL_CameraSpec *specs, void *handle) { SDL_assert(name != NULL); - SDL_assert(num_specs > 0); - SDL_assert(specs != NULL); + SDL_assert(num_specs >= 0); + SDL_assert((specs != NULL) == (num_specs > 0)); SDL_assert(handle != NULL); SDL_LockRWLockForReading(camera_driver.device_hash_lock); @@ -284,22 +287,24 @@ SDL_CameraDevice *SDL_AddCameraDevice(const char *name, int num_specs, const SDL return NULL; } - SDL_memcpy(device->all_specs, specs, sizeof (*specs) * num_specs); - SDL_qsort(device->all_specs, num_specs, sizeof (*specs), CameraSpecCmp); + if (num_specs > 0) { + SDL_memcpy(device->all_specs, specs, sizeof (*specs) * num_specs); + SDL_qsort(device->all_specs, num_specs, sizeof (*specs), CameraSpecCmp); - // weed out duplicates, just in case. - for (int i = 0; i < num_specs; i++) { - SDL_CameraSpec *a = &device->all_specs[i]; - SDL_CameraSpec *b = &device->all_specs[i + 1]; - if (SDL_memcmp(a, b, sizeof (*a)) == 0) { - SDL_memmove(a, b, sizeof (*specs) * (num_specs - i)); - i--; - num_specs--; + // weed out duplicates, just in case. + for (int i = 0; i < num_specs; i++) { + SDL_CameraSpec *a = &device->all_specs[i]; + SDL_CameraSpec *b = &device->all_specs[i + 1]; + if (SDL_memcmp(a, b, sizeof (*a)) == 0) { + SDL_memmove(a, b, sizeof (*specs) * (num_specs - i)); + i--; + num_specs--; + } } } #if DEBUG_CAMERA - SDL_Log("CAMERA: Adding device ('%s') with %d spec%s:", name, num_specs, (num_specs == 1) ? "" : "s"); + SDL_Log("CAMERA: Adding device ('%s') with %d spec%s%s", name, num_specs, (num_specs == 1) ? "" : "s", (num_specs == 0) ? "" : ":"); for (int i = 0; i < num_specs; i++) { const SDL_CameraSpec *spec = &device->all_specs[i]; SDL_Log("CAMERA: - fmt=%s, w=%d, h=%d, numerator=%d, denominator=%d", SDL_GetPixelFormatName(spec->format), spec->width, spec->height, spec->interval_numerator, spec->interval_denominator); @@ -398,6 +403,42 @@ sdfsdf } } +void SDL_CameraDevicePermissionOutcome(SDL_CameraDevice *device, SDL_bool approved) +{ + if (!device) { + return; + } + + SDL_PendingCameraDeviceEvent pending; + pending.next = NULL; + SDL_PendingCameraDeviceEvent *pending_tail = &pending; + + const int permission = approved ? 1 : -1; + + ObtainPhysicalCameraDeviceObj(device); + if (device->permission != permission) { + device->permission = permission; + SDL_PendingCameraDeviceEvent *p = (SDL_PendingCameraDeviceEvent *) SDL_malloc(sizeof (SDL_PendingCameraDeviceEvent)); + if (p) { // if this failed, no event for you, but you have deeper problems anyhow. + p->type = approved ? SDL_EVENT_CAMERA_DEVICE_APPROVED : SDL_EVENT_CAMERA_DEVICE_DENIED; + p->devid = device->instance_id; + p->next = NULL; + pending_tail->next = p; + pending_tail = p; + } + } + + ReleaseCameraDevice(device); + + SDL_LockRWLockForWriting(camera_driver.device_hash_lock); + SDL_assert(camera_driver.pending_events_tail != NULL); + SDL_assert(camera_driver.pending_events_tail->next == NULL); + camera_driver.pending_events_tail->next = pending.next; + camera_driver.pending_events_tail = pending_tail; + SDL_UnlockRWLock(camera_driver.device_hash_lock); +} + + SDL_CameraDevice *SDL_FindPhysicalCameraDeviceByCallback(SDL_bool (*callback)(SDL_CameraDevice *device, void *userdata), void *userdata) { if (!SDL_GetCurrentCameraDriver()) { @@ -439,7 +480,14 @@ int SDL_GetCameraFormat(SDL_Camera *camera, SDL_CameraSpec *spec) } SDL_CameraDevice *device = (SDL_CameraDevice *) camera; // currently there's no separation between physical and logical device. - SDL_copyp(spec, &device->spec); + ObtainPhysicalCameraDeviceObj(device); + const int retval = (device->permission > 0) ? 0 : SDL_SetError("Camera permission has not been granted"); + if (retval == 0) { + SDL_copyp(spec, &device->spec); + } else { + SDL_zerop(spec); + } + ReleaseCameraDevice(device); return 0; } @@ -545,7 +593,11 @@ SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device) return SDL_FALSE; // we're done, shut it down. } - // !!! FIXME: this should block elsewhere without holding the lock until a frame is available, like the audio subsystem does. + const int permission = device->permission; + if (permission <= 0) { + SDL_UnlockMutex(device->lock); + return (permission < 0) ? SDL_FALSE : SDL_TRUE; // if permission was denied, shut it down. if undecided, we're done for now. + } SDL_bool failed = SDL_FALSE; // set to true if disaster worthy of treating the device as lost has happened. SDL_Surface *acquired = NULL; @@ -661,7 +713,7 @@ static int SDLCALL CameraThread(void *devicep) SDL_CameraDevice *device = (SDL_CameraDevice *) devicep; #if DEBUG_CAMERA - SDL_Log("CAMERA: Start thread 'SDL_CameraThread'"); + SDL_Log("CAMERA: dev[%p] Start thread 'CameraThread'", devicep); #endif SDL_assert(device != NULL); @@ -676,7 +728,7 @@ static int SDLCALL CameraThread(void *devicep) SDL_CameraThreadShutdown(device); #if DEBUG_CAMERA - SDL_Log("CAMERA: dev[%p] End thread 'SDL_CameraThread'", (void *)device); + SDL_Log("CAMERA: dev[%p] End thread 'CameraThread'", devicep); #endif return 0; @@ -697,9 +749,13 @@ static void ChooseBestCameraSpec(SDL_CameraDevice *device, const SDL_CameraSpec SDL_zerop(closest); SDL_assert(((Uint32) SDL_PIXELFORMAT_UNKNOWN) == 0); // since we SDL_zerop'd to this value. - if (!spec) { // nothing specifically requested, get the best format we can... + if (device->num_specs == 0) { // device listed no specs! You get whatever you want! + if (spec) { + SDL_copyp(closest, spec); + } + return; + } else if (!spec) { // nothing specifically requested, get the best format we can... // we sorted this into the "best" format order when adding the camera. - SDL_assert(device->num_specs > 0); SDL_copyp(closest, &device->all_specs[0]); } else { // specific thing requested, try to get as close to that as possible... const int num_specs = device->num_specs; @@ -924,6 +980,12 @@ SDL_Surface *SDL_AcquireCameraFrame(SDL_Camera *camera, Uint64 *timestampNS) ObtainPhysicalCameraDeviceObj(device); + if (device->permission <= 0) { + ReleaseCameraDevice(device); + SDL_SetError("Camera permission has not been granted"); + return NULL; + } + SDL_Surface *retval = NULL; // frames are in this list from newest to oldest, so find the end of the list... @@ -996,8 +1058,6 @@ int SDL_ReleaseCameraFrame(SDL_Camera *camera, SDL_Surface *frame) return 0; } -// !!! FIXME: add a way to "pause" camera output. - SDL_CameraDeviceID SDL_GetCameraInstanceID(SDL_Camera *camera) { SDL_CameraDeviceID retval = 0; @@ -1031,6 +1091,22 @@ SDL_PropertiesID SDL_GetCameraProperties(SDL_Camera *camera) return retval; } +int SDL_GetCameraPermissionState(SDL_Camera *camera) +{ + int retval; + if (!camera) { + retval = SDL_InvalidParamError("camera"); + } else { + SDL_CameraDevice *device = (SDL_CameraDevice *) camera; // currently there's no separation between physical and logical device. + ObtainPhysicalCameraDeviceObj(device); + retval = device->permission; + ReleaseCameraDevice(device); + } + + return retval; +} + + static void CompleteCameraEntryPoints(void) { // this doesn't currently fill in stub implementations, it just asserts the backend filled them all in. diff --git a/src/camera/SDL_syscamera.h b/src/camera/SDL_syscamera.h index 27672890b54cf..e6b204771a441 100644 --- a/src/camera/SDL_syscamera.h +++ b/src/camera/SDL_syscamera.h @@ -50,6 +50,9 @@ extern void SDL_CameraDeviceDisconnected(SDL_CameraDevice *device); // Find an SDL_CameraDevice, selected by a callback. NULL if not found. DOES NOT LOCK THE DEVICE. extern SDL_CameraDevice *SDL_FindPhysicalCameraDeviceByCallback(SDL_bool (*callback)(SDL_CameraDevice *device, void *userdata), void *userdata); +// Backends should call this when the user has approved/denied access to a camera. +extern void SDL_CameraDevicePermissionOutcome(SDL_CameraDevice *device, SDL_bool approved); + // These functions are the heart of the camera threads. Backends can call them directly if they aren't using the SDL-provided thread. extern void SDL_CameraThreadSetup(SDL_CameraDevice *device); extern SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device); @@ -129,6 +132,9 @@ struct SDL_CameraDevice // Optional properties. SDL_PropertiesID props; + // -1: user denied permission, 0: waiting for user response, 1: user approved permission. + int permission; + // Data private to this driver, used when device is opened and running. struct SDL_PrivateCameraData *hidden; }; @@ -182,5 +188,6 @@ extern CameraBootStrap DUMMYCAMERA_bootstrap; extern CameraBootStrap V4L2_bootstrap; extern CameraBootStrap COREMEDIA_bootstrap; extern CameraBootStrap ANDROIDCAMERA_bootstrap; +extern CameraBootStrap EMSCRIPTENCAMERA_bootstrap; #endif // SDL_syscamera_h_ diff --git a/src/camera/emscripten/SDL_camera_emscripten.c b/src/camera/emscripten/SDL_camera_emscripten.c new file mode 100644 index 0000000000000..a04a8e4546660 --- /dev/null +++ b/src/camera/emscripten/SDL_camera_emscripten.c @@ -0,0 +1,269 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_CAMERA_DRIVER_EMSCRIPTEN + +#include "../SDL_syscamera.h" +#include "../SDL_camera_c.h" +#include "../../video/SDL_pixels_c.h" + +#include + +// just turn off clang-format for this whole file, this INDENT_OFF stuff on +// each EM_ASM section is ugly. +/* *INDENT-OFF* */ /* clang-format off */ + +EM_JS_DEPS(sdlcamera, "$dynCall"); + +static int EMSCRIPTENCAMERA_WaitDevice(SDL_CameraDevice *device) +{ + SDL_assert(!"This shouldn't be called"); // we aren't using SDL's internal thread. + return -1; +} + +static int EMSCRIPTENCAMERA_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame, Uint64 *timestampNS) +{ + void *rgba = SDL_malloc(device->actual_spec.width * device->actual_spec.height * 4); + if (!rgba) { + return SDL_OutOfMemory(); + } + + *timestampNS = SDL_GetTicksNS(); // best we can do here. + + const int rc = MAIN_THREAD_EM_ASM_INT({ + const w = $0; + const h = $1; + const rgba = $2; + const SDL3 = Module['SDL3']; + if ((typeof(SDL3) === 'undefined') || (typeof(SDL3.camera) === 'undefined') || (typeof(SDL3.camera.ctx2d) === 'undefined')) { + return 0; // don't have something we need, oh well. + } + + SDL3.camera.ctx2d.drawImage(SDL3.camera.video, 0, 0, w, h); + const imgrgba = SDL3.camera.ctx2d.getImageData(0, 0, w, h).data; + Module.HEAPU8.set(imgrgba, rgba); + + return 1; + }, device->actual_spec.width, device->actual_spec.height, rgba); + + if (!rc) { + SDL_free(rgba); + return 0; // something went wrong, maybe shutting down; just don't return a frame. + } + + frame->pixels = rgba; + frame->pitch = device->actual_spec.width * 4; + + return 1; +} + +static void EMSCRIPTENCAMERA_ReleaseFrame(SDL_CameraDevice *device, SDL_Surface *frame) +{ + SDL_free(frame->pixels); + frame->pixels = NULL; + frame->pitch = 0; +} + +static void EMSCRIPTENCAMERA_CloseDevice(SDL_CameraDevice *device) +{ + if (device) { + MAIN_THREAD_EM_ASM({ + const SDL3 = Module['SDL3']; + if ((typeof(SDL3) === 'undefined') || (typeof(SDL3.camera) === 'undefined') || (typeof(SDL3.camera.stream) === 'undefined')) { + return; // camera was closed and/or subsystem was shut down, we're already done. + } + SDL3.camera.stream.getTracks().forEach(track => track.stop()); // stop all recording. + _SDL_free(SDL3.camera.rgba); + SDL3.camera = {}; // dump our references to everything. + }); + SDL_free(device->hidden); + device->hidden = NULL; + } +} + +static void SDLEmscriptenCameraDevicePermissionOutcome(SDL_CameraDevice *device, int approved, int w, int h, int fps) +{ + device->spec.width = device->actual_spec.width = w; + device->spec.height = device->actual_spec.height = h; + device->spec.interval_numerator = device->actual_spec.interval_numerator = 1; + device->spec.interval_denominator = device->actual_spec.interval_denominator = fps; + SDL_CameraDevicePermissionOutcome(device, approved ? SDL_TRUE : SDL_FALSE); +} + +static int EMSCRIPTENCAMERA_OpenDevice(SDL_CameraDevice *device, const SDL_CameraSpec *spec) +{ + MAIN_THREAD_EM_ASM({ + // Since we can't get actual specs until we make a move that prompts the user for + // permission, we don't list any specs for the device and wrangle it during device open. + const device = $0; + const w = $1; + const h = $2; + const interval_numerator = $3; + const interval_denominator = $4; + const outcome = $5; + const iterate = $6; + + const constraints = {}; + if ((w <= 0) || (h <= 0)) { + constraints.video = true; // didn't ask for anything, let the system choose. + } else { + constraints.video = {}; // asked for a specific thing: request it as "ideal" but take closest hardware will offer. + constraints.video.width = w; + constraints.video.height = h; + } + + if ((interval_numerator > 0) && (interval_denominator > 0)) { + var fps = interval_denominator / interval_numerator; + constraints.video.frameRate = { ideal: fps }; + } + + function grabNextCameraFrame() { // !!! FIXME: this (currently) runs as a requestAnimationFrame callback, for lack of a better option. + const SDL3 = Module['SDL3']; + if ((typeof(SDL3) === 'undefined') || (typeof(SDL3.camera) === 'undefined') || (typeof(SDL3.camera.stream) === 'undefined')) { + return; // camera was closed and/or subsystem was shut down, stop iterating here. + } + + // time for a new frame from the camera? + const nextframems = SDL3.camera.next_frame_time; + const now = performance.now(); + if (now >= nextframems) { + dynCall('vi', iterate, [device]); // calls SDL_CameraThreadIterate, which will call our AcquireFrame implementation. + + // bump ahead but try to stay consistent on timing, in case we dropped frames. + while (SDL3.camera.next_frame_time < now) { + SDL3.camera.next_frame_time += SDL3.camera.fpsincrms; + } + } + + requestAnimationFrame(grabNextCameraFrame); // run this function again at the display framerate. (!!! FIXME: would this be better as requestIdleCallback?) + } + + navigator.mediaDevices.getUserMedia(constraints) + .then((stream) => { + const settings = stream.getVideoTracks()[0].getSettings(); + const actualw = settings.width; + const actualh = settings.height; + const actualfps = settings.frameRate; + console.log("Camera is opened! Actual spec: (" + actualw + "x" + actualh + "), fps=" + actualfps); + + dynCall('viiiii', outcome, [device, 1, actualw, actualh, actualfps]); + + const video = document.createElement("video"); + video.width = actualw; + video.height = actualh; + video.style.display = 'none'; // we need to attach this to a hidden video node so we can read it as pixels. + video.srcObject = stream; + + const canvas = document.createElement("canvas"); + canvas.width = actualw; + canvas.height = actualh; + canvas.style.display = 'none'; // we need to attach this to a hidden video node so we can read it as pixels. + + const ctx2d = canvas.getContext('2d'); + + const SDL3 = Module['SDL3']; + SDL3.camera.width = actualw; + SDL3.camera.height = actualh; + SDL3.camera.fps = actualfps; + SDL3.camera.fpsincrms = 1000.0 / actualfps; + SDL3.camera.stream = stream; + SDL3.camera.video = video; + SDL3.camera.canvas = canvas; + SDL3.camera.ctx2d = ctx2d; + SDL3.camera.rgba = 0; + SDL3.camera.next_frame_time = performance.now(); + + video.play(); + video.addEventListener('loadedmetadata', () => { + grabNextCameraFrame(); // start this loop going. + }); + }) + .catch((err) => { + console.error("Tried to open camera but it threw an error! " + err.name + ": " + err.message); + dynCall('viiiii', outcome, [device, 0, 0, 0, 0]); // we call this a permission error, because it probably is. + }); + }, device, spec->width, spec->height, spec->interval_numerator, spec->interval_denominator, SDLEmscriptenCameraDevicePermissionOutcome, SDL_CameraThreadIterate); + + return 0; // the real work waits until the user approves a camera. +} + +static void EMSCRIPTENCAMERA_FreeDeviceHandle(SDL_CameraDevice *device) +{ + // no-op. +} + +static void EMSCRIPTENCAMERA_Deinitialize(void) +{ + MAIN_THREAD_EM_ASM({ + if (typeof(Module['SDL3']) !== 'undefined') { + Module['SDL3'].camera = undefined; + } + }); +} + +static void EMSCRIPTENCAMERA_DetectDevices(void) +{ + // `navigator.mediaDevices` is not defined if unsupported or not in a secure context! + const int supported = MAIN_THREAD_EM_ASM_INT({ return (navigator.mediaDevices === undefined) ? 0 : 1; }); + + // if we have support at all, report a single generic camera with no specs. + // We'll find out if there really _is_ a camera when we try to open it, but querying it for real here + // will pop up a user permission dialog warning them we're trying to access the camera, and we generally + // don't want that during SDL_Init(). + if (supported) { + SDL_AddCameraDevice("Web browser's camera", 0, NULL, (void *) (size_t) 0x1); + } +} + +static SDL_bool EMSCRIPTENCAMERA_Init(SDL_CameraDriverImpl *impl) +{ +SDL_Log("EMSCRIPTENCAMERA_Init, %s:%d", __FILE__, __LINE__); + MAIN_THREAD_EM_ASM({ + if (typeof(Module['SDL3']) === 'undefined') { + Module['SDL3'] = {}; + } + Module['SDL3'].camera = {}; + }); + +SDL_Log("EMSCRIPTENCAMERA_Init, %s:%d", __FILE__, __LINE__); + impl->DetectDevices = EMSCRIPTENCAMERA_DetectDevices; + impl->OpenDevice = EMSCRIPTENCAMERA_OpenDevice; + impl->CloseDevice = EMSCRIPTENCAMERA_CloseDevice; + impl->WaitDevice = EMSCRIPTENCAMERA_WaitDevice; + impl->AcquireFrame = EMSCRIPTENCAMERA_AcquireFrame; + impl->ReleaseFrame = EMSCRIPTENCAMERA_ReleaseFrame; + impl->FreeDeviceHandle = EMSCRIPTENCAMERA_FreeDeviceHandle; + impl->Deinitialize = EMSCRIPTENCAMERA_Deinitialize; + + impl->ProvidesOwnCallbackThread = SDL_TRUE; + + return SDL_TRUE; +} + +CameraBootStrap EMSCRIPTENCAMERA_bootstrap = { + "emscripten", "SDL Emscripten MediaStream camera driver", EMSCRIPTENCAMERA_Init, SDL_FALSE +}; + +/* *INDENT-ON* */ /* clang-format on */ + +#endif // SDL_CAMERA_DRIVER_EMSCRIPTEN + diff --git a/src/camera/v4l2/SDL_camera_v4l2.c b/src/camera/v4l2/SDL_camera_v4l2.c index fd76ad4f675ab..9f3b1e08b0da0 100644 --- a/src/camera/v4l2/SDL_camera_v4l2.c +++ b/src/camera/v4l2/SDL_camera_v4l2.c @@ -619,6 +619,9 @@ static int V4L2_OpenDevice(SDL_CameraDevice *device, const SDL_CameraSpec *spec) } } + // Currently there is no user permission prompt for camera access, but maybe there will be a D-Bus portal interface at some point. + SDL_CameraDevicePermissionOutcome(device, SDL_TRUE); + return 0; } diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index a098f270972d3..c97153b87f758 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -970,6 +970,7 @@ SDL3_0.0.0 { SDL_AcquireCameraFrame; SDL_ReleaseCameraFrame; SDL_CloseCamera; + SDL_GetCameraPermissionState; # 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 db16355d7927f..9ee787ef8f550 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -995,3 +995,4 @@ #define SDL_AcquireCameraFrame SDL_AcquireCameraFrame_REAL #define SDL_ReleaseCameraFrame SDL_ReleaseCameraFrame_REAL #define SDL_CloseCamera SDL_CloseCamera_REAL +#define SDL_GetCameraPermissionState SDL_GetCameraPermissionState_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 49d5e1a7532cd..bcff07cee78e5 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1020,3 +1020,4 @@ SDL_DYNAPI_PROC(int,SDL_GetCameraFormat,(SDL_Camera *a, SDL_CameraSpec *b),(a,b) SDL_DYNAPI_PROC(SDL_Surface*,SDL_AcquireCameraFrame,(SDL_Camera *a, Uint64 *b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_ReleaseCameraFrame,(SDL_Camera *a, SDL_Surface *b),(a,b),return) SDL_DYNAPI_PROC(void,SDL_CloseCamera,(SDL_Camera *a),(a),) +SDL_DYNAPI_PROC(int,SDL_GetCameraPermissionState,(SDL_Camera *a),(a),return) diff --git a/src/events/SDL_events.c b/src/events/SDL_events.c index 04a54953abf14..baaff2c992b65 100644 --- a/src/events/SDL_events.c +++ b/src/events/SDL_events.c @@ -562,6 +562,12 @@ static void SDL_LogEvent(const SDL_Event *event) SDL_EVENT_CASE(SDL_EVENT_CAMERA_DEVICE_REMOVED) PRINT_CAMERADEV_EVENT(event); break; + SDL_EVENT_CASE(SDL_EVENT_CAMERA_DEVICE_APPROVED) + PRINT_CAMERADEV_EVENT(event); + break; + SDL_EVENT_CASE(SDL_EVENT_CAMERA_DEVICE_DENIED) + PRINT_CAMERADEV_EVENT(event); + break; #undef PRINT_CAMERADEV_EVENT SDL_EVENT_CASE(SDL_EVENT_SENSOR_UPDATE) diff --git a/test/testcameraminimal.c b/test/testcameraminimal.c index df8a08b175852..6eb4f7968289a 100644 --- a/test/testcameraminimal.c +++ b/test/testcameraminimal.c @@ -9,38 +9,27 @@ including commercial applications, and to alter it and redistribute it freely. */ -#include "SDL3/SDL_main.h" -#include "SDL3/SDL.h" -#include "SDL3/SDL_test.h" -#include "SDL3/SDL_camera.h" -#ifdef SDL_PLATFORM_EMSCRIPTEN -#include -#endif - -#include - -int main(int argc, char **argv) +#define SDL_MAIN_USE_CALLBACKS 1 +#include +#include +#include + +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; +static SDLTest_CommonState *state = NULL; +static SDL_Camera *camera = NULL; +static SDL_CameraSpec spec; +static SDL_Texture *texture = NULL; +static SDL_bool texture_updated = SDL_FALSE; +static SDL_Surface *frame_current = NULL; + +int SDL_AppInit(int argc, char *argv[]) { - SDL_Window *window = NULL; - SDL_Renderer *renderer = NULL; - SDL_Event evt; - int quit = 0; - SDLTest_CommonState *state = NULL; - - SDL_Camera *camera = NULL; - SDL_CameraSpec spec; - SDL_Texture *texture = NULL; - int texture_updated = 0; - SDL_Surface *frame_current = NULL; - - SDL_zero(evt); - SDL_zero(spec); - /* Initialize test framework */ state = SDLTest_CommonCreateState(argv, 0); if (state == NULL) { - return 1; + return -1; } /* Enable standard application logging */ @@ -49,13 +38,13 @@ int main(int argc, char **argv) /* Load the SDL library */ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError()); - return 1; + return -1; } window = SDL_CreateWindow("Local Video", 1000, 800, 0); if (window == NULL) { SDL_Log("Couldn't create window: %s", SDL_GetError()); - return 1; + return -1; } SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); @@ -63,13 +52,13 @@ int main(int argc, char **argv) renderer = SDL_CreateRenderer(window, NULL, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); if (renderer == NULL) { /* SDL_Log("Couldn't create renderer: %s", SDL_GetError()); */ - return 1; + return -1; } SDL_CameraDeviceID *devices = SDL_GetCameraDevices(NULL); if (!devices) { SDL_Log("SDL_GetCameraDevices failed: %s", SDL_GetError()); - return 1; + return -1; } const SDL_CameraDeviceID devid = devices[0]; /* just take the first one. */ @@ -77,7 +66,7 @@ int main(int argc, char **argv) if (!devid) { SDL_Log("No cameras available? %s", SDL_GetError()); - return 1; + return -1; } SDL_CameraSpec *pspec = NULL; @@ -91,115 +80,108 @@ int main(int argc, char **argv) camera = SDL_OpenCameraDevice(devid, pspec); if (!camera) { SDL_Log("Failed to open camera device: %s", SDL_GetError()); - return 1; - } - - if (SDL_GetCameraFormat(camera, &spec) < 0) { - SDL_Log("Couldn't get camera spec: %s", SDL_GetError()); - return 1; - } - - /* Create texture with appropriate format */ - if (texture == NULL) { - texture = SDL_CreateTexture(renderer, spec.format, SDL_TEXTUREACCESS_STATIC, spec.width, spec.height); - if (texture == NULL) { - SDL_Log("Couldn't create texture: %s", SDL_GetError()); - return 1; - } + return -1; } - while (!quit) { - while (SDL_PollEvent(&evt)) { - int sym = 0; - switch (evt.type) - { - case SDL_EVENT_KEY_DOWN: - { - sym = evt.key.keysym.sym; - break; - } - - case SDL_EVENT_QUIT: - { - quit = 1; - SDL_Log("Ctlr+C : Quit!"); - } - } + return 0; /* start the main app loop. */ +} +int SDL_AppEvent(const SDL_Event *event) +{ + switch (event->type) { + case SDL_EVENT_KEY_DOWN: { + const SDL_Keycode sym = event->key.keysym.sym; if (sym == SDLK_ESCAPE || sym == SDLK_AC_BACK) { - quit = 1; SDL_Log("Key : Escape!"); + return 1; } + break; } - { - Uint64 timestampNS = 0; - SDL_Surface *frame_next = SDL_AcquireCameraFrame(camera, ×tampNS); + case SDL_EVENT_QUIT: + SDL_Log("Ctlr+C : Quit!"); + return 1; -#if 0 - if (frame_next) { - SDL_Log("frame: %p at %" SDL_PRIu64, (void*)frame_next->pixels, timestampNS); + case SDL_EVENT_CAMERA_DEVICE_APPROVED: + if (SDL_GetCameraFormat(camera, &spec) < 0) { + SDL_Log("Couldn't get camera spec: %s", SDL_GetError()); + return -1; } -#endif - if (frame_next) { - if (frame_current) { - if (SDL_ReleaseCameraFrame(camera, frame_current) < 0) { - SDL_Log("err SDL_ReleaseCameraFrame: %s", SDL_GetError()); - } - } + /* Create texture with appropriate format */ + texture = SDL_CreateTexture(renderer, spec.format, SDL_TEXTUREACCESS_STATIC, spec.width, spec.height); + if (texture == NULL) { + SDL_Log("Couldn't create texture: %s", SDL_GetError()); + return -1; + } + break; - /* It's not needed to keep the frame once updated the texture is updated. - * But in case of 0-copy, it's needed to have the frame while using the texture. - */ - frame_current = frame_next; - texture_updated = 0; + case SDL_EVENT_CAMERA_DEVICE_DENIED: + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Camera permission denied!", "User denied access to the camera!", window); + return -1; + } + + return SDLTest_CommonEventMainCallbacks(state, event); +} + +int SDL_AppIterate(void) +{ + SDL_SetRenderDrawColor(renderer, 0x99, 0x99, 0x99, 255); + SDL_RenderClear(renderer); + + if (texture != NULL) { /* if not NULL, camera is ready to go. */ + int win_w, win_h, tw, th; + SDL_FRect d; + Uint64 timestampNS = 0; + SDL_Surface *frame_next = SDL_AcquireCameraFrame(camera, ×tampNS); + + #if 0 + if (frame_next) { + SDL_Log("frame: %p at %" SDL_PRIu64, (void*)frame_next->pixels, timestampNS); + } + #endif + + if (frame_next) { + if (frame_current) { + if (SDL_ReleaseCameraFrame(camera, frame_current) < 0) { + SDL_Log("err SDL_ReleaseCameraFrame: %s", SDL_GetError()); + } } + + /* It's not needed to keep the frame once updated the texture is updated. + * But in case of 0-copy, it's needed to have the frame while using the texture. + */ + frame_current = frame_next; + texture_updated = SDL_FALSE; } /* Update SDL_Texture with last video frame (only once per new frame) */ - if (frame_current && texture_updated == 0) { + if (frame_current && !texture_updated) { SDL_UpdateTexture(texture, NULL, frame_current->pixels, frame_current->pitch); - texture_updated = 1; + texture_updated = SDL_TRUE; } - SDL_SetRenderDrawColor(renderer, 0x99, 0x99, 0x99, 255); - SDL_RenderClear(renderer); - { - int win_w, win_h, tw, th, w; - SDL_FRect d; - SDL_QueryTexture(texture, NULL, NULL, &tw, &th); - SDL_GetRenderOutputSize(renderer, &win_w, &win_h); - w = win_w; - if (tw > w - 20) { - float scale = (float) (w - 20) / (float) tw; - tw = w - 20; - th = (int)((float) th * scale); - } - d.x = (float)(10 ); - d.y = ((float)(win_h - th)) / 2.0f; - d.w = (float)tw; - d.h = (float)(th - 10); - SDL_RenderTexture(renderer, texture, NULL, &d); - } - SDL_Delay(10); - SDL_RenderPresent(renderer); + SDL_QueryTexture(texture, NULL, NULL, &tw, &th); + SDL_GetRenderOutputSize(renderer, &win_w, &win_h); + d.x = (float) ((win_w - tw) / 2); + d.y = (float) ((win_h - th) / 2); + d.w = (float) tw; + d.h = (float) th; + SDL_RenderTexture(renderer, texture, NULL, &d); } - if (frame_current) { - SDL_ReleaseCameraFrame(camera, frame_current); - } - SDL_CloseCamera(camera); + SDL_RenderPresent(renderer); - if (texture) { - SDL_DestroyTexture(texture); - } + return 0; /* keep iterating. */ +} +void SDL_AppQuit(void) +{ + SDL_ReleaseCameraFrame(camera, frame_current); + SDL_CloseCamera(camera); + SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); - SDL_Quit(); SDLTest_CommonDestroyState(state); - - return 0; } From 3dca8a03da5161b5e64c80e5d97f3b176a94d056 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 22 Dec 2023 10:12:48 -0500 Subject: [PATCH 046/220] camera: Removed some debug logging. --- src/camera/emscripten/SDL_camera_emscripten.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/camera/emscripten/SDL_camera_emscripten.c b/src/camera/emscripten/SDL_camera_emscripten.c index a04a8e4546660..5e74cb928a535 100644 --- a/src/camera/emscripten/SDL_camera_emscripten.c +++ b/src/camera/emscripten/SDL_camera_emscripten.c @@ -236,7 +236,6 @@ static void EMSCRIPTENCAMERA_DetectDevices(void) static SDL_bool EMSCRIPTENCAMERA_Init(SDL_CameraDriverImpl *impl) { -SDL_Log("EMSCRIPTENCAMERA_Init, %s:%d", __FILE__, __LINE__); MAIN_THREAD_EM_ASM({ if (typeof(Module['SDL3']) === 'undefined') { Module['SDL3'] = {}; @@ -244,7 +243,6 @@ SDL_Log("EMSCRIPTENCAMERA_Init, %s:%d", __FILE__, __LINE__); Module['SDL3'].camera = {}; }); -SDL_Log("EMSCRIPTENCAMERA_Init, %s:%d", __FILE__, __LINE__); impl->DetectDevices = EMSCRIPTENCAMERA_DetectDevices; impl->OpenDevice = EMSCRIPTENCAMERA_OpenDevice; impl->CloseDevice = EMSCRIPTENCAMERA_CloseDevice; From 7191a97fe3b8a203aacc0336e7b3f5de49a33084 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 31 Jan 2024 15:07:07 -0500 Subject: [PATCH 047/220] camera: Windows support, through the Media Foundation API! --- VisualC/SDL/SDL.vcxproj | 1 + VisualC/SDL/SDL.vcxproj.filters | 6 + Xcode/SDL/SDL.xcodeproj/project.pbxproj | 12 + include/build_config/SDL_build_config.h.cmake | 3 +- .../build_config/SDL_build_config_windows.h | 5 +- src/camera/SDL_camera.c | 40 +- src/camera/SDL_syscamera.h | 11 + src/camera/dummy/SDL_camera_dummy.c | 2 +- src/camera/emscripten/SDL_camera_emscripten.c | 2 - .../SDL_camera_mediafoundation.c | 918 ++++++++++++++++++ src/camera/v4l2/SDL_camera_v4l2.c | 42 +- 11 files changed, 998 insertions(+), 44 deletions(-) create mode 100644 src/camera/mediafoundation/SDL_camera_mediafoundation.c diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index 2ddcdb1a6bb2c..1a07f3c2fb9e8 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -397,6 +397,7 @@ Create + diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index 6673e9e816e2b..7c0f5190f73ab 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -184,6 +184,9 @@ {0000fc2700d453b3c8d79fe81e1c0000} + + {0000fbfe2d21e4f451142e7d0e870000} + @@ -856,6 +859,9 @@ camera\dummy + + camera\mediafoundation + camera diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj index e5623fe149a7f..7ec548712d9a7 100644 --- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -499,6 +499,7 @@ F3FA5A242B59ACE000FEAD97 /* yuv_rgb_lsx.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FA5A1B2B59ACE000FEAD97 /* yuv_rgb_lsx.h */; }; F3FA5A252B59ACE000FEAD97 /* yuv_rgb_common.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FA5A1C2B59ACE000FEAD97 /* yuv_rgb_common.h */; }; FA73671D19A540EF004122E4 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA73671C19A540EF004122E4 /* CoreVideo.framework */; platformFilters = (ios, maccatalyst, macos, tvos, watchos, ); }; + 00009F560664255CCB6C0000 /* SDL_camera_mediafoundation.c in Sources */ = {isa = PBXBuildFile; fileRef = 0000C61C247BAAAF757D0000 /* SDL_camera_mediafoundation.c */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1027,6 +1028,7 @@ F59C710600D5CB5801000001 /* SDL.info */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = SDL.info; sourceTree = ""; }; F5A2EF3900C6A39A01000001 /* BUGS.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = BUGS.txt; path = ../../BUGS.txt; sourceTree = SOURCE_ROOT; }; FA73671C19A540EF004122E4 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; + 0000C61C247BAAAF757D0000 /* SDL_camera_mediafoundation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_camera_mediafoundation.c; path = SDL_camera_mediafoundation.c; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1068,6 +1070,7 @@ 0000035D38C3899C7EFD0000 /* SDL_camera.c */, 00009003C7148E1126CA0000 /* SDL_camera_c.h */, 00005D3EB902478835E20000 /* SDL_syscamera.h */, + 0000926F2501CA0BDD650000 /* mediafoundation */, ); path = camera; sourceTree = ""; @@ -2175,6 +2178,14 @@ path = resources; sourceTree = ""; }; + 0000926F2501CA0BDD650000 /* mediafoundation */ = { + isa = PBXGroup; + children = ( + 0000C61C247BAAAF757D0000 /* SDL_camera_mediafoundation.c */, + ); + path = mediafoundation; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -2754,6 +2765,7 @@ 000098E9DAA43EF6FF7F0000 /* SDL_camera.c in Sources */, 00001B2471F503DD3C1B0000 /* SDL_camera_dummy.c in Sources */, 00002B20A48E055EB0350000 /* SDL_camera_coremedia.m in Sources */, + 00009F560664255CCB6C0000 /* SDL_camera_mediafoundation.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index 9f4302a79a5f2..11c1ad716ecd0 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -472,7 +472,8 @@ #cmakedefine SDL_CAMERA_DRIVER_V4L2 @SDL_CAMERA_DRIVER_V4L2@ #cmakedefine SDL_CAMERA_DRIVER_COREMEDIA @SDL_CAMERA_DRIVER_COREMEDIA@ #cmakedefine SDL_CAMERA_DRIVER_ANDROID @SDL_CAMERA_DRIVER_ANDROID@ -#cmakedefine SDL_CAMERA_DRIVER_EMSCRIPTEN @SDL_CAMERA_DRIVER_EMSCRIPTEND@ +#cmakedefine SDL_CAMERA_DRIVER_EMSCRIPTEN @SDL_CAMERA_DRIVER_EMSCRIPTEN@ +#cmakedefine SDL_CAMERA_DRIVER_MEDIAFOUNDATION @SDL_CAMERA_DRIVER_MEDIAFOUNDATION@ /* Enable misc subsystem */ #cmakedefine SDL_MISC_DUMMY @SDL_MISC_DUMMY@ diff --git a/include/build_config/SDL_build_config_windows.h b/include/build_config/SDL_build_config_windows.h index 066888fbd7bc7..a8d2dc4fd49c0 100644 --- a/include/build_config/SDL_build_config_windows.h +++ b/include/build_config/SDL_build_config_windows.h @@ -311,7 +311,8 @@ typedef unsigned int uintptr_t; /* Enable filesystem support */ #define SDL_FILESYSTEM_WINDOWS 1 -/* Enable the camera driver (src/camera/dummy/\*.c) */ /* !!! FIXME */ -#define SDL_CAMERA_DRIVER_DUMMY 1 +/* Enable the camera driver */ +#define SDL_CAMERA_DRIVER_MEDIAFOUNDATION 1 +#define SDL_CAMERA_DRIVER_DUMMY 1 #endif /* SDL_build_config_windows_h_ */ diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index 01179dc44f97b..486b5001361e3 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -43,6 +43,9 @@ static const CameraBootStrap *const bootstrap[] = { #ifdef SDL_CAMERA_DRIVER_EMSCRIPTEN &EMSCRIPTENCAMERA_bootstrap, #endif +#ifdef SDL_CAMERA_DRIVER_MEDIAFOUNDATION + &MEDIAFOUNDATION_bootstrap, +#endif #ifdef SDL_CAMERA_DRIVER_DUMMY &DUMMYCAMERA_bootstrap, #endif @@ -70,6 +73,32 @@ const char *SDL_GetCurrentCameraDriver(void) return camera_driver.name; } +int SDL_AddCameraFormat(CameraFormatAddData *data, Uint32 fmt, int w, int h, int interval_numerator, int interval_denominator) +{ + SDL_assert(data != NULL); + if (data->allocated_specs <= data->num_specs) { + const int newalloc = data->allocated_specs ? (data->allocated_specs * 2) : 16; + void *ptr = SDL_realloc(data->specs, sizeof (SDL_CameraSpec) * newalloc); + if (!ptr) { + return -1; + } + data->specs = (SDL_CameraSpec *) ptr; + data->allocated_specs = newalloc; + } + + SDL_CameraSpec *spec = &data->specs[data->num_specs]; + spec->format = fmt; + spec->width = w; + spec->height = h; + spec->interval_numerator = interval_numerator; + spec->interval_denominator = interval_denominator; + + data->num_specs++; + + return 0; +} + + static void ClosePhysicalCameraDevice(SDL_CameraDevice *device) { if (!device) { @@ -610,10 +639,13 @@ SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device) if (rc == 1) { // new frame acquired! #if DEBUG_CAMERA - SDL_Log("CAMERA: New frame available!"); + SDL_Log("CAMERA: New frame available! pixels=%p pitch=%d", device->acquire_surface->pixels, device->acquire_surface->pitch); #endif if (device->drop_frames > 0) { + #if DEBUG_CAMERA + SDL_Log("CAMERA: Dropping an initial frame"); + #endif device->drop_frames--; camera_driver.impl.ReleaseFrame(device, device->acquire_surface); device->acquire_surface->pixels = NULL; @@ -662,9 +694,15 @@ SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device) } else if (acquired) { // we have a new frame, scale/convert if necessary and queue it for the app! SDL_assert(slist != NULL); if (!device->needs_scaling && !device->needs_conversion) { // no conversion needed? Just move the pointer/pitch into the output surface. + #if DEBUG_CAMERA + SDL_Log("CAMERA: Frame is going through without conversion!"); + #endif output_surface->pixels = acquired->pixels; output_surface->pitch = acquired->pitch; } else { // convert/scale into a different surface. + #if DEBUG_CAMERA + SDL_Log("CAMERA: Frame is getting converted!"); + #endif SDL_Surface *srcsurf = acquired; if (device->needs_scaling == -1) { // downscaling? Do it first. -1: downscale, 0: no scaling, 1: upscale SDL_Surface *dstsurf = device->needs_conversion ? device->conversion_surface : output_surface; diff --git a/src/camera/SDL_syscamera.h b/src/camera/SDL_syscamera.h index e6b204771a441..b99239ec51d72 100644 --- a/src/camera/SDL_syscamera.h +++ b/src/camera/SDL_syscamera.h @@ -58,6 +58,16 @@ extern void SDL_CameraThreadSetup(SDL_CameraDevice *device); extern SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device); extern void SDL_CameraThreadShutdown(SDL_CameraDevice *device); +// common utility functionality to gather up camera specs. Not required! +typedef struct CameraFormatAddData +{ + SDL_CameraSpec *specs; + int num_specs; + int allocated_specs; +} CameraFormatAddData; + +int SDL_AddCameraFormat(CameraFormatAddData *data, Uint32 fmt, int w, int h, int interval_numerator, int interval_denominator); + typedef struct SurfaceList { SDL_Surface *surface; @@ -189,5 +199,6 @@ extern CameraBootStrap V4L2_bootstrap; extern CameraBootStrap COREMEDIA_bootstrap; extern CameraBootStrap ANDROIDCAMERA_bootstrap; extern CameraBootStrap EMSCRIPTENCAMERA_bootstrap; +extern CameraBootStrap MEDIAFOUNDATION_bootstrap; #endif // SDL_syscamera_h_ diff --git a/src/camera/dummy/SDL_camera_dummy.c b/src/camera/dummy/SDL_camera_dummy.c index 1dcdd295858b3..b93eecbbe1824 100644 --- a/src/camera/dummy/SDL_camera_dummy.c +++ b/src/camera/dummy/SDL_camera_dummy.c @@ -77,4 +77,4 @@ CameraBootStrap DUMMYCAMERA_bootstrap = { "dummy", "SDL dummy camera driver", DUMMYCAMERA_Init, SDL_TRUE }; -#endif +#endif // SDL_CAMERA_DRIVER_DUMMY diff --git a/src/camera/emscripten/SDL_camera_emscripten.c b/src/camera/emscripten/SDL_camera_emscripten.c index 5e74cb928a535..45be949692181 100644 --- a/src/camera/emscripten/SDL_camera_emscripten.c +++ b/src/camera/emscripten/SDL_camera_emscripten.c @@ -79,8 +79,6 @@ static int EMSCRIPTENCAMERA_AcquireFrame(SDL_CameraDevice *device, SDL_Surface * static void EMSCRIPTENCAMERA_ReleaseFrame(SDL_CameraDevice *device, SDL_Surface *frame) { SDL_free(frame->pixels); - frame->pixels = NULL; - frame->pitch = 0; } static void EMSCRIPTENCAMERA_CloseDevice(SDL_CameraDevice *device) diff --git a/src/camera/mediafoundation/SDL_camera_mediafoundation.c b/src/camera/mediafoundation/SDL_camera_mediafoundation.c new file mode 100644 index 0000000000000..960ab1e543a02 --- /dev/null +++ b/src/camera/mediafoundation/SDL_camera_mediafoundation.c @@ -0,0 +1,918 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2023 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +// the Windows Media Foundation API + +#ifdef SDL_CAMERA_DRIVER_MEDIAFOUNDATION + +#define COBJMACROS + +// this seems to be a bug in mfidl.h, just define this to avoid the problem section. +#define __IMFVideoProcessorControl3_INTERFACE_DEFINED__ + +#include "../../core/windows/SDL_windows.h" + +#include +#include +#include + +#include "../SDL_syscamera.h" +#include "../SDL_camera_c.h" + +static const IID SDL_IID_IMFMediaSource = { 0x279a808d, 0xaec7, 0x40c8, { 0x9c, 0x6b, 0xa6, 0xb4, 0x92, 0xc7, 0x8a, 0x66 } }; +static const IID SDL_IID_IMF2DBuffer = { 0x7dc9d5f9, 0x9ed9, 0x44ec, { 0x9b, 0xbf, 0x06, 0x00, 0xbb, 0x58, 0x9f, 0xbb } }; +static const IID SDL_IID_IMF2DBuffer2 = { 0x33ae5ea6, 0x4316, 0x436f, { 0x8d, 0xdd, 0xd7, 0x3d, 0x22, 0xf8, 0x29, 0xec } }; +static const GUID SDL_MF_MT_DEFAULT_STRIDE = { 0x644b4e48, 0x1e02, 0x4516, { 0xb0, 0xeb, 0xc0, 0x1c, 0xa9, 0xd4, 0x9a, 0xc6 } }; +static const GUID SDL_MF_MT_MAJOR_TYPE = { 0x48eba18e, 0xf8c9, 0x4687, { 0xbf, 0x11, 0x0a, 0x74, 0xc9, 0xf9, 0x6a, 0x8f } }; +static const GUID SDL_MF_MT_SUBTYPE = { 0xf7e34c9a, 0x42e8, 0x4714, { 0xb7, 0x4b, 0xcb, 0x29, 0xd7, 0x2c, 0x35, 0xe5 } }; +static const GUID SDL_MF_MT_FRAME_SIZE = { 0x1652c33d, 0xd6b2, 0x4012, { 0xb8, 0x34, 0x72, 0x03, 0x08, 0x49, 0xa3, 0x7d } }; +static const GUID SDL_MF_MT_FRAME_RATE = { 0xc459a2e8, 0x3d2c, 0x4e44, { 0xb1, 0x32, 0xfe, 0xe5, 0x15, 0x6c, 0x7b, 0xb0 } }; +static const GUID SDL_MFMediaType_Video = { 0x73646976, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 } }; + +#define SDL_DEFINE_MEDIATYPE_GUID(name, fmt) static const GUID SDL_##name = { fmt, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } } +SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_RGB555, 24); +SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_RGB565, 23); +SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_RGB24, 20); +SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_RGB32, 22); +SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_ARGB32, 21); +SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_A2R10G10B10, 31); +SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_YV12, FCC('YV12')); +SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_IYUV, FCC('IYUV')); +SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_YUY2, FCC('YUY2')); +SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_UYVY, FCC('UYVY')); +SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_YVYU, FCC('YVYU')); +SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_NV12, FCC('NV12')); +SDL_DEFINE_MEDIATYPE_GUID(MFVideoFormat_NV21, FCC('NV21')); +#undef SDL_DEFINE_MEDIATYPE_GUID + +static const struct { const GUID *guid; const Uint32 sdlfmt; } fmtmappings[] = { + // This is not every possible format, just popular ones that SDL can reasonably handle. + // (and we should probably trim this list more.) + { &SDL_MFVideoFormat_RGB555, SDL_PIXELFORMAT_XRGB1555 }, + { &SDL_MFVideoFormat_RGB565, SDL_PIXELFORMAT_RGB565 }, + { &SDL_MFVideoFormat_RGB24, SDL_PIXELFORMAT_RGB24 }, + { &SDL_MFVideoFormat_RGB32, SDL_PIXELFORMAT_XRGB8888 }, + { &SDL_MFVideoFormat_ARGB32, SDL_PIXELFORMAT_ARGB8888 }, + { &SDL_MFVideoFormat_A2R10G10B10, SDL_PIXELFORMAT_ARGB2101010 }, + { &SDL_MFVideoFormat_YV12, SDL_PIXELFORMAT_YV12 }, + { &SDL_MFVideoFormat_IYUV, SDL_PIXELFORMAT_IYUV }, + { &SDL_MFVideoFormat_YUY2, SDL_PIXELFORMAT_YUY2 }, + { &SDL_MFVideoFormat_UYVY, SDL_PIXELFORMAT_UYVY }, + { &SDL_MFVideoFormat_YVYU, SDL_PIXELFORMAT_YVYU }, + { &SDL_MFVideoFormat_NV12, SDL_PIXELFORMAT_NV12 }, + { &SDL_MFVideoFormat_NV21, SDL_PIXELFORMAT_NV21 } +}; + +static Uint32 MFVidFmtGuidToSDLFmt(const GUID *guid) { + for (size_t i = 0; i < SDL_arraysize(fmtmappings); i++) { + if (WIN_IsEqualGUID(guid, fmtmappings[i].guid)) { + return fmtmappings[i].sdlfmt; + } + } + return SDL_PIXELFORMAT_UNKNOWN; +} + +static const GUID *SDLFmtToMFVidFmtGuid(Uint32 sdlfmt) { + for (size_t i = 0; i < SDL_arraysize(fmtmappings); i++) { + if (fmtmappings[i].sdlfmt == sdlfmt) { + return fmtmappings[i].guid; + } + } + return NULL; +} + + +// handle to Media Foundation libs--Vista and later!--for access to the Media Foundation API. + +// mf.dll ... +static HMODULE libmf = NULL; +typedef HRESULT(WINAPI *pfnMFEnumDeviceSources)(IMFAttributes *,IMFActivate ***,UINT32 *); +typedef HRESULT(WINAPI *pfnMFCreateDeviceSource)(IMFAttributes *, IMFMediaSource **); +static pfnMFEnumDeviceSources pMFEnumDeviceSources = NULL; +static pfnMFCreateDeviceSource pMFCreateDeviceSource = NULL; + +// mfplat.dll ... +static HMODULE libmfplat = NULL; +typedef HRESULT(WINAPI *pfnMFStartup)(ULONG, DWORD); +typedef HRESULT(WINAPI *pfnMFShutdown)(void); +typedef HRESULT(WINAPI *pfnMFCreateAttributes)(IMFAttributes **, UINT32); +typedef HRESULT(WINAPI *pfnMFCreateMediaType)(IMFMediaType **); +typedef HRESULT(WINAPI *pfnMFGetStrideForBitmapInfoHeader)(DWORD, DWORD, LONG *); + +static pfnMFStartup pMFStartup = NULL; +static pfnMFShutdown pMFShutdown = NULL; +static pfnMFCreateAttributes pMFCreateAttributes = NULL; +static pfnMFCreateMediaType pMFCreateMediaType = NULL; +static pfnMFGetStrideForBitmapInfoHeader pMFGetStrideForBitmapInfoHeader = NULL; + +// mfreadwrite.dll ... +static HMODULE libmfreadwrite = NULL; +typedef HRESULT(WINAPI *pfnMFCreateSourceReaderFromMediaSource)(IMFMediaSource *, IMFAttributes *, IMFSourceReader **); +static pfnMFCreateSourceReaderFromMediaSource pMFCreateSourceReaderFromMediaSource = NULL; + + +typedef struct SDL_PrivateCameraData +{ + IMFSourceReader *srcreader; + IMFSample *current_sample; + int pitch; +} SDL_PrivateCameraData; + +static int MEDIAFOUNDATION_WaitDevice(SDL_CameraDevice *device) +{ + SDL_assert(device->hidden->current_sample == NULL); + + IMFSourceReader *srcreader = device->hidden->srcreader; + IMFSample *sample = NULL; + + while (!SDL_AtomicGet(&device->shutdown)) { + DWORD stream_flags = 0; + const HRESULT ret = IMFSourceReader_ReadSample(srcreader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, &stream_flags, NULL, &sample); + if (FAILED(ret)) { + return -1; // ruh roh. + } + + // we currently ignore stream_flags format changes, but my _hope_ is that IMFSourceReader is handling this and + // will continue to give us the explictly-specified format we requested when opening the device, though, and + // we don't have to manually deal with it. + + if (sample != NULL) { + break; + } else if (stream_flags & (MF_SOURCE_READERF_ERROR | MF_SOURCE_READERF_ENDOFSTREAM)) { + return -1; // apparently this camera has gone down. :/ + } + + // otherwise, there was some minor burp, probably; just try again. + } + + device->hidden->current_sample = sample; + + return 0; +} + + +#if KEEP_ACQUIRED_BUFFERS_LOCKED + +#define PROP_SURFACE_IMFOBJS_POINTER "SDL.camera.mediafoundation.imfobjs" + +typedef struct SDL_IMFObjects +{ + IMF2DBuffer2 *buffer2d2; + IMF2DBuffer *buffer2d; + IMFMediaBuffer *buffer; + IMFSample *sample; +} SDL_IMFObjects; + +static void SDLCALL CleanupIMF2DBuffer2(void *userdata, void *value) +{ + SDL_IMFObjects *objs = (SDL_IMFObjects *)value; + IMF2DBuffer2_Unlock2D(objs->buffer2d2); + IMF2DBuffer2_Release(objs->buffer2d2); + IMFMediaBuffer_Release(objs->buffer); + IMFSample_Release(objs->sample); + SDL_free(objs); +} + +static void SDLCALL CleanupIMF2DBuffer(void *userdata, void *value) +{ + SDL_IMFObjects *objs = (SDL_IMFObjects *)value; + IMF2DBuffer_Unlock2D(objs->buffer2d); + IMF2DBuffer_Release(objs->buffer2d); + IMFMediaBuffer_Release(objs->buffer); + IMFSample_Release(objs->sample); + SDL_free(objs); +} + +static void SDLCALL CleanupIMFMediaBuffer(void *userdata, void *value) +{ + SDL_IMFObjects *objs = (SDL_IMFObjects *)value; + IMFMediaBuffer_Unlock(objs->buffer); + IMFMediaBuffer_Release(objs->buffer); + IMFSample_Release(objs->sample); + SDL_free(objs); +} + +static int MEDIAFOUNDATION_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame, Uint64 *timestampNS) +{ + SDL_assert(device->hidden->current_sample != NULL); + + int retval = 1; + HRESULT ret; + LONGLONG timestamp100NS = 0; + SDL_IMFObjects *objs = (SDL_IMFObjects *) SDL_calloc(1, sizeof (SDL_IMFObjects)); + + if (objs == NULL) { + return -1; + } + + objs->sample = device->hidden->current_sample; + device->hidden->current_sample = NULL; + + const SDL_PropertiesID surfprops = SDL_GetSurfaceProperties(frame); + if (!surfprops) { + retval = -1; + } else { + ret = IMFSample_GetSampleTime(objs->sample, ×tamp100NS); + if (FAILED(ret)) { + retval = -1; + } + + *timestampNS = timestamp100NS * 100; // the timestamps are in 100-nanosecond increments; move to full nanoseconds. + } + + ret = (retval < 0) ? E_FAIL : IMFSample_ConvertToContiguousBuffer(objs->sample, &objs->buffer); /*IMFSample_GetBufferByIndex(objs->sample, 0, &objs->buffer);*/ + + if (FAILED(ret)) { + SDL_free(objs); + retval = -1; + } else { + BYTE *pixels = NULL; + LONG pitch = 0; + + if (SUCCEEDED(IMFMediaBuffer_QueryInterface(objs->buffer, &SDL_IID_IMF2DBuffer2, (void **)&objs->buffer2d2))) { + BYTE *bufstart = NULL; + DWORD buflen = 0; + ret = IMF2DBuffer2_Lock2DSize(objs->buffer2d2, MF2DBuffer_LockFlags_Read, &pixels, &pitch, &bufstart, &buflen); + if (FAILED(ret)) { + retval = -1; + CleanupIMF2DBuffer2(NULL, objs); + } else { + frame->pixels = pixels; + frame->pitch = (int) pitch; + if (SDL_SetPropertyWithCleanup(surfprops, PROP_SURFACE_IMFOBJS_POINTER, objs, CleanupIMF2DBuffer2, NULL) == -1) { + CleanupIMF2DBuffer2(NULL, objs); + retval = -1; + } + } + } else if (SUCCEEDED(IMFMediaBuffer_QueryInterface(objs->buffer, &SDL_IID_IMF2DBuffer, (void **)&objs->buffer2d))) { + ret = IMF2DBuffer_Lock2D(objs->buffer2d, &pixels, &pitch); + if (FAILED(ret)) { + CleanupIMF2DBuffer(NULL, objs); + retval = -1; + } else { + frame->pixels = pixels; + frame->pitch = (int) pitch; + if (SDL_SetPropertyWithCleanup(surfprops, PROP_SURFACE_IMFOBJS_POINTER, objs, CleanupIMF2DBuffer, NULL) == -1) { + CleanupIMF2DBuffer(NULL, objs); + retval = -1; + } + } + } else { + DWORD maxlen = 0, currentlen = 0; + ret = IMFMediaBuffer_Lock(objs->buffer, &pixels, &maxlen, ¤tlen); + if (FAILED(ret)) { + CleanupIMFMediaBuffer(NULL, objs); + retval = -1; + } else { + pitch = (LONG) device->hidden->pitch; + if (pitch < 0) { // image rows are reversed. + pixels += -pitch * (frame->h - 1); + } + frame->pixels = pixels; + frame->pitch = (int) pitch; + if (SDL_SetPropertyWithCleanup(surfprops, PROP_SURFACE_IMFOBJS_POINTER, objs, CleanupIMFMediaBuffer, NULL) == -1) { + CleanupIMFMediaBuffer(NULL, objs); + retval = -1; + } + } + } + } + + if (retval < 0) { + *timestampNS = 0; + } + + return retval; +} + +static void MEDIAFOUNDATION_ReleaseFrame(SDL_CameraDevice *device, SDL_Surface *frame) +{ + const SDL_PropertiesID surfprops = SDL_GetSurfaceProperties(frame); + if (surfprops) { + // this will release the IMFBuffer and IMFSample objects for this frame. + SDL_ClearProperty(surfprops, PROP_SURFACE_IMFOBJS_POINTER); + } +} + +#else + +static int MEDIAFOUNDATION_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame, Uint64 *timestampNS) +{ + SDL_assert(device->hidden->current_sample != NULL); + + int retval = 1; + HRESULT ret; + LONGLONG timestamp100NS = 0; + + IMFSample *sample = device->hidden->current_sample; + device->hidden->current_sample = NULL; + + const SDL_PropertiesID surfprops = SDL_GetSurfaceProperties(frame); + if (!surfprops) { + retval = -1; + } else { + ret = IMFSample_GetSampleTime(sample, ×tamp100NS); + if (FAILED(ret)) { + retval = -1; + } + + *timestampNS = timestamp100NS * 100; // the timestamps are in 100-nanosecond increments; move to full nanoseconds. + } + + IMFMediaBuffer *buffer = NULL; + ret = (retval < 0) ? E_FAIL : IMFSample_ConvertToContiguousBuffer(sample, &buffer); /*IMFSample_GetBufferByIndex(sample, 0, &buffer);*/ + + if (FAILED(ret)) { + retval = -1; + } else { + IMF2DBuffer *buffer2d = NULL; + IMF2DBuffer2 *buffer2d2 = NULL; + BYTE *pixels = NULL; + LONG pitch = 0; + + if (SUCCEEDED(IMFMediaBuffer_QueryInterface(buffer, &SDL_IID_IMF2DBuffer2, (void **)&buffer2d2))) { + BYTE *bufstart = NULL; + DWORD buflen = 0; + ret = IMF2DBuffer2_Lock2DSize(buffer2d2, MF2DBuffer_LockFlags_Read, &pixels, &pitch, &bufstart, &buflen); + if (FAILED(ret)) { + retval = -1; + } else { + frame->pixels = SDL_aligned_alloc(SDL_SIMDGetAlignment(), buflen); + if (frame->pixels == NULL) { + retval = -1; + } else { + SDL_memcpy(frame->pixels, pixels, buflen); + frame->pitch = (int)pitch; + } + IMF2DBuffer2_Unlock2D(buffer2d2); + } + IMF2DBuffer2_Release(buffer2d2); + } else if (SUCCEEDED(IMFMediaBuffer_QueryInterface(buffer, &SDL_IID_IMF2DBuffer, (void **)&buffer2d))) { + ret = IMF2DBuffer_Lock2D(buffer2d, &pixels, &pitch); + if (FAILED(ret)) { + retval = -1; + } else { + BYTE *bufstart = pixels; + const DWORD buflen = (SDL_abs((int)pitch) * frame->w) * frame->h; + if (pitch < 0) { // image rows are reversed. + bufstart += -pitch * (frame->h - 1); + } + frame->pixels = SDL_aligned_alloc(SDL_SIMDGetAlignment(), buflen); + if (frame->pixels == NULL) { + retval = -1; + } else { + SDL_memcpy(frame->pixels, bufstart, buflen); + frame->pitch = (int)pitch; + } + IMF2DBuffer_Unlock2D(buffer2d); + } + IMF2DBuffer_Release(buffer2d); + } else { + DWORD maxlen = 0, currentlen = 0; + ret = IMFMediaBuffer_Lock(buffer, &pixels, &maxlen, ¤tlen); + if (FAILED(ret)) { + retval = -1; + } else { + BYTE *bufstart = pixels; + pitch = (LONG)device->hidden->pitch; + const DWORD buflen = (SDL_abs((int)pitch) * frame->w) * frame->h; + if (pitch < 0) { // image rows are reversed. + bufstart += -pitch * (frame->h - 1); + } + frame->pixels = SDL_aligned_alloc(SDL_SIMDGetAlignment(), buflen); + if (frame->pixels == NULL) { + retval = -1; + } else { + SDL_memcpy(frame->pixels, bufstart, buflen); + frame->pitch = (int)pitch; + } + IMFMediaBuffer_Unlock(buffer); + } + } + IMFMediaBuffer_Release(buffer); + } + + IMFSample_Release(sample); + + if (retval < 0) { + *timestampNS = 0; + } + + return retval; +} + +static void MEDIAFOUNDATION_ReleaseFrame(SDL_CameraDevice *device, SDL_Surface *frame) +{ + SDL_aligned_free(frame->pixels); +} + +#endif + +static void MEDIAFOUNDATION_CloseDevice(SDL_CameraDevice *device) +{ + if (device && device->hidden) { + if (device->hidden->srcreader) { + IMFSourceReader_Release(device->hidden->srcreader); + } + if (device->hidden->current_sample) { + IMFSample_Release(device->hidden->current_sample); + } + SDL_free(device->hidden); + device->hidden = NULL; + } +} + +// this function is from https://learn.microsoft.com/en-us/windows/win32/medfound/uncompressed-video-buffers +static HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride) +{ + LONG lStride = 0; + + // Try to get the default stride from the media type. + HRESULT ret = IMFMediaType_GetUINT32(pType, &SDL_MF_MT_DEFAULT_STRIDE, (UINT32*)&lStride); + if (FAILED(ret)) { + // Attribute not set. Try to calculate the default stride. + + GUID subtype = GUID_NULL; + UINT32 width = 0; + UINT32 height = 0; + UINT64 val = 0; + + // Get the subtype and the image size. + ret = IMFMediaType_GetGUID(pType, &SDL_MF_MT_SUBTYPE, &subtype); + if (FAILED(ret)) { + goto done; + } + + ret = IMFMediaType_GetUINT64(pType, &SDL_MF_MT_FRAME_SIZE, &val); + if (FAILED(ret)) { + goto done; + } + + width = (UINT32) (val >> 32); + height = (UINT32) val; + + ret = pMFGetStrideForBitmapInfoHeader(subtype.Data1, width, &lStride); + if (FAILED(ret)) { + goto done; + } + + // Set the attribute for later reference. + IMFMediaType_SetUINT32(pType, &SDL_MF_MT_DEFAULT_STRIDE, (UINT32) lStride); + } + + if (SUCCEEDED(ret)) { + *plStride = lStride; + } + +done: + return ret; +} + + +static int MEDIAFOUNDATION_OpenDevice(SDL_CameraDevice *device, const SDL_CameraSpec *spec) +{ + const char *utf8symlink = (const char *) device->handle; + IMFAttributes *attrs = NULL; + LPWSTR wstrsymlink = NULL; + IMFMediaSource *source = NULL; + IMFMediaType *mediatype = NULL; + IMFSourceReader *srcreader = NULL; + DWORD num_streams = 0; + LONG lstride = 0; + //PROPVARIANT var; + HRESULT ret; + +SDL_Log("MEDIAFOUNDATION spec format: %s", SDL_GetPixelFormatName(spec->format)); + + #if 0 + IMFStreamDescriptor *streamdesc = NULL; + IMFPresentationDescriptor *presentdesc = NULL; + IMFMediaTypeHandler *handler = NULL; + #endif + + #if DEBUG_CAMERA + SDL_Log("CAMERA: opening device with symlink of '%s'", utf8symlink); + #endif + + wstrsymlink = WIN_UTF8ToString(utf8symlink); + if (!wstrsymlink) { + goto failed; + } + + #define CHECK_HRESULT(what, r) if (FAILED(r)) { WIN_SetErrorFromHRESULT(what " failed", r); goto failed; } + + ret = pMFCreateAttributes(&attrs, 1); + CHECK_HRESULT("MFCreateAttributes", ret); + + ret = IMFAttributes_SetGUID(attrs, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); + CHECK_HRESULT("IMFAttributes_SetGUID(srctype)", ret); + + ret = IMFAttributes_SetString(attrs, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, wstrsymlink); + CHECK_HRESULT("IMFAttributes_SetString(symlink)", ret); + + ret = pMFCreateDeviceSource(attrs, &source); + CHECK_HRESULT("MFCreateDeviceSource", ret); + + IMFAttributes_Release(attrs); + SDL_free(wstrsymlink); + attrs = NULL; + wstrsymlink = NULL; + + // !!! FIXME: I think it'd be nice to do this without an IMFSourceReader, + // since it's just utility code that has to handle more complex media streams + // than we're dealing with, but this will do for now. The docs are slightly + // insistent that you should use one, though...Maybe it's extremely hard + // to handle directly at the IMFMediaSource layer...? + ret = pMFCreateSourceReaderFromMediaSource(source, NULL, &srcreader); + CHECK_HRESULT("MFCreateSourceReaderFromMediaSource", ret); + + // !!! FIXME: do we actually have to find the media type object in the source reader or can we just roll our own like this? + ret = pMFCreateMediaType(&mediatype); + CHECK_HRESULT("MFCreateMediaType", ret); + + ret = IMFMediaType_SetGUID(mediatype, &SDL_MF_MT_MAJOR_TYPE, &SDL_MFMediaType_Video); + CHECK_HRESULT("IMFMediaType_SetGUID(major_type)", ret); + + ret = IMFMediaType_SetGUID(mediatype, &SDL_MF_MT_SUBTYPE, SDLFmtToMFVidFmtGuid(spec->format)); + CHECK_HRESULT("IMFMediaType_SetGUID(subtype)", ret); + + ret = IMFMediaType_SetUINT64(mediatype, &SDL_MF_MT_FRAME_SIZE, (((UINT64)spec->width) << 32) | ((UINT64)spec->height)); + CHECK_HRESULT("MFSetAttributeSize(frame_size)", ret); + + ret = IMFMediaType_SetUINT64(mediatype, &SDL_MF_MT_FRAME_RATE, (((UINT64)spec->interval_numerator) << 32) | ((UINT64)spec->interval_denominator)); + CHECK_HRESULT("MFSetAttributeRatio(frame_rate)", ret); + + ret = IMFSourceReader_SetCurrentMediaType(srcreader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, mediatype); + CHECK_HRESULT("IMFSourceReader_SetCurrentMediaType", ret); + + #if 0 // this (untested thing) is what we would do to get started with a IMFMediaSource that _doesn't_ use IMFSourceReader... + ret = IMFMediaSource_CreatePresentationDescriptor(source, &presentdesc); + CHECK_HRESULT("IMFMediaSource_CreatePresentationDescriptor", ret); + + ret = IMFPresentationDescriptor_GetStreamDescriptorCount(presentdesc, &num_streams); + CHECK_HRESULT("IMFPresentationDescriptor_GetStreamDescriptorCount", ret); + + for (DWORD i = 0; i < num_streams; i++) { + BOOL selected = FALSE; + ret = IMFPresentationDescriptor_GetStreamDescriptorByIndex(presentdesc, i, &selected, &streamdesc); + CHECK_HRESULT("IMFPresentationDescriptor_GetStreamDescriptorByIndex", ret); + + if (selected) { + ret = IMFStreamDescriptor_GetMediaTypeHandler(streamdesc, &handler); + CHECK_HRESULT("IMFStreamDescriptor_GetMediaTypeHandler", ret); + IMFMediaTypeHandler_SetCurrentMediaType(handler, mediatype); + IMFMediaTypeHandler_Release(handler); + handler = NULL; + } + + IMFStreamDescriptor_Release(streamdesc); + streamdesc = NULL; + } + + PropVariantInit(&var); + var.vt = VT_EMPTY; + ret = IMFMediaSource_Start(source, presentdesc, NULL, &var); + PropVariantClear(&var); + CHECK_HRESULT("IMFMediaSource_Start", ret); + + IMFPresentationDescriptor_Release(presentdesc); + presentdesc = NULL; + #endif + + ret = GetDefaultStride(mediatype, &lstride); + CHECK_HRESULT("GetDefaultStride", ret); + + IMFMediaType_Release(mediatype); + mediatype = NULL; + + device->hidden = (SDL_PrivateCameraData *) SDL_calloc(1, sizeof (SDL_PrivateCameraData)); + if (!device->hidden) { + goto failed; + } + + device->hidden->pitch = (int) lstride; + device->hidden->srcreader = srcreader; + IMFMediaSource_Release(source); // srcreader is holding a reference to this. + + // There is no user permission prompt for camera access (I think?) + SDL_CameraDevicePermissionOutcome(device, SDL_TRUE); + + #undef CHECK_HRESULT + + return 0; + +failed: + + if (srcreader) { + IMFSourceReader_Release(srcreader); + } + + #if 0 + if (handler) { + IMFMediaTypeHandler_Release(handler); + } + + if (streamdesc) { + IMFStreamDescriptor_Release(streamdesc); + } + + if (presentdesc) { + IMFPresentationDescriptor_Release(presentdesc); + } + #endif + + if (source) { + IMFMediaSource_Shutdown(source); + IMFMediaSource_Release(source); + } + + if (mediatype) { + IMFMediaType_Release(mediatype); + } + + if (attrs) { + IMFAttributes_Release(attrs); + } + SDL_free(wstrsymlink); + + return -1; +} + +static void MEDIAFOUNDATION_FreeDeviceHandle(SDL_CameraDevice *device) +{ + if (device) { + SDL_free(device->handle); // the device's symlink string. + } +} + +static char *QueryActivationObjectString(IMFActivate *activation, const GUID *pguid) +{ + LPWSTR wstr = NULL; + UINT32 wlen = 0; + HRESULT ret = IMFActivate_GetAllocatedString(activation, pguid, &wstr, &wlen); + if (FAILED(ret)) { + return NULL; + } + + char *utf8str = WIN_StringToUTF8(wstr); + CoTaskMemFree(wstr); + return utf8str; +} + +static void GatherCameraSpecs(IMFMediaSource *source, CameraFormatAddData *add_data) +{ + HRESULT ret; + + // this has like a thousand steps. :/ + + SDL_zerop(add_data); + + IMFPresentationDescriptor *presentdesc = NULL; + ret = IMFMediaSource_CreatePresentationDescriptor(source, &presentdesc); + if (FAILED(ret) || !presentdesc) { + return; + } + + DWORD num_streams = 0; + ret = IMFPresentationDescriptor_GetStreamDescriptorCount(presentdesc, &num_streams); + if (FAILED(ret)) { + num_streams = 0; + } + + for (DWORD i = 0; i < num_streams; i++) { + IMFStreamDescriptor *streamdesc = NULL; + BOOL selected = FALSE; + ret = IMFPresentationDescriptor_GetStreamDescriptorByIndex(presentdesc, i, &selected, &streamdesc); + if (FAILED(ret) || !streamdesc) { + continue; + } + + if (selected) { + IMFMediaTypeHandler *handler = NULL; + ret = IMFStreamDescriptor_GetMediaTypeHandler(streamdesc, &handler); + if (SUCCEEDED(ret) && handler) { + DWORD num_mediatype = 0; + ret = IMFMediaTypeHandler_GetMediaTypeCount(handler, &num_mediatype); + if (FAILED(ret)) { + num_mediatype = 0; + } + + for (DWORD j = 0; j < num_mediatype; j++) { + IMFMediaType *mediatype = NULL; + ret = IMFMediaTypeHandler_GetMediaTypeByIndex(handler, j, &mediatype); + if (SUCCEEDED(ret) && mediatype) { + GUID type; + ret = IMFMediaType_GetGUID(mediatype, &SDL_MF_MT_MAJOR_TYPE, &type); + if (SUCCEEDED(ret) && WIN_IsEqualGUID(&type, &SDL_MFMediaType_Video)) { + ret = IMFMediaType_GetGUID(mediatype, &SDL_MF_MT_SUBTYPE, &type); + if (SUCCEEDED(ret)) { + const Uint32 sdlfmt = MFVidFmtGuidToSDLFmt(&type); + if (sdlfmt != SDL_PIXELFORMAT_UNKNOWN) { + UINT64 val = 0; + UINT32 w = 0, h = 0; + ret = IMFMediaType_GetUINT64(mediatype, &SDL_MF_MT_FRAME_SIZE, &val); + w = (UINT32)(val >> 32); + h = (UINT32)val; + if (SUCCEEDED(ret) && w && h) { + UINT32 interval_numerator = 0, interval_denominator = 0; + ret = IMFMediaType_GetUINT64(mediatype, &SDL_MF_MT_FRAME_RATE, &val); + interval_numerator = (UINT32)(val >> 32); + interval_denominator = (UINT32)val; + if (SUCCEEDED(ret) && interval_numerator && interval_denominator) { + SDL_AddCameraFormat(add_data, sdlfmt, (int) w, (int) h, (int) interval_numerator, (int) interval_denominator); // whew. + } + } + } + } + } + IMFMediaType_Release(mediatype); + } + } + IMFMediaTypeHandler_Release(handler); + } + } + IMFStreamDescriptor_Release(streamdesc); + } + + IMFPresentationDescriptor_Release(presentdesc); +} + +static SDL_bool FindMediaFoundationCameraDeviceBySymlink(SDL_CameraDevice *device, void *userdata) +{ + return (SDL_strcmp((const char *) device->handle, (const char *) userdata) == 0); +} + +static void MaybeAddDevice(IMFActivate *activation) +{ + char *symlink = QueryActivationObjectString(activation, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK); + + if (SDL_FindPhysicalCameraDeviceByCallback(FindMediaFoundationCameraDeviceBySymlink, symlink)) { + SDL_free(symlink); + return; // already have this one. + } + + char *name = QueryActivationObjectString(activation, &MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME); + if (name && symlink) { + IMFMediaSource *source = NULL; + // "activating" here only creates an object, it doesn't open the actual camera hardware or start recording. + HRESULT ret = IMFActivate_ActivateObject(activation, &SDL_IID_IMFMediaSource, &source); + if (SUCCEEDED(ret) && source) { + CameraFormatAddData add_data; + GatherCameraSpecs(source, &add_data); + if (add_data.num_specs > 0) { + SDL_AddCameraDevice(name, add_data.num_specs, add_data.specs, symlink); + } + SDL_free(add_data.specs); + IMFActivate_ShutdownObject(activation); + IMFMediaSource_Release(source); + } + } + + SDL_free(name); +} + +static void MEDIAFOUNDATION_DetectDevices(void) +{ + // !!! FIXME: use CM_Register_Notification (Win8+) to get device notifications. + // !!! FIXME: Earlier versions can use RegisterDeviceNotification, but I'm not bothering: no hotplug for you! + HRESULT ret; + + IMFAttributes *attrs = NULL; + ret = pMFCreateAttributes(&attrs, 1); + if (FAILED(ret)) { + return; // oh well, no cameras for you. + } + + // !!! FIXME: We need these GUIDs hardcoded in this file. + ret = IMFAttributes_SetGUID(attrs, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, &MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID); + if (FAILED(ret)) { + IMFAttributes_Release(attrs); + return; // oh well, no cameras for you. + } + + IMFActivate **activations = NULL; + UINT32 total = 0; + ret = pMFEnumDeviceSources(attrs, &activations, &total); + IMFAttributes_Release(attrs); + if (FAILED(ret)) { + return; // oh well, no cameras for you. + } + + for (UINT32 i = 0; i < total; i++) { + MaybeAddDevice(activations[i]); + IMFActivate_Release(activations[i]); + } + + CoTaskMemFree(activations); +} + +static void MEDIAFOUNDATION_Deinitialize(void) +{ + pMFShutdown(); + + FreeLibrary(libmfreadwrite); + libmfreadwrite = NULL; + FreeLibrary(libmfplat); + libmfplat = NULL; + FreeLibrary(libmf); + libmf = NULL; + + pMFEnumDeviceSources = NULL; + pMFCreateDeviceSource = NULL; + pMFStartup = NULL; + pMFShutdown = NULL; + pMFCreateAttributes = NULL; + pMFCreateMediaType = NULL; + pMFCreateSourceReaderFromMediaSource = NULL; + pMFGetStrideForBitmapInfoHeader = NULL; +} + +static SDL_bool MEDIAFOUNDATION_Init(SDL_CameraDriverImpl *impl) +{ + // !!! FIXME: slide this off into a subroutine + HANDLE mf = LoadLibrary(TEXT("Mf.dll")); // this library is available in Vista and later, but also can be on XP with service packs and Windows + if (!mf) { + return SDL_FALSE; + } + + HANDLE mfplat = LoadLibrary(TEXT("Mfplat.dll")); // this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now! + if (!mfplat) { + FreeLibrary(mf); + return SDL_FALSE; + } + + HANDLE mfreadwrite = LoadLibrary(TEXT("Mfreadwrite.dll")); // this library is available in Vista and later. No WinXP, so have to LoadLibrary to use it for now! + if (!mfreadwrite) { + FreeLibrary(mfplat); + FreeLibrary(mf); + return SDL_FALSE; + } + + SDL_bool okay = SDL_TRUE; + #define LOADSYM(lib, fn) if (okay) { p##fn = (pfn##fn) GetProcAddress(lib, #fn); if (!p##fn) { okay = SDL_FALSE; } } + LOADSYM(mf, MFEnumDeviceSources); + LOADSYM(mf, MFCreateDeviceSource); + LOADSYM(mfplat, MFStartup); + LOADSYM(mfplat, MFShutdown); + LOADSYM(mfplat, MFCreateAttributes); + LOADSYM(mfplat, MFCreateMediaType); + LOADSYM(mfplat, MFGetStrideForBitmapInfoHeader); + LOADSYM(mfreadwrite, MFCreateSourceReaderFromMediaSource); + #undef LOADSYM + + if (!okay) { + FreeLibrary(mfreadwrite); + FreeLibrary(mfplat); + FreeLibrary(mf); + } + + libmf = mf; + libmfplat = mfplat; + libmfreadwrite = mfreadwrite; + + const HRESULT ret = pMFStartup(MF_VERSION, MFSTARTUP_LITE); + if (FAILED(ret)) { + FreeLibrary(libmfplat); + libmfplat = NULL; + FreeLibrary(libmf); + libmf = NULL; + return SDL_FALSE; + } + + impl->DetectDevices = MEDIAFOUNDATION_DetectDevices; + impl->OpenDevice = MEDIAFOUNDATION_OpenDevice; + impl->CloseDevice = MEDIAFOUNDATION_CloseDevice; + impl->WaitDevice = MEDIAFOUNDATION_WaitDevice; + impl->AcquireFrame = MEDIAFOUNDATION_AcquireFrame; + impl->ReleaseFrame = MEDIAFOUNDATION_ReleaseFrame; + impl->FreeDeviceHandle = MEDIAFOUNDATION_FreeDeviceHandle; + impl->Deinitialize = MEDIAFOUNDATION_Deinitialize; + + return SDL_TRUE; +} + +CameraBootStrap MEDIAFOUNDATION_bootstrap = { + "mediafoundation", "SDL Windows Media Foundation camera driver", MEDIAFOUNDATION_Init, SDL_FALSE +}; + +#endif // SDL_CAMERA_DRIVER_MEDIAFOUNDATION + diff --git a/src/camera/v4l2/SDL_camera_v4l2.c b/src/camera/v4l2/SDL_camera_v4l2.c index 9f3b1e08b0da0..0e2915910fbff 100644 --- a/src/camera/v4l2/SDL_camera_v4l2.c +++ b/src/camera/v4l2/SDL_camera_v4l2.c @@ -631,39 +631,7 @@ static SDL_bool FindV4L2CameraDeviceByBusInfoCallback(SDL_CameraDevice *device, return (SDL_strcmp(handle->bus_info, (const char *) userdata) == 0); } -typedef struct FormatAddData -{ - SDL_CameraSpec *specs; - int num_specs; - int allocated_specs; -} FormatAddData; - -static int AddCameraCompleteFormat(FormatAddData *data, Uint32 fmt, int w, int h, int interval_numerator, int interval_denominator) -{ - SDL_assert(data != NULL); - if (data->allocated_specs <= data->num_specs) { - const int newalloc = data->allocated_specs ? (data->allocated_specs * 2) : 16; - void *ptr = SDL_realloc(data->specs, sizeof (SDL_CameraSpec) * newalloc); - if (!ptr) { - return -1; - } - data->specs = (SDL_CameraSpec *) ptr; - data->allocated_specs = newalloc; - } - - SDL_CameraSpec *spec = &data->specs[data->num_specs]; - spec->format = fmt; - spec->width = w; - spec->height = h; - spec->interval_numerator = interval_numerator; - spec->interval_denominator = interval_denominator; - - data->num_specs++; - - return 0; -} - -static int AddCameraFormat(const int fd, FormatAddData *data, Uint32 sdlfmt, Uint32 v4l2fmt, int w, int h) +static int AddCameraFormat(const int fd, CameraFormatAddData *data, Uint32 sdlfmt, Uint32 v4l2fmt, int w, int h) { struct v4l2_frmivalenum frmivalenum; SDL_zero(frmivalenum); @@ -679,7 +647,7 @@ static int AddCameraFormat(const int fd, FormatAddData *data, Uint32 sdlfmt, Uin const float fps = (float) denominator / (float) numerator; SDL_Log("CAMERA: * Has discrete frame interval (%d / %d), fps=%f", numerator, denominator, fps); #endif - if (AddCameraCompleteFormat(data, sdlfmt, w, h, numerator, denominator) == -1) { + if (SDL_AddCameraFormat(data, sdlfmt, w, h, numerator, denominator) == -1) { return -1; // Probably out of memory; we'll go with what we have, if anything. } frmivalenum.index++; // set up for the next one. @@ -691,7 +659,7 @@ static int AddCameraFormat(const int fd, FormatAddData *data, Uint32 sdlfmt, Uin const float fps = (float) d / (float) n; SDL_Log("CAMERA: * Has %s frame interval (%d / %d), fps=%f", (frmivalenum.type == V4L2_FRMIVAL_TYPE_STEPWISE) ? "stepwise" : "continuous", n, d, fps); #endif - if (AddCameraCompleteFormat(data, sdlfmt, w, h, n, d) == -1) { + if (SDL_AddCameraFormat(data, sdlfmt, w, h, n, d) == -1) { return -1; // Probably out of memory; we'll go with what we have, if anything. } d += (int) frmivalenum.stepwise.step.denominator; @@ -739,7 +707,7 @@ static void MaybeAddDevice(const char *path) SDL_Log("CAMERA: V4L2 camera path='%s' bus_info='%s' name='%s'", path, (const char *) vcap.bus_info, vcap.card); #endif - FormatAddData add_data; + CameraFormatAddData add_data; SDL_zero(add_data); struct v4l2_fmtdesc fmtdesc; @@ -911,5 +879,5 @@ CameraBootStrap V4L2_bootstrap = { "v4l2", "SDL Video4Linux2 camera driver", V4L2_Init, SDL_FALSE }; -#endif // SDL_CAMERA_V4L2 +#endif // SDL_CAMERA_DRIVER_V4L2 From 22dbc0f32f9ea3a73c67c07cb7766dd1348f76dd Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 31 Jan 2024 15:32:03 -0500 Subject: [PATCH 048/220] camera: Patched to compile after rebasing to latest in main. --- include/SDL3/SDL_events.h | 1 + src/camera/SDL_camera.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index cbd1f2a8af6df..c60b351e6bd2e 100644 --- a/include/SDL3/SDL_events.h +++ b/include/SDL3/SDL_events.h @@ -39,6 +39,7 @@ #include #include #include +#include #include /* Set up for C function definitions, even when using C++ */ diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index 486b5001361e3..b4823f8063514 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -393,7 +393,7 @@ void SDL_CameraDeviceDisconnected(SDL_CameraDevice *device) ObtainPhysicalCameraDeviceObj(device); - const SDL_bool first_disconnect = SDL_AtomicCAS(&device->zombie, 0, 1); + const SDL_bool first_disconnect = SDL_AtomicCompareAndSwap(&device->zombie, 0, 1); if (first_disconnect) { // if already disconnected this device, don't do it twice. // Swap in "Zombie" versions of the usual platform interfaces, so the device will keep // making progress until the app closes it. @@ -706,7 +706,7 @@ SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device) SDL_Surface *srcsurf = acquired; if (device->needs_scaling == -1) { // downscaling? Do it first. -1: downscale, 0: no scaling, 1: upscale SDL_Surface *dstsurf = device->needs_conversion ? device->conversion_surface : output_surface; - SDL_SoftStretch(srcsurf, NULL, dstsurf, NULL); // !!! FIXME: linear scale? letterboxing? + SDL_SoftStretch(srcsurf, NULL, dstsurf, NULL, SDL_SCALEMODE_NEAREST); // !!! FIXME: linear scale? letterboxing? srcsurf = dstsurf; } if (device->needs_conversion) { @@ -717,7 +717,7 @@ SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device) srcsurf = dstsurf; } if (device->needs_scaling == 1) { // upscaling? Do it last. -1: downscale, 0: no scaling, 1: upscale - SDL_SoftStretch(srcsurf, NULL, output_surface, NULL); // !!! FIXME: linear scale? letterboxing? + SDL_SoftStretch(srcsurf, NULL, output_surface, NULL, SDL_SCALEMODE_NEAREST); // !!! FIXME: linear scale? letterboxing? } // we made a copy, so we can give the driver back its resources. From f8fa08d2b1a6e4a1f601d0981e66df28d5519d1d Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 31 Jan 2024 21:47:33 -0500 Subject: [PATCH 049/220] camera: Fix compiler warnings on some platforms. --- src/camera/SDL_camera.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index b4823f8063514..3a7854a19f31d 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -988,7 +988,7 @@ SDL_Camera *SDL_OpenCameraDevice(SDL_CameraDeviceID instance_id, const SDL_Camer // Start the camera thread if necessary if (!camera_driver.impl.ProvidesOwnCallbackThread) { char threadname[64]; - SDL_snprintf(threadname, sizeof (threadname), "SDLCamera%d", instance_id); + SDL_snprintf(threadname, sizeof (threadname), "SDLCamera%d", (int) instance_id); device->thread = SDL_CreateThreadInternal(CameraThread, threadname, 0, device); if (!device->thread) { ClosePhysicalCameraDevice(device); From 99d1337de2191f4d11957d0036a2a56ea8f9aaf6 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 6 Feb 2024 01:19:12 -0500 Subject: [PATCH 050/220] camera: Reenabled macOS/iOS support, with rewritten CoreMedia implementation. --- include/SDL3/SDL_camera.h | 4 +- include/build_config/SDL_build_config.h.cmake | 2 - include/build_config/SDL_build_config_ios.h | 6 +- include/build_config/SDL_build_config_macos.h | 2 - src/camera/SDL_camera.c | 10 +- src/camera/SDL_syscamera.h | 8 +- src/camera/coremedia/SDL_camera_coremedia.m | 712 ++++++++---------- .../SDL_camera_mediafoundation.c | 2 - 8 files changed, 317 insertions(+), 429 deletions(-) diff --git a/include/SDL3/SDL_camera.h b/include/SDL3/SDL_camera.h index a57a66a069967..c070666273d70 100644 --- a/include/SDL3/SDL_camera.h +++ b/include/SDL3/SDL_camera.h @@ -66,8 +66,8 @@ typedef struct SDL_CameraSpec Uint32 format; /**< Frame SDL_PixelFormatEnum format */ int width; /**< Frame width */ int height; /**< Frame height */ - int interval_numerator; /**< Frame rate numerator ((dom / num) == fps) */ - int interval_denominator; /**< Frame rate demoninator ((dom / num) == fps)*/ + int interval_numerator; /**< Frame rate numerator ((dom / num) == fps, (num / dom) == duration) */ + int interval_denominator; /**< Frame rate demoninator ((dom / num) == fps, (num / dom) == duration) */ } SDL_CameraSpec; /** diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index 11c1ad716ecd0..d25b31b632072 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -246,8 +246,6 @@ #cmakedefine USE_POSIX_SPAWN @USE_POSIX_SPAWN@ -#cmakedefine HAVE_COREMEDIA - /* SDL internal assertion support */ #if @SDL_DEFAULT_ASSERT_LEVEL_CONFIGURED@ #cmakedefine SDL_DEFAULT_ASSERT_LEVEL @SDL_DEFAULT_ASSERT_LEVEL@ diff --git a/include/build_config/SDL_build_config_ios.h b/include/build_config/SDL_build_config_ios.h index 649ff4e86690d..ed3b5480895cd 100644 --- a/include/build_config/SDL_build_config_ios.h +++ b/include/build_config/SDL_build_config_ios.h @@ -198,8 +198,6 @@ #define SDL_VIDEO_METAL 1 #endif -#define HAVE_COREMEDIA 1 - /* Enable system power support */ #define SDL_POWER_UIKIT 1 @@ -213,6 +211,10 @@ #define SDL_FILESYSTEM_COCOA 1 /* enable camera support */ +#ifndef SDL_PLATFORM_TVOS #define SDL_CAMERA_DRIVER_COREMEDIA 1 +#endif + +#define SDL_CAMERA_DRIVER_DUMMY 1 #endif /* SDL_build_config_ios_h_ */ diff --git a/include/build_config/SDL_build_config_macos.h b/include/build_config/SDL_build_config_macos.h index a8bed484d94d3..7766ddc0932e8 100644 --- a/include/build_config/SDL_build_config_macos.h +++ b/include/build_config/SDL_build_config_macos.h @@ -261,8 +261,6 @@ #endif #endif -#define HAVE_COREMEDIA 1 - /* Enable system power support */ #define SDL_POWER_MACOSX 1 diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index 3a7854a19f31d..6088f3d49d4ac 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -73,6 +73,12 @@ const char *SDL_GetCurrentCameraDriver(void) return camera_driver.name; } +char *SDL_GetCameraThreadName(SDL_CameraDevice *device, char *buf, size_t buflen) +{ + (void)SDL_snprintf(buf, buflen, "SDLCamera%d", (int) device->instance_id); + return buf; +} + int SDL_AddCameraFormat(CameraFormatAddData *data, Uint32 fmt, int w, int h, int interval_numerator, int interval_denominator) { SDL_assert(data != NULL); @@ -683,7 +689,7 @@ SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device) failed = SDL_TRUE; } - // we can let go of the lock once we've tried to grab a frame of video and maybe moved the output frame from the empty to the filled list. + // we can let go of the lock once we've tried to grab a frame of video and maybe moved the output frame off the empty list. // this lets us chew up the CPU for conversion and scaling without blocking other threads. SDL_UnlockMutex(device->lock); @@ -988,7 +994,7 @@ SDL_Camera *SDL_OpenCameraDevice(SDL_CameraDeviceID instance_id, const SDL_Camer // Start the camera thread if necessary if (!camera_driver.impl.ProvidesOwnCallbackThread) { char threadname[64]; - SDL_snprintf(threadname, sizeof (threadname), "SDLCamera%d", (int) instance_id); + SDL_GetCameraThreadName(device, threadname, sizeof (threadname)); device->thread = SDL_CreateThreadInternal(CameraThread, threadname, 0, device); if (!device->thread) { ClosePhysicalCameraDevice(device); diff --git a/src/camera/SDL_syscamera.h b/src/camera/SDL_syscamera.h index b99239ec51d72..9f556523d109a 100644 --- a/src/camera/SDL_syscamera.h +++ b/src/camera/SDL_syscamera.h @@ -28,10 +28,7 @@ #define DEBUG_CAMERA 0 -// !!! FIXME: update these drivers! -#ifdef SDL_CAMERA_DRIVER_COREMEDIA -#undef SDL_CAMERA_DRIVER_COREMEDIA -#endif +// !!! FIXME: update this driver! #ifdef SDL_CAMERA_DRIVER_ANDROID #undef SDL_CAMERA_DRIVER_ANDROID #endif @@ -53,6 +50,9 @@ extern SDL_CameraDevice *SDL_FindPhysicalCameraDeviceByCallback(SDL_bool (*callb // Backends should call this when the user has approved/denied access to a camera. extern void SDL_CameraDevicePermissionOutcome(SDL_CameraDevice *device, SDL_bool approved); +// Backends can call this to get a standardized name for a thread to power a specific camera device. +extern char *SDL_GetCameraThreadName(SDL_CameraDevice *device, char *buf, size_t buflen); + // These functions are the heart of the camera threads. Backends can call them directly if they aren't using the SDL-provided thread. extern void SDL_CameraThreadSetup(SDL_CameraDevice *device); extern SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device); diff --git a/src/camera/coremedia/SDL_camera_coremedia.m b/src/camera/coremedia/SDL_camera_coremedia.m index c37962026c205..cf07d47cce7a6 100644 --- a/src/camera/coremedia/SDL_camera_coremedia.m +++ b/src/camera/coremedia/SDL_camera_coremedia.m @@ -26,17 +26,6 @@ #include "../SDL_camera_c.h" #include "../../thread/SDL_systhread.h" -#if defined(HAVE_COREMEDIA) && defined(SDL_PLATFORM_MACOS) && (__MAC_OS_X_VERSION_MAX_ALLOWED < 101500) -// AVCaptureDeviceTypeBuiltInWideAngleCamera requires macOS SDK 10.15 -#undef HAVE_COREMEDIA -#endif - -#ifdef SDL_PLATFORM_TVOS -#undef HAVE_COREMEDIA -#endif - -#ifdef HAVE_COREMEDIA - #import #import @@ -50,537 +39,434 @@ * MACOSX: * Add to the Code Sign Entitlement file: * com.apple.security.device.camera - * - * - * IOS: - * - * - Need to link with:: CoreMedia CoreVideo - * - Add #define SDL_CAMERA 1 - * to SDL_build_config_ios.h */ -@class MySampleBufferDelegate; - -struct SDL_PrivateCameraData -{ - dispatch_queue_t queue; - MySampleBufferDelegate *delegate; - AVCaptureSession *session; - CMSimpleQueueRef frame_queue; -}; - -static NSString *fourcc_to_nstring(Uint32 code) +static Uint32 CoreMediaFormatToSDL(FourCharCode fmt) { - Uint8 buf[4]; - *(Uint32 *)buf = code; - return [NSString stringWithFormat:@"%c%c%c%c", buf[3], buf[2], buf[1], buf[0]]; + switch (fmt) { + #define CASE(x, y) case x: return y + // the 16LE ones should use 16BE if we're on a Bigendian system like PowerPC, + // but at current time there is no bigendian Apple platform that has CoreMedia. + CASE(kCMPixelFormat_16LE555, SDL_PIXELFORMAT_RGB555); + CASE(kCMPixelFormat_16LE5551, SDL_PIXELFORMAT_RGBA5551); + CASE(kCMPixelFormat_16LE565, SDL_PIXELFORMAT_RGB565); + CASE(kCMPixelFormat_24RGB, SDL_PIXELFORMAT_RGB24); + CASE(kCMPixelFormat_32ARGB, SDL_PIXELFORMAT_ARGB32); + CASE(kCMPixelFormat_32BGRA, SDL_PIXELFORMAT_BGRA32); + CASE(kCMPixelFormat_422YpCbCr8, SDL_PIXELFORMAT_YUY2); + CASE(kCMPixelFormat_422YpCbCr8_yuvs, SDL_PIXELFORMAT_UYVY); + #undef CASE + default: + #if DEBUG_CAMERA + SDL_Log("CAMERA: Unknown format FourCharCode '%d'", (int) fmt); + #endif + break; + } + return SDL_PIXELFORMAT_UNKNOWN; } -static NSArray *DiscoverCameraDevices() -{ - NSArray *deviceType = @[AVCaptureDeviceTypeBuiltInWideAngleCamera]; +@class SDLCaptureVideoDataOutputSampleBufferDelegate; - AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession - discoverySessionWithDeviceTypes:deviceType - mediaType:AVMediaTypeVideo - position:AVCaptureDevicePositionUnspecified]; +// just a simple wrapper to help ARC manage memory... +@interface SDLPrivateCameraData : NSObject +@property(nonatomic, retain) AVCaptureSession *session; +@property(nonatomic, retain) SDLCaptureVideoDataOutputSampleBufferDelegate *delegate; +@property(nonatomic, assign) CMSampleBufferRef current_sample; +@end - NSArray *devices = discoverySession.devices; +@implementation SDLPrivateCameraData +@end - if ([devices count] > 0) { - return devices; - } else { - AVCaptureDevice *captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; - if (captureDevice == nil) { - return devices; + +static SDL_bool CheckCameraPermissions(SDL_CameraDevice *device) +{ + if (device->permission == 0) { // still expecting a permission result. + if (@available(macOS 14, *)) { + const AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; + if (status != AVAuthorizationStatusNotDetermined) { // NotDetermined == still waiting for an answer from the user. + SDL_CameraDevicePermissionOutcome(device, (status == AVAuthorizationStatusAuthorized) ? SDL_TRUE : SDL_FALSE); + } } else { - NSArray *default_device = @[ captureDevice ]; - return default_device; + SDL_CameraDevicePermissionOutcome(device, SDL_TRUE); // always allowed (or just unqueryable...?) on older macOS. } } - return devices; + return (device->permission > 0); } -static AVCaptureDevice *GetCameraDeviceByName(const char *dev_name) -{ - NSArray *devices = DiscoverCameraDevices(); +// this delegate just receives new video frames on a Grand Central Dispatch queue, and fires off the +// main device thread iterate function directly to consume it. +@interface SDLCaptureVideoDataOutputSampleBufferDelegate : NSObject + @property SDL_CameraDevice *device; + -(id) init:(SDL_CameraDevice *) dev; + -(void) captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection; +@end - for (AVCaptureDevice *device in devices) { - char buf[1024]; - NSString *cameraID = [device localizedName]; - const char *str = [cameraID UTF8String]; - SDL_snprintf(buf, sizeof (buf) - 1, "%s", str); - if (SDL_strcmp(buf, dev_name) == 0) { - return device; +@implementation SDLCaptureVideoDataOutputSampleBufferDelegate + + -(id) init:(SDL_CameraDevice *) dev { + if ( self = [super init] ) { + _device = dev; } + return self; } - return nil; -} -static Uint32 nsfourcc_to_sdlformat(NSString *nsfourcc) -{ - const char *str = [nsfourcc UTF8String]; - - /* FIXME - * on IOS this mode gives 2 planes, and it's NV12 - * on macos, 1 plane/ YVYU - */ - #ifdef SDL_PLATFORM_MACOS - if (SDL_strcmp("420v", str) == 0) return SDL_PIXELFORMAT_YVYU; - #else - if (SDL_strcmp("420v", str) == 0) return SDL_PIXELFORMAT_NV12; - #endif + - (void) captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection + { + SDL_CameraDevice *device = self.device; + if (!device || !device->hidden) { + return; // oh well. + } - if (SDL_strcmp("yuvs", str) == 0) return SDL_PIXELFORMAT_UYVY; - if (SDL_strcmp("420f", str) == 0) return SDL_PIXELFORMAT_UNKNOWN; + if (!CheckCameraPermissions(device)) { + return; // nothing to do right now, dump what is probably a completely black frame. + } - #if DEBUG_CAMERA - SDL_Log("CAMERA: Unknown format '%s'", str); - #endif + SDLPrivateCameraData *hidden = (__bridge SDLPrivateCameraData *) device->hidden; + hidden.current_sample = sampleBuffer; + SDL_CameraThreadIterate(device); + hidden.current_sample = NULL; + } - return SDL_PIXELFORMAT_UNKNOWN; -} + - (void)captureOutput:(AVCaptureOutput *)output didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection + { + #if DEBUG_CAMERA + SDL_Log("CAMERA: Drop frame."); + #endif + } +@end -static NSString *sdlformat_to_nsfourcc(Uint32 fmt) +static int COREMEDIA_WaitDevice(SDL_CameraDevice *device) { - const char *str = ""; - NSString *result; - -#ifdef SDL_PLATFORM_MACOS - if (fmt == SDL_PIXELFORMAT_YVYU) str = "420v"; -#else - if (fmt == SDL_PIXELFORMAT_NV12) str = "420v"; -#endif - if (fmt == SDL_PIXELFORMAT_UYVY) str = "yuvs"; - - return [[NSString alloc] initWithUTF8String: str]; + return 0; // this isn't used atm, since we run our own thread out of Grand Central Dispatch. } +static int COREMEDIA_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame, Uint64 *timestampNS) +{ + int retval = 1; + SDLPrivateCameraData *hidden = (__bridge SDLPrivateCameraData *) device->hidden; + CMSampleBufferRef sample_buffer = hidden.current_sample; + hidden.current_sample = NULL; + SDL_assert(sample_buffer != NULL); // should only have been called from our delegate with a new frame. + + CMSampleTimingInfo timinginfo; + if (CMSampleBufferGetSampleTimingInfo(sample_buffer, 0, &timinginfo) == noErr) { + *timestampNS = (Uint64) (CMTimeGetSeconds(timinginfo.presentationTimeStamp) * ((Float64) SDL_NS_PER_SECOND)); + } else { + SDL_assert(!"this shouldn't happen, I think."); + *timestampNS = 0; + } -@interface MySampleBufferDelegate : NSObject - @property struct SDL_PrivateCameraData *hidden; - - (void) set: (struct SDL_PrivateCameraData *) val; -@end + CVImageBufferRef image = CMSampleBufferGetImageBuffer(sample_buffer); // does not retain `image` (and we don't want it to). + const int numPlanes = (int) CVPixelBufferGetPlaneCount(image); + const int planar = (int) CVPixelBufferIsPlanar(image); -@implementation MySampleBufferDelegate + #if DEBUG_CAMERA + const int w = (int) CVPixelBufferGetWidth(image); + const int h = (int) CVPixelBufferGetHeight(image); + const int sz = (int) CVPixelBufferGetDataSize(image); + const int pitch = (int) CVPixelBufferGetBytesPerRow(image); + SDL_Log("CAMERA: buffer planar=%d numPlanes=%d %d x %d sz=%d pitch=%d", planar, numPlanes, w, h, sz, pitch); + #endif - - (void) set: (struct SDL_PrivateCameraData *) val { - _hidden = val; - } + // !!! FIXME: this currently copies the data to the surface (see FIXME about non-contiguous planar surfaces, but in theory we could just keep this locked until ReleaseFrame... + CVPixelBufferLockBaseAddress(image, 0); - - (void) captureOutput:(AVCaptureOutput *)output - didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer - fromConnection:(AVCaptureConnection *) connection { - CFRetain(sampleBuffer); - CMSimpleQueueEnqueue(_hidden->frame_queue, sampleBuffer); + if ((planar == 0) && (numPlanes == 0)) { + const int pitch = (int) CVPixelBufferGetBytesPerRow(image); + const size_t buflen = pitch * frame->h; + frame->pixels = SDL_aligned_alloc(SDL_SIMDGetAlignment(), buflen); + if (frame->pixels == NULL) { + retval = -1; + } else { + frame->pitch = pitch; + SDL_memcpy(frame->pixels, CVPixelBufferGetBaseAddress(image), buflen); } - - - (void)captureOutput:(AVCaptureOutput *)output - didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer - fromConnection:(AVCaptureConnection *)connection { - #if DEBUG_CAMERA - SDL_Log("CAMERA: Drop frame.."); - #endif + } else { + // !!! FIXME: we have an open issue in SDL3 to allow SDL_Surface to support non-contiguous planar data, but we don't have it yet. + size_t buflen = 0; + for (int i = 0; (i < numPlanes) && (i < 3); i++) { + buflen += CVPixelBufferGetBytesPerRowOfPlane(image, i); } -@end + buflen *= frame->h; -static int COREMEDIA_OpenDevice(SDL_CameraDevice *_this) -{ - _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); - if (_this->hidden == NULL) { - return -1; + frame->pixels = SDL_aligned_alloc(SDL_SIMDGetAlignment(), buflen); + if (frame->pixels == NULL) { + retval = -1; + } else { + Uint8 *dst = frame->pixels; + frame->pitch = (int) CVPixelBufferGetBytesPerRowOfPlane(image, 0); // this is what SDL3 currently expects, probably incorrectly. + for (int i = 0; (i < numPlanes) && (i < 3); i++) { + const void *src = CVPixelBufferGetBaseAddressOfPlane(image, i); + const size_t pitch = CVPixelBufferGetBytesPerRowOfPlane(image, i); + SDL_memcpy(dst, src, pitch * frame->h); + dst += pitch * frame->h; + } + } } - return 0; + + CVPixelBufferUnlockBaseAddress(image, 0); + + return retval; } -static void COREMEDIA_CloseDevice(SDL_CameraDevice *_this) +static void COREMEDIA_ReleaseFrame(SDL_CameraDevice *device, SDL_Surface *frame) { - if (!_this) { - return; - } + // !!! FIXME: this currently copies the data to the surface, but in theory we could just keep this locked until ReleaseFrame... + SDL_aligned_free(frame->pixels); +} - if (_this->hidden) { - AVCaptureSession *session = _this->hidden->session; +static void COREMEDIA_CloseDevice(SDL_CameraDevice *device) +{ + if (device && device->hidden) { + SDLPrivateCameraData *hidden = (SDLPrivateCameraData *) CFBridgingRelease(device->hidden); + device->hidden = NULL; + AVCaptureSession *session = hidden.session; if (session) { - AVCaptureInput *input; - AVCaptureVideoDataOutput *output; - input = [session.inputs objectAtIndex:0]; - [session removeInput:input]; - output = (AVCaptureVideoDataOutput*)[session.outputs objectAtIndex:0]; - [session removeOutput:output]; - // TODO more cleanup ? + hidden.session = nil; + [session stopRunning]; + [session removeInput:[session.inputs objectAtIndex:0]]; + [session removeOutput:(AVCaptureVideoDataOutput*)[session.outputs objectAtIndex:0]]; + session = nil; } - if (_this->hidden->frame_queue) { - CFRelease(_this->hidden->frame_queue); - } - - SDL_free(_this->hidden); - _this->hidden = NULL; + hidden.delegate = NULL; + hidden.current_sample = NULL; } } -static int COREMEDIA_InitDevice(SDL_CameraDevice *_this) +static int COREMEDIA_OpenDevice(SDL_CameraDevice *device, const SDL_CameraSpec *spec) { - // !!! FIXME: autorelease pool? - NSString *fmt = sdlformat_to_nsfourcc(_this->spec.format); - int w = _this->spec.width; - int h = _this->spec.height; - - NSError *error = nil; - AVCaptureDevice *device = nil; - AVCaptureDeviceInput *input = nil; - AVCaptureVideoDataOutput *output = nil; - - AVCaptureDeviceFormat *spec_format = nil; - -#ifdef SDL_PLATFORM_MACOS - if (@available(macOS 10.15, *)) { - // good. - } else { - return -1; - } -#endif - - device = GetCameraDeviceByName(_this->dev_name); - if (!device) { - goto error; - } - - _this->hidden->session = [[AVCaptureSession alloc] init]; - if (_this->hidden->session == nil) { - goto error; - } - - [_this->hidden->session setSessionPreset:AVCaptureSessionPresetHigh]; + AVCaptureDevice *avdevice = (__bridge AVCaptureDevice *) device->handle; // Pick format that matches the spec - NSArray *formats = [device formats]; + const Uint32 sdlfmt = spec->format; + const int w = spec->width; + const int h = spec->height; + const int rate = spec->interval_denominator; + AVCaptureDeviceFormat *spec_format = nil; + NSArray *formats = [avdevice formats]; for (AVCaptureDeviceFormat *format in formats) { CMFormatDescriptionRef formatDescription = [format formatDescription]; - FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription); - NSString *str = fourcc_to_nstring(mediaSubType); - if ([str isEqualToString:fmt]) { - CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDescription); - if (dim.width == w && dim.height == h) { - spec_format = format; - break; - } + if (CoreMediaFormatToSDL(CMFormatDescriptionGetMediaSubType(formatDescription)) != sdlfmt) { + continue; + } + + const CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDescription); + if ( ((int) dim.width != w) || (((int) dim.height) != h) ) { + continue; + } + + for (AVFrameRateRange *framerate in format.videoSupportedFrameRateRanges) { + if ((rate == (int) SDL_ceil((double) framerate.minFrameRate)) || (rate == (int) SDL_floor((double) framerate.maxFrameRate))) { + spec_format = format; + break; } } + + if (spec_format != nil) { + break; + } } if (spec_format == nil) { - return SDL_SetError("format not found"); + return SDL_SetError("camera spec format not available"); + } else if (![avdevice lockForConfiguration:NULL]) { + return SDL_SetError("Cannot lockForConfiguration"); } - // Set format - if ([device lockForConfiguration:NULL] == YES) { - device.activeFormat = spec_format; - [device unlockForConfiguration]; - } else { - return SDL_SetError("Cannot lockForConfiguration"); + avdevice.activeFormat = spec_format; + [avdevice unlockForConfiguration]; + + AVCaptureSession *session = [[AVCaptureSession alloc] init]; + if (session == nil) { + return SDL_SetError("Failed to allocate/init AVCaptureSession"); } - // Input - input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error]; + session.sessionPreset = AVCaptureSessionPresetHigh; + + NSError *error = nil; + AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:avdevice error:&error]; if (!input) { return SDL_SetError("Cannot create AVCaptureDeviceInput"); } - // Output - output = [[AVCaptureVideoDataOutput alloc] init]; - -#ifdef SDL_PLATFORM_MACOS - // FIXME this now fail on ios ... but not using anything works... - - // Specify the pixel format - output.videoSettings = - [NSDictionary dictionaryWithObject: - [NSNumber numberWithInt:kCVPixelFormatType_422YpCbCr8] - forKey:(id)kCVPixelBufferPixelFormatTypeKey]; -#endif - - _this->hidden->delegate = [[MySampleBufferDelegate alloc] init]; - [_this->hidden->delegate set:_this->hidden]; - + AVCaptureVideoDataOutput *output = [[AVCaptureVideoDataOutput alloc] init]; + if (!output) { + return SDL_SetError("Cannot create AVCaptureVideoDataOutput"); + } - CMSimpleQueueCreate(kCFAllocatorDefault, 30 /* buffers */, &_this->hidden->frame_queue); - if (_this->hidden->frame_queue == nil) { - return SDL_SetError("CMSimpleQueueCreate() failed"); + char threadname[64]; + SDL_GetCameraThreadName(device, threadname, sizeof (threadname)); + dispatch_queue_t queue = dispatch_queue_create(threadname, NULL); + //dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + if (!queue) { + return SDL_SetError("dispatch_queue_create() failed"); } - _this->hidden->queue = dispatch_queue_create("my_queue", NULL); - [output setSampleBufferDelegate:_this->hidden->delegate queue:_this->hidden->queue]; + SDLCaptureVideoDataOutputSampleBufferDelegate *delegate = [[SDLCaptureVideoDataOutputSampleBufferDelegate alloc] init:device]; + if (delegate == nil) { + return SDL_SetError("Cannot create SDLCaptureVideoDataOutputSampleBufferDelegate"); + } + [output setSampleBufferDelegate:delegate queue:queue]; - if ([_this->hidden->session canAddInput:input] ){ - [_this->hidden->session addInput:input]; - } else { + if (![session canAddInput:input]) { return SDL_SetError("Cannot add AVCaptureDeviceInput"); } + [session addInput:input]; - if ([_this->hidden->session canAddOutput:output] ){ - [_this->hidden->session addOutput:output]; - } else { + if (![session canAddOutput:output]) { return SDL_SetError("Cannot add AVCaptureVideoDataOutput"); } + [session addOutput:output]; - [_this->hidden->session commitConfiguration]; - - return 0; -} + [session commitConfiguration]; -static int COREMEDIA_GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) -{ - // !!! FIXME: make sure higher level checks spec != NULL - if (spec) { - SDL_copyp(spec, &_this->spec); - return 0; + SDLPrivateCameraData *hidden = [[SDLPrivateCameraData alloc] init]; + if (hidden == nil) { + return SDL_SetError("Cannot create SDLPrivateCameraData"); } - return -1; -} -static int COREMEDIA_StartCamera(SDL_CameraDevice *_this) -{ - [_this->hidden->session startRunning]; - return 0; -} + hidden.session = session; + hidden.delegate = delegate; + hidden.current_sample = NULL; + device->hidden = (struct SDL_PrivateCameraData *)CFBridgingRetain(hidden); -static int COREMEDIA_StopCamera(SDL_CameraDevice *_this) -{ - [_this->hidden->session stopRunning]; - return 0; -} + [session startRunning]; // !!! FIXME: docs say this can block while camera warms up and shouldn't be done on main thread. Maybe push through `queue`? -static int COREMEDIA_AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) -{ - if (CMSimpleQueueGetCount(_this->hidden->frame_queue) > 0) { - CMSampleBufferRef sampleBuffer = (CMSampleBufferRef)CMSimpleQueueDequeue(_this->hidden->frame_queue); - frame->internal = (void *) sampleBuffer; - frame->timestampNS = SDL_GetTicksNS(); - - CVImageBufferRef image = CMSampleBufferGetImageBuffer(sampleBuffer); - const int numPlanes = CVPixelBufferGetPlaneCount(image); - const int planar = CVPixelBufferIsPlanar(image); - - #if DEBUG_CAMERA - const int w = CVPixelBufferGetWidth(image); - const int h = CVPixelBufferGetHeight(image); - const int sz = CVPixelBufferGetDataSize(image); - const int pitch = CVPixelBufferGetBytesPerRow(image); - SDL_Log("CAMERA: buffer planar=%d count:%d %d x %d sz=%d pitch=%d", planar, numPlanes, w, h, sz, pitch); - #endif + CheckCameraPermissions(device); // check right away, in case the process is already granted permission. - CVPixelBufferLockBaseAddress(image, 0); - - if ((planar == 0) && (numPlanes == 0)) { - frame->pitch[0] = CVPixelBufferGetBytesPerRow(image); - frame->data[0] = CVPixelBufferGetBaseAddress(image); - frame->num_planes = 1; - } else { - for (int i = 0; (i < numPlanes) && (i < 3); i++) { - frame->num_planes += 1; - frame->data[i] = CVPixelBufferGetBaseAddressOfPlane(image, i); - frame->pitch[i] = CVPixelBufferGetBytesPerRowOfPlane(image, i); - } - } - - // Unlocked when frame is released - } else { - // no frame - SDL_Delay(20); // TODO fix some delay - } return 0; } -static int COREMEDIA_ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +static void COREMEDIA_FreeDeviceHandle(SDL_CameraDevice *device) { - if (frame->internal) { - CMSampleBufferRef sampleBuffer = (CMSampleBufferRef) frame->internal; - CVImageBufferRef image = CMSampleBufferGetImageBuffer(sampleBuffer); - CVPixelBufferUnlockBaseAddress(image, 0); - CFRelease(sampleBuffer); + if (device && device->handle) { + CFBridgingRelease(device->handle); } - - return 0; } -static int COREMEDIA_GetNumFormats(SDL_CameraDevice *_this) +static void GatherCameraSpecs(AVCaptureDevice *device, CameraFormatAddData *add_data) { - AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name); - if (device) { - // LIST FORMATS - NSMutableOrderedSet *array_formats = [NSMutableOrderedSet new]; - NSArray *formats = [device formats]; - for (AVCaptureDeviceFormat *format in formats) { - // NSLog(@"%@", formats); - CMFormatDescriptionRef formatDescription = [format formatDescription]; - //NSLog(@"%@", formatDescription); - FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription); - NSString *str = fourcc_to_nstring(mediaSubType); - [array_formats addObject:str]; - } - return [array_formats count]; - } - return 0; -} + SDL_zerop(add_data); -static int COREMEDIA_GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) -{ - AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name); - if (device) { - // LIST FORMATS - NSMutableOrderedSet *array_formats = [NSMutableOrderedSet new]; - NSArray *formats = [device formats]; - NSString *str; - - for (AVCaptureDeviceFormat *f in formats) { - FourCharCode mediaSubType; - CMFormatDescriptionRef formatDescription; - - formatDescription = [f formatDescription]; - mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription); - str = fourcc_to_nstring(mediaSubType); - [array_formats addObject:str]; + for (AVCaptureDeviceFormat *fmt in device.formats) { + if (CMFormatDescriptionGetMediaType(fmt.formatDescription) != kCMMediaType_Video) { + continue; } - str = array_formats[index]; - *format = nsfourcc_to_sdlformat(str); + const Uint32 sdlfmt = CoreMediaFormatToSDL(CMFormatDescriptionGetMediaSubType(fmt.formatDescription)); + if (sdlfmt == SDL_PIXELFORMAT_UNKNOWN) { + continue; + } - return 0; - } - return -1; -} + const CMVideoDimensions dims = CMVideoFormatDescriptionGetDimensions(fmt.formatDescription); + const int w = (int) dims.width; + const int h = (int) dims.height; + for (AVFrameRateRange *framerate in fmt.videoSupportedFrameRateRanges) { + int rate; -static int COREMEDIA_GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) -{ - AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name); - if (device) { - NSString *fmt = sdlformat_to_nsfourcc(format); - int count = 0; - - NSArray *formats = [device formats]; - for (AVCaptureDeviceFormat *f in formats) { - CMFormatDescriptionRef formatDescription = [f formatDescription]; - FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription); - NSString *str = fourcc_to_nstring(mediaSubType); - - if ([str isEqualToString:fmt]) { - count++; + rate = (int) SDL_ceil((double) framerate.minFrameRate); + if (rate) { + SDL_AddCameraFormat(add_data, sdlfmt, w, h, 1, rate); + } + rate = (int) SDL_floor((double) framerate.maxFrameRate); + if (rate) { + SDL_AddCameraFormat(add_data, sdlfmt, w, h, 1, rate); } } - return count; } - return 0; } -static int COREMEDIA_GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) +static SDL_bool FindCoreMediaCameraDeviceByUniqueID(SDL_CameraDevice *device, void *userdata) { - AVCaptureDevice *device = GetCameraDeviceByName(_this->dev_name); - if (device) { - NSString *fmt = sdlformat_to_nsfourcc(format); - int count = 0; - - NSArray *formats = [device formats]; - for (AVCaptureDeviceFormat *f in formats) { - CMFormatDescriptionRef formatDescription = [f formatDescription]; - FourCharCode mediaSubType = CMFormatDescriptionGetMediaSubType(formatDescription); - NSString *str = fourcc_to_nstring(mediaSubType); - - if ([str isEqualToString:fmt]) { - if (index == count) { - CMVideoDimensions dim = CMVideoFormatDescriptionGetDimensions(formatDescription); - *width = dim.width; - *height = dim.height; - return 0; - } - count++; - } - } - } - return -1; + NSString *uniqueid = (__bridge NSString *) userdata; + AVCaptureDevice *avdev = (__bridge AVCaptureDevice *) device->handle; + return ([uniqueid isEqualToString:avdev.uniqueID]) ? SDL_TRUE : SDL_FALSE; } -static int COREMEDIA_GetDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) +static void MaybeAddDevice(AVCaptureDevice *device) { - int index = instance_id - 1; - NSArray *devices = DiscoverCameraDevices(); - if (index < [devices count]) { - AVCaptureDevice *device = devices[index]; - NSString *cameraID = [device localizedName]; - const char *str = [cameraID UTF8String]; - SDL_snprintf(buf, size, "%s", str); - return 0; + if (!device.connected) { + return; // not connected. + } else if (![device hasMediaType:AVMediaTypeVideo]) { + return; // not a camera. + } else if (SDL_FindPhysicalCameraDeviceByCallback(FindCoreMediaCameraDeviceByUniqueID, (__bridge void *) device.uniqueID)) { + return; // already have this one. } - return -1; -} -static int GetNumCameraDevices(void) -{ - NSArray *devices = DiscoverCameraDevices(); - return [devices count]; + CameraFormatAddData add_data; + GatherCameraSpecs(device, &add_data); + if (add_data.num_specs > 0) { + SDL_AddCameraDevice(device.localizedName.UTF8String, add_data.num_specs, add_data.specs, (void *) CFBridgingRetain(device)); + } + SDL_free(add_data.specs); } -static SDL_CameraDeviceID *COREMEDIA_GetDevices(int *count) +static void COREMEDIA_DetectDevices(void) { - // hard-coded list of ID - const int num = GetNumCameraDevices(); - SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_calloc((num + 1), sizeof(*ret)); + NSArray *devices = nil; + + if (@available(macOS 10.15, iOS 13, *)) { + // kind of annoying that there isn't a "give me anything that looks like a camera" option, + // so this list will need to be updated when Apple decides to add + // AVCaptureDeviceTypeBuiltInQuadrupleCamera some day. + NSArray *device_types = @[ + #ifdef SDL_PLATFORM_IOS + AVCaptureDeviceTypeBuiltInTelephotoCamera, + AVCaptureDeviceTypeBuiltInDualCamera, + AVCaptureDeviceTypeBuiltInDualWideCamera, + AVCaptureDeviceTypeBuiltInTripleCamera, + AVCaptureDeviceTypeBuiltInUltraWideCamera, + #else + AVCaptureDeviceTypeExternalUnknown, + #endif + AVCaptureDeviceTypeBuiltInWideAngleCamera + ]; - if (retval == NULL) { - *count = 0; - return NULL; - } + AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession + discoverySessionWithDeviceTypes:device_types + mediaType:AVMediaTypeVideo + position:AVCaptureDevicePositionUnspecified]; - for (int i = 0; i < num; i++) { - retval[i] = i + 1; + devices = discoverySession.devices; + // !!! FIXME: this can use Key Value Observation to get hotplug events. + } else { + // this is deprecated but works back to macOS 10.7; 10.15 added AVCaptureDeviceDiscoverySession as a replacement. + devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; + // !!! FIXME: this can use AVCaptureDeviceWasConnectedNotification and AVCaptureDeviceWasDisconnectedNotification with NSNotificationCenter to get hotplug events. } - retval[num] = 0; - *count = num; - return ret; -} -static void COREMEDIA_DetectDevices(void) -{ + for (AVCaptureDevice *device in devices) { + MaybeAddDevice(device); + } } static void COREMEDIA_Deinitialize(void) { + // !!! FIXME: disable hotplug. } static SDL_bool COREMEDIA_Init(SDL_CameraDriverImpl *impl) { -#ifndef HAVE_COREMEDIA - return SDL_FALSE; -#else impl->DetectDevices = COREMEDIA_DetectDevices; impl->OpenDevice = COREMEDIA_OpenDevice; impl->CloseDevice = COREMEDIA_CloseDevice; - impl->InitDevice = COREMEDIA_InitDevice; - impl->GetDeviceSpec = COREMEDIA_GetDeviceSpec; - impl->StartCamera = COREMEDIA_StartCamera; - impl->StopCamera = COREMEDIA_StopCamera; + impl->WaitDevice = COREMEDIA_WaitDevice; impl->AcquireFrame = COREMEDIA_AcquireFrame; impl->ReleaseFrame = COREMEDIA_ReleaseFrame; - impl->GetNumFormats = COREMEDIA_GetNumFormats; - impl->GetFormat = COREMEDIA_GetFormat; - impl->GetNumFrameSizes = COREMEDIA_GetNumFrameSizes; - impl->GetFrameSize = COREMEDIA_GetFrameSize; - impl->GetDeviceName = COREMEDIA_GetDeviceName; - impl->GetDevices = COREMEDIA_GetDevices; + impl->FreeDeviceHandle = COREMEDIA_FreeDeviceHandle; impl->Deinitialize = COREMEDIA_Deinitialize; + impl->ProvidesOwnCallbackThread = SDL_TRUE; + return SDL_TRUE; -#endif } CameraBootStrap COREMEDIA_bootstrap = { "coremedia", "SDL Apple CoreMedia camera driver", COREMEDIA_Init, SDL_FALSE }; -#endif // HAVE_COREMEDIA - -#endif // SDL_CAMERA_COREMEDIA +#endif // SDL_CAMERA_DRIVER_COREMEDIA diff --git a/src/camera/mediafoundation/SDL_camera_mediafoundation.c b/src/camera/mediafoundation/SDL_camera_mediafoundation.c index 960ab1e543a02..d2d79f94cc018 100644 --- a/src/camera/mediafoundation/SDL_camera_mediafoundation.c +++ b/src/camera/mediafoundation/SDL_camera_mediafoundation.c @@ -501,8 +501,6 @@ static int MEDIAFOUNDATION_OpenDevice(SDL_CameraDevice *device, const SDL_Camera //PROPVARIANT var; HRESULT ret; -SDL_Log("MEDIAFOUNDATION spec format: %s", SDL_GetPixelFormatName(spec->format)); - #if 0 IMFStreamDescriptor *streamdesc = NULL; IMFPresentationDescriptor *presentdesc = NULL; From 0b8617f71df031abb139741b01108081e8b68c1a Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 6 Feb 2024 01:51:41 -0500 Subject: [PATCH 051/220] test: Fixed CMake to build testcameraminimal correctly on WinRT. --- test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 38e72e4161292..db796e825716f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -395,7 +395,7 @@ add_sdl_test_executable(testtimer NONINTERACTIVE NONINTERACTIVE_ARGS --no-intera add_sdl_test_executable(testurl SOURCES testurl.c) add_sdl_test_executable(testver NONINTERACTIVE SOURCES testver.c) add_sdl_test_executable(testcamera SOURCES testcamera.c) -add_sdl_test_executable(testcameraminimal SOURCES testcameraminimal.c) +add_sdl_test_executable(testcameraminimal MAIN_CALLBACKS SOURCES testcameraminimal.c) add_sdl_test_executable(testviewport NEEDS_RESOURCES TESTUTILS SOURCES testviewport.c) add_sdl_test_executable(testwm SOURCES testwm.c) add_sdl_test_executable(testyuv NONINTERACTIVE NONINTERACTIVE_ARGS "--automated" NEEDS_RESOURCES TESTUTILS SOURCES testyuv.c testyuv_cvt.c) From 8db2a3b27a98054e4ac5f0bd23bd5c3d336cd90c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 7 Feb 2024 09:17:01 -0500 Subject: [PATCH 052/220] camera: Add an optional property that reports if a camera is back or front. This is useful for iOS and Android, so an app can find the camera it cares about in the list of devices. --- include/SDL3/SDL_camera.h | 12 +++++++++ include/SDL3/SDL_properties.h | 3 +++ src/camera/coremedia/SDL_camera_coremedia.m | 28 ++++++++++++++++----- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/include/SDL3/SDL_camera.h b/include/SDL3/SDL_camera.h index c070666273d70..57c41a130aa46 100644 --- a/include/SDL3/SDL_camera.h +++ b/include/SDL3/SDL_camera.h @@ -305,6 +305,16 @@ extern DECLSPEC SDL_CameraDeviceID SDLCALL SDL_GetCameraInstanceID(SDL_Camera *c /** * Get the properties associated with an opened camera. * + * The following read-only properties are provided by SDL: + * + * - `SDL_PROP_CAMERA_POSITION_STRING`: the position of the camera in + * relation to the hardware it is connected to. This is currently either + * the string "front" or "back", to signify which side of the user's + * device a camera is on. Future versions of SDL may add other position + * strings. This property is only set if this information can be + * determined by SDL. Most platforms do not set this attribute at all, + * but mobile devices tend to. + * * \param camera the SDL_Camera obtained from SDL_OpenCameraDevice() * \returns a valid property ID on success or 0 on failure; call * SDL_GetError() for more information. @@ -318,6 +328,8 @@ extern DECLSPEC SDL_CameraDeviceID SDLCALL SDL_GetCameraInstanceID(SDL_Camera *c */ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetCameraProperties(SDL_Camera *camera); +#define SDL_PROP_CAMERA_POSITION_STRING "SDL.camera.position" + /** * Get the spec that a camera is using when generating images. * diff --git a/include/SDL3/SDL_properties.h b/include/SDL3/SDL_properties.h index 8061972373c39..f2fda2810b8d2 100644 --- a/include/SDL3/SDL_properties.h +++ b/include/SDL3/SDL_properties.h @@ -183,6 +183,9 @@ extern DECLSPEC int SDLCALL SDL_SetProperty(SDL_PropertiesID props, const char * /** * Set a string property on a set of properties * + * This function makes a copy of the string; the caller does not have to + * preserve the data after this call completes. + * * \param props the properties to modify * \param name the name of the property to modify * \param value the new value of the property, or NULL to delete the property diff --git a/src/camera/coremedia/SDL_camera_coremedia.m b/src/camera/coremedia/SDL_camera_coremedia.m index cf07d47cce7a6..4d2e40f351f1f 100644 --- a/src/camera/coremedia/SDL_camera_coremedia.m +++ b/src/camera/coremedia/SDL_camera_coremedia.m @@ -386,20 +386,36 @@ static SDL_bool FindCoreMediaCameraDeviceByUniqueID(SDL_CameraDevice *device, vo return ([uniqueid isEqualToString:avdev.uniqueID]) ? SDL_TRUE : SDL_FALSE; } -static void MaybeAddDevice(AVCaptureDevice *device) +static void MaybeAddDevice(AVCaptureDevice *avdevice) { - if (!device.connected) { + if (!avdevice.connected) { return; // not connected. - } else if (![device hasMediaType:AVMediaTypeVideo]) { + } else if (![avdevice hasMediaType:AVMediaTypeVideo]) { return; // not a camera. - } else if (SDL_FindPhysicalCameraDeviceByCallback(FindCoreMediaCameraDeviceByUniqueID, (__bridge void *) device.uniqueID)) { + } else if (SDL_FindPhysicalCameraDeviceByCallback(FindCoreMediaCameraDeviceByUniqueID, (__bridge void *) avdevice.uniqueID)) { return; // already have this one. } CameraFormatAddData add_data; - GatherCameraSpecs(device, &add_data); + GatherCameraSpecs(avdevice, &add_data); if (add_data.num_specs > 0) { - SDL_AddCameraDevice(device.localizedName.UTF8String, add_data.num_specs, add_data.specs, (void *) CFBridgingRetain(device)); + SDL_CameraDevice *device = SDL_AddCameraDevice(avdevice.localizedName.UTF8String, add_data.num_specs, add_data.specs, (void *) CFBridgingRetain(avdevice)); + if (device) { + const char *posstr = NULL; + if (avdevice.position == AVCaptureDevicePositionFront) { + posstr = "front"; + } else if (avdevice.position == AVCaptureDevicePositionBack) { + posstr = "back"; + } + + if (posstr) { + SDL_Camera *camera = (SDL_Camera *) device; // currently there's no separation between physical and logical device. + SDL_PropertiesID props = SDL_GetCameraProperties(camera); + if (props) { + SDL_SetStringProperty(props, SDL_PROP_CAMERA_POSITION_STRING, posstr); + } + } + } } SDL_free(add_data.specs); } From f3485a47b3b21a3537d3321b2c00dbcdd4d3140b Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 13 Feb 2024 16:15:59 -0500 Subject: [PATCH 053/220] android: Add src/main/ files to Android.mk --- Android.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Android.mk b/Android.mk index 3779eb53416ba..9c15c72c50a12 100644 --- a/Android.mk +++ b/Android.mk @@ -41,6 +41,8 @@ LOCAL_SRC_FILES := \ $(wildcard $(LOCAL_PATH)/src/loadso/dlopen/*.c) \ $(wildcard $(LOCAL_PATH)/src/locale/*.c) \ $(wildcard $(LOCAL_PATH)/src/locale/android/*.c) \ + $(wildcard $(LOCAL_PATH)/src/main/*.c) \ + $(wildcard $(LOCAL_PATH)/src/main/generic/*.c) \ $(wildcard $(LOCAL_PATH)/src/misc/*.c) \ $(wildcard $(LOCAL_PATH)/src/misc/android/*.c) \ $(wildcard $(LOCAL_PATH)/src/power/*.c) \ From 47313bba32157edddbac4bb206878e4772513fd7 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 18 Feb 2024 00:47:03 -0500 Subject: [PATCH 054/220] camera: SDL_GetCameraDevices should not report "no devices" like an error. --- src/camera/SDL_camera.c | 39 ++++++++++++++++++++------------------- test/testcameraminimal.c | 14 ++++++++++++-- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index 6088f3d49d4ac..eb957d73d358e 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -539,7 +539,13 @@ char *SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id) SDL_CameraDeviceID *SDL_GetCameraDevices(int *count) { + int dummy_count; + if (!count) { + count = &dummy_count; + } + if (!SDL_GetCurrentCameraDriver()) { + *count = 0; SDL_SetError("Camera subsystem is not initialized"); return NULL; } @@ -548,31 +554,26 @@ SDL_CameraDeviceID *SDL_GetCameraDevices(int *count) SDL_LockRWLockForReading(camera_driver.device_hash_lock); int num_devices = SDL_AtomicGet(&camera_driver.device_count); - if (num_devices > 0) { - retval = (SDL_CameraDeviceID *) SDL_malloc((num_devices + 1) * sizeof (SDL_CameraDeviceID)); - if (!retval) { - num_devices = 0; - } else { - int devs_seen = 0; - const void *key; - const void *value; - void *iter = NULL; - while (SDL_IterateHashTable(camera_driver.device_hash, &key, &value, &iter)) { - retval[devs_seen++] = (SDL_CameraDeviceID) (uintptr_t) key; - } - - SDL_assert(devs_seen == num_devices); - retval[devs_seen] = 0; // null-terminated. + retval = (SDL_CameraDeviceID *) SDL_malloc((num_devices + 1) * sizeof (SDL_CameraDeviceID)); + if (!retval) { + num_devices = 0; + } else { + int devs_seen = 0; + const void *key; + const void *value; + void *iter = NULL; + while (SDL_IterateHashTable(camera_driver.device_hash, &key, &value, &iter)) { + retval[devs_seen++] = (SDL_CameraDeviceID) (uintptr_t) key; } + + SDL_assert(devs_seen == num_devices); + retval[devs_seen] = 0; // null-terminated. } SDL_UnlockRWLock(camera_driver.device_hash_lock); - if (count) { - *count = num_devices; - } + *count = num_devices; return retval; - } SDL_CameraSpec *SDL_GetCameraDeviceSupportedFormats(SDL_CameraDeviceID instance_id, int *count) diff --git a/test/testcameraminimal.c b/test/testcameraminimal.c index 6eb4f7968289a..9fc04c534b9db 100644 --- a/test/testcameraminimal.c +++ b/test/testcameraminimal.c @@ -26,6 +26,9 @@ static SDL_Surface *frame_current = NULL; int SDL_AppInit(int argc, char *argv[]) { + int devcount = 0; + int i; + /* Initialize test framework */ state = SDLTest_CommonCreateState(argv, 0); if (state == NULL) { @@ -55,17 +58,24 @@ int SDL_AppInit(int argc, char *argv[]) return -1; } - SDL_CameraDeviceID *devices = SDL_GetCameraDevices(NULL); + SDL_CameraDeviceID *devices = SDL_GetCameraDevices(&devcount); if (!devices) { SDL_Log("SDL_GetCameraDevices failed: %s", SDL_GetError()); return -1; } + SDL_Log("Saw %d camera devices.", devcount); + for (i = 0; i < devcount; i++) { + char *name = SDL_GetCameraDeviceName(devices[i]); + SDL_Log(" - Camera #%d: %s", i, name); + SDL_free(name); + } + const SDL_CameraDeviceID devid = devices[0]; /* just take the first one. */ SDL_free(devices); if (!devid) { - SDL_Log("No cameras available? %s", SDL_GetError()); + SDL_Log("No cameras available?"); return -1; } From 848dcf8a5fdfdd3dfbd1aabccb66794769d086bf Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 18 Feb 2024 00:49:20 -0500 Subject: [PATCH 055/220] main: Fixed compiler warning on Android. (the NDK got upset about a function with void params using a bare `()`.) --- src/main/SDL_main_callbacks.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/SDL_main_callbacks.h b/src/main/SDL_main_callbacks.h index 5a906cfc8e65b..54cda2fca275c 100644 --- a/src/main/SDL_main_callbacks.h +++ b/src/main/SDL_main_callbacks.h @@ -22,7 +22,7 @@ #ifndef SDL_main_callbacks_h_ #define SDL_main_callbacks_h_ -SDL_bool SDL_HasMainCallbacks(); +SDL_bool SDL_HasMainCallbacks(void); int SDL_InitMainCallbacks(int argc, char *argv[], SDL_AppInit_func appinit, SDL_AppIterate_func _appiter, SDL_AppEvent_func _appevent, SDL_AppQuit_func _appquit); int SDL_IterateMainCallbacks(SDL_bool pump_events); void SDL_QuitMainCallbacks(void); From 2613e3da24a9c083286d67bb4462531772c215ef Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 18 Feb 2024 00:50:32 -0500 Subject: [PATCH 056/220] camera: Rewrote Android support. This does something a little weird, in that it doesn't care what `__ANDROID_API__` is set to, but will attempt to dlopen the system libraries, like we do for many other platform-specific pieces of SDL. This allows us to a) not bump the minimum required Android version, which is extremely ancient but otherwise still working, doing the right thing on old and new hardware in the field, and b) not require the app to link against more libraries than it previously did before the feature was available. The downside is that it's a little messy, but it's okay for now, I think. --- Android.mk | 3 + .../app/src/main/AndroidManifest.xml | 7 + .../build_config/SDL_build_config_android.h | 1 + src/camera/SDL_syscamera.h | 10 +- src/camera/android/SDL_camera_android.c | 1121 ++++++++++------- 5 files changed, 665 insertions(+), 477 deletions(-) diff --git a/Android.mk b/Android.mk index 9c15c72c50a12..2758331744ddb 100644 --- a/Android.mk +++ b/Android.mk @@ -24,6 +24,9 @@ LOCAL_SRC_FILES := \ $(wildcard $(LOCAL_PATH)/src/audio/openslES/*.c) \ $(LOCAL_PATH)/src/atomic/SDL_atomic.c.arm \ $(LOCAL_PATH)/src/atomic/SDL_spinlock.c.arm \ + $(wildcard $(LOCAL_PATH)/src/camera/*.c) \ + $(wildcard $(LOCAL_PATH)/src/camera/android/*.c) \ + $(wildcard $(LOCAL_PATH)/src/camera/dummy/*.c) \ $(wildcard $(LOCAL_PATH)/src/core/*.c) \ $(wildcard $(LOCAL_PATH)/src/core/android/*.c) \ $(wildcard $(LOCAL_PATH)/src/cpuinfo/*.c) \ diff --git a/android-project/app/src/main/AndroidManifest.xml b/android-project/app/src/main/AndroidManifest.xml index 8617dca9e8ed0..9b0a816d2b9d9 100644 --- a/android-project/app/src/main/AndroidManifest.xml +++ b/android-project/app/src/main/AndroidManifest.xml @@ -37,6 +37,13 @@ android:name="android.hardware.microphone" android:required="false" /> --> + + + + diff --git a/include/build_config/SDL_build_config_android.h b/include/build_config/SDL_build_config_android.h index 9ae2ca4d95bbe..f279a40c12698 100644 --- a/include/build_config/SDL_build_config_android.h +++ b/include/build_config/SDL_build_config_android.h @@ -192,5 +192,6 @@ /* Enable the camera driver */ #define SDL_CAMERA_DRIVER_ANDROID 1 +#define SDL_CAMERA_DRIVER_DUMMY 1 #endif /* SDL_build_config_android_h_ */ diff --git a/src/camera/SDL_syscamera.h b/src/camera/SDL_syscamera.h index 9f556523d109a..fc55b071be3fd 100644 --- a/src/camera/SDL_syscamera.h +++ b/src/camera/SDL_syscamera.h @@ -27,12 +27,6 @@ #define DEBUG_CAMERA 0 - -// !!! FIXME: update this driver! -#ifdef SDL_CAMERA_DRIVER_ANDROID -#undef SDL_CAMERA_DRIVER_ANDROID -#endif - typedef struct SDL_CameraDevice SDL_CameraDevice; /* Backends should call this as devices are added to the system (such as @@ -53,6 +47,10 @@ extern void SDL_CameraDevicePermissionOutcome(SDL_CameraDevice *device, SDL_bool // Backends can call this to get a standardized name for a thread to power a specific camera device. extern char *SDL_GetCameraThreadName(SDL_CameraDevice *device, char *buf, size_t buflen); +// Backends can call these to change a device's refcount. +extern void RefPhysicalCameraDevice(SDL_CameraDevice *device); +extern void UnrefPhysicalCameraDevice(SDL_CameraDevice *device); + // These functions are the heart of the camera threads. Backends can call them directly if they aren't using the SDL-provided thread. extern void SDL_CameraThreadSetup(SDL_CameraDevice *device); extern SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device); diff --git a/src/camera/android/SDL_camera_android.c b/src/camera/android/SDL_camera_android.c index 20aeb06f106bd..5da1cc0cbe5bc 100644 --- a/src/camera/android/SDL_camera_android.c +++ b/src/camera/android/SDL_camera_android.c @@ -25,32 +25,34 @@ #include "../../video/SDL_pixels_c.h" #include "../../thread/SDL_systhread.h" -#if defined(SDL_CAMERA_DRIVER_ANDROID) - -#if __ANDROID_API__ >= 24 +#ifdef SDL_CAMERA_DRIVER_ANDROID /* - * APP_PLATFORM=android-24 - * minSdkVersion=24 - * - * link with: -lcamera2ndk -lmediandk - * * AndroidManifest.xml: * * * - * - * Add: #define SDL_CAMERA 1 - * in: include/build_config/SDL_build_config_android.h - * - * * Very likely SDL must be build with YUV support (done by default) * * https://developer.android.com/reference/android/hardware/camera2/CameraManager * "All camera devices intended to be operated concurrently, must be opened using openCamera(String, CameraDevice.StateCallback, Handler), - * before configuring sessions on any of the camera devices. * " + * before configuring sessions on any of the camera devices." */ +// this is kinda gross, but on older NDK headers all the camera stuff is +// gated behind __ANDROID_API__. We'll dlopen() it at runtime, so we'll do +// the right thing on pre-Android 7.0 devices, but we still +// need the struct declarations and such in those headers. +// The other option is to make a massive jump in minimum Android version we +// support--going from ancient to merely really old--but this seems less +// distasteful and using dlopen matches practices on other SDL platforms. +// We'll see if it works out. +#if __ANDROID_API__ < 24 +#undef __ANDROID_API__ +#define __ANDROID_API__ 24 +#endif + +#include #include #include #include @@ -58,87 +60,195 @@ #include "../../core/android/SDL_android.h" +static void *libcamera2ndk = NULL; +typedef ACameraManager* (*pfnACameraManager_create)(void); +typedef camera_status_t (*pfnACameraManager_registerAvailabilityCallback)(ACameraManager*, const ACameraManager_AvailabilityCallbacks*); +typedef camera_status_t (*pfnACameraManager_unregisterAvailabilityCallback)(ACameraManager*, const ACameraManager_AvailabilityCallbacks*); +typedef camera_status_t (*pfnACameraManager_getCameraIdList)(ACameraManager*, ACameraIdList**); +typedef void (*pfnACameraManager_deleteCameraIdList)(ACameraIdList*); +typedef void (*pfnACameraCaptureSession_close)(ACameraCaptureSession*); +typedef void (*pfnACaptureRequest_free)(ACaptureRequest*); +typedef void (*pfnACameraOutputTarget_free)(ACameraOutputTarget*); +typedef camera_status_t (*pfnACameraDevice_close)(ACameraDevice*); +typedef void (*pfnACameraManager_delete)(ACameraManager*); +typedef void (*pfnACaptureSessionOutputContainer_free)(ACaptureSessionOutputContainer*); +typedef void (*pfnACaptureSessionOutput_free)(ACaptureSessionOutput*); +typedef camera_status_t (*pfnACameraManager_openCamera)(ACameraManager*, const char*, ACameraDevice_StateCallbacks*, ACameraDevice**); +typedef camera_status_t (*pfnACameraDevice_createCaptureRequest)(const ACameraDevice*, ACameraDevice_request_template, ACaptureRequest**); +typedef camera_status_t (*pfnACameraDevice_createCaptureSession)(ACameraDevice*, const ACaptureSessionOutputContainer*, const ACameraCaptureSession_stateCallbacks*,ACameraCaptureSession**); +typedef camera_status_t (*pfnACameraManager_getCameraCharacteristics)(ACameraManager*, const char*, ACameraMetadata**); +typedef void (*pfnACameraMetadata_free)(ACameraMetadata*); +typedef camera_status_t (*pfnACameraMetadata_getConstEntry)(const ACameraMetadata*, uint32_t tag, ACameraMetadata_const_entry*); +typedef camera_status_t (*pfnACameraCaptureSession_setRepeatingRequest)(ACameraCaptureSession*, ACameraCaptureSession_captureCallbacks*, int numRequests, ACaptureRequest**, int*); +typedef camera_status_t (*pfnACameraOutputTarget_create)(ACameraWindowType*,ACameraOutputTarget**); +typedef camera_status_t (*pfnACaptureRequest_addTarget)(ACaptureRequest*, const ACameraOutputTarget*); +typedef camera_status_t (*pfnACaptureSessionOutputContainer_add)(ACaptureSessionOutputContainer*, const ACaptureSessionOutput*); +typedef camera_status_t (*pfnACaptureSessionOutputContainer_create)(ACaptureSessionOutputContainer**); +typedef camera_status_t (*pfnACaptureSessionOutput_create)(ACameraWindowType*, ACaptureSessionOutput**); +static pfnACameraManager_create pACameraManager_create = NULL; +static pfnACameraManager_registerAvailabilityCallback pACameraManager_registerAvailabilityCallback = NULL; +static pfnACameraManager_unregisterAvailabilityCallback pACameraManager_unregisterAvailabilityCallback = NULL; +static pfnACameraManager_getCameraIdList pACameraManager_getCameraIdList = NULL; +static pfnACameraManager_deleteCameraIdList pACameraManager_deleteCameraIdList = NULL; +static pfnACameraCaptureSession_close pACameraCaptureSession_close = NULL; +static pfnACaptureRequest_free pACaptureRequest_free = NULL; +static pfnACameraOutputTarget_free pACameraOutputTarget_free = NULL; +static pfnACameraDevice_close pACameraDevice_close = NULL; +static pfnACameraManager_delete pACameraManager_delete = NULL; +static pfnACaptureSessionOutputContainer_free pACaptureSessionOutputContainer_free = NULL; +static pfnACaptureSessionOutput_free pACaptureSessionOutput_free = NULL; +static pfnACameraManager_openCamera pACameraManager_openCamera = NULL; +static pfnACameraDevice_createCaptureRequest pACameraDevice_createCaptureRequest = NULL; +static pfnACameraDevice_createCaptureSession pACameraDevice_createCaptureSession = NULL; +static pfnACameraManager_getCameraCharacteristics pACameraManager_getCameraCharacteristics = NULL; +static pfnACameraMetadata_free pACameraMetadata_free = NULL; +static pfnACameraMetadata_getConstEntry pACameraMetadata_getConstEntry = NULL; +static pfnACameraCaptureSession_setRepeatingRequest pACameraCaptureSession_setRepeatingRequest = NULL; +static pfnACameraOutputTarget_create pACameraOutputTarget_create = NULL; +static pfnACaptureRequest_addTarget pACaptureRequest_addTarget = NULL; +static pfnACaptureSessionOutputContainer_add pACaptureSessionOutputContainer_add = NULL; +static pfnACaptureSessionOutputContainer_create pACaptureSessionOutputContainer_create = NULL; +static pfnACaptureSessionOutput_create pACaptureSessionOutput_create = NULL; + +static void *libmediandk = NULL; +typedef void (*pfnAImage_delete)(AImage*); +typedef media_status_t (*pfnAImage_getTimestamp)(const AImage*, int64_t*); +typedef media_status_t (*pfnAImage_getNumberOfPlanes)(const AImage*, int32_t*); +typedef media_status_t (*pfnAImage_getPlaneRowStride)(const AImage*, int, int32_t*); +typedef media_status_t (*pfnAImage_getPlaneData)(const AImage*, int, uint8_t**, int*); +typedef media_status_t (*pfnAImageReader_acquireNextImage)(AImageReader*, AImage**); +typedef void (*pfnAImageReader_delete)(AImageReader*); +typedef media_status_t (*pfnAImageReader_setImageListener)(AImageReader*, AImageReader_ImageListener*); +typedef media_status_t (*pfnAImageReader_getWindow)(AImageReader*, ANativeWindow**); +typedef media_status_t (*pfnAImageReader_new)(int32_t, int32_t, int32_t, int32_t, AImageReader**); +static pfnAImage_delete pAImage_delete = NULL; +static pfnAImage_getTimestamp pAImage_getTimestamp = NULL; +static pfnAImage_getNumberOfPlanes pAImage_getNumberOfPlanes = NULL; +static pfnAImage_getPlaneRowStride pAImage_getPlaneRowStride = NULL; +static pfnAImage_getPlaneData pAImage_getPlaneData = NULL; +static pfnAImageReader_acquireNextImage pAImageReader_acquireNextImage = NULL; +static pfnAImageReader_delete pAImageReader_delete = NULL; +static pfnAImageReader_setImageListener pAImageReader_setImageListener = NULL; +static pfnAImageReader_getWindow pAImageReader_getWindow = NULL; +static pfnAImageReader_new pAImageReader_new = NULL; + +typedef media_status_t (*pfnAImage_getWidth)(const AImage*, int32_t*); +typedef media_status_t (*pfnAImage_getHeight)(const AImage*, int32_t*); +static pfnAImage_getWidth pAImage_getWidth = NULL; +static pfnAImage_getHeight pAImage_getHeight = NULL; -static ACameraManager *cameraMgr = NULL; -static ACameraIdList *cameraIdList = NULL; +struct SDL_PrivateCameraData +{ + ACameraDevice *device; + AImageReader *reader; + ANativeWindow *window; + ACaptureSessionOutput *sessionOutput; + ACaptureSessionOutputContainer *sessionOutputContainer; + ACameraOutputTarget *outputTarget; + ACaptureRequest *request; + ACameraCaptureSession *session; + SDL_CameraSpec requested_spec; +}; -static int CreateCameraManager(void) +static int SetErrorStr(const char *what, const char *errstr, const int rc) { - if (cameraMgr == NULL) { - #if 0 // !!! FIXME: this is getting replaced in a different branch. - if (!Android_JNI_RequestPermission("android.permission.CAMERA")) { - SDL_SetError("This app doesn't have CAMERA permission"); - return; - } - #endif - cameraMgr = ACameraManager_create(); - if (cameraMgr == NULL) { - SDL_Log("Error creating ACameraManager"); - } else { - SDL_Log("Create ACameraManager"); - } + char errbuf[128]; + if (!errstr) { + SDL_snprintf(errbuf, sizeof (errbuf), "Unknown error #%d", rc); + errstr = errbuf; } + return SDL_SetError("%s: %s", what, errstr); +} - cameraMgr = ACameraManager_create(); - - return cameraMgr ? 0 : SDL_SetError("Error creating ACameraManager"); +static const char *CameraStatusStr(const camera_status_t rc) +{ + switch (rc) { + case ACAMERA_OK: return "no error"; + case ACAMERA_ERROR_UNKNOWN: return "unknown error"; + case ACAMERA_ERROR_INVALID_PARAMETER: return "invalid parameter"; + case ACAMERA_ERROR_CAMERA_DISCONNECTED: return "camera disconnected"; + case ACAMERA_ERROR_NOT_ENOUGH_MEMORY: return "not enough memory"; + case ACAMERA_ERROR_METADATA_NOT_FOUND: return "metadata not found"; + case ACAMERA_ERROR_CAMERA_DEVICE: return "camera device error"; + case ACAMERA_ERROR_CAMERA_SERVICE: return "camera service error"; + case ACAMERA_ERROR_SESSION_CLOSED: return "session closed"; + case ACAMERA_ERROR_INVALID_OPERATION: return "invalid operation"; + case ACAMERA_ERROR_STREAM_CONFIGURE_FAIL: return "configure failure"; + case ACAMERA_ERROR_CAMERA_IN_USE: return "camera in use"; + case ACAMERA_ERROR_MAX_CAMERA_IN_USE: return "max cameras in use"; + case ACAMERA_ERROR_CAMERA_DISABLED: return "camera disabled"; + case ACAMERA_ERROR_PERMISSION_DENIED: return "permission denied"; + case ACAMERA_ERROR_UNSUPPORTED_OPERATION: return "unsupported operation"; + default: break; + } + + return NULL; // unknown error } -static void DestroyCameraManager(void) +static int SetCameraError(const char *what, const camera_status_t rc) { - if (cameraIdList) { - ACameraManager_deleteCameraIdList(cameraIdList); - cameraIdList = NULL; - } + return SetErrorStr(what, CameraStatusStr(rc), (int) rc); +} - if (cameraMgr) { - ACameraManager_delete(cameraMgr); - cameraMgr = NULL; - } +static const char *MediaStatusStr(const media_status_t rc) +{ + switch (rc) { + case AMEDIA_OK: return "no error"; + case AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE: return "insuffient resources"; + case AMEDIACODEC_ERROR_RECLAIMED: return "reclaimed"; + case AMEDIA_ERROR_UNKNOWN: return "unknown error"; + case AMEDIA_ERROR_MALFORMED: return "malformed"; + case AMEDIA_ERROR_UNSUPPORTED: return "unsupported"; + case AMEDIA_ERROR_INVALID_OBJECT: return "invalid object"; + case AMEDIA_ERROR_INVALID_PARAMETER: return "invalid parameter"; + case AMEDIA_ERROR_INVALID_OPERATION: return "invalid operation"; + case AMEDIA_ERROR_END_OF_STREAM: return "end of stream"; + case AMEDIA_ERROR_IO: return "i/o error"; + case AMEDIA_ERROR_WOULD_BLOCK: return "operation would block"; + case AMEDIA_DRM_NOT_PROVISIONED: return "DRM not provisioned"; + case AMEDIA_DRM_RESOURCE_BUSY: return "DRM resource busy"; + case AMEDIA_DRM_DEVICE_REVOKED: return "DRM device revoked"; + case AMEDIA_DRM_SHORT_BUFFER: return "DRM short buffer"; + case AMEDIA_DRM_SESSION_NOT_OPENED: return "DRM session not opened"; + case AMEDIA_DRM_TAMPER_DETECTED: return "DRM tampering detected"; + case AMEDIA_DRM_VERIFY_FAILED: return "DRM verify failed"; + case AMEDIA_DRM_NEED_KEY: return "DRM need key"; + case AMEDIA_DRM_LICENSE_EXPIRED: return "DRM license expired"; + case AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE: return "no buffer available"; + case AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED: return "maximum images acquired"; + case AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE: return "cannot lock image"; + case AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE: return "cannot unlock image"; + case AMEDIA_IMGREADER_IMAGE_NOT_LOCKED: return "image not locked"; + default: break; + } + + return NULL; // unknown error } -struct SDL_PrivateCameraData +static int SetMediaError(const char *what, const media_status_t rc) { - ACameraDevice *device; - ACameraCaptureSession *session; - ACameraDevice_StateCallbacks dev_callbacks; - ACameraCaptureSession_stateCallbacks capture_callbacks; - ACaptureSessionOutputContainer *sessionOutputContainer; - AImageReader *reader; - int num_formats; - int count_formats[6]; // see format_to_id -}; + return SetErrorStr(what, MediaStatusStr(rc), (int) rc); +} -#define FORMAT_SDL SDL_PIXELFORMAT_NV12 +static ACameraManager *cameraMgr = NULL; -static int format_to_id(int fmt) { - switch (fmt) { - #define CASE(x, y) case x: return y - CASE(FORMAT_SDL, 0); - CASE(SDL_PIXELFORMAT_RGB565, 1); - CASE(SDL_PIXELFORMAT_XRGB8888, 2); - CASE(SDL_PIXELFORMAT_RGBA8888, 3); - CASE(SDL_PIXELFORMAT_RGBX8888, 4); - CASE(SDL_PIXELFORMAT_UNKNOWN, 5); - #undef CASE - default: - return 5; +static int CreateCameraManager(void) +{ + SDL_assert(cameraMgr == NULL); + + cameraMgr = pACameraManager_create(); + if (!cameraMgr) { + return SDL_SetError("Error creating ACameraManager"); } + return 0; } -static int id_to_format(int fmt) { - switch (fmt) { - #define CASE(x, y) case y: return x - CASE(FORMAT_SDL, 0); - CASE(SDL_PIXELFORMAT_RGB565, 1); - CASE(SDL_PIXELFORMAT_XRGB8888, 2); - CASE(SDL_PIXELFORMAT_RGBA8888, 3); - CASE(SDL_PIXELFORMAT_RGBX8888, 4); - CASE(SDL_PIXELFORMAT_UNKNOWN, 5); - #undef CASE - default: - return SDL_PIXELFORMAT_UNKNOWN; +static void DestroyCameraManager(void) +{ + if (cameraMgr) { + pACameraManager_delete(cameraMgr); + cameraMgr = NULL; } } @@ -146,27 +256,30 @@ static Uint32 format_android_to_sdl(Uint32 fmt) { switch (fmt) { #define CASE(x, y) case x: return y - CASE(AIMAGE_FORMAT_YUV_420_888, FORMAT_SDL); + CASE(AIMAGE_FORMAT_YUV_420_888, SDL_PIXELFORMAT_NV12); CASE(AIMAGE_FORMAT_RGB_565, SDL_PIXELFORMAT_RGB565); CASE(AIMAGE_FORMAT_RGB_888, SDL_PIXELFORMAT_XRGB8888); CASE(AIMAGE_FORMAT_RGBA_8888, SDL_PIXELFORMAT_RGBA8888); CASE(AIMAGE_FORMAT_RGBX_8888, SDL_PIXELFORMAT_RGBX8888); - - CASE(AIMAGE_FORMAT_RGBA_FP16, SDL_PIXELFORMAT_UNKNOWN); // 64bits - CASE(AIMAGE_FORMAT_RAW_PRIVATE, SDL_PIXELFORMAT_UNKNOWN); - CASE(AIMAGE_FORMAT_JPEG, SDL_PIXELFORMAT_UNKNOWN); + //CASE(AIMAGE_FORMAT_RGBA_FP16, SDL_PIXELFORMAT_UNKNOWN); // 64bits + //CASE(AIMAGE_FORMAT_RAW_PRIVATE, SDL_PIXELFORMAT_UNKNOWN); + //CASE(AIMAGE_FORMAT_JPEG, SDL_PIXELFORMAT_UNKNOWN); #undef CASE - default: - SDL_Log("Unknown format AIMAGE_FORMAT '%d'", fmt); - return SDL_PIXELFORMAT_UNKNOWN; + default: break; } + + #if DEBUG_CAMERA + //SDL_Log("Unknown format AIMAGE_FORMAT '%d'", fmt); + #endif + + return SDL_PIXELFORMAT_UNKNOWN; } static Uint32 format_sdl_to_android(Uint32 fmt) { switch (fmt) { #define CASE(x, y) case y: return x - CASE(AIMAGE_FORMAT_YUV_420_888, FORMAT_SDL); + CASE(AIMAGE_FORMAT_YUV_420_888, SDL_PIXELFORMAT_NV12); CASE(AIMAGE_FORMAT_RGB_565, SDL_PIXELFORMAT_RGB565); CASE(AIMAGE_FORMAT_RGB_888, SDL_PIXELFORMAT_XRGB8888); CASE(AIMAGE_FORMAT_RGBA_8888, SDL_PIXELFORMAT_RGBA8888); @@ -177,12 +290,95 @@ static Uint32 format_sdl_to_android(Uint32 fmt) } } +static int ANDROIDCAMERA_WaitDevice(SDL_CameraDevice *device) +{ + return 0; // this isn't used atm, since we run our own thread via onImageAvailable callbacks. +} + +static int ANDROIDCAMERA_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame, Uint64 *timestampNS) +{ + int retval = 1; + media_status_t res; + AImage *image = NULL; + + res = pAImageReader_acquireNextImage(device->hidden->reader, &image); + // We could also use this one: + //res = AImageReader_acquireLatestImage(device->hidden->reader, &image); + + SDL_assert(res != AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE); // we should only be here if onImageAvailable was called. + + if (res != AMEDIA_OK) { + return SetMediaError("Error AImageReader_acquireNextImage", res); + } + + int64_t atimestamp = 0; + if (pAImage_getTimestamp(image, &atimestamp) == AMEDIA_OK) { + *timestampNS = (Uint64) atimestamp; + } else { + *timestampNS = 0; + } + + // !!! FIXME: this currently copies the data to the surface (see FIXME about non-contiguous planar surfaces, but in theory we could just keep this locked until ReleaseFrame... + int32_t num_planes = 0; + pAImage_getNumberOfPlanes(image, &num_planes); + + if ((num_planes == 3) && (device->spec.format == SDL_PIXELFORMAT_NV12)) { + num_planes--; // treat the interleaved planes as one. + } + + // !!! FIXME: we have an open issue in SDL3 to allow SDL_Surface to support non-contiguous planar data, but we don't have it yet. + size_t buflen = 0; + for (int i = 0; (i < num_planes) && (i < 3); i++) { + uint8_t *data = NULL; + int32_t datalen = 0; + pAImage_getPlaneData(image, i, &data, &datalen); + buflen += (int) datalen; + } + + frame->pixels = SDL_aligned_alloc(SDL_SIMDGetAlignment(), buflen); + if (frame->pixels == NULL) { + retval = -1; + } else { + int32_t row_stride = 0; + Uint8 *dst = frame->pixels; + pAImage_getPlaneRowStride(image, 0, &row_stride); + frame->pitch = (int) row_stride; // this is what SDL3 currently expects, probably incorrectly. + + for (int i = 0; (i < num_planes) && (i < 3); i++) { + uint8_t *data = NULL; + int32_t datalen = 0; + pAImage_getPlaneData(image, i, &data, &datalen); + const void *src = data; + SDL_memcpy(dst, src, datalen); + dst += datalen; + } + } + + pAImage_delete(image); + + return retval; +} + +static void ANDROIDCAMERA_ReleaseFrame(SDL_CameraDevice *device, SDL_Surface *frame) +{ + // !!! FIXME: this currently copies the data to the surface, but in theory we could just keep the AImage until ReleaseFrame... + SDL_aligned_free(frame->pixels); +} + +static void onImageAvailable(void *context, AImageReader *reader) +{ + #if DEBUG_CAMERA + SDL_Log("CAMERA: CB onImageAvailable"); + #endif + SDL_CameraDevice *device = (SDL_CameraDevice *) context; + SDL_CameraThreadIterate(device); +} static void onDisconnected(void *context, ACameraDevice *device) { // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; #if DEBUG_CAMERA - SDL_Log("CB onDisconnected"); + SDL_Log("CAMERA: CB onDisconnected"); #endif } @@ -190,16 +386,15 @@ static void onError(void *context, ACameraDevice *device, int error) { // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; #if DEBUG_CAMERA - SDL_Log("CB onError"); + SDL_Log("CAMERA: CB onError"); #endif } - static void onClosed(void* context, ACameraCaptureSession *session) { // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; #if DEBUG_CAMERA - SDL_Log("CB onClosed"); + SDL_Log("CAMERA: CB onClosed"); #endif } @@ -207,7 +402,7 @@ static void onReady(void* context, ACameraCaptureSession *session) { // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; #if DEBUG_CAMERA - SDL_Log("CB onReady"); + SDL_Log("CAMERA: CB onReady"); #endif } @@ -215,493 +410,478 @@ static void onActive(void* context, ACameraCaptureSession *session) { // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; #if DEBUG_CAMERA - SDL_Log("CB onActive"); + SDL_Log("CAMERA: CB onActive"); #endif } -static int ANDROIDCAMERA_OpenDevice(SDL_CameraDevice *_this) +static void ANDROIDCAMERA_CloseDevice(SDL_CameraDevice *device) { - /* Cannot open a second camera, while the first one is opened. - * If you want to play several camera, they must all be opened first, then played. - * - * https://developer.android.com/reference/android/hardware/camera2/CameraManager - * "All camera devices intended to be operated concurrently, must be opened using openCamera(String, CameraDevice.StateCallback, Handler), - * before configuring sessions on any of the camera devices. * " - * - */ - if (CheckDevicePlaying()) { - return SDL_SetError("A camera is already playing"); - } + if (device && device->hidden) { + struct SDL_PrivateCameraData *hidden = device->hidden; + device->hidden = NULL; - _this->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); - if (_this->hidden == NULL) { - return -1; - } - - CreateCameraManager(); - - _this->hidden->dev_callbacks.context = (void *) _this; - _this->hidden->dev_callbacks.onDisconnected = onDisconnected; - _this->hidden->dev_callbacks.onError = onError; - - camera_status_t res = ACameraManager_openCamera(cameraMgr, _this->dev_name, &_this->hidden->dev_callbacks, &_this->hidden->device); - if (res != ACAMERA_OK) { - return SDL_SetError("Failed to open camera"); - } + if (hidden->reader) { + pAImageReader_setImageListener(hidden->reader, NULL); + } - return 0; -} + if (hidden->session) { + pACameraCaptureSession_close(hidden->session); + } -static void ANDROIDCAMERA_CloseDevice(SDL_CameraDevice *_this) -{ - if (_this && _this->hidden) { - if (_this->hidden->session) { - ACameraCaptureSession_close(_this->hidden->session); + if (hidden->request) { + pACaptureRequest_free(hidden->request); } - if (_this->hidden->sessionOutputContainer) { - ACaptureSessionOutputContainer_free(_this->hidden->sessionOutputContainer); + if (hidden->outputTarget) { + pACameraOutputTarget_free(hidden->outputTarget); } - if (_this->hidden->reader) { - AImageReader_delete(_this->hidden->reader); + if (hidden->sessionOutputContainer) { + pACaptureSessionOutputContainer_free(hidden->sessionOutputContainer); } - if (_this->hidden->device) { - ACameraDevice_close(_this->hidden->device); + if (hidden->sessionOutput) { + pACaptureSessionOutput_free(hidden->sessionOutput); } - SDL_free(_this->hidden); + // we don't free hidden->window here, it'll be cleaned up by AImageReader_delete. - _this->hidden = NULL; - } -} + if (hidden->reader) { + pAImageReader_delete(hidden->reader); + } -static int ANDROIDCAMERA_InitDevice(SDL_CameraDevice *_this) -{ - size_t size, pitch; - SDL_CalculateSize(_this->spec.format, _this->spec.width, _this->spec.height, &size, &pitch, SDL_FALSE); - SDL_Log("Buffer size: %d x %d", _this->spec.width, _this->spec.height); - return 0; -} + if (hidden->device) { + pACameraDevice_close(hidden->device); + } -static int ANDROIDCAMERA_GetDeviceSpec(SDL_CameraDevice *_this, SDL_CameraSpec *spec) -{ - // !!! FIXME: catch NULLs at higher level - if (spec) { - SDL_copyp(spec, &_this->spec); - return 0; + SDL_free(hidden); } - return -1; } -static int ANDROIDCAMERA_StartCamera(SDL_CameraDevice *_this) +// this is where the "opening" of the camera happens, after permission is granted. +static int PrepareCamera(SDL_CameraDevice *device) { - // !!! FIXME: maybe log the error code in SDL_SetError + SDL_assert(device->hidden != NULL); + camera_status_t res; media_status_t res2; - ANativeWindow *window = NULL; - ACaptureSessionOutput *sessionOutput; - ACameraOutputTarget *outputTarget; - ACaptureRequest *request; - - res2 = AImageReader_new(_this->spec.width, _this->spec.height, format_sdl_to_android(_this->spec.format), 10 /* nb buffers */, &_this->hidden->reader); - if (res2 != AMEDIA_OK) { - SDL_SetError("Error AImageReader_new"); - goto error; - } - res2 = AImageReader_getWindow(_this->hidden->reader, &window); - if (res2 != AMEDIA_OK) { - SDL_SetError("Error AImageReader_new"); - goto error; - } - - res = ACaptureSessionOutput_create(window, &sessionOutput); - if (res != ACAMERA_OK) { - SDL_SetError("Error ACaptureSessionOutput_create"); - goto error; - } - res = ACaptureSessionOutputContainer_create(&_this->hidden->sessionOutputContainer); - if (res != ACAMERA_OK) { - SDL_SetError("Error ACaptureSessionOutputContainer_create"); - goto error; - } - res = ACaptureSessionOutputContainer_add(_this->hidden->sessionOutputContainer, sessionOutput); - if (res != ACAMERA_OK) { - SDL_SetError("Error ACaptureSessionOutputContainer_add"); - goto error; - } - - res = ACameraOutputTarget_create(window, &outputTarget); - if (res != ACAMERA_OK) { - SDL_SetError("Error ACameraOutputTarget_create"); - goto error; - } - res = ACameraDevice_createCaptureRequest(_this->hidden->device, TEMPLATE_RECORD, &request); - if (res != ACAMERA_OK) { - SDL_SetError("Error ACameraDevice_createCaptureRequest"); - goto error; - } - - res = ACaptureRequest_addTarget(request, outputTarget); - if (res != ACAMERA_OK) { - SDL_SetError("Error ACaptureRequest_addTarget"); - goto error; - } - - _this->hidden->capture_callbacks.context = (void *) _this; - _this->hidden->capture_callbacks.onClosed = onClosed; - _this->hidden->capture_callbacks.onReady = onReady; - _this->hidden->capture_callbacks.onActive = onActive; - - res = ACameraDevice_createCaptureSession(_this->hidden->device, - _this->hidden->sessionOutputContainer, - &_this->hidden->capture_callbacks, - &_this->hidden->session); - if (res != ACAMERA_OK) { - SDL_SetError("Error ACameraDevice_createCaptureSession"); - goto error; - } + ACameraDevice_StateCallbacks dev_callbacks; + SDL_zero(dev_callbacks); + dev_callbacks.context = device; + dev_callbacks.onDisconnected = onDisconnected; + dev_callbacks.onError = onError; - res = ACameraCaptureSession_setRepeatingRequest(_this->hidden->session, NULL, 1, &request, NULL); - if (res != ACAMERA_OK) { - SDL_SetError("Error ACameraDevice_createCaptureSession"); - goto error; + ACameraCaptureSession_stateCallbacks capture_callbacks; + SDL_zero(capture_callbacks); + capture_callbacks.context = device; + capture_callbacks.onClosed = onClosed; + capture_callbacks.onReady = onReady; + capture_callbacks.onActive = onActive; + + AImageReader_ImageListener imglistener; + SDL_zero(imglistener); + imglistener.context = device; + imglistener.onImageAvailable = onImageAvailable; + + // just in case SDL_OpenCameraDevice is overwriting device->spec as CameraPermissionCallback runs, we work from a different copy. + const SDL_CameraSpec *spec = &device->hidden->requested_spec; + + if ((res = pACameraManager_openCamera(cameraMgr, (const char *) device->handle, &dev_callbacks, &device->hidden->device)) != ACAMERA_OK) { + return SetCameraError("Failed to open camera", res); + } else if ((res2 = pAImageReader_new(spec->width, spec->height, format_sdl_to_android(spec->format), 10 /* nb buffers */, &device->hidden->reader)) != AMEDIA_OK) { + return SetMediaError("Error AImageReader_new", res2); + } else if ((res2 = pAImageReader_getWindow(device->hidden->reader, &device->hidden->window)) != AMEDIA_OK) { + return SetMediaError("Error AImageReader_getWindow", res2); + } else if ((res = pACaptureSessionOutput_create(device->hidden->window, &device->hidden->sessionOutput)) != ACAMERA_OK) { + return SetCameraError("Error ACaptureSessionOutput_create", res); + } else if ((res = pACaptureSessionOutputContainer_create(&device->hidden->sessionOutputContainer)) != ACAMERA_OK) { + return SetCameraError("Error ACaptureSessionOutputContainer_create", res); + } else if ((res = pACaptureSessionOutputContainer_add(device->hidden->sessionOutputContainer, device->hidden->sessionOutput)) != ACAMERA_OK) { + return SetCameraError("Error ACaptureSessionOutputContainer_add", res); + } else if ((res = pACameraOutputTarget_create(device->hidden->window, &device->hidden->outputTarget)) != ACAMERA_OK) { + return SetCameraError("Error ACameraOutputTarget_create", res); + } else if ((res = pACameraDevice_createCaptureRequest(device->hidden->device, TEMPLATE_RECORD, &device->hidden->request)) != ACAMERA_OK) { + return SetCameraError("Error ACameraDevice_createCaptureRequest", res); + } else if ((res = pACaptureRequest_addTarget(device->hidden->request, device->hidden->outputTarget)) != ACAMERA_OK) { + return SetCameraError("Error ACaptureRequest_addTarget", res); + } else if ((res = pACameraDevice_createCaptureSession(device->hidden->device, device->hidden->sessionOutputContainer, &capture_callbacks, &device->hidden->session)) != ACAMERA_OK) { + return SetCameraError("Error ACameraDevice_createCaptureSession", res); + } else if ((res = pACameraCaptureSession_setRepeatingRequest(device->hidden->session, NULL, 1, &device->hidden->request, NULL)) != ACAMERA_OK) { + return SetCameraError("Error ACameraCaptureSession_setRepeatingRequest", res); + } else if ((res2 = pAImageReader_setImageListener(device->hidden->reader, &imglistener)) != AMEDIA_OK) { + return SetMediaError("Error AImageReader_setImageListener", res2); } return 0; - -error: - return -1; -} - -static int ANDROIDCAMERA_StopCamera(SDL_CameraDevice *_this) -{ - ACameraCaptureSession_close(_this->hidden->session); - _this->hidden->session = NULL; - return 0; } -static int ANDROIDCAMERA_AcquireFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) +static void SDLCALL CameraPermissionCallback(void *userdata, const char *permission, SDL_bool granted) { - media_status_t res; - AImage *image; - res = AImageReader_acquireNextImage(_this->hidden->reader, &image); - /* We could also use this one: - res = AImageReader_acquireLatestImage(_this->hidden->reader, &image); - */ - if (res == AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE ) { - SDL_Delay(20); // TODO fix some delay - #if DEBUG_CAMERA - //SDL_Log("AImageReader_acquireNextImage: AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE"); - #endif - } else if (res == AMEDIA_OK ) { - int32_t numPlanes = 0; - AImage_getNumberOfPlanes(image, &numPlanes); - - frame->timestampNS = SDL_GetTicksNS(); - - for (int i = 0; i < numPlanes && i < 3; i++) { - int dataLength = 0; - int rowStride = 0; - uint8_t *data = NULL; - frame->num_planes += 1; - AImage_getPlaneRowStride(image, i, &rowStride); - res = AImage_getPlaneData(image, i, &data, &dataLength); - if (res == AMEDIA_OK) { - frame->data[i] = data; - frame->pitch[i] = rowStride; - } - } - - if (frame->num_planes == 3) { - /* plane 2 and 3 are interleaved NV12. SDL only takes two planes for this format */ - int pixelStride = 0; - AImage_getPlanePixelStride(image, 1, &pixelStride); - if (pixelStride == 2) { - frame->num_planes -= 1; - } + SDL_CameraDevice *device = (SDL_CameraDevice *) userdata; + if (device->hidden != NULL) { // if device was already closed, don't send an event. + if (!granted) { + SDL_CameraDevicePermissionOutcome(device, SDL_FALSE); // sorry, permission denied. + } else if (PrepareCamera(device) < 0) { // permission given? Actually open the camera now. + // uhoh, setup failed; since the app thinks we already "opened" the device, mark it as disconnected and don't report the permission. + SDL_CameraDeviceDisconnected(device); + } else { + // okay! We have permission to use the camera _and_ opening the hardware worked out, report that the camera is usable! + SDL_CameraDevicePermissionOutcome(device, SDL_TRUE); // go go go! } - - frame->internal = (void*)image; - } else if (res == AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED) { - return SDL_SetError("AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED"); - } else { - return SDL_SetError("AImageReader_acquireNextImage: %d", res); } - return 0; + UnrefPhysicalCameraDevice(device); // we ref'd this in OpenDevice, release the extra reference. } -static int ANDROIDCAMERA_ReleaseFrame(SDL_CameraDevice *_this, SDL_CameraFrame *frame) -{ - if (frame->internal){ - AImage_delete((AImage *)frame->internal); - } - return 0; -} -static int ANDROIDCAMERA_GetNumFormats(SDL_CameraDevice *_this) +static int ANDROIDCAMERA_OpenDevice(SDL_CameraDevice *device, const SDL_CameraSpec *spec) { - camera_status_t res; - SDL_bool unknown = SDL_FALSE; - ACameraMetadata *metadata; - ACameraMetadata_const_entry entry; - - if (_this->hidden->num_formats != 0) { - return _this->hidden->num_formats; - } - - res = ACameraManager_getCameraCharacteristics(cameraMgr, _this->dev_name, &metadata); - if (res != ACAMERA_OK) { - return -1; +#if 0 // !!! FIXME: for now, we'll just let this fail if it is going to fail, without checking for this + /* Cannot open a second camera, while the first one is opened. + * If you want to play several camera, they must all be opened first, then played. + * + * https://developer.android.com/reference/android/hardware/camera2/CameraManager + * "All camera devices intended to be operated concurrently, must be opened using openCamera(String, CameraDevice.StateCallback, Handler), + * before configuring sessions on any of the camera devices. * " + * + */ + if (CheckDevicePlaying()) { + return SDL_SetError("A camera is already playing"); } +#endif - res = ACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry); - if (res != ACAMERA_OK) { + device->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData)); + if (device->hidden == NULL) { return -1; } - SDL_Log("got entry ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS"); - - for (int i = 0; i < entry.count; i += 4) { - const int32_t format = entry.data.i32[i + 0]; - const int32_t type = entry.data.i32[i + 3]; - - if (type == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) { - continue; - } - - const Uint32 fmt = format_android_to_sdl(format); - _this->hidden->count_formats[format_to_id(fmt)] += 1; + RefPhysicalCameraDevice(device); // ref'd until permission callback fires. - #if DEBUG_CAMERA - if (fmt != SDL_PIXELFORMAT_UNKNOWN) { - int w = entry.data.i32[i + 1]; - int h = entry.data.i32[i + 2]; - SDL_Log("Got format android 0x%08x -> %s %d x %d", format, SDL_GetPixelFormatName(fmt), w, h); - } else { - unknown = SDL_TRUE; - } - #endif - } - - #if DEBUG_CAMERA - if (unknown) { - SDL_Log("Got unknown android"); - } - #endif - - - if ( _this->hidden->count_formats[0]) _this->hidden->num_formats += 1; - if ( _this->hidden->count_formats[1]) _this->hidden->num_formats += 1; - if ( _this->hidden->count_formats[2]) _this->hidden->num_formats += 1; - if ( _this->hidden->count_formats[3]) _this->hidden->num_formats += 1; - if ( _this->hidden->count_formats[4]) _this->hidden->num_formats += 1; - if ( _this->hidden->count_formats[5]) _this->hidden->num_formats += 1; - - return _this->hidden->num_formats; -} - -static int ANDROIDCAMERA_GetFormat(SDL_CameraDevice *_this, int index, Uint32 *format) -{ - int i2 = 0; - - if (_this->hidden->num_formats == 0) { - GetNumFormats(_this); - } - - if (index < 0 || index >= _this->hidden->num_formats) { - // !!! FIXME: call SDL_SetError()? + // just in case SDL_OpenCameraDevice is overwriting device->spec as CameraPermissionCallback runs, we work from a different copy. + SDL_copyp(&device->hidden->requested_spec, spec); + if (SDL_AndroidRequestPermission("android.permission.CAMERA", CameraPermissionCallback, device) < 0) { + UnrefPhysicalCameraDevice(device); return -1; } - for (int i = 0; i < SDL_arraysize(_this->hidden->count_formats); i++) { - if (_this->hidden->count_formats[i] == 0) { - continue; - } - - if (i2 == index) { - *format = id_to_format(i); - } - - i2++; - - } - return 0; + return 0; // we don't open the camera until permission is granted, so always succeed for now. } -static int ANDROIDCAMERA_GetNumFrameSizes(SDL_CameraDevice *_this, Uint32 format) +static void ANDROIDCAMERA_FreeDeviceHandle(SDL_CameraDevice *device) { - // !!! FIXME: call SDL_SetError()? - if (_this->hidden->num_formats == 0) { - GetNumFormats(_this); - } - - const int index = format_to_id(format); - - int i2 = 0; - for (int i = 0; i < SDL_arraysize(_this->hidden->count_formats); i++) { - if (_this->hidden->count_formats[i] == 0) { - continue; - } - - if (i2 == index) { - /* number of resolution for this format */ - return _this->hidden->count_formats[i]; - } - - i2++; + if (device) { + SDL_free(device->handle); } - - return -1; } -static int ANDROIDCAMERA_GetFrameSize(SDL_CameraDevice *_this, Uint32 format, int index, int *width, int *height) +static void GatherCameraSpecs(const char *devid, CameraFormatAddData *add_data, char **fullname, const char **posstr) { - // !!! FIXME: call SDL_SetError()? - camera_status_t res; - ACameraMetadata *metadata; - ACameraMetadata_const_entry entry; - - if (_this->hidden->num_formats == 0) { - GetNumFormats(_this); + SDL_zerop(add_data); + + ACameraMetadata *metadata = NULL; + ACameraMetadata_const_entry cfgentry; + ACameraMetadata_const_entry durentry; + ACameraMetadata_const_entry infoentry; + + // This can fail with an "unknown error" (with `adb logcat` reporting "no such file or directory") + // for "LEGACY" level cameras. I saw this happen on a 30-dollar budget phone I have for testing + // (but a different brand budget phone worked, so it's not strictly the low-end of Android devices). + // LEGACY devices are seen by onCameraAvailable, but are not otherwise accessible through + // libcamera2ndk. The Java camera2 API apparently _can_ access these cameras, but we're going on + // without them here for now, in hopes that such hardware is a dying breed. + if (pACameraManager_getCameraCharacteristics(cameraMgr, devid, &metadata) != ACAMERA_OK) { + return; // oh well. + } else if (pACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &cfgentry) != ACAMERA_OK) { + pACameraMetadata_free(metadata); + return; // oh well. + } else if (pACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_MIN_FRAME_DURATIONS, &durentry) != ACAMERA_OK) { + pACameraMetadata_free(metadata); + return; // oh well. + } + + *fullname = NULL; + if (pACameraMetadata_getConstEntry(metadata, ACAMERA_INFO_VERSION, &infoentry) == ACAMERA_OK) { + *fullname = (char *) SDL_malloc(infoentry.count + 1); + if (*fullname) { + SDL_strlcpy(*fullname, (const char *) infoentry.data.u8, infoentry.count + 1); + } } - res = ACameraManager_getCameraCharacteristics(cameraMgr, _this->dev_name, &metadata); - if (res != ACAMERA_OK) { - return -1; + *posstr = NULL; + ACameraMetadata_const_entry posentry; + if (pACameraMetadata_getConstEntry(metadata, ACAMERA_LENS_FACING, &posentry) == ACAMERA_OK) { // ignore this if it fails. + if (*posentry.data.u8 == ACAMERA_LENS_FACING_FRONT) { + *posstr = "front"; + if (!*fullname) { + *fullname = SDL_strdup("Front-facing camera"); + } + } else if (*posentry.data.u8 == ACAMERA_LENS_FACING_BACK) { + *posstr = "back"; + if (!*fullname) { + *fullname = SDL_strdup("Back-facing camera"); + } + } } - res = ACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry); - if (res != ACAMERA_OK) { - return -1; + if (!*fullname) { + *fullname = SDL_strdup("Generic camera"); // we tried. } - int i2 = 0; - for (int i = 0; i < entry.count; i += 4) { - int32_t f = entry.data.i32[i + 0]; - const int w = entry.data.i32[i + 1]; - const int h = entry.data.i32[i + 2]; - int32_t type = entry.data.i32[i + 3]; + const int32_t *i32ptr = cfgentry.data.i32; + for (int i = 0; i < cfgentry.count; i++, i32ptr += 4) { + const int32_t fmt = i32ptr[0]; + const int w = (int) i32ptr[1]; + const int h = (int) i32ptr[2]; + const int32_t type = i32ptr[3]; + Uint32 sdlfmt; if (type == ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT) { continue; - } - - Uint32 fmt = format_android_to_sdl(f); - if (fmt != format) { + } else if ((w <= 0) || (h <= 0)) { + continue; + } else if ((sdlfmt = format_android_to_sdl(fmt)) == SDL_PIXELFORMAT_UNKNOWN) { continue; } - if (i2 == index) { - *width = w; - *height = h; - return 0; +#if 0 // !!! FIXME: these all come out with 0 durations on my test phone. :( + const int64_t *i64ptr = durentry.data.i64; + for (int j = 0; j < durentry.count; j++, i64ptr += 4) { + const int32_t fpsfmt = (int32_t) i64ptr[0]; + const int fpsw = (int) i64ptr[1]; + const int fpsh = (int) i64ptr[2]; + const long long duration = (long long) i64ptr[3]; + SDL_Log("CAMERA: possible fps %s %dx%d duration=%lld", SDL_GetPixelFormatName(format_android_to_sdl(fpsfmt)), fpsw, fpsh, duration); + if ((duration > 0) && (fpsfmt == fmt) && (fpsw == w) && (fpsh == h)) { + SDL_AddCameraFormat(add_data, sdlfmt, w, h, duration, 1000000000); + } } - - i2++; +#else + SDL_AddCameraFormat(add_data, sdlfmt, w, h, 1, 30); +#endif } - return -1; + pACameraMetadata_free(metadata); } -static int ANDROIDCAMERA_GetNumDevices(void) +static SDL_bool FindAndroidCameraDeviceByID(SDL_CameraDevice *device, void *userdata) { - camera_status_t res; - CreateCameraManager(); - - if (cameraIdList) { - ACameraManager_deleteCameraIdList(cameraIdList); - cameraIdList = NULL; - } + const char *devid = (const char *) userdata; + return (SDL_strcmp(devid, (const char *) device->handle) == 0); +} - res = ACameraManager_getCameraIdList(cameraMgr, &cameraIdList); +static void MaybeAddDevice(const char *devid) +{ + #if DEBUG_CAMERA + SDL_Log("CAMERA: MaybeAddDevice('%s')", devid); + #endif - if (res == ACAMERA_OK) { - if (cameraIdList) { - return cameraIdList->numCameras; + if (SDL_FindPhysicalCameraDeviceByCallback(FindAndroidCameraDeviceByID, (void *) devid)) { + return; // already have this one. + } + + const char *posstr = NULL; + char *fullname = NULL; + CameraFormatAddData add_data; + GatherCameraSpecs(devid, &add_data, &fullname, &posstr); + if (add_data.num_specs > 0) { + char *namecpy = SDL_strdup(devid); + if (namecpy) { + SDL_CameraDevice *device = SDL_AddCameraDevice(fullname, add_data.num_specs, add_data.specs, namecpy); + if (!device) { + SDL_free(namecpy); + } else if (device && posstr) { + SDL_Camera *camera = (SDL_Camera *) device; // currently there's no separation between physical and logical device. + SDL_PropertiesID props = SDL_GetCameraProperties(camera); + if (props) { + SDL_SetStringProperty(props, SDL_PROP_CAMERA_POSITION_STRING, posstr); + } + } } } - return -1; -} -static int ANDROIDCAMERA_GetDeviceName(SDL_CameraDeviceID instance_id, char *buf, int size) -{ - // !!! FIXME: call SDL_SetError()? - int index = instance_id - 1; - CreateCameraManager(); + SDL_free(fullname); + SDL_free(add_data.specs); +} - if (cameraIdList == NULL) { - GetNumDevices(); - } +// note that camera "availability" covers both hotplugging and whether another +// has the device opened, but for something like Android, it's probably fine +// to treat both unplugging and loss of access as disconnection events. When +// the other app closes the camera, we get an available event as if it was +// just plugged back in. - if (cameraIdList) { - if (index >= 0 && index < cameraIdList->numCameras) { - SDL_snprintf(buf, size, "%s", cameraIdList->cameraIds[index]); - return 0; - } - } +static void onCameraAvailable(void *context, const char *cameraId) +{ + #if DEBUG_CAMERA + SDL_Log("CAMERA: CB onCameraAvailable('%s')", cameraId); + #endif + SDL_assert(cameraId != NULL); + MaybeAddDevice(cameraId); +} - return -1; +static void onCameraUnavailable(void *context, const char *cameraId) +{ + #if DEBUG_CAMERA + SDL_Log("CAMERA: CB onCameraUnvailable('%s')", cameraId); + #endif + SDL_assert(cameraId != NULL); + SDL_CameraDeviceDisconnected(SDL_FindPhysicalCameraDeviceByCallback(FindAndroidCameraDeviceByID, (void *) cameraId)); } -static SDL_CameraDeviceID *ANDROIDCAMERA_GetDevices(int *count) +static const ACameraManager_AvailabilityCallbacks camera_availability_listener = { + NULL, + onCameraAvailable, + onCameraUnavailable +}; + +static void ANDROIDCAMERA_DetectDevices(void) { - // hard-coded list of ID - const int num = GetNumDevices(); - SDL_CameraDeviceID *retval = (SDL_CameraDeviceID *)SDL_malloc((num + 1) * sizeof(*ret)); + ACameraIdList *list = NULL; + camera_status_t res = pACameraManager_getCameraIdList(cameraMgr, &list); - if (retval == NULL) { - *count = 0; - return NULL; - } + if ((res == ACAMERA_OK) && list) { + const int total = list->numCameras; + for (int i = 0; i < total; i++) { + MaybeAddDevice(list->cameraIds[i]); + } - for (int i = 0; i < num; i++) { - retval[i] = i + 1; + pACameraManager_deleteCameraIdList(list); } - retval[num] = 0; - *count = num; - return retval; + + pACameraManager_registerAvailabilityCallback(cameraMgr, &camera_availability_listener); } static void ANDROIDCAMERA_Deinitialize(void) { + pACameraManager_unregisterAvailabilityCallback(cameraMgr, &camera_availability_listener); DestroyCameraManager(); -} - -#endif // __ANDROID_API__ >= 24 + dlclose(libcamera2ndk); + libcamera2ndk = NULL; + pACameraManager_create = NULL; + pACameraManager_registerAvailabilityCallback = NULL; + pACameraManager_unregisterAvailabilityCallback = NULL; + pACameraManager_getCameraIdList = NULL; + pACameraManager_deleteCameraIdList = NULL; + pACameraCaptureSession_close = NULL; + pACaptureRequest_free = NULL; + pACameraOutputTarget_free = NULL; + pACameraDevice_close = NULL; + pACameraManager_delete = NULL; + pACaptureSessionOutputContainer_free = NULL; + pACaptureSessionOutput_free = NULL; + pACameraManager_openCamera = NULL; + pACameraDevice_createCaptureRequest = NULL; + pACameraDevice_createCaptureSession = NULL; + pACameraManager_getCameraCharacteristics = NULL; + pACameraMetadata_free = NULL; + pACameraMetadata_getConstEntry = NULL; + pACameraCaptureSession_setRepeatingRequest = NULL; + pACameraOutputTarget_create = NULL; + pACaptureRequest_addTarget = NULL; + pACaptureSessionOutputContainer_add = NULL; + pACaptureSessionOutputContainer_create = NULL; + pACaptureSessionOutput_create = NULL; + + dlclose(libmediandk); + libmediandk = NULL; + pAImage_delete = NULL; + pAImage_getTimestamp = NULL; + pAImage_getNumberOfPlanes = NULL; + pAImage_getPlaneRowStride = NULL; + pAImage_getPlaneData = NULL; + pAImageReader_acquireNextImage = NULL; + pAImageReader_delete = NULL; + pAImageReader_setImageListener = NULL; + pAImageReader_getWindow = NULL; + pAImageReader_new = NULL; +} static SDL_bool ANDROIDCAMERA_Init(SDL_CameraDriverImpl *impl) { -#if __ANDROID_API__ < 24 - return SDL_FALSE; -#else + // !!! FIXME: slide this off into a subroutine + // system libraries are in android-24 and later; we currently target android-16 and later, so check if they exist at runtime. + void *libcamera2 = dlopen("libcamera2ndk.so", RTLD_NOW | RTLD_LOCAL); + if (!libcamera2) { + SDL_Log("CAMERA: libcamera2ndk.so can't be loaded: %s", dlerror()); + return SDL_FALSE; + } + + void *libmedia = dlopen("libmediandk.so", RTLD_NOW | RTLD_LOCAL); + if (!libmedia) { + SDL_Log("CAMERA: libmediandk.so can't be loaded: %s", dlerror()); + dlclose(libcamera2); + return SDL_FALSE; + } + + SDL_bool okay = SDL_TRUE; + #define LOADSYM(lib, fn) if (okay) { p##fn = (pfn##fn) dlsym(lib, #fn); if (!p##fn) { SDL_Log("CAMERA: symbol '%s' can't be found in %s: %s", #fn, #lib "ndk.so", dlerror()); okay = SDL_FALSE; } } + //#define LOADSYM(lib, fn) p##fn = (pfn##fn) fn + LOADSYM(libcamera2, ACameraManager_create); + LOADSYM(libcamera2, ACameraManager_registerAvailabilityCallback); + LOADSYM(libcamera2, ACameraManager_unregisterAvailabilityCallback); + LOADSYM(libcamera2, ACameraManager_getCameraIdList); + LOADSYM(libcamera2, ACameraManager_deleteCameraIdList); + LOADSYM(libcamera2, ACameraCaptureSession_close); + LOADSYM(libcamera2, ACaptureRequest_free); + LOADSYM(libcamera2, ACameraOutputTarget_free); + LOADSYM(libcamera2, ACameraDevice_close); + LOADSYM(libcamera2, ACameraManager_delete); + LOADSYM(libcamera2, ACaptureSessionOutputContainer_free); + LOADSYM(libcamera2, ACaptureSessionOutput_free); + LOADSYM(libcamera2, ACameraManager_openCamera); + LOADSYM(libcamera2, ACameraDevice_createCaptureRequest); + LOADSYM(libcamera2, ACameraDevice_createCaptureSession); + LOADSYM(libcamera2, ACameraManager_getCameraCharacteristics); + LOADSYM(libcamera2, ACameraMetadata_free); + LOADSYM(libcamera2, ACameraMetadata_getConstEntry); + LOADSYM(libcamera2, ACameraCaptureSession_setRepeatingRequest); + LOADSYM(libcamera2, ACameraOutputTarget_create); + LOADSYM(libcamera2, ACaptureRequest_addTarget); + LOADSYM(libcamera2, ACaptureSessionOutputContainer_add); + LOADSYM(libcamera2, ACaptureSessionOutputContainer_create); + LOADSYM(libcamera2, ACaptureSessionOutput_create); + LOADSYM(libmedia, AImage_delete); + LOADSYM(libmedia, AImage_getTimestamp); + LOADSYM(libmedia, AImage_getNumberOfPlanes); + LOADSYM(libmedia, AImage_getPlaneRowStride); + LOADSYM(libmedia, AImage_getPlaneData); + LOADSYM(libmedia, AImageReader_acquireNextImage); + LOADSYM(libmedia, AImageReader_delete); + LOADSYM(libmedia, AImageReader_setImageListener); + LOADSYM(libmedia, AImageReader_getWindow); + LOADSYM(libmedia, AImageReader_new); + LOADSYM(libmedia, AImage_getWidth); + LOADSYM(libmedia, AImage_getHeight); + + #undef LOADSYM + + if (!okay) { + dlclose(libmedia); + dlclose(libcamera2); + } + if (CreateCameraManager() < 0) { + dlclose(libmedia); + dlclose(libcamera2); return SDL_FALSE; } + libcamera2ndk = libcamera2; + libmediandk = libmedia; + impl->DetectDevices = ANDROIDCAMERA_DetectDevices; impl->OpenDevice = ANDROIDCAMERA_OpenDevice; impl->CloseDevice = ANDROIDCAMERA_CloseDevice; - impl->InitDevice = ANDROIDCAMERA_InitDevice; - impl->GetDeviceSpec = ANDROIDCAMERA_GetDeviceSpec; - impl->StartCamera = ANDROIDCAMERA_StartCamera; - impl->StopCamera = ANDROIDCAMERA_StopCamera; + impl->WaitDevice = ANDROIDCAMERA_WaitDevice; impl->AcquireFrame = ANDROIDCAMERA_AcquireFrame; impl->ReleaseFrame = ANDROIDCAMERA_ReleaseFrame; - impl->GetNumFormats = ANDROIDCAMERA_GetNumFormats; - impl->GetFormat = ANDROIDCAMERA_GetFormat; - impl->GetNumFrameSizes = ANDROIDCAMERA_GetNumFrameSizes; - impl->GetFrameSize = ANDROIDCAMERA_GetFrameSize; - impl->GetDeviceName = ANDROIDCAMERA_GetDeviceName; - impl->GetDevices = ANDROIDCAMERA_GetDevices; + impl->FreeDeviceHandle = ANDROIDCAMERA_FreeDeviceHandle; impl->Deinitialize = ANDROIDCAMERA_Deinitialize; + impl->ProvidesOwnCallbackThread = SDL_TRUE; + return SDL_TRUE; -#endif } CameraBootStrap ANDROIDCAMERA_bootstrap = { @@ -709,4 +889,3 @@ CameraBootStrap ANDROIDCAMERA_bootstrap = { }; #endif - From bdcddf481076de6f98eba291fb9c72f08cfb95be Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 19 Feb 2024 12:18:00 -0500 Subject: [PATCH 057/220] camera: Disconnected cameras become zombies that feed blank frames. --- src/camera/SDL_camera.c | 162 ++++++++++++++++++++++++++++++++----- src/camera/SDL_syscamera.h | 8 ++ 2 files changed, 152 insertions(+), 18 deletions(-) diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index eb957d73d358e..f70cd75f552a9 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -105,6 +105,121 @@ int SDL_AddCameraFormat(CameraFormatAddData *data, Uint32 fmt, int w, int h, int } +// Zombie device implementation... + +// These get used when a device is disconnected or fails. Apps that ignore the +// loss notifications will get black frames but otherwise keep functioning. +static int ZombieWaitDevice(SDL_CameraDevice *device) +{ + if (!SDL_AtomicGet(&device->shutdown)) { + // !!! FIXME: this is bad for several reasons (uses double, could be precalculated, doesn't track elasped time). + const double duration = ((double) device->actual_spec.interval_numerator / ((double) device->actual_spec.interval_denominator)); + SDL_Delay((Uint32) (duration * 1000.0)); + } + return 0; +} + +static size_t GetFrameBufLen(const SDL_CameraSpec *spec) +{ + const size_t w = (const size_t) spec->width; + const size_t h = (const size_t) spec->height; + const size_t wxh = w * h; + const Uint32 fmt = spec->format; + + switch (fmt) { + // Some YUV formats have a larger Y plane than their U or V planes. + case SDL_PIXELFORMAT_YV12: + case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_NV12: + case SDL_PIXELFORMAT_NV21: + return wxh + (wxh / 2); + + default: break; + } + + // this is correct for most things. + return wxh * SDL_BYTESPERPIXEL(fmt); +} + +static int ZombieAcquireFrame(SDL_CameraDevice *device, SDL_Surface *frame, Uint64 *timestampNS) +{ + const SDL_CameraSpec *spec = &device->actual_spec; + + if (!device->zombie_pixels) { + // attempt to allocate and initialize a fake frame of pixels. + const size_t buflen = GetFrameBufLen(&device->actual_spec); + device->zombie_pixels = SDL_aligned_alloc(SDL_SIMDGetAlignment(), buflen); + if (!device->zombie_pixels) { + *timestampNS = 0; + return 0; // oh well, say there isn't a frame yet, so we'll go back to waiting. Maybe allocation will succeed later...? + } + + Uint8 *dst = device->zombie_pixels; + switch (spec->format) { + // in YUV formats, the U and V values must be 128 to get a black frame. If set to zero, it'll be bright green. + case SDL_PIXELFORMAT_YV12: + case SDL_PIXELFORMAT_IYUV: + case SDL_PIXELFORMAT_NV12: + case SDL_PIXELFORMAT_NV21: + SDL_memset(dst, 0, spec->width * spec->height); // set Y to zero. + SDL_memset(dst + (spec->width * spec->height), 128, (spec->width * spec->height) / 2); // set U and V to 128. + break; + + case SDL_PIXELFORMAT_YUY2: + case SDL_PIXELFORMAT_YVYU: + // Interleaved Y1[U1|V1]Y2[U2|V2]. + for (size_t i = 0; i < buflen; i += 4) { + dst[i] = 0; + dst[i+1] = 128; + dst[i+2] = 0; + dst[i+3] = 128; + } + break; + + + case SDL_PIXELFORMAT_UYVY: + // Interleaved [U1|V1]Y1[U2|V2]Y2. + for (size_t i = 0; i < buflen; i += 4) { + dst[i] = 128; + dst[i+1] = 0; + dst[i+2] = 128; + dst[i+3] = 0; + } + break; + + default: + // just zero everything else, it'll _probably_ be okay. + SDL_memset(dst, 0, buflen); + break; + } + } + + + *timestampNS = SDL_GetTicksNS(); + frame->pixels = device->zombie_pixels; + + // SDL (currently) wants the pitch of YUV formats to be the pitch of the (1-byte-per-pixel) Y plane. + frame->pitch = spec->width; + if (!SDL_ISPIXELFORMAT_FOURCC(spec->format)) { // checking if it's not FOURCC to only do this for non-YUV data is good enough for now. + frame->pitch *= SDL_BYTESPERPIXEL(spec->format); + } + + #if DEBUG_CAMERA + SDL_Log("CAMERA: dev[%p] Acquired Zombie frame, timestamp %llu", device, (unsigned long long) *timestampNS); + #endif + + return 1; // frame is available. +} + +static void ZombieReleaseFrame(SDL_CameraDevice *device, SDL_Surface *frame) // Reclaim frame->pixels and frame->pitch! +{ + if (frame->pixels != device->zombie_pixels) { + // this was a frame from before the disconnect event; let the backend make an attempt to free it. + camera_driver.impl.ReleaseFrame(device, frame); + } + // we just leave zombie_pixels alone, as we'll reuse it for every new frame until the camera is closed. +} + static void ClosePhysicalCameraDevice(SDL_CameraDevice *device) { if (!device) { @@ -123,10 +238,10 @@ static void ClosePhysicalCameraDevice(SDL_CameraDevice *device) // release frames that are queued up somewhere... if (!device->needs_conversion && !device->needs_scaling) { for (SurfaceList *i = device->filled_output_surfaces.next; i != NULL; i = i->next) { - camera_driver.impl.ReleaseFrame(device, i->surface); + device->ReleaseFrame(device, i->surface); } for (SurfaceList *i = device->app_held_output_surfaces.next; i != NULL; i = i->next) { - camera_driver.impl.ReleaseFrame(device, i->surface); + device->ReleaseFrame(device, i->surface); } } @@ -144,6 +259,9 @@ static void ClosePhysicalCameraDevice(SDL_CameraDevice *device) } SDL_zeroa(device->output_surfaces); + SDL_aligned_free(device->zombie_pixels); + + device->zombie_pixels = NULL; device->filled_output_surfaces.next = NULL; device->empty_output_surfaces.next = NULL; device->app_held_output_surfaces.next = NULL; @@ -389,6 +507,10 @@ void SDL_CameraDeviceDisconnected(SDL_CameraDevice *device) return; } + #if DEBUG_CAMERA + SDL_Log("CAMERA: DISCONNECTED! dev[%p]", device); + #endif + // Save off removal info in a list so we can send events for each, next // time the event queue pumps, in case something tries to close a device // from an event filter, as this would risk deadlocks and other disasters @@ -402,17 +524,16 @@ void SDL_CameraDeviceDisconnected(SDL_CameraDevice *device) const SDL_bool first_disconnect = SDL_AtomicCompareAndSwap(&device->zombie, 0, 1); if (first_disconnect) { // if already disconnected this device, don't do it twice. // Swap in "Zombie" versions of the usual platform interfaces, so the device will keep - // making progress until the app closes it. -#if 0 // !!! FIXME -sdfsdf + // making progress until the app closes it. Otherwise, streams might continue to + // accumulate waste data that never drains, apps that depend on audio callbacks to + // progress will freeze, etc. device->WaitDevice = ZombieWaitDevice; - device->GetDeviceBuf = ZombieGetDeviceBuf; - device->PlayDevice = ZombiePlayDevice; - device->WaitCaptureDevice = ZombieWaitDevice; - device->CaptureFromDevice = ZombieCaptureFromDevice; - device->FlushCapture = ZombieFlushCapture; -sdfsdf -#endif + device->AcquireFrame = ZombieAcquireFrame; + device->ReleaseFrame = ZombieReleaseFrame; + + // Zombie functions will just report the timestamp as SDL_GetTicksNS(), so we don't need to adjust anymore to get it to match. + device->adjust_timestamp = 0; + device->base_timestamp = 0; SDL_PendingCameraDeviceEvent *p = (SDL_PendingCameraDeviceEvent *) SDL_malloc(sizeof (SDL_PendingCameraDeviceEvent)); if (p) { // if this failed, no event for you, but you have deeper problems anyhow. @@ -642,7 +763,7 @@ SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device) Uint64 timestampNS = 0; // AcquireFrame SHOULD NOT BLOCK, as we are holding a lock right now. Block in WaitDevice instead! - const int rc = camera_driver.impl.AcquireFrame(device, device->acquire_surface, ×tampNS); + const int rc = device->AcquireFrame(device, device->acquire_surface, ×tampNS); if (rc == 1) { // new frame acquired! #if DEBUG_CAMERA @@ -654,7 +775,7 @@ SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device) SDL_Log("CAMERA: Dropping an initial frame"); #endif device->drop_frames--; - camera_driver.impl.ReleaseFrame(device, device->acquire_surface); + device->ReleaseFrame(device, device->acquire_surface); device->acquire_surface->pixels = NULL; device->acquire_surface->pitch = 0; } else if (device->empty_output_surfaces.next == NULL) { @@ -662,7 +783,7 @@ SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device) #if DEBUG_CAMERA SDL_Log("CAMERA: No empty output surfaces! Dropping frame!"); #endif - camera_driver.impl.ReleaseFrame(device, device->acquire_surface); + device->ReleaseFrame(device, device->acquire_surface); device->acquire_surface->pixels = NULL; device->acquire_surface->pitch = 0; } else { @@ -728,7 +849,7 @@ SDL_bool SDL_CameraThreadIterate(SDL_CameraDevice *device) } // we made a copy, so we can give the driver back its resources. - camera_driver.impl.ReleaseFrame(device, acquired); + device->ReleaseFrame(device, acquired); } // we either released these already after we copied the data, or the pointer was migrated to output_surface. @@ -765,7 +886,7 @@ static int SDLCALL CameraThread(void *devicep) SDL_CameraThreadSetup(device); do { - if (camera_driver.impl.WaitDevice(device) < 0) { + if (device->WaitDevice(device) < 0) { SDL_CameraDeviceDisconnected(device); // doh. (but don't break out of the loop, just be a zombie for now!) } } while (SDL_CameraThreadIterate(device)); @@ -912,6 +1033,11 @@ SDL_Camera *SDL_OpenCameraDevice(SDL_CameraDeviceID instance_id, const SDL_Camer SDL_AtomicSet(&device->shutdown, 0); + // These start with the backend's implementation, but we might swap them out with zombie versions later. + device->WaitDevice = camera_driver.impl.WaitDevice; + device->AcquireFrame = camera_driver.impl.AcquireFrame; + device->ReleaseFrame = camera_driver.impl.ReleaseFrame; + SDL_CameraSpec closest; ChooseBestCameraSpec(device, spec, &closest); @@ -1084,7 +1210,7 @@ int SDL_ReleaseCameraFrame(SDL_Camera *camera, SDL_Surface *frame) // this pointer was owned by the backend (DMA memory or whatever), clear it out. if (!device->needs_conversion && !device->needs_scaling) { - camera_driver.impl.ReleaseFrame(device, frame); + device->ReleaseFrame(device, frame); frame->pixels = NULL; frame->pitch = 0; } diff --git a/src/camera/SDL_syscamera.h b/src/camera/SDL_syscamera.h index fc55b071be3fd..82032a563e79d 100644 --- a/src/camera/SDL_syscamera.h +++ b/src/camera/SDL_syscamera.h @@ -85,6 +85,11 @@ struct SDL_CameraDevice // When refcount hits zero, we destroy the device object. SDL_AtomicInt refcount; + // These are, initially, set from camera_driver, but we might swap them out with Zombie versions on disconnect/failure. + int (*WaitDevice)(SDL_CameraDevice *device); + int (*AcquireFrame)(SDL_CameraDevice *device, SDL_Surface *frame, Uint64 *timestampNS); + void (*ReleaseFrame)(SDL_CameraDevice *device, SDL_Surface *frame); + // All supported formats/dimensions for this device. SDL_CameraSpec *all_specs; @@ -124,6 +129,9 @@ struct SDL_CameraDevice SurfaceList empty_output_surfaces; // this is LIFO SurfaceList app_held_output_surfaces; + // A fake video frame we allocate if the camera fails/disconnects. + Uint8 *zombie_pixels; + // non-zero if acquire_surface needs to be scaled for final output. int needs_scaling; // -1: downscale, 0: no scaling, 1: upscale From b1ed49772cbb8545abc5de91ca0f4ba7e685b66f Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 19 Feb 2024 12:20:11 -0500 Subject: [PATCH 058/220] camera: Replace testcamera.c with testcameraminimal.c --- test/CMakeLists.txt | 3 +- test/testcamera.c | 814 ++++++--------------------------------- test/testcameraminimal.c | 197 ---------- 3 files changed, 118 insertions(+), 896 deletions(-) delete mode 100644 test/testcameraminimal.c diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index db796e825716f..e7c12e30cb60a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -394,8 +394,7 @@ add_sdl_test_executable(teststreaming NEEDS_RESOURCES TESTUTILS SOURCES teststre add_sdl_test_executable(testtimer NONINTERACTIVE NONINTERACTIVE_ARGS --no-interactive NONINTERACTIVE_TIMEOUT 60 SOURCES testtimer.c) add_sdl_test_executable(testurl SOURCES testurl.c) add_sdl_test_executable(testver NONINTERACTIVE SOURCES testver.c) -add_sdl_test_executable(testcamera SOURCES testcamera.c) -add_sdl_test_executable(testcameraminimal MAIN_CALLBACKS SOURCES testcameraminimal.c) +add_sdl_test_executable(testcamera MAIN_CALLBACKS SOURCES testcamera.c) add_sdl_test_executable(testviewport NEEDS_RESOURCES TESTUTILS SOURCES testviewport.c) add_sdl_test_executable(testwm SOURCES testwm.c) add_sdl_test_executable(testyuv NONINTERACTIVE NONINTERACTIVE_ARGS "--automated" NEEDS_RESOURCES TESTUTILS SOURCES testyuv.c testyuv_cvt.c) diff --git a/test/testcamera.c b/test/testcamera.c index e5389d218fc25..9fc04c534b9db 100644 --- a/test/testcamera.c +++ b/test/testcamera.c @@ -9,224 +9,45 @@ including commercial applications, and to alter it and redistribute it freely. */ -#include "SDL3/SDL_main.h" -#include "SDL3/SDL.h" -#include "SDL3/SDL_test.h" -#include "SDL3/SDL_camera.h" -#ifdef SDL_PLATFORM_EMSCRIPTEN -#include -#endif - -#if 1 -int main(int argc, char **argv) -{ - SDL_Log("FIXME: update me"); - return 0; -} -#else -static const char *usage = "\ - \n\ - =========================================================================\n\ - \n\ -Use keyboards:\n\ - o: open first camera device. (close previously opened)\n\ - l: switch to, and list camera devices\n\ - i: information about status (Init, Playing, Stopped)\n\ - f: formats and resolutions available\n\ - s: start / stop capture\n\ - h: display help\n\ - esc: exit \n\ - \n\ - =========================================================================\n\ - \n\ -"; - -typedef struct { - Uint64 next_check; - int frame_counter; - int check_delay; - double last_fps; -} measure_fps_t; - -static void -update_fps(measure_fps_t *m) -{ - Uint64 now = SDL_GetTicks(); - Uint64 deadline; - m->frame_counter++; - if (m->check_delay == 0) { - m->check_delay = 1500; - } - deadline = m->next_check; - if (now >= deadline) { - /* Print out some timing information */ - const Uint64 then = m->next_check - m->check_delay; - m->last_fps = ((double) m->frame_counter * 1000) / (now - then); - m->next_check = now + m->check_delay; - m->frame_counter = 0; - } -} - -#if defined(SDL_PLATFORM_LINUX) && !defined(SDL_PLATFORM_ANDROID) -static void load_average(float *val) -{ - FILE *fp = 0; - char line[1024]; - fp = fopen("/proc/loadavg", "rt"); - if (fp) { - char *s = fgets(line, sizeof(line), fp); - if (s) { - SDL_sscanf(s, "%f", val); - } - fclose(fp); - } -} -#endif - - -struct data_capture_t { - SDL_CameraDevice *device; - SDL_CameraSpec obtained; - int stopped; - SDL_CameraFrame frame_current; - measure_fps_t fps_capture; - SDL_Texture *texture; - int texture_updated; -}; - -#define SAVE_CAPTURE_STATE(x) \ - data_capture_tab[(x)].device = device; \ - data_capture_tab[(x)].obtained = obtained; \ - data_capture_tab[(x)].stopped = stopped; \ - data_capture_tab[(x)].frame_current = frame_current; \ - data_capture_tab[(x)].fps_capture = fps_capture; \ - data_capture_tab[(x)].texture = texture; \ - data_capture_tab[(x)].texture_updated = texture_updated; \ - - -#define RESTORE_CAPTURE_STATE(x) \ - device = data_capture_tab[(x)].device; \ - obtained = data_capture_tab[(x)].obtained; \ - stopped = data_capture_tab[(x)].stopped; \ - frame_current = data_capture_tab[(x)].frame_current; \ - fps_capture = data_capture_tab[(x)].fps_capture; \ - texture = data_capture_tab[(x)].texture; \ - texture_updated = data_capture_tab[(x)].texture_updated; \ - - - - - -static SDL_CameraDeviceID get_instance_id(int index) { - int ret = 0; - int num = 0; - SDL_CameraDeviceID *devices; - devices = SDL_GetCameraDevices(&num); - if (devices) { - if (index >= 0 && index < num) { - ret = devices[index]; - } - SDL_free(devices); - } - - if (ret == 0) { -/* SDL_Log("invalid index"); */ - } - - return ret; -} - - - -int main(int argc, char **argv) +#define SDL_MAIN_USE_CALLBACKS 1 +#include +#include +#include + +static SDL_Window *window = NULL; +static SDL_Renderer *renderer = NULL; +static SDLTest_CommonState *state = NULL; +static SDL_Camera *camera = NULL; +static SDL_CameraSpec spec; +static SDL_Texture *texture = NULL; +static SDL_bool texture_updated = SDL_FALSE; +static SDL_Surface *frame_current = NULL; + +int SDL_AppInit(int argc, char *argv[]) { - SDL_Window *window = NULL; - SDL_Renderer *renderer = NULL; - SDL_Event evt; - int quit = 0; - - SDLTest_CommonState *state; - - int current_dev = 0; - measure_fps_t fps_main; - - - SDL_FRect r_playstop = { 50, 50, 120, 50 }; - SDL_FRect r_close = { 50 + (120 + 50) * 1, 50, 120, 50 }; - - SDL_FRect r_open = { 50 + (120 + 50) * 2, 50, 120, 50 }; - - SDL_FRect r_format = { 50 + (120 + 50) * 3, 50, 120, 50 }; - SDL_FRect r_listdev = { 50 + (120 + 50) * 4, 50, 120, 50 }; - - SDL_CameraDevice *device; - SDL_CameraSpec obtained; - int stopped = 0; - SDL_CameraFrame frame_current; - measure_fps_t fps_capture; - SDL_Texture *texture = NULL; - int texture_updated = 0; - - struct data_capture_t data_capture_tab[16]; - const int data_capture_tab_size = SDL_arraysize(data_capture_tab); - - SDL_zero(fps_main); - SDL_zero(fps_capture); - SDL_zero(frame_current); - SDL_zeroa(data_capture_tab); - - /* Set 0 to disable TouchEvent to be duplicated as MouseEvent with SDL_TOUCH_MOUSEID */ - SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); - /* Set 0 to disable MouseEvent to be duplicated as TouchEvent with SDL_MOUSE_TOUCHID */ - SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0"); - - { - int i; - for (i = 0; i < data_capture_tab_size; i++) { - data_capture_tab[i].device = NULL; - } - } + int devcount = 0; + int i; /* Initialize test framework */ state = SDLTest_CommonCreateState(argv, 0); if (state == NULL) { - return 1; + return -1; } /* Enable standard application logging */ SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); - /* Parse commandline */ - { - int i; - for (i = 1; i < argc;) { - int consumed; - - consumed = SDLTest_CommonArg(state, i); - if (consumed <= 0) { - static const char *options[] = {NULL}; - SDLTest_CommonLogUsage(state, argv[0], options); - SDLTest_CommonDestroyState(state); - return 1; - } - - i += consumed; - } - } - - SDL_Log("%s", usage); - /* Load the SDL library */ if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError()); - return 1; + return -1; } window = SDL_CreateWindow("Local Video", 1000, 800, 0); if (window == NULL) { SDL_Log("Couldn't create window: %s", SDL_GetError()); - return 1; + return -1; } SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); @@ -234,544 +55,143 @@ int main(int argc, char **argv) renderer = SDL_CreateRenderer(window, NULL, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); if (renderer == NULL) { /* SDL_Log("Couldn't create renderer: %s", SDL_GetError()); */ - return 1; - } - - SDL_LogSetAllPriority(SDL_LOG_PRIORITY_INFO); - - device = SDL_OpenCamera(0); - - if (!device) { - SDL_Log("Error SDL_OpenCamera: %s", SDL_GetError()); + return -1; } - { - /* List formats */ - int i, num = SDL_GetNumCameraFormats(device); - for (i = 0; i < num; i++) { - Uint32 format; - SDL_GetCameraFormat(device, i, &format); - SDL_Log("format %d/%d: %s", i, num, SDL_GetPixelFormatName(format)); - { - int w, h; - int j, num2 = SDL_GetNumCameraFrameSizes(device, format); - for (j = 0; j < num2; j++) { - SDL_GetCameraFrameSize(device, format, j, &w, &h); - SDL_Log(" framesizes %d/%d : %d x %d", j, num2, w, h); - } - } - } + SDL_CameraDeviceID *devices = SDL_GetCameraDevices(&devcount); + if (!devices) { + SDL_Log("SDL_GetCameraDevices failed: %s", SDL_GetError()); + return -1; } - /* Set Spec */ - { - int ret; - /* forced_format */ - SDL_CameraSpec desired; - SDL_zero(desired); - desired.width = 640 * 2; - desired.height = 360 * 2; - desired.format = SDL_PIXELFORMAT_NV12; - ret = SDL_SetCameraSpec(device, &desired, &obtained, SDL_CAMERA_ALLOW_ANY_CHANGE); - - if (ret < 0) { - SDL_SetCameraSpec(device, NULL, &obtained, 0); - } + SDL_Log("Saw %d camera devices.", devcount); + for (i = 0; i < devcount; i++) { + char *name = SDL_GetCameraDeviceName(devices[i]); + SDL_Log(" - Camera #%d: %s", i, name); + SDL_free(name); } - SDL_Log("Open camera device. Obtained spec: size=%d x %d format=%s", - obtained.width, obtained.height, SDL_GetPixelFormatName(obtained.format)); + const SDL_CameraDeviceID devid = devices[0]; /* just take the first one. */ + SDL_free(devices); - { - SDL_CameraSpec spec; - if (SDL_GetCameraFormat(device, &spec) == 0) { - SDL_Log("Read spec: size=%d x %d format=%s", - spec.width, spec.height, SDL_GetPixelFormatName(spec.format)); - } else { - SDL_Log("Error read spec: %s", SDL_GetError()); - } + if (!devid) { + SDL_Log("No cameras available?"); + return -1; } + + SDL_CameraSpec *pspec = NULL; + #if 0 /* just for edge-case testing purposes, ignore. */ + pspec = &spec; + spec.width = 100 /*1280 * 2*/; + spec.height = 100 /*720 * 2*/; + spec.format = SDL_PIXELFORMAT_YUY2 /*SDL_PIXELFORMAT_RGBA8888*/; + #endif - if (SDL_StartCamera(device) < 0) { - SDL_Log("error SDL_StartCamera(): %s", SDL_GetError()); + camera = SDL_OpenCameraDevice(devid, pspec); + if (!camera) { + SDL_Log("Failed to open camera device: %s", SDL_GetError()); + return -1; } - while (!quit) { - - SDL_SetRenderDrawColor(renderer, 0x99, 0x99, 0x99, 255); - SDL_RenderClear(renderer); - - SDL_SetRenderDrawColor(renderer, 0x33, 0x33, 0x33, 255); - - SDL_RenderFillRect(renderer, &r_playstop); - SDL_RenderFillRect(renderer, &r_close); - SDL_RenderFillRect(renderer, &r_open); - SDL_RenderFillRect(renderer, &r_format); - SDL_RenderFillRect(renderer, &r_listdev); - - SDL_SetRenderDrawColor(renderer, 0xcc, 0xcc, 0xcc, 255); - - SDLTest_DrawString(renderer, r_playstop.x + 5, r_playstop.y + 5, "play stop"); - SDLTest_DrawString(renderer, r_close.x + 5, r_close.y + 5, "close"); - SDLTest_DrawString(renderer, r_open.x + 5, r_open.y + 5, "open dev"); - SDLTest_DrawString(renderer, r_format.x + 5, r_format.y + 5, "formats"); - - { - char buf[256]; - SDL_snprintf(buf, 256, "device %d", current_dev); - SDLTest_DrawString(renderer, r_listdev.x + 5, r_listdev.y + 5, buf); - } - - while (SDL_PollEvent(&evt)) { - SDL_FRect *r = NULL; - SDL_FPoint pt; - int sym = 0; - - pt.x = 0; - pt.y = 0; - - SDL_ConvertEventToRenderCoordinates(renderer, &evt); - - switch (evt.type) - { - case SDL_EVENT_KEY_DOWN: - { - sym = evt.key.keysym.sym; - break; - } - case SDL_EVENT_QUIT: - { - quit = 1; - SDL_Log("Ctlr+C : Quit!"); - } - break; - - case SDL_EVENT_FINGER_DOWN: - { - pt.x = evt.tfinger.x; - pt.y = evt.tfinger.y; - } - break; - - case SDL_EVENT_MOUSE_BUTTON_DOWN: - { - pt.x = evt.button.x; - pt.y = evt.button.y; - } - break; - } - - if (pt.x != 0 && pt.y != 0) { - if (SDL_PointInRectFloat(&pt, &r_playstop)) { - r = &r_playstop; - sym = SDLK_s; - } - if (SDL_PointInRectFloat(&pt, &r_close)) { - r = &r_close; - sym = SDLK_c; - } - if (SDL_PointInRectFloat(&pt, &r_open)) { - r = &r_open; - sym = SDLK_o; - } - - if (SDL_PointInRectFloat(&pt, &r_format)) { - r = &r_format; - sym = SDLK_f; - } - if (SDL_PointInRectFloat(&pt, &r_listdev)) { - r = &r_listdev; - sym = SDLK_l; - } - } - - - if (r) { - SDL_SetRenderDrawColor(renderer, 0x33, 0, 0, 255); - SDL_RenderFillRect(renderer, r); - } - - - if (sym == SDLK_c) { - if (frame_current.num_planes) { - SDL_ReleaseCameraFrame(device, &frame_current); - } - SDL_CloseCamera(device); - device = NULL; - SDL_Log("Close"); - } - - if (sym == SDLK_o) { - if (device) { - SDL_Log("Close previous .."); - if (frame_current.num_planes) { - SDL_ReleaseCameraFrame(device, &frame_current); - } - SDL_CloseCamera(device); - } - - texture_updated = 0; - - SDL_ClearError(); - - SDL_Log("Try to open:%s", SDL_GetCameraDeviceName(get_instance_id(current_dev))); - - obtained.width = 640 * 2; - obtained.height = 360 * 2; - device = SDL_OpenCameraWithSpec(get_instance_id(current_dev), &obtained, &obtained, SDL_CAMERA_ALLOW_ANY_CHANGE); - - /* spec may have changed because of re-open */ - if (texture) { - SDL_DestroyTexture(texture); - texture = NULL; - } - - SDL_Log("Open device:%p %s", (void*)device, SDL_GetError()); - stopped = 0; - } - - if (sym == SDLK_l) { - int num = 0; - SDL_CameraDeviceID *devices; - int i; - devices = SDL_GetCameraDevices(&num); - - SDL_Log("Num devices : %d", num); - for (i = 0; i < num; i++) { - SDL_Log("Device %d/%d : %s", i, num, SDL_GetCameraDeviceName(devices[i])); - } - SDL_free(devices); - - SAVE_CAPTURE_STATE(current_dev); - - current_dev += 1; - if (current_dev >= num || current_dev >= (int) SDL_arraysize(data_capture_tab)) { - current_dev = 0; - } - - RESTORE_CAPTURE_STATE(current_dev); - SDL_Log("--> select dev %d / %d", current_dev, num); - } - - if (sym == SDLK_i) { - SDL_CameraStatus status = SDL_GetCameraStatus(device); - if (status == SDL_CAMERA_STOPPED) { SDL_Log("STOPPED"); } - if (status == SDL_CAMERA_PLAYING) { SDL_Log("PLAYING"); } - if (status == SDL_CAMERA_INIT) { SDL_Log("INIT"); } - } - - if (sym == SDLK_s) { - if (stopped) { - SDL_Log("Stop"); - SDL_StopCamera(device); - } else { - SDL_Log("Start"); - SDL_StartCamera(device); - } - stopped = !stopped; - } - - if (sym == SDLK_f) { - SDL_Log("List formats"); - - if (!device) { - device = SDL_OpenCamera(get_instance_id(current_dev)); - } + return 0; /* start the main app loop. */ +} - /* List formats */ - { - int i, num = SDL_GetNumCameraFormats(device); - for (i = 0; i < num; i++) { - Uint32 format; - SDL_GetCameraFormat(device, i, &format); - SDL_Log("format %d/%d : %s", i, num, SDL_GetPixelFormatName(format)); - { - int w, h; - int j, num2 = SDL_GetNumCameraFrameSizes(device, format); - for (j = 0; j < num2; j++) { - SDL_GetCameraFrameSize(device, format, j, &w, &h); - SDL_Log(" framesizes %d/%d : %d x %d", j, num2, w, h); - } - } - } - } - } +int SDL_AppEvent(const SDL_Event *event) +{ + switch (event->type) { + case SDL_EVENT_KEY_DOWN: { + const SDL_Keycode sym = event->key.keysym.sym; if (sym == SDLK_ESCAPE || sym == SDLK_AC_BACK) { - quit = 1; SDL_Log("Key : Escape!"); + return 1; } - - if (sym == SDLK_h || sym == SDLK_F1) { - SDL_Log("%s", usage); - } - } - - - SAVE_CAPTURE_STATE(current_dev); - - { - int i, n = SDL_arraysize(data_capture_tab); - for (i = 0; i < n; i++) { - RESTORE_CAPTURE_STATE(i); - - if (!device) { - /* device has been closed */ - frame_current.num_planes = 0; - texture_updated = 0; - } else { - int ret; - SDL_CameraFrame frame_next; - SDL_zero(frame_next); - - ret = SDL_AcquireCameraFrame(device, &frame_next); - if (ret < 0) { - SDL_Log("dev[%d] err SDL_AcquireCameraFrame: %s", i, SDL_GetError()); - } -#if 1 - if (frame_next.num_planes) { - SDL_Log("dev[%d] frame: %p at %" SDL_PRIu64, i, (void*)frame_next.data[0], frame_next.timestampNS); - } -#endif - - if (frame_next.num_planes) { - - update_fps(&fps_capture); - - if (frame_current.num_planes) { - ret = SDL_ReleaseCameraFrame(device, &frame_current); - if (ret < 0) { - SDL_Log("dev[%d] err SDL_ReleaseCameraFrame: %s", i, SDL_GetError()); - } - } - frame_current = frame_next; - texture_updated = 0; - } - } - - SAVE_CAPTURE_STATE(i); - } + break; } + case SDL_EVENT_QUIT: + SDL_Log("Ctlr+C : Quit!"); + return 1; - RESTORE_CAPTURE_STATE(current_dev); - - - - /* Moving square */ - SDL_SetRenderDrawColor(renderer, 0, 0xff, 0, 255); - { - SDL_FRect r; - static float x = 0; - x += 10; - if (x > 1000) { - x = 0; + case SDL_EVENT_CAMERA_DEVICE_APPROVED: + if (SDL_GetCameraFormat(camera, &spec) < 0) { + SDL_Log("Couldn't get camera spec: %s", SDL_GetError()); + return -1; } - r.x = x; - r.y = 100; - r.w = r.h = 10; - SDL_RenderFillRect(renderer, &r); - } - SDL_SetRenderDrawColor(renderer, 0x33, 0x33, 0x33, 255); - - - SAVE_CAPTURE_STATE(current_dev); - - { - int i, n = SDL_arraysize(data_capture_tab); - for (i = 0; i < n; i++) { - RESTORE_CAPTURE_STATE(i); - - /* Update SDL_Texture with last video frame (only once per new frame) */ - if (frame_current.num_planes && texture_updated == 0) { - - /* Create texture with appropriate format (for DMABUF or not) */ - if (texture == NULL) { - Uint32 format = obtained.format; - texture = SDL_CreateTexture(renderer, format, SDL_TEXTUREACCESS_STATIC, obtained.width, obtained.height); - if (texture == NULL) { - SDL_Log("Couldn't create texture: %s", SDL_GetError()); - return 1; - } - } - - { - /* Use software data */ - if (frame_current.num_planes == 1) { - SDL_UpdateTexture(texture, NULL, - frame_current.data[0], frame_current.pitch[0]); - } else if (frame_current.num_planes == 2) { - SDL_UpdateNVTexture(texture, NULL, - frame_current.data[0], frame_current.pitch[0], - frame_current.data[1], frame_current.pitch[1]); - } else if (frame_current.num_planes == 3) { - SDL_UpdateYUVTexture(texture, NULL, frame_current.data[0], frame_current.pitch[0], - frame_current.data[1], frame_current.pitch[1], - frame_current.data[2], frame_current.pitch[2]); - } - texture_updated = 1; - } - } - - SAVE_CAPTURE_STATE(i); + /* Create texture with appropriate format */ + texture = SDL_CreateTexture(renderer, spec.format, SDL_TEXTUREACCESS_STATIC, spec.width, spec.height); + if (texture == NULL) { + SDL_Log("Couldn't create texture: %s", SDL_GetError()); + return -1; } - } + break; + case SDL_EVENT_CAMERA_DEVICE_DENIED: + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Camera permission denied!", "User denied access to the camera!", window); + return -1; + } - RESTORE_CAPTURE_STATE(current_dev); - - { - int i, n = SDL_arraysize(data_capture_tab); - int win_w, win_h; - int total_texture_updated = 0; - int curr_texture_updated = 0; - for (i = 0; i < n; i++) { - if (data_capture_tab[i].texture_updated) { - total_texture_updated += 1; - } - } - - SDL_GetRenderOutputSize(renderer, &win_w, &win_h); - - - for (i = 0; i < n; i++) { - RESTORE_CAPTURE_STATE(i); - /* RenderCopy the SDL_Texture */ - if (texture_updated == 1) { - /* Scale texture to fit the screen */ - - int tw, th; - int w; - SDL_FRect d; - SDL_QueryTexture(texture, NULL, NULL, &tw, &th); - - w = win_w / total_texture_updated; + return SDLTest_CommonEventMainCallbacks(state, event); +} - if (tw > w - 20) { - float scale = (float) (w - 20) / (float) tw; - tw = w - 20; - th = (int)((float) th * scale); - } - d.x = (float)(10 + curr_texture_updated * w); - d.y = (float)(win_h - th); - d.w = (float)tw; - d.h = (float)(th - 10); - SDL_RenderTexture(renderer, texture, NULL, &d); +int SDL_AppIterate(void) +{ + SDL_SetRenderDrawColor(renderer, 0x99, 0x99, 0x99, 255); + SDL_RenderClear(renderer); - curr_texture_updated += 1; - } - } + if (texture != NULL) { /* if not NULL, camera is ready to go. */ + int win_w, win_h, tw, th; + SDL_FRect d; + Uint64 timestampNS = 0; + SDL_Surface *frame_next = SDL_AcquireCameraFrame(camera, ×tampNS); + #if 0 + if (frame_next) { + SDL_Log("frame: %p at %" SDL_PRIu64, (void*)frame_next->pixels, timestampNS); } + #endif - RESTORE_CAPTURE_STATE(current_dev); - - - /* display status and FPS */ - if (!device) { -#ifdef SDL_PLATFORM_IOS - const float x_offset = 500; -#else - const float x_offset = 0; -#endif - char buf[256]; - SDL_snprintf(buf, 256, "Device %d (%s) is not opened", current_dev, SDL_GetCameraDeviceName(get_instance_id(current_dev))); - SDLTest_DrawString(renderer, x_offset + 10, 10, buf); - } else { -#ifdef SDL_PLATFORM_IOS - const float x_offset = 500; -#else - const float x_offset = 0; -#endif - const char *status = "no status"; - char buf[256]; - - if (device) { - SDL_CameraStatus s = SDL_GetCameraStatus(device); - if (s == SDL_CAMERA_INIT) { - status = "init"; - } else if (s == SDL_CAMERA_PLAYING) { - status = "playing"; - } else if (s == SDL_CAMERA_STOPPED) { - status = "stopped"; - } else if (s == SDL_CAMERA_FAIL) { - status = "failed"; + if (frame_next) { + if (frame_current) { + if (SDL_ReleaseCameraFrame(camera, frame_current) < 0) { + SDL_Log("err SDL_ReleaseCameraFrame: %s", SDL_GetError()); } - } - /* capture device, capture fps, capture status */ - SDL_snprintf(buf, 256, "Device %d - %2.2f fps - %s", current_dev, fps_capture.last_fps, status); - SDLTest_DrawString(renderer, x_offset + 10, 10, buf); - - /* capture spec */ - SDL_snprintf(buf, sizeof(buf), "%d x %d %s", obtained.width, obtained.height, SDL_GetPixelFormatName(obtained.format)); - SDLTest_DrawString(renderer, x_offset + 10, 20, buf); - - /* video fps */ - SDL_snprintf(buf, sizeof(buf), "%2.2f fps", fps_main.last_fps); - SDLTest_DrawString(renderer, x_offset + 10, 30, buf); - + /* It's not needed to keep the frame once updated the texture is updated. + * But in case of 0-copy, it's needed to have the frame while using the texture. + */ + frame_current = frame_next; + texture_updated = SDL_FALSE; } - /* display last error */ - { - SDLTest_DrawString(renderer, 400, 10, SDL_GetError()); - } - - /* display load average */ -#if defined(SDL_PLATFORM_LINUX) && !defined(SDL_PLATFORM_ANDROID) - { - float val = 0.0f; - char buf[128]; - load_average(&val); - if (val != 0.0f) { - SDL_snprintf(buf, sizeof(buf), "load avg %2.2f percent", val); - SDLTest_DrawString(renderer, 800, 10, buf); - } + /* Update SDL_Texture with last video frame (only once per new frame) */ + if (frame_current && !texture_updated) { + SDL_UpdateTexture(texture, NULL, frame_current->pixels, frame_current->pitch); + texture_updated = SDL_TRUE; } -#endif - - - SDL_Delay(20); - SDL_RenderPresent(renderer); - - update_fps(&fps_main); + SDL_QueryTexture(texture, NULL, NULL, &tw, &th); + SDL_GetRenderOutputSize(renderer, &win_w, &win_h); + d.x = (float) ((win_w - tw) / 2); + d.y = (float) ((win_h - th) / 2); + d.w = (float) tw; + d.h = (float) th; + SDL_RenderTexture(renderer, texture, NULL, &d); } + SDL_RenderPresent(renderer); + return 0; /* keep iterating. */ +} - SAVE_CAPTURE_STATE(current_dev); - - { - int i, n = SDL_arraysize(data_capture_tab); - for (i = 0; i < n; i++) { - RESTORE_CAPTURE_STATE(i); - - if (device) { - if (SDL_StopCamera(device) < 0) { - SDL_Log("error SDL_StopCamera(): %s", SDL_GetError()); - } - if (frame_current.num_planes) { - SDL_ReleaseCameraFrame(device, &frame_current); - } - SDL_CloseCamera(device); - } - - if (texture) { - SDL_DestroyTexture(texture); - } - } - } - +void SDL_AppQuit(void) +{ + SDL_ReleaseCameraFrame(camera, frame_current); + SDL_CloseCamera(camera); + SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); - - SDL_Quit(); - SDLTest_CommonDestroyState(state); - - return 0; } -#endif \ No newline at end of file + diff --git a/test/testcameraminimal.c b/test/testcameraminimal.c deleted file mode 100644 index 9fc04c534b9db..0000000000000 --- a/test/testcameraminimal.c +++ /dev/null @@ -1,197 +0,0 @@ -/* - Copyright (C) 1997-2024 Sam Lantinga - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely. -*/ - -#define SDL_MAIN_USE_CALLBACKS 1 -#include -#include -#include - -static SDL_Window *window = NULL; -static SDL_Renderer *renderer = NULL; -static SDLTest_CommonState *state = NULL; -static SDL_Camera *camera = NULL; -static SDL_CameraSpec spec; -static SDL_Texture *texture = NULL; -static SDL_bool texture_updated = SDL_FALSE; -static SDL_Surface *frame_current = NULL; - -int SDL_AppInit(int argc, char *argv[]) -{ - int devcount = 0; - int i; - - /* Initialize test framework */ - state = SDLTest_CommonCreateState(argv, 0); - if (state == NULL) { - return -1; - } - - /* Enable standard application logging */ - SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); - - /* Load the SDL library */ - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA) < 0) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError()); - return -1; - } - - window = SDL_CreateWindow("Local Video", 1000, 800, 0); - if (window == NULL) { - SDL_Log("Couldn't create window: %s", SDL_GetError()); - return -1; - } - - SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); - - renderer = SDL_CreateRenderer(window, NULL, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - if (renderer == NULL) { - /* SDL_Log("Couldn't create renderer: %s", SDL_GetError()); */ - return -1; - } - - SDL_CameraDeviceID *devices = SDL_GetCameraDevices(&devcount); - if (!devices) { - SDL_Log("SDL_GetCameraDevices failed: %s", SDL_GetError()); - return -1; - } - - SDL_Log("Saw %d camera devices.", devcount); - for (i = 0; i < devcount; i++) { - char *name = SDL_GetCameraDeviceName(devices[i]); - SDL_Log(" - Camera #%d: %s", i, name); - SDL_free(name); - } - - const SDL_CameraDeviceID devid = devices[0]; /* just take the first one. */ - SDL_free(devices); - - if (!devid) { - SDL_Log("No cameras available?"); - return -1; - } - - SDL_CameraSpec *pspec = NULL; - #if 0 /* just for edge-case testing purposes, ignore. */ - pspec = &spec; - spec.width = 100 /*1280 * 2*/; - spec.height = 100 /*720 * 2*/; - spec.format = SDL_PIXELFORMAT_YUY2 /*SDL_PIXELFORMAT_RGBA8888*/; - #endif - - camera = SDL_OpenCameraDevice(devid, pspec); - if (!camera) { - SDL_Log("Failed to open camera device: %s", SDL_GetError()); - return -1; - } - - return 0; /* start the main app loop. */ -} - -int SDL_AppEvent(const SDL_Event *event) -{ - switch (event->type) { - case SDL_EVENT_KEY_DOWN: { - const SDL_Keycode sym = event->key.keysym.sym; - if (sym == SDLK_ESCAPE || sym == SDLK_AC_BACK) { - SDL_Log("Key : Escape!"); - return 1; - } - break; - } - - case SDL_EVENT_QUIT: - SDL_Log("Ctlr+C : Quit!"); - return 1; - - case SDL_EVENT_CAMERA_DEVICE_APPROVED: - if (SDL_GetCameraFormat(camera, &spec) < 0) { - SDL_Log("Couldn't get camera spec: %s", SDL_GetError()); - return -1; - } - - /* Create texture with appropriate format */ - texture = SDL_CreateTexture(renderer, spec.format, SDL_TEXTUREACCESS_STATIC, spec.width, spec.height); - if (texture == NULL) { - SDL_Log("Couldn't create texture: %s", SDL_GetError()); - return -1; - } - break; - - case SDL_EVENT_CAMERA_DEVICE_DENIED: - SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Camera permission denied!", "User denied access to the camera!", window); - return -1; - } - - return SDLTest_CommonEventMainCallbacks(state, event); -} - -int SDL_AppIterate(void) -{ - SDL_SetRenderDrawColor(renderer, 0x99, 0x99, 0x99, 255); - SDL_RenderClear(renderer); - - if (texture != NULL) { /* if not NULL, camera is ready to go. */ - int win_w, win_h, tw, th; - SDL_FRect d; - Uint64 timestampNS = 0; - SDL_Surface *frame_next = SDL_AcquireCameraFrame(camera, ×tampNS); - - #if 0 - if (frame_next) { - SDL_Log("frame: %p at %" SDL_PRIu64, (void*)frame_next->pixels, timestampNS); - } - #endif - - if (frame_next) { - if (frame_current) { - if (SDL_ReleaseCameraFrame(camera, frame_current) < 0) { - SDL_Log("err SDL_ReleaseCameraFrame: %s", SDL_GetError()); - } - } - - /* It's not needed to keep the frame once updated the texture is updated. - * But in case of 0-copy, it's needed to have the frame while using the texture. - */ - frame_current = frame_next; - texture_updated = SDL_FALSE; - } - - /* Update SDL_Texture with last video frame (only once per new frame) */ - if (frame_current && !texture_updated) { - SDL_UpdateTexture(texture, NULL, frame_current->pixels, frame_current->pitch); - texture_updated = SDL_TRUE; - } - - SDL_QueryTexture(texture, NULL, NULL, &tw, &th); - SDL_GetRenderOutputSize(renderer, &win_w, &win_h); - d.x = (float) ((win_w - tw) / 2); - d.y = (float) ((win_h - th) / 2); - d.w = (float) tw; - d.h = (float) th; - SDL_RenderTexture(renderer, texture, NULL, &d); - } - - SDL_RenderPresent(renderer); - - return 0; /* keep iterating. */ -} - -void SDL_AppQuit(void) -{ - SDL_ReleaseCameraFrame(camera, frame_current); - SDL_CloseCamera(camera); - SDL_DestroyTexture(texture); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); - SDLTest_CommonDestroyState(state); -} - From 70b89ab70d06257af009ed132acd19794a86c553 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 19 Feb 2024 14:19:57 -0500 Subject: [PATCH 059/220] camera: Added SDL_GetCameraDevicePosition. Otherwise, as a property, you have to open each camera device to figure out which ones are which. --- include/SDL3/SDL_camera.h | 44 ++++++++++++++----- src/camera/SDL_camera.c | 25 +++++++++-- src/camera/SDL_syscamera.h | 7 ++- src/camera/android/SDL_camera_android.c | 19 +++----- src/camera/coremedia/SDL_camera_coremedia.m | 23 +++------- src/camera/emscripten/SDL_camera_emscripten.c | 2 +- .../SDL_camera_mediafoundation.c | 2 +- src/camera/v4l2/SDL_camera_v4l2.c | 2 +- 8 files changed, 75 insertions(+), 49 deletions(-) diff --git a/include/SDL3/SDL_camera.h b/include/SDL3/SDL_camera.h index 57c41a130aa46..e4ac69a88ac14 100644 --- a/include/SDL3/SDL_camera.h +++ b/include/SDL3/SDL_camera.h @@ -70,6 +70,19 @@ typedef struct SDL_CameraSpec int interval_denominator; /**< Frame rate demoninator ((dom / num) == fps, (num / dom) == duration) */ } SDL_CameraSpec; +/** + * The position of camera in relation to system device. + * + * \sa SDL_GetCameraDevicePosition + */ +typedef enum SDL_CameraPosition +{ + SDL_CAMERA_POSITION_UNKNOWN, + SDL_CAMERA_POSITION_FRONT_FACING, + SDL_CAMERA_POSITION_BACK_FACING +} SDL_CameraPosition; + + /** * Use this function to get the number of built-in camera drivers. * @@ -210,6 +223,25 @@ extern DECLSPEC SDL_CameraSpec *SDLCALL SDL_GetCameraDeviceSupportedFormats(SDL_ */ extern DECLSPEC char * SDLCALL SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id); +/** + * Get the position of the camera in relation to the system. + * + * Most platforms will report UNKNOWN, but mobile devices, like phones, can + * often make a distiction between cameras on the front of the device (that + * points towards the user, for taking "selfies") and cameras on the back + * (for filming in the direction the user is facing). + * + * \param instance_id the camera device instance ID + * \returns The position of the camera on the system hardware. + * + * \threadsafety It is safe to call this function from any thread. + * + * \since This function is available since SDL 3.0.0. + * + * \sa SDL_GetCameraDevices + */ +extern DECLSPEC SDL_CameraPosition SDLCALL SDL_GetCameraDevicePosition(SDL_CameraDeviceID instance_id); + /** * Open a video capture device (a "camera"). * @@ -305,16 +337,6 @@ extern DECLSPEC SDL_CameraDeviceID SDLCALL SDL_GetCameraInstanceID(SDL_Camera *c /** * Get the properties associated with an opened camera. * - * The following read-only properties are provided by SDL: - * - * - `SDL_PROP_CAMERA_POSITION_STRING`: the position of the camera in - * relation to the hardware it is connected to. This is currently either - * the string "front" or "back", to signify which side of the user's - * device a camera is on. Future versions of SDL may add other position - * strings. This property is only set if this information can be - * determined by SDL. Most platforms do not set this attribute at all, - * but mobile devices tend to. - * * \param camera the SDL_Camera obtained from SDL_OpenCameraDevice() * \returns a valid property ID on success or 0 on failure; call * SDL_GetError() for more information. @@ -328,8 +350,6 @@ extern DECLSPEC SDL_CameraDeviceID SDLCALL SDL_GetCameraInstanceID(SDL_Camera *c */ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetCameraProperties(SDL_Camera *camera); -#define SDL_PROP_CAMERA_POSITION_STRING "SDL.camera.position" - /** * Get the spec that a camera is using when generating images. * diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index f70cd75f552a9..8710be864183b 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -398,9 +398,8 @@ static int SDLCALL CameraSpecCmp(const void *vpa, const void *vpb) return 0; // apparently, they're equal. } - // The camera backends call this when a new device is plugged in. -SDL_CameraDevice *SDL_AddCameraDevice(const char *name, int num_specs, const SDL_CameraSpec *specs, void *handle) +SDL_CameraDevice *SDL_AddCameraDevice(const char *name, SDL_CameraPosition position, int num_specs, const SDL_CameraSpec *specs, void *handle) { SDL_assert(name != NULL); SDL_assert(num_specs >= 0); @@ -425,6 +424,8 @@ SDL_CameraDevice *SDL_AddCameraDevice(const char *name, int num_specs, const SDL return NULL; } + device->position = position; + device->lock = SDL_CreateMutex(); if (!device->lock) { SDL_free(device->name); @@ -457,7 +458,13 @@ SDL_CameraDevice *SDL_AddCameraDevice(const char *name, int num_specs, const SDL } #if DEBUG_CAMERA - SDL_Log("CAMERA: Adding device ('%s') with %d spec%s%s", name, num_specs, (num_specs == 1) ? "" : "s", (num_specs == 0) ? "" : ":"); + const char *posstr = "unknown position"; + if (position == SDL_CAMERA_POSITION_FRONT_FACING) { + posstr = "front-facing"; + } else if (position == SDL_CAMERA_POSITION_BACK_FACING) { + posstr = "back-facing"; + } + SDL_Log("CAMERA: Adding device '%s' (%s) with %d spec%s%s", name, posstr, num_specs, (num_specs == 1) ? "" : "s", (num_specs == 0) ? "" : ":"); for (int i = 0; i < num_specs; i++) { const SDL_CameraSpec *spec = &device->all_specs[i]; SDL_Log("CAMERA: - fmt=%s, w=%d, h=%d, numerator=%d, denominator=%d", SDL_GetPixelFormatName(spec->format), spec->width, spec->height, spec->interval_numerator, spec->interval_denominator); @@ -658,6 +665,18 @@ char *SDL_GetCameraDeviceName(SDL_CameraDeviceID instance_id) return retval; } +SDL_CameraPosition SDL_GetCameraDevicePosition(SDL_CameraDeviceID instance_id) +{ + SDL_CameraPosition retval = SDL_CAMERA_POSITION_UNKNOWN; + SDL_CameraDevice *device = ObtainPhysicalCameraDevice(instance_id); + if (device) { + retval = device->position; + ReleaseCameraDevice(device); + } + return retval; +} + + SDL_CameraDeviceID *SDL_GetCameraDevices(int *count) { int dummy_count; diff --git a/src/camera/SDL_syscamera.h b/src/camera/SDL_syscamera.h index 82032a563e79d..1ff5271089d9d 100644 --- a/src/camera/SDL_syscamera.h +++ b/src/camera/SDL_syscamera.h @@ -25,14 +25,14 @@ #include "../SDL_hashtable.h" -#define DEBUG_CAMERA 0 +#define DEBUG_CAMERA 1 typedef struct SDL_CameraDevice SDL_CameraDevice; /* Backends should call this as devices are added to the system (such as a USB camera being plugged in), and should also be called for for every device found during DetectDevices(). */ -extern SDL_CameraDevice *SDL_AddCameraDevice(const char *name, int num_specs, const SDL_CameraSpec *specs, void *handle); +extern SDL_CameraDevice *SDL_AddCameraDevice(const char *name, SDL_CameraPosition position, int num_specs, const SDL_CameraSpec *specs, void *handle); /* Backends should call this if an opened camera device is lost. This can happen due to i/o errors, or a device being unplugged, etc. */ @@ -82,6 +82,9 @@ struct SDL_CameraDevice // Human-readable device name. char *name; + // Position of camera (front-facing, back-facing, etc). + SDL_CameraPosition position; + // When refcount hits zero, we destroy the device object. SDL_AtomicInt refcount; diff --git a/src/camera/android/SDL_camera_android.c b/src/camera/android/SDL_camera_android.c index 5da1cc0cbe5bc..621f7c7dcaf81 100644 --- a/src/camera/android/SDL_camera_android.c +++ b/src/camera/android/SDL_camera_android.c @@ -575,7 +575,7 @@ static void ANDROIDCAMERA_FreeDeviceHandle(SDL_CameraDevice *device) } } -static void GatherCameraSpecs(const char *devid, CameraFormatAddData *add_data, char **fullname, const char **posstr) +static void GatherCameraSpecs(const char *devid, CameraFormatAddData *add_data, char **fullname, SDL_CameraPosition *position) { SDL_zerop(add_data); @@ -608,16 +608,15 @@ static void GatherCameraSpecs(const char *devid, CameraFormatAddData *add_data, } } - *posstr = NULL; ACameraMetadata_const_entry posentry; if (pACameraMetadata_getConstEntry(metadata, ACAMERA_LENS_FACING, &posentry) == ACAMERA_OK) { // ignore this if it fails. if (*posentry.data.u8 == ACAMERA_LENS_FACING_FRONT) { - *posstr = "front"; + *position = SDL_CAMERA_POSITION_FRONT_FACING; if (!*fullname) { *fullname = SDL_strdup("Front-facing camera"); } } else if (*posentry.data.u8 == ACAMERA_LENS_FACING_BACK) { - *posstr = "back"; + *position = SDL_CAMERA_POSITION_BACK_FACING; if (!*fullname) { *fullname = SDL_strdup("Back-facing camera"); } @@ -680,22 +679,16 @@ static void MaybeAddDevice(const char *devid) return; // already have this one. } - const char *posstr = NULL; + SDL_CameraPosition position = SDL_CAMERA_POSITION_UNKNOWN; char *fullname = NULL; CameraFormatAddData add_data; - GatherCameraSpecs(devid, &add_data, &fullname, &posstr); + GatherCameraSpecs(devid, &add_data, &fullname, &position); if (add_data.num_specs > 0) { char *namecpy = SDL_strdup(devid); if (namecpy) { - SDL_CameraDevice *device = SDL_AddCameraDevice(fullname, add_data.num_specs, add_data.specs, namecpy); + SDL_CameraDevice *device = SDL_AddCameraDevice(fullname, position, add_data.num_specs, add_data.specs, namecpy); if (!device) { SDL_free(namecpy); - } else if (device && posstr) { - SDL_Camera *camera = (SDL_Camera *) device; // currently there's no separation between physical and logical device. - SDL_PropertiesID props = SDL_GetCameraProperties(camera); - if (props) { - SDL_SetStringProperty(props, SDL_PROP_CAMERA_POSITION_STRING, posstr); - } } } } diff --git a/src/camera/coremedia/SDL_camera_coremedia.m b/src/camera/coremedia/SDL_camera_coremedia.m index 4d2e40f351f1f..049c4a4065d13 100644 --- a/src/camera/coremedia/SDL_camera_coremedia.m +++ b/src/camera/coremedia/SDL_camera_coremedia.m @@ -399,24 +399,15 @@ static void MaybeAddDevice(AVCaptureDevice *avdevice) CameraFormatAddData add_data; GatherCameraSpecs(avdevice, &add_data); if (add_data.num_specs > 0) { - SDL_CameraDevice *device = SDL_AddCameraDevice(avdevice.localizedName.UTF8String, add_data.num_specs, add_data.specs, (void *) CFBridgingRetain(avdevice)); - if (device) { - const char *posstr = NULL; - if (avdevice.position == AVCaptureDevicePositionFront) { - posstr = "front"; - } else if (avdevice.position == AVCaptureDevicePositionBack) { - posstr = "back"; - } - - if (posstr) { - SDL_Camera *camera = (SDL_Camera *) device; // currently there's no separation between physical and logical device. - SDL_PropertiesID props = SDL_GetCameraProperties(camera); - if (props) { - SDL_SetStringProperty(props, SDL_PROP_CAMERA_POSITION_STRING, posstr); - } - } + SDL_CameraPosition position = SDL_CAMERA_POSITION_UNKNOWN; + if (avdevice.position == AVCaptureDevicePositionFront) { + position = SDL_CAMERA_POSITION_FRONT_FACING; + } else if (avdevice.position == AVCaptureDevicePositionBack) { + position = SDL_CAMERA_POSITION_BACK_FACING; } + SDL_AddCameraDevice(avdevice.localizedName.UTF8String, position, add_data.num_specs, add_data.specs, (void *) CFBridgingRetain(avdevice)); } + SDL_free(add_data.specs); } diff --git a/src/camera/emscripten/SDL_camera_emscripten.c b/src/camera/emscripten/SDL_camera_emscripten.c index 45be949692181..9a605a7527904 100644 --- a/src/camera/emscripten/SDL_camera_emscripten.c +++ b/src/camera/emscripten/SDL_camera_emscripten.c @@ -228,7 +228,7 @@ static void EMSCRIPTENCAMERA_DetectDevices(void) // will pop up a user permission dialog warning them we're trying to access the camera, and we generally // don't want that during SDL_Init(). if (supported) { - SDL_AddCameraDevice("Web browser's camera", 0, NULL, (void *) (size_t) 0x1); + SDL_AddCameraDevice("Web browser's camera", SDL_CAMERA_POSITION_UNKNOWN, 0, NULL, (void *) (size_t) 0x1); } } diff --git a/src/camera/mediafoundation/SDL_camera_mediafoundation.c b/src/camera/mediafoundation/SDL_camera_mediafoundation.c index d2d79f94cc018..dfbfc57996f82 100644 --- a/src/camera/mediafoundation/SDL_camera_mediafoundation.c +++ b/src/camera/mediafoundation/SDL_camera_mediafoundation.c @@ -777,7 +777,7 @@ static void MaybeAddDevice(IMFActivate *activation) CameraFormatAddData add_data; GatherCameraSpecs(source, &add_data); if (add_data.num_specs > 0) { - SDL_AddCameraDevice(name, add_data.num_specs, add_data.specs, symlink); + SDL_AddCameraDevice(name, SDL_CAMERA_POSITION_UNKNOWN, add_data.num_specs, add_data.specs, symlink); } SDL_free(add_data.specs); IMFActivate_ShutdownObject(activation); diff --git a/src/camera/v4l2/SDL_camera_v4l2.c b/src/camera/v4l2/SDL_camera_v4l2.c index 0e2915910fbff..4a94b2563b04f 100644 --- a/src/camera/v4l2/SDL_camera_v4l2.c +++ b/src/camera/v4l2/SDL_camera_v4l2.c @@ -778,7 +778,7 @@ static void MaybeAddDevice(const char *path) if (handle->path) { handle->bus_info = SDL_strdup((char *)vcap.bus_info); if (handle->bus_info) { - if (SDL_AddCameraDevice((const char *) vcap.card, add_data.num_specs, add_data.specs, handle)) { + if (SDL_AddCameraDevice((const char *) vcap.card, SDL_CAMERA_POSITION_UNKNOWN, add_data.num_specs, add_data.specs, handle)) { SDL_free(add_data.specs); return; // good to go. } From db8caa029baf96d68cb8c4984a68dd47e344454a Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 19 Feb 2024 22:38:23 -0500 Subject: [PATCH 060/220] camera: Added new function with gendynapi.py. --- src/dynapi/SDL_dynapi.sym | 1 + src/dynapi/SDL_dynapi_overrides.h | 1 + src/dynapi/SDL_dynapi_procs.h | 1 + 3 files changed, 3 insertions(+) diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index c97153b87f758..93d0f32ebbc1a 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -971,6 +971,7 @@ SDL3_0.0.0 { SDL_ReleaseCameraFrame; SDL_CloseCamera; SDL_GetCameraPermissionState; + SDL_GetCameraDevicePosition; # 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 9ee787ef8f550..c116c68f6135f 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -996,3 +996,4 @@ #define SDL_ReleaseCameraFrame SDL_ReleaseCameraFrame_REAL #define SDL_CloseCamera SDL_CloseCamera_REAL #define SDL_GetCameraPermissionState SDL_GetCameraPermissionState_REAL +#define SDL_GetCameraDevicePosition SDL_GetCameraDevicePosition_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index bcff07cee78e5..2ea4e36b04a28 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1021,3 +1021,4 @@ SDL_DYNAPI_PROC(SDL_Surface*,SDL_AcquireCameraFrame,(SDL_Camera *a, Uint64 *b),( SDL_DYNAPI_PROC(int,SDL_ReleaseCameraFrame,(SDL_Camera *a, SDL_Surface *b),(a,b),return) SDL_DYNAPI_PROC(void,SDL_CloseCamera,(SDL_Camera *a),(a),) SDL_DYNAPI_PROC(int,SDL_GetCameraPermissionState,(SDL_Camera *a),(a),return) +SDL_DYNAPI_PROC(SDL_CameraPosition,SDL_GetCameraDevicePosition,(SDL_CameraDeviceID a),(a),return) From 6c080717f28b050b5407fa7004ef2be2772731cb Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 19 Feb 2024 23:51:30 -0500 Subject: [PATCH 061/220] camera: Reset permissions to undecided when closing camera. Otherwise, the permission-granted event will not fire when reopened. --- src/camera/SDL_camera.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/camera/SDL_camera.c b/src/camera/SDL_camera.c index 8710be864183b..f0e58fc16ae2d 100644 --- a/src/camera/SDL_camera.c +++ b/src/camera/SDL_camera.c @@ -261,6 +261,7 @@ static void ClosePhysicalCameraDevice(SDL_CameraDevice *device) SDL_aligned_free(device->zombie_pixels); + device->permission = 0; device->zombie_pixels = NULL; device->filled_output_surfaces.next = NULL; device->empty_output_surfaces.next = NULL; From 6296677bc9355f178cef152ab4d542ebd81eb0f0 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 19 Feb 2024 23:52:13 -0500 Subject: [PATCH 062/220] camera: Fixed Android hotplug. --- src/camera/android/SDL_camera_android.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/camera/android/SDL_camera_android.c b/src/camera/android/SDL_camera_android.c index 621f7c7dcaf81..f96990eef5907 100644 --- a/src/camera/android/SDL_camera_android.c +++ b/src/camera/android/SDL_camera_android.c @@ -376,18 +376,18 @@ static void onImageAvailable(void *context, AImageReader *reader) static void onDisconnected(void *context, ACameraDevice *device) { - // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; #if DEBUG_CAMERA SDL_Log("CAMERA: CB onDisconnected"); #endif + SDL_CameraDeviceDisconnected((SDL_CameraDevice *) context); } static void onError(void *context, ACameraDevice *device, int error) { - // SDL_CameraDevice *_this = (SDL_CameraDevice *) context; #if DEBUG_CAMERA SDL_Log("CAMERA: CB onError"); #endif + SDL_CameraDeviceDisconnected((SDL_CameraDevice *) context); } static void onClosed(void* context, ACameraCaptureSession *session) @@ -717,8 +717,15 @@ static void onCameraUnavailable(void *context, const char *cameraId) #if DEBUG_CAMERA SDL_Log("CAMERA: CB onCameraUnvailable('%s')", cameraId); #endif + SDL_assert(cameraId != NULL); - SDL_CameraDeviceDisconnected(SDL_FindPhysicalCameraDeviceByCallback(FindAndroidCameraDeviceByID, (void *) cameraId)); + + // THIS CALLBACK FIRES WHEN YOU OPEN THE DEVICE YOURSELF. :( + // Make sure we don't have the device opened, in which case onDisconnected will fire instead if actually lost. + SDL_CameraDevice *device = SDL_FindPhysicalCameraDeviceByCallback(FindAndroidCameraDeviceByID, (void *) cameraId); + if (device && !device->hidden) { + SDL_CameraDeviceDisconnected(device); + } } static const ACameraManager_AvailabilityCallbacks camera_availability_listener = { From f59c66a97f9a42c6db8b29c786a05404d0a10243 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 19 Feb 2024 23:52:43 -0500 Subject: [PATCH 063/220] testcamera: Allow app to flip between a front and back camera. --- test/testcamera.c | 78 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/test/testcamera.c b/test/testcamera.c index 9fc04c534b9db..50c690aa41e6b 100644 --- a/test/testcamera.c +++ b/test/testcamera.c @@ -23,6 +23,8 @@ static SDL_CameraSpec spec; static SDL_Texture *texture = NULL; static SDL_bool texture_updated = SDL_FALSE; static SDL_Surface *frame_current = NULL; +static SDL_CameraDeviceID front_camera = 0; +static SDL_CameraDeviceID back_camera = 0; int SDL_AppInit(int argc, char *argv[]) { @@ -66,12 +68,23 @@ int SDL_AppInit(int argc, char *argv[]) SDL_Log("Saw %d camera devices.", devcount); for (i = 0; i < devcount; i++) { - char *name = SDL_GetCameraDeviceName(devices[i]); - SDL_Log(" - Camera #%d: %s", i, name); + const SDL_CameraDeviceID device = devices[i]; + char *name = SDL_GetCameraDeviceName(device); + const SDL_CameraPosition position = SDL_GetCameraDevicePosition(device); + const char *posstr = ""; + if (position == SDL_CAMERA_POSITION_FRONT_FACING) { + front_camera = device; + posstr = "[front-facing] "; + } else if (position == SDL_CAMERA_POSITION_BACK_FACING) { + back_camera = device; + posstr = "[back-facing] "; + } + SDL_Log(" - Camera #%d: %s %s", i, posstr, name); SDL_free(name); + } - const SDL_CameraDeviceID devid = devices[0]; /* just take the first one. */ + const SDL_CameraDeviceID devid = front_camera ? front_camera : devices[0]; /* no front-facing? just take the first one. */ SDL_free(devices); if (!devid) { @@ -96,6 +109,51 @@ int SDL_AppInit(int argc, char *argv[]) return 0; /* start the main app loop. */ } + +static int FlipCamera(void) +{ + static Uint64 last_flip = 0; + if ((SDL_GetTicks() - last_flip) < 3000) { /* must wait at least 3 seconds between flips. */ + return 0; + } + + if (camera) { + const SDL_CameraDeviceID current = SDL_GetCameraInstanceID(camera); + SDL_CameraDeviceID nextcam = 0; + if (current == front_camera) { + nextcam = back_camera; + } else if (current == back_camera) { + nextcam = front_camera; + } + + if (nextcam) { + SDL_Log("Flip camera!"); + + if (frame_current) { + SDL_ReleaseCameraFrame(camera, frame_current); + frame_current = NULL; + } + + SDL_CloseCamera(camera); + + if (texture) { + SDL_DestroyTexture(texture); + texture = NULL; /* will rebuild when new camera is approved. */ + } + + camera = SDL_OpenCameraDevice(nextcam, NULL); + if (!camera) { + SDL_Log("Failed to open camera device: %s", SDL_GetError()); + return -1; + } + + last_flip = SDL_GetTicks(); + } + } + + return 0; +} + int SDL_AppEvent(const SDL_Event *event) { switch (event->type) { @@ -104,21 +162,30 @@ int SDL_AppEvent(const SDL_Event *event) if (sym == SDLK_ESCAPE || sym == SDLK_AC_BACK) { SDL_Log("Key : Escape!"); return 1; + } else if (sym == SDLK_SPACE) { + FlipCamera(); + return 0; } break; } + case SDL_EVENT_MOUSE_BUTTON_DOWN: + /* !!! FIXME: only flip if clicked in the area of a "flip" icon. */ + return FlipCamera(); + case SDL_EVENT_QUIT: SDL_Log("Ctlr+C : Quit!"); return 1; case SDL_EVENT_CAMERA_DEVICE_APPROVED: + SDL_Log("Camera approved!"); if (SDL_GetCameraFormat(camera, &spec) < 0) { SDL_Log("Couldn't get camera spec: %s", SDL_GetError()); return -1; } /* Create texture with appropriate format */ + SDL_assert(texture == NULL); texture = SDL_CreateTexture(renderer, spec.format, SDL_TEXTUREACCESS_STATIC, spec.width, spec.height); if (texture == NULL) { SDL_Log("Couldn't create texture: %s", SDL_GetError()); @@ -127,6 +194,7 @@ int SDL_AppEvent(const SDL_Event *event) break; case SDL_EVENT_CAMERA_DEVICE_DENIED: + SDL_Log("Camera denied!"); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Camera permission denied!", "User denied access to the camera!", window); return -1; } @@ -143,7 +211,7 @@ int SDL_AppIterate(void) int win_w, win_h, tw, th; SDL_FRect d; Uint64 timestampNS = 0; - SDL_Surface *frame_next = SDL_AcquireCameraFrame(camera, ×tampNS); + SDL_Surface *frame_next = camera ? SDL_AcquireCameraFrame(camera, ×tampNS) : NULL; #if 0 if (frame_next) { @@ -180,6 +248,8 @@ int SDL_AppIterate(void) SDL_RenderTexture(renderer, texture, NULL, &d); } + /* !!! FIXME: Render a "flip" icon if front_camera and back_camera are both != 0. */ + SDL_RenderPresent(renderer); return 0; /* keep iterating. */ From 31fe061ab5777e54a384e912c085ca2e8dbaeb78 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Tue, 20 Feb 2024 20:57:27 +0000 Subject: [PATCH 064/220] Sync SDL3 wiki -> header --- include/SDL3/SDL_camera.h | 143 ++++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 68 deletions(-) diff --git a/include/SDL3/SDL_camera.h b/include/SDL3/SDL_camera.h index e4ac69a88ac14..1b81edad94d16 100644 --- a/include/SDL3/SDL_camera.h +++ b/include/SDL3/SDL_camera.h @@ -119,8 +119,8 @@ extern DECLSPEC int SDLCALL SDL_GetNumCameraDrivers(void); * * \param index the index of the camera driver; the value ranges from 0 to * SDL_GetNumCameraDrivers() - 1 - * \returns the name of the camera driver at the requested index, or NULL if an - * invalid index was specified. + * \returns the name of the camera driver at the requested index, or NULL if + * an invalid index was specified. * * \threadsafety It is safe to call this function from any thread. * @@ -139,8 +139,8 @@ extern DECLSPEC const char *SDLCALL SDL_GetCameraDriver(int index); * call to this function, of course). As such, you should not modify or free * the returned string. * - * \returns the name of the current camera driver or NULL if no driver has been - * initialized. + * \returns the name of the current camera driver or NULL if no driver has + * been initialized. * * \threadsafety It is safe to call this function from any thread. * @@ -151,10 +151,11 @@ extern DECLSPEC const char *SDLCALL SDL_GetCurrentCameraDriver(void); /** * Get a list of currently connected camera devices. * - * \param count a pointer filled in with the number of camera devices. Can be NULL. - * \returns a 0 terminated array of camera instance IDs which should be - * freed with SDL_free(), or NULL on error; call SDL_GetError() for - * more details. + * \param count a pointer filled in with the number of camera devices. Can be + * NULL. + * \returns a 0 terminated array of camera instance IDs which should be freed + * with SDL_free(), or NULL on error; call SDL_GetError() for more + * details. * * \threadsafety It is safe to call this function from any thread. * @@ -167,19 +168,19 @@ extern DECLSPEC SDL_CameraDeviceID *SDLCALL SDL_GetCameraDevices(int *count); /** * Get the list of native formats/sizes a camera supports. * - * This returns a list of all formats and frame sizes that a specific - * camera can offer. This is useful if your app can accept a variety - * of image formats and sizes and so want to find the optimal spec - * that doesn't require conversion. + * This returns a list of all formats and frame sizes that a specific camera + * can offer. This is useful if your app can accept a variety of image formats + * and sizes and so want to find the optimal spec that doesn't require + * conversion. * * This function isn't strictly required; if you call SDL_OpenCameraDevice * with a NULL spec, SDL will choose a native format for you, and if you * instead specify a desired format, it will transparently convert to the * requested format on your behalf. * - * If `count` is not NULL, it will be filled with the number of elements - * in the returned array. Additionally, the last element of the array - * has all fields set to zero (this element is not included in `count`). + * If `count` is not NULL, it will be filled with the number of elements in + * the returned array. Additionally, the last element of the array has all + * fields set to zero (this element is not included in `count`). * * The returned list is owned by the caller, and should be released with * SDL_free() when no longer needed. @@ -188,14 +189,15 @@ extern DECLSPEC SDL_CameraDeviceID *SDLCALL SDL_GetCameraDevices(int *count); * final element and `*count` set to zero; this is what will happen on * Emscripten builds, since that platform won't tell _anything_ about * available cameras until you've opened one, and won't even tell if there - * _is_ a camera until the user has given you permission to check through - * a scary warning popup. + * _is_ a camera until the user has given you permission to check through a + * scary warning popup. * * \param devid the camera device instance ID to query. - * \param count a pointer filled in with the number of elements in the list. Can be NULL. - * \returns a 0 terminated array of SDL_CameraSpecs, which should be - * freed with SDL_free(), or NULL on error; call SDL_GetError() for - * more details. + * \param count a pointer filled in with the number of elements in the list. + * Can be NULL. + * \returns a 0 terminated array of SDL_CameraSpecs, which should be freed + * with SDL_free(), or NULL on error; call SDL_GetError() for more + * details. * * \threadsafety It is safe to call this function from any thread. * @@ -213,7 +215,8 @@ extern DECLSPEC SDL_CameraSpec *SDLCALL SDL_GetCameraDeviceSupportedFormats(SDL_ * SDL_free() when done with it. * * \param instance_id the camera device instance ID - * \returns Human-readable device name, or NULL on error; call SDL_GetError() for more information. + * \returns Human-readable device name, or NULL on error; call SDL_GetError() + * for more information. * * \threadsafety It is safe to call this function from any thread. * @@ -228,8 +231,8 @@ extern DECLSPEC char * SDLCALL SDL_GetCameraDeviceName(SDL_CameraDeviceID instan * * Most platforms will report UNKNOWN, but mobile devices, like phones, can * often make a distiction between cameras on the front of the device (that - * points towards the user, for taking "selfies") and cameras on the back - * (for filming in the direction the user is facing). + * points towards the user, for taking "selfies") and cameras on the back (for + * filming in the direction the user is facing). * * \param instance_id the camera device instance ID * \returns The position of the camera on the system hardware. @@ -249,24 +252,24 @@ extern DECLSPEC SDL_CameraPosition SDLCALL SDL_GetCameraDevicePosition(SDL_Camer * directly support it, it will convert data seamlessly to the requested * format. This might incur overhead, including scaling of image data. * - * If you would rather accept whatever format the device offers, you can - * pass a NULL spec here and it will choose one for you (and you can use + * If you would rather accept whatever format the device offers, you can pass + * a NULL spec here and it will choose one for you (and you can use * SDL_Surface's conversion/scaling functions directly if necessary). * - * You can call SDL_GetCameraFormat() to get the actual data format if - * passing a NULL spec here. You can see the exact specs a device can - * support without conversion with SDL_GetCameraSupportedSpecs(). + * You can call SDL_GetCameraFormat() to get the actual data format if passing + * a NULL spec here. You can see the exact specs a device can support without + * conversion with SDL_GetCameraSupportedSpecs(). * - * SDL will not attempt to emulate framerate; it will try to set the - * hardware to the rate closest to the requested speed, but it won't - * attempt to limit or duplicate frames artificially; call - * SDL_GetCameraFormat() to see the actual framerate of the opened the device, - * and check your timestamps if this is crucial to your app! + * SDL will not attempt to emulate framerate; it will try to set the hardware + * to the rate closest to the requested speed, but it won't attempt to limit + * or duplicate frames artificially; call SDL_GetCameraFormat() to see the + * actual framerate of the opened the device, and check your timestamps if + * this is crucial to your app! * - * Note that the camera is not usable until the user approves its use! On - * some platforms, the operating system will prompt the user to permit access - * to the camera, and they can choose Yes or No at that point. Until they do, - * the camera will not be usable. The app should either wait for an + * Note that the camera is not usable until the user approves its use! On some + * platforms, the operating system will prompt the user to permit access to + * the camera, and they can choose Yes or No at that point. Until they do, the + * camera will not be usable. The app should either wait for an * SDL_EVENT_CAMERA_DEVICE_APPROVED (or SDL_EVENT_CAMERA_DEVICE_DENIED) event, * or poll SDL_IsCameraApproved() occasionally until it returns non-zero. On * platforms that don't require explicit user approval (and perhaps in places @@ -274,7 +277,8 @@ extern DECLSPEC SDL_CameraPosition SDLCALL SDL_GetCameraDevicePosition(SDL_Camer * immediately, but it might come seconds, minutes, or hours later! * * \param instance_id the camera device instance ID - * \param spec The desired format for data the device will provide. Can be NULL. + * \param spec The desired format for data the device will provide. Can be + * NULL. * \returns device, or NULL on failure; call SDL_GetError() for more * information. * @@ -290,8 +294,8 @@ extern DECLSPEC SDL_Camera *SDLCALL SDL_OpenCameraDevice(SDL_CameraDeviceID inst /** * Query if camera access has been approved by the user. * - * Cameras will not function between when the device is opened by the app - * and when the user permits access to the hardware. On some platforms, this + * Cameras will not function between when the device is opened by the app and + * when the user permits access to the hardware. On some platforms, this * presents as a popup dialog where the user has to explicitly approve access; * on others the approval might be implicit and not alert the user at all. * @@ -308,7 +312,8 @@ extern DECLSPEC SDL_Camera *SDLCALL SDL_OpenCameraDevice(SDL_CameraDeviceID inst * SDL_CloseCamera() to dispose of it. * * \param camera the opened camera device to query - * \returns -1 if user denied access to the camera, 1 if user approved access, 0 if no decision has been made yet. + * \returns -1 if user denied access to the camera, 1 if user approved access, + * 0 if no decision has been made yet. * * \threadsafety It is safe to call this function from any thread. * @@ -353,8 +358,8 @@ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetCameraProperties(SDL_Camera *cam /** * Get the spec that a camera is using when generating images. * - * Note that this might not be the native format of the hardware, as SDL - * might be converting to this format behind the scenes. + * Note that this might not be the native format of the hardware, as SDL might + * be converting to this format behind the scenes. * * If the system is waiting for the user to approve access to the camera, as * some platforms require, this will return -1, but this isn't necessarily a @@ -381,31 +386,34 @@ extern DECLSPEC int SDLCALL SDL_GetCameraFormat(SDL_Camera *camera, SDL_CameraSp * The frame is a memory pointer to the image data, whose size and format are * given by the spec requested when opening the device. * - * This is a non blocking API. If there is a frame available, a non-NULL surface is - * returned, and timestampNS will be filled with a non-zero value. + * This is a non blocking API. If there is a frame available, a non-NULL + * surface is returned, and timestampNS will be filled with a non-zero value. * - * Note that an error case can also return NULL, but a NULL by itself is normal - * and just signifies that a new frame is not yet available. Note that even if a - * camera device fails outright (a USB camera is unplugged while in use, etc), SDL - * will send an event separately to notify the app, but continue to provide blank - * frames at ongoing intervals until SDL_CloseCamera() is called, so real - * failure here is almost always an out of memory condition. + * Note that an error case can also return NULL, but a NULL by itself is + * normal and just signifies that a new frame is not yet available. Note that + * even if a camera device fails outright (a USB camera is unplugged while in + * use, etc), SDL will send an event separately to notify the app, but + * continue to provide blank frames at ongoing intervals until + * SDL_CloseCamera() is called, so real failure here is almost always an out + * of memory condition. * - * After use, the frame should be released with SDL_ReleaseCameraFrame(). If you - * don't do this, the system may stop providing more video! + * After use, the frame should be released with SDL_ReleaseCameraFrame(). If + * you don't do this, the system may stop providing more video! * - * Do not call SDL_FreeSurface() on the returned surface! It must be given back - * to the camera subsystem with SDL_ReleaseCameraFrame! + * Do not call SDL_FreeSurface() on the returned surface! It must be given + * back to the camera subsystem with SDL_ReleaseCameraFrame! * * If the system is waiting for the user to approve access to the camera, as - * some platforms require, this will return NULL (no frames available); you should - * either wait for an SDL_EVENT_CAMERA_DEVICE_APPROVED (or + * some platforms require, this will return NULL (no frames available); you + * should either wait for an SDL_EVENT_CAMERA_DEVICE_APPROVED (or * SDL_EVENT_CAMERA_DEVICE_DENIED) event, or poll SDL_IsCameraApproved() * occasionally until it returns non-zero. * * \param camera opened camera device - * \param timestampNS a pointer filled in with the frame's timestamp, or 0 on error. Can be NULL. - * \returns A new frame of video on success, NULL if none is currently available. + * \param timestampNS a pointer filled in with the frame's timestamp, or 0 on + * error. Can be NULL. + * \returns A new frame of video on success, NULL if none is currently + * available. * * \threadsafety It is safe to call this function from any thread. * @@ -422,9 +430,9 @@ extern DECLSPEC SDL_Surface * SDLCALL SDL_AcquireCameraFrame(SDL_Camera *camera, * * This function _must_ be called only on surface objects returned by * SDL_AcquireCameraFrame(). This function should be called as quickly as - * possible after acquisition, as SDL keeps a small FIFO queue of surfaces - * for video frames; if surfaces aren't released in a timely manner, SDL - * may drop upcoming video frames from the camera. + * possible after acquisition, as SDL keeps a small FIFO queue of surfaces for + * video frames; if surfaces aren't released in a timely manner, SDL may drop + * upcoming video frames from the camera. * * If the app needs to keep the surface for a significant time, they should * make a copy of it and release the original. @@ -446,14 +454,13 @@ extern DECLSPEC SDL_Surface * SDLCALL SDL_AcquireCameraFrame(SDL_Camera *camera, extern DECLSPEC int SDLCALL SDL_ReleaseCameraFrame(SDL_Camera *camera, SDL_Surface *frame); /** - * Use this function to shut down camera processing and close the - * camera device. + * Use this function to shut down camera processing and close the camera + * device. * * \param camera opened camera device * - * \threadsafety It is safe to call this function from any thread, but - * no thread may reference `device` once this function - * is called. + * \threadsafety It is safe to call this function from any thread, but no + * thread may reference `device` once this function is called. * * \since This function is available since SDL 3.0.0. * From 26ffbe43c246b71004821d2fcc91d332f2eb2d76 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 20 Feb 2024 16:09:02 -0500 Subject: [PATCH 065/220] camera: turn OFF `DEBUG_CAMERA` debug logging. --- src/camera/SDL_syscamera.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/camera/SDL_syscamera.h b/src/camera/SDL_syscamera.h index 1ff5271089d9d..2d1851c75297d 100644 --- a/src/camera/SDL_syscamera.h +++ b/src/camera/SDL_syscamera.h @@ -25,7 +25,7 @@ #include "../SDL_hashtable.h" -#define DEBUG_CAMERA 1 +#define DEBUG_CAMERA 0 typedef struct SDL_CameraDevice SDL_CameraDevice; From ecfbb6719f4812d29e2f3d86ce3c3d6e1d67518e Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Wed, 21 Feb 2024 00:04:41 +0100 Subject: [PATCH 066/220] SDL_test: support SDL_INIT_CAMERA for SDL_CommonInit and SDL_CommonQuit --- src/test/SDL_test_common.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index 4b2502e2b246d..40c59e85023c4 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -1567,6 +1567,10 @@ SDL_bool SDLTest_CommonInit(SDLTest_CommonState *state) } } + if (state->flags & SDL_INIT_CAMERA) { + SDL_InitSubSystem(SDL_INIT_CAMERA); + } + return SDL_TRUE; } @@ -2562,6 +2566,9 @@ void SDLTest_CommonQuit(SDLTest_CommonState *state) } SDL_free(state->windows); } + if (state->flags & SDL_INIT_CAMERA) { + SDL_QuitSubSystem(SDL_INIT_CAMERA); + } if (state->flags & SDL_INIT_VIDEO) { SDL_QuitSubSystem(SDL_INIT_VIDEO); } From cbf0b1ce81b05cf2c8937494dd766a9fc2cc987f Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Wed, 21 Feb 2024 00:05:32 +0100 Subject: [PATCH 067/220] testcamera: create window and renderer through test library --- test/testcamera.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/test/testcamera.c b/test/testcamera.c index 50c690aa41e6b..d6e971bb88d1d 100644 --- a/test/testcamera.c +++ b/test/testcamera.c @@ -32,30 +32,36 @@ int SDL_AppInit(int argc, char *argv[]) int i; /* Initialize test framework */ - state = SDLTest_CommonCreateState(argv, 0); - if (state == NULL) { + state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO | SDL_INIT_CAMERA); + if (!state) { return -1; } /* Enable standard application logging */ SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); + if (!SDLTest_CommonDefaultArgs(state, argc, argv)) { + return -1; + } + + state->num_windows = 1; + /* Load the SDL library */ - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_CAMERA) < 0) { + if (!SDLTest_CommonInit(state)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError()); return -1; } - window = SDL_CreateWindow("Local Video", 1000, 800, 0); - if (window == NULL) { + window = state->windows[0]; + if (!window) { SDL_Log("Couldn't create window: %s", SDL_GetError()); return -1; } SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); - renderer = SDL_CreateRenderer(window, NULL, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - if (renderer == NULL) { + renderer = state->renderers[0]; + if (!renderer) { /* SDL_Log("Couldn't create renderer: %s", SDL_GetError()); */ return -1; } @@ -81,7 +87,6 @@ int SDL_AppInit(int argc, char *argv[]) } SDL_Log(" - Camera #%d: %s %s", i, posstr, name); SDL_free(name); - } const SDL_CameraDeviceID devid = front_camera ? front_camera : devices[0]; /* no front-facing? just take the first one. */ @@ -91,7 +96,7 @@ int SDL_AppInit(int argc, char *argv[]) SDL_Log("No cameras available?"); return -1; } - + SDL_CameraSpec *pspec = NULL; #if 0 /* just for edge-case testing purposes, ignore. */ pspec = &spec; @@ -187,7 +192,7 @@ int SDL_AppEvent(const SDL_Event *event) /* Create texture with appropriate format */ SDL_assert(texture == NULL); texture = SDL_CreateTexture(renderer, spec.format, SDL_TEXTUREACCESS_STATIC, spec.width, spec.height); - if (texture == NULL) { + if (!texture) { SDL_Log("Couldn't create texture: %s", SDL_GetError()); return -1; } @@ -207,7 +212,7 @@ int SDL_AppIterate(void) SDL_SetRenderDrawColor(renderer, 0x99, 0x99, 0x99, 255); SDL_RenderClear(renderer); - if (texture != NULL) { /* if not NULL, camera is ready to go. */ + if (texture) { /* if not NULL, camera is ready to go. */ int win_w, win_h, tw, th; SDL_FRect d; Uint64 timestampNS = 0; @@ -262,6 +267,6 @@ void SDL_AppQuit(void) SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); - SDLTest_CommonDestroyState(state); + SDLTest_CommonQuit(state); } From 7eca84d57ed30b320d6c36c34557d48f448cfee0 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Wed, 21 Feb 2024 00:51:40 +0100 Subject: [PATCH 068/220] cmake: don't use target_compile_features when the CMake thinks the compiler does not support it This happens when using an older CMake with a new LLVM toolchain (e.g. Android ndk) --- CMakeLists.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74ed3a10de2da..5818a6e189557 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -372,14 +372,22 @@ if(SDL_SHARED) add_library(SDL3-shared SHARED) add_library(SDL3::SDL3-shared ALIAS SDL3-shared) SDL_AddCommonCompilerFlags(SDL3-shared) - target_compile_features(SDL3-shared PRIVATE c_std_99) + if ("c_std_99" IN_LIST CMAKE_C_COMPILE_FEATURES) + target_compile_features(SDL3-shared PRIVATE c_std_99) + else() + message(WARNING "target_compile_features does not know c_std_99 for C compiler") + endif() endif() if(SDL_STATIC) add_library(SDL3-static STATIC) add_library(SDL3::SDL3-static ALIAS SDL3-static) SDL_AddCommonCompilerFlags(SDL3-static) - target_compile_features(SDL3-static PRIVATE c_std_99) + if ("c_std_99" IN_LIST CMAKE_C_COMPILE_FEATURES) + target_compile_features(SDL3-static PRIVATE c_std_99) + else() + message(WARNING "target_compile_features does not know c_std_99 for C compiler") + endif() endif() if(SDL_TEST_LIBRARY) From 58e6eacf970be48343a962255996b82e479e934e Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Wed, 21 Feb 2024 00:52:04 +0100 Subject: [PATCH 069/220] docs: SDL_INIT_EVERYTHING does not exist anymore --- docs/README-cmake.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/README-cmake.md b/docs/README-cmake.md index 337a41ab089ad..0eef99c7a72dd 100644 --- a/docs/README-cmake.md +++ b/docs/README-cmake.md @@ -275,9 +275,13 @@ file(WRITE main.c [===========================================[ /* START of source modifications */ #include +#include int main(int argc, char *argv[]) { - if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { + (void)argc; + (void)argv; + + if (SDL_Init(SDL_INIT_VIDEO) < 0) { SDL_Log("SDL_Init failed (%s)", SDL_GetError()); return 1; } @@ -314,6 +318,7 @@ int main(int argc, char *argv[]) { SDL_DestroyWindow(window); SDL_Quit(); + return 0; } /* END of source modifications */ From 3b7533f4a291b3b75997c6cc306c91a6775ac597 Mon Sep 17 00:00:00 2001 From: Ozkan Sezer Date: Thu, 22 Feb 2024 00:04:10 +0300 Subject: [PATCH 070/220] SDL_camera_v4l2: allow building against older kernel headers --- src/camera/v4l2/SDL_camera_v4l2.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/camera/v4l2/SDL_camera_v4l2.c b/src/camera/v4l2/SDL_camera_v4l2.c index 4a94b2563b04f..07968923d0471 100644 --- a/src/camera/v4l2/SDL_camera_v4l2.c +++ b/src/camera/v4l2/SDL_camera_v4l2.c @@ -22,6 +22,7 @@ #ifdef SDL_CAMERA_DRIVER_V4L2 +#include #include #include #include // low-level i/o @@ -30,6 +31,12 @@ #include #include +#ifndef V4L2_CAP_DEVICE_CAPS +// device_caps was added to struct v4l2_capability as of kernel 3.4. +#define device_caps reserved[0] +SDL_COMPILE_TIME_ASSERT(v4l2devicecaps, offsetof(struct v4l2_capability,device_caps) == offsetof(struct v4l2_capability,capabilities) + 4); +#endif + #include "../SDL_syscamera.h" #include "../SDL_camera_c.h" #include "../../video/SDL_pixels_c.h" From 4ba6aeee9d6fe224aaf8b7a682647891991969a6 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 19 Feb 2024 08:45:02 -0800 Subject: [PATCH 071/220] A second take on HDR support with an SDR white point and HDR headroom This better reflects how HDR content is actually used, e.g. most content is in the SDR range, with specular highlights and bright details beyond the SDR range, in the HDR headroom. This more closely matches how HDR is handled on Apple platforms, as EDR. This also greatly simplifies application code which no longer has to think about color scaling. SDR content is rendered at the appropriate brightness automatically, and HDR content is scaled to the correct range for the display HDR headroom. --- include/SDL3/SDL_pixels.h | 10 +- include/SDL3/SDL_render.h | 23 +- include/SDL3/SDL_surface.h | 23 +- include/SDL3/SDL_video.h | 15 +- src/dynapi/SDL_dynapi_procs.h | 4 +- src/render/SDL_render.c | 140 ++++++++-- src/render/SDL_sysrender.h | 4 + src/render/direct3d11/SDL_render_d3d11.c | 14 +- src/render/direct3d12/SDL_render_d3d12.c | 20 +- src/render/metal/SDL_render_metal.m | 8 +- src/video/SDL_blit_slow.c | 66 +++-- src/video/SDL_pixels.c | 20 +- src/video/SDL_pixels_c.h | 10 +- src/video/SDL_surface.c | 105 ++++++-- src/video/SDL_sysvideo.h | 5 +- src/video/SDL_video.c | 44 +++- src/video/SDL_yuv.c | 16 +- src/video/SDL_yuv_c.h | 6 +- src/video/cocoa/SDL_cocoamodes.m | 11 +- src/video/uikit/SDL_uikitmodes.m | 9 +- src/video/windows/SDL_windowsmodes.c | 16 +- test/testcolorspace.c | 320 +++-------------------- test/testffmpeg.c | 73 +----- test/testyuv.c | 12 +- 24 files changed, 429 insertions(+), 545 deletions(-) diff --git a/include/SDL3/SDL_pixels.h b/include/SDL3/SDL_pixels.h index e2f238bb1be19..e08ac6315431d 100644 --- a/include/SDL3/SDL_pixels.h +++ b/include/SDL3/SDL_pixels.h @@ -576,16 +576,16 @@ typedef enum SDL_COLOR_RANGE_FULL, SDL_COLOR_PRIMARIES_BT709, SDL_TRANSFER_CHARACTERISTICS_SRGB, - SDL_MATRIX_COEFFICIENTS_UNSPECIFIED, + SDL_MATRIX_COEFFICIENTS_IDENTITY, SDL_CHROMA_LOCATION_NONE), - /* scRGB is a linear colorspace and the default colorspace for floating point surfaces */ - SDL_COLORSPACE_SCRGB = /**< Equivalent to DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 */ + /* This is a linear colorspace and the default colorspace for floating point surfaces. On Windows this is the scRGB colorspace, and on Apple platforms this is kCGColorSpaceExtendedLinearSRGB for EDR content */ + SDL_COLORSPACE_SRGB_LINEAR = /**< Equivalent to DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 */ SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_RGB, SDL_COLOR_RANGE_FULL, SDL_COLOR_PRIMARIES_BT709, SDL_TRANSFER_CHARACTERISTICS_LINEAR, - SDL_MATRIX_COEFFICIENTS_UNSPECIFIED, + SDL_MATRIX_COEFFICIENTS_IDENTITY, SDL_CHROMA_LOCATION_NONE), /* HDR10 is a non-linear HDR colorspace and the default colorspace for 10-bit surfaces */ @@ -594,7 +594,7 @@ typedef enum SDL_COLOR_RANGE_FULL, SDL_COLOR_PRIMARIES_BT2020, SDL_TRANSFER_CHARACTERISTICS_PQ, - SDL_MATRIX_COEFFICIENTS_UNSPECIFIED, + SDL_MATRIX_COEFFICIENTS_IDENTITY, SDL_CHROMA_LOCATION_NONE), SDL_COLORSPACE_JPEG = /**< Equivalent to DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601 */ diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 8d80beee199ef..9a5c1eb3215e8 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -242,9 +242,9 @@ extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window *window, co * is displayed, if you want a software renderer without a window * - `SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER`: an SDL_ColorSpace * value describing the colorspace for output to the display, defaults to - * SDL_COLORSPACE_SRGB. The direct3d11 and direct3d12 renderers support - * SDL_COLORSPACE_SCRGB, which is a linear color space and supports HDR - * output. If you select SDL_COLORSPACE_SCRGB, drawing still uses the sRGB + * SDL_COLORSPACE_SRGB. The direct3d11, direct3d12, and metal renderers support + * SDL_COLORSPACE_SRGB_LINEAR, which is a linear color space and supports HDR + * output. If you select SDL_COLORSPACE_SRGB_LINEAR, drawing still uses the sRGB * colorspace, but values can go beyond 1.0 and float (linear) format * textures can be used for HDR content. * - `SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_BOOLEAN`: true if you want @@ -342,6 +342,9 @@ extern DECLSPEC int SDLCALL SDL_GetRendererInfo(SDL_Renderer *renderer, SDL_Rend * - `SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER`: an SDL_ColorSpace value * describing the colorspace for output to the display, defaults to * SDL_COLORSPACE_SRGB. + * - `SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN`: true if the output colorspace is SDL_COLORSPACE_SRGB_LINEAR and the renderer is showing on a display with HDR enabled. + * - `SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT`: the value of SDR white in the SDL_COLORSPACE_SRGB_LINEAR colorspace. When HDR is enabled, this value is automatically multiplied into the color scale. + * - `SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT`: the additional high dynamic range that can be displayed, in terms of the SDR white point. When HDR is not enabled, this will be 1.0. * - `SDL_PROP_RENDERER_D3D9_DEVICE_POINTER`: the IDirect3DDevice9 associated * with the renderer * - `SDL_PROP_RENDERER_D3D11_DEVICE_POINTER`: the ID3D11Device associated @@ -366,6 +369,9 @@ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetRendererProperties(SDL_Renderer #define SDL_PROP_RENDERER_WINDOW_POINTER "SDL.renderer.window" #define SDL_PROP_RENDERER_SURFACE_POINTER "SDL.renderer.surface" #define SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER "SDL.renderer.output_colorspace" +#define SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN "SDL.renderer.HDR_enabled" +#define SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT "SDL.renderer.SDR_white_point" +#define SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT "SDL.renderer.HDR_headroom" #define SDL_PROP_RENDERER_D3D9_DEVICE_POINTER "SDL.renderer.d3d9.device" #define SDL_PROP_RENDERER_D3D11_DEVICE_POINTER "SDL.renderer.d3d11.device" #define SDL_PROP_RENDERER_D3D12_DEVICE_POINTER "SDL.renderer.d3d12.device" @@ -468,7 +474,7 @@ extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTextureFromSurface(SDL_Renderer * * These are the supported properties: * * - `SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER`: an SDL_ColorSpace value - * describing the texture colorspace, defaults to SDL_COLORSPACE_SCRGB for + * describing the texture colorspace, defaults to SDL_COLORSPACE_SRGB_LINEAR for * floating point textures, SDL_COLORSPACE_HDR10 for 10-bit textures, * SDL_COLORSPACE_SRGB for other RGB textures and SDL_COLORSPACE_JPEG for * YUV textures. @@ -480,6 +486,8 @@ extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTextureFromSurface(SDL_Renderer * * pixels, required * - `SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER`: the height of the texture in * pixels, required + * - `SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT`: for HDR10 and floating point textures, this defines the value of 100% diffuse white, with higher values being displayed in the High Dynamic Range headroom. This defaults to 100 for HDR10 textures and 1.0 for floating point textures. + * - `SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT`: for HDR10 and floating point textures, this defines the maximum dynamic range used by the content, in terms of the SDR white point. This would be equivalent to maxCLL / SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT for HDR10 content. If this is defined, any values outside the range supported by the display will be scaled into the available HDR headroom, otherwise they are clipped. * * With the direct3d11 renderer: * @@ -560,6 +568,8 @@ extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTextureWithProperties(SDL_Rendere #define SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER "access" #define SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER "width" #define SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER "height" +#define SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT "SDR_white_point" +#define SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT "HDR_headroom" #define SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_POINTER "d3d11.texture" #define SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_U_POINTER "d3d11.texture_u" #define SDL_PROP_TEXTURE_CREATE_D3D11_TEXTURE_V_POINTER "d3d11.texture_v" @@ -577,7 +587,6 @@ extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTextureWithProperties(SDL_Rendere #define SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_U_NUMBER "opengles2.texture_u" #define SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_V_NUMBER "opengles2.texture_v" - /** * Get the properties associated with a texture. * @@ -585,6 +594,8 @@ extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTextureWithProperties(SDL_Rendere * * - `SDL_PROP_TEXTURE_COLORSPACE_NUMBER`: an SDL_ColorSpace value describing * the colorspace used by the texture + * - `SDL_PROP_TEXTURE_SDR_WHITE_POINT_FLOAT`: for HDR10 and floating point textures, this defines the value of 100% diffuse white, with higher values being displayed in the High Dynamic Range headroom. This defaults to 100 for HDR10 textures and 1.0 for other textures. + * - `SDL_PROP_TEXTURE_HDR_HEADROOM_FLOAT`: for HDR10 and floating point textures, this defines the maximum dynamic range used by the content, in terms of the SDR white point. If this is defined, any values outside the range supported by the display will be scaled into the available HDR headroom, otherwise they are clipped. This defaults to 1.0 for SDR textures, 4.0 for HDR10 textures, and no default for floating point textures. * * With the direct3d11 renderer: * @@ -646,6 +657,8 @@ extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTextureWithProperties(SDL_Rendere extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetTextureProperties(SDL_Texture *texture); #define SDL_PROP_TEXTURE_COLORSPACE_NUMBER "SDL.texture.colorspace" +#define SDL_PROP_TEXTURE_SDR_WHITE_POINT_FLOAT "SDL.texture.SDR_white_point" +#define SDL_PROP_TEXTURE_HDR_HEADROOM_FLOAT "SDL.texture.HDR_headroom" #define SDL_PROP_TEXTURE_D3D11_TEXTURE_POINTER "SDL.texture.d3d11.texture" #define SDL_PROP_TEXTURE_D3D11_TEXTURE_U_POINTER "SDL.texture.d3d11.texture_u" #define SDL_PROP_TEXTURE_D3D11_TEXTURE_V_POINTER "SDL.texture.d3d11.texture_v" diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index 8e064109a9b1e..0bfcafde0318f 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -206,14 +206,15 @@ extern DECLSPEC void SDLCALL SDL_DestroySurface(SDL_Surface *surface); * The following properties are understood by SDL: * * - `SDL_PROP_SURFACE_COLORSPACE_NUMBER`: an SDL_ColorSpace value describing - * the surface colorspace, defaults to SDL_COLORSPACE_SCRGB for floating + * the surface colorspace, defaults to SDL_COLORSPACE_SRGB_LINEAR for floating * point formats, SDL_COLORSPACE_HDR10 for 10-bit formats, * SDL_COLORSPACE_SRGB for other RGB surfaces and SDL_COLORSPACE_BT709_FULL - * for YUV textures. - * - `SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING`: the tone mapping operator - * used when converting between different colorspaces. Currently this - * supports the form "*=N", where N is a floating point scale factor applied - * in linear space. + * for YUV surfaces. + * - `SDL_PROP_SURFACE_MAXCLL_NUMBER`: MaxCLL (Maximum Content Light Level) indicates the maximum light level of any single pixel (in cd/m2 or nits) of the content. MaxCLL is usually measured off the final delivered content after mastering. If one uses the full light level of the HDR mastering display and adds a hard clip at its maximum value, MaxCLL would be equal to the peak luminance of the mastering monitor. This defaults to 400 for HDR10 surfaces. + * - `SDL_PROP_SURFACE_MAXFALL_NUMBER`: MaxFALL (Maximum Frame Average Light Level) indicates the maximum value of the frame average light level (in cd/m2 or nits) of the content. MaxFALL is calculated by averaging the decoded luminance values of all the pixels within a frame. MaxFALL is usually much lower than MaxCLL. + * - `SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT`: for HDR10 and floating point surfaces, this defines the value of 100% diffuse white, with higher values being displayed in the High Dynamic Range headroom. This defaults to 100 for surfaces using the PQ colorspace and 1.0 for other surfaces. + * - `SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT`: for HDR10 and floating point surfaces, this defines the maximum dynamic range used by the content, in terms of the SDR white point. This defaults to SDL_PROP_SURFACE_MAXCLL_NUMBER / SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, or 4.0, for HDR10 surfaces. + * - `SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING`: the tone mapping operator used when converting between different SDR/HDR ranges. Currently this supports the form "*=N", where N is a floating point scale factor applied in linear space. * * \param surface the SDL_Surface structure to query * \returns a valid property ID on success or 0 on failure; call @@ -227,6 +228,10 @@ extern DECLSPEC void SDLCALL SDL_DestroySurface(SDL_Surface *surface); extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetSurfaceProperties(SDL_Surface *surface); #define SDL_PROP_SURFACE_COLORSPACE_NUMBER "SDL.surface.colorspace" +#define SDL_PROP_SURFACE_MAXCLL_NUMBER "SDL.surface.maxCLL" +#define SDL_PROP_SURFACE_MAXFALL_NUMBER "SDL.surface.maxFALL" +#define SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT "SDL.surface.SDR_white_point" +#define SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT "SDL.surface.HDR_headroom" #define SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING "SDL.surface.tonemap" /** @@ -247,7 +252,7 @@ extern DECLSPEC int SDLCALL SDL_SetSurfaceColorspace(SDL_Surface *surface, SDL_C /** * Get the colorspace used by a surface. * - * The colorspace defaults to SDL_COLORSPACE_SCRGB for floating point formats, + * The colorspace defaults to SDL_COLORSPACE_SRGB_LINEAR for floating point formats, * SDL_COLORSPACE_HDR10 for 10-bit formats, SDL_COLORSPACE_SRGB for other RGB * surfaces and SDL_COLORSPACE_BT709_FULL for YUV textures. * @@ -723,7 +728,7 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_ConvertSurfaceFormat(SDL_Surface *surfa * \sa SDL_ConvertSurface * \sa SDL_CreateSurface */ -extern DECLSPEC SDL_Surface *SDLCALL SDL_ConvertSurfaceFormatAndColorspace(SDL_Surface *surface, Uint32 pixel_format, SDL_Colorspace colorspace); +extern DECLSPEC SDL_Surface *SDLCALL SDL_ConvertSurfaceFormatAndColorspace(SDL_Surface *surface, Uint32 pixel_format, SDL_Colorspace colorspace, SDL_PropertiesID props); /** * Copy a block of pixels of one format to another format. @@ -764,7 +769,7 @@ extern DECLSPEC int SDLCALL SDL_ConvertPixels(int width, int height, Uint32 src_ * * \since This function is available since SDL 3.0.0. */ -extern DECLSPEC int SDLCALL SDL_ConvertPixelsAndColorspace(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch); +extern DECLSPEC int SDLCALL SDL_ConvertPixelsAndColorspace(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch); /** * Premultiply the alpha on a block of pixels. diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h index 6fbae05533f2a..554a90fe71c08 100644 --- a/include/SDL3/SDL_video.h +++ b/include/SDL3/SDL_video.h @@ -355,14 +355,9 @@ extern DECLSPEC SDL_DisplayID SDLCALL SDL_GetPrimaryDisplay(void); * * The following read-only properties are provided by SDL: * - * - `SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN`: true if the display has High - * Dynamic Range enabled - * - `SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT`: the luminance, in nits, that - * SDR white is rendered on this display. If this value is not set or is - * zero, the value 200 is a reasonable default when HDR is enabled. - * - `SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT`: the maximum luminance, in nits, - * of HDR content on this display. If this value is not set or is zero, the - * value 400 is a reasonable default when HDR is enabled. + * - `SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN`: true if the display has HDR headroom above the SDR white point. + * - `SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT`: the value of SDR white in the SDL_COLORSPACE_SRGB_LINEAR colorspace. On Windows this corresponds to the SDR white level in scRGB colorspace, and on Apple platforms this is always 1.0 for EDR content. + * - `SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT`: the additional high dynamic range that can be displayed, in terms of the SDR white point. When HDR is not enabled, this will be 1.0. * * \param displayID the instance ID of the display to query * \returns a valid property ID on success or 0 on failure; call @@ -376,8 +371,8 @@ extern DECLSPEC SDL_DisplayID SDLCALL SDL_GetPrimaryDisplay(void); extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetDisplayProperties(SDL_DisplayID displayID); #define SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN "SDL.display.HDR_enabled" -#define SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT "SDL.display.SDR_white_level" -#define SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT "SDL.display.HDR_white_level" +#define SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT "SDL.display.SDR_white_point" +#define SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT "SDL.display.HDR_headroom" /** * Get the name of a display in UTF-8 encoding. diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 2ea4e36b04a28..b98ba5e406942 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -996,10 +996,10 @@ SDL_DYNAPI_PROC(int,SDL_SetTextureAlphaModFloat,(SDL_Texture *a, float b),(a,b), SDL_DYNAPI_PROC(int,SDL_GetTextureAlphaModFloat,(SDL_Texture *a, float *b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_SetRenderDrawColorFloat,(SDL_Renderer *a, float b, float c, float d, float e),(a,b,c,d,e),return) SDL_DYNAPI_PROC(int,SDL_GetRenderDrawColorFloat,(SDL_Renderer *a, float *b, float *c, float *d, float *e),(a,b,c,d,e),return) -SDL_DYNAPI_PROC(int,SDL_ConvertPixelsAndColorspace,(int a, int b, Uint32 c, SDL_Colorspace d, const void *e, int f, Uint32 g, SDL_Colorspace h, void *i, int j),(a,b,c,d,e,f,g,h,i,j),return) +SDL_DYNAPI_PROC(int,SDL_ConvertPixelsAndColorspace,(int a, int b, Uint32 c, SDL_Colorspace d, SDL_PropertiesID e, const void *f, int g, Uint32 h, SDL_Colorspace i, SDL_PropertiesID j, void *k, int l),(a,b,c,d,e,f,g,h,i,j,k,l),return) SDL_DYNAPI_PROC(int,SDL_SetSurfaceColorspace,(SDL_Surface *a, SDL_Colorspace b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_GetSurfaceColorspace,(SDL_Surface *a, SDL_Colorspace *b),(a,b),return) -SDL_DYNAPI_PROC(SDL_Surface*,SDL_ConvertSurfaceFormatAndColorspace,(SDL_Surface *a, Uint32 b, SDL_Colorspace c),(a,b,c),return) +SDL_DYNAPI_PROC(SDL_Surface*,SDL_ConvertSurfaceFormatAndColorspace,(SDL_Surface *a, Uint32 b, SDL_Colorspace c, SDL_PropertiesID d),(a,b,c,d),return) SDL_DYNAPI_PROC(int,SDL_CopyProperties,(SDL_PropertiesID a, SDL_PropertiesID b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_SetRenderColorScale,(SDL_Renderer *a, float b),(a,b),return) SDL_DYNAPI_PROC(int,SDL_GetRenderColorScale,(SDL_Renderer *a, float *b),(a,b),return) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 92bc539eb5c1b..71c4ede680db5 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -150,7 +150,7 @@ SDL_bool SDL_RenderingLinearSpace(SDL_Renderer *renderer) } else { colorspace = renderer->output_colorspace; } - if (colorspace == SDL_COLORSPACE_SCRGB) { + if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) { return SDL_TRUE; } return SDL_FALSE; @@ -737,6 +737,47 @@ static void UpdateMainViewDimensions(SDL_Renderer *renderer) } } +static void UpdateHDRProperties(SDL_Renderer *renderer) +{ + SDL_DisplayID displayID = SDL_GetDisplayForWindow(renderer->window); + SDL_PropertiesID display_props; + SDL_PropertiesID renderer_props; + + if (!displayID) { + return; + } + + display_props = SDL_GetDisplayProperties(displayID); + if (!display_props) { + return; + } + + renderer_props = SDL_GetRendererProperties(renderer); + if (!renderer_props) { + return; + } + + renderer->color_scale /= renderer->SDR_white_point; + + if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { + renderer->SDR_white_point = SDL_GetFloatProperty(display_props, SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT, 1.0f); + renderer->HDR_headroom = SDL_GetFloatProperty(display_props, SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT, 1.0f); + } else { + renderer->SDR_white_point = 1.0f; + renderer->HDR_headroom = 1.0f; + } + + if (renderer->HDR_headroom > 1.0f) { + SDL_SetBooleanProperty(renderer_props, SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN, SDL_TRUE); + } else { + SDL_SetBooleanProperty(renderer_props, SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN, SDL_FALSE); + } + SDL_SetFloatProperty(renderer_props, SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_point); + SDL_SetFloatProperty(renderer_props, SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT, renderer->HDR_headroom); + + renderer->color_scale *= renderer->SDR_white_point; +} + static int UpdateLogicalPresentation(SDL_Renderer *renderer); @@ -792,8 +833,12 @@ static int SDLCALL SDL_RendererEventWatch(void *userdata, SDL_Event *event) if (!(SDL_GetWindowFlags(window) & SDL_WINDOW_HIDDEN)) { renderer->hidden = SDL_FALSE; } + } else if (event->type == SDL_EVENT_WINDOW_DISPLAY_CHANGED) { + UpdateHDRProperties(renderer); } } + } else if (event->type == SDL_EVENT_DISPLAY_HDR_STATE_CHANGED) { + UpdateHDRProperties(renderer); } return 0; @@ -987,6 +1032,8 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props) renderer->line_method = SDL_GetRenderLineMethod(); + renderer->SDR_white_point = 1.0f; + renderer->HDR_headroom = 1.0f; renderer->color_scale = 1.0f; if (window) { @@ -1008,6 +1055,7 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props) SDL_SetProperty(new_props, SDL_PROP_RENDERER_SURFACE_POINTER, surface); } SDL_SetNumberProperty(new_props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, renderer->output_colorspace); + UpdateHDRProperties(renderer); SDL_SetProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_RENDERER_POINTER, renderer); @@ -1186,9 +1234,15 @@ static Uint32 GetClosestSupportedFormat(SDL_Renderer *renderer, Uint32 format) } } } else if (SDL_ISPIXELFORMAT_10BIT(format) || SDL_ISPIXELFORMAT_FLOAT(format)) { + if (SDL_ISPIXELFORMAT_10BIT(format)) { + for (i = 0; i < renderer->info.num_texture_formats; ++i) { + if (SDL_ISPIXELFORMAT_10BIT(renderer->info.texture_formats[i])) { + return renderer->info.texture_formats[i]; + } + } + } for (i = 0; i < renderer->info.num_texture_formats; ++i) { - if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) && - SDL_ISPIXELFORMAT_FLOAT(renderer->info.texture_formats[i])) { + if (SDL_ISPIXELFORMAT_FLOAT(renderer->info.texture_formats[i])) { return renderer->info.texture_formats[i]; } } @@ -1214,6 +1268,8 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert int w = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, 0); int h = (int)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, 0); SDL_Colorspace default_colorspace; + float SDR_white_point_default = 1.0f; + float HDR_headroom_default = 1.0f; SDL_bool texture_is_fourcc_and_target; CHECK_RENDERER_MAGIC(renderer, NULL); @@ -1271,6 +1327,15 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert } renderer->textures = texture; + if (SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) { + SDR_white_point_default = 100.0f; + HDR_headroom_default = 4.0f; + } else if (SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_TRANSFER_CHARACTERISTICS_LINEAR) { + HDR_headroom_default = 0.0f; + } + texture->SDR_white_point = SDL_GetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, SDR_white_point_default); + texture->HDR_headroom = SDL_GetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, HDR_headroom_default); + /* FOURCC format cannot be used directly by renderer back-ends for target texture */ texture_is_fourcc_and_target = (access == SDL_TEXTUREACCESS_TARGET && SDL_ISPIXELFORMAT_FOURCC(format)); @@ -1335,6 +1400,14 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert } } } + + /* Now set the properties for the new texture */ + props = SDL_GetTextureProperties(texture); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_COLORSPACE_NUMBER, texture->colorspace); + SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_SDR_WHITE_POINT_FLOAT, texture->SDR_white_point); + if (texture->HDR_headroom > 0.0f) { + SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_HDR_HEADROOM_FLOAT, texture->HDR_headroom); + } return texture; } @@ -1359,8 +1432,9 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s int i; Uint32 format = SDL_PIXELFORMAT_UNKNOWN; SDL_Texture *texture; - SDL_PropertiesID props; - SDL_Colorspace colorspace = SDL_COLORSPACE_UNKNOWN; + SDL_PropertiesID surface_props, props; + SDL_Colorspace surface_colorspace = SDL_COLORSPACE_UNKNOWN; + SDL_Colorspace texture_colorspace = SDL_COLORSPACE_UNKNOWN; CHECK_RENDERER_MAGIC(renderer, NULL); @@ -1386,9 +1460,10 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s } } - if (SDL_GetSurfaceColorspace(surface, &colorspace) < 0) { + if (SDL_GetSurfaceColorspace(surface, &surface_colorspace) < 0) { return NULL; } + texture_colorspace = surface_colorspace; /* Try to have the best pixel format for the texture */ /* No alpha, but a colorkey => promote to alpha */ @@ -1418,6 +1493,16 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s } } + /* Look for 10-bit pixel formats if needed */ + if (format == SDL_PIXELFORMAT_UNKNOWN && SDL_ISPIXELFORMAT_10BIT(fmt->format)) { + for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) { + if (SDL_ISPIXELFORMAT_10BIT(renderer->info.texture_formats[i])) { + format = renderer->info.texture_formats[i]; + break; + } + } + } + /* Look for floating point pixel formats if needed */ if (format == SDL_PIXELFORMAT_UNKNOWN && (SDL_ISPIXELFORMAT_10BIT(fmt->format) || SDL_ISPIXELFORMAT_FLOAT(fmt->format))) { @@ -1455,17 +1540,29 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s direct_update = SDL_FALSE; } - if ((SDL_COLORSPACETRANSFER(colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ && !SDL_ISPIXELFORMAT_10BIT(format)) || - colorspace == SDL_COLORSPACE_SCRGB) { + if ((SDL_COLORSPACETRANSFER(surface_colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ && !SDL_ISPIXELFORMAT_10BIT(format)) || + surface_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { if (SDL_ISPIXELFORMAT_FLOAT(format)) { - colorspace = SDL_COLORSPACE_SCRGB; + texture_colorspace = SDL_COLORSPACE_SRGB_LINEAR; } else { - colorspace = SDL_COLORSPACE_SRGB; + texture_colorspace = SDL_COLORSPACE_SRGB; } } + if (surface->flags & SDL_SURFACE_USES_PROPERTIES) { + surface_props = SDL_GetSurfaceProperties(surface); + } else { + surface_props = 0; + } + props = SDL_CreateProperties(); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, colorspace); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, texture_colorspace); + if (surface_colorspace == texture_colorspace) { + SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, + SDL_GetSurfaceSDRWhitePoint(surface, surface_colorspace)); + } + SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, + SDL_GetSurfaceHDRHeadroom(surface, surface_colorspace)); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, surface->w); @@ -1487,7 +1584,7 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s SDL_Surface *temp = NULL; /* Set up a destination surface for the texture update */ - temp = SDL_ConvertSurfaceFormatAndColorspace(surface, format, colorspace); + temp = SDL_ConvertSurfaceFormatAndColorspace(surface, format, texture_colorspace, surface_props); if (temp) { SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch); SDL_DestroySurface(temp); @@ -2888,7 +2985,7 @@ int SDL_SetRenderColorScale(SDL_Renderer *renderer, float scale) { CHECK_RENDERER_MAGIC(renderer, -1); - renderer->color_scale = scale; + renderer->color_scale = scale * renderer->SDR_white_point; return 0; } @@ -2897,7 +2994,7 @@ int SDL_GetRenderColorScale(SDL_Renderer *renderer, float *scale) CHECK_RENDERER_MAGIC(renderer, -1); if (scale) { - *scale = renderer->color_scale; + *scale = renderer->color_scale / renderer->SDR_white_point; } return 0; } @@ -4229,6 +4326,7 @@ int SDL_RenderGeometryRaw(SDL_Renderer *renderer, SDL_Texture *texture, const fl SDL_Surface *SDL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect) { SDL_Rect real_rect; + SDL_Surface *surface; CHECK_RENDERER_MAGIC(renderer, NULL); @@ -4247,7 +4345,19 @@ SDL_Surface *SDL_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect) } } - return renderer->RenderReadPixels(renderer, &real_rect); + surface = renderer->RenderReadPixels(renderer, &real_rect); + if (surface) { + SDL_PropertiesID props = SDL_GetSurfaceProperties(surface); + + if (renderer->target) { + SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, renderer->target->SDR_white_point); + SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, renderer->target->HDR_headroom); + } else { + SDL_SetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, renderer->SDR_white_point); + SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, renderer->HDR_headroom); + } + } + return surface; } static void SDL_RenderApplyWindowShape(SDL_Renderer *renderer) diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 8d12b5d271f2a..613afd665406a 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -64,6 +64,8 @@ struct SDL_Texture { const void *magic; SDL_Colorspace colorspace; /**< The colorspace of the texture */ + float SDR_white_point; /**< The SDR white point for this content */ + float HDR_headroom; /**< The HDR headroom needed by this content */ Uint32 format; /**< The pixel format of the texture */ int access; /**< SDL_TextureAccess */ int w; /**< The width of the texture */ @@ -254,6 +256,8 @@ struct SDL_Renderer SDL_Mutex *target_mutex; SDL_Colorspace output_colorspace; + float SDR_white_point; + float HDR_headroom; float color_scale; SDL_FColor color; /**< Color for drawing operations values */ diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index ff5435000c013..358e1318ec229 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -246,12 +246,12 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 color case SDL_PIXELFORMAT_RGBA64_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT; case SDL_PIXELFORMAT_ARGB8888: - if (colorspace == SDL_COLORSPACE_SCRGB) { + if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) { return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8A8_UNORM; case SDL_PIXELFORMAT_XRGB8888: - if (colorspace == SDL_COLORSPACE_SCRGB) { + if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) { return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8X8_UNORM; @@ -276,12 +276,12 @@ static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(Uint32 format, Uin case SDL_PIXELFORMAT_RGBA64_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT; case SDL_PIXELFORMAT_ARGB8888: - if (colorspace == SDL_COLORSPACE_SCRGB) { + if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) { return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8A8_UNORM; case SDL_PIXELFORMAT_XRGB8888: - if (colorspace == SDL_COLORSPACE_SCRGB) { + if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) { return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8X8_UNORM; @@ -880,7 +880,7 @@ static HRESULT D3D11_CreateSwapChain(SDL_Renderer *renderer, int w, int h) swapChainDesc.Width = w; swapChainDesc.Height = h; switch (renderer->output_colorspace) { - case SDL_COLORSPACE_SCRGB: + case SDL_COLORSPACE_SRGB_LINEAR: swapChainDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; break; case SDL_COLORSPACE_HDR10: @@ -979,7 +979,7 @@ static HRESULT D3D11_CreateSwapChain(SDL_Renderer *renderer, int w, int h) UINT colorspace_support = 0; DXGI_COLOR_SPACE_TYPE colorspace; switch (renderer->output_colorspace) { - case SDL_COLORSPACE_SCRGB: + case SDL_COLORSPACE_SRGB_LINEAR: colorspace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; break; case SDL_COLORSPACE_HDR10: @@ -2699,7 +2699,7 @@ SDL_Renderer *D3D11_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_p SDL_SetupRendererColorspace(renderer, create_props); if (renderer->output_colorspace != SDL_COLORSPACE_SRGB && - renderer->output_colorspace != SDL_COLORSPACE_SCRGB + renderer->output_colorspace != SDL_COLORSPACE_SRGB_LINEAR /*&& renderer->output_colorspace != SDL_COLORSPACE_HDR10*/) { SDL_SetError("Unsupported output colorspace"); SDL_free(renderer); diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index 31018260c766f..3f11bf0ed10ce 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -310,12 +310,12 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 color case SDL_PIXELFORMAT_RGBA64_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT; case SDL_PIXELFORMAT_ARGB8888: - if (colorspace == SDL_COLORSPACE_SCRGB) { + if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) { return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8A8_UNORM; case SDL_PIXELFORMAT_XRGB8888: - if (colorspace == SDL_COLORSPACE_SCRGB) { + if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) { return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8X8_UNORM; @@ -340,12 +340,12 @@ static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(Uint32 format, Uin case SDL_PIXELFORMAT_RGBA64_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT; case SDL_PIXELFORMAT_ARGB8888: - if (colorspace == SDL_COLORSPACE_SCRGB) { + if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) { return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8A8_UNORM; case SDL_PIXELFORMAT_XRGB8888: - if (colorspace == SDL_COLORSPACE_SCRGB) { + if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) { return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; } return DXGI_FORMAT_B8G8R8X8_UNORM; @@ -1218,7 +1218,7 @@ static HRESULT D3D12_CreateSwapChain(SDL_Renderer *renderer, int w, int h) swapChainDesc.Width = w; swapChainDesc.Height = h; switch (renderer->output_colorspace) { - case SDL_COLORSPACE_SCRGB: + case SDL_COLORSPACE_SRGB_LINEAR: swapChainDesc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT; data->renderTargetFormat = DXGI_FORMAT_R16G16B16A16_FLOAT; break; @@ -1282,7 +1282,7 @@ static HRESULT D3D12_CreateSwapChain(SDL_Renderer *renderer, int w, int h) UINT colorspace_support = 0; DXGI_COLOR_SPACE_TYPE colorspace; switch (renderer->output_colorspace) { - case SDL_COLORSPACE_SCRGB: + case SDL_COLORSPACE_SRGB_LINEAR: colorspace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709; break; case SDL_COLORSPACE_HDR10: @@ -2985,11 +2985,7 @@ static SDL_Surface *D3D12_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rec pitchedDesc.Width = (UINT)textureDesc.Width; pitchedDesc.Height = textureDesc.Height; pitchedDesc.Depth = 1; - if (pitchedDesc.Format == DXGI_FORMAT_R8_UNORM) { - bpp = 1; - } else { - bpp = 4; - } + bpp = SDL_BYTESPERPIXEL(D3D12_DXGIFormatToSDLPixelFormat(pitchedDesc.Format)); pitchedDesc.RowPitch = D3D12_Align(pitchedDesc.Width * bpp, D3D12_TEXTURE_DATA_PITCH_ALIGNMENT); SDL_zero(placedTextureDesc); @@ -3155,7 +3151,7 @@ SDL_Renderer *D3D12_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_p SDL_SetupRendererColorspace(renderer, create_props); if (renderer->output_colorspace != SDL_COLORSPACE_SRGB && - renderer->output_colorspace != SDL_COLORSPACE_SCRGB + renderer->output_colorspace != SDL_COLORSPACE_SRGB_LINEAR /*&& renderer->output_colorspace != SDL_COLORSPACE_HDR10*/) { SDL_SetError("Unsupported output colorspace"); SDL_free(renderer); diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index 2da7719ab9fec..9f0bdec46b9cc 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -652,14 +652,14 @@ static int METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL switch (texture->format) { case SDL_PIXELFORMAT_ABGR8888: - if (renderer->output_colorspace == SDL_COLORSPACE_SCRGB) { + if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { pixfmt = MTLPixelFormatRGBA8Unorm_sRGB; } else { pixfmt = MTLPixelFormatRGBA8Unorm; } break; case SDL_PIXELFORMAT_ARGB8888: - if (renderer->output_colorspace == SDL_COLORSPACE_SCRGB) { + if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { pixfmt = MTLPixelFormatBGRA8Unorm_sRGB; } else { pixfmt = MTLPixelFormatBGRA8Unorm; @@ -1895,7 +1895,7 @@ static SDL_MetalView GetWindowView(SDL_Window *window) } #endif if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) { - if (renderer->output_colorspace == SDL_COLORSPACE_SCRGB && scRGB_supported) { + if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR && scRGB_supported) { /* This colorspace is supported */ } else { SDL_SetError("Unsupported output colorspace"); @@ -1963,7 +1963,7 @@ in case we want to use it later (recreating the renderer) #endif #ifndef SDL_PLATFORM_TVOS - if (renderer->output_colorspace == SDL_COLORSPACE_SCRGB) { + if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { if (@available(macos 10.11, iOS 16.0, *)) { layer.wantsExtendedDynamicRangeContent = YES; } else { diff --git a/src/video/SDL_blit_slow.c b/src/video/SDL_blit_slow.c index 6c422c3188884..3d9da4d9f6e63 100644 --- a/src/video/SDL_blit_slow.c +++ b/src/video/SDL_blit_slow.c @@ -370,7 +370,7 @@ static Uint16 float_to_half(float a) return ir; } -static void ReadFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_PixelFormat *fmt, SDL_Colorspace colorspace, +static void ReadFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_PixelFormat *fmt, SDL_Colorspace colorspace, float SDR_white_point, float *outR, float *outG, float *outB, float *outA) { Uint32 pixel; @@ -500,20 +500,19 @@ static void ReadFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_PixelF /* Convert to nits so src and dst are guaranteed to be linear and in the same units */ switch (SDL_COLORSPACETRANSFER(colorspace)) { case SDL_TRANSFER_CHARACTERISTICS_SRGB: - fR = SDL_sRGBtoNits(fR); - fG = SDL_sRGBtoNits(fG); - fB = SDL_sRGBtoNits(fB); + fR = SDL_sRGBtoLinear(fR); + fG = SDL_sRGBtoLinear(fG); + fB = SDL_sRGBtoLinear(fB); break; case SDL_TRANSFER_CHARACTERISTICS_PQ: - fR = SDL_PQtoNits(fR); - fG = SDL_PQtoNits(fG); - fB = SDL_PQtoNits(fB); + fR = SDL_PQtoNits(fR) / SDR_white_point; + fG = SDL_PQtoNits(fG) / SDR_white_point; + fB = SDL_PQtoNits(fB) / SDR_white_point; break; case SDL_TRANSFER_CHARACTERISTICS_LINEAR: - /* Assuming scRGB for now */ - fR = SDL_scRGBtoNits(fR); - fG = SDL_scRGBtoNits(fG); - fB = SDL_scRGBtoNits(fB); + fR /= SDR_white_point; + fG /= SDR_white_point; + fB /= SDR_white_point; break; default: /* Unknown, leave it alone */ @@ -526,7 +525,7 @@ static void ReadFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_PixelF *outA = fA; } -static void WriteFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_PixelFormat *fmt, SDL_Colorspace colorspace, +static void WriteFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_PixelFormat *fmt, SDL_Colorspace colorspace, float SDR_white_point, float fR, float fG, float fB, float fA) { Uint32 R, G, B, A; @@ -535,20 +534,19 @@ static void WriteFloatPixel(Uint8 *pixels, SlowBlitPixelAccess access, SDL_Pixel /* We converted to nits so src and dst are guaranteed to be linear and in the same units */ switch (SDL_COLORSPACETRANSFER(colorspace)) { case SDL_TRANSFER_CHARACTERISTICS_SRGB: - fR = SDL_sRGBfromNits(fR); - fG = SDL_sRGBfromNits(fG); - fB = SDL_sRGBfromNits(fB); + fR = SDL_sRGBfromLinear(fR); + fG = SDL_sRGBfromLinear(fG); + fB = SDL_sRGBfromLinear(fB); break; case SDL_TRANSFER_CHARACTERISTICS_PQ: - fR = SDL_PQfromNits(fR); - fG = SDL_PQfromNits(fG); - fB = SDL_PQfromNits(fB); + fR = SDL_PQfromNits(fR * SDR_white_point); + fG = SDL_PQfromNits(fG * SDR_white_point); + fB = SDL_PQfromNits(fB * SDR_white_point); break; case SDL_TRANSFER_CHARACTERISTICS_LINEAR: - /* Assuming scRGB for now */ - fR = SDL_scRGBfromNits(fR); - fG = SDL_scRGBfromNits(fG); - fB = SDL_scRGBfromNits(fB); + fR *= SDR_white_point; + fG *= SDR_white_point; + fB *= SDR_white_point; break; default: /* Unknown, leave it alone */ @@ -672,6 +670,7 @@ typedef enum { SDL_TONEMAP_NONE, SDL_TONEMAP_LINEAR, + SDL_TONEMAP_PIECEWISE_HDR, } SDL_TonemapOperator; typedef struct @@ -682,6 +681,11 @@ typedef struct struct { float scale; } linear; + + struct + { + float scale; + } piecewise_HDR; } data; } SDL_TonemapContext; @@ -694,6 +698,11 @@ static void ApplyTonemap(SDL_TonemapContext *ctx, float *r, float *g, float *b) *g *= ctx->data.linear.scale; *b *= ctx->data.linear.scale; break; + case SDL_TONEMAP_PIECEWISE_HDR: + *r = (*r <= 1.0f) ? *r : (1.0f + (*r - 1.0f) * ctx->data.piecewise_HDR.scale); + *g = (*g <= 1.0f) ? *g : (1.0f + (*g - 1.0f) * ctx->data.piecewise_HDR.scale); + *b = (*r <= 1.0f) ? *b : (1.0f + (*b - 1.0f) * ctx->data.piecewise_HDR.scale); + break; default: break; } @@ -701,7 +710,7 @@ static void ApplyTonemap(SDL_TonemapContext *ctx, float *r, float *g, float *b) static SDL_bool IsHDRColorspace(SDL_Colorspace colorspace) { - if (colorspace == SDL_COLORSPACE_SCRGB || + if (colorspace == SDL_COLORSPACE_SRGB_LINEAR || SDL_COLORSPACETRANSFER(colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) { return SDL_TRUE; } @@ -732,6 +741,8 @@ void SDL_Blit_Slow_Float(SDL_BlitInfo *info) SDL_Colorspace src_colorspace; SDL_Colorspace dst_colorspace; const float *color_primaries_matrix = NULL; + float SDR_white_point_src = 1.0f; + float SDR_white_point_dst = 1.0f; SDL_TonemapContext tonemap; if (SDL_GetSurfaceColorspace(info->src_surface, &src_colorspace) < 0 || @@ -756,6 +767,9 @@ void SDL_Blit_Slow_Float(SDL_BlitInfo *info) } } + SDR_white_point_src = SDL_GetSurfaceSDRWhitePoint(info->src_surface, src_colorspace); + SDR_white_point_dst = SDL_GetSurfaceSDRWhitePoint(info->dst_surface, dst_colorspace); + src_access = GetPixelAccessMethod(src_fmt); dst_access = GetPixelAccessMethod(dst_fmt); @@ -773,7 +787,7 @@ void SDL_Blit_Slow_Float(SDL_BlitInfo *info) srcx = posx >> 16; src = (info->src + (srcy * info->src_pitch) + (srcx * srcbpp)); - ReadFloatPixel(src, src_access, src_fmt, src_colorspace, &srcR, &srcG, &srcB, &srcA); + ReadFloatPixel(src, src_access, src_fmt, src_colorspace, SDR_white_point_src, &srcR, &srcG, &srcB, &srcA); if (color_primaries_matrix) { SDL_ConvertColorPrimaries(&srcR, &srcG, &srcB, color_primaries_matrix); @@ -787,7 +801,7 @@ void SDL_Blit_Slow_Float(SDL_BlitInfo *info) /* colorkey isn't supported */ } if ((flags & (SDL_COPY_BLEND | SDL_COPY_ADD | SDL_COPY_MOD | SDL_COPY_MUL))) { - ReadFloatPixel(dst, dst_access, dst_fmt, dst_colorspace, &dstR, &dstG, &dstB, &dstA); + ReadFloatPixel(dst, dst_access, dst_fmt, dst_colorspace, SDR_white_point_dst, &dstR, &dstG, &dstB, &dstA); } else { /* don't care */ dstR = dstG = dstB = dstA = 0.0f; @@ -839,7 +853,7 @@ void SDL_Blit_Slow_Float(SDL_BlitInfo *info) break; } - WriteFloatPixel(dst, dst_access, dst_fmt, dst_colorspace, dstR, dstG, dstB, dstA); + WriteFloatPixel(dst, dst_access, dst_fmt, dst_colorspace, SDR_white_point_dst, dstR, dstG, dstB, dstA); posx += incx; dst += dstbpp; diff --git a/src/video/SDL_pixels.c b/src/video/SDL_pixels.c index 88b13ccbdf7b0..1efee366efe5f 100644 --- a/src/video/SDL_pixels.c +++ b/src/video/SDL_pixels.c @@ -706,7 +706,7 @@ SDL_Colorspace SDL_GetDefaultColorspaceForFormat(Uint32 format) if (SDL_ISPIXELFORMAT_FOURCC(format)) { return SDL_COLORSPACE_YUV_DEFAULT; } else if (SDL_ISPIXELFORMAT_FLOAT(format)) { - return SDL_COLORSPACE_SCRGB; + return SDL_COLORSPACE_SRGB_LINEAR; } else if (SDL_ISPIXELFORMAT_10BIT(format)) { return SDL_COLORSPACE_HDR10; } else { @@ -714,30 +714,18 @@ SDL_Colorspace SDL_GetDefaultColorspaceForFormat(Uint32 format) } } -float SDL_scRGBtoNits(float v) -{ - return v * 80.0f; -} - -float SDL_scRGBfromNits(float v) -{ - return v / 80.0f; -} - -float SDL_sRGBtoNits(float v) +float SDL_sRGBtoLinear(float v) { if (v <= 0.04045f) { v = (v / 12.92f); } else { v = SDL_powf((v + 0.055f) / 1.055f, 2.4f); } - return SDL_scRGBtoNits(v); + return v; } -float SDL_sRGBfromNits(float v) +float SDL_sRGBfromLinear(float v) { - v = SDL_scRGBfromNits(v); - if (v <= 0.0031308f) { v = (v * 12.92f); } else { diff --git a/src/video/SDL_pixels_c.h b/src/video/SDL_pixels_c.h index 3f98bbaffaab7..38a3aa59d1220 100644 --- a/src/video/SDL_pixels_c.h +++ b/src/video/SDL_pixels_c.h @@ -34,10 +34,8 @@ extern int SDL_CalculateSize(Uint32 format, int width, int height, size_t *size, extern SDL_Colorspace SDL_GetDefaultColorspaceForFormat(Uint32 pixel_format); /* Colorspace conversion functions */ -extern float SDL_scRGBtoNits(float v); -extern float SDL_scRGBfromNits(float v); -extern float SDL_sRGBtoNits(float v); -extern float SDL_sRGBfromNits(float v); +extern float SDL_sRGBtoLinear(float v); +extern float SDL_sRGBfromLinear(float v); extern float SDL_PQtoNits(float v); extern float SDL_PQfromNits(float v); extern const float *SDL_GetYCbCRtoRGBConversionMatrix(SDL_Colorspace colorspace, int w, int h, int bits_per_pixel); @@ -52,6 +50,10 @@ extern void SDL_FreeBlitMap(SDL_BlitMap *map); extern void SDL_InvalidateAllBlitMap(SDL_Surface *surface); +/* Surface functions */ +extern float SDL_GetSurfaceSDRWhitePoint(SDL_Surface *surface, SDL_Colorspace colorspace); +extern float SDL_GetSurfaceHDRHeadroom(SDL_Surface *surface, SDL_Colorspace colorspace); + /* Miscellaneous functions */ extern void SDL_DitherColors(SDL_Color *colors, int bpp); extern Uint8 SDL_FindColor(SDL_Palette *pal, Uint8 r, Uint8 g, Uint8 b, Uint8 a); diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index 6111dbe4c7637..896851526d54e 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -303,6 +303,52 @@ int SDL_GetSurfaceColorspace(SDL_Surface *surface, SDL_Colorspace *colorspace) return 0; } +float SDL_GetSurfaceSDRWhitePoint(SDL_Surface *surface, SDL_Colorspace colorspace) +{ + SDL_TransferCharacteristics transfer = SDL_COLORSPACETRANSFER(colorspace); + + if (transfer == SDL_TRANSFER_CHARACTERISTICS_LINEAR || + transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) { + SDL_PropertiesID props; + float default_value = 1.0f; + + if (surface->flags & SDL_SURFACE_USES_PROPERTIES) { + props = SDL_GetSurfaceProperties(surface); + } else { + props = 0; + } + if (transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) { + const float DEFAULT_PQ_SDR_WHITE_POINT = 100.0f; + default_value = DEFAULT_PQ_SDR_WHITE_POINT; + } + return SDL_GetFloatProperty(props, SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, default_value); + } + return 1.0f; +} + +float SDL_GetSurfaceHDRHeadroom(SDL_Surface *surface, SDL_Colorspace colorspace) +{ + SDL_TransferCharacteristics transfer = SDL_COLORSPACETRANSFER(colorspace); + + if (transfer == SDL_TRANSFER_CHARACTERISTICS_LINEAR || + transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) { + SDL_PropertiesID props; + float default_value = 0.0f; + + if (surface->flags & SDL_SURFACE_USES_PROPERTIES) { + props = SDL_GetSurfaceProperties(surface); + } else { + props = 0; + } + if (transfer == SDL_TRANSFER_CHARACTERISTICS_PQ && + SDL_HasProperty(props, SDL_PROP_SURFACE_MAXCLL_NUMBER)) { + default_value = (float)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_MAXCLL_NUMBER, 0) / SDL_GetSurfaceSDRWhitePoint(surface, colorspace); + } + return SDL_GetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, default_value); + } + return 1.0f; +} + int SDL_SetSurfacePalette(SDL_Surface *surface, SDL_Palette *palette) { if (!surface) { @@ -1193,10 +1239,11 @@ int SDL_FlipSurface(SDL_Surface *surface, SDL_FlipMode flip) } } -static SDL_Surface *SDL_ConvertSurfaceWithPixelFormatAndColorspace(SDL_Surface *surface, const SDL_PixelFormat *format, Uint32 colorspace) +static SDL_Surface *SDL_ConvertSurfaceWithPixelFormatAndColorspace(SDL_Surface *surface, const SDL_PixelFormat *format, Uint32 colorspace, SDL_PropertiesID props) { SDL_Surface *convert; SDL_Colorspace src_colorspace; + SDL_PropertiesID src_properties; Uint32 copy_flags; SDL_Color copy_color; SDL_Rect bounds; @@ -1234,6 +1281,12 @@ static SDL_Surface *SDL_ConvertSurfaceWithPixelFormatAndColorspace(SDL_Surface * return NULL; } + if (surface->flags & SDL_SURFACE_USES_PROPERTIES) { + src_properties = SDL_GetSurfaceProperties(surface); + } else { + src_properties = 0; + } + /* Create a new surface with the desired format */ convert = SDL_CreateSurface(surface->w, surface->h, format->format); if (!convert) { @@ -1246,7 +1299,7 @@ static SDL_Surface *SDL_ConvertSurfaceWithPixelFormatAndColorspace(SDL_Surface * SDL_SetSurfaceColorspace(convert, colorspace); if (SDL_ISPIXELFORMAT_FOURCC(format->format) || SDL_ISPIXELFORMAT_FOURCC(surface->format->format)) { - if (SDL_ConvertPixelsAndColorspace(surface->w, surface->h, surface->format->format, src_colorspace, surface->pixels, surface->pitch, convert->format->format, colorspace, convert->pixels, convert->pitch) < 0) { + if (SDL_ConvertPixelsAndColorspace(surface->w, surface->h, surface->format->format, src_colorspace, src_properties, surface->pixels, surface->pitch, convert->format->format, colorspace, props, convert->pixels, convert->pitch) < 0) { SDL_DestroySurface(convert); return NULL; } @@ -1411,7 +1464,7 @@ static SDL_Surface *SDL_ConvertSurfaceWithPixelFormatAndColorspace(SDL_Surface * tmp->map->info.flags &= ~SDL_COPY_COLORKEY; /* Conversion of the colorkey */ - tmp2 = SDL_ConvertSurfaceWithPixelFormatAndColorspace(tmp, format, colorspace); + tmp2 = SDL_ConvertSurfaceWithPixelFormatAndColorspace(tmp, format, colorspace, props); if (!tmp2) { SDL_DestroySurface(tmp); SDL_DestroySurface(convert); @@ -1460,7 +1513,7 @@ SDL_Surface *SDL_DuplicateSurface(SDL_Surface *surface) return NULL; } - return SDL_ConvertSurfaceWithPixelFormatAndColorspace(surface, surface->format, SDL_COLORSPACE_UNKNOWN); + return SDL_ConvertSurfaceWithPixelFormatAndColorspace(surface, surface->format, SDL_COLORSPACE_UNKNOWN, 0); } SDL_Surface *SDL_ConvertSurface(SDL_Surface *surface, const SDL_PixelFormat *format) @@ -1479,12 +1532,13 @@ SDL_Surface *SDL_ConvertSurface(SDL_Surface *surface, const SDL_PixelFormat *for colorspace = SDL_GetDefaultColorspaceForFormat(format->format); - return SDL_ConvertSurfaceWithPixelFormatAndColorspace(surface, format, colorspace); + return SDL_ConvertSurfaceWithPixelFormatAndColorspace(surface, format, colorspace, 0); } SDL_Surface *SDL_ConvertSurfaceFormat(SDL_Surface *surface, Uint32 pixel_format) { SDL_Colorspace colorspace; + SDL_PropertiesID props; if (!surface) { SDL_InvalidParamError("surface"); @@ -1493,17 +1547,23 @@ SDL_Surface *SDL_ConvertSurfaceFormat(SDL_Surface *surface, Uint32 pixel_format) colorspace = SDL_GetDefaultColorspaceForFormat(pixel_format); - return SDL_ConvertSurfaceFormatAndColorspace(surface, pixel_format, colorspace); + if (surface->flags & SDL_SURFACE_USES_PROPERTIES) { + props = SDL_GetSurfaceProperties(surface); + } else { + props = 0; + } + + return SDL_ConvertSurfaceFormatAndColorspace(surface, pixel_format, colorspace, props); } -SDL_Surface *SDL_ConvertSurfaceFormatAndColorspace(SDL_Surface *surface, Uint32 pixel_format, SDL_Colorspace colorspace) +SDL_Surface *SDL_ConvertSurfaceFormatAndColorspace(SDL_Surface *surface, Uint32 pixel_format, SDL_Colorspace colorspace, SDL_PropertiesID props) { SDL_PixelFormat *format; SDL_Surface *convert = NULL; format = SDL_CreatePixelFormat(pixel_format); if (format) { - convert = SDL_ConvertSurfaceWithPixelFormatAndColorspace(surface, format, colorspace); + convert = SDL_ConvertSurfaceWithPixelFormatAndColorspace(surface, format, colorspace, props); SDL_DestroyPixelFormat(format); } return convert; @@ -1512,7 +1572,7 @@ SDL_Surface *SDL_ConvertSurfaceFormatAndColorspace(SDL_Surface *surface, Uint32 /* * Create a surface on the stack for quick blit operations */ -static SDL_bool SDL_CreateSurfaceOnStack(int width, int height, Uint32 pixel_format, SDL_Colorspace colorspace, void *pixels, int pitch, SDL_Surface *surface, SDL_PixelFormat *format, SDL_BlitMap *blitmap) +static SDL_bool SDL_CreateSurfaceOnStack(int width, int height, Uint32 pixel_format, SDL_Colorspace colorspace, SDL_PropertiesID props, void *pixels, int pitch, SDL_Surface *surface, SDL_PixelFormat *format, SDL_BlitMap *blitmap) { if (SDL_ISPIXELFORMAT_INDEXED(pixel_format)) { SDL_SetError("Indexed pixel formats not supported"); @@ -1542,6 +1602,13 @@ static SDL_bool SDL_CreateSurfaceOnStack(int width, int height, Uint32 pixel_for SDL_SetSurfaceColorspace(surface, colorspace); + if (props) { + SDL_PropertiesID surface_props = SDL_GetSurfaceProperties(surface); + if (SDL_CopyProperties(props, surface_props) < 0) { + return SDL_FALSE; + } + } + /* The surface is ready to go */ surface->refcount = 1; return SDL_TRUE; @@ -1577,8 +1644,8 @@ SDL_Surface *SDL_DuplicatePixels(int width, int height, Uint32 format, SDL_Color } int SDL_ConvertPixelsAndColorspace(int width, int height, - Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch, - Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch) + Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, + Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch) { SDL_Surface src_surface, dst_surface; SDL_PixelFormat src_fmt, dst_fmt; @@ -1609,11 +1676,11 @@ int SDL_ConvertPixelsAndColorspace(int width, int height, #if SDL_HAVE_YUV if (SDL_ISPIXELFORMAT_FOURCC(src_format) && SDL_ISPIXELFORMAT_FOURCC(dst_format)) { - return SDL_ConvertPixels_YUV_to_YUV(width, height, src_format, src_colorspace, src, src_pitch, dst_format, dst_colorspace, dst, dst_pitch); + return SDL_ConvertPixels_YUV_to_YUV(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); } else if (SDL_ISPIXELFORMAT_FOURCC(src_format)) { - return SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src_colorspace, src, src_pitch, dst_format, dst_colorspace, dst, dst_pitch); + return SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); } else if (SDL_ISPIXELFORMAT_FOURCC(dst_format)) { - return SDL_ConvertPixels_RGB_to_YUV(width, height, src_format, src_colorspace, src, src_pitch, dst_format, dst_colorspace, dst, dst_pitch); + return SDL_ConvertPixels_RGB_to_YUV(width, height, src_format, src_colorspace, src_properties, src, src_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); } #else if (SDL_ISPIXELFORMAT_FOURCC(src_format) || SDL_ISPIXELFORMAT_FOURCC(dst_format)) { @@ -1634,11 +1701,11 @@ int SDL_ConvertPixelsAndColorspace(int width, int height, return 0; } - if (!SDL_CreateSurfaceOnStack(width, height, src_format, src_colorspace, nonconst_src, src_pitch, &src_surface, &src_fmt, &src_blitmap)) { + if (!SDL_CreateSurfaceOnStack(width, height, src_format, src_colorspace, src_properties, nonconst_src, src_pitch, &src_surface, &src_fmt, &src_blitmap)) { return -1; } - if (!SDL_CreateSurfaceOnStack(width, height, dst_format, dst_colorspace, dst, dst_pitch, &dst_surface, &dst_fmt, &dst_blitmap)) { + if (!SDL_CreateSurfaceOnStack(width, height, dst_format, dst_colorspace, dst_properties, dst, dst_pitch, &dst_surface, &dst_fmt, &dst_blitmap)) { return -1; } @@ -1660,8 +1727,8 @@ int SDL_ConvertPixels(int width, int height, Uint32 dst_format, void *dst, int dst_pitch) { return SDL_ConvertPixelsAndColorspace(width, height, - src_format, SDL_COLORSPACE_UNKNOWN, src, src_pitch, - dst_format, SDL_COLORSPACE_UNKNOWN, dst, dst_pitch); + src_format, SDL_COLORSPACE_UNKNOWN, 0, src, src_pitch, + dst_format, SDL_COLORSPACE_UNKNOWN, 0, dst, dst_pitch); } /* @@ -1777,7 +1844,7 @@ int SDL_ReadSurfacePixel(SDL_Surface *surface, int x, int y, Uint8 *r, Uint8 *g, SDL_Colorspace colorspace; if (SDL_GetSurfaceColorspace(surface, &colorspace) == 0 && - SDL_ConvertPixelsAndColorspace(1, 1, surface->format->format, colorspace, p, surface->pitch, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, rgba, sizeof(rgba)) == 0) { + SDL_ConvertPixelsAndColorspace(1, 1, surface->format->format, colorspace, SDL_GetSurfaceProperties(surface), p, surface->pitch, SDL_PIXELFORMAT_RGBA32, SDL_COLORSPACE_SRGB, 0, rgba, sizeof(rgba)) == 0) { *r = rgba[0]; *g = rgba[1]; *b = rgba[2]; diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index ac112cbd31a99..dbae19bffd315 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -120,9 +120,8 @@ struct SDL_Window typedef struct { - SDL_bool enabled; - float SDR_whitelevel; - float HDR_whitelevel; + float SDR_white_point; + float HDR_headroom; } SDL_HDRDisplayProperties; /* diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index cedc7556be499..7d56633450faa 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -701,14 +701,20 @@ SDL_DisplayID SDL_AddVideoDisplay(const SDL_VideoDisplay *display, SDL_bool send props = SDL_GetDisplayProperties(id); - if (display->HDR.enabled) { + if (display->HDR.HDR_headroom > 1.0f) { SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_TRUE); + } else { + SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE); } - if (display->HDR.SDR_whitelevel != 0.0f) { - SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, display->HDR.SDR_whitelevel); + if (display->HDR.SDR_white_point <= 1.0f) { + SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT, 1.0f); + } else { + SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT, display->HDR.SDR_white_point); } - if (display->HDR.HDR_whitelevel != 0.0f) { - SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT, display->HDR.HDR_whitelevel); + if (display->HDR.HDR_headroom <= 1.0f) { + SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT, 1.0f); + } else { + SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT, display->HDR.HDR_headroom); } return id; @@ -979,22 +985,32 @@ void SDL_SetDisplayHDRProperties(SDL_VideoDisplay *display, const SDL_HDRDisplay SDL_PropertiesID props = SDL_GetDisplayProperties(display->id); SDL_bool changed = SDL_FALSE; - if (HDR->enabled != display->HDR.enabled) { - SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, HDR->enabled); - changed = SDL_TRUE; - } - if (HDR->SDR_whitelevel != display->HDR.SDR_whitelevel) { - SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, HDR->SDR_whitelevel); + if (HDR->SDR_white_point != display->HDR.SDR_white_point) { + if (HDR->SDR_white_point <= 1.0f) { + SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT, 1.0f); + } else { + SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT, HDR->SDR_white_point); + } changed = SDL_TRUE; } - if (HDR->HDR_whitelevel != display->HDR.HDR_whitelevel) { - SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT, HDR->HDR_whitelevel); + if (HDR->HDR_headroom != display->HDR.HDR_headroom) { + if (HDR->HDR_headroom > 1.0f) { + SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_TRUE); + } else { + SDL_SetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE); + } + if (HDR->HDR_headroom <= 1.0f) { + SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT, 1.0f); + } else { + SDL_SetFloatProperty(props, SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT, HDR->HDR_headroom); + } changed = SDL_TRUE; } SDL_copyp(&display->HDR, HDR); if (changed) { - SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_HDR_STATE_CHANGED, HDR->enabled); + SDL_bool enabled = SDL_GetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE); + SDL_SendDisplayEvent(display, SDL_EVENT_DISPLAY_HDR_STATE_CHANGED, enabled); } } diff --git a/src/video/SDL_yuv.c b/src/video/SDL_yuv.c index 91e2aa8b61c9b..4a8e00ef3a745 100644 --- a/src/video/SDL_yuv.c +++ b/src/video/SDL_yuv.c @@ -555,8 +555,8 @@ static SDL_bool yuv_rgb_std( } int SDL_ConvertPixels_YUV_to_RGB(int width, int height, - Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch, - Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch) + Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, + Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch) { const Uint8 *y = NULL; const Uint8 *u = NULL; @@ -597,14 +597,14 @@ int SDL_ConvertPixels_YUV_to_RGB(int width, int height, } /* convert src/src_format to tmp/ARGB8888 */ - ret = SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src_colorspace, src, src_pitch, SDL_PIXELFORMAT_ARGB8888, SDL_COLORSPACE_SRGB, tmp, tmp_pitch); + ret = SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, SDL_PIXELFORMAT_ARGB8888, SDL_COLORSPACE_SRGB, 0, tmp, tmp_pitch); if (ret < 0) { SDL_free(tmp); return ret; } /* convert tmp/ARGB8888 to dst/RGB */ - ret = SDL_ConvertPixelsAndColorspace(width, height, SDL_PIXELFORMAT_ARGB8888, SDL_COLORSPACE_SRGB, tmp, tmp_pitch, dst_format, dst_colorspace, dst, dst_pitch); + ret = SDL_ConvertPixelsAndColorspace(width, height, SDL_PIXELFORMAT_ARGB8888, SDL_COLORSPACE_SRGB, 0, tmp, tmp_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); SDL_free(tmp); return ret; } @@ -935,8 +935,8 @@ static int SDL_ConvertPixels_ARGB8888_to_YUV(int width, int height, const void * } int SDL_ConvertPixels_RGB_to_YUV(int width, int height, - Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch, - Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch) + Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, + Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch) { YCbCrType yuv_type = YCBCR_601; @@ -2323,8 +2323,8 @@ static int SDL_ConvertPixels_Packed4_to_Planar2x2(int width, int height, #endif /* SDL_HAVE_YUV */ int SDL_ConvertPixels_YUV_to_YUV(int width, int height, - Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch, - Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch) + Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, + Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch) { #if SDL_HAVE_YUV if (src_colorspace != dst_colorspace) { diff --git a/src/video/SDL_yuv_c.h b/src/video/SDL_yuv_c.h index 4c2cf5d0fa159..242dd684db317 100644 --- a/src/video/SDL_yuv_c.h +++ b/src/video/SDL_yuv_c.h @@ -26,9 +26,9 @@ /* YUV conversion functions */ -extern int SDL_ConvertPixels_YUV_to_RGB(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch); -extern int SDL_ConvertPixels_RGB_to_YUV(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch); -extern int SDL_ConvertPixels_YUV_to_YUV(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, void *dst, int dst_pitch); +extern int SDL_ConvertPixels_YUV_to_RGB(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch); +extern int SDL_ConvertPixels_RGB_to_YUV(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch); +extern int SDL_ConvertPixels_YUV_to_YUV(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch); extern int SDL_CalculateYUVSize(Uint32 format, int w, int h, size_t *size, size_t *pitch); diff --git a/src/video/cocoa/SDL_cocoamodes.m b/src/video/cocoa/SDL_cocoamodes.m index 66bb3a72e3f29..42d54a4bd67e8 100644 --- a/src/video/cocoa/SDL_cocoamodes.m +++ b/src/video/cocoa/SDL_cocoamodes.m @@ -292,17 +292,14 @@ static SDL_bool GetDisplayMode(SDL_VideoDevice *_this, CGDisplayModeRef vidmode, static void Cocoa_GetHDRProperties(CGDirectDisplayID displayID, SDL_HDRDisplayProperties *HDR) { - HDR->enabled = SDL_FALSE; - HDR->SDR_whitelevel = 0.0f; + HDR->SDR_white_point = 1.0f; + HDR->HDR_headroom = 1.0f; #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 /* Added in the 10.15 SDK */ if (@available(macOS 10.15, *)) { NSScreen *screen = GetNSScreenForDisplayID(displayID); - - if (screen && screen.maximumPotentialExtendedDynamicRangeColorComponentValue > 1.0f) { - HDR->enabled = SDL_TRUE; - HDR->SDR_whitelevel = 80.0f; /* SDR content is always at scRGB 1.0 */ - HDR->HDR_whitelevel = HDR->SDR_whitelevel * screen.maximumExtendedDynamicRangeColorComponentValue; + if (screen) { + HDR->HDR_headroom = screen.maximumExtendedDynamicRangeColorComponentValue; } } #endif diff --git a/src/video/uikit/SDL_uikitmodes.m b/src/video/uikit/SDL_uikitmodes.m index e655c7c39346e..8da779e0163ec 100644 --- a/src/video/uikit/SDL_uikitmodes.m +++ b/src/video/uikit/SDL_uikitmodes.m @@ -242,13 +242,12 @@ int UIKit_AddDisplay(UIScreen *uiscreen, SDL_bool send_event) } display.desktop_mode = mode; + display.HDR.SDR_white_point = 1.0f; + display.HDR.HDR_headroom = 1.0f; + #ifndef SDL_PLATFORM_TVOS if (@available(iOS 16.0, *)) { - if (uiscreen.potentialEDRHeadroom > 1.0f) { - display.HDR.enabled = SDL_TRUE; - display.HDR.SDR_whitelevel = 80.0f; /* SDR content is always at scRGB 1.0 */ - display.HDR.HDR_whitelevel = display.HDR.SDR_whitelevel * uiscreen.currentEDRHeadroom; - } + display.HDR.HDR_headroom = uiscreen.currentEDRHeadroom; } #endif /* !SDL_PLATFORM_TVOS */ diff --git a/src/video/windows/SDL_windowsmodes.c b/src/video/windows/SDL_windowsmodes.c index c417420e9c180..6d3b4b9a9bca5 100644 --- a/src/video/windows/SDL_windowsmodes.c +++ b/src/video/windows/SDL_windowsmodes.c @@ -452,10 +452,10 @@ static SDL_bool WIN_GetMonitorPathInfo(HMONITOR hMonitor, DISPLAYCONFIG_PATH_INF return found; } -static float WIN_GetSDRWhiteLevel(HMONITOR hMonitor) +static float WIN_GetSDRWhitePoint(HMONITOR hMonitor) { DISPLAYCONFIG_PATH_INFO path_info; - float SDR_whitelevel = 200.0f; + float SDR_white_point = 1.0f; if (WIN_GetMonitorPathInfo(hMonitor, &path_info)) { DISPLAYCONFIG_SDR_WHITE_LEVEL white_level; @@ -465,11 +465,12 @@ static float WIN_GetSDRWhiteLevel(HMONITOR hMonitor) white_level.header.size = sizeof(white_level); white_level.header.adapterId = path_info.targetInfo.adapterId; white_level.header.id = path_info.targetInfo.id; - if (DisplayConfigGetDeviceInfo(&white_level.header) == ERROR_SUCCESS) { - SDR_whitelevel = (white_level.SDRWhiteLevel / 1000.0f) * 80.0f; + if (DisplayConfigGetDeviceInfo(&white_level.header) == ERROR_SUCCESS && + white_level.SDRWhiteLevel > 0) { + SDR_white_point = (white_level.SDRWhiteLevel / 1000.0f); } } - return SDR_whitelevel; + return SDR_white_point; } static void WIN_GetHDRProperties(SDL_VideoDevice *_this, HMONITOR hMonitor, SDL_HDRDisplayProperties *HDR) @@ -480,9 +481,8 @@ static void WIN_GetHDRProperties(SDL_VideoDevice *_this, HMONITOR hMonitor, SDL_ if (WIN_GetMonitorDESC1(hMonitor, &desc)) { if (desc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) { - HDR->enabled = SDL_TRUE; - HDR->SDR_whitelevel = WIN_GetSDRWhiteLevel(hMonitor); - HDR->HDR_whitelevel = desc.MaxLuminance; + HDR->SDR_white_point = WIN_GetSDRWhitePoint(hMonitor); + HDR->HDR_headroom = (desc.MaxLuminance / 80.0f) / HDR->SDR_white_point; } } } diff --git a/test/testcolorspace.c b/test/testcolorspace.c index 3b479ae09d977..7fb262b161c31 100644 --- a/test/testcolorspace.c +++ b/test/testcolorspace.c @@ -18,18 +18,6 @@ #include #endif -/* The value for the SDR white level on an SDR display, scRGB 1.0 */ -#define SDR_DISPLAY_WHITE_LEVEL 80.0f - -/* The default value for the SDR white level on an HDR display */ -#define DEFAULT_SDR_WHITE_LEVEL 200.0f - -/* The default value for the HDR white level on an HDR display */ -#define DEFAULT_HDR_WHITE_LEVEL 400.0f - -/* The maximum value for the HDR white level on an HDR display */ -#define MAXIMUM_HDR_WHITE_LEVEL 1000.0f - #define WINDOW_WIDTH 640 #define WINDOW_HEIGHT 480 @@ -46,13 +34,7 @@ static int renderer_count = 0; static int renderer_index = 0; static int stage_index = 0; static int done; -static SDL_bool HDR_enabled = SDL_FALSE; -static float SDR_white_level = SDR_DISPLAY_WHITE_LEVEL; -static float SDR_color_scale = 1.0f; -static SDL_FRect SDR_calibration_rect; -static float HDR_white_level = SDR_DISPLAY_WHITE_LEVEL; -static float HDR_color_scale = 1.0f; -static SDL_FRect HDR_calibration_rect; +static float HDR_headroom = 1.0f; enum { @@ -60,7 +42,6 @@ enum StageDrawBackground, StageBlendDrawing, StageBlendTexture, - StageHDRCalibration, StageGradientDrawing, StageGradientTexture, StageCount @@ -73,84 +54,22 @@ static void FreeRenderer(void) renderer = NULL; } -static float GetDisplaySDRWhiteLevel(void) -{ - SDL_PropertiesID props; - - if (!HDR_enabled) { - /* HDR is not enabled, use the SDR white level */ - return SDR_DISPLAY_WHITE_LEVEL; - } - - props = SDL_GetRendererProperties(renderer); - if (SDL_GetNumberProperty(props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB) != SDL_COLORSPACE_SCRGB) { - /* We're not displaying in HDR, use the SDR white level */ - return SDR_DISPLAY_WHITE_LEVEL; - } - - props = SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window)); - return SDL_GetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, DEFAULT_SDR_WHITE_LEVEL); -} - -static void SetSDRWhiteLevel(float value) -{ - if (value == SDR_white_level) { - return; - } - - SDL_Log("SDR white level set to %g nits\n", value); - SDR_white_level = value; - SDR_color_scale = SDR_white_level / SDR_DISPLAY_WHITE_LEVEL; - SDL_SetRenderColorScale(renderer, SDR_color_scale); -} - -static float GetDisplayHDRWhiteLevel(void) -{ - SDL_PropertiesID props; - - if (!HDR_enabled) { - /* HDR is not enabled, use the SDR white level */ - return SDR_DISPLAY_WHITE_LEVEL; - } - - props = SDL_GetRendererProperties(renderer); - if (SDL_GetNumberProperty(props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB) != SDL_COLORSPACE_SCRGB) { - /* We're not displaying in HDR, use the SDR white level */ - return SDR_DISPLAY_WHITE_LEVEL; - } - - props = SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window)); - return SDL_GetFloatProperty(props, SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT, DEFAULT_HDR_WHITE_LEVEL); -} - -static void SetHDRWhiteLevel(float value) -{ - if (value == HDR_white_level) { - return; - } - - SDL_Log("HDR white level set to %g nits\n", value); - HDR_white_level = value; - HDR_color_scale = HDR_white_level / SDR_DISPLAY_WHITE_LEVEL; -} - static void UpdateHDRState(void) { SDL_PropertiesID props; + SDL_bool HDR_enabled; props = SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window)); HDR_enabled = SDL_GetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE); - SetHDRWhiteLevel(GetDisplayHDRWhiteLevel()); - SetSDRWhiteLevel(GetDisplaySDRWhiteLevel()); - SDL_Log("HDR %s\n", HDR_enabled ? "enabled" : "disabled"); if (HDR_enabled) { props = SDL_GetRendererProperties(renderer); - if (SDL_GetNumberProperty(props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB) != SDL_COLORSPACE_SCRGB) { - SDL_Log("Run with --colorspace scRGB to display HDR colors\n"); + if (SDL_GetNumberProperty(props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB) != SDL_COLORSPACE_SRGB_LINEAR) { + SDL_Log("Run with --colorspace linear to display HDR colors\n"); } + HDR_headroom = SDL_GetFloatProperty(props, SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT, 1.0f); } } @@ -238,6 +157,9 @@ static SDL_bool ReadPixel(int x, int y, SDL_Color *c) surface = SDL_RenderReadPixels(renderer, &r); if (surface) { + /* We don't want to do any HDR -> SDR tone mapping */ + SDL_SetFloatProperty(SDL_GetSurfaceProperties(surface), SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, 0.0f); + if (SDL_ReadSurfacePixel(surface, 0, 0, &c->r, &c->g, &c->b, &c->a) == 0) { result = SDL_TRUE; } else { @@ -266,20 +188,6 @@ static void DrawText(float x, float y, const char *fmt, ...) SDL_free(text); } -static void DrawTextWhite(float x, float y, const char *fmt, ...) -{ - char *text; - - va_list ap; - va_start(ap, fmt); - SDL_vasprintf(&text, fmt, ap); - va_end(ap); - - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDLTest_DrawString(renderer, x, y, text); - SDL_free(text); -} - static void RenderClearBackground(void) { /* Draw a 50% gray background. @@ -488,85 +396,38 @@ static void DrawGradient(float x, float y, float width, float height, float star SDL_RenderGeometryRawFloat(renderer, NULL, xy, xy_stride, color, color_stride, NULL, 0, num_vertices, indices, num_indices, size_indices); } -static float scRGBtoNits(float v) -{ - return v * 80.0f; -} - -static float scRGBfromNits(float v) -{ - return v / 80.0f; -} - -static float sRGBtoLinear(float v) -{ - if (v <= 0.04045f) { - v = (v / 12.92f); - } else { - v = SDL_powf(((v + 0.055f) / 1.055f), 2.4f); - } - return v; -} - -static float sRGBFromLinear(float v) -{ - if (v <= 0.0031308f) { - v = (v * 12.92f); - } else { - v = (SDL_powf(v, 1.0f / 2.4f) * 1.055f - 0.055f); - } - return v; -} - -static float sRGBtoNits(float v) -{ - return scRGBtoNits(sRGBtoLinear(v)); -} - -static float sRGBfromNits(float v) -{ - return sRGBFromLinear(scRGBfromNits(v)); -} - static void RenderGradientDrawing(void) { - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_RenderClear(renderer); float x = TEXT_START_X; float y = TEXT_START_Y; - DrawTextWhite(x, y, "%s %s", renderer_name, colorspace_name); - y += TEXT_LINE_ADVANCE; - DrawTextWhite(x, y, "Test: Draw SDR and HDR gradients"); - y += TEXT_LINE_ADVANCE; - + DrawText(x, y, "%s %s", renderer_name, colorspace_name); y += TEXT_LINE_ADVANCE; - - DrawTextWhite(x, y, "SDR gradient (%g nits)", SDR_white_level); + DrawText(x, y, "Test: Draw SDR and HDR gradients"); y += TEXT_LINE_ADVANCE; - SDL_SetRenderColorScale(renderer, 1.0f); - DrawGradient(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, sRGBfromNits(SDR_white_level)); - SDL_SetRenderColorScale(renderer, SDR_color_scale); - y += 64.0f; - y += TEXT_LINE_ADVANCE; y += TEXT_LINE_ADVANCE; - DrawTextWhite(x, y, "HDR gradient (%g nits)", HDR_white_level); + DrawText(x, y, "SDR gradient"); y += TEXT_LINE_ADVANCE; - SDL_SetRenderColorScale(renderer, 1.0f); - DrawGradient(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, sRGBfromNits(HDR_white_level)); - SDL_SetRenderColorScale(renderer, SDR_color_scale); + DrawGradient(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, 1.0f); y += 64.0f; y += TEXT_LINE_ADVANCE; y += TEXT_LINE_ADVANCE; - DrawTextWhite(x, y, "HDR gradient (%g nits)", MAXIMUM_HDR_WHITE_LEVEL); + if (HDR_headroom > 1.0f) { + DrawText(x, y, "HDR gradient"); + } else { + DrawText(x, y, "No HDR headroom, HDR and SDR gradient are the same"); + } y += TEXT_LINE_ADVANCE; + /* Drawing is in the sRGB colorspace, so we need to use the color scale, which is applied in linear space, to get into high dynamic range */ + SDL_SetRenderColorScale(renderer, HDR_headroom); + DrawGradient(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, 1.0f); SDL_SetRenderColorScale(renderer, 1.0f); - DrawGradient(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, sRGBfromNits(MAXIMUM_HDR_WHITE_LEVEL)); - SDL_SetRenderColorScale(renderer, SDR_color_scale); y += 64.0f; } @@ -575,20 +436,12 @@ static SDL_Texture *CreateGradientTexture(int width, float start, float end) SDL_Texture *texture; float *pixels; - /* Floating point textures are in the scRGB colorspace by default */ + /* Floating point textures are in the linear colorspace by default */ texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA128_FLOAT, SDL_TEXTUREACCESS_STATIC, width, 1); if (!texture) { return NULL; } - if (colorspace == SDL_COLORSPACE_SCRGB) { - /* The input values are in the sRGB colorspace, but we're blending in linear space */ - start = sRGBtoLinear(start); - end = sRGBtoLinear(end); - } else if (colorspace == SDL_COLORSPACE_SRGB) { - /* The input values are in the sRGB colorspace, and we're blending in sRGB space */ - } - pixels = (float *)SDL_malloc(width * sizeof(float) * 4); if (pixels) { int i; @@ -617,129 +470,35 @@ static void DrawGradientTexture(float x, float y, float width, float height, flo static void RenderGradientTexture(void) { - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); SDL_RenderClear(renderer); float x = TEXT_START_X; float y = TEXT_START_Y; - DrawTextWhite(x, y, "%s %s", renderer_name, colorspace_name); - y += TEXT_LINE_ADVANCE; - DrawTextWhite(x, y, "Test: Texture SDR and HDR gradients"); + DrawText(x, y, "%s %s", renderer_name, colorspace_name); y += TEXT_LINE_ADVANCE; - + DrawText(x, y, "Test: Texture SDR and HDR gradients"); y += TEXT_LINE_ADVANCE; - DrawTextWhite(x, y, "SDR gradient (%g nits)", SDR_white_level); y += TEXT_LINE_ADVANCE; - SDL_SetRenderColorScale(renderer, 1.0f); - DrawGradientTexture(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, sRGBfromNits(SDR_white_level)); - SDL_SetRenderColorScale(renderer, SDR_color_scale); - y += 64.0f; + DrawText(x, y, "SDR gradient"); y += TEXT_LINE_ADVANCE; - y += TEXT_LINE_ADVANCE; - - DrawTextWhite(x, y, "HDR gradient (%g nits)", HDR_white_level); - y += TEXT_LINE_ADVANCE; - SDL_SetRenderColorScale(renderer, 1.0f); - DrawGradientTexture(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, sRGBfromNits(HDR_white_level)); - SDL_SetRenderColorScale(renderer, SDR_color_scale); + DrawGradientTexture(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, 1.0f); y += 64.0f; y += TEXT_LINE_ADVANCE; y += TEXT_LINE_ADVANCE; - DrawTextWhite(x, y, "HDR gradient (%g nits)", MAXIMUM_HDR_WHITE_LEVEL); - y += TEXT_LINE_ADVANCE; - SDL_SetRenderColorScale(renderer, 1.0f); - DrawGradientTexture(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, sRGBfromNits(MAXIMUM_HDR_WHITE_LEVEL)); - SDL_SetRenderColorScale(renderer, SDR_color_scale); - y += 64.0f; -} - -static void RenderHDRCalibration(void) -{ - SDL_FRect rect; - - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); - SDL_RenderClear(renderer); - - float x = TEXT_START_X; - float y = TEXT_START_Y; - DrawTextWhite(x, y, "%s %s", renderer_name, colorspace_name); - y += TEXT_LINE_ADVANCE; - DrawTextWhite(x, y, "Test: HDR calibration"); - y += TEXT_LINE_ADVANCE; - - y += TEXT_LINE_ADVANCE; - - if (!HDR_enabled) { - DrawTextWhite(x, y, "HDR not enabled"); - return; + if (HDR_headroom > 1.0f) { + DrawText(x, y, "HDR gradient"); + } else { + DrawText(x, y, "No HDR headroom, HDR and SDR gradient are the same"); } - - DrawTextWhite(x, y, "Select HDR maximum brightness (%g nits)", HDR_white_level); y += TEXT_LINE_ADVANCE; - DrawTextWhite(x, y, "The square in the middle should just barely be visible"); - y += TEXT_LINE_ADVANCE; - HDR_calibration_rect.x = x; - HDR_calibration_rect.y = y; - HDR_calibration_rect.w = WINDOW_WIDTH - 2 * x; - HDR_calibration_rect.h = 64.0f; - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDL_SetRenderColorScale(renderer, MAXIMUM_HDR_WHITE_LEVEL / SDR_DISPLAY_WHITE_LEVEL); - SDL_RenderFillRect(renderer, &HDR_calibration_rect); - SDL_SetRenderColorScale(renderer, HDR_color_scale * 0.90f); - rect = HDR_calibration_rect; - rect.h -= 4.0f; - rect.w = 60.0f; - rect.x = (WINDOW_WIDTH - rect.w) / 2.0f; - rect.y += 2.0f; - SDL_RenderFillRect(renderer, &rect); - SDL_SetRenderColorScale(renderer, SDR_color_scale); + /* The gradient texture is in the linear colorspace, so we can use the HDR_headroom value directly */ + DrawGradientTexture(x, y, WINDOW_WIDTH - 2 * x, 64.0f, 0.0f, HDR_headroom); y += 64.0f; - - y += TEXT_LINE_ADVANCE; - y += TEXT_LINE_ADVANCE; - - DrawTextWhite(x, y, "Select SDR maximum brightness (%g nits)", SDR_white_level); - y += TEXT_LINE_ADVANCE; - SDR_calibration_rect.x = x; - SDR_calibration_rect.y = y; - SDR_calibration_rect.w = WINDOW_WIDTH - 2 * x; - SDR_calibration_rect.h = 64.0f; - SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); - SDL_RenderFillRect(renderer, &SDR_calibration_rect); -} - -static void OnHDRCalibrationMouseHeld(float x, float y) -{ - SDL_FPoint mouse = { x, y }; - - if (SDL_PointInRectFloat(&mouse, &HDR_calibration_rect)) { - float v = (x - HDR_calibration_rect.x) / HDR_calibration_rect.w; - v *= (sRGBfromNits(MAXIMUM_HDR_WHITE_LEVEL) - 1.0f); - v += 1.0f; - v = sRGBtoNits(v); - SetHDRWhiteLevel(v); - return; - } - - if (SDL_PointInRectFloat(&mouse, &SDR_calibration_rect)) { - float v = (x - SDR_calibration_rect.x) / SDR_calibration_rect.w; - v *= (sRGBfromNits(MAXIMUM_HDR_WHITE_LEVEL) - 1.0f); - v += 1.0f; - v = sRGBtoNits(v); - SetSDRWhiteLevel(v); - return; - } -} - -static void OnMouseHeld(float x, float y) -{ - if (stage_index == StageHDRCalibration) { - OnHDRCalibrationMouseHeld(x, y); - } } static void loop(void) @@ -768,12 +527,6 @@ static void loop(void) default: break; } - } else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { - OnMouseHeld(event.button.x, event.button.y); - } else if (event.type == SDL_EVENT_MOUSE_MOTION) { - if (event.motion.state) { - OnMouseHeld(event.button.x, event.button.y); - } } else if (event.type == SDL_EVENT_DISPLAY_HDR_STATE_CHANGED) { UpdateHDRState(); } else if (event.type == SDL_EVENT_QUIT) { @@ -798,9 +551,6 @@ static void loop(void) case StageBlendTexture: RenderBlendTexture(); break; - case StageHDRCalibration: - RenderHDRCalibration(); - break; case StageGradientDrawing: RenderGradientDrawing(); break; @@ -844,8 +594,8 @@ int main(int argc, char *argv[]) colorspace_name = argv[i + 1]; if (SDL_strcasecmp(colorspace_name, "sRGB") == 0) { colorspace = SDL_COLORSPACE_SRGB; - } else if (SDL_strcasecmp(colorspace_name, "scRGB") == 0) { - colorspace = SDL_COLORSPACE_SCRGB; + } else if (SDL_strcasecmp(colorspace_name, "linear") == 0) { + colorspace = SDL_COLORSPACE_SRGB_LINEAR; /* Not currently supported } else if (SDL_strcasecmp(colorspace_name, "HDR10") == 0) { colorspace = SDL_COLORSPACE_HDR10; diff --git a/test/testffmpeg.c b/test/testffmpeg.c index b5babd53862a4..d151ff05b0193 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -57,15 +57,6 @@ #include "icon.h" -/* The value for the SDR white level on an SDR display, scRGB 1.0 */ -#define SDR_DISPLAY_WHITE_LEVEL 80.0f - -/* The default value for the SDR white level on an HDR display */ -#define DEFAULT_SDR_WHITE_LEVEL 200.0f - -/* The default value for the HDR white level on an HDR display */ -#define DEFAULT_HDR_WHITE_LEVEL 400.0f - static SDL_Texture *sprite; static SDL_FRect *positions; @@ -97,56 +88,6 @@ struct SwsContextContainer static const char *SWS_CONTEXT_CONTAINER_PROPERTY = "SWS_CONTEXT_CONTAINER"; static int done; -/* This function isn't platform specific, but we haven't hooked up HDR video support on other platforms yet */ -#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_APPLE) -static void GetDisplayHDRProperties(SDL_bool *HDR_display, float *SDR_white_level, float *HDR_white_level) -{ - SDL_PropertiesID props; - - *HDR_display = SDL_FALSE; - *SDR_white_level = SDR_DISPLAY_WHITE_LEVEL; - *HDR_white_level = SDR_DISPLAY_WHITE_LEVEL; - - props = SDL_GetRendererProperties(renderer); - if (SDL_GetNumberProperty(props, SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB) != SDL_COLORSPACE_SCRGB) { - /* We're not displaying in HDR, use the SDR white level */ - return; - } - - props = SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window)); - if (!SDL_GetBooleanProperty(props, SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE)) { - /* HDR is not enabled on the display */ - return; - } - - *HDR_display = SDL_TRUE; - *SDR_white_level = SDL_GetFloatProperty(props, SDL_PROP_DISPLAY_SDR_WHITE_LEVEL_FLOAT, DEFAULT_SDR_WHITE_LEVEL); - *HDR_white_level = SDL_GetFloatProperty(props, SDL_PROP_DISPLAY_HDR_WHITE_LEVEL_FLOAT, DEFAULT_HDR_WHITE_LEVEL); -} - -static void UpdateVideoColorScale(SDL_bool HDR_video) -{ - SDL_bool HDR_display = SDL_FALSE; - float SDR_white_level, HDR_white_level, video_white_level; - - GetDisplayHDRProperties(&HDR_display, &SDR_white_level, &HDR_white_level); - - if (HDR_video) { - video_white_level = DEFAULT_HDR_WHITE_LEVEL; - } else { - video_white_level = SDR_DISPLAY_WHITE_LEVEL; - } - - if (HDR_display && HDR_video) { - /* Scale the HDR range of the video to the HDR range of the display */ - SDL_SetRenderColorScale(renderer, HDR_white_level / video_white_level); - } else { - /* Scale the range of the video to the SDR range of the display */ - SDL_SetRenderColorScale(renderer, SDR_white_level / video_white_level); - } -} -#endif /* SDL_PLATFORM_WIN32 || SDL_PLATFORM_APPLE */ - static SDL_bool CreateWindowAndRenderer(Uint32 window_flags, const char *driver) { SDL_PropertiesID props; @@ -178,7 +119,7 @@ static SDL_bool CreateWindowAndRenderer(Uint32 window_flags, const char *driver) props = SDL_CreateProperties(); SDL_SetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING, driver); SDL_SetProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, window); - SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SCRGB); + SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB_LINEAR); renderer = SDL_CreateRendererWithProperties(props); if (!renderer) { /* Try again with the sRGB colorspace */ @@ -699,7 +640,6 @@ static SDL_bool GetTextureForD3D11Frame(AVFrame *frame, SDL_Texture **texture) } if (!*texture || (UINT)texture_width != desc.Width || (UINT)texture_height != desc.Height) { Uint32 format; - SDL_bool HDR_video = SDL_FALSE; switch (desc.Format) { case DXGI_FORMAT_NV12: @@ -707,11 +647,6 @@ static SDL_bool GetTextureForD3D11Frame(AVFrame *frame, SDL_Texture **texture) break; case DXGI_FORMAT_P010: format = SDL_PIXELFORMAT_P010; - HDR_video = SDL_TRUE; - break; - case DXGI_FORMAT_P016: - format = SDL_PIXELFORMAT_P016; - HDR_video = SDL_TRUE; break; default: SDL_SetError("Unsupported texture format %d", desc.Format); @@ -733,8 +668,6 @@ static SDL_bool GetTextureForD3D11Frame(AVFrame *frame, SDL_Texture **texture) if (!*texture) { return SDL_FALSE; } - - UpdateVideoColorScale(HDR_video); } ID3D11Resource *dx11_resource = SDL_GetProperty(SDL_GetTextureProperties(*texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_POINTER, NULL); @@ -759,7 +692,6 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex size_t nPixelBufferHeight = CVPixelBufferGetHeightOfPlane(pPixelBuffer, 0); SDL_PropertiesID props; Uint32 format; - SDL_bool HDR_video = SDL_FALSE; switch (nPixelBufferType) { case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: @@ -769,7 +701,6 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange: case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange: format = SDL_PIXELFORMAT_P010; - HDR_video = SDL_TRUE; break; default: SDL_SetError("Unsupported texture format %c%c%c%c", @@ -799,8 +730,6 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex return SDL_FALSE; } - UpdateVideoColorScale(HDR_video); - return SDL_TRUE; #else return SDL_FALSE; diff --git a/test/testyuv.c b/test/testyuv.c index 5d2025be89c67..f215ee98ef5cc 100644 --- a/test/testyuv.c +++ b/test/testyuv.c @@ -78,7 +78,7 @@ static SDL_bool verify_yuv_data(Uint32 format, SDL_Colorspace colorspace, const return SDL_FALSE; } - if (SDL_ConvertPixelsAndColorspace(surface->w, surface->h, format, colorspace, yuv, yuv_pitch, surface->format->format, SDL_COLORSPACE_SRGB, rgb, surface->pitch) == 0) { + if (SDL_ConvertPixelsAndColorspace(surface->w, surface->h, format, colorspace, 0, yuv, yuv_pitch, surface->format->format, SDL_COLORSPACE_SRGB, 0, rgb, surface->pitch) == 0) { int x, y; result = SDL_TRUE; for (y = 0; y < surface->h; ++y) { @@ -156,7 +156,7 @@ static int run_automated_tests(int pattern_size, int extra_pitch) /* Verify conversion to YUV formats */ for (i = 0; i < SDL_arraysize(formats); ++i) { yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch; - if (SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format->format, SDL_COLORSPACE_SRGB, pattern->pixels, pattern->pitch, formats[i], colorspace, yuv1, yuv1_pitch) < 0) { + if (SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, formats[i], colorspace, 0, yuv1, yuv1_pitch) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError()); goto done; } @@ -171,11 +171,11 @@ static int run_automated_tests(int pattern_size, int extra_pitch) for (j = 0; j < SDL_arraysize(formats); ++j) { yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch; yuv2_pitch = CalculateYUVPitch(formats[j], pattern->w) + extra_pitch; - if (SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format->format, SDL_COLORSPACE_SRGB, pattern->pixels, pattern->pitch, formats[i], colorspace, yuv1, yuv1_pitch) < 0) { + if (SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, formats[i], colorspace, 0, yuv1, yuv1_pitch) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError()); goto done; } - if (SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, formats[i], colorspace, yuv1, yuv1_pitch, formats[j], colorspace, yuv2, yuv2_pitch) < 0) { + if (SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, formats[i], colorspace, 0, yuv1, yuv1_pitch, formats[j], colorspace, 0, yuv2, yuv2_pitch) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError()); goto done; } @@ -196,11 +196,11 @@ static int run_automated_tests(int pattern_size, int extra_pitch) yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w) + extra_pitch; yuv2_pitch = CalculateYUVPitch(formats[j], pattern->w) + extra_pitch; - if (SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format->format, SDL_COLORSPACE_SRGB, pattern->pixels, pattern->pitch, formats[i], colorspace, yuv1, yuv1_pitch) < 0) { + if (SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, formats[i], colorspace, 0, yuv1, yuv1_pitch) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError()); goto done; } - if (SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, formats[i], colorspace, yuv1, yuv1_pitch, formats[j], colorspace, yuv1, yuv2_pitch) < 0) { + if (SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, formats[i], colorspace, 0, yuv1, yuv1_pitch, formats[j], colorspace, 0, yuv1, yuv2_pitch) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError()); goto done; } From 54c2ba6afd776d0d0c8f2bc438ad34d49a9cd87c Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 21 Feb 2024 09:03:03 -0800 Subject: [PATCH 072/220] Added the Chrome HDR tonemap operator Also added support for the SDL_PIXELFORMAT_XBGR2101010 pixel format to the D3D12, D3D11, and Metal renderers. --- include/SDL3/SDL_surface.h | 4 +- .../direct3d11/D3D11_PixelShader_Advanced.h | 1060 ++++ .../D3D11_PixelShader_Advanced.hlsl | 7 + .../direct3d11/D3D11_PixelShader_Colors.h | 118 +- .../direct3d11/D3D11_PixelShader_Common.incl | 175 +- .../direct3d11/D3D11_PixelShader_HDR10.h | 507 -- .../direct3d11/D3D11_PixelShader_HDR10.hlsl | 45 - .../direct3d11/D3D11_PixelShader_NV12.h | 545 -- .../direct3d11/D3D11_PixelShader_NV12.hlsl | 20 - .../direct3d11/D3D11_PixelShader_NV21.h | 548 -- .../direct3d11/D3D11_PixelShader_NV21.hlsl | 20 - .../direct3d11/D3D11_PixelShader_Textures.h | 120 +- src/render/direct3d11/D3D11_PixelShader_YUV.h | 580 -- .../direct3d11/D3D11_PixelShader_YUV.hlsl | 22 - src/render/direct3d11/SDL_render_d3d11.c | 200 +- src/render/direct3d11/SDL_shaders_d3d11.c | 23 +- src/render/direct3d11/SDL_shaders_d3d11.h | 7 +- src/render/direct3d11/compile_shaders.bat | 5 +- .../direct3d12/D3D12_PixelShader_Advanced.h | 1387 +++++ ...1.hlsl => D3D12_PixelShader_Advanced.hlsl} | 22 +- .../direct3d12/D3D12_PixelShader_Colors.h | 442 +- .../direct3d12/D3D12_PixelShader_Colors.hlsl | 2 +- .../direct3d12/D3D12_PixelShader_Common.incl | 175 +- .../direct3d12/D3D12_PixelShader_HDR10.h | 856 --- .../direct3d12/D3D12_PixelShader_HDR10.hlsl | 56 - .../direct3d12/D3D12_PixelShader_NV12.h | 729 --- .../direct3d12/D3D12_PixelShader_NV12.hlsl | 31 - .../direct3d12/D3D12_PixelShader_NV21.h | 729 --- .../direct3d12/D3D12_PixelShader_Textures.h | 495 +- .../D3D12_PixelShader_Textures.hlsl | 6 +- src/render/direct3d12/D3D12_PixelShader_YUV.h | 745 --- .../direct3d12/D3D12_PixelShader_YUV.hlsl | 34 - ...RootSig_YUV.h => D3D12_RootSig_Advanced.h} | 2 +- src/render/direct3d12/D3D12_RootSig_NV.h | 27 - src/render/direct3d12/D3D12_VertexShader.hlsl | 23 +- ...er_YUV.h => D3D12_VertexShader_Advanced.h} | 434 +- src/render/direct3d12/D3D12_VertexShader_NV.h | 647 -- src/render/direct3d12/SDL_render_d3d12.c | 230 +- src/render/direct3d12/SDL_shaders_d3d12.c | 58 +- src/render/direct3d12/SDL_shaders_d3d12.h | 12 +- src/render/direct3d12/compile_shaders.bat | 11 +- src/render/metal/SDL_render_metal.m | 203 +- src/render/metal/SDL_shaders_metal.metal | 240 +- src/render/metal/SDL_shaders_metal_ios.h | 4603 +++++++------- .../metal/SDL_shaders_metal_iphonesimulator.h | 5369 ++++++++--------- src/render/metal/SDL_shaders_metal_macos.h | 4475 ++++++-------- src/render/metal/SDL_shaders_metal_tvos.h | 4603 +++++++------- .../metal/SDL_shaders_metal_tvsimulator.h | 5366 ++++++++-------- src/video/SDL_blit_slow.c | 130 +- src/video/SDL_surface.c | 6 +- 50 files changed, 15071 insertions(+), 21083 deletions(-) create mode 100644 src/render/direct3d11/D3D11_PixelShader_Advanced.h create mode 100644 src/render/direct3d11/D3D11_PixelShader_Advanced.hlsl delete mode 100644 src/render/direct3d11/D3D11_PixelShader_HDR10.h delete mode 100644 src/render/direct3d11/D3D11_PixelShader_HDR10.hlsl delete mode 100644 src/render/direct3d11/D3D11_PixelShader_NV12.h delete mode 100644 src/render/direct3d11/D3D11_PixelShader_NV12.hlsl delete mode 100644 src/render/direct3d11/D3D11_PixelShader_NV21.h delete mode 100644 src/render/direct3d11/D3D11_PixelShader_NV21.hlsl delete mode 100644 src/render/direct3d11/D3D11_PixelShader_YUV.h delete mode 100644 src/render/direct3d11/D3D11_PixelShader_YUV.hlsl create mode 100644 src/render/direct3d12/D3D12_PixelShader_Advanced.h rename src/render/direct3d12/{D3D12_PixelShader_NV21.hlsl => D3D12_PixelShader_Advanced.hlsl} (50%) delete mode 100644 src/render/direct3d12/D3D12_PixelShader_HDR10.h delete mode 100644 src/render/direct3d12/D3D12_PixelShader_HDR10.hlsl delete mode 100644 src/render/direct3d12/D3D12_PixelShader_NV12.h delete mode 100644 src/render/direct3d12/D3D12_PixelShader_NV12.hlsl delete mode 100644 src/render/direct3d12/D3D12_PixelShader_NV21.h delete mode 100644 src/render/direct3d12/D3D12_PixelShader_YUV.h delete mode 100644 src/render/direct3d12/D3D12_PixelShader_YUV.hlsl rename src/render/direct3d12/{D3D12_RootSig_YUV.h => D3D12_RootSig_Advanced.h} (97%) delete mode 100644 src/render/direct3d12/D3D12_RootSig_NV.h rename src/render/direct3d12/{D3D12_VertexShader_YUV.h => D3D12_VertexShader_Advanced.h} (74%) delete mode 100644 src/render/direct3d12/D3D12_VertexShader_NV.h diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index 0bfcafde0318f..f58b39b7a33da 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -212,9 +212,9 @@ extern DECLSPEC void SDLCALL SDL_DestroySurface(SDL_Surface *surface); * for YUV surfaces. * - `SDL_PROP_SURFACE_MAXCLL_NUMBER`: MaxCLL (Maximum Content Light Level) indicates the maximum light level of any single pixel (in cd/m2 or nits) of the content. MaxCLL is usually measured off the final delivered content after mastering. If one uses the full light level of the HDR mastering display and adds a hard clip at its maximum value, MaxCLL would be equal to the peak luminance of the mastering monitor. This defaults to 400 for HDR10 surfaces. * - `SDL_PROP_SURFACE_MAXFALL_NUMBER`: MaxFALL (Maximum Frame Average Light Level) indicates the maximum value of the frame average light level (in cd/m2 or nits) of the content. MaxFALL is calculated by averaging the decoded luminance values of all the pixels within a frame. MaxFALL is usually much lower than MaxCLL. - * - `SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT`: for HDR10 and floating point surfaces, this defines the value of 100% diffuse white, with higher values being displayed in the High Dynamic Range headroom. This defaults to 100 for surfaces using the PQ colorspace and 1.0 for other surfaces. + * - `SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT`: for HDR10 and floating point surfaces, this defines the value of 100% diffuse white, with higher values being displayed in the High Dynamic Range headroom. This defaults to 100 for HDR10 surfaces and 1.0 for other surfaces. * - `SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT`: for HDR10 and floating point surfaces, this defines the maximum dynamic range used by the content, in terms of the SDR white point. This defaults to SDL_PROP_SURFACE_MAXCLL_NUMBER / SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, or 4.0, for HDR10 surfaces. - * - `SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING`: the tone mapping operator used when converting between different SDR/HDR ranges. Currently this supports the form "*=N", where N is a floating point scale factor applied in linear space. + * - `SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING`: the tone mapping operator used when compressing from a surface with high dynamic range to another with lower dynamic range. Currently this supports "chrome", which uses the same tone mapping that Chrome uses for HDR content, the form "*=N", where N is a floating point scale factor applied in linear space, and "none", which disables tone mapping. This defaults to "chrome". * * \param surface the SDL_Surface structure to query * \returns a valid property ID on success or 0 on failure; call diff --git a/src/render/direct3d11/D3D11_PixelShader_Advanced.h b/src/render/direct3d11/D3D11_PixelShader_Advanced.h new file mode 100644 index 0000000000000..a1297c981cffa --- /dev/null +++ b/src/render/direct3d11/D3D11_PixelShader_Advanced.h @@ -0,0 +1,1060 @@ +#if 0 +// +// Generated by Microsoft (R) HLSL Shader Compiler 10.1 +// +// +// Buffer Definitions: +// +// cbuffer Constants +// { +// +// float scRGB_output; // Offset: 0 Size: 4 +// float texture_type; // Offset: 4 Size: 4 +// float input_type; // Offset: 8 Size: 4 +// float color_scale; // Offset: 12 Size: 4 +// float tonemap_method; // Offset: 16 Size: 4 +// float tonemap_factor1; // Offset: 20 Size: 4 +// float tonemap_factor2; // Offset: 24 Size: 4 +// float sdr_white_point; // Offset: 28 Size: 4 +// float4 Yoffset; // Offset: 32 Size: 16 +// float4 Rcoeff; // Offset: 48 Size: 16 +// float4 Gcoeff; // Offset: 64 Size: 16 +// float4 Bcoeff; // Offset: 80 Size: 16 +// +// } +// +// +// Resource Bindings: +// +// Name Type Format Dim HLSL Bind Count +// ------------------------------ ---------- ------- ----------- -------------- ------ +// sampler0 sampler NA NA s0 1 +// texture0 texture float4 2d t0 1 +// texture1 texture float4 2d t1 1 +// texture2 texture float4 2d t2 1 +// Constants cbuffer NA NA cb0 1 +// +// +// +// Input signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_POSITION 0 xyzw 0 POS float +// TEXCOORD 0 xy 1 NONE float xy +// COLOR 0 xyzw 2 NONE float xyzw +// +// +// Output signature: +// +// Name Index Mask Register SysValue Format Used +// -------------------- ----- ------ -------- -------- ------- ------ +// SV_TARGET 0 xyzw 0 TARGET float xyzw +// +ps_5_0 +dcl_globalFlags refactoringAllowed +dcl_constantbuffer CB0[6], immediateIndexed +dcl_sampler s0, mode_default +dcl_resource_texture2d (float,float,float,float) t0 +dcl_resource_texture2d (float,float,float,float) t1 +dcl_resource_texture2d (float,float,float,float) t2 +dcl_input_ps linear v1.xy +dcl_input_ps linear v2.xyzw +dcl_output o0.xyzw +dcl_temps 7 +eq r0.xyzw, cb0[0].yzzz, l(0.000000, 3.000000, 2.000000, 1.000000) +if_nz r0.x + mov r1.xyzw, l(1.000000,1.000000,1.000000,1.000000) +else + eq r0.x, cb0[0].y, l(1.000000) + if_nz r0.x + sample_indexable(texture2d)(float,float,float,float) r1.xyzw, v1.xyxx, t0.xyzw, s0 + else + eq r0.x, cb0[0].y, l(2.000000) + if_nz r0.x + sample_indexable(texture2d)(float,float,float,float) r2.x, v1.xyxx, t0.xyzw, s0 + sample_indexable(texture2d)(float,float,float,float) r2.yz, v1.xyxx, t1.zxyw, s0 + add r2.xyz, r2.xyzx, cb0[2].xyzx + dp3 r1.x, r2.xyzx, cb0[3].xyzx + dp3 r1.y, r2.xyzx, cb0[4].xyzx + dp3 r1.z, r2.xyzx, cb0[5].xyzx + else + eq r0.x, cb0[0].y, l(3.000000) + if_nz r0.x + sample_indexable(texture2d)(float,float,float,float) r2.x, v1.xyxx, t0.xyzw, s0 + sample_indexable(texture2d)(float,float,float,float) r2.yz, v1.xyxx, t1.zyxw, s0 + add r2.xyz, r2.xyzx, cb0[2].xyzx + dp3 r1.x, r2.xyzx, cb0[3].xyzx + dp3 r1.y, r2.xyzx, cb0[4].xyzx + dp3 r1.z, r2.xyzx, cb0[5].xyzx + else + eq r0.x, cb0[0].y, l(4.000000) + if_nz r0.x + sample_indexable(texture2d)(float,float,float,float) r2.x, v1.xyxx, t0.xyzw, s0 + sample_indexable(texture2d)(float,float,float,float) r2.y, v1.xyxx, t1.yxzw, s0 + sample_indexable(texture2d)(float,float,float,float) r2.z, v1.xyxx, t2.yzxw, s0 + add r2.xyz, r2.xyzx, cb0[2].xyzx + dp3 r1.x, r2.xyzx, cb0[3].xyzx + dp3 r1.y, r2.xyzx, cb0[4].xyzx + dp3 r1.z, r2.xyzx, cb0[5].xyzx + else + mov r1.xyz, l(1.000000,0,0,0) + endif + endif + endif + mov r1.w, l(1.000000) + endif +endif +log r2.xyz, |r1.xyzx| +mul r2.xyz, r2.xyzx, l(0.012683, 0.012683, 0.012683, 0.000000) +exp r2.xyz, r2.xyzx +add r3.xyz, r2.xyzx, l(-0.835938, -0.835938, -0.835938, 0.000000) +max r3.xyz, r3.xyzx, l(0.000000, 0.000000, 0.000000, 0.000000) +mad r2.xyz, -r2.xyzx, l(18.687500, 18.687500, 18.687500, 0.000000), l(18.851562, 18.851562, 18.851562, 0.000000) +div r2.xyz, r3.xyzx, r2.xyzx +log r2.xyz, |r2.xyzx| +mul r2.xyz, r2.xyzx, l(6.277395, 6.277395, 6.277395, 0.000000) +exp r2.xyz, r2.xyzx +mul r2.xyz, r2.xyzx, l(10000.000000, 10000.000000, 10000.000000, 0.000000) +div r2.xyz, r2.xyzx, cb0[1].wwww +movc r2.xyz, r0.yyyy, r2.xyzx, r1.xyzx +ne r0.x, cb0[1].x, l(0.000000) +mul r3.xyz, r2.xyzx, cb0[1].yyyy +eq r4.xy, cb0[1].xxxx, l(1.000000, 2.000000, 0.000000, 0.000000) +dp3 r5.x, l(0.627404, 0.329283, 0.043313, 0.000000), r2.xyzx +dp3 r5.y, l(0.069097, 0.919541, 0.011362, 0.000000), r2.xyzx +dp3 r5.z, l(0.016391, 0.088013, 0.895595, 0.000000), r2.xyzx +movc r5.xyz, r0.zzzz, r5.xyzx, r2.xyzx +max r2.w, r5.z, r5.y +max r2.w, r2.w, r5.x +lt r3.w, l(0.000000), r2.w +mad r4.zw, cb0[1].yyyz, r2.wwww, l(0.000000, 0.000000, 1.000000, 1.000000) +div r2.w, r4.z, r4.w +mul r6.xyz, r2.wwww, r5.xyzx +movc r5.xyz, r3.wwww, r6.xyzx, r5.xyzx +dp3 r6.x, l(1.660496, -0.587656, -0.072840, 0.000000), r5.xyzx +dp3 r6.y, l(-0.124547, 1.132895, -0.008348, 0.000000), r5.xyzx +dp3 r6.z, l(-0.018154, -0.100597, 1.118751, 0.000000), r5.xyzx +movc r5.xyz, r0.zzzz, r6.xyzx, r5.xyzx +movc r4.yzw, r4.yyyy, r5.xxyz, r2.xxyz +movc r3.xyz, r4.xxxx, r3.xyzx, r4.yzwy +movc r2.xyz, r0.xxxx, r3.xyzx, r2.xyzx +if_nz r0.w + ne r0.x, l(0.000000, 0.000000, 0.000000, 0.000000), cb0[0].x + if_nz r0.x + ge r3.xyz, l(0.040450, 0.040450, 0.040450, 0.000000), r2.xyzx + mul r4.xyz, r2.xyzx, l(0.077399, 0.077399, 0.077399, 0.000000) + add r5.xyz, r2.xyzx, l(0.055000, 0.055000, 0.055000, 0.000000) + mul r5.xyz, |r5.xyzx|, l(0.947867, 0.947867, 0.947867, 0.000000) + log r5.xyz, r5.xyzx + mul r5.xyz, r5.xyzx, l(2.400000, 2.400000, 2.400000, 0.000000) + exp r5.xyz, r5.xyzx + movc r2.xyz, r3.xyzx, r4.xyzx, r5.xyzx + endif + mul r1.xyz, r2.xyzx, cb0[0].wwww +else + if_nz r0.z + mul r1.xyz, r2.xyzx, cb0[0].wwww + ne r0.x, l(0.000000, 0.000000, 0.000000, 0.000000), cb0[0].x + if_z r0.x + ge r0.xzw, l(0.003131, 0.000000, 0.003131, 0.003131), r1.xxyz + mul r3.xyz, r1.xyzx, l(12.920000, 12.920000, 12.920000, 0.000000) + log r4.xyz, |r1.xyzx| + mul r4.xyz, r4.xyzx, l(0.416667, 0.416667, 0.416667, 0.000000) + exp r4.xyz, r4.xyzx + mad r4.xyz, r4.xyzx, l(1.055000, 1.055000, 1.055000, 0.000000), l(-0.055000, -0.055000, -0.055000, 0.000000) + movc_sat r1.xyz, r0.xzwx, r3.xyzx, r4.xyzx + endif + else + if_nz r0.y + dp3 r0.x, l(1.660496, -0.587656, -0.072840, 0.000000), r2.xyzx + dp3 r0.y, l(-0.124547, 1.132895, -0.008348, 0.000000), r2.xyzx + dp3 r0.z, l(-0.018154, -0.100597, 1.118751, 0.000000), r2.xyzx + mul r1.xyz, r0.xyzx, cb0[0].wwww + ne r0.x, l(0.000000, 0.000000, 0.000000, 0.000000), cb0[0].x + if_z r0.x + ge r0.xyz, l(0.003131, 0.003131, 0.003131, 0.000000), r1.xyzx + mul r3.xyz, r1.xyzx, l(12.920000, 12.920000, 12.920000, 0.000000) + log r4.xyz, |r1.xyzx| + mul r4.xyz, r4.xyzx, l(0.416667, 0.416667, 0.416667, 0.000000) + exp r4.xyz, r4.xyzx + mad r4.xyz, r4.xyzx, l(1.055000, 1.055000, 1.055000, 0.000000), l(-0.055000, -0.055000, -0.055000, 0.000000) + movc_sat r1.xyz, r0.xyzx, r3.xyzx, r4.xyzx + endif + else + mul r1.xyz, r2.xyzx, cb0[0].wwww + endif + endif +endif +mul o0.xyzw, r1.xyzw, v2.xyzw +ret +// Approximately 126 instruction slots used +#endif + +const BYTE g_main[] = +{ + 68, 88, 66, 67, 154, 200, + 28, 25, 30, 140, 0, 183, + 251, 32, 6, 185, 210, 29, + 251, 110, 1, 0, 0, 0, + 64, 20, 0, 0, 5, 0, + 0, 0, 52, 0, 0, 0, + 80, 4, 0, 0, 196, 4, + 0, 0, 248, 4, 0, 0, + 164, 19, 0, 0, 82, 68, + 69, 70, 20, 4, 0, 0, + 1, 0, 0, 0, 12, 1, + 0, 0, 5, 0, 0, 0, + 60, 0, 0, 0, 0, 5, + 255, 255, 0, 1, 0, 0, + 233, 3, 0, 0, 82, 68, + 49, 49, 60, 0, 0, 0, + 24, 0, 0, 0, 32, 0, + 0, 0, 40, 0, 0, 0, + 36, 0, 0, 0, 12, 0, + 0, 0, 0, 0, 0, 0, + 220, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 1, 0, + 0, 0, 229, 0, 0, 0, + 2, 0, 0, 0, 5, 0, + 0, 0, 4, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 1, 0, 0, 0, + 13, 0, 0, 0, 238, 0, + 0, 0, 2, 0, 0, 0, + 5, 0, 0, 0, 4, 0, + 0, 0, 255, 255, 255, 255, + 1, 0, 0, 0, 1, 0, + 0, 0, 13, 0, 0, 0, + 247, 0, 0, 0, 2, 0, + 0, 0, 5, 0, 0, 0, + 4, 0, 0, 0, 255, 255, + 255, 255, 2, 0, 0, 0, + 1, 0, 0, 0, 13, 0, + 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 115, 97, + 109, 112, 108, 101, 114, 48, + 0, 116, 101, 120, 116, 117, + 114, 101, 48, 0, 116, 101, + 120, 116, 117, 114, 101, 49, + 0, 116, 101, 120, 116, 117, + 114, 101, 50, 0, 67, 111, + 110, 115, 116, 97, 110, 116, + 115, 0, 171, 171, 0, 1, + 0, 0, 12, 0, 0, 0, + 36, 1, 0, 0, 96, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 3, + 0, 0, 0, 0, 0, 0, + 4, 0, 0, 0, 2, 0, + 0, 0, 24, 3, 0, 0, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 60, 3, 0, 0, + 4, 0, 0, 0, 4, 0, + 0, 0, 2, 0, 0, 0, + 24, 3, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 73, 3, 0, 0, 8, 0, + 0, 0, 4, 0, 0, 0, + 2, 0, 0, 0, 24, 3, + 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 84, 3, + 0, 0, 12, 0, 0, 0, + 4, 0, 0, 0, 2, 0, + 0, 0, 24, 3, 0, 0, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 96, 3, 0, 0, + 16, 0, 0, 0, 4, 0, + 0, 0, 2, 0, 0, 0, + 24, 3, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 111, 3, 0, 0, 20, 0, + 0, 0, 4, 0, 0, 0, + 2, 0, 0, 0, 24, 3, + 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 127, 3, + 0, 0, 24, 0, 0, 0, + 4, 0, 0, 0, 2, 0, + 0, 0, 24, 3, 0, 0, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 143, 3, 0, 0, + 28, 0, 0, 0, 4, 0, + 0, 0, 2, 0, 0, 0, + 24, 3, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 159, 3, 0, 0, 32, 0, + 0, 0, 16, 0, 0, 0, + 2, 0, 0, 0, 176, 3, + 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 212, 3, + 0, 0, 48, 0, 0, 0, + 16, 0, 0, 0, 2, 0, + 0, 0, 176, 3, 0, 0, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 219, 3, 0, 0, + 64, 0, 0, 0, 16, 0, + 0, 0, 2, 0, 0, 0, + 176, 3, 0, 0, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 255, 255, + 255, 255, 0, 0, 0, 0, + 226, 3, 0, 0, 80, 0, + 0, 0, 16, 0, 0, 0, + 2, 0, 0, 0, 176, 3, + 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 0, 0, + 0, 0, 255, 255, 255, 255, + 0, 0, 0, 0, 115, 99, + 82, 71, 66, 95, 111, 117, + 116, 112, 117, 116, 0, 102, + 108, 111, 97, 116, 0, 171, + 0, 0, 3, 0, 1, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 17, 3, 0, 0, + 116, 101, 120, 116, 117, 114, + 101, 95, 116, 121, 112, 101, + 0, 105, 110, 112, 117, 116, + 95, 116, 121, 112, 101, 0, + 99, 111, 108, 111, 114, 95, + 115, 99, 97, 108, 101, 0, + 116, 111, 110, 101, 109, 97, + 112, 95, 109, 101, 116, 104, + 111, 100, 0, 116, 111, 110, + 101, 109, 97, 112, 95, 102, + 97, 99, 116, 111, 114, 49, + 0, 116, 111, 110, 101, 109, + 97, 112, 95, 102, 97, 99, + 116, 111, 114, 50, 0, 115, + 100, 114, 95, 119, 104, 105, + 116, 101, 95, 112, 111, 105, + 110, 116, 0, 89, 111, 102, + 102, 115, 101, 116, 0, 102, + 108, 111, 97, 116, 52, 0, + 171, 171, 1, 0, 3, 0, + 1, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 167, 3, + 0, 0, 82, 99, 111, 101, + 102, 102, 0, 71, 99, 111, + 101, 102, 102, 0, 66, 99, + 111, 101, 102, 102, 0, 77, + 105, 99, 114, 111, 115, 111, + 102, 116, 32, 40, 82, 41, + 32, 72, 76, 83, 76, 32, + 83, 104, 97, 100, 101, 114, + 32, 67, 111, 109, 112, 105, + 108, 101, 114, 32, 49, 48, + 46, 49, 0, 171, 171, 171, + 73, 83, 71, 78, 108, 0, + 0, 0, 3, 0, 0, 0, + 8, 0, 0, 0, 80, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0, 92, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 1, 0, 0, 0, + 3, 3, 0, 0, 101, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 3, 0, + 0, 0, 2, 0, 0, 0, + 15, 15, 0, 0, 83, 86, + 95, 80, 79, 83, 73, 84, + 73, 79, 78, 0, 84, 69, + 88, 67, 79, 79, 82, 68, + 0, 67, 79, 76, 79, 82, + 0, 171, 79, 83, 71, 78, + 44, 0, 0, 0, 1, 0, + 0, 0, 8, 0, 0, 0, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, + 83, 86, 95, 84, 65, 82, + 71, 69, 84, 0, 171, 171, + 83, 72, 69, 88, 164, 14, + 0, 0, 80, 0, 0, 0, + 169, 3, 0, 0, 106, 8, + 0, 1, 89, 0, 0, 4, + 70, 142, 32, 0, 0, 0, + 0, 0, 6, 0, 0, 0, + 90, 0, 0, 3, 0, 96, + 16, 0, 0, 0, 0, 0, + 88, 24, 0, 4, 0, 112, + 16, 0, 0, 0, 0, 0, + 85, 85, 0, 0, 88, 24, + 0, 4, 0, 112, 16, 0, + 1, 0, 0, 0, 85, 85, + 0, 0, 88, 24, 0, 4, + 0, 112, 16, 0, 2, 0, + 0, 0, 85, 85, 0, 0, + 98, 16, 0, 3, 50, 16, + 16, 0, 1, 0, 0, 0, + 98, 16, 0, 3, 242, 16, + 16, 0, 2, 0, 0, 0, + 101, 0, 0, 3, 242, 32, + 16, 0, 0, 0, 0, 0, + 104, 0, 0, 2, 7, 0, + 0, 0, 24, 0, 0, 11, + 242, 0, 16, 0, 0, 0, + 0, 0, 150, 138, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 64, 64, 0, 0, 0, 64, + 0, 0, 128, 63, 31, 0, + 4, 3, 10, 0, 16, 0, + 0, 0, 0, 0, 54, 0, + 0, 8, 242, 0, 16, 0, + 1, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 128, 63, + 0, 0, 128, 63, 0, 0, + 128, 63, 0, 0, 128, 63, + 18, 0, 0, 1, 24, 0, + 0, 8, 18, 0, 16, 0, + 0, 0, 0, 0, 26, 128, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 31, 0, 4, 3, 10, 0, + 16, 0, 0, 0, 0, 0, + 69, 0, 0, 139, 194, 0, + 0, 128, 67, 85, 21, 0, + 242, 0, 16, 0, 1, 0, + 0, 0, 70, 16, 16, 0, + 1, 0, 0, 0, 70, 126, + 16, 0, 0, 0, 0, 0, + 0, 96, 16, 0, 0, 0, + 0, 0, 18, 0, 0, 1, + 24, 0, 0, 8, 18, 0, + 16, 0, 0, 0, 0, 0, + 26, 128, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 64, 31, 0, 4, 3, + 10, 0, 16, 0, 0, 0, + 0, 0, 69, 0, 0, 139, + 194, 0, 0, 128, 67, 85, + 21, 0, 18, 0, 16, 0, + 2, 0, 0, 0, 70, 16, + 16, 0, 1, 0, 0, 0, + 70, 126, 16, 0, 0, 0, + 0, 0, 0, 96, 16, 0, + 0, 0, 0, 0, 69, 0, + 0, 139, 194, 0, 0, 128, + 67, 85, 21, 0, 98, 0, + 16, 0, 2, 0, 0, 0, + 70, 16, 16, 0, 1, 0, + 0, 0, 38, 125, 16, 0, + 1, 0, 0, 0, 0, 96, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 114, 0, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 70, 130, 32, 0, + 0, 0, 0, 0, 2, 0, + 0, 0, 16, 0, 0, 8, + 18, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 70, 130, + 32, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 16, 0, + 0, 8, 34, 0, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 70, 130, 32, 0, 0, 0, + 0, 0, 4, 0, 0, 0, + 16, 0, 0, 8, 66, 0, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 70, 130, 32, 0, + 0, 0, 0, 0, 5, 0, + 0, 0, 18, 0, 0, 1, + 24, 0, 0, 8, 18, 0, + 16, 0, 0, 0, 0, 0, + 26, 128, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 64, 64, 31, 0, 4, 3, + 10, 0, 16, 0, 0, 0, + 0, 0, 69, 0, 0, 139, + 194, 0, 0, 128, 67, 85, + 21, 0, 18, 0, 16, 0, + 2, 0, 0, 0, 70, 16, + 16, 0, 1, 0, 0, 0, + 70, 126, 16, 0, 0, 0, + 0, 0, 0, 96, 16, 0, + 0, 0, 0, 0, 69, 0, + 0, 139, 194, 0, 0, 128, + 67, 85, 21, 0, 98, 0, + 16, 0, 2, 0, 0, 0, + 70, 16, 16, 0, 1, 0, + 0, 0, 102, 124, 16, 0, + 1, 0, 0, 0, 0, 96, + 16, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 114, 0, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 70, 130, 32, 0, + 0, 0, 0, 0, 2, 0, + 0, 0, 16, 0, 0, 8, + 18, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 70, 130, + 32, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 16, 0, + 0, 8, 34, 0, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 70, 130, 32, 0, 0, 0, + 0, 0, 4, 0, 0, 0, + 16, 0, 0, 8, 66, 0, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 70, 130, 32, 0, + 0, 0, 0, 0, 5, 0, + 0, 0, 18, 0, 0, 1, + 24, 0, 0, 8, 18, 0, + 16, 0, 0, 0, 0, 0, + 26, 128, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 128, 64, 31, 0, 4, 3, + 10, 0, 16, 0, 0, 0, + 0, 0, 69, 0, 0, 139, + 194, 0, 0, 128, 67, 85, + 21, 0, 18, 0, 16, 0, + 2, 0, 0, 0, 70, 16, + 16, 0, 1, 0, 0, 0, + 70, 126, 16, 0, 0, 0, + 0, 0, 0, 96, 16, 0, + 0, 0, 0, 0, 69, 0, + 0, 139, 194, 0, 0, 128, + 67, 85, 21, 0, 34, 0, + 16, 0, 2, 0, 0, 0, + 70, 16, 16, 0, 1, 0, + 0, 0, 22, 126, 16, 0, + 1, 0, 0, 0, 0, 96, + 16, 0, 0, 0, 0, 0, + 69, 0, 0, 139, 194, 0, + 0, 128, 67, 85, 21, 0, + 66, 0, 16, 0, 2, 0, + 0, 0, 70, 16, 16, 0, + 1, 0, 0, 0, 150, 124, + 16, 0, 2, 0, 0, 0, + 0, 96, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 8, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 70, 130, + 32, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 16, 0, + 0, 8, 18, 0, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 70, 130, 32, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 16, 0, 0, 8, 34, 0, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 70, 130, 32, 0, + 0, 0, 0, 0, 4, 0, + 0, 0, 16, 0, 0, 8, + 66, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 70, 130, + 32, 0, 0, 0, 0, 0, + 5, 0, 0, 0, 18, 0, + 0, 1, 54, 0, 0, 8, + 114, 0, 16, 0, 1, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 128, 63, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 21, 0, + 0, 1, 21, 0, 0, 1, + 21, 0, 0, 1, 54, 0, + 0, 5, 130, 0, 16, 0, + 1, 0, 0, 0, 1, 64, + 0, 0, 0, 0, 128, 63, + 21, 0, 0, 1, 21, 0, + 0, 1, 47, 0, 0, 6, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 128, + 129, 0, 0, 0, 1, 0, + 0, 0, 56, 0, 0, 10, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 2, 64, + 0, 0, 172, 205, 79, 60, + 172, 205, 79, 60, 172, 205, + 79, 60, 0, 0, 0, 0, + 25, 0, 0, 5, 114, 0, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 0, 0, 0, 10, + 114, 0, 16, 0, 3, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 86, 191, + 0, 0, 86, 191, 0, 0, + 86, 191, 0, 0, 0, 0, + 52, 0, 0, 10, 114, 0, + 16, 0, 3, 0, 0, 0, + 70, 2, 16, 0, 3, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 50, 0, + 0, 16, 114, 0, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 128, 65, 0, 0, 0, + 2, 0, 0, 0, 2, 64, + 0, 0, 0, 128, 149, 65, + 0, 128, 149, 65, 0, 128, + 149, 65, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 208, + 150, 65, 0, 208, 150, 65, + 0, 208, 150, 65, 0, 0, + 0, 0, 14, 0, 0, 7, + 114, 0, 16, 0, 2, 0, + 0, 0, 70, 2, 16, 0, + 3, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 47, 0, 0, 6, 114, 0, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 128, 129, 0, + 0, 0, 2, 0, 0, 0, + 56, 0, 0, 10, 114, 0, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 2, 64, 0, 0, + 107, 224, 200, 64, 107, 224, + 200, 64, 107, 224, 200, 64, + 0, 0, 0, 0, 25, 0, + 0, 5, 114, 0, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 56, 0, 0, 10, 114, 0, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 2, 64, 0, 0, + 0, 64, 28, 70, 0, 64, + 28, 70, 0, 64, 28, 70, + 0, 0, 0, 0, 14, 0, + 0, 8, 114, 0, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 246, 143, 32, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 55, 0, 0, 9, 114, 0, + 16, 0, 2, 0, 0, 0, + 86, 5, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 0, 1, 0, 0, 0, + 57, 0, 0, 8, 18, 0, + 16, 0, 0, 0, 0, 0, + 10, 128, 32, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 1, 64, 0, 0, 0, 0, + 0, 0, 56, 0, 0, 8, + 114, 0, 16, 0, 3, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 86, 133, + 32, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 24, 0, + 0, 11, 50, 0, 16, 0, + 4, 0, 0, 0, 6, 128, + 32, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 2, 64, + 0, 0, 0, 0, 128, 63, + 0, 0, 0, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 16, 0, 0, 10, 18, 0, + 16, 0, 5, 0, 0, 0, + 2, 64, 0, 0, 140, 157, + 32, 63, 200, 151, 168, 62, + 249, 104, 49, 61, 0, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 16, 0, + 0, 10, 34, 0, 16, 0, + 5, 0, 0, 0, 2, 64, + 0, 0, 186, 130, 141, 61, + 10, 103, 107, 63, 175, 39, + 58, 60, 0, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 16, 0, 0, 10, + 66, 0, 16, 0, 5, 0, + 0, 0, 2, 64, 0, 0, + 107, 70, 134, 60, 41, 64, + 180, 61, 183, 69, 101, 63, + 0, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 55, 0, 0, 9, 114, 0, + 16, 0, 5, 0, 0, 0, + 166, 10, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 5, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 52, 0, 0, 7, 130, 0, + 16, 0, 2, 0, 0, 0, + 42, 0, 16, 0, 5, 0, + 0, 0, 26, 0, 16, 0, + 5, 0, 0, 0, 52, 0, + 0, 7, 130, 0, 16, 0, + 2, 0, 0, 0, 58, 0, + 16, 0, 2, 0, 0, 0, + 10, 0, 16, 0, 5, 0, + 0, 0, 49, 0, 0, 7, + 130, 0, 16, 0, 3, 0, + 0, 0, 1, 64, 0, 0, + 0, 0, 0, 0, 58, 0, + 16, 0, 2, 0, 0, 0, + 50, 0, 0, 13, 194, 0, + 16, 0, 4, 0, 0, 0, + 86, 137, 32, 0, 0, 0, + 0, 0, 1, 0, 0, 0, + 246, 15, 16, 0, 2, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 63, + 0, 0, 128, 63, 14, 0, + 0, 7, 130, 0, 16, 0, + 2, 0, 0, 0, 42, 0, + 16, 0, 4, 0, 0, 0, + 58, 0, 16, 0, 4, 0, + 0, 0, 56, 0, 0, 7, + 114, 0, 16, 0, 6, 0, + 0, 0, 246, 15, 16, 0, + 2, 0, 0, 0, 70, 2, + 16, 0, 5, 0, 0, 0, + 55, 0, 0, 9, 114, 0, + 16, 0, 5, 0, 0, 0, + 246, 15, 16, 0, 3, 0, + 0, 0, 70, 2, 16, 0, + 6, 0, 0, 0, 70, 2, + 16, 0, 5, 0, 0, 0, + 16, 0, 0, 10, 18, 0, + 16, 0, 6, 0, 0, 0, + 2, 64, 0, 0, 34, 139, + 212, 63, 160, 112, 22, 191, + 35, 45, 149, 189, 0, 0, + 0, 0, 70, 2, 16, 0, + 5, 0, 0, 0, 16, 0, + 0, 10, 34, 0, 16, 0, + 6, 0, 0, 0, 2, 64, + 0, 0, 127, 18, 255, 189, + 180, 2, 145, 63, 13, 198, + 8, 188, 0, 0, 0, 0, + 70, 2, 16, 0, 5, 0, + 0, 0, 16, 0, 0, 10, + 66, 0, 16, 0, 6, 0, + 0, 0, 2, 64, 0, 0, + 179, 183, 148, 188, 205, 5, + 206, 189, 60, 51, 143, 63, + 0, 0, 0, 0, 70, 2, + 16, 0, 5, 0, 0, 0, + 55, 0, 0, 9, 114, 0, + 16, 0, 5, 0, 0, 0, + 166, 10, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 6, 0, 0, 0, 70, 2, + 16, 0, 5, 0, 0, 0, + 55, 0, 0, 9, 226, 0, + 16, 0, 4, 0, 0, 0, + 86, 5, 16, 0, 4, 0, + 0, 0, 6, 9, 16, 0, + 5, 0, 0, 0, 6, 9, + 16, 0, 2, 0, 0, 0, + 55, 0, 0, 9, 114, 0, + 16, 0, 3, 0, 0, 0, + 6, 0, 16, 0, 4, 0, + 0, 0, 70, 2, 16, 0, + 3, 0, 0, 0, 150, 7, + 16, 0, 4, 0, 0, 0, + 55, 0, 0, 9, 114, 0, + 16, 0, 2, 0, 0, 0, + 6, 0, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 3, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 31, 0, 4, 3, 58, 0, + 16, 0, 0, 0, 0, 0, + 57, 0, 0, 11, 18, 0, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 10, 128, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 31, 0, 4, 3, + 10, 0, 16, 0, 0, 0, + 0, 0, 29, 0, 0, 10, + 114, 0, 16, 0, 3, 0, + 0, 0, 2, 64, 0, 0, + 230, 174, 37, 61, 230, 174, + 37, 61, 230, 174, 37, 61, + 0, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 56, 0, 0, 10, 114, 0, + 16, 0, 4, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 2, 64, 0, 0, + 145, 131, 158, 61, 145, 131, + 158, 61, 145, 131, 158, 61, + 0, 0, 0, 0, 0, 0, + 0, 10, 114, 0, 16, 0, + 5, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 2, 64, 0, 0, 174, 71, + 97, 61, 174, 71, 97, 61, + 174, 71, 97, 61, 0, 0, + 0, 0, 56, 0, 0, 11, + 114, 0, 16, 0, 5, 0, + 0, 0, 70, 2, 16, 128, + 129, 0, 0, 0, 5, 0, + 0, 0, 2, 64, 0, 0, + 111, 167, 114, 63, 111, 167, + 114, 63, 111, 167, 114, 63, + 0, 0, 0, 0, 47, 0, + 0, 5, 114, 0, 16, 0, + 5, 0, 0, 0, 70, 2, + 16, 0, 5, 0, 0, 0, + 56, 0, 0, 10, 114, 0, + 16, 0, 5, 0, 0, 0, + 70, 2, 16, 0, 5, 0, + 0, 0, 2, 64, 0, 0, + 154, 153, 25, 64, 154, 153, + 25, 64, 154, 153, 25, 64, + 0, 0, 0, 0, 25, 0, + 0, 5, 114, 0, 16, 0, + 5, 0, 0, 0, 70, 2, + 16, 0, 5, 0, 0, 0, + 55, 0, 0, 9, 114, 0, + 16, 0, 2, 0, 0, 0, + 70, 2, 16, 0, 3, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 70, 2, + 16, 0, 5, 0, 0, 0, + 21, 0, 0, 1, 56, 0, + 0, 8, 114, 0, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 246, 143, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 18, 0, 0, 1, 31, 0, + 4, 3, 42, 0, 16, 0, + 0, 0, 0, 0, 56, 0, + 0, 8, 114, 0, 16, 0, + 1, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 246, 143, 32, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 57, 0, 0, 11, 18, 0, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 10, 128, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 31, 0, 0, 3, + 10, 0, 16, 0, 0, 0, + 0, 0, 29, 0, 0, 10, + 210, 0, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 28, 46, 77, 59, 0, 0, + 0, 0, 28, 46, 77, 59, + 28, 46, 77, 59, 6, 9, + 16, 0, 1, 0, 0, 0, + 56, 0, 0, 10, 114, 0, + 16, 0, 3, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 2, 64, 0, 0, + 82, 184, 78, 65, 82, 184, + 78, 65, 82, 184, 78, 65, + 0, 0, 0, 0, 47, 0, + 0, 6, 114, 0, 16, 0, + 4, 0, 0, 0, 70, 2, + 16, 128, 129, 0, 0, 0, + 1, 0, 0, 0, 56, 0, + 0, 10, 114, 0, 16, 0, + 4, 0, 0, 0, 70, 2, + 16, 0, 4, 0, 0, 0, + 2, 64, 0, 0, 85, 85, + 213, 62, 85, 85, 213, 62, + 85, 85, 213, 62, 0, 0, + 0, 0, 25, 0, 0, 5, + 114, 0, 16, 0, 4, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 50, 0, + 0, 15, 114, 0, 16, 0, + 4, 0, 0, 0, 70, 2, + 16, 0, 4, 0, 0, 0, + 2, 64, 0, 0, 61, 10, + 135, 63, 61, 10, 135, 63, + 61, 10, 135, 63, 0, 0, + 0, 0, 2, 64, 0, 0, + 174, 71, 97, 189, 174, 71, + 97, 189, 174, 71, 97, 189, + 0, 0, 0, 0, 55, 32, + 0, 9, 114, 0, 16, 0, + 1, 0, 0, 0, 134, 3, + 16, 0, 0, 0, 0, 0, + 70, 2, 16, 0, 3, 0, + 0, 0, 70, 2, 16, 0, + 4, 0, 0, 0, 21, 0, + 0, 1, 18, 0, 0, 1, + 31, 0, 4, 3, 26, 0, + 16, 0, 0, 0, 0, 0, + 16, 0, 0, 10, 18, 0, + 16, 0, 0, 0, 0, 0, + 2, 64, 0, 0, 34, 139, + 212, 63, 160, 112, 22, 191, + 35, 45, 149, 189, 0, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 16, 0, + 0, 10, 34, 0, 16, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 127, 18, 255, 189, + 180, 2, 145, 63, 13, 198, + 8, 188, 0, 0, 0, 0, + 70, 2, 16, 0, 2, 0, + 0, 0, 16, 0, 0, 10, + 66, 0, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 179, 183, 148, 188, 205, 5, + 206, 189, 60, 51, 143, 63, + 0, 0, 0, 0, 70, 2, + 16, 0, 2, 0, 0, 0, + 56, 0, 0, 8, 114, 0, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 246, 143, 32, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 57, 0, 0, 11, + 18, 0, 16, 0, 0, 0, + 0, 0, 2, 64, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 10, 128, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 31, 0, + 0, 3, 10, 0, 16, 0, + 0, 0, 0, 0, 29, 0, + 0, 10, 114, 0, 16, 0, + 0, 0, 0, 0, 2, 64, + 0, 0, 28, 46, 77, 59, + 28, 46, 77, 59, 28, 46, + 77, 59, 0, 0, 0, 0, + 70, 2, 16, 0, 1, 0, + 0, 0, 56, 0, 0, 10, + 114, 0, 16, 0, 3, 0, + 0, 0, 70, 2, 16, 0, + 1, 0, 0, 0, 2, 64, + 0, 0, 82, 184, 78, 65, + 82, 184, 78, 65, 82, 184, + 78, 65, 0, 0, 0, 0, + 47, 0, 0, 6, 114, 0, + 16, 0, 4, 0, 0, 0, + 70, 2, 16, 128, 129, 0, + 0, 0, 1, 0, 0, 0, + 56, 0, 0, 10, 114, 0, + 16, 0, 4, 0, 0, 0, + 70, 2, 16, 0, 4, 0, + 0, 0, 2, 64, 0, 0, + 85, 85, 213, 62, 85, 85, + 213, 62, 85, 85, 213, 62, + 0, 0, 0, 0, 25, 0, + 0, 5, 114, 0, 16, 0, + 4, 0, 0, 0, 70, 2, + 16, 0, 4, 0, 0, 0, + 50, 0, 0, 15, 114, 0, + 16, 0, 4, 0, 0, 0, + 70, 2, 16, 0, 4, 0, + 0, 0, 2, 64, 0, 0, + 61, 10, 135, 63, 61, 10, + 135, 63, 61, 10, 135, 63, + 0, 0, 0, 0, 2, 64, + 0, 0, 174, 71, 97, 189, + 174, 71, 97, 189, 174, 71, + 97, 189, 0, 0, 0, 0, + 55, 32, 0, 9, 114, 0, + 16, 0, 1, 0, 0, 0, + 70, 2, 16, 0, 0, 0, + 0, 0, 70, 2, 16, 0, + 3, 0, 0, 0, 70, 2, + 16, 0, 4, 0, 0, 0, + 21, 0, 0, 1, 18, 0, + 0, 1, 56, 0, 0, 8, + 114, 0, 16, 0, 1, 0, + 0, 0, 70, 2, 16, 0, + 2, 0, 0, 0, 246, 143, + 32, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 21, 0, + 0, 1, 21, 0, 0, 1, + 21, 0, 0, 1, 56, 0, + 0, 7, 242, 32, 16, 0, + 0, 0, 0, 0, 70, 14, + 16, 0, 1, 0, 0, 0, + 70, 30, 16, 0, 2, 0, + 0, 0, 62, 0, 0, 1, + 83, 84, 65, 84, 148, 0, + 0, 0, 126, 0, 0, 0, + 7, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 0, + 71, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 9, 0, 0, 0, 11, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 10, 0, + 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0 +}; diff --git a/src/render/direct3d11/D3D11_PixelShader_Advanced.hlsl b/src/render/direct3d11/D3D11_PixelShader_Advanced.hlsl new file mode 100644 index 0000000000000..39dda47bca849 --- /dev/null +++ b/src/render/direct3d11/D3D11_PixelShader_Advanced.hlsl @@ -0,0 +1,7 @@ + +#include "D3D11_PixelShader_Common.incl" + +float4 main(PixelShaderInput input) : SV_TARGET +{ + return AdvancedPixelShader(input); +} diff --git a/src/render/direct3d11/D3D11_PixelShader_Colors.h b/src/render/direct3d11/D3D11_PixelShader_Colors.h index ad68f93bb76a4..6658e4d11eee5 100644 --- a/src/render/direct3d11/D3D11_PixelShader_Colors.h +++ b/src/render/direct3d11/D3D11_PixelShader_Colors.h @@ -9,13 +9,17 @@ // { // // float scRGB_output; // Offset: 0 Size: 4 [unused] -// float color_scale; // Offset: 4 Size: 4 -// float unused1; // Offset: 8 Size: 4 [unused] -// float unused2; // Offset: 12 Size: 4 [unused] -// float4 Yoffset; // Offset: 16 Size: 16 [unused] -// float4 Rcoeff; // Offset: 32 Size: 16 [unused] -// float4 Gcoeff; // Offset: 48 Size: 16 [unused] -// float4 Bcoeff; // Offset: 64 Size: 16 [unused] +// float texture_type; // Offset: 4 Size: 4 [unused] +// float input_type; // Offset: 8 Size: 4 [unused] +// float color_scale; // Offset: 12 Size: 4 +// float tonemap_method; // Offset: 16 Size: 4 [unused] +// float tonemap_factor1; // Offset: 20 Size: 4 [unused] +// float tonemap_factor2; // Offset: 24 Size: 4 [unused] +// float sdr_white_point; // Offset: 28 Size: 4 [unused] +// float4 Yoffset; // Offset: 32 Size: 16 [unused] +// float4 Rcoeff; // Offset: 48 Size: 16 [unused] +// float4 Gcoeff; // Offset: 64 Size: 16 [unused] +// float4 Bcoeff; // Offset: 80 Size: 16 [unused] // // } // @@ -55,7 +59,7 @@ // ps_2_0 dcl t1 - mul r0.xyz, t1, c0.y + mul r0.xyz, t1, c0.w mov r0.w, t1.w mov oC0, r0 @@ -65,7 +69,7 @@ dcl_constantbuffer CB0[1], immediateIndexed dcl_input_ps linear v2.xyzw dcl_output o0.xyzw dcl_temps 1 -mov r0.x, cb0[0].y +mov r0.x, cb0[0].w mov r0.w, l(1.000000) mul o0.xyzw, r0.xxxw, v2.xyzw ret @@ -74,15 +78,15 @@ ret const BYTE g_main[] = { - 68, 88, 66, 67, 76, 154, - 233, 103, 201, 50, 167, 173, - 112, 159, 134, 20, 133, 254, - 166, 35, 1, 0, 0, 0, - 24, 4, 0, 0, 6, 0, + 68, 88, 66, 67, 78, 223, + 23, 23, 93, 184, 255, 26, + 153, 0, 220, 179, 25, 194, + 30, 249, 1, 0, 0, 0, + 192, 4, 0, 0, 6, 0, 0, 0, 56, 0, 0, 0, 172, 0, 0, 0, 56, 1, 0, 0, 180, 1, 0, 0, - 112, 3, 0, 0, 228, 3, + 24, 4, 0, 0, 140, 4, 0, 0, 65, 111, 110, 57, 108, 0, 0, 0, 108, 0, 0, 0, 0, 2, 255, 255, @@ -97,7 +101,7 @@ const BYTE g_main[] = 0, 0, 0, 128, 1, 0, 15, 176, 5, 0, 0, 3, 0, 0, 7, 128, 1, 0, - 228, 176, 0, 0, 85, 160, + 228, 176, 0, 0, 255, 160, 1, 0, 0, 2, 0, 0, 8, 128, 1, 0, 255, 176, 1, 0, 0, 2, 0, 8, @@ -115,7 +119,7 @@ const BYTE g_main[] = 104, 0, 0, 2, 1, 0, 0, 0, 54, 0, 0, 6, 18, 0, 16, 0, 0, 0, - 0, 0, 26, 128, 32, 0, + 0, 0, 58, 128, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 5, 130, 0, 16, 0, 0, 0, @@ -147,12 +151,12 @@ const BYTE g_main[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 68, - 69, 70, 180, 1, 0, 0, + 69, 70, 92, 2, 0, 0, 1, 0, 0, 0, 72, 0, 0, 0, 1, 0, 0, 0, 28, 0, 0, 0, 0, 4, 255, 255, 0, 1, 0, 0, - 137, 1, 0, 0, 60, 0, + 49, 2, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -160,54 +164,82 @@ const BYTE g_main[] = 0, 0, 1, 0, 0, 0, 67, 111, 110, 115, 116, 97, 110, 116, 115, 0, 171, 171, - 60, 0, 0, 0, 8, 0, + 60, 0, 0, 0, 12, 0, 0, 0, 96, 0, 0, 0, - 80, 0, 0, 0, 0, 0, + 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 32, 1, 0, 0, 0, 0, + 128, 1, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 48, 1, + 0, 0, 0, 0, 144, 1, 0, 0, 0, 0, 0, 0, - 64, 1, 0, 0, 4, 0, + 160, 1, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, - 2, 0, 0, 0, 48, 1, + 0, 0, 0, 0, 144, 1, 0, 0, 0, 0, 0, 0, - 76, 1, 0, 0, 8, 0, + 173, 1, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 48, 1, + 0, 0, 0, 0, 144, 1, 0, 0, 0, 0, 0, 0, - 84, 1, 0, 0, 12, 0, + 184, 1, 0, 0, 12, 0, 0, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 48, 1, + 2, 0, 0, 0, 144, 1, 0, 0, 0, 0, 0, 0, - 92, 1, 0, 0, 16, 0, + 196, 1, 0, 0, 16, 0, + 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 144, 1, + 0, 0, 0, 0, 0, 0, + 211, 1, 0, 0, 20, 0, + 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 144, 1, + 0, 0, 0, 0, 0, 0, + 227, 1, 0, 0, 24, 0, + 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 144, 1, + 0, 0, 0, 0, 0, 0, + 243, 1, 0, 0, 28, 0, + 0, 0, 4, 0, 0, 0, + 0, 0, 0, 0, 144, 1, + 0, 0, 0, 0, 0, 0, + 3, 2, 0, 0, 32, 0, 0, 0, 16, 0, 0, 0, - 0, 0, 0, 0, 100, 1, + 0, 0, 0, 0, 12, 2, 0, 0, 0, 0, 0, 0, - 116, 1, 0, 0, 32, 0, + 28, 2, 0, 0, 48, 0, 0, 0, 16, 0, 0, 0, - 0, 0, 0, 0, 100, 1, + 0, 0, 0, 0, 12, 2, 0, 0, 0, 0, 0, 0, - 123, 1, 0, 0, 48, 0, + 35, 2, 0, 0, 64, 0, 0, 0, 16, 0, 0, 0, - 0, 0, 0, 0, 100, 1, + 0, 0, 0, 0, 12, 2, 0, 0, 0, 0, 0, 0, - 130, 1, 0, 0, 64, 0, + 42, 2, 0, 0, 80, 0, 0, 0, 16, 0, 0, 0, - 0, 0, 0, 0, 100, 1, + 0, 0, 0, 0, 12, 2, 0, 0, 0, 0, 0, 0, 115, 99, 82, 71, 66, 95, 111, 117, 116, 112, 117, 116, 0, 171, 171, 171, 0, 0, 3, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 99, 111, 108, 111, + 0, 0, 116, 101, 120, 116, + 117, 114, 101, 95, 116, 121, + 112, 101, 0, 105, 110, 112, + 117, 116, 95, 116, 121, 112, + 101, 0, 99, 111, 108, 111, 114, 95, 115, 99, 97, 108, - 101, 0, 117, 110, 117, 115, - 101, 100, 49, 0, 117, 110, - 117, 115, 101, 100, 50, 0, - 89, 111, 102, 102, 115, 101, - 116, 0, 1, 0, 3, 0, + 101, 0, 116, 111, 110, 101, + 109, 97, 112, 95, 109, 101, + 116, 104, 111, 100, 0, 116, + 111, 110, 101, 109, 97, 112, + 95, 102, 97, 99, 116, 111, + 114, 49, 0, 116, 111, 110, + 101, 109, 97, 112, 95, 102, + 97, 99, 116, 111, 114, 50, + 0, 115, 100, 114, 95, 119, + 104, 105, 116, 101, 95, 112, + 111, 105, 110, 116, 0, 89, + 111, 102, 102, 115, 101, 116, + 0, 171, 1, 0, 3, 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 99, 111, 101, 102, 102, diff --git a/src/render/direct3d11/D3D11_PixelShader_Common.incl b/src/render/direct3d11/D3D11_PixelShader_Common.incl index 35623b762f179..61053d8c476dc 100644 --- a/src/render/direct3d11/D3D11_PixelShader_Common.incl +++ b/src/render/direct3d11/D3D11_PixelShader_Common.incl @@ -1,3 +1,9 @@ + +Texture2D texture0 : register(t0); +Texture2D texture1 : register(t1); +Texture2D texture2 : register(t2); +SamplerState sampler0 : register(s0); + struct PixelShaderInput { float4 pos : SV_POSITION; @@ -5,12 +11,33 @@ struct PixelShaderInput float4 color : COLOR0; }; +// These should mirror the definitions in SDL_render_d3d12.c +static const float TONEMAP_NONE = 0; +static const float TONEMAP_LINEAR = 1; +static const float TONEMAP_CHROME = 2; + +static const float TEXTURETYPE_NONE = 0; +static const float TEXTURETYPE_RGB = 1; +static const float TEXTURETYPE_NV12 = 2; +static const float TEXTURETYPE_NV21 = 3; +static const float TEXTURETYPE_YUV = 4; + +static const float INPUTTYPE_UNSPECIFIED = 0; +static const float INPUTTYPE_SRGB = 1; +static const float INPUTTYPE_SCRGB = 2; +static const float INPUTTYPE_HDR10 = 3; + cbuffer Constants : register(b0) { float scRGB_output; + float texture_type; + float input_type; float color_scale; - float unused1; - float unused2; + + float tonemap_method; + float tonemap_factor1; + float tonemap_factor2; + float sdr_white_point; float4 Yoffset; float4 Rcoeff; @@ -18,15 +45,17 @@ cbuffer Constants : register(b0) float4 Bcoeff; }; -float3 scRGBtoNits(float3 v) -{ - return v * 80.0; -} +static const float3x3 mat709to2020 = { + { 0.627404, 0.329283, 0.043313 }, + { 0.069097, 0.919541, 0.011362 }, + { 0.016391, 0.088013, 0.895595 } +}; -float3 scRGBfromNits(float3 v) -{ - return v / 80.0; -} +static const float3x3 mat2020to709 = { + { 1.660496, -0.587656, -0.072840 }, + { -0.124547, 1.132895, -0.008348 }, + { -0.018154, -0.100597, 1.118751 } +}; float sRGBtoLinear(float v) { @@ -48,6 +77,92 @@ float sRGBfromLinear(float v) return v; } +float3 PQtoLinear(float3 v) +{ + const float c1 = 0.8359375; + const float c2 = 18.8515625; + const float c3 = 18.6875; + const float oo_m1 = 1.0 / 0.1593017578125; + const float oo_m2 = 1.0 / 78.84375; + + float3 num = max(pow(abs(v), oo_m2) - c1, 0.0); + float3 den = c2 - c3 * pow(abs(v), oo_m2); + return (10000.0 * pow(abs(num / den), oo_m1) / sdr_white_point); +} + +float3 ApplyTonemap(float3 v) +{ + if (tonemap_method == TONEMAP_LINEAR) { + v *= tonemap_factor1; + } else if (tonemap_method == TONEMAP_CHROME) { + if (input_type == INPUTTYPE_SCRGB) { + // Convert to BT.2020 colorspace for tone mapping + v = mul(mat709to2020, v); + } + + float vmax = max(v.r, max(v.g, v.b)); + if (vmax > 0.0) { + float scale = (1.0 + tonemap_factor1 * vmax) / (1.0 + tonemap_factor2 * vmax); + v *= scale; + } + + if (input_type == INPUTTYPE_SCRGB) { + // Convert to BT.709 colorspace after tone mapping + v = mul(mat2020to709, v); + } + } + return v; +} + +float4 GetInputColor(PixelShaderInput input) +{ + float4 rgba; + + if (texture_type == TEXTURETYPE_NONE) { + rgba = 1.0; + } else if (texture_type == TEXTURETYPE_RGB) { + rgba = texture0.Sample(sampler0, input.tex); + } else if (texture_type == TEXTURETYPE_NV12) { + float3 yuv; + yuv.x = texture0.Sample(sampler0, input.tex).r; + yuv.yz = texture1.Sample(sampler0, input.tex).rg; + + yuv += Yoffset.xyz; + rgba.r = dot(yuv, Rcoeff.xyz); + rgba.g = dot(yuv, Gcoeff.xyz); + rgba.b = dot(yuv, Bcoeff.xyz); + rgba.a = 1.0; + } else if (texture_type == TEXTURETYPE_NV21) { + float3 yuv; + yuv.x = texture0.Sample(sampler0, input.tex).r; + yuv.yz = texture1.Sample(sampler0, input.tex).gr; + + yuv += Yoffset.xyz; + rgba.r = dot(yuv, Rcoeff.xyz); + rgba.g = dot(yuv, Gcoeff.xyz); + rgba.b = dot(yuv, Bcoeff.xyz); + rgba.a = 1.0; + } else if (texture_type == TEXTURETYPE_YUV) { + float3 yuv; + yuv.x = texture0.Sample(sampler0, input.tex).r; + yuv.y = texture1.Sample(sampler0, input.tex).r; + yuv.z = texture2.Sample(sampler0, input.tex).r; + + yuv += Yoffset.xyz; + rgba.r = dot(yuv, Rcoeff.xyz); + rgba.g = dot(yuv, Gcoeff.xyz); + rgba.b = dot(yuv, Bcoeff.xyz); + rgba.a = 1.0; + } else { + // Error! + rgba.r = 1.0; + rgba.g = 0.0; + rgba.b = 0.0; + rgba.a = 1.0; + } + return rgba; +} + float4 GetOutputColor(float4 rgba) { float4 output; @@ -58,9 +173,9 @@ float4 GetOutputColor(float4 rgba) return output; } -float4 GetOutputColorFromSRGB(float3 rgb) +float3 GetOutputColorFromSRGB(float3 rgb) { - float4 output; + float3 output; if (scRGB_output) { rgb.r = sRGBtoLinear(rgb.r); @@ -69,17 +184,15 @@ float4 GetOutputColorFromSRGB(float3 rgb) } output.rgb = rgb * color_scale; - output.a = 1.0; return output; } -float4 GetOutputColorFromSCRGB(float3 rgb) +float3 GetOutputColorFromLinear(float3 rgb) { - float4 output; + float3 output; output.rgb = rgb * color_scale; - output.a = 1.0; if (!scRGB_output) { output.r = sRGBfromLinear(output.r); @@ -90,3 +203,33 @@ float4 GetOutputColorFromSCRGB(float3 rgb) return output; } + +float4 AdvancedPixelShader(PixelShaderInput input) +{ + float4 rgba = GetInputColor(input); + float4 output; + + if (input_type == INPUTTYPE_HDR10) { + rgba.rgb = PQtoLinear(rgba.rgb); + } + + if (tonemap_method != TONEMAP_NONE) { + rgba.rgb = ApplyTonemap(rgba.rgb); + } + + if (input_type == INPUTTYPE_SRGB) { + output.rgb = GetOutputColorFromSRGB(rgba.rgb); + output.a = rgba.a; + } else if (input_type == INPUTTYPE_SCRGB) { + output.rgb = GetOutputColorFromLinear(rgba.rgb); + output.a = rgba.a; + } else if (input_type == INPUTTYPE_HDR10) { + rgba.rgb = mul(mat2020to709, rgba.rgb); + output.rgb = GetOutputColorFromLinear(rgba.rgb); + output.a = rgba.a; + } else { + output = GetOutputColor(rgba); + } + + return output * input.color; +} diff --git a/src/render/direct3d11/D3D11_PixelShader_HDR10.h b/src/render/direct3d11/D3D11_PixelShader_HDR10.h deleted file mode 100644 index d8e17c33f615e..0000000000000 --- a/src/render/direct3d11/D3D11_PixelShader_HDR10.h +++ /dev/null @@ -1,507 +0,0 @@ -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Buffer Definitions: -// -// cbuffer Constants -// { -// -// float scRGB_output; // Offset: 0 Size: 4 -// float color_scale; // Offset: 4 Size: 4 -// float unused1; // Offset: 8 Size: 4 [unused] -// float unused2; // Offset: 12 Size: 4 [unused] -// float4 Yoffset; // Offset: 16 Size: 16 -// float4 Rcoeff; // Offset: 32 Size: 16 -// float4 Gcoeff; // Offset: 48 Size: 16 -// float4 Bcoeff; // Offset: 64 Size: 16 -// -// } -// -// -// Resource Bindings: -// -// Name Type Format Dim HLSL Bind Count -// ------------------------------ ---------- ------- ----------- -------------- ------ -// theSampler sampler NA NA s0 1 -// theTextureY texture float4 2d t0 1 -// theTextureUV texture float4 2d t1 1 -// Constants cbuffer NA NA cb0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_POSITION 0 xyzw 0 POS float -// TEXCOORD 0 xy 1 NONE float xy -// COLOR 0 xyzw 2 NONE float xyzw -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_TARGET 0 xyzw 0 TARGET float xyzw -// -ps_5_0 -dcl_globalFlags refactoringAllowed -dcl_constantbuffer CB0[5], immediateIndexed -dcl_sampler s0, mode_default -dcl_resource_texture2d (float,float,float,float) t0 -dcl_resource_texture2d (float,float,float,float) t1 -dcl_input_ps linear v1.xy -dcl_input_ps linear v2.xyzw -dcl_output o0.xyzw -dcl_temps 4 -sample_indexable(texture2d)(float,float,float,float) r0.x, v1.xyxx, t0.xyzw, s0 -sample_indexable(texture2d)(float,float,float,float) r0.yz, v1.xyxx, t1.zxyw, s0 -add r0.xyz, r0.xyzx, cb0[1].xyzx -dp3 r1.x, r0.xyzx, cb0[2].xyzx -dp3 r1.y, r0.xyzx, cb0[3].xyzx -dp3 r1.z, r0.xyzx, cb0[4].xyzx -log r0.xyz, |r1.xyzx| -mul r0.xyz, r0.xyzx, l(0.012683, 0.012683, 0.012683, 0.000000) -exp r0.xyz, r0.xyzx -add r1.xyz, r0.xyzx, l(-0.835938, -0.835938, -0.835938, 0.000000) -max r1.xyz, r1.xyzx, l(0.000000, 0.000000, 0.000000, 0.000000) -mad r0.xyz, -r0.xyzx, l(18.687500, 18.687500, 18.687500, 0.000000), l(18.851562, 18.851562, 18.851562, 0.000000) -div r0.xyz, r1.xyzx, r0.xyzx -log r0.xyz, |r0.xyzx| -mul r0.xyz, r0.xyzx, l(6.277395, 6.277395, 6.277395, 0.000000) -exp r0.xyz, r0.xyzx -mul r0.xyz, r0.xyzx, l(10000.000000, 10000.000000, 10000.000000, 0.000000) -dp3 r1.x, l(1.660496, -0.587656, -0.072840, 0.000000), r0.xyzx -dp3 r1.y, l(-0.124547, 1.132895, -0.008348, 0.000000), r0.xyzx -dp3 r1.z, l(-0.018154, -0.100597, 1.118751, 0.000000), r0.xyzx -mul r0.xyz, r1.xyzx, cb0[0].yyyy -mul r1.xyz, r0.xyzx, l(0.012500, 0.012500, 0.012500, 0.000000) -ne r0.w, l(0.000000, 0.000000, 0.000000, 0.000000), cb0[0].x -if_z r0.w - ge r2.xyz, l(0.250464, 0.250464, 0.250464, 0.000000), r0.xyzx - mul r0.xyz, r0.xyzx, l(0.161500, 0.161500, 0.161500, 0.000000) - log r3.xyz, |r1.xyzx| - mul r3.xyz, r3.xyzx, l(0.416667, 0.416667, 0.416667, 0.000000) - exp r3.xyz, r3.xyzx - mad r3.xyz, r3.xyzx, l(1.055000, 1.055000, 1.055000, 0.000000), l(-0.055000, -0.055000, -0.055000, 0.000000) - movc_sat r1.xyz, r2.xyzx, r0.xyzx, r3.xyzx -endif -mov r1.w, l(1.000000) -mul o0.xyzw, r1.xyzw, v2.xyzw -ret -// Approximately 35 instruction slots used -#endif - -const BYTE g_main[] = -{ - 68, 88, 66, 67, 95, 217, - 108, 181, 38, 184, 86, 20, - 51, 33, 123, 248, 135, 60, - 0, 250, 1, 0, 0, 0, - 144, 9, 0, 0, 5, 0, - 0, 0, 52, 0, 0, 0, - 72, 3, 0, 0, 188, 3, - 0, 0, 240, 3, 0, 0, - 244, 8, 0, 0, 82, 68, - 69, 70, 12, 3, 0, 0, - 1, 0, 0, 0, 236, 0, - 0, 0, 4, 0, 0, 0, - 60, 0, 0, 0, 0, 5, - 255, 255, 0, 1, 0, 0, - 225, 2, 0, 0, 82, 68, - 49, 49, 60, 0, 0, 0, - 24, 0, 0, 0, 32, 0, - 0, 0, 40, 0, 0, 0, - 36, 0, 0, 0, 12, 0, - 0, 0, 0, 0, 0, 0, - 188, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 199, 0, 0, 0, - 2, 0, 0, 0, 5, 0, - 0, 0, 4, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 1, 0, 0, 0, - 13, 0, 0, 0, 211, 0, - 0, 0, 2, 0, 0, 0, - 5, 0, 0, 0, 4, 0, - 0, 0, 255, 255, 255, 255, - 1, 0, 0, 0, 1, 0, - 0, 0, 13, 0, 0, 0, - 224, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 116, 104, 101, 83, - 97, 109, 112, 108, 101, 114, - 0, 116, 104, 101, 84, 101, - 120, 116, 117, 114, 101, 89, - 0, 116, 104, 101, 84, 101, - 120, 116, 117, 114, 101, 85, - 86, 0, 67, 111, 110, 115, - 116, 97, 110, 116, 115, 0, - 171, 171, 224, 0, 0, 0, - 8, 0, 0, 0, 4, 1, - 0, 0, 80, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 68, 2, 0, 0, - 0, 0, 0, 0, 4, 0, - 0, 0, 2, 0, 0, 0, - 88, 2, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 124, 2, 0, 0, 4, 0, - 0, 0, 4, 0, 0, 0, - 2, 0, 0, 0, 88, 2, - 0, 0, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 136, 2, - 0, 0, 8, 0, 0, 0, - 4, 0, 0, 0, 0, 0, - 0, 0, 88, 2, 0, 0, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 144, 2, 0, 0, - 12, 0, 0, 0, 4, 0, - 0, 0, 0, 0, 0, 0, - 88, 2, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 152, 2, 0, 0, 16, 0, - 0, 0, 16, 0, 0, 0, - 2, 0, 0, 0, 168, 2, - 0, 0, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 204, 2, - 0, 0, 32, 0, 0, 0, - 16, 0, 0, 0, 2, 0, - 0, 0, 168, 2, 0, 0, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 211, 2, 0, 0, - 48, 0, 0, 0, 16, 0, - 0, 0, 2, 0, 0, 0, - 168, 2, 0, 0, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 218, 2, 0, 0, 64, 0, - 0, 0, 16, 0, 0, 0, - 2, 0, 0, 0, 168, 2, - 0, 0, 0, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 255, 255, 255, 255, - 0, 0, 0, 0, 115, 99, - 82, 71, 66, 95, 111, 117, - 116, 112, 117, 116, 0, 102, - 108, 111, 97, 116, 0, 171, - 0, 0, 3, 0, 1, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 81, 2, 0, 0, - 99, 111, 108, 111, 114, 95, - 115, 99, 97, 108, 101, 0, - 117, 110, 117, 115, 101, 100, - 49, 0, 117, 110, 117, 115, - 101, 100, 50, 0, 89, 111, - 102, 102, 115, 101, 116, 0, - 102, 108, 111, 97, 116, 52, - 0, 171, 1, 0, 3, 0, - 1, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 160, 2, - 0, 0, 82, 99, 111, 101, - 102, 102, 0, 71, 99, 111, - 101, 102, 102, 0, 66, 99, - 111, 101, 102, 102, 0, 77, - 105, 99, 114, 111, 115, 111, - 102, 116, 32, 40, 82, 41, - 32, 72, 76, 83, 76, 32, - 83, 104, 97, 100, 101, 114, - 32, 67, 111, 109, 112, 105, - 108, 101, 114, 32, 49, 48, - 46, 49, 0, 171, 171, 171, - 73, 83, 71, 78, 108, 0, - 0, 0, 3, 0, 0, 0, - 8, 0, 0, 0, 80, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 15, 0, 0, 0, 92, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 1, 0, 0, 0, - 3, 3, 0, 0, 101, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 2, 0, 0, 0, - 15, 15, 0, 0, 83, 86, - 95, 80, 79, 83, 73, 84, - 73, 79, 78, 0, 84, 69, - 88, 67, 79, 79, 82, 68, - 0, 67, 79, 76, 79, 82, - 0, 171, 79, 83, 71, 78, - 44, 0, 0, 0, 1, 0, - 0, 0, 8, 0, 0, 0, - 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, - 0, 0, 15, 0, 0, 0, - 83, 86, 95, 84, 65, 82, - 71, 69, 84, 0, 171, 171, - 83, 72, 69, 88, 252, 4, - 0, 0, 80, 0, 0, 0, - 63, 1, 0, 0, 106, 8, - 0, 1, 89, 0, 0, 4, - 70, 142, 32, 0, 0, 0, - 0, 0, 5, 0, 0, 0, - 90, 0, 0, 3, 0, 96, - 16, 0, 0, 0, 0, 0, - 88, 24, 0, 4, 0, 112, - 16, 0, 0, 0, 0, 0, - 85, 85, 0, 0, 88, 24, - 0, 4, 0, 112, 16, 0, - 1, 0, 0, 0, 85, 85, - 0, 0, 98, 16, 0, 3, - 50, 16, 16, 0, 1, 0, - 0, 0, 98, 16, 0, 3, - 242, 16, 16, 0, 2, 0, - 0, 0, 101, 0, 0, 3, - 242, 32, 16, 0, 0, 0, - 0, 0, 104, 0, 0, 2, - 4, 0, 0, 0, 69, 0, - 0, 139, 194, 0, 0, 128, - 67, 85, 21, 0, 18, 0, - 16, 0, 0, 0, 0, 0, - 70, 16, 16, 0, 1, 0, - 0, 0, 70, 126, 16, 0, - 0, 0, 0, 0, 0, 96, - 16, 0, 0, 0, 0, 0, - 69, 0, 0, 139, 194, 0, - 0, 128, 67, 85, 21, 0, - 98, 0, 16, 0, 0, 0, - 0, 0, 70, 16, 16, 0, - 1, 0, 0, 0, 38, 125, - 16, 0, 1, 0, 0, 0, - 0, 96, 16, 0, 0, 0, - 0, 0, 0, 0, 0, 8, - 114, 0, 16, 0, 0, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 70, 130, - 32, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 16, 0, - 0, 8, 18, 0, 16, 0, - 1, 0, 0, 0, 70, 2, - 16, 0, 0, 0, 0, 0, - 70, 130, 32, 0, 0, 0, - 0, 0, 2, 0, 0, 0, - 16, 0, 0, 8, 34, 0, - 16, 0, 1, 0, 0, 0, - 70, 2, 16, 0, 0, 0, - 0, 0, 70, 130, 32, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 16, 0, 0, 8, - 66, 0, 16, 0, 1, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 70, 130, - 32, 0, 0, 0, 0, 0, - 4, 0, 0, 0, 47, 0, - 0, 6, 114, 0, 16, 0, - 0, 0, 0, 0, 70, 2, - 16, 128, 129, 0, 0, 0, - 1, 0, 0, 0, 56, 0, - 0, 10, 114, 0, 16, 0, - 0, 0, 0, 0, 70, 2, - 16, 0, 0, 0, 0, 0, - 2, 64, 0, 0, 172, 205, - 79, 60, 172, 205, 79, 60, - 172, 205, 79, 60, 0, 0, - 0, 0, 25, 0, 0, 5, - 114, 0, 16, 0, 0, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 0, 0, - 0, 10, 114, 0, 16, 0, - 1, 0, 0, 0, 70, 2, - 16, 0, 0, 0, 0, 0, - 2, 64, 0, 0, 0, 0, - 86, 191, 0, 0, 86, 191, - 0, 0, 86, 191, 0, 0, - 0, 0, 52, 0, 0, 10, - 114, 0, 16, 0, 1, 0, - 0, 0, 70, 2, 16, 0, - 1, 0, 0, 0, 2, 64, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 50, 0, 0, 16, 114, 0, - 16, 0, 0, 0, 0, 0, - 70, 2, 16, 128, 65, 0, - 0, 0, 0, 0, 0, 0, - 2, 64, 0, 0, 0, 128, - 149, 65, 0, 128, 149, 65, - 0, 128, 149, 65, 0, 0, - 0, 0, 2, 64, 0, 0, - 0, 208, 150, 65, 0, 208, - 150, 65, 0, 208, 150, 65, - 0, 0, 0, 0, 14, 0, - 0, 7, 114, 0, 16, 0, - 0, 0, 0, 0, 70, 2, - 16, 0, 1, 0, 0, 0, - 70, 2, 16, 0, 0, 0, - 0, 0, 47, 0, 0, 6, - 114, 0, 16, 0, 0, 0, - 0, 0, 70, 2, 16, 128, - 129, 0, 0, 0, 0, 0, - 0, 0, 56, 0, 0, 10, - 114, 0, 16, 0, 0, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 2, 64, - 0, 0, 107, 224, 200, 64, - 107, 224, 200, 64, 107, 224, - 200, 64, 0, 0, 0, 0, - 25, 0, 0, 5, 114, 0, - 16, 0, 0, 0, 0, 0, - 70, 2, 16, 0, 0, 0, - 0, 0, 56, 0, 0, 10, - 114, 0, 16, 0, 0, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 2, 64, - 0, 0, 0, 64, 28, 70, - 0, 64, 28, 70, 0, 64, - 28, 70, 0, 0, 0, 0, - 16, 0, 0, 10, 18, 0, - 16, 0, 1, 0, 0, 0, - 2, 64, 0, 0, 34, 139, - 212, 63, 160, 112, 22, 191, - 35, 45, 149, 189, 0, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 16, 0, - 0, 10, 34, 0, 16, 0, - 1, 0, 0, 0, 2, 64, - 0, 0, 127, 18, 255, 189, - 180, 2, 145, 63, 13, 198, - 8, 188, 0, 0, 0, 0, - 70, 2, 16, 0, 0, 0, - 0, 0, 16, 0, 0, 10, - 66, 0, 16, 0, 1, 0, - 0, 0, 2, 64, 0, 0, - 179, 183, 148, 188, 205, 5, - 206, 189, 60, 51, 143, 63, - 0, 0, 0, 0, 70, 2, - 16, 0, 0, 0, 0, 0, - 56, 0, 0, 8, 114, 0, - 16, 0, 0, 0, 0, 0, - 70, 2, 16, 0, 1, 0, - 0, 0, 86, 133, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 56, 0, 0, 10, - 114, 0, 16, 0, 1, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 2, 64, - 0, 0, 205, 204, 76, 60, - 205, 204, 76, 60, 205, 204, - 76, 60, 0, 0, 0, 0, - 57, 0, 0, 11, 130, 0, - 16, 0, 0, 0, 0, 0, - 2, 64, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 10, 128, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 31, 0, 0, 3, - 58, 0, 16, 0, 0, 0, - 0, 0, 29, 0, 0, 10, - 114, 0, 16, 0, 2, 0, - 0, 0, 2, 64, 0, 0, - 209, 60, 128, 62, 209, 60, - 128, 62, 209, 60, 128, 62, - 0, 0, 0, 0, 70, 2, - 16, 0, 0, 0, 0, 0, - 56, 0, 0, 10, 114, 0, - 16, 0, 0, 0, 0, 0, - 70, 2, 16, 0, 0, 0, - 0, 0, 2, 64, 0, 0, - 66, 96, 37, 62, 66, 96, - 37, 62, 66, 96, 37, 62, - 0, 0, 0, 0, 47, 0, - 0, 6, 114, 0, 16, 0, - 3, 0, 0, 0, 70, 2, - 16, 128, 129, 0, 0, 0, - 1, 0, 0, 0, 56, 0, - 0, 10, 114, 0, 16, 0, - 3, 0, 0, 0, 70, 2, - 16, 0, 3, 0, 0, 0, - 2, 64, 0, 0, 85, 85, - 213, 62, 85, 85, 213, 62, - 85, 85, 213, 62, 0, 0, - 0, 0, 25, 0, 0, 5, - 114, 0, 16, 0, 3, 0, - 0, 0, 70, 2, 16, 0, - 3, 0, 0, 0, 50, 0, - 0, 15, 114, 0, 16, 0, - 3, 0, 0, 0, 70, 2, - 16, 0, 3, 0, 0, 0, - 2, 64, 0, 0, 61, 10, - 135, 63, 61, 10, 135, 63, - 61, 10, 135, 63, 0, 0, - 0, 0, 2, 64, 0, 0, - 174, 71, 97, 189, 174, 71, - 97, 189, 174, 71, 97, 189, - 0, 0, 0, 0, 55, 32, - 0, 9, 114, 0, 16, 0, - 1, 0, 0, 0, 70, 2, - 16, 0, 2, 0, 0, 0, - 70, 2, 16, 0, 0, 0, - 0, 0, 70, 2, 16, 0, - 3, 0, 0, 0, 21, 0, - 0, 1, 54, 0, 0, 5, - 130, 0, 16, 0, 1, 0, - 0, 0, 1, 64, 0, 0, - 0, 0, 128, 63, 56, 0, - 0, 7, 242, 32, 16, 0, - 0, 0, 0, 0, 70, 14, - 16, 0, 1, 0, 0, 0, - 70, 30, 16, 0, 2, 0, - 0, 0, 62, 0, 0, 1, - 83, 84, 65, 84, 148, 0, - 0, 0, 35, 0, 0, 0, - 4, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 27, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0 -}; diff --git a/src/render/direct3d11/D3D11_PixelShader_HDR10.hlsl b/src/render/direct3d11/D3D11_PixelShader_HDR10.hlsl deleted file mode 100644 index d7878e91cd440..0000000000000 --- a/src/render/direct3d11/D3D11_PixelShader_HDR10.hlsl +++ /dev/null @@ -1,45 +0,0 @@ -Texture2D theTextureY : register(t0); -Texture2D theTextureUV : register(t1); -SamplerState theSampler : register(s0); - -#include "D3D11_PixelShader_Common.incl" - -float3 PQtoNits(float3 v) -{ - const float c1 = 0.8359375; - const float c2 = 18.8515625; - const float c3 = 18.6875; - const float oo_m1 = 1.0 / 0.1593017578125; - const float oo_m2 = 1.0 / 78.84375; - - float3 num = max(pow(abs(v), oo_m2) - c1, 0.0); - float3 den = c2 - c3 * pow(abs(v), oo_m2); - return 10000.0 * pow(abs(num / den), oo_m1); -} - -float4 main(PixelShaderInput input) : SV_TARGET -{ - const float3x3 mat2020to709 = { - 1.660496, -0.587656, -0.072840, - -0.124547, 1.132895, -0.008348, - -0.018154, -0.100597, 1.118751 - }; - - float3 yuv; - yuv.x = theTextureY.Sample(theSampler, input.tex).r; - yuv.yz = theTextureUV.Sample(theSampler, input.tex).rg; - - float3 rgb; - yuv += Yoffset.xyz; - rgb.r = dot(yuv, Rcoeff.xyz); - rgb.g = dot(yuv, Gcoeff.xyz); - rgb.b = dot(yuv, Bcoeff.xyz); - - rgb = PQtoNits(rgb); - - rgb = mul(mat2020to709, rgb); - - rgb = scRGBfromNits(rgb); - - return GetOutputColorFromSCRGB(rgb) * input.color; -} diff --git a/src/render/direct3d11/D3D11_PixelShader_NV12.h b/src/render/direct3d11/D3D11_PixelShader_NV12.h deleted file mode 100644 index 2a76fbaed39fa..0000000000000 --- a/src/render/direct3d11/D3D11_PixelShader_NV12.h +++ /dev/null @@ -1,545 +0,0 @@ -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Buffer Definitions: -// -// cbuffer Constants -// { -// -// float scRGB_output; // Offset: 0 Size: 4 -// float color_scale; // Offset: 4 Size: 4 -// float unused1; // Offset: 8 Size: 4 [unused] -// float unused2; // Offset: 12 Size: 4 [unused] -// float4 Yoffset; // Offset: 16 Size: 16 -// float4 Rcoeff; // Offset: 32 Size: 16 -// float4 Gcoeff; // Offset: 48 Size: 16 -// float4 Bcoeff; // Offset: 64 Size: 16 -// -// } -// -// -// Resource Bindings: -// -// Name Type Format Dim HLSL Bind Count -// ------------------------------ ---------- ------- ----------- -------------- ------ -// theSampler sampler NA NA s0 1 -// theTextureY texture float4 2d t0 1 -// theTextureUV texture float4 2d t1 1 -// Constants cbuffer NA NA cb0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_POSITION 0 xyzw 0 POS float -// TEXCOORD 0 xy 1 NONE float xy -// COLOR 0 xyzw 2 NONE float xyzw -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_TARGET 0 xyzw 0 TARGET float xyzw -// -// -// Constant buffer to DX9 shader constant mappings: -// -// Target Reg Buffer Start Reg # of Regs Data Conversion -// ---------- ------- --------- --------- ---------------------- -// c0 cb0 0 5 ( FLT, FLT, FLT, FLT) -// -// -// Sampler/Resource to DX9 shader sampler mappings: -// -// Target Sampler Source Sampler Source Resource -// -------------- --------------- ---------------- -// s0 s0 t0 -// s1 s0 t1 -// -// -// Level9 shader bytecode: -// - ps_2_0 - def c5, 0.0404499993, 0.0773993805, 0.0549999997, 0.947867334 - def c6, 2.4000001, 1, 0, 0 - dcl t0.xy - dcl t1 - dcl_2d s0 - dcl_2d s1 - texld r0, t0, s0 - texld r1, t0, s1 - mov r0.yz, r1.zxyw - add r0.xyz, r0, c1 - dp3 r1.x, r0, c2 - add r0.w, r1.x, c5.z - abs r0.w, r0.w - mul r0.w, r0.w, c5.w - pow r1.w, r0.w, c6.x - add r0.w, -r1.x, c5.x - mul r2.w, r1.x, c5.y - cmp r2.x, r0.w, r2.w, r1.w - dp3 r1.y, r0, c3 - dp3 r1.z, r0, c4 - add r1.w, r1.y, c5.z - abs r1.w, r1.w - mul r1.w, r1.w, c5.w - pow r2.w, r1.w, c6.x - add r1.w, -r1.y, c5.x - mul r0.x, r1.y, c5.y - cmp r2.y, r1.w, r0.x, r2.w - add r1.w, r1.z, c5.z - abs r1.w, r1.w - mul r1.w, r1.w, c5.w - pow r2.w, r1.w, c6.x - add r1.w, -r1.z, c5.x - mul r0.x, r1.z, c5.y - cmp r2.z, r1.w, r0.x, r2.w - mul r1.w, c0.x, c0.x - cmp r0.xyz, -r1.w, r1, r2 - mul r0.xyz, r0, c0.y - mov r0.w, c6.y - mul r0, r0, t1 - mov oC0, r0 - -// approximately 40 instruction slots used (2 texture, 38 arithmetic) -ps_4_0 -dcl_constantbuffer CB0[5], immediateIndexed -dcl_sampler s0, mode_default -dcl_resource_texture2d (float,float,float,float) t0 -dcl_resource_texture2d (float,float,float,float) t1 -dcl_input_ps linear v1.xy -dcl_input_ps linear v2.xyzw -dcl_output o0.xyzw -dcl_temps 4 -sample r0.xyzw, v1.xyxx, t0.xyzw, s0 -sample r1.xyzw, v1.xyxx, t1.zxyw, s0 -mov r1.x, r0.x -add r0.xyz, r1.xyzx, cb0[1].xyzx -dp3 r1.x, r0.xyzx, cb0[2].xyzx -dp3 r1.y, r0.xyzx, cb0[3].xyzx -dp3 r1.z, r0.xyzx, cb0[4].xyzx -ne r0.x, l(0.000000, 0.000000, 0.000000, 0.000000), cb0[0].x -if_nz r0.x - ge r0.xyz, l(0.040450, 0.040450, 0.040450, 0.000000), r1.xyzx - mul r2.xyz, r1.xyzx, l(0.077399, 0.077399, 0.077399, 0.000000) - add r3.xyz, r1.xyzx, l(0.055000, 0.055000, 0.055000, 0.000000) - mul r3.xyz, |r3.xyzx|, l(0.947867, 0.947867, 0.947867, 0.000000) - log r3.xyz, r3.xyzx - mul r3.xyz, r3.xyzx, l(2.400000, 2.400000, 2.400000, 0.000000) - exp r3.xyz, r3.xyzx - movc r1.xyz, r0.xyzx, r2.xyzx, r3.xyzx -endif -mul r0.xyz, r1.xyzx, cb0[0].yyyy -mov r0.w, l(1.000000) -mul o0.xyzw, r0.xyzw, v2.xyzw -ret -// Approximately 22 instruction slots used -#endif - -const BYTE g_main[] = -{ - 68, 88, 66, 67, 58, 20, - 110, 34, 151, 86, 32, 17, - 118, 15, 98, 32, 139, 247, - 137, 113, 1, 0, 0, 0, - 88, 9, 0, 0, 6, 0, - 0, 0, 56, 0, 0, 0, - 248, 2, 0, 0, 244, 5, - 0, 0, 112, 6, 0, 0, - 176, 8, 0, 0, 36, 9, - 0, 0, 65, 111, 110, 57, - 184, 2, 0, 0, 184, 2, - 0, 0, 0, 2, 255, 255, - 128, 2, 0, 0, 56, 0, - 0, 0, 1, 0, 44, 0, - 0, 0, 56, 0, 0, 0, - 56, 0, 2, 0, 36, 0, - 0, 0, 56, 0, 0, 0, - 0, 0, 1, 0, 1, 0, - 0, 0, 0, 0, 5, 0, - 0, 0, 0, 0, 0, 0, - 0, 2, 255, 255, 81, 0, - 0, 5, 5, 0, 15, 160, - 230, 174, 37, 61, 145, 131, - 158, 61, 174, 71, 97, 61, - 111, 167, 114, 63, 81, 0, - 0, 5, 6, 0, 15, 160, - 154, 153, 25, 64, 0, 0, - 128, 63, 0, 0, 0, 0, - 0, 0, 0, 0, 31, 0, - 0, 2, 0, 0, 0, 128, - 0, 0, 3, 176, 31, 0, - 0, 2, 0, 0, 0, 128, - 1, 0, 15, 176, 31, 0, - 0, 2, 0, 0, 0, 144, - 0, 8, 15, 160, 31, 0, - 0, 2, 0, 0, 0, 144, - 1, 8, 15, 160, 66, 0, - 0, 3, 0, 0, 15, 128, - 0, 0, 228, 176, 0, 8, - 228, 160, 66, 0, 0, 3, - 1, 0, 15, 128, 0, 0, - 228, 176, 1, 8, 228, 160, - 1, 0, 0, 2, 0, 0, - 6, 128, 1, 0, 210, 128, - 2, 0, 0, 3, 0, 0, - 7, 128, 0, 0, 228, 128, - 1, 0, 228, 160, 8, 0, - 0, 3, 1, 0, 1, 128, - 0, 0, 228, 128, 2, 0, - 228, 160, 2, 0, 0, 3, - 0, 0, 8, 128, 1, 0, - 0, 128, 5, 0, 170, 160, - 35, 0, 0, 2, 0, 0, - 8, 128, 0, 0, 255, 128, - 5, 0, 0, 3, 0, 0, - 8, 128, 0, 0, 255, 128, - 5, 0, 255, 160, 32, 0, - 0, 3, 1, 0, 8, 128, - 0, 0, 255, 128, 6, 0, - 0, 160, 2, 0, 0, 3, - 0, 0, 8, 128, 1, 0, - 0, 129, 5, 0, 0, 160, - 5, 0, 0, 3, 2, 0, - 8, 128, 1, 0, 0, 128, - 5, 0, 85, 160, 88, 0, - 0, 4, 2, 0, 1, 128, - 0, 0, 255, 128, 2, 0, - 255, 128, 1, 0, 255, 128, - 8, 0, 0, 3, 1, 0, - 2, 128, 0, 0, 228, 128, - 3, 0, 228, 160, 8, 0, - 0, 3, 1, 0, 4, 128, - 0, 0, 228, 128, 4, 0, - 228, 160, 2, 0, 0, 3, - 1, 0, 8, 128, 1, 0, - 85, 128, 5, 0, 170, 160, - 35, 0, 0, 2, 1, 0, - 8, 128, 1, 0, 255, 128, - 5, 0, 0, 3, 1, 0, - 8, 128, 1, 0, 255, 128, - 5, 0, 255, 160, 32, 0, - 0, 3, 2, 0, 8, 128, - 1, 0, 255, 128, 6, 0, - 0, 160, 2, 0, 0, 3, - 1, 0, 8, 128, 1, 0, - 85, 129, 5, 0, 0, 160, - 5, 0, 0, 3, 0, 0, - 1, 128, 1, 0, 85, 128, - 5, 0, 85, 160, 88, 0, - 0, 4, 2, 0, 2, 128, - 1, 0, 255, 128, 0, 0, - 0, 128, 2, 0, 255, 128, - 2, 0, 0, 3, 1, 0, - 8, 128, 1, 0, 170, 128, - 5, 0, 170, 160, 35, 0, - 0, 2, 1, 0, 8, 128, - 1, 0, 255, 128, 5, 0, - 0, 3, 1, 0, 8, 128, - 1, 0, 255, 128, 5, 0, - 255, 160, 32, 0, 0, 3, - 2, 0, 8, 128, 1, 0, - 255, 128, 6, 0, 0, 160, - 2, 0, 0, 3, 1, 0, - 8, 128, 1, 0, 170, 129, - 5, 0, 0, 160, 5, 0, - 0, 3, 0, 0, 1, 128, - 1, 0, 170, 128, 5, 0, - 85, 160, 88, 0, 0, 4, - 2, 0, 4, 128, 1, 0, - 255, 128, 0, 0, 0, 128, - 2, 0, 255, 128, 5, 0, - 0, 3, 1, 0, 8, 128, - 0, 0, 0, 160, 0, 0, - 0, 160, 88, 0, 0, 4, - 0, 0, 7, 128, 1, 0, - 255, 129, 1, 0, 228, 128, - 2, 0, 228, 128, 5, 0, - 0, 3, 0, 0, 7, 128, - 0, 0, 228, 128, 0, 0, - 85, 160, 1, 0, 0, 2, - 0, 0, 8, 128, 6, 0, - 85, 160, 5, 0, 0, 3, - 0, 0, 15, 128, 0, 0, - 228, 128, 1, 0, 228, 176, - 1, 0, 0, 2, 0, 8, - 15, 128, 0, 0, 228, 128, - 255, 255, 0, 0, 83, 72, - 68, 82, 244, 2, 0, 0, - 64, 0, 0, 0, 189, 0, - 0, 0, 89, 0, 0, 4, - 70, 142, 32, 0, 0, 0, - 0, 0, 5, 0, 0, 0, - 90, 0, 0, 3, 0, 96, - 16, 0, 0, 0, 0, 0, - 88, 24, 0, 4, 0, 112, - 16, 0, 0, 0, 0, 0, - 85, 85, 0, 0, 88, 24, - 0, 4, 0, 112, 16, 0, - 1, 0, 0, 0, 85, 85, - 0, 0, 98, 16, 0, 3, - 50, 16, 16, 0, 1, 0, - 0, 0, 98, 16, 0, 3, - 242, 16, 16, 0, 2, 0, - 0, 0, 101, 0, 0, 3, - 242, 32, 16, 0, 0, 0, - 0, 0, 104, 0, 0, 2, - 4, 0, 0, 0, 69, 0, - 0, 9, 242, 0, 16, 0, - 0, 0, 0, 0, 70, 16, - 16, 0, 1, 0, 0, 0, - 70, 126, 16, 0, 0, 0, - 0, 0, 0, 96, 16, 0, - 0, 0, 0, 0, 69, 0, - 0, 9, 242, 0, 16, 0, - 1, 0, 0, 0, 70, 16, - 16, 0, 1, 0, 0, 0, - 38, 125, 16, 0, 1, 0, - 0, 0, 0, 96, 16, 0, - 0, 0, 0, 0, 54, 0, - 0, 5, 18, 0, 16, 0, - 1, 0, 0, 0, 10, 0, - 16, 0, 0, 0, 0, 0, - 0, 0, 0, 8, 114, 0, - 16, 0, 0, 0, 0, 0, - 70, 2, 16, 0, 1, 0, - 0, 0, 70, 130, 32, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 16, 0, 0, 8, - 18, 0, 16, 0, 1, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 70, 130, - 32, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 16, 0, - 0, 8, 34, 0, 16, 0, - 1, 0, 0, 0, 70, 2, - 16, 0, 0, 0, 0, 0, - 70, 130, 32, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 16, 0, 0, 8, 66, 0, - 16, 0, 1, 0, 0, 0, - 70, 2, 16, 0, 0, 0, - 0, 0, 70, 130, 32, 0, - 0, 0, 0, 0, 4, 0, - 0, 0, 57, 0, 0, 11, - 18, 0, 16, 0, 0, 0, - 0, 0, 2, 64, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 10, 128, - 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 31, 0, - 4, 3, 10, 0, 16, 0, - 0, 0, 0, 0, 29, 0, - 0, 10, 114, 0, 16, 0, - 0, 0, 0, 0, 2, 64, - 0, 0, 230, 174, 37, 61, - 230, 174, 37, 61, 230, 174, - 37, 61, 0, 0, 0, 0, - 70, 2, 16, 0, 1, 0, - 0, 0, 56, 0, 0, 10, - 114, 0, 16, 0, 2, 0, - 0, 0, 70, 2, 16, 0, - 1, 0, 0, 0, 2, 64, - 0, 0, 145, 131, 158, 61, - 145, 131, 158, 61, 145, 131, - 158, 61, 0, 0, 0, 0, - 0, 0, 0, 10, 114, 0, - 16, 0, 3, 0, 0, 0, - 70, 2, 16, 0, 1, 0, - 0, 0, 2, 64, 0, 0, - 174, 71, 97, 61, 174, 71, - 97, 61, 174, 71, 97, 61, - 0, 0, 0, 0, 56, 0, - 0, 11, 114, 0, 16, 0, - 3, 0, 0, 0, 70, 2, - 16, 128, 129, 0, 0, 0, - 3, 0, 0, 0, 2, 64, - 0, 0, 111, 167, 114, 63, - 111, 167, 114, 63, 111, 167, - 114, 63, 0, 0, 0, 0, - 47, 0, 0, 5, 114, 0, - 16, 0, 3, 0, 0, 0, - 70, 2, 16, 0, 3, 0, - 0, 0, 56, 0, 0, 10, - 114, 0, 16, 0, 3, 0, - 0, 0, 70, 2, 16, 0, - 3, 0, 0, 0, 2, 64, - 0, 0, 154, 153, 25, 64, - 154, 153, 25, 64, 154, 153, - 25, 64, 0, 0, 0, 0, - 25, 0, 0, 5, 114, 0, - 16, 0, 3, 0, 0, 0, - 70, 2, 16, 0, 3, 0, - 0, 0, 55, 0, 0, 9, - 114, 0, 16, 0, 1, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 70, 2, - 16, 0, 2, 0, 0, 0, - 70, 2, 16, 0, 3, 0, - 0, 0, 21, 0, 0, 1, - 56, 0, 0, 8, 114, 0, - 16, 0, 0, 0, 0, 0, - 70, 2, 16, 0, 1, 0, - 0, 0, 86, 133, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 5, - 130, 0, 16, 0, 0, 0, - 0, 0, 1, 64, 0, 0, - 0, 0, 128, 63, 56, 0, - 0, 7, 242, 32, 16, 0, - 0, 0, 0, 0, 70, 14, - 16, 0, 0, 0, 0, 0, - 70, 30, 16, 0, 2, 0, - 0, 0, 62, 0, 0, 1, - 83, 84, 65, 84, 116, 0, - 0, 0, 22, 0, 0, 0, - 4, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 13, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 82, 68, - 69, 70, 56, 2, 0, 0, - 1, 0, 0, 0, 204, 0, - 0, 0, 4, 0, 0, 0, - 28, 0, 0, 0, 0, 4, - 255, 255, 0, 1, 0, 0, - 13, 2, 0, 0, 156, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 167, 0, 0, 0, 2, 0, - 0, 0, 5, 0, 0, 0, - 4, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 1, 0, 0, 0, 13, 0, - 0, 0, 179, 0, 0, 0, - 2, 0, 0, 0, 5, 0, - 0, 0, 4, 0, 0, 0, - 255, 255, 255, 255, 1, 0, - 0, 0, 1, 0, 0, 0, - 13, 0, 0, 0, 192, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 116, 104, 101, 83, 97, 109, - 112, 108, 101, 114, 0, 116, - 104, 101, 84, 101, 120, 116, - 117, 114, 101, 89, 0, 116, - 104, 101, 84, 101, 120, 116, - 117, 114, 101, 85, 86, 0, - 67, 111, 110, 115, 116, 97, - 110, 116, 115, 0, 171, 171, - 192, 0, 0, 0, 8, 0, - 0, 0, 228, 0, 0, 0, - 80, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 164, 1, 0, 0, 0, 0, - 0, 0, 4, 0, 0, 0, - 2, 0, 0, 0, 180, 1, - 0, 0, 0, 0, 0, 0, - 196, 1, 0, 0, 4, 0, - 0, 0, 4, 0, 0, 0, - 2, 0, 0, 0, 180, 1, - 0, 0, 0, 0, 0, 0, - 208, 1, 0, 0, 8, 0, - 0, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 180, 1, - 0, 0, 0, 0, 0, 0, - 216, 1, 0, 0, 12, 0, - 0, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 180, 1, - 0, 0, 0, 0, 0, 0, - 224, 1, 0, 0, 16, 0, - 0, 0, 16, 0, 0, 0, - 2, 0, 0, 0, 232, 1, - 0, 0, 0, 0, 0, 0, - 248, 1, 0, 0, 32, 0, - 0, 0, 16, 0, 0, 0, - 2, 0, 0, 0, 232, 1, - 0, 0, 0, 0, 0, 0, - 255, 1, 0, 0, 48, 0, - 0, 0, 16, 0, 0, 0, - 2, 0, 0, 0, 232, 1, - 0, 0, 0, 0, 0, 0, - 6, 2, 0, 0, 64, 0, - 0, 0, 16, 0, 0, 0, - 2, 0, 0, 0, 232, 1, - 0, 0, 0, 0, 0, 0, - 115, 99, 82, 71, 66, 95, - 111, 117, 116, 112, 117, 116, - 0, 171, 171, 171, 0, 0, - 3, 0, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 99, 111, 108, 111, - 114, 95, 115, 99, 97, 108, - 101, 0, 117, 110, 117, 115, - 101, 100, 49, 0, 117, 110, - 117, 115, 101, 100, 50, 0, - 89, 111, 102, 102, 115, 101, - 116, 0, 1, 0, 3, 0, - 1, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 82, 99, 111, 101, 102, 102, - 0, 71, 99, 111, 101, 102, - 102, 0, 66, 99, 111, 101, - 102, 102, 0, 77, 105, 99, - 114, 111, 115, 111, 102, 116, - 32, 40, 82, 41, 32, 72, - 76, 83, 76, 32, 83, 104, - 97, 100, 101, 114, 32, 67, - 111, 109, 112, 105, 108, 101, - 114, 32, 49, 48, 46, 49, - 0, 171, 171, 171, 73, 83, - 71, 78, 108, 0, 0, 0, - 3, 0, 0, 0, 8, 0, - 0, 0, 80, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 15, 0, - 0, 0, 92, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 1, 0, 0, 0, 3, 3, - 0, 0, 101, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 2, 0, 0, 0, 15, 15, - 0, 0, 83, 86, 95, 80, - 79, 83, 73, 84, 73, 79, - 78, 0, 84, 69, 88, 67, - 79, 79, 82, 68, 0, 67, - 79, 76, 79, 82, 0, 171, - 79, 83, 71, 78, 44, 0, - 0, 0, 1, 0, 0, 0, - 8, 0, 0, 0, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 15, 0, 0, 0, 83, 86, - 95, 84, 65, 82, 71, 69, - 84, 0, 171, 171 -}; diff --git a/src/render/direct3d11/D3D11_PixelShader_NV12.hlsl b/src/render/direct3d11/D3D11_PixelShader_NV12.hlsl deleted file mode 100644 index f203173855764..0000000000000 --- a/src/render/direct3d11/D3D11_PixelShader_NV12.hlsl +++ /dev/null @@ -1,20 +0,0 @@ -Texture2D theTextureY : register(t0); -Texture2D theTextureUV : register(t1); -SamplerState theSampler : register(s0); - -#include "D3D11_PixelShader_Common.incl" - -float4 main(PixelShaderInput input) : SV_TARGET -{ - float3 yuv; - yuv.x = theTextureY.Sample(theSampler, input.tex).r; - yuv.yz = theTextureUV.Sample(theSampler, input.tex).rg; - - float3 rgb; - yuv += Yoffset.xyz; - rgb.r = dot(yuv, Rcoeff.xyz); - rgb.g = dot(yuv, Gcoeff.xyz); - rgb.b = dot(yuv, Bcoeff.xyz); - - return GetOutputColorFromSRGB(rgb) * input.color; -} diff --git a/src/render/direct3d11/D3D11_PixelShader_NV21.h b/src/render/direct3d11/D3D11_PixelShader_NV21.h deleted file mode 100644 index 3db1d52c28590..0000000000000 --- a/src/render/direct3d11/D3D11_PixelShader_NV21.h +++ /dev/null @@ -1,548 +0,0 @@ -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Buffer Definitions: -// -// cbuffer Constants -// { -// -// float scRGB_output; // Offset: 0 Size: 4 -// float color_scale; // Offset: 4 Size: 4 -// float unused1; // Offset: 8 Size: 4 [unused] -// float unused2; // Offset: 12 Size: 4 [unused] -// float4 Yoffset; // Offset: 16 Size: 16 -// float4 Rcoeff; // Offset: 32 Size: 16 -// float4 Gcoeff; // Offset: 48 Size: 16 -// float4 Bcoeff; // Offset: 64 Size: 16 -// -// } -// -// -// Resource Bindings: -// -// Name Type Format Dim HLSL Bind Count -// ------------------------------ ---------- ------- ----------- -------------- ------ -// theSampler sampler NA NA s0 1 -// theTextureY texture float4 2d t0 1 -// theTextureUV texture float4 2d t1 1 -// Constants cbuffer NA NA cb0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_POSITION 0 xyzw 0 POS float -// TEXCOORD 0 xy 1 NONE float xy -// COLOR 0 xyzw 2 NONE float xyzw -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_TARGET 0 xyzw 0 TARGET float xyzw -// -// -// Constant buffer to DX9 shader constant mappings: -// -// Target Reg Buffer Start Reg # of Regs Data Conversion -// ---------- ------- --------- --------- ---------------------- -// c0 cb0 0 5 ( FLT, FLT, FLT, FLT) -// -// -// Sampler/Resource to DX9 shader sampler mappings: -// -// Target Sampler Source Sampler Source Resource -// -------------- --------------- ---------------- -// s0 s0 t0 -// s1 s0 t1 -// -// -// Level9 shader bytecode: -// - ps_2_0 - def c5, 0.0404499993, 0.0773993805, 0.0549999997, 0.947867334 - def c6, 2.4000001, 1, 0, 0 - dcl t0.xy - dcl t1 - dcl_2d s0 - dcl_2d s1 - texld r0, t0, s0 - texld r1, t0, s1 - mov r0.y, r1.y - mov r0.z, r1.x - add r0.xyz, r0, c1 - dp3 r1.x, r0, c2 - add r0.w, r1.x, c5.z - abs r0.w, r0.w - mul r0.w, r0.w, c5.w - pow r1.w, r0.w, c6.x - add r0.w, -r1.x, c5.x - mul r2.w, r1.x, c5.y - cmp r2.x, r0.w, r2.w, r1.w - dp3 r1.y, r0, c3 - dp3 r1.z, r0, c4 - add r1.w, r1.y, c5.z - abs r1.w, r1.w - mul r1.w, r1.w, c5.w - pow r2.w, r1.w, c6.x - add r1.w, -r1.y, c5.x - mul r0.x, r1.y, c5.y - cmp r2.y, r1.w, r0.x, r2.w - add r1.w, r1.z, c5.z - abs r1.w, r1.w - mul r1.w, r1.w, c5.w - pow r2.w, r1.w, c6.x - add r1.w, -r1.z, c5.x - mul r0.x, r1.z, c5.y - cmp r2.z, r1.w, r0.x, r2.w - mul r1.w, c0.x, c0.x - cmp r0.xyz, -r1.w, r1, r2 - mul r0.xyz, r0, c0.y - mov r0.w, c6.y - mul r0, r0, t1 - mov oC0, r0 - -// approximately 41 instruction slots used (2 texture, 39 arithmetic) -ps_4_0 -dcl_constantbuffer CB0[5], immediateIndexed -dcl_sampler s0, mode_default -dcl_resource_texture2d (float,float,float,float) t0 -dcl_resource_texture2d (float,float,float,float) t1 -dcl_input_ps linear v1.xy -dcl_input_ps linear v2.xyzw -dcl_output o0.xyzw -dcl_temps 4 -sample r0.xyzw, v1.xyxx, t0.xyzw, s0 -sample r1.xyzw, v1.xyxx, t1.zyxw, s0 -mov r1.x, r0.x -add r0.xyz, r1.xyzx, cb0[1].xyzx -dp3 r1.x, r0.xyzx, cb0[2].xyzx -dp3 r1.y, r0.xyzx, cb0[3].xyzx -dp3 r1.z, r0.xyzx, cb0[4].xyzx -ne r0.x, l(0.000000, 0.000000, 0.000000, 0.000000), cb0[0].x -if_nz r0.x - ge r0.xyz, l(0.040450, 0.040450, 0.040450, 0.000000), r1.xyzx - mul r2.xyz, r1.xyzx, l(0.077399, 0.077399, 0.077399, 0.000000) - add r3.xyz, r1.xyzx, l(0.055000, 0.055000, 0.055000, 0.000000) - mul r3.xyz, |r3.xyzx|, l(0.947867, 0.947867, 0.947867, 0.000000) - log r3.xyz, r3.xyzx - mul r3.xyz, r3.xyzx, l(2.400000, 2.400000, 2.400000, 0.000000) - exp r3.xyz, r3.xyzx - movc r1.xyz, r0.xyzx, r2.xyzx, r3.xyzx -endif -mul r0.xyz, r1.xyzx, cb0[0].yyyy -mov r0.w, l(1.000000) -mul o0.xyzw, r0.xyzw, v2.xyzw -ret -// Approximately 22 instruction slots used -#endif - -const BYTE g_main[] = -{ - 68, 88, 66, 67, 150, 187, - 194, 202, 50, 132, 111, 243, - 88, 169, 196, 166, 186, 146, - 56, 89, 1, 0, 0, 0, - 100, 9, 0, 0, 6, 0, - 0, 0, 56, 0, 0, 0, - 4, 3, 0, 0, 0, 6, - 0, 0, 124, 6, 0, 0, - 188, 8, 0, 0, 48, 9, - 0, 0, 65, 111, 110, 57, - 196, 2, 0, 0, 196, 2, - 0, 0, 0, 2, 255, 255, - 140, 2, 0, 0, 56, 0, - 0, 0, 1, 0, 44, 0, - 0, 0, 56, 0, 0, 0, - 56, 0, 2, 0, 36, 0, - 0, 0, 56, 0, 0, 0, - 0, 0, 1, 0, 1, 0, - 0, 0, 0, 0, 5, 0, - 0, 0, 0, 0, 0, 0, - 0, 2, 255, 255, 81, 0, - 0, 5, 5, 0, 15, 160, - 230, 174, 37, 61, 145, 131, - 158, 61, 174, 71, 97, 61, - 111, 167, 114, 63, 81, 0, - 0, 5, 6, 0, 15, 160, - 154, 153, 25, 64, 0, 0, - 128, 63, 0, 0, 0, 0, - 0, 0, 0, 0, 31, 0, - 0, 2, 0, 0, 0, 128, - 0, 0, 3, 176, 31, 0, - 0, 2, 0, 0, 0, 128, - 1, 0, 15, 176, 31, 0, - 0, 2, 0, 0, 0, 144, - 0, 8, 15, 160, 31, 0, - 0, 2, 0, 0, 0, 144, - 1, 8, 15, 160, 66, 0, - 0, 3, 0, 0, 15, 128, - 0, 0, 228, 176, 0, 8, - 228, 160, 66, 0, 0, 3, - 1, 0, 15, 128, 0, 0, - 228, 176, 1, 8, 228, 160, - 1, 0, 0, 2, 0, 0, - 2, 128, 1, 0, 85, 128, - 1, 0, 0, 2, 0, 0, - 4, 128, 1, 0, 0, 128, - 2, 0, 0, 3, 0, 0, - 7, 128, 0, 0, 228, 128, - 1, 0, 228, 160, 8, 0, - 0, 3, 1, 0, 1, 128, - 0, 0, 228, 128, 2, 0, - 228, 160, 2, 0, 0, 3, - 0, 0, 8, 128, 1, 0, - 0, 128, 5, 0, 170, 160, - 35, 0, 0, 2, 0, 0, - 8, 128, 0, 0, 255, 128, - 5, 0, 0, 3, 0, 0, - 8, 128, 0, 0, 255, 128, - 5, 0, 255, 160, 32, 0, - 0, 3, 1, 0, 8, 128, - 0, 0, 255, 128, 6, 0, - 0, 160, 2, 0, 0, 3, - 0, 0, 8, 128, 1, 0, - 0, 129, 5, 0, 0, 160, - 5, 0, 0, 3, 2, 0, - 8, 128, 1, 0, 0, 128, - 5, 0, 85, 160, 88, 0, - 0, 4, 2, 0, 1, 128, - 0, 0, 255, 128, 2, 0, - 255, 128, 1, 0, 255, 128, - 8, 0, 0, 3, 1, 0, - 2, 128, 0, 0, 228, 128, - 3, 0, 228, 160, 8, 0, - 0, 3, 1, 0, 4, 128, - 0, 0, 228, 128, 4, 0, - 228, 160, 2, 0, 0, 3, - 1, 0, 8, 128, 1, 0, - 85, 128, 5, 0, 170, 160, - 35, 0, 0, 2, 1, 0, - 8, 128, 1, 0, 255, 128, - 5, 0, 0, 3, 1, 0, - 8, 128, 1, 0, 255, 128, - 5, 0, 255, 160, 32, 0, - 0, 3, 2, 0, 8, 128, - 1, 0, 255, 128, 6, 0, - 0, 160, 2, 0, 0, 3, - 1, 0, 8, 128, 1, 0, - 85, 129, 5, 0, 0, 160, - 5, 0, 0, 3, 0, 0, - 1, 128, 1, 0, 85, 128, - 5, 0, 85, 160, 88, 0, - 0, 4, 2, 0, 2, 128, - 1, 0, 255, 128, 0, 0, - 0, 128, 2, 0, 255, 128, - 2, 0, 0, 3, 1, 0, - 8, 128, 1, 0, 170, 128, - 5, 0, 170, 160, 35, 0, - 0, 2, 1, 0, 8, 128, - 1, 0, 255, 128, 5, 0, - 0, 3, 1, 0, 8, 128, - 1, 0, 255, 128, 5, 0, - 255, 160, 32, 0, 0, 3, - 2, 0, 8, 128, 1, 0, - 255, 128, 6, 0, 0, 160, - 2, 0, 0, 3, 1, 0, - 8, 128, 1, 0, 170, 129, - 5, 0, 0, 160, 5, 0, - 0, 3, 0, 0, 1, 128, - 1, 0, 170, 128, 5, 0, - 85, 160, 88, 0, 0, 4, - 2, 0, 4, 128, 1, 0, - 255, 128, 0, 0, 0, 128, - 2, 0, 255, 128, 5, 0, - 0, 3, 1, 0, 8, 128, - 0, 0, 0, 160, 0, 0, - 0, 160, 88, 0, 0, 4, - 0, 0, 7, 128, 1, 0, - 255, 129, 1, 0, 228, 128, - 2, 0, 228, 128, 5, 0, - 0, 3, 0, 0, 7, 128, - 0, 0, 228, 128, 0, 0, - 85, 160, 1, 0, 0, 2, - 0, 0, 8, 128, 6, 0, - 85, 160, 5, 0, 0, 3, - 0, 0, 15, 128, 0, 0, - 228, 128, 1, 0, 228, 176, - 1, 0, 0, 2, 0, 8, - 15, 128, 0, 0, 228, 128, - 255, 255, 0, 0, 83, 72, - 68, 82, 244, 2, 0, 0, - 64, 0, 0, 0, 189, 0, - 0, 0, 89, 0, 0, 4, - 70, 142, 32, 0, 0, 0, - 0, 0, 5, 0, 0, 0, - 90, 0, 0, 3, 0, 96, - 16, 0, 0, 0, 0, 0, - 88, 24, 0, 4, 0, 112, - 16, 0, 0, 0, 0, 0, - 85, 85, 0, 0, 88, 24, - 0, 4, 0, 112, 16, 0, - 1, 0, 0, 0, 85, 85, - 0, 0, 98, 16, 0, 3, - 50, 16, 16, 0, 1, 0, - 0, 0, 98, 16, 0, 3, - 242, 16, 16, 0, 2, 0, - 0, 0, 101, 0, 0, 3, - 242, 32, 16, 0, 0, 0, - 0, 0, 104, 0, 0, 2, - 4, 0, 0, 0, 69, 0, - 0, 9, 242, 0, 16, 0, - 0, 0, 0, 0, 70, 16, - 16, 0, 1, 0, 0, 0, - 70, 126, 16, 0, 0, 0, - 0, 0, 0, 96, 16, 0, - 0, 0, 0, 0, 69, 0, - 0, 9, 242, 0, 16, 0, - 1, 0, 0, 0, 70, 16, - 16, 0, 1, 0, 0, 0, - 102, 124, 16, 0, 1, 0, - 0, 0, 0, 96, 16, 0, - 0, 0, 0, 0, 54, 0, - 0, 5, 18, 0, 16, 0, - 1, 0, 0, 0, 10, 0, - 16, 0, 0, 0, 0, 0, - 0, 0, 0, 8, 114, 0, - 16, 0, 0, 0, 0, 0, - 70, 2, 16, 0, 1, 0, - 0, 0, 70, 130, 32, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 16, 0, 0, 8, - 18, 0, 16, 0, 1, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 70, 130, - 32, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 16, 0, - 0, 8, 34, 0, 16, 0, - 1, 0, 0, 0, 70, 2, - 16, 0, 0, 0, 0, 0, - 70, 130, 32, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 16, 0, 0, 8, 66, 0, - 16, 0, 1, 0, 0, 0, - 70, 2, 16, 0, 0, 0, - 0, 0, 70, 130, 32, 0, - 0, 0, 0, 0, 4, 0, - 0, 0, 57, 0, 0, 11, - 18, 0, 16, 0, 0, 0, - 0, 0, 2, 64, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 10, 128, - 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 31, 0, - 4, 3, 10, 0, 16, 0, - 0, 0, 0, 0, 29, 0, - 0, 10, 114, 0, 16, 0, - 0, 0, 0, 0, 2, 64, - 0, 0, 230, 174, 37, 61, - 230, 174, 37, 61, 230, 174, - 37, 61, 0, 0, 0, 0, - 70, 2, 16, 0, 1, 0, - 0, 0, 56, 0, 0, 10, - 114, 0, 16, 0, 2, 0, - 0, 0, 70, 2, 16, 0, - 1, 0, 0, 0, 2, 64, - 0, 0, 145, 131, 158, 61, - 145, 131, 158, 61, 145, 131, - 158, 61, 0, 0, 0, 0, - 0, 0, 0, 10, 114, 0, - 16, 0, 3, 0, 0, 0, - 70, 2, 16, 0, 1, 0, - 0, 0, 2, 64, 0, 0, - 174, 71, 97, 61, 174, 71, - 97, 61, 174, 71, 97, 61, - 0, 0, 0, 0, 56, 0, - 0, 11, 114, 0, 16, 0, - 3, 0, 0, 0, 70, 2, - 16, 128, 129, 0, 0, 0, - 3, 0, 0, 0, 2, 64, - 0, 0, 111, 167, 114, 63, - 111, 167, 114, 63, 111, 167, - 114, 63, 0, 0, 0, 0, - 47, 0, 0, 5, 114, 0, - 16, 0, 3, 0, 0, 0, - 70, 2, 16, 0, 3, 0, - 0, 0, 56, 0, 0, 10, - 114, 0, 16, 0, 3, 0, - 0, 0, 70, 2, 16, 0, - 3, 0, 0, 0, 2, 64, - 0, 0, 154, 153, 25, 64, - 154, 153, 25, 64, 154, 153, - 25, 64, 0, 0, 0, 0, - 25, 0, 0, 5, 114, 0, - 16, 0, 3, 0, 0, 0, - 70, 2, 16, 0, 3, 0, - 0, 0, 55, 0, 0, 9, - 114, 0, 16, 0, 1, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 70, 2, - 16, 0, 2, 0, 0, 0, - 70, 2, 16, 0, 3, 0, - 0, 0, 21, 0, 0, 1, - 56, 0, 0, 8, 114, 0, - 16, 0, 0, 0, 0, 0, - 70, 2, 16, 0, 1, 0, - 0, 0, 86, 133, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 54, 0, 0, 5, - 130, 0, 16, 0, 0, 0, - 0, 0, 1, 64, 0, 0, - 0, 0, 128, 63, 56, 0, - 0, 7, 242, 32, 16, 0, - 0, 0, 0, 0, 70, 14, - 16, 0, 0, 0, 0, 0, - 70, 30, 16, 0, 2, 0, - 0, 0, 62, 0, 0, 1, - 83, 84, 65, 84, 116, 0, - 0, 0, 22, 0, 0, 0, - 4, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 13, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 2, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 82, 68, - 69, 70, 56, 2, 0, 0, - 1, 0, 0, 0, 204, 0, - 0, 0, 4, 0, 0, 0, - 28, 0, 0, 0, 0, 4, - 255, 255, 0, 1, 0, 0, - 13, 2, 0, 0, 156, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 167, 0, 0, 0, 2, 0, - 0, 0, 5, 0, 0, 0, - 4, 0, 0, 0, 255, 255, - 255, 255, 0, 0, 0, 0, - 1, 0, 0, 0, 13, 0, - 0, 0, 179, 0, 0, 0, - 2, 0, 0, 0, 5, 0, - 0, 0, 4, 0, 0, 0, - 255, 255, 255, 255, 1, 0, - 0, 0, 1, 0, 0, 0, - 13, 0, 0, 0, 192, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 1, 0, 0, 0, - 116, 104, 101, 83, 97, 109, - 112, 108, 101, 114, 0, 116, - 104, 101, 84, 101, 120, 116, - 117, 114, 101, 89, 0, 116, - 104, 101, 84, 101, 120, 116, - 117, 114, 101, 85, 86, 0, - 67, 111, 110, 115, 116, 97, - 110, 116, 115, 0, 171, 171, - 192, 0, 0, 0, 8, 0, - 0, 0, 228, 0, 0, 0, - 80, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 164, 1, 0, 0, 0, 0, - 0, 0, 4, 0, 0, 0, - 2, 0, 0, 0, 180, 1, - 0, 0, 0, 0, 0, 0, - 196, 1, 0, 0, 4, 0, - 0, 0, 4, 0, 0, 0, - 2, 0, 0, 0, 180, 1, - 0, 0, 0, 0, 0, 0, - 208, 1, 0, 0, 8, 0, - 0, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 180, 1, - 0, 0, 0, 0, 0, 0, - 216, 1, 0, 0, 12, 0, - 0, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 180, 1, - 0, 0, 0, 0, 0, 0, - 224, 1, 0, 0, 16, 0, - 0, 0, 16, 0, 0, 0, - 2, 0, 0, 0, 232, 1, - 0, 0, 0, 0, 0, 0, - 248, 1, 0, 0, 32, 0, - 0, 0, 16, 0, 0, 0, - 2, 0, 0, 0, 232, 1, - 0, 0, 0, 0, 0, 0, - 255, 1, 0, 0, 48, 0, - 0, 0, 16, 0, 0, 0, - 2, 0, 0, 0, 232, 1, - 0, 0, 0, 0, 0, 0, - 6, 2, 0, 0, 64, 0, - 0, 0, 16, 0, 0, 0, - 2, 0, 0, 0, 232, 1, - 0, 0, 0, 0, 0, 0, - 115, 99, 82, 71, 66, 95, - 111, 117, 116, 112, 117, 116, - 0, 171, 171, 171, 0, 0, - 3, 0, 1, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 99, 111, 108, 111, - 114, 95, 115, 99, 97, 108, - 101, 0, 117, 110, 117, 115, - 101, 100, 49, 0, 117, 110, - 117, 115, 101, 100, 50, 0, - 89, 111, 102, 102, 115, 101, - 116, 0, 1, 0, 3, 0, - 1, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 82, 99, 111, 101, 102, 102, - 0, 71, 99, 111, 101, 102, - 102, 0, 66, 99, 111, 101, - 102, 102, 0, 77, 105, 99, - 114, 111, 115, 111, 102, 116, - 32, 40, 82, 41, 32, 72, - 76, 83, 76, 32, 83, 104, - 97, 100, 101, 114, 32, 67, - 111, 109, 112, 105, 108, 101, - 114, 32, 49, 48, 46, 49, - 0, 171, 171, 171, 73, 83, - 71, 78, 108, 0, 0, 0, - 3, 0, 0, 0, 8, 0, - 0, 0, 80, 0, 0, 0, - 0, 0, 0, 0, 1, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 15, 0, - 0, 0, 92, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 1, 0, 0, 0, 3, 3, - 0, 0, 101, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 2, 0, 0, 0, 15, 15, - 0, 0, 83, 86, 95, 80, - 79, 83, 73, 84, 73, 79, - 78, 0, 84, 69, 88, 67, - 79, 79, 82, 68, 0, 67, - 79, 76, 79, 82, 0, 171, - 79, 83, 71, 78, 44, 0, - 0, 0, 1, 0, 0, 0, - 8, 0, 0, 0, 32, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 15, 0, 0, 0, 83, 86, - 95, 84, 65, 82, 71, 69, - 84, 0, 171, 171 -}; diff --git a/src/render/direct3d11/D3D11_PixelShader_NV21.hlsl b/src/render/direct3d11/D3D11_PixelShader_NV21.hlsl deleted file mode 100644 index bea988b7315d0..0000000000000 --- a/src/render/direct3d11/D3D11_PixelShader_NV21.hlsl +++ /dev/null @@ -1,20 +0,0 @@ -Texture2D theTextureY : register(t0); -Texture2D theTextureUV : register(t1); -SamplerState theSampler : register(s0); - -#include "D3D11_PixelShader_Common.incl" - -float4 main(PixelShaderInput input) : SV_TARGET -{ - float3 yuv; - yuv.x = theTextureY.Sample(theSampler, input.tex).r; - yuv.yz = theTextureUV.Sample(theSampler, input.tex).gr; - - float3 rgb; - yuv += Yoffset.xyz; - rgb.r = dot(yuv, Rcoeff.xyz); - rgb.g = dot(yuv, Gcoeff.xyz); - rgb.b = dot(yuv, Bcoeff.xyz); - - return GetOutputColorFromSRGB(rgb) * input.color; -} diff --git a/src/render/direct3d11/D3D11_PixelShader_Textures.h b/src/render/direct3d11/D3D11_PixelShader_Textures.h index 8103dbdb8bc6d..996cac6228331 100644 --- a/src/render/direct3d11/D3D11_PixelShader_Textures.h +++ b/src/render/direct3d11/D3D11_PixelShader_Textures.h @@ -9,13 +9,17 @@ // { // // float scRGB_output; // Offset: 0 Size: 4 [unused] -// float color_scale; // Offset: 4 Size: 4 -// float unused1; // Offset: 8 Size: 4 [unused] -// float unused2; // Offset: 12 Size: 4 [unused] -// float4 Yoffset; // Offset: 16 Size: 16 [unused] -// float4 Rcoeff; // Offset: 32 Size: 16 [unused] -// float4 Gcoeff; // Offset: 48 Size: 16 [unused] -// float4 Bcoeff; // Offset: 64 Size: 16 [unused] +// float texture_type; // Offset: 4 Size: 4 [unused] +// float input_type; // Offset: 8 Size: 4 [unused] +// float color_scale; // Offset: 12 Size: 4 +// float tonemap_method; // Offset: 16 Size: 4 [unused] +// float tonemap_factor1; // Offset: 20 Size: 4 [unused] +// float tonemap_factor2; // Offset: 24 Size: 4 [unused] +// float sdr_white_point; // Offset: 28 Size: 4 [unused] +// float4 Yoffset; // Offset: 32 Size: 16 [unused] +// float4 Rcoeff; // Offset: 48 Size: 16 [unused] +// float4 Gcoeff; // Offset: 64 Size: 16 [unused] +// float4 Bcoeff; // Offset: 80 Size: 16 [unused] // // } // @@ -67,7 +71,7 @@ dcl t1 dcl_2d s0 texld r0, t0, s0 - mul r0.xyz, r0, c0.y + mul r0.xyz, r0, c0.w mul r0, r0, t1 mov oC0, r0 @@ -81,7 +85,7 @@ dcl_input_ps linear v2.xyzw dcl_output o0.xyzw dcl_temps 1 sample r0.xyzw, v1.xyxx, t0.xyzw, s0 -mul r0.xyz, r0.xyzx, cb0[0].yyyy +mul r0.xyz, r0.xyzx, cb0[0].wwww mul o0.xyzw, r0.xyzw, v2.xyzw ret // Approximately 4 instruction slots used @@ -89,15 +93,15 @@ ret const BYTE g_main[] = { - 68, 88, 66, 67, 159, 171, - 49, 134, 139, 171, 105, 34, - 166, 1, 101, 132, 110, 4, - 121, 76, 1, 0, 0, 0, - 220, 4, 0, 0, 6, 0, + 68, 88, 66, 67, 8, 152, + 224, 210, 182, 254, 37, 89, + 68, 213, 13, 174, 95, 42, + 2, 11, 1, 0, 0, 0, + 132, 5, 0, 0, 6, 0, 0, 0, 56, 0, 0, 0, 220, 0, 0, 0, 168, 1, 0, 0, 36, 2, 0, 0, - 52, 4, 0, 0, 168, 4, + 220, 4, 0, 0, 80, 5, 0, 0, 65, 111, 110, 57, 156, 0, 0, 0, 156, 0, 0, 0, 0, 2, 255, 255, @@ -120,7 +124,7 @@ const BYTE g_main[] = 0, 8, 228, 160, 5, 0, 0, 3, 0, 0, 7, 128, 0, 0, 228, 128, 0, 0, - 85, 160, 5, 0, 0, 3, + 255, 160, 5, 0, 0, 3, 0, 0, 15, 128, 0, 0, 228, 128, 1, 0, 228, 176, 1, 0, 0, 2, 0, 8, @@ -152,7 +156,7 @@ const BYTE g_main[] = 56, 0, 0, 8, 114, 0, 16, 0, 0, 0, 0, 0, 70, 2, 16, 0, 0, 0, - 0, 0, 86, 133, 32, 0, + 0, 0, 246, 143, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 7, 242, 32, 16, 0, 0, 0, @@ -181,11 +185,11 @@ const BYTE g_main[] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 68, 69, 70, - 8, 2, 0, 0, 1, 0, + 176, 2, 0, 0, 1, 0, 0, 0, 156, 0, 0, 0, 3, 0, 0, 0, 28, 0, 0, 0, 0, 4, 255, 255, - 0, 1, 0, 0, 221, 1, + 0, 1, 0, 0, 133, 2, 0, 0, 124, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -208,53 +212,81 @@ const BYTE g_main[] = 120, 116, 117, 114, 101, 0, 67, 111, 110, 115, 116, 97, 110, 116, 115, 0, 146, 0, - 0, 0, 8, 0, 0, 0, - 180, 0, 0, 0, 80, 0, + 0, 0, 12, 0, 0, 0, + 180, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 116, 1, + 0, 0, 0, 0, 212, 1, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, - 0, 0, 132, 1, 0, 0, - 0, 0, 0, 0, 148, 1, + 0, 0, 228, 1, 0, 0, + 0, 0, 0, 0, 244, 1, 0, 0, 4, 0, 0, 0, - 4, 0, 0, 0, 2, 0, - 0, 0, 132, 1, 0, 0, - 0, 0, 0, 0, 160, 1, + 4, 0, 0, 0, 0, 0, + 0, 0, 228, 1, 0, 0, + 0, 0, 0, 0, 1, 2, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, 0, - 0, 0, 132, 1, 0, 0, - 0, 0, 0, 0, 168, 1, + 0, 0, 228, 1, 0, 0, + 0, 0, 0, 0, 12, 2, 0, 0, 12, 0, 0, 0, - 4, 0, 0, 0, 0, 0, - 0, 0, 132, 1, 0, 0, - 0, 0, 0, 0, 176, 1, + 4, 0, 0, 0, 2, 0, + 0, 0, 228, 1, 0, 0, + 0, 0, 0, 0, 24, 2, 0, 0, 16, 0, 0, 0, - 16, 0, 0, 0, 0, 0, - 0, 0, 184, 1, 0, 0, - 0, 0, 0, 0, 200, 1, + 4, 0, 0, 0, 0, 0, + 0, 0, 228, 1, 0, 0, + 0, 0, 0, 0, 39, 2, + 0, 0, 20, 0, 0, 0, + 4, 0, 0, 0, 0, 0, + 0, 0, 228, 1, 0, 0, + 0, 0, 0, 0, 55, 2, + 0, 0, 24, 0, 0, 0, + 4, 0, 0, 0, 0, 0, + 0, 0, 228, 1, 0, 0, + 0, 0, 0, 0, 71, 2, + 0, 0, 28, 0, 0, 0, + 4, 0, 0, 0, 0, 0, + 0, 0, 228, 1, 0, 0, + 0, 0, 0, 0, 87, 2, 0, 0, 32, 0, 0, 0, 16, 0, 0, 0, 0, 0, - 0, 0, 184, 1, 0, 0, - 0, 0, 0, 0, 207, 1, + 0, 0, 96, 2, 0, 0, + 0, 0, 0, 0, 112, 2, 0, 0, 48, 0, 0, 0, 16, 0, 0, 0, 0, 0, - 0, 0, 184, 1, 0, 0, - 0, 0, 0, 0, 214, 1, + 0, 0, 96, 2, 0, 0, + 0, 0, 0, 0, 119, 2, 0, 0, 64, 0, 0, 0, 16, 0, 0, 0, 0, 0, - 0, 0, 184, 1, 0, 0, + 0, 0, 96, 2, 0, 0, + 0, 0, 0, 0, 126, 2, + 0, 0, 80, 0, 0, 0, + 16, 0, 0, 0, 0, 0, + 0, 0, 96, 2, 0, 0, 0, 0, 0, 0, 115, 99, 82, 71, 66, 95, 111, 117, 116, 112, 117, 116, 0, 171, 171, 171, 0, 0, 3, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 116, 101, 120, 116, 117, 114, + 101, 95, 116, 121, 112, 101, + 0, 105, 110, 112, 117, 116, + 95, 116, 121, 112, 101, 0, 99, 111, 108, 111, 114, 95, 115, 99, 97, 108, 101, 0, - 117, 110, 117, 115, 101, 100, - 49, 0, 117, 110, 117, 115, - 101, 100, 50, 0, 89, 111, - 102, 102, 115, 101, 116, 0, + 116, 111, 110, 101, 109, 97, + 112, 95, 109, 101, 116, 104, + 111, 100, 0, 116, 111, 110, + 101, 109, 97, 112, 95, 102, + 97, 99, 116, 111, 114, 49, + 0, 116, 111, 110, 101, 109, + 97, 112, 95, 102, 97, 99, + 116, 111, 114, 50, 0, 115, + 100, 114, 95, 119, 104, 105, + 116, 101, 95, 112, 111, 105, + 110, 116, 0, 89, 111, 102, + 102, 115, 101, 116, 0, 171, 1, 0, 3, 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 99, diff --git a/src/render/direct3d11/D3D11_PixelShader_YUV.h b/src/render/direct3d11/D3D11_PixelShader_YUV.h deleted file mode 100644 index 158763bf749b4..0000000000000 --- a/src/render/direct3d11/D3D11_PixelShader_YUV.h +++ /dev/null @@ -1,580 +0,0 @@ -#if 0 -// -// Generated by Microsoft (R) HLSL Shader Compiler 10.1 -// -// -// Buffer Definitions: -// -// cbuffer Constants -// { -// -// float scRGB_output; // Offset: 0 Size: 4 -// float color_scale; // Offset: 4 Size: 4 -// float unused1; // Offset: 8 Size: 4 [unused] -// float unused2; // Offset: 12 Size: 4 [unused] -// float4 Yoffset; // Offset: 16 Size: 16 -// float4 Rcoeff; // Offset: 32 Size: 16 -// float4 Gcoeff; // Offset: 48 Size: 16 -// float4 Bcoeff; // Offset: 64 Size: 16 -// -// } -// -// -// Resource Bindings: -// -// Name Type Format Dim HLSL Bind Count -// ------------------------------ ---------- ------- ----------- -------------- ------ -// theSampler sampler NA NA s0 1 -// theTextureY texture float4 2d t0 1 -// theTextureU texture float4 2d t1 1 -// theTextureV texture float4 2d t2 1 -// Constants cbuffer NA NA cb0 1 -// -// -// -// Input signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_POSITION 0 xyzw 0 POS float -// TEXCOORD 0 xy 1 NONE float xy -// COLOR 0 xyzw 2 NONE float xyzw -// -// -// Output signature: -// -// Name Index Mask Register SysValue Format Used -// -------------------- ----- ------ -------- -------- ------- ------ -// SV_TARGET 0 xyzw 0 TARGET float xyzw -// -// -// Constant buffer to DX9 shader constant mappings: -// -// Target Reg Buffer Start Reg # of Regs Data Conversion -// ---------- ------- --------- --------- ---------------------- -// c0 cb0 0 5 ( FLT, FLT, FLT, FLT) -// -// -// Sampler/Resource to DX9 shader sampler mappings: -// -// Target Sampler Source Sampler Source Resource -// -------------- --------------- ---------------- -// s0 s0 t0 -// s1 s0 t1 -// s2 s0 t2 -// -// -// Level9 shader bytecode: -// - ps_2_0 - def c5, 0.0404499993, 0.0773993805, 0.0549999997, 0.947867334 - def c6, 2.4000001, 1, 0, 0 - dcl t0.xy - dcl t1 - dcl_2d s0 - dcl_2d s1 - dcl_2d s2 - texld r0, t0, s0 - texld r1, t0, s1 - texld r2, t0, s2 - mov r0.y, r1.x - mov r0.z, r2.x - add r0.xyz, r0, c1 - dp3 r1.x, r0, c2 - add r0.w, r1.x, c5.z - abs r0.w, r0.w - mul r0.w, r0.w, c5.w - pow r1.w, r0.w, c6.x - add r0.w, -r1.x, c5.x - mul r2.x, r1.x, c5.y - cmp r2.x, r0.w, r2.x, r1.w - dp3 r1.y, r0, c3 - dp3 r1.z, r0, c4 - add r1.w, r1.y, c5.z - abs r1.w, r1.w - mul r1.w, r1.w, c5.w - pow r2.w, r1.w, c6.x - add r1.w, -r1.y, c5.x - mul r0.x, r1.y, c5.y - cmp r2.y, r1.w, r0.x, r2.w - add r1.w, r1.z, c5.z - abs r1.w, r1.w - mul r1.w, r1.w, c5.w - pow r2.w, r1.w, c6.x - add r1.w, -r1.z, c5.x - mul r0.x, r1.z, c5.y - cmp r2.z, r1.w, r0.x, r2.w - mul r1.w, c0.x, c0.x - cmp r0.xyz, -r1.w, r1, r2 - mul r0.xyz, r0, c0.y - mov r0.w, c6.y - mul r0, r0, t1 - mov oC0, r0 - -// approximately 42 instruction slots used (3 texture, 39 arithmetic) -ps_4_0 -dcl_constantbuffer CB0[5], immediateIndexed -dcl_sampler s0, mode_default -dcl_resource_texture2d (float,float,float,float) t0 -dcl_resource_texture2d (float,float,float,float) t1 -dcl_resource_texture2d (float,float,float,float) t2 -dcl_input_ps linear v1.xy -dcl_input_ps linear v2.xyzw -dcl_output o0.xyzw -dcl_temps 4 -sample r0.xyzw, v1.xyxx, t0.xyzw, s0 -sample r1.xyzw, v1.xyxx, t1.xyzw, s0 -sample r2.xyzw, v1.xyxx, t2.yzxw, s0 -mov r2.x, r0.x -mov r2.y, r1.x -add r0.xyz, r2.xyzx, cb0[1].xyzx -dp3 r1.x, r0.xyzx, cb0[2].xyzx -dp3 r1.y, r0.xyzx, cb0[3].xyzx -dp3 r1.z, r0.xyzx, cb0[4].xyzx -ne r0.x, l(0.000000, 0.000000, 0.000000, 0.000000), cb0[0].x -if_nz r0.x - ge r0.xyz, l(0.040450, 0.040450, 0.040450, 0.000000), r1.xyzx - mul r2.xyz, r1.xyzx, l(0.077399, 0.077399, 0.077399, 0.000000) - add r3.xyz, r1.xyzx, l(0.055000, 0.055000, 0.055000, 0.000000) - mul r3.xyz, |r3.xyzx|, l(0.947867, 0.947867, 0.947867, 0.000000) - log r3.xyz, r3.xyzx - mul r3.xyz, r3.xyzx, l(2.400000, 2.400000, 2.400000, 0.000000) - exp r3.xyz, r3.xyzx - movc r1.xyz, r0.xyzx, r2.xyzx, r3.xyzx -endif -mul r0.xyz, r1.xyzx, cb0[0].yyyy -mov r0.w, l(1.000000) -mul o0.xyzw, r0.xyzw, v2.xyzw -ret -// Approximately 24 instruction slots used -#endif - -const BYTE g_main[] = -{ - 68, 88, 66, 67, 84, 14, - 232, 134, 42, 204, 66, 203, - 56, 83, 173, 74, 161, 2, - 47, 168, 1, 0, 0, 0, - 248, 9, 0, 0, 6, 0, - 0, 0, 56, 0, 0, 0, - 36, 3, 0, 0, 104, 6, - 0, 0, 228, 6, 0, 0, - 80, 9, 0, 0, 196, 9, - 0, 0, 65, 111, 110, 57, - 228, 2, 0, 0, 228, 2, - 0, 0, 0, 2, 255, 255, - 168, 2, 0, 0, 60, 0, - 0, 0, 1, 0, 48, 0, - 0, 0, 60, 0, 0, 0, - 60, 0, 3, 0, 36, 0, - 0, 0, 60, 0, 0, 0, - 0, 0, 1, 0, 1, 0, - 2, 0, 2, 0, 0, 0, - 0, 0, 5, 0, 0, 0, - 0, 0, 0, 0, 0, 2, - 255, 255, 81, 0, 0, 5, - 5, 0, 15, 160, 230, 174, - 37, 61, 145, 131, 158, 61, - 174, 71, 97, 61, 111, 167, - 114, 63, 81, 0, 0, 5, - 6, 0, 15, 160, 154, 153, - 25, 64, 0, 0, 128, 63, - 0, 0, 0, 0, 0, 0, - 0, 0, 31, 0, 0, 2, - 0, 0, 0, 128, 0, 0, - 3, 176, 31, 0, 0, 2, - 0, 0, 0, 128, 1, 0, - 15, 176, 31, 0, 0, 2, - 0, 0, 0, 144, 0, 8, - 15, 160, 31, 0, 0, 2, - 0, 0, 0, 144, 1, 8, - 15, 160, 31, 0, 0, 2, - 0, 0, 0, 144, 2, 8, - 15, 160, 66, 0, 0, 3, - 0, 0, 15, 128, 0, 0, - 228, 176, 0, 8, 228, 160, - 66, 0, 0, 3, 1, 0, - 15, 128, 0, 0, 228, 176, - 1, 8, 228, 160, 66, 0, - 0, 3, 2, 0, 15, 128, - 0, 0, 228, 176, 2, 8, - 228, 160, 1, 0, 0, 2, - 0, 0, 2, 128, 1, 0, - 0, 128, 1, 0, 0, 2, - 0, 0, 4, 128, 2, 0, - 0, 128, 2, 0, 0, 3, - 0, 0, 7, 128, 0, 0, - 228, 128, 1, 0, 228, 160, - 8, 0, 0, 3, 1, 0, - 1, 128, 0, 0, 228, 128, - 2, 0, 228, 160, 2, 0, - 0, 3, 0, 0, 8, 128, - 1, 0, 0, 128, 5, 0, - 170, 160, 35, 0, 0, 2, - 0, 0, 8, 128, 0, 0, - 255, 128, 5, 0, 0, 3, - 0, 0, 8, 128, 0, 0, - 255, 128, 5, 0, 255, 160, - 32, 0, 0, 3, 1, 0, - 8, 128, 0, 0, 255, 128, - 6, 0, 0, 160, 2, 0, - 0, 3, 0, 0, 8, 128, - 1, 0, 0, 129, 5, 0, - 0, 160, 5, 0, 0, 3, - 2, 0, 1, 128, 1, 0, - 0, 128, 5, 0, 85, 160, - 88, 0, 0, 4, 2, 0, - 1, 128, 0, 0, 255, 128, - 2, 0, 0, 128, 1, 0, - 255, 128, 8, 0, 0, 3, - 1, 0, 2, 128, 0, 0, - 228, 128, 3, 0, 228, 160, - 8, 0, 0, 3, 1, 0, - 4, 128, 0, 0, 228, 128, - 4, 0, 228, 160, 2, 0, - 0, 3, 1, 0, 8, 128, - 1, 0, 85, 128, 5, 0, - 170, 160, 35, 0, 0, 2, - 1, 0, 8, 128, 1, 0, - 255, 128, 5, 0, 0, 3, - 1, 0, 8, 128, 1, 0, - 255, 128, 5, 0, 255, 160, - 32, 0, 0, 3, 2, 0, - 8, 128, 1, 0, 255, 128, - 6, 0, 0, 160, 2, 0, - 0, 3, 1, 0, 8, 128, - 1, 0, 85, 129, 5, 0, - 0, 160, 5, 0, 0, 3, - 0, 0, 1, 128, 1, 0, - 85, 128, 5, 0, 85, 160, - 88, 0, 0, 4, 2, 0, - 2, 128, 1, 0, 255, 128, - 0, 0, 0, 128, 2, 0, - 255, 128, 2, 0, 0, 3, - 1, 0, 8, 128, 1, 0, - 170, 128, 5, 0, 170, 160, - 35, 0, 0, 2, 1, 0, - 8, 128, 1, 0, 255, 128, - 5, 0, 0, 3, 1, 0, - 8, 128, 1, 0, 255, 128, - 5, 0, 255, 160, 32, 0, - 0, 3, 2, 0, 8, 128, - 1, 0, 255, 128, 6, 0, - 0, 160, 2, 0, 0, 3, - 1, 0, 8, 128, 1, 0, - 170, 129, 5, 0, 0, 160, - 5, 0, 0, 3, 0, 0, - 1, 128, 1, 0, 170, 128, - 5, 0, 85, 160, 88, 0, - 0, 4, 2, 0, 4, 128, - 1, 0, 255, 128, 0, 0, - 0, 128, 2, 0, 255, 128, - 5, 0, 0, 3, 1, 0, - 8, 128, 0, 0, 0, 160, - 0, 0, 0, 160, 88, 0, - 0, 4, 0, 0, 7, 128, - 1, 0, 255, 129, 1, 0, - 228, 128, 2, 0, 228, 128, - 5, 0, 0, 3, 0, 0, - 7, 128, 0, 0, 228, 128, - 0, 0, 85, 160, 1, 0, - 0, 2, 0, 0, 8, 128, - 6, 0, 85, 160, 5, 0, - 0, 3, 0, 0, 15, 128, - 0, 0, 228, 128, 1, 0, - 228, 176, 1, 0, 0, 2, - 0, 8, 15, 128, 0, 0, - 228, 128, 255, 255, 0, 0, - 83, 72, 68, 82, 60, 3, - 0, 0, 64, 0, 0, 0, - 207, 0, 0, 0, 89, 0, - 0, 4, 70, 142, 32, 0, - 0, 0, 0, 0, 5, 0, - 0, 0, 90, 0, 0, 3, - 0, 96, 16, 0, 0, 0, - 0, 0, 88, 24, 0, 4, - 0, 112, 16, 0, 0, 0, - 0, 0, 85, 85, 0, 0, - 88, 24, 0, 4, 0, 112, - 16, 0, 1, 0, 0, 0, - 85, 85, 0, 0, 88, 24, - 0, 4, 0, 112, 16, 0, - 2, 0, 0, 0, 85, 85, - 0, 0, 98, 16, 0, 3, - 50, 16, 16, 0, 1, 0, - 0, 0, 98, 16, 0, 3, - 242, 16, 16, 0, 2, 0, - 0, 0, 101, 0, 0, 3, - 242, 32, 16, 0, 0, 0, - 0, 0, 104, 0, 0, 2, - 4, 0, 0, 0, 69, 0, - 0, 9, 242, 0, 16, 0, - 0, 0, 0, 0, 70, 16, - 16, 0, 1, 0, 0, 0, - 70, 126, 16, 0, 0, 0, - 0, 0, 0, 96, 16, 0, - 0, 0, 0, 0, 69, 0, - 0, 9, 242, 0, 16, 0, - 1, 0, 0, 0, 70, 16, - 16, 0, 1, 0, 0, 0, - 70, 126, 16, 0, 1, 0, - 0, 0, 0, 96, 16, 0, - 0, 0, 0, 0, 69, 0, - 0, 9, 242, 0, 16, 0, - 2, 0, 0, 0, 70, 16, - 16, 0, 1, 0, 0, 0, - 150, 124, 16, 0, 2, 0, - 0, 0, 0, 96, 16, 0, - 0, 0, 0, 0, 54, 0, - 0, 5, 18, 0, 16, 0, - 2, 0, 0, 0, 10, 0, - 16, 0, 0, 0, 0, 0, - 54, 0, 0, 5, 34, 0, - 16, 0, 2, 0, 0, 0, - 10, 0, 16, 0, 1, 0, - 0, 0, 0, 0, 0, 8, - 114, 0, 16, 0, 0, 0, - 0, 0, 70, 2, 16, 0, - 2, 0, 0, 0, 70, 130, - 32, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 16, 0, - 0, 8, 18, 0, 16, 0, - 1, 0, 0, 0, 70, 2, - 16, 0, 0, 0, 0, 0, - 70, 130, 32, 0, 0, 0, - 0, 0, 2, 0, 0, 0, - 16, 0, 0, 8, 34, 0, - 16, 0, 1, 0, 0, 0, - 70, 2, 16, 0, 0, 0, - 0, 0, 70, 130, 32, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 16, 0, 0, 8, - 66, 0, 16, 0, 1, 0, - 0, 0, 70, 2, 16, 0, - 0, 0, 0, 0, 70, 130, - 32, 0, 0, 0, 0, 0, - 4, 0, 0, 0, 57, 0, - 0, 11, 18, 0, 16, 0, - 0, 0, 0, 0, 2, 64, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 10, 128, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 31, 0, 4, 3, 10, 0, - 16, 0, 0, 0, 0, 0, - 29, 0, 0, 10, 114, 0, - 16, 0, 0, 0, 0, 0, - 2, 64, 0, 0, 230, 174, - 37, 61, 230, 174, 37, 61, - 230, 174, 37, 61, 0, 0, - 0, 0, 70, 2, 16, 0, - 1, 0, 0, 0, 56, 0, - 0, 10, 114, 0, 16, 0, - 2, 0, 0, 0, 70, 2, - 16, 0, 1, 0, 0, 0, - 2, 64, 0, 0, 145, 131, - 158, 61, 145, 131, 158, 61, - 145, 131, 158, 61, 0, 0, - 0, 0, 0, 0, 0, 10, - 114, 0, 16, 0, 3, 0, - 0, 0, 70, 2, 16, 0, - 1, 0, 0, 0, 2, 64, - 0, 0, 174, 71, 97, 61, - 174, 71, 97, 61, 174, 71, - 97, 61, 0, 0, 0, 0, - 56, 0, 0, 11, 114, 0, - 16, 0, 3, 0, 0, 0, - 70, 2, 16, 128, 129, 0, - 0, 0, 3, 0, 0, 0, - 2, 64, 0, 0, 111, 167, - 114, 63, 111, 167, 114, 63, - 111, 167, 114, 63, 0, 0, - 0, 0, 47, 0, 0, 5, - 114, 0, 16, 0, 3, 0, - 0, 0, 70, 2, 16, 0, - 3, 0, 0, 0, 56, 0, - 0, 10, 114, 0, 16, 0, - 3, 0, 0, 0, 70, 2, - 16, 0, 3, 0, 0, 0, - 2, 64, 0, 0, 154, 153, - 25, 64, 154, 153, 25, 64, - 154, 153, 25, 64, 0, 0, - 0, 0, 25, 0, 0, 5, - 114, 0, 16, 0, 3, 0, - 0, 0, 70, 2, 16, 0, - 3, 0, 0, 0, 55, 0, - 0, 9, 114, 0, 16, 0, - 1, 0, 0, 0, 70, 2, - 16, 0, 0, 0, 0, 0, - 70, 2, 16, 0, 2, 0, - 0, 0, 70, 2, 16, 0, - 3, 0, 0, 0, 21, 0, - 0, 1, 56, 0, 0, 8, - 114, 0, 16, 0, 0, 0, - 0, 0, 70, 2, 16, 0, - 1, 0, 0, 0, 86, 133, - 32, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 54, 0, - 0, 5, 130, 0, 16, 0, - 0, 0, 0, 0, 1, 64, - 0, 0, 0, 0, 128, 63, - 56, 0, 0, 7, 242, 32, - 16, 0, 0, 0, 0, 0, - 70, 14, 16, 0, 0, 0, - 0, 0, 70, 30, 16, 0, - 2, 0, 0, 0, 62, 0, - 0, 1, 83, 84, 65, 84, - 116, 0, 0, 0, 24, 0, - 0, 0, 4, 0, 0, 0, - 0, 0, 0, 0, 3, 0, - 0, 0, 13, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 1, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 82, 68, 69, 70, 100, 2, - 0, 0, 1, 0, 0, 0, - 248, 0, 0, 0, 5, 0, - 0, 0, 28, 0, 0, 0, - 0, 4, 255, 255, 0, 1, - 0, 0, 57, 2, 0, 0, - 188, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 1, 0, - 0, 0, 199, 0, 0, 0, - 2, 0, 0, 0, 5, 0, - 0, 0, 4, 0, 0, 0, - 255, 255, 255, 255, 0, 0, - 0, 0, 1, 0, 0, 0, - 13, 0, 0, 0, 211, 0, - 0, 0, 2, 0, 0, 0, - 5, 0, 0, 0, 4, 0, - 0, 0, 255, 255, 255, 255, - 1, 0, 0, 0, 1, 0, - 0, 0, 13, 0, 0, 0, - 223, 0, 0, 0, 2, 0, - 0, 0, 5, 0, 0, 0, - 4, 0, 0, 0, 255, 255, - 255, 255, 2, 0, 0, 0, - 1, 0, 0, 0, 13, 0, - 0, 0, 235, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 1, 0, 0, 0, 116, 104, - 101, 83, 97, 109, 112, 108, - 101, 114, 0, 116, 104, 101, - 84, 101, 120, 116, 117, 114, - 101, 89, 0, 116, 104, 101, - 84, 101, 120, 116, 117, 114, - 101, 85, 0, 116, 104, 101, - 84, 101, 120, 116, 117, 114, - 101, 86, 0, 67, 111, 110, - 115, 116, 97, 110, 116, 115, - 0, 171, 171, 171, 235, 0, - 0, 0, 8, 0, 0, 0, - 16, 1, 0, 0, 80, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 208, 1, - 0, 0, 0, 0, 0, 0, - 4, 0, 0, 0, 2, 0, - 0, 0, 224, 1, 0, 0, - 0, 0, 0, 0, 240, 1, - 0, 0, 4, 0, 0, 0, - 4, 0, 0, 0, 2, 0, - 0, 0, 224, 1, 0, 0, - 0, 0, 0, 0, 252, 1, - 0, 0, 8, 0, 0, 0, - 4, 0, 0, 0, 0, 0, - 0, 0, 224, 1, 0, 0, - 0, 0, 0, 0, 4, 2, - 0, 0, 12, 0, 0, 0, - 4, 0, 0, 0, 0, 0, - 0, 0, 224, 1, 0, 0, - 0, 0, 0, 0, 12, 2, - 0, 0, 16, 0, 0, 0, - 16, 0, 0, 0, 2, 0, - 0, 0, 20, 2, 0, 0, - 0, 0, 0, 0, 36, 2, - 0, 0, 32, 0, 0, 0, - 16, 0, 0, 0, 2, 0, - 0, 0, 20, 2, 0, 0, - 0, 0, 0, 0, 43, 2, - 0, 0, 48, 0, 0, 0, - 16, 0, 0, 0, 2, 0, - 0, 0, 20, 2, 0, 0, - 0, 0, 0, 0, 50, 2, - 0, 0, 64, 0, 0, 0, - 16, 0, 0, 0, 2, 0, - 0, 0, 20, 2, 0, 0, - 0, 0, 0, 0, 115, 99, - 82, 71, 66, 95, 111, 117, - 116, 112, 117, 116, 0, 171, - 171, 171, 0, 0, 3, 0, - 1, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 99, 111, 108, 111, 114, 95, - 115, 99, 97, 108, 101, 0, - 117, 110, 117, 115, 101, 100, - 49, 0, 117, 110, 117, 115, - 101, 100, 50, 0, 89, 111, - 102, 102, 115, 101, 116, 0, - 1, 0, 3, 0, 1, 0, - 4, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 82, 99, - 111, 101, 102, 102, 0, 71, - 99, 111, 101, 102, 102, 0, - 66, 99, 111, 101, 102, 102, - 0, 77, 105, 99, 114, 111, - 115, 111, 102, 116, 32, 40, - 82, 41, 32, 72, 76, 83, - 76, 32, 83, 104, 97, 100, - 101, 114, 32, 67, 111, 109, - 112, 105, 108, 101, 114, 32, - 49, 48, 46, 49, 0, 171, - 171, 171, 73, 83, 71, 78, - 108, 0, 0, 0, 3, 0, - 0, 0, 8, 0, 0, 0, - 80, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, - 3, 0, 0, 0, 0, 0, - 0, 0, 15, 0, 0, 0, - 92, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 1, 0, - 0, 0, 3, 3, 0, 0, - 101, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 3, 0, 0, 0, 2, 0, - 0, 0, 15, 15, 0, 0, - 83, 86, 95, 80, 79, 83, - 73, 84, 73, 79, 78, 0, - 84, 69, 88, 67, 79, 79, - 82, 68, 0, 67, 79, 76, - 79, 82, 0, 171, 79, 83, - 71, 78, 44, 0, 0, 0, - 1, 0, 0, 0, 8, 0, - 0, 0, 32, 0, 0, 0, - 0, 0, 0, 0, 0, 0, - 0, 0, 3, 0, 0, 0, - 0, 0, 0, 0, 15, 0, - 0, 0, 83, 86, 95, 84, - 65, 82, 71, 69, 84, 0, - 171, 171 -}; diff --git a/src/render/direct3d11/D3D11_PixelShader_YUV.hlsl b/src/render/direct3d11/D3D11_PixelShader_YUV.hlsl deleted file mode 100644 index 7af85428956fa..0000000000000 --- a/src/render/direct3d11/D3D11_PixelShader_YUV.hlsl +++ /dev/null @@ -1,22 +0,0 @@ -Texture2D theTextureY : register(t0); -Texture2D theTextureU : register(t1); -Texture2D theTextureV : register(t2); -SamplerState theSampler : register(s0); - -#include "D3D11_PixelShader_Common.incl" - -float4 main(PixelShaderInput input) : SV_TARGET -{ - float3 yuv; - yuv.x = theTextureY.Sample(theSampler, input.tex).r; - yuv.y = theTextureU.Sample(theSampler, input.tex).r; - yuv.z = theTextureV.Sample(theSampler, input.tex).r; - - float3 rgb; - yuv += Yoffset.xyz; - rgb.r = dot(yuv, Rcoeff.xyz); - rgb.g = dot(yuv, Gcoeff.xyz); - rgb.b = dot(yuv, Bcoeff.xyz); - - return GetOutputColorFromSRGB(rgb) * input.color; -} diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index 358e1318ec229..c875285aab9a7 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -75,21 +75,41 @@ typedef struct Float4X4 projectionAndView; } VertexShaderConstants; +/* These should mirror the definitions in D3D11_PixelShader_Common.incl */ +//static const float TONEMAP_NONE = 0; +//static const float TONEMAP_LINEAR = 1; +static const float TONEMAP_CHROME = 2; + +//static const float TEXTURETYPE_NONE = 0; +static const float TEXTURETYPE_RGB = 1; +static const float TEXTURETYPE_NV12 = 2; +static const float TEXTURETYPE_NV21 = 3; +static const float TEXTURETYPE_YUV = 4; + +static const float INPUTTYPE_UNSPECIFIED = 0; +static const float INPUTTYPE_SRGB = 1; +static const float INPUTTYPE_SCRGB = 2; +static const float INPUTTYPE_HDR10 = 3; + typedef struct { float scRGB_output; + float texture_type; + float input_type; float color_scale; - float unused1; - float unused2; + + float tonemap_method; + float tonemap_factor1; + float tonemap_factor2; + float sdr_white_point; + float YCbCr_matrix[16]; } PixelShaderConstants; typedef struct { ID3D11Buffer *constants; - SDL_bool scRGB_output; - float color_scale; - const float *shader_params; + PixelShaderConstants shader_constants; } PixelShaderState; /* Per-vertex data */ @@ -112,7 +132,7 @@ typedef struct int lockedTexturePositionY; D3D11_FILTER scaleMode; D3D11_Shader shader; - const float *shader_params; + const float *YCbCr_matrix; #if SDL_HAVE_YUV /* YV12 texture support */ SDL_bool yuv; @@ -245,6 +265,8 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 color switch (format) { case SDL_PIXELFORMAT_RGBA64_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT; + case SDL_PIXELFORMAT_XBGR2101010: + return DXGI_FORMAT_R10G10B10A2_UNORM; case SDL_PIXELFORMAT_ARGB8888: if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) { return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; @@ -263,8 +285,6 @@ static DXGI_FORMAT SDLPixelFormatToDXGITextureFormat(Uint32 format, Uint32 color return DXGI_FORMAT_NV12; case SDL_PIXELFORMAT_P010: return DXGI_FORMAT_P010; - case SDL_PIXELFORMAT_P016: - return DXGI_FORMAT_P016; default: return DXGI_FORMAT_UNKNOWN; } @@ -275,6 +295,8 @@ static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(Uint32 format, Uin switch (format) { case SDL_PIXELFORMAT_RGBA64_FLOAT: return DXGI_FORMAT_R16G16B16A16_FLOAT; + case SDL_PIXELFORMAT_XBGR2101010: + return DXGI_FORMAT_R10G10B10A2_UNORM; case SDL_PIXELFORMAT_ARGB8888: if (colorspace == SDL_COLORSPACE_SRGB_LINEAR) { return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; @@ -291,7 +313,6 @@ static DXGI_FORMAT SDLPixelFormatToDXGIMainResourceViewFormat(Uint32 format, Uin case SDL_PIXELFORMAT_NV21: /* For the Y texture */ return DXGI_FORMAT_R8_UNORM; case SDL_PIXELFORMAT_P010: /* For the Y texture */ - case SDL_PIXELFORMAT_P016: /* For the Y texture */ return DXGI_FORMAT_R16_UNORM; default: return DXGI_FORMAT_UNKNOWN; @@ -1267,14 +1288,17 @@ static int D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL /* NV12 textures must have even width and height */ if (texture->format == SDL_PIXELFORMAT_NV12 || texture->format == SDL_PIXELFORMAT_NV21 || - texture->format == SDL_PIXELFORMAT_P010 || - texture->format == SDL_PIXELFORMAT_P016) { + texture->format == SDL_PIXELFORMAT_P010) { textureDesc.Width = (textureDesc.Width + 1) & ~1; textureDesc.Height = (textureDesc.Height + 1) & ~1; } textureData->w = (int)textureDesc.Width; textureData->h = (int)textureDesc.Height; - textureData->shader = SHADER_RGB; + if (SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_TRANSFER_CHARACTERISTICS_SRGB) { + textureData->shader = SHADER_RGB; + } else { + textureData->shader = SHADER_ADVANCED; + } if (texture->access == SDL_TEXTUREACCESS_STREAMING) { textureDesc.Usage = D3D11_USAGE_DYNAMIC; @@ -1339,53 +1363,28 @@ static int D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL } SDL_SetProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_V_POINTER, textureData->mainTextureV); - textureData->shader = SHADER_YUV; - textureData->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8); - if (!textureData->shader_params) { + textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8); + if (!textureData->YCbCr_matrix) { return SDL_SetError("Unsupported YUV colorspace"); } } if (texture->format == SDL_PIXELFORMAT_NV12 || texture->format == SDL_PIXELFORMAT_NV21 || - texture->format == SDL_PIXELFORMAT_P010 || - texture->format == SDL_PIXELFORMAT_P016) { + texture->format == SDL_PIXELFORMAT_P010) { int bits_per_pixel; textureData->nv12 = SDL_TRUE; - switch (texture->format) { - case SDL_PIXELFORMAT_NV12: - textureData->shader = SHADER_NV12; - break; - case SDL_PIXELFORMAT_NV21: - textureData->shader = SHADER_NV21; - break; - case SDL_PIXELFORMAT_P010: - case SDL_PIXELFORMAT_P016: - if(SDL_COLORSPACEPRIMARIES(texture->colorspace) == SDL_COLOR_PRIMARIES_BT2020 && - SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) { - textureData->shader = SHADER_HDR10; - } else { - return SDL_SetError("Unsupported YUV colorspace"); - } - break; - default: - /* This should never happen because of the check above */ - return SDL_SetError("Unsupported YUV colorspace"); - } switch (texture->format) { case SDL_PIXELFORMAT_P010: bits_per_pixel = 10; break; - case SDL_PIXELFORMAT_P016: - bits_per_pixel = 16; - break; default: bits_per_pixel = 8; break; } - textureData->shader_params = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel); - if (!textureData->shader_params) { + textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel); + if (!textureData->YCbCr_matrix) { return SDL_SetError("Unsupported YUV colorspace"); } } @@ -1425,7 +1424,7 @@ static int D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL if (texture->format == SDL_PIXELFORMAT_NV12 || texture->format == SDL_PIXELFORMAT_NV21) { nvResourceViewDesc.Format = DXGI_FORMAT_R8G8_UNORM; - } else if (texture->format == SDL_PIXELFORMAT_P010 || texture->format == SDL_PIXELFORMAT_P016) { + } else if (texture->format == SDL_PIXELFORMAT_P010) { nvResourceViewDesc.Format = DXGI_FORMAT_R16G16_UNORM; } @@ -1503,8 +1502,7 @@ static int D3D11_UpdateTextureInternal(D3D11_RenderData *rendererData, ID3D11Tex stagingTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; stagingTextureDesc.Usage = D3D11_USAGE_STAGING; if (stagingTextureDesc.Format == DXGI_FORMAT_NV12 || - stagingTextureDesc.Format == DXGI_FORMAT_P010 || - stagingTextureDesc.Format == DXGI_FORMAT_P016) { + stagingTextureDesc.Format == DXGI_FORMAT_P010) { stagingTextureDesc.Width = (stagingTextureDesc.Width + 1) & ~1; stagingTextureDesc.Height = (stagingTextureDesc.Height + 1) & ~1; } @@ -1548,8 +1546,7 @@ static int D3D11_UpdateTextureInternal(D3D11_RenderData *rendererData, ID3D11Tex } if (stagingTextureDesc.Format == DXGI_FORMAT_NV12 || - stagingTextureDesc.Format == DXGI_FORMAT_P010 || - stagingTextureDesc.Format == DXGI_FORMAT_P016) { + stagingTextureDesc.Format == DXGI_FORMAT_P010) { /* Copy the UV plane as well */ h = (h + 1) / 2; length = (length + 1) & ~1; @@ -2170,8 +2167,71 @@ static ID3D11RenderTargetView *D3D11_GetCurrentRenderTargetView(SDL_Renderer *re } } +static void D3D11_SetupShaderConstants(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, const SDL_Texture *texture, PixelShaderConstants *constants) +{ + float output_headroom; + + SDL_zerop(constants); + + constants->scRGB_output = (float)SDL_RenderingLinearSpace(renderer); + constants->color_scale = cmd->data.draw.color_scale; + + if (texture) { + D3D11_TextureData *textureData = (D3D11_TextureData *)texture->driverdata; + + switch (texture->format) { + case SDL_PIXELFORMAT_YV12: + case SDL_PIXELFORMAT_IYUV: + constants->texture_type = TEXTURETYPE_YUV; + constants->input_type = INPUTTYPE_SRGB; + break; + case SDL_PIXELFORMAT_NV12: + constants->texture_type = TEXTURETYPE_NV12; + constants->input_type = INPUTTYPE_SRGB; + break; + case SDL_PIXELFORMAT_NV21: + constants->texture_type = TEXTURETYPE_NV21; + constants->input_type = INPUTTYPE_SRGB; + break; + case SDL_PIXELFORMAT_P010: + constants->texture_type = TEXTURETYPE_NV12; + constants->input_type = INPUTTYPE_HDR10; + break; + default: + constants->texture_type = TEXTURETYPE_RGB; + if (texture->colorspace == SDL_COLORSPACE_SRGB_LINEAR) { + constants->input_type = INPUTTYPE_SCRGB; + } else if (SDL_COLORSPACEPRIMARIES(texture->colorspace) == SDL_COLOR_PRIMARIES_BT2020 && + SDL_COLORSPACETRANSFER(texture->colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) { + constants->input_type = INPUTTYPE_HDR10; + } else { + constants->input_type = INPUTTYPE_UNSPECIFIED; + } + break; + } + + constants->sdr_white_point = texture->SDR_white_point; + + if (renderer->target) { + output_headroom = renderer->target->HDR_headroom; + } else { + output_headroom = renderer->HDR_headroom; + } + + if (texture->HDR_headroom > output_headroom) { + constants->tonemap_method = TONEMAP_CHROME; + constants->tonemap_factor1 = (output_headroom / (texture->HDR_headroom * texture->HDR_headroom)); + constants->tonemap_factor2 = (1.0f / output_headroom); + } + + if (textureData->YCbCr_matrix) { + SDL_memcpy(constants->YCbCr_matrix, textureData->YCbCr_matrix, sizeof(constants->YCbCr_matrix)); + } + } +} + static int D3D11_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, - D3D11_Shader shader, const float *shader_params, + D3D11_Shader shader, const PixelShaderConstants *shader_constants, const int numShaderResources, ID3D11ShaderResourceView **shaderResources, ID3D11SamplerState *sampler, const Float4X4 *matrix) @@ -2184,9 +2244,8 @@ static int D3D11_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *c const SDL_BlendMode blendMode = cmd->data.draw.blend; ID3D11BlendState *blendState = NULL; SDL_bool updateSubresource = SDL_FALSE; - SDL_bool scRGB_output = SDL_RenderingLinearSpace(renderer); - float color_scale = cmd->data.draw.color_scale; PixelShaderState *shader_state = &rendererData->currentShaderState[shader]; + PixelShaderConstants solid_constants; if (numShaderResources > 0) { shaderResource = shaderResources[0]; @@ -2260,37 +2319,31 @@ static int D3D11_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *c rendererData->currentBlendState = blendState; } + if (!shader_constants) { + D3D11_SetupShaderConstants(renderer, cmd, NULL, &solid_constants); + shader_constants = &solid_constants; + } + if (!shader_state->constants || - scRGB_output != shader_state->scRGB_output || - color_scale != shader_state->color_scale || - shader_params != shader_state->shader_params) { + SDL_memcmp(shader_constants, &shader_state->shader_constants, sizeof(*shader_constants)) != 0) { SAFE_RELEASE(shader_state->constants); - PixelShaderConstants constants; - constants.scRGB_output = (float)scRGB_output; - constants.color_scale = color_scale; - if (shader_params) { - SDL_memcpy(constants.YCbCr_matrix, shader_params, sizeof(constants.YCbCr_matrix)); - } - D3D11_BUFFER_DESC desc; SDL_zero(desc); desc.Usage = D3D11_USAGE_DEFAULT; - desc.ByteWidth = sizeof(constants); + desc.ByteWidth = sizeof(*shader_constants); desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; D3D11_SUBRESOURCE_DATA data; SDL_zero(data); - data.pSysMem = &constants; + data.pSysMem = shader_constants; HRESULT result = ID3D11Device_CreateBuffer(rendererData->d3dDevice, &desc, &data, &shader_state->constants); if (FAILED(result)) { WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device::CreateBuffer [create shader constants]"), result); return -1; } - shader_state->scRGB_output = scRGB_output; - shader_state->color_scale = color_scale; - shader_state->shader_params = shader_params; + SDL_memcpy(&shader_state->shader_constants, shader_constants, sizeof(*shader_constants)); /* Force the shader parameters to be re-set */ rendererData->currentShader = SHADER_NONE; @@ -2302,8 +2355,8 @@ static int D3D11_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *c } } ID3D11DeviceContext_PSSetShader(rendererData->d3dContext, rendererData->pixelShaders[shader], NULL, 0); - if (rendererData->currentShaderState[shader].constants) { - ID3D11DeviceContext_PSSetConstantBuffers(rendererData->d3dContext, 0, 1, &rendererData->currentShaderState[shader].constants); + if (shader_state->constants) { + ID3D11DeviceContext_PSSetConstantBuffers(rendererData->d3dContext, 0, 1, &shader_state->constants); } rendererData->currentShader = shader; } @@ -2336,6 +2389,9 @@ static int D3D11_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *c D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->driverdata; D3D11_TextureData *textureData = (D3D11_TextureData *)texture->driverdata; ID3D11SamplerState *textureSampler; + PixelShaderConstants constants; + + D3D11_SetupShaderConstants(renderer, cmd, texture, &constants); switch (textureData->scaleMode) { case D3D11_FILTER_MIN_MAG_MIP_POINT: @@ -2355,7 +2411,7 @@ static int D3D11_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *c shaderResources[1] = textureData->mainTextureResourceViewU; shaderResources[2] = textureData->mainTextureResourceViewV; - return D3D11_SetDrawState(renderer, cmd, textureData->shader, textureData->shader_params, + return D3D11_SetDrawState(renderer, cmd, textureData->shader, &constants, SDL_arraysize(shaderResources), shaderResources, textureSampler, matrix); } else if (textureData->nv12) { @@ -2364,11 +2420,11 @@ static int D3D11_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *c shaderResources[0] = textureData->mainTextureResourceView; shaderResources[1] = textureData->mainTextureResourceViewNV; - return D3D11_SetDrawState(renderer, cmd, textureData->shader, textureData->shader_params, + return D3D11_SetDrawState(renderer, cmd, textureData->shader, &constants, SDL_arraysize(shaderResources), shaderResources, textureSampler, matrix); } #endif /* SDL_HAVE_YUV */ - return D3D11_SetDrawState(renderer, cmd, textureData->shader, textureData->shader_params, + return D3D11_SetDrawState(renderer, cmd, textureData->shader, &constants, 1, &textureData->mainTextureResourceView, textureSampler, matrix); } @@ -2791,13 +2847,13 @@ SDL_RenderDriver D3D11_RenderDriver = { { /* texture_formats */ SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORMAT_XRGB8888, + SDL_PIXELFORMAT_XBGR2101010, SDL_PIXELFORMAT_RGBA64_FLOAT, SDL_PIXELFORMAT_YV12, SDL_PIXELFORMAT_IYUV, SDL_PIXELFORMAT_NV12, SDL_PIXELFORMAT_NV21, - SDL_PIXELFORMAT_P010, - SDL_PIXELFORMAT_P016 }, + SDL_PIXELFORMAT_P010 }, 0, /* max_texture_width: will be filled in later */ 0 /* max_texture_height: will be filled in later */ } diff --git a/src/render/direct3d11/SDL_shaders_d3d11.c b/src/render/direct3d11/SDL_shaders_d3d11.c index 557297fb6455a..7a88999fd977e 100644 --- a/src/render/direct3d11/SDL_shaders_d3d11.c +++ b/src/render/direct3d11/SDL_shaders_d3d11.c @@ -44,20 +44,8 @@ #include "D3D11_PixelShader_Textures.h" #undef g_main -#define g_main D3D11_PixelShader_YUV -#include "D3D11_PixelShader_YUV.h" -#undef g_main - -#define g_main D3D11_PixelShader_NV12 -#include "D3D11_PixelShader_NV12.h" -#undef g_main - -#define g_main D3D11_PixelShader_NV21 -#include "D3D11_PixelShader_NV21.h" -#undef g_main - -#define g_main D3D11_PixelShader_HDR10 -#include "D3D11_PixelShader_HDR10.h" +#define g_main D3D11_PixelShader_Advanced +#include "D3D11_PixelShader_Advanced.h" #undef g_main #define g_main D3D11_VertexShader @@ -73,12 +61,7 @@ static struct { NULL, 0 }, { D3D11_PixelShader_Colors, sizeof(D3D11_PixelShader_Colors) }, { D3D11_PixelShader_Textures, sizeof(D3D11_PixelShader_Textures) }, -#if SDL_HAVE_YUV - { D3D11_PixelShader_YUV, sizeof(D3D11_PixelShader_YUV) }, - { D3D11_PixelShader_NV12, sizeof(D3D11_PixelShader_NV12) }, - { D3D11_PixelShader_NV21, sizeof(D3D11_PixelShader_NV21) }, - { D3D11_PixelShader_HDR10, sizeof(D3D11_PixelShader_HDR10) }, -#endif + { D3D11_PixelShader_Advanced, sizeof(D3D11_PixelShader_Advanced) }, }; SDL_COMPILE_TIME_ASSERT(D3D11_shaders, SDL_arraysize(D3D11_shaders) == NUM_SHADERS); diff --git a/src/render/direct3d11/SDL_shaders_d3d11.h b/src/render/direct3d11/SDL_shaders_d3d11.h index 9567cf6f9c8b8..53ce2625a13ef 100644 --- a/src/render/direct3d11/SDL_shaders_d3d11.h +++ b/src/render/direct3d11/SDL_shaders_d3d11.h @@ -27,12 +27,7 @@ typedef enum SHADER_NONE, SHADER_SOLID, SHADER_RGB, -#if SDL_HAVE_YUV - SHADER_YUV, - SHADER_NV12, - SHADER_NV21, - SHADER_HDR10, -#endif + SHADER_ADVANCED, NUM_SHADERS } D3D11_Shader; diff --git a/src/render/direct3d11/compile_shaders.bat b/src/render/direct3d11/compile_shaders.bat index 8f33d582dbfa0..51d2a34cea320 100644 --- a/src/render/direct3d11/compile_shaders.bat +++ b/src/render/direct3d11/compile_shaders.bat @@ -1,7 +1,4 @@ fxc /T ps_4_0_level_9_1 /Fh D3D11_PixelShader_Colors.h D3D11_PixelShader_Colors.hlsl fxc /T ps_4_0_level_9_1 /Fh D3D11_PixelShader_Textures.h D3D11_PixelShader_Textures.hlsl -fxc /T ps_4_0_level_9_1 /Fh D3D11_PixelShader_YUV.h D3D11_PixelShader_YUV.hlsl -fxc /T ps_4_0_level_9_1 /Fh D3D11_PixelShader_NV12.h D3D11_PixelShader_NV12.hlsl -fxc /T ps_4_0_level_9_1 /Fh D3D11_PixelShader_NV21.h D3D11_PixelShader_NV21.hlsl -fxc /T ps_5_0 /Fh D3D11_PixelShader_HDR10.h D3D11_PixelShader_HDR10.hlsl +fxc /T ps_5_0 /Fh D3D11_PixelShader_Advanced.h D3D11_PixelShader_Advanced.hlsl fxc /T vs_4_0_level_9_1 /Fh D3D11_VertexShader.h D3D11_VertexShader.hlsl diff --git a/src/render/direct3d12/D3D12_PixelShader_Advanced.h b/src/render/direct3d12/D3D12_PixelShader_Advanced.h new file mode 100644 index 0000000000000..e795a0c5941d0 --- /dev/null +++ b/src/render/direct3d12/D3D12_PixelShader_Advanced.h @@ -0,0 +1,1387 @@ +#if 0 +; +; Input signature: +; +; Name Index Mask Register SysValue Format Used +; -------------------- ----- ------ -------- -------- ------- ------ +; SV_Position 0 xyzw 0 POS float +; TEXCOORD 0 xy 1 NONE float xy +; COLOR 0 xyzw 2 NONE float xyzw +; +; +; Output signature: +; +; Name Index Mask Register SysValue Format Used +; -------------------- ----- ------ -------- -------- ------- ------ +; SV_Target 0 xyzw 0 TARGET float xyzw +; +; shader hash: f6874446eaee41c102142c02d5bb3ab7 +; +; Pipeline Runtime Information: +; +; Pixel Shader +; DepthOutput=0 +; SampleFrequency=0 +; +; +; Input signature: +; +; Name Index InterpMode DynIdx +; -------------------- ----- ---------------------- ------ +; SV_Position 0 noperspective +; TEXCOORD 0 linear +; COLOR 0 linear +; +; Output signature: +; +; Name Index InterpMode DynIdx +; -------------------- ----- ---------------------- ------ +; SV_Target 0 +; +; Buffer Definitions: +; +; cbuffer Constants +; { +; +; struct Constants +; { +; +; float scRGB_output; ; Offset: 0 +; float texture_type; ; Offset: 4 +; float input_type; ; Offset: 8 +; float color_scale; ; Offset: 12 +; float tonemap_method; ; Offset: 16 +; float tonemap_factor1; ; Offset: 20 +; float tonemap_factor2; ; Offset: 24 +; float sdr_white_point; ; Offset: 28 +; float4 Yoffset; ; Offset: 32 +; float4 Rcoeff; ; Offset: 48 +; float4 Gcoeff; ; Offset: 64 +; float4 Bcoeff; ; Offset: 80 +; +; } Constants; ; Offset: 0 Size: 96 +; +; } +; +; +; Resource Bindings: +; +; Name Type Format Dim ID HLSL Bind Count +; ------------------------------ ---------- ------- ----------- ------- -------------- ------ +; Constants cbuffer NA NA CB0 cb1 1 +; sampler0 sampler NA NA S0 s0 1 +; texture0 texture f32 2d T0 t0 1 +; texture1 texture f32 2d T1 t1 1 +; texture2 texture f32 2d T2 t2 1 +; +; +; ViewId state: +; +; Number of inputs: 12, outputs: 4 +; Outputs dependent on ViewId: { } +; Inputs contributing to computation of Outputs: +; output 0 depends on inputs: { 4, 5, 8 } +; output 1 depends on inputs: { 4, 5, 9 } +; output 2 depends on inputs: { 4, 5, 10 } +; output 3 depends on inputs: { 4, 5, 11 } +; +target datalayout = "e-m:e-p:32:32-i1:32-i8:32-i16:32-i32:32-i64:64-f16:32-f32:32-f64:64-n8:16:32:64" +target triple = "dxil-ms-dx" + +%dx.types.Handle = type { i8* } +%dx.types.CBufRet.f32 = type { float, float, float, float } +%dx.types.ResRet.f32 = type { float, float, float, float, i32 } +%"class.Texture2D >" = type { <4 x float>, %"class.Texture2D >::mips_type" } +%"class.Texture2D >::mips_type" = type { i32 } +%Constants = type { float, float, float, float, float, float, float, float, <4 x float>, <4 x float>, <4 x float>, <4 x float> } +%struct.SamplerState = type { i32 } + +define void @main() { + %1 = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 2, i32 2, i1 false) ; CreateHandle(resourceClass,rangeId,index,nonUniformIndex) + %2 = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 1, i32 1, i1 false) ; CreateHandle(resourceClass,rangeId,index,nonUniformIndex) + %3 = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 0, i32 0, i32 0, i1 false) ; CreateHandle(resourceClass,rangeId,index,nonUniformIndex) + %4 = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 3, i32 0, i32 0, i1 false) ; CreateHandle(resourceClass,rangeId,index,nonUniformIndex) + %5 = call %dx.types.Handle @dx.op.createHandle(i32 57, i8 2, i32 0, i32 1, i1 false) ; CreateHandle(resourceClass,rangeId,index,nonUniformIndex) + %6 = call float @dx.op.loadInput.f32(i32 4, i32 2, i32 0, i8 0, i32 undef) ; LoadInput(inputSigId,rowIndex,colIndex,gsVertexAxis) + %7 = call float @dx.op.loadInput.f32(i32 4, i32 2, i32 0, i8 1, i32 undef) ; LoadInput(inputSigId,rowIndex,colIndex,gsVertexAxis) + %8 = call float @dx.op.loadInput.f32(i32 4, i32 2, i32 0, i8 2, i32 undef) ; LoadInput(inputSigId,rowIndex,colIndex,gsVertexAxis) + %9 = call float @dx.op.loadInput.f32(i32 4, i32 2, i32 0, i8 3, i32 undef) ; LoadInput(inputSigId,rowIndex,colIndex,gsVertexAxis) + %10 = call float @dx.op.loadInput.f32(i32 4, i32 1, i32 0, i8 0, i32 undef) ; LoadInput(inputSigId,rowIndex,colIndex,gsVertexAxis) + %11 = call float @dx.op.loadInput.f32(i32 4, i32 1, i32 0, i8 1, i32 undef) ; LoadInput(inputSigId,rowIndex,colIndex,gsVertexAxis) + %12 = call %dx.types.CBufRet.f32 @dx.op.cbufferLoadLegacy.f32(i32 59, %dx.types.Handle %5, i32 0) ; CBufferLoadLegacy(handle,regIndex) + %13 = extractvalue %dx.types.CBufRet.f32 %12, 1 + %14 = fcmp fast oeq float %13, 0.000000e+00 + br i1 %14, label %114, label %15 + +; @@ -853,6 +856,9 @@ + + render\vulkan + @@ -1434,8 +1440,17 @@ + + render\vulkan + + + render\vulkan + - + + + + \ No newline at end of file diff --git a/include/build_config/SDL_build_config_windows.h b/include/build_config/SDL_build_config_windows.h index a8d2dc4fd49c0..55f4f7c53d3ed 100644 --- a/include/build_config/SDL_build_config_windows.h +++ b/include/build_config/SDL_build_config_windows.h @@ -305,6 +305,10 @@ typedef unsigned int uintptr_t; /* Enable Vulkan support */ #define SDL_VIDEO_VULKAN 1 +#ifndef SDL_VIDEO_RENDER_VULKAN +#define SDL_VIDEO_RENDER_VULKAN 1 +#endif + /* Enable system power support */ #define SDL_POWER_WINDOWS 1 diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 9fcba37aa9363..45808901ccbf1 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -31,13 +31,12 @@ #define VK_NO_PROTOTYPES -#include "SDL_vulkan.h" -#include "SDL_shaders_vulkan.h" -#include +#include "../../video/SDL_vulkan_internal.h" +#include "../../video/SDL_sysvideo.h" #include "../SDL_sysrender.h" -#include "../SDL_sysvideo.h" #include "../SDL_d3dmath.h" #include "../../video/SDL_pixels_c.h" +#include "SDL_shaders_vulkan.h" extern const char *SDL_Vulkan_GetResultString(VkResult result); @@ -526,7 +525,7 @@ static void VULKAN_DestroyAll(SDL_Renderer *renderer) rendererData->pipelineLayouts[i] = VK_NULL_HANDLE; } } - for (uint32_t i = 0; i < rendererData->pipelineStateCount; i++) { + for (int i = 0; i < rendererData->pipelineStateCount; i++) { vkDestroyPipeline(rendererData->device, rendererData->pipelineStates[i].pipeline, NULL); } SDL_free(rendererData->pipelineStates); @@ -534,7 +533,7 @@ static void VULKAN_DestroyAll(SDL_Renderer *renderer) if (rendererData->currentUploadBuffer) { for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) { - for (uint32_t j = 0; j < rendererData->currentUploadBuffer[i]; ++j) { + for (int j = 0; j < rendererData->currentUploadBuffer[i]; ++j) { VULKAN_DestroyBuffer(rendererData, &rendererData->uploadBuffers[i][j]); } SDL_free(rendererData->uploadBuffers[i]); @@ -914,7 +913,7 @@ static void VULKAN_ResetCommandList(VULKAN_RenderData *rendererData) rendererData->currentConstantBufferOffset = -1; /* Release any upload buffers that were inflight */ - for (uint32_t i = 0; i < rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex]; ++i) { + for (int i = 0; i < rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex]; ++i) { VULKAN_DestroyBuffer(rendererData, &rendererData->uploadBuffers[rendererData->currentCommandBufferIndex][i]); } rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex] = 0; @@ -1843,7 +1842,7 @@ static VkResult VULKAN_CreateFramebuffersAndRenderPasses(SDL_Renderer *renderer, framebufferCreateInfo.height = rendererData->swapchainSize.height; framebufferCreateInfo.layers = 1; - for (uint32_t i = 0; i < imageViewCount; i++) { + for (int i = 0; i < imageViewCount; i++) { framebufferCreateInfo.pAttachments = &imageViews[i]; result = vkCreateFramebuffer(rendererData->device, &framebufferCreateInfo, NULL, &framebuffers[i]); if (result != VK_SUCCESS) { @@ -3107,7 +3106,7 @@ static SDL_bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderComm /* Align the next address to the minUniformBufferOffsetAlignment */ VkDeviceSize alignment = rendererData->physicalDeviceProperties.limits.minUniformBufferOffsetAlignment; SDL_assert(rendererData->currentConstantBufferOffset >= 0 ); - rendererData->currentConstantBufferOffset += (sizeof(PixelShaderConstants) + alignment - 1) & ~(alignment - 1); + rendererData->currentConstantBufferOffset += (int32_t)(sizeof(PixelShaderConstants) + alignment - 1) & ~(alignment - 1); constantBufferOffset = rendererData->currentConstantBufferOffset; } @@ -3303,7 +3302,7 @@ static SDL_bool VULKAN_SetCopyState(SDL_Renderer *renderer, const SDL_RenderComm static void VULKAN_DrawPrimitives(SDL_Renderer *renderer, VkPrimitiveTopology primitiveTopology, const size_t vertexStart, const size_t vertexCount) { VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; - vkCmdDraw(rendererData->currentCommandBuffer, vertexCount, 1, vertexStart, 0); + vkCmdDraw(rendererData->currentCommandBuffer, (uint32_t)vertexCount, 1, (uint32_t)vertexStart, 0); } static void VULKAN_InvalidateCachedState(SDL_Renderer *renderer) From b1431e6702b941ec4404b8d20341a78674fde512 Mon Sep 17 00:00:00 2001 From: danginsburg Date: Fri, 23 Feb 2024 08:43:37 -0500 Subject: [PATCH 089/220] Vulkan Renderer - implement support for vsync disabled. Closes #9116. --- src/render/vulkan/SDL_render_vulkan.c | 41 ++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 45808901ccbf1..8771492a09d1b 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -1912,6 +1912,45 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) return VK_ERROR_OUT_OF_DATE_KHR; } + /* Choose a present mode. If vsync is requested, then use VK_PRESENT_MODE_FIFO_KHR which is guaranteed to be supported */ + VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; + if (!(renderer->info.flags & SDL_RENDERER_PRESENTVSYNC)) { + uint32_t presentModeCount = 0; + result = vkGetPhysicalDeviceSurfacePresentModesKHR(rendererData->physicalDevice, rendererData->surface, &presentModeCount, NULL); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkGetPhysicalDeviceSurfacePresentModesKHR(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + if (presentModeCount > 0) { + VkPresentModeKHR *presentModes = SDL_calloc(sizeof(VkPresentModeKHR), presentModeCount); + result = vkGetPhysicalDeviceSurfacePresentModesKHR(rendererData->physicalDevice, rendererData->surface, &presentModeCount, presentModes); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkGetPhysicalDeviceSurfacePresentModesKHR(): %s\n", SDL_Vulkan_GetResultString(result)); + SDL_free(presentModes); + return result; + } + + /* If vsync is not requested, in favor these options in order: + VK_PRESENT_MODE_IMMEDIATE_KHR - no v-sync with tearing + VK_PRESENT_MODE_MAILBOX_KHR - no v-sync without tearing + VK_PRESENT_MODE_FIFO_RELAXED_KHR - no v-sync, may tear */ + for (uint32_t i = 0; i < presentModeCount; i++) { + if (presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR) { + presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + break; + } + else if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { + presentMode = VK_PRESENT_MODE_MAILBOX_KHR; + } + else if ((presentMode != VK_PRESENT_MODE_MAILBOX_KHR) && + (presentModes[i] == VK_PRESENT_MODE_FIFO_RELAXED_KHR)) { + presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR; + } + } + SDL_free(presentModes); + } + } + VkSwapchainCreateInfoKHR swapchainCreateInfo = { 0 }; swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; @@ -1925,7 +1964,7 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; swapchainCreateInfo.preTransform = rendererData->surfaceCapabilities.currentTransform; swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - swapchainCreateInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; // TODO + swapchainCreateInfo.presentMode = presentMode; swapchainCreateInfo.clipped = VK_TRUE; swapchainCreateInfo.oldSwapchain = rendererData->swapchain; result = vkCreateSwapchainKHR(rendererData->device, &swapchainCreateInfo, NULL, &rendererData->swapchain); From 97372b56e8064117132d553b43d88cd563f8cb51 Mon Sep 17 00:00:00 2001 From: danginsburg Date: Fri, 23 Feb 2024 10:45:10 -0500 Subject: [PATCH 090/220] Vulkan Renderer - handle dynamic resetting of vsync, requires swapchain recreation. --- src/render/vulkan/SDL_render_vulkan.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 8771492a09d1b..faf72458fc1dc 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -287,7 +287,7 @@ typedef struct VkFence *fences; VkSurfaceCapabilitiesKHR surfaceCapabilities; VkSurfaceFormatKHR *surfaceFormats; - SDL_bool pixelSizeChanged; + SDL_bool recreateSwapchain; VkFramebuffer *framebuffers; VkRenderPass renderPasses[SDL_VULKAN_NUM_RENDERPASSES]; @@ -2225,7 +2225,7 @@ static VkResult VULKAN_CreateWindowSizeDependentResources(SDL_Renderer *renderer result = VULKAN_CreateSwapChain(renderer, w, h); if (result != VK_SUCCESS) { - rendererData->pixelSizeChanged = VK_TRUE; + rendererData->recreateSwapchain = VK_TRUE; } rendererData->viewportDirty = SDL_TRUE; @@ -2248,7 +2248,7 @@ static void VULKAN_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *ev VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; if (event->type == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED) { - rendererData->pixelSizeChanged = SDL_TRUE; + rendererData->recreateSwapchain = SDL_TRUE; } } @@ -3361,11 +3361,11 @@ static int VULKAN_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd VULKAN_DrawStateCache stateCache; SDL_memset(&stateCache, 0, sizeof(stateCache)); - if (rendererData->pixelSizeChanged) { + if (rendererData->recreateSwapchain) { if (VULKAN_UpdateForWindowSizeChange(renderer) != VK_SUCCESS) { return -1; } - rendererData->pixelSizeChanged = SDL_FALSE; + rendererData->recreateSwapchain = SDL_FALSE; } if (VULKAN_UpdateVertexBuffer(renderer, vertices, vertsize, &stateCache) < 0) { @@ -3663,11 +3663,17 @@ static int VULKAN_RenderPresent(SDL_Renderer *renderer) static int VULKAN_SetVSync(SDL_Renderer *renderer, const int vsync) { + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + + Uint32 prevFlags = renderer->info.flags; if (vsync) { renderer->info.flags |= SDL_RENDERER_PRESENTVSYNC; } else { renderer->info.flags &= ~SDL_RENDERER_PRESENTVSYNC; } + if (prevFlags != renderer->info.flags) { + rendererData->recreateSwapchain = SDL_TRUE; + } return 0; } From cbe330befd5a56c52753704507642b19998221e0 Mon Sep 17 00:00:00 2001 From: scribam Date: Fri, 23 Feb 2024 20:57:58 +0100 Subject: [PATCH 091/220] ci: bump cross-platform-actions/action version to v0.23.0 --- .github/workflows/cpactions.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cpactions.yml b/.github/workflows/cpactions.yml index d0324413f7c55..02ae6a420736e 100644 --- a/.github/workflows/cpactions.yml +++ b/.github/workflows/cpactions.yml @@ -28,7 +28,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Build - uses: cross-platform-actions/action@v0.21.1 + uses: cross-platform-actions/action@v0.23.0 with: operating_system: ${{ matrix.platform.os }} architecture: ${{ matrix.platform.os-arch }} From 38d24778ed528db361a991b5e449ec907b35711b Mon Sep 17 00:00:00 2001 From: scribam Date: Fri, 23 Feb 2024 21:20:30 +0100 Subject: [PATCH 092/220] ci: update deprecated node.js 16 actions --- .github/workflows/android.yml | 2 +- .github/workflows/emscripten.yml | 2 +- .github/workflows/loongarch64.yml | 2 +- .github/workflows/msvc.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 5c76a07e68032..08774c9bfc55d 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -29,7 +29,7 @@ jobs: if: ${{ matrix.platform.name == 'Android.mk' }} run: | ./build-scripts/androidbuildlibs.sh - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 if: ${{ matrix.platform.name == 'CMake' }} with: distribution: 'temurin' diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml index d7ebf6913a6bb..eedbb42bbc2a1 100644 --- a/.github/workflows/emscripten.yml +++ b/.github/workflows/emscripten.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: mymindstorm/setup-emsdk@v12 + - uses: mymindstorm/setup-emsdk@v14 with: version: 3.1.35 - name: Install ninja diff --git a/.github/workflows/loongarch64.yml b/.github/workflows/loongarch64.yml index 56d96c6c17f1b..ae46e3e13456b 100644 --- a/.github/workflows/loongarch64.yml +++ b/.github/workflows/loongarch64.yml @@ -21,7 +21,7 @@ jobs: sudo apt-get update -y sudo apt-get install -y --no-install-recommends cmake ninja-build pkg-config tar wget - - uses: actions/cache/restore@v3 + - uses: actions/cache/restore@v4 id: restore-cache with: path: /opt/cross-tools diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index 110aafbd22b29..fa2b405f23f12 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -88,7 +88,7 @@ jobs: - name: Add msbuild to PATH if: ${{ matrix.platform.project != '' }} - uses: microsoft/setup-msbuild@v1.1.3 + uses: microsoft/setup-msbuild@v2 - name: Build msbuild if: ${{ matrix.platform.project != '' }} run: msbuild ${{ matrix.platform.project }} /m /p:BuildInParallel=true /p:Configuration=Release ${{ matrix.platform.projectflags }} From 5593ddb6a75515765182a499f338df859954bc88 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Sat, 24 Feb 2024 17:31:48 +0100 Subject: [PATCH 093/220] cmake: X11 is for Video, not Audio --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 566879bb2e78c..f504f933f8347 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -314,7 +314,7 @@ dep_option(SDL_SNDIO "Support the sndio audio API" ${UNIX_SYS} "SD dep_option(SDL_SNDIO_SHARED "Dynamically load the sndio audio API" ON "SDL_SNDIO" OFF) set_option(SDL_RPATH "Use an rpath when linking SDL" ${UNIX_SYS}) set_option(SDL_CLOCK_GETTIME "Use clock_gettime() instead of gettimeofday()" ${SDL_CLOCK_GETTIME_DEFAULT}) -dep_option(SDL_X11 "Use X11 video driver" ${UNIX_SYS} "SDL_AUDIO" OFF) +dep_option(SDL_X11 "Use X11 video driver" ${UNIX_SYS} "SDL_VIDEO" OFF) dep_option(SDL_X11_SHARED "Dynamically load X11 support" ON "SDL_X11" OFF) set(SDL_X11_OPTIONS Xcursor Xdbe XInput Xfixes Xrandr Xscrnsaver XShape) foreach(_SUB ${SDL_X11_OPTIONS}) From 2b0e7c40ef7d4cfb729d1a19d526219ac0ca1136 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 24 Feb 2024 19:55:10 -0800 Subject: [PATCH 094/220] Verify that we can create pipeline state objects for the D3D12 renderer Fixes https://github.com/libsdl-org/SDL/issues/9093 --- src/render/direct3d12/SDL_render_d3d12.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index 43f7a8c360d84..212916e6f9992 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -1080,32 +1080,23 @@ static HRESULT D3D12_CreateDeviceResources(SDL_Renderer *renderer) } } -#if 0 /* Actually, don't do this, it causes a huge startup time on some systems */ { const SDL_BlendMode defaultBlendModes[] = { - SDL_BLENDMODE_NONE, SDL_BLENDMODE_BLEND, - SDL_BLENDMODE_ADD, - SDL_BLENDMODE_MOD, - SDL_BLENDMODE_MUL }; const DXGI_FORMAT defaultRTVFormats[] = { - DXGI_FORMAT_R16G16B16A16_FLOAT, - DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, - DXGI_FORMAT_B8G8R8X8_UNORM, - DXGI_FORMAT_R8_UNORM }; int i, j, k, l; - /* Create all the default pipeline state objects - (will add everything except custom blend states) */ + /* Create a few default pipeline state objects, to verify that this renderer will work */ for (i = 0; i < NUM_SHADERS; ++i) { for (j = 0; j < SDL_arraysize(defaultBlendModes); ++j) { for (k = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; k < D3D12_PRIMITIVE_TOPOLOGY_TYPE_PATCH; ++k) { for (l = 0; l < SDL_arraysize(defaultRTVFormats); ++l) { if (!D3D12_CreatePipelineState(renderer, (D3D12_Shader)i, defaultBlendModes[j], (D3D12_PRIMITIVE_TOPOLOGY_TYPE)k, defaultRTVFormats[l])) { /* D3D12_CreatePipelineState will set the SDL error, if it fails */ + result = E_FAIL; goto done; } } @@ -1113,7 +1104,6 @@ static HRESULT D3D12_CreateDeviceResources(SDL_Renderer *renderer) } } } -#endif /* 0 */ /* Create default vertex buffers */ for (i = 0; i < SDL_D3D12_NUM_VERTEX_BUFFERS; ++i) { From 4c5584174b71ac39760485301ce042778ec3333c Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 24 Feb 2024 20:04:07 -0800 Subject: [PATCH 095/220] Fixed error: declaration shadows a local variable [-Werror,-Wshadow] --- src/render/direct3d12/SDL_render_d3d12.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index 212916e6f9992..98c815e2113e3 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -1087,7 +1087,7 @@ static HRESULT D3D12_CreateDeviceResources(SDL_Renderer *renderer) const DXGI_FORMAT defaultRTVFormats[] = { DXGI_FORMAT_B8G8R8A8_UNORM, }; - int i, j, k, l; + int j, k, l; /* Create a few default pipeline state objects, to verify that this renderer will work */ for (i = 0; i < NUM_SHADERS; ++i) { From f6c42406cd18c83d222c4263868b2caeab655b8f Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 24 Feb 2024 20:02:11 -0800 Subject: [PATCH 096/220] SDL_COLORSPACE_HDR10 is the default colorspace for SDL_PIXELFORMAT_P010 surfaces --- src/video/SDL_pixels.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/video/SDL_pixels.c b/src/video/SDL_pixels.c index 297255d57f160..64b40abae0c02 100644 --- a/src/video/SDL_pixels.c +++ b/src/video/SDL_pixels.c @@ -704,7 +704,11 @@ void SDL_DestroyPixelFormat(SDL_PixelFormat *format) SDL_Colorspace SDL_GetDefaultColorspaceForFormat(Uint32 format) { if (SDL_ISPIXELFORMAT_FOURCC(format)) { - return SDL_COLORSPACE_YUV_DEFAULT; + if (format == SDL_PIXELFORMAT_P010) { + return SDL_COLORSPACE_HDR10; + } else { + return SDL_COLORSPACE_YUV_DEFAULT; + } } else if (SDL_ISPIXELFORMAT_FLOAT(format)) { return SDL_COLORSPACE_SRGB_LINEAR; } else if (SDL_ISPIXELFORMAT_10BIT(format)) { From a1ea706215efbefa530b880696756ef39f650762 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 24 Feb 2024 20:13:59 -0800 Subject: [PATCH 097/220] Added names for the newly supported pixel formats --- src/test/SDL_test_common.c | 115 +++---------------------------------- src/video/SDL_pixels.c | 20 +++++++ 2 files changed, 27 insertions(+), 108 deletions(-) diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index 40c59e85023c4..229862afd36ab 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -972,115 +972,14 @@ static void SDLTest_PrintRendererFlag(char *text, size_t maxlen, Uint32 flag) static void SDLTest_PrintPixelFormat(char *text, size_t maxlen, Uint32 format) { - switch (format) { - case SDL_PIXELFORMAT_UNKNOWN: - SDL_snprintfcat(text, maxlen, "Unknown"); - break; - case SDL_PIXELFORMAT_INDEX1LSB: - SDL_snprintfcat(text, maxlen, "Index1LSB"); - break; - case SDL_PIXELFORMAT_INDEX1MSB: - SDL_snprintfcat(text, maxlen, "Index1MSB"); - break; - case SDL_PIXELFORMAT_INDEX2LSB: - SDL_snprintfcat(text, maxlen, "Index2LSB"); - break; - case SDL_PIXELFORMAT_INDEX2MSB: - SDL_snprintfcat(text, maxlen, "Index2MSB"); - break; - case SDL_PIXELFORMAT_INDEX4LSB: - SDL_snprintfcat(text, maxlen, "Index4LSB"); - break; - case SDL_PIXELFORMAT_INDEX4MSB: - SDL_snprintfcat(text, maxlen, "Index4MSB"); - break; - case SDL_PIXELFORMAT_INDEX8: - SDL_snprintfcat(text, maxlen, "Index8"); - break; - case SDL_PIXELFORMAT_RGB332: - SDL_snprintfcat(text, maxlen, "RGB332"); - break; - case SDL_PIXELFORMAT_RGB444: - SDL_snprintfcat(text, maxlen, "RGB444"); - break; - case SDL_PIXELFORMAT_BGR444: - SDL_snprintfcat(text, maxlen, "BGR444"); - break; - case SDL_PIXELFORMAT_RGB555: - SDL_snprintfcat(text, maxlen, "RGB555"); - break; - case SDL_PIXELFORMAT_BGR555: - SDL_snprintfcat(text, maxlen, "BGR555"); - break; - case SDL_PIXELFORMAT_ARGB4444: - SDL_snprintfcat(text, maxlen, "ARGB4444"); - break; - case SDL_PIXELFORMAT_ABGR4444: - SDL_snprintfcat(text, maxlen, "ABGR4444"); - break; - case SDL_PIXELFORMAT_ARGB1555: - SDL_snprintfcat(text, maxlen, "ARGB1555"); - break; - case SDL_PIXELFORMAT_ABGR1555: - SDL_snprintfcat(text, maxlen, "ABGR1555"); - break; - case SDL_PIXELFORMAT_RGB565: - SDL_snprintfcat(text, maxlen, "RGB565"); - break; - case SDL_PIXELFORMAT_BGR565: - SDL_snprintfcat(text, maxlen, "BGR565"); - break; - case SDL_PIXELFORMAT_RGB24: - SDL_snprintfcat(text, maxlen, "RGB24"); - break; - case SDL_PIXELFORMAT_BGR24: - SDL_snprintfcat(text, maxlen, "BGR24"); - break; - case SDL_PIXELFORMAT_XRGB8888: - SDL_snprintfcat(text, maxlen, "XRGB8888"); - break; - case SDL_PIXELFORMAT_XBGR8888: - SDL_snprintfcat(text, maxlen, "XBGR8888"); - break; - case SDL_PIXELFORMAT_ARGB8888: - SDL_snprintfcat(text, maxlen, "ARGB8888"); - break; - case SDL_PIXELFORMAT_RGBA8888: - SDL_snprintfcat(text, maxlen, "RGBA8888"); - break; - case SDL_PIXELFORMAT_ABGR8888: - SDL_snprintfcat(text, maxlen, "ABGR8888"); - break; - case SDL_PIXELFORMAT_BGRA8888: - SDL_snprintfcat(text, maxlen, "BGRA8888"); - break; - case SDL_PIXELFORMAT_ARGB2101010: - SDL_snprintfcat(text, maxlen, "ARGB2101010"); - break; - case SDL_PIXELFORMAT_YV12: - SDL_snprintfcat(text, maxlen, "YV12"); - break; - case SDL_PIXELFORMAT_IYUV: - SDL_snprintfcat(text, maxlen, "IYUV"); - break; - case SDL_PIXELFORMAT_YUY2: - SDL_snprintfcat(text, maxlen, "YUY2"); - break; - case SDL_PIXELFORMAT_UYVY: - SDL_snprintfcat(text, maxlen, "UYVY"); - break; - case SDL_PIXELFORMAT_YVYU: - SDL_snprintfcat(text, maxlen, "YVYU"); - break; - case SDL_PIXELFORMAT_NV12: - SDL_snprintfcat(text, maxlen, "NV12"); - break; - case SDL_PIXELFORMAT_NV21: - SDL_snprintfcat(text, maxlen, "NV21"); - break; - default: + const char *name = SDL_GetPixelFormatName(format); + if (name) { + if (SDL_strncmp(name, "SDL_PIXELFORMAT_", 16) == 0) { + name += 16; + } + SDL_snprintfcat(text, maxlen, name); + } else { SDL_snprintfcat(text, maxlen, "0x%8.8x", format); - break; } } diff --git a/src/video/SDL_pixels.c b/src/video/SDL_pixels.c index 64b40abae0c02..58d2064d1a112 100644 --- a/src/video/SDL_pixels.c +++ b/src/video/SDL_pixels.c @@ -123,6 +123,24 @@ const char *SDL_GetPixelFormatName(Uint32 format) CASE(SDL_PIXELFORMAT_XBGR2101010) CASE(SDL_PIXELFORMAT_ARGB2101010) CASE(SDL_PIXELFORMAT_ABGR2101010) + CASE(SDL_PIXELFORMAT_RGB48) + CASE(SDL_PIXELFORMAT_BGR48) + CASE(SDL_PIXELFORMAT_RGBA64) + CASE(SDL_PIXELFORMAT_ARGB64) + CASE(SDL_PIXELFORMAT_BGRA64) + CASE(SDL_PIXELFORMAT_ABGR64) + CASE(SDL_PIXELFORMAT_RGB48_FLOAT) + CASE(SDL_PIXELFORMAT_BGR48_FLOAT) + CASE(SDL_PIXELFORMAT_RGBA64_FLOAT) + CASE(SDL_PIXELFORMAT_ARGB64_FLOAT) + CASE(SDL_PIXELFORMAT_BGRA64_FLOAT) + CASE(SDL_PIXELFORMAT_ABGR64_FLOAT) + CASE(SDL_PIXELFORMAT_RGB96_FLOAT) + CASE(SDL_PIXELFORMAT_BGR96_FLOAT) + CASE(SDL_PIXELFORMAT_RGBA128_FLOAT) + CASE(SDL_PIXELFORMAT_ARGB128_FLOAT) + CASE(SDL_PIXELFORMAT_BGRA128_FLOAT) + CASE(SDL_PIXELFORMAT_ABGR128_FLOAT) CASE(SDL_PIXELFORMAT_YV12) CASE(SDL_PIXELFORMAT_IYUV) CASE(SDL_PIXELFORMAT_YUY2) @@ -130,6 +148,8 @@ const char *SDL_GetPixelFormatName(Uint32 format) CASE(SDL_PIXELFORMAT_YVYU) CASE(SDL_PIXELFORMAT_NV12) CASE(SDL_PIXELFORMAT_NV21) + CASE(SDL_PIXELFORMAT_P010) + CASE(SDL_PIXELFORMAT_P016) CASE(SDL_PIXELFORMAT_EXTERNAL_OES) default: From 276566235c4b2079c16ff52ab55b66ee5ec3a9b9 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 24 Feb 2024 20:29:39 -0800 Subject: [PATCH 098/220] Removed SDL_ClearHints() from the public API Fixes https://github.com/libsdl-org/SDL/issues/9129 --- docs/README-migration.md | 3 +++ include/SDL3/SDL_hints.h | 17 ----------------- src/SDL.c | 1 + src/SDL_hints_c.h | 1 + src/dynapi/SDL_dynapi.sym | 1 - src/dynapi/SDL_dynapi_overrides.h | 1 - src/dynapi/SDL_dynapi_procs.h | 1 - 7 files changed, 5 insertions(+), 20 deletions(-) diff --git a/docs/README-migration.md b/docs/README-migration.md index 5f8555a31bd4c..f5b00db1015a2 100644 --- a/docs/README-migration.md +++ b/docs/README-migration.md @@ -740,6 +740,9 @@ The following hints have been renamed: * SDL_HINT_LINUX_JOYSTICK_DEADZONES => SDL_HINT_JOYSTICK_LINUX_DEADZONES * SDL_HINT_PS2_DYNAMIC_VSYNC => SDL_HINT_RENDER_PS2_DYNAMIC_VSYNC +The following functions have been removed: +* SDL_ClearHints() - replaced with SDL_ResetHints() + ## SDL_init.h The following symbols have been renamed: diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index dd43c3ed11730..80d4ff3cbc3b9 100644 --- a/include/SDL3/SDL_hints.h +++ b/include/SDL3/SDL_hints.h @@ -2661,23 +2661,6 @@ extern DECLSPEC void SDLCALL SDL_DelHintCallback(const char *name, SDL_HintCallback callback, void *userdata); -/** - * Clear all hints. - * - * This function is automatically called during SDL_Quit(), and deletes all - * callbacks without calling them and frees all memory associated with hints. - * If you're calling this from application code you probably want to call - * SDL_ResetHints() instead. - * - * This function will be removed from the API the next time we rev the ABI. - * - * \since This function is available since SDL 3.0.0. - * - * \sa SDL_ResetHints - */ -extern DECLSPEC void SDLCALL SDL_ClearHints(void); - - /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/src/SDL.c b/src/SDL.c index 1553f80bcfbff..3653552a12463 100644 --- a/src/SDL.c +++ b/src/SDL.c @@ -37,6 +37,7 @@ /* Initialization code for SDL */ #include "SDL_assert_c.h" +#include "SDL_hints_c.h" #include "SDL_log_c.h" #include "SDL_properties_c.h" #include "audio/SDL_sysaudio.h" diff --git a/src/SDL_hints_c.h b/src/SDL_hints_c.h index 68cceac7f36d5..486280ec6b2d8 100644 --- a/src/SDL_hints_c.h +++ b/src/SDL_hints_c.h @@ -27,5 +27,6 @@ extern SDL_bool SDL_GetStringBoolean(const char *value, SDL_bool default_value); extern int SDL_GetStringInteger(const char *value, int default_value); +extern void SDL_ClearHints(void); #endif /* SDL_hints_c_h_ */ diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index 93d0f32ebbc1a..e91067cfdc75a 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -37,7 +37,6 @@ SDL3_0.0.0 { SDL_CleanupTLS; SDL_ClearComposition; SDL_ClearError; - SDL_ClearHints; SDL_CloseGamepad; SDL_CloseJoystick; SDL_CloseSensor; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index c116c68f6135f..e786e75aa0c8f 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -61,7 +61,6 @@ #define SDL_CleanupTLS SDL_CleanupTLS_REAL #define SDL_ClearComposition SDL_ClearComposition_REAL #define SDL_ClearError SDL_ClearError_REAL -#define SDL_ClearHints SDL_ClearHints_REAL #define SDL_CloseGamepad SDL_CloseGamepad_REAL #define SDL_CloseJoystick SDL_CloseJoystick_REAL #define SDL_CloseSensor SDL_CloseSensor_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index b98ba5e406942..98e2ffc2a3f0d 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -127,7 +127,6 @@ SDL_DYNAPI_PROC(int,SDL_CaptureMouse,(SDL_bool a),(a),return) SDL_DYNAPI_PROC(void,SDL_CleanupTLS,(void),(),) SDL_DYNAPI_PROC(void,SDL_ClearComposition,(void),(),) SDL_DYNAPI_PROC(void,SDL_ClearError,(void),(),) -SDL_DYNAPI_PROC(void,SDL_ClearHints,(void),(),) SDL_DYNAPI_PROC(void,SDL_CloseGamepad,(SDL_Gamepad *a),(a),) SDL_DYNAPI_PROC(void,SDL_CloseJoystick,(SDL_Joystick *a),(a),) SDL_DYNAPI_PROC(void,SDL_CloseSensor,(SDL_Sensor *a),(a),) From b8a52c12377428f8743f503fbdc80a032a67391e Mon Sep 17 00:00:00 2001 From: David Gow Date: Sun, 25 Feb 2024 16:35:34 +0800 Subject: [PATCH 099/220] Vulkan: Make sure validation layer name is in-scope When enabling the Vulkan validation layers, the 'validationLayerName' variable technically went out of scope before vkCreateInstance() was called. While most compilers won't clean up stack variables after random 'if' statements, some will, particularly when optimisation or memory sanitizers are enabled. This can lead to vkCreateInstance() segfaulting when SDL_HINT_RENDER_VULKAN_DEBUG is enabled. Instead, make the validationLayerName visible throughout the entire VULKAN_CreateDeviceResources() function. While we're at it, extract the validation layer name out into a preprocessor #define, so that we are definitely using the same name in VULKAN_ValidationLayersFound(). Signed-off-by: David Gow --- src/render/vulkan/SDL_render_vulkan.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index faf72458fc1dc..603e01943e853 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -29,6 +29,7 @@ #define SDL_VULKAN_NUM_UPLOAD_BUFFERS 32 #define SDL_VULKAN_MAX_DESCRIPTOR_SETS 4096 +#define SDL_VULKAN_VALIDATION_LAYER_NAME "VK_LAYER_KHRONOS_validation" #define VK_NO_PROTOTYPES #include "../../video/SDL_vulkan_internal.h" @@ -1478,7 +1479,6 @@ static SDL_bool VULKAN_InstanceExtensionFound(VULKAN_RenderData *rendererData, c static SDL_bool VULKAN_ValidationLayersFound() { - const char *validationLayerName = "VK_LAYER_KHRONOS_validation"; uint32_t instanceLayerCount = 0; uint32_t i; SDL_bool foundValidation = SDL_FALSE; @@ -1488,7 +1488,7 @@ static SDL_bool VULKAN_ValidationLayersFound() VkLayerProperties *instanceLayers = SDL_calloc(instanceLayerCount, sizeof(VkLayerProperties)); vkEnumerateInstanceLayerProperties(&instanceLayerCount, instanceLayers); for (i = 0; i < instanceLayerCount; i++) { - if (!SDL_strcmp(validationLayerName, instanceLayers[i].layerName)) { + if (!SDL_strcmp(SDL_VULKAN_VALIDATION_LAYER_NAME, instanceLayers[i].layerName)) { foundValidation = SDL_TRUE; break; } @@ -1507,6 +1507,7 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer) VkResult result = VK_SUCCESS; PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = NULL; SDL_bool createDebug = SDL_GetHintBoolean(SDL_HINT_RENDER_VULKAN_DEBUG, SDL_FALSE); + const char *validationLayerName[] = { SDL_VULKAN_VALIDATION_LAYER_NAME }; if (SDL_Vulkan_LoadLibrary(NULL) < 0) { SDL_LogDebug(SDL_LOG_CATEGORY_RENDER, "SDL_Vulkan_LoadLibrary failed." ); @@ -1551,7 +1552,6 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer) } instanceCreateInfo.ppEnabledExtensionNames = (const char* const*) instanceExtensionsCopy; if (createDebug && VULKAN_ValidationLayersFound()) { - const char *validationLayerName[] = { "VK_LAYER_KHRONOS_validation" }; instanceCreateInfo.ppEnabledLayerNames = validationLayerName; instanceCreateInfo.enabledLayerCount = 1; } From 1cae52bbacc9a9d2bdd7fd13d7cd545cf3729378 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 25 Feb 2024 08:40:47 -0800 Subject: [PATCH 100/220] Added JNI native methods to proguard-rules.pro (thanks @AntTheAlchemist!) Fixes https://github.com/libsdl-org/SDL/issues/3563 --- android-project/app/proguard-rules.pro | 68 ++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/android-project/app/proguard-rules.pro b/android-project/app/proguard-rules.pro index eaf0e916cdf0b..0cb79dc8e592b 100644 --- a/android-project/app/proguard-rules.pro +++ b/android-project/app/proguard-rules.pro @@ -15,3 +15,71 @@ #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} + +-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLActivity { + void manualBackButton(); + boolean setActivityTitle(java.lang.String); + void setWindowStyle(boolean); + void setOrientation(int, int, boolean, java.lang.String); + void minimizeWindow(); + boolean shouldMinimizeOnFocusLoss(); + boolean isScreenKeyboardShown(); + boolean supportsRelativeMouse(); + boolean setRelativeMouseEnabled(boolean); + boolean sendMessage(int, int); + android.content.Context getContext(); + boolean isAndroidTV(); + boolean isTablet(); + boolean isChromebook(); + boolean isDeXMode(); + boolean getManifestEnvironmentVariables(); + boolean showTextInput(int, int, int, int); + android.view.Surface getNativeSurface(); + void initTouch(); + int messageboxShowMessageBox(int, java.lang.String, java.lang.String, int[], int[], java.lang.String[], int[]); + boolean clipboardHasText(); + java.lang.String clipboardGetText(); + void clipboardSetText(java.lang.String); + int createCustomCursor(int[], int, int, int, int); + void destroyCustomCursor(int); + boolean setCustomCursor(int); + boolean setSystemCursor(int); + void requestPermission(java.lang.String, int); + int openURL(java.lang.String); + int showToast(java.lang.String, int, int, int, int); + native java.lang.String nativeGetHint(java.lang.String); +} + +-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.HIDDeviceManager { + boolean initialize(boolean, boolean); + boolean openDevice(int); + int writeReport(int, byte[], boolean); + boolean readReport(int, byte[], boolean); + void closeDevice(int); +} + +-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLAudioManager { + void registerAudioDeviceCallback(); + void unregisterAudioDeviceCallback(); + int[] audioOpen(int, int, int, int, int); + void audioWriteFloatBuffer(float[]); + void audioWriteShortBuffer(short[]); + void audioWriteByteBuffer(byte[]); + int[] captureOpen(int, int, int, int, int); + int captureReadFloatBuffer(float[], boolean); + int captureReadShortBuffer(short[], boolean); + int captureReadByteBuffer(byte[], boolean); + void audioClose(); + void captureClose(); + void audioSetThreadPriority(boolean, int); + int nativeSetupJNI(); + void removeAudioDevice(boolean, int); + void addAudioDevice(boolean, java.lang.String, int); +} + +-keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLControllerManager { + void pollInputDevices(); + void pollHapticDevices(); + void hapticRun(int, float, int); + void hapticStop(int); +} From d0af01e7d4f05379e8e9c60ca33bc74f432a4967 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 25 Feb 2024 09:37:56 -0800 Subject: [PATCH 101/220] If the viewport changes the cliprect should be updated The clip rectangle is defined to be viewport relative, so if the viewport changes we need to update it. Fixes https://github.com/libsdl-org/SDL/issues/9094 --- src/render/direct3d/SDL_render_d3d.c | 1 + src/render/direct3d11/SDL_render_d3d11.c | 1 + src/render/direct3d12/SDL_render_d3d12.c | 1 + src/render/opengl/SDL_render_gl.c | 1 + src/render/opengles2/SDL_render_gles2.c | 1 + src/render/ps2/SDL_render_ps2.c | 1 + src/render/psp/SDL_render_psp.c | 1 + src/render/vitagxm/SDL_render_vita_gxm.c | 1 + src/render/vulkan/SDL_render_vulkan.c | 47 ++++++++++++------------ 9 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index 871538269f25b..1d83139782e14 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -1187,6 +1187,7 @@ static int D3D_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, v if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) { SDL_copyp(viewport, &cmd->data.viewport.rect); data->drawstate.viewport_dirty = SDL_TRUE; + data->drawstate.cliprect_dirty = SDL_TRUE; } break; } diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index 7ed0eff9fc451..2d6841fae8e10 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -2479,6 +2479,7 @@ static int D3D11_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) { SDL_copyp(viewport, &cmd->data.viewport.rect); rendererData->viewportDirty = SDL_TRUE; + rendererData->cliprectDirty = SDL_TRUE; } break; } diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index 98c815e2113e3..470293a1bf7a9 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -2823,6 +2823,7 @@ static int D3D12_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) { SDL_copyp(viewport, &cmd->data.viewport.rect); rendererData->viewportDirty = SDL_TRUE; + rendererData->cliprectDirty = SDL_TRUE; } break; } diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index 30be60e0eab8e..cb29bfce0f239 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -1257,6 +1257,7 @@ static int GL_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, vo if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) { SDL_copyp(viewport, &cmd->data.viewport.rect); data->drawstate.viewport_dirty = SDL_TRUE; + data->drawstate.cliprect_dirty = SDL_TRUE; } break; } diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index 9bbd05d0d915b..e0bff04b0762b 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -1244,6 +1244,7 @@ static int GLES2_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) { SDL_copyp(viewport, &cmd->data.viewport.rect); data->drawstate.viewport_dirty = SDL_TRUE; + data->drawstate.cliprect_dirty = SDL_TRUE; } break; } diff --git a/src/render/ps2/SDL_render_ps2.c b/src/render/ps2/SDL_render_ps2.c index 3af81d173b0a3..6cb0a0ed96ea7 100644 --- a/src/render/ps2/SDL_render_ps2.c +++ b/src/render/ps2/SDL_render_ps2.c @@ -492,6 +492,7 @@ static int PS2_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, v case SDL_RENDERCMD_SETVIEWPORT: { PS2_RenderSetViewPort(renderer, cmd); + /* FIXME: We need to update the clip rect too, see https://github.com/libsdl-org/SDL/issues/9094 */ break; } case SDL_RENDERCMD_SETCLIPRECT: diff --git a/src/render/psp/SDL_render_psp.c b/src/render/psp/SDL_render_psp.c index 1d7e60b71467e..ec4ae3684c449 100644 --- a/src/render/psp/SDL_render_psp.c +++ b/src/render/psp/SDL_render_psp.c @@ -1078,6 +1078,7 @@ static int PSP_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, v sceGuOffset(2048 - (viewport->w >> 1), 2048 - (viewport->h >> 1)); sceGuViewport(2048, 2048, viewport->w, viewport->h); sceGuScissor(viewport->x, viewport->y, viewport->w, viewport->h); + /* FIXME: We need to update the clip rect too, see https://github.com/libsdl-org/SDL/issues/9094 */ break; } diff --git a/src/render/vitagxm/SDL_render_vita_gxm.c b/src/render/vitagxm/SDL_render_vita_gxm.c index 959e9ea5cb34e..ea93cb1f39ba6 100644 --- a/src/render/vitagxm/SDL_render_vita_gxm.c +++ b/src/render/vitagxm/SDL_render_vita_gxm.c @@ -981,6 +981,7 @@ static int VITA_GXM_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *c if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) { SDL_copyp(viewport, &cmd->data.viewport.rect); data->drawstate.viewport_dirty = SDL_TRUE; + data->drawstate.cliprect_dirty = SDL_TRUE; } break; } diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 603e01943e853..368381a28cf5c 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -211,7 +211,7 @@ typedef struct } VULKAN_Buffer; /* Vulkan image */ -typedef struct +typedef struct { SDL_bool allocatedImage; VkImage image; @@ -332,7 +332,7 @@ typedef struct VkSemaphore imageAvailableSemaphore; VkSemaphore renderingFinishedSemaphore; uint32_t currentSwapchainImageIndex; - + /* Cached renderer properties */ VULKAN_TextureData *textureRenderTarget; SDL_bool cliprectDirty; @@ -421,7 +421,7 @@ static void VULKAN_DestroyAll(SDL_Renderer *renderer) if (rendererData == NULL) { return; } - + if (rendererData->surfaceFormats != NULL) { SDL_free(rendererData->surfaceFormats); rendererData->surfaceFormats = NULL; @@ -550,7 +550,7 @@ static void VULKAN_DestroyAll(SDL_Renderer *renderer) SDL_free(rendererData->constantBuffers); rendererData->constantBuffers = NULL; } - + if (rendererData->device != VK_NULL_HANDLE) { vkDestroyDevice(rendererData->device, NULL); rendererData->device = VK_NULL_HANDLE; @@ -1192,7 +1192,7 @@ static VkResult VULKAN_CreateVertexBuffer(VULKAN_RenderData *rendererData, size_ VkResult result; VULKAN_DestroyBuffer(rendererData, &rendererData->vertexBuffers[vbidx]); - + result = VULKAN_AllocateBuffer(rendererData, size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | @@ -1430,7 +1430,7 @@ static VkResult VULKAN_GetSurfaceFormats(VULKAN_RenderData *rendererData) SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkGetPhysicalDeviceSurfaceFormatsKHR(): %s\n", SDL_Vulkan_GetResultString(result)); return result; } - + return VK_SUCCESS; } @@ -1482,7 +1482,7 @@ static SDL_bool VULKAN_ValidationLayersFound() uint32_t instanceLayerCount = 0; uint32_t i; SDL_bool foundValidation = SDL_FALSE; - + vkEnumerateInstanceLayerProperties(&instanceLayerCount, NULL); if (instanceLayerCount > 0) { VkLayerProperties *instanceLayers = SDL_calloc(instanceLayerCount, sizeof(VkLayerProperties)); @@ -1495,7 +1495,7 @@ static SDL_bool VULKAN_ValidationLayersFound() } SDL_free(instanceLayers); } - + return foundValidation; } @@ -1614,7 +1614,7 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer) VULKAN_DestroyAll(renderer); return VK_ERROR_UNKNOWN; } - + /* Get graphics/present queues */ vkGetDeviceQueue(rendererData->device, rendererData->graphicsQueueFamilyIndex, 0, &rendererData->graphicsQueue); if (rendererData->graphicsQueueFamilyIndex != rendererData->presentQueueFamilyIndex) { @@ -1622,7 +1622,7 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer) } else { rendererData->presentQueue = rendererData->graphicsQueue; } - + /* Create command pool/command buffers */ VkCommandPoolCreateInfo commandPoolCreateInfo = { 0 }; commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; @@ -1634,7 +1634,7 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer) SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateCommandPool(): %s\n", SDL_Vulkan_GetResultString(result)); return result; } - + if (VULKAN_GetSurfaceFormats(rendererData) != VK_SUCCESS) { VULKAN_DestroyAll(renderer); return result; @@ -1862,7 +1862,7 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR(): %s\n", SDL_Vulkan_GetResultString(result)); return result; } - + // pick an image count rendererData->swapchainDesiredImageCount = rendererData->surfaceCapabilities.minImageCount + SDL_VULKAN_FRAME_QUEUE_DEPTH; if ((rendererData->swapchainDesiredImageCount > rendererData->surfaceCapabilities.maxImageCount) && @@ -1880,7 +1880,7 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) desiredFormat = VK_FORMAT_A2B10G10R10_UNORM_PACK32; desiredColorSpace = VK_COLOR_SPACE_HDR10_ST2084_EXT; } - + if ((rendererData->surfaceFormatsCount == 1) && (rendererData->surfaceFormats[0].format == VK_FORMAT_UNDEFINED)) { // aren't any preferred formats, so we pick @@ -2036,7 +2036,7 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) } rendererData->swapchainImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED; } - + } VkCommandBufferAllocateInfo commandBufferAllocateInfo = { 0 }; @@ -2203,7 +2203,7 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) rendererData->currentConstantBufferOffset = -1; VULKAN_AcquireNextSwapchainImage(renderer); - + return result; } @@ -2307,7 +2307,7 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD textureData->shader = SHADER_ADVANCED; } textureData->scaleMode = (texture->scaleMode == SDL_SCALEMODE_NEAREST) ? VK_FILTER_NEAREST : VK_FILTER_LINEAR; - + /* NV12 textures must have even width and height */ if (texture->format == SDL_PIXELFORMAT_NV12 || texture->format == SDL_PIXELFORMAT_NV21 || @@ -2333,7 +2333,7 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_AllocateImage(): %s\n", SDL_Vulkan_GetResultString(result)); return result; } - + SDL_SetProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_VULKAN_TEXTURE_POINTER, &textureData->mainImage.image); @@ -2449,7 +2449,7 @@ static void VULKAN_DestroyTexture(SDL_Renderer *renderer, VULKAN_WaitForGPU(rendererData); VULKAN_DestroyImage(rendererData, &textureData->mainImage); - + #if SDL_HAVE_YUV VULKAN_DestroyImage(rendererData, &textureData->mainImageU); VULKAN_DestroyImage(rendererData, &textureData->mainImageV); @@ -2535,7 +2535,7 @@ static VkResult VULKAN_UpdateTextureInternal(VULKAN_RenderData *rendererData, Vk region.imageExtent.width = w; region.imageExtent.height = h; region.imageExtent.depth = 1; - + vkCmdCopyBufferToImage(rendererData->currentCommandBuffer, uploadBuffer->buffer, image, *imageLayout, 1, ®ion); /* Transition the texture to be shader accessible */ @@ -2688,7 +2688,7 @@ static int VULKAN_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, if (!textureData) { return SDL_SetError("Texture is not currently available"); } - + if (textureData->stagingBuffer.buffer != VK_NULL_HANDLE) { return SDL_SetError("texture is already locked"); } @@ -3262,7 +3262,7 @@ static SDL_bool VULKAN_SetCopyState(SDL_Renderer *renderer, const SDL_RenderComm return SDL_SetError("Unknown scale mode: %d\n", textureData->scaleMode); } - + if (textureData->mainImage.imageLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { SDL_bool stoppedRenderPass = SDL_FALSE; if (rendererData->currentRenderPass != VK_NULL_HANDLE) { @@ -3385,6 +3385,7 @@ static int VULKAN_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof(cmd->data.viewport.rect)) != 0) { SDL_copyp(viewport, &cmd->data.viewport.rect); rendererData->viewportDirty = SDL_TRUE; + rendererData->cliprectDirty = SDL_TRUE; } break; } @@ -3645,7 +3646,7 @@ static int VULKAN_RenderPresent(SDL_Renderer *renderer) SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkQueuePresentKHR(): %s\n", SDL_Vulkan_GetResultString(result)); return -1; } - + rendererData->currentCommandBufferIndex = ( rendererData->currentCommandBufferIndex + 1 ) % rendererData->swapchainImageCount; /* Wait for previous time this command buffer was submitted, will be N frames ago */ @@ -3657,7 +3658,7 @@ static int VULKAN_RenderPresent(SDL_Renderer *renderer) } VULKAN_AcquireNextSwapchainImage(renderer); - + return (result == VK_SUCCESS); } From 141497b14f1697c37116c92d30ddb497c99be2b6 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 25 Feb 2024 09:44:41 -0800 Subject: [PATCH 102/220] Added an automated test to verify clip rect functionality --- test/testautomation_render.c | 63 +++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/test/testautomation_render.c b/test/testautomation_render.c index daa459d9a34ec..82ac1e3b2892c 100644 --- a/test/testautomation_render.c +++ b/test/testautomation_render.c @@ -859,6 +859,63 @@ static int render_testViewport(void *arg) return TEST_COMPLETED; } +/** + * Test clip rect + */ +static int render_testClipRect(void *arg) +{ + SDL_Surface *referenceSurface; + SDL_Rect cliprect; + + cliprect.x = TESTRENDER_SCREEN_W / 3; + cliprect.y = TESTRENDER_SCREEN_H / 3; + cliprect.w = TESTRENDER_SCREEN_W / 2; + cliprect.h = TESTRENDER_SCREEN_H / 2; + + /* Create expected result */ + referenceSurface = SDL_CreateSurface(TESTRENDER_SCREEN_W, TESTRENDER_SCREEN_H, RENDER_COMPARE_FORMAT); + CHECK_FUNC(SDL_FillSurfaceRect, (referenceSurface, NULL, RENDER_COLOR_CLEAR)) + CHECK_FUNC(SDL_FillSurfaceRect, (referenceSurface, &cliprect, RENDER_COLOR_GREEN)) + + /* Clear surface. */ + clearScreen(); + + /* Set the cliprect and do a fill operation */ + CHECK_FUNC(SDL_SetRenderClipRect, (renderer, &cliprect)) + CHECK_FUNC(SDL_SetRenderDrawColor, (renderer, 0, 255, 0, SDL_ALPHA_OPAQUE)) + CHECK_FUNC(SDL_RenderFillRect, (renderer, NULL)) + CHECK_FUNC(SDL_SetRenderClipRect, (renderer, NULL)) + + /* Check to see if final image matches. */ + compare(referenceSurface, ALLOWABLE_ERROR_OPAQUE); + + /* + * Verify that clear ignores the cliprect + */ + + /* Create expected result */ + CHECK_FUNC(SDL_FillSurfaceRect, (referenceSurface, NULL, RENDER_COLOR_GREEN)) + + /* Clear surface. */ + clearScreen(); + + /* Set the cliprect and do a clear operation */ + CHECK_FUNC(SDL_SetRenderClipRect, (renderer, &cliprect)) + CHECK_FUNC(SDL_SetRenderDrawColor, (renderer, 0, 255, 0, SDL_ALPHA_OPAQUE)) + CHECK_FUNC(SDL_RenderClear, (renderer)) + CHECK_FUNC(SDL_SetRenderClipRect, (renderer, NULL)) + + /* Check to see if final image matches. */ + compare(referenceSurface, ALLOWABLE_ERROR_OPAQUE); + + /* Make current */ + SDL_RenderPresent(renderer); + + SDL_DestroySurface(referenceSurface); + + return TEST_COMPLETED; +} + /** * Test logical size */ @@ -1319,6 +1376,10 @@ static const SDLTest_TestCaseReference renderTest9 = { }; static const SDLTest_TestCaseReference renderTest10 = { + (SDLTest_TestCaseFp)render_testClipRect, "render_testClipRect", "Tests clip rect", TEST_ENABLED +}; + +static const SDLTest_TestCaseReference renderTest11 = { (SDLTest_TestCaseFp)render_testLogicalSize, "render_testLogicalSize", "Tests logical size", TEST_ENABLED }; @@ -1326,7 +1387,7 @@ static const SDLTest_TestCaseReference renderTest10 = { static const SDLTest_TestCaseReference *renderTests[] = { &renderTest1, &renderTest2, &renderTest3, &renderTest4, &renderTest5, &renderTest6, &renderTest7, &renderTest8, - &renderTest9, &renderTest10, NULL + &renderTest9, &renderTest10, &renderTest11, NULL }; /* Render test suite (global) */ From 9dbbf0a2f7a0ed460e13a031313e269cc1899067 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 25 Feb 2024 10:13:59 -0800 Subject: [PATCH 103/220] Implemented clip rect functionality for the Vulkan renderer --- src/render/vulkan/SDL_render_vulkan.c | 40 +++++++++++++++++---------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 368381a28cf5c..9005d9a33b878 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -2989,14 +2989,30 @@ static int VULKAN_UpdateViewport(SDL_Renderer *renderer) vkViewport.maxDepth = 1.0f; vkCmdSetViewport(rendererData->currentCommandBuffer, 0, 1, &vkViewport); + rendererData->viewportDirty = SDL_FALSE; + return 0; +} + +static int VULKAN_UpdateClipRect(SDL_Renderer *renderer) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + const SDL_Rect *viewport = &rendererData->currentViewport; + VkRect2D scissor; - scissor.offset.x = viewport->x; - scissor.offset.y = viewport->y; - scissor.extent.width = viewport->w; - scissor.extent.height = viewport->h; + if (rendererData->currentCliprectEnabled) { + scissor.offset.x = viewport->x + rendererData->currentCliprect.x; + scissor.offset.y = viewport->y + rendererData->currentCliprect.y; + scissor.extent.width = rendererData->currentCliprect.w; + scissor.extent.height = rendererData->currentCliprect.h; + } else { + scissor.offset.x = viewport->x; + scissor.offset.y = viewport->y; + scissor.extent.width = viewport->w; + scissor.extent.height = viewport->h; + } vkCmdSetScissor(rendererData->currentCommandBuffer, 0, 1, &scissor); - rendererData->viewportDirty = SDL_FALSE; + rendererData->cliprectDirty = SDL_FALSE; return 0; } @@ -3120,6 +3136,10 @@ static SDL_bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderComm } } + if (rendererData->cliprectDirty) { + VULKAN_UpdateClipRect(renderer); + } + if (updateConstants == SDL_TRUE || SDL_memcmp(&rendererData->vertexShaderConstantsData.model, newmatrix, sizeof(*newmatrix)) != 0) { SDL_memcpy(&rendererData->vertexShaderConstantsData.model, newmatrix, sizeof(*newmatrix)); vkCmdPushConstants(rendererData->currentCommandBuffer, rendererData->currentPipelineState->pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, @@ -3393,20 +3413,10 @@ static int VULKAN_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd case SDL_RENDERCMD_SETCLIPRECT: { const SDL_Rect *rect = &cmd->data.cliprect.rect; - SDL_Rect viewport_cliprect; if (rendererData->currentCliprectEnabled != cmd->data.cliprect.enabled) { rendererData->currentCliprectEnabled = cmd->data.cliprect.enabled; rendererData->cliprectDirty = SDL_TRUE; } - if (!rendererData->currentCliprectEnabled) { - /* If the clip rect is disabled, then the scissor rect should be the whole viewport, - since direct3d12 doesn't allow disabling the scissor rectangle */ - viewport_cliprect.x = 0; - viewport_cliprect.y = 0; - viewport_cliprect.w = rendererData->currentViewport.w; - viewport_cliprect.h = rendererData->currentViewport.h; - rect = &viewport_cliprect; - } if (SDL_memcmp(&rendererData->currentCliprect, rect, sizeof(*rect)) != 0) { SDL_copyp(&rendererData->currentCliprect, rect); rendererData->cliprectDirty = SDL_TRUE; From d211da75ac635641c7872af12d460cb25ad5f17b Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 25 Feb 2024 13:38:47 -0800 Subject: [PATCH 104/220] Fixed crash if app delegate method is called when SDL isn't initialized --- src/video/cocoa/SDL_cocoaevents.m | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/video/cocoa/SDL_cocoaevents.m b/src/video/cocoa/SDL_cocoaevents.m index daaa0ba33b998..81f086cea30d0 100644 --- a/src/video/cocoa/SDL_cocoaevents.m +++ b/src/video/cocoa/SDL_cocoaevents.m @@ -284,7 +284,10 @@ - (void)focusSomeWindow:(NSNotification *)aNotification - (void)screenParametersChanged:(NSNotification *)aNotification { - Cocoa_UpdateDisplays(SDL_GetVideoDevice()); + SDL_VideoDevice *device = SDL_GetVideoDevice(); + if (device) { + Cocoa_UpdateDisplays(device); + } } - (void)localeDidChange:(NSNotification *)notification From 7a9c6c7ce961601e65926382e9251075ae0cfcc2 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 25 Feb 2024 13:55:52 -0800 Subject: [PATCH 105/220] Include SDL_PIXELFORMAT_P010 as a supported format for the metal renderer --- src/render/metal/SDL_render_metal.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index f8ed8c2413605..cddbfcfb9c2b6 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -2216,7 +2216,7 @@ in case we want to use it later (recreating the renderer) { "metal", (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC), - 9, + 10, { SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORMAT_ABGR8888, SDL_PIXELFORMAT_XBGR2101010, From e6d9251ecb7eec05765fee8cbe5080af52d2e9df Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Sun, 25 Feb 2024 03:06:45 +0100 Subject: [PATCH 106/220] docs: improve CMake documentation for Apple --- docs/README-cmake.md | 98 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 81 insertions(+), 17 deletions(-) diff --git a/docs/README-cmake.md b/docs/README-cmake.md index 0eef99c7a72dd..9578f87ca869e 100644 --- a/docs/README-cmake.md +++ b/docs/README-cmake.md @@ -8,14 +8,15 @@ The CMake build system is supported on the following platforms: * Linux * Microsoft Visual C * MinGW and Msys -* macOS, iOS, and tvOS, with support for XCode +* macOS, iOS, tvOS, and visionOS with support for XCode * Android * Emscripten -* FreeBSD +* NetBSD * Haiku * Nintendo 3DS -* Playstation 2 -* Playstation Vita +* PlayStation 2 +* PlayStation Portable +* PlayStation Vita * QNX 7.x/8.x * RiscOS @@ -136,27 +137,88 @@ flags to the compiler. cmake .. -DCMAKE_C_FLAGS="/ARCH:AVX2" -DCMAKE_CXX_FLAGS="/ARCH:AVX2" ``` -### iOS/tvOS +### Apple + +CMake documentation for cross building for Apple: +[link](https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-ios-tvos-visionos-or-watchos) + +#### iOS/tvOS/visionOS + +CMake 3.14+ natively includes support for iOS, tvOS and watchOS. visionOS requires CMake 3.28+. +SDL binaries may be built using Xcode or Make, possibly among other build-systems. + +When using a compatible version of CMake, it should be possible to: + +- build SDL dylibs, both static and dynamic dylibs +- build SDL frameworks, only shared +- build SDL test apps + +#### Frameworks + +Configure with `-DSDL_FRAMEWORK=ON` to build a SDL framework instead of a dylib shared library. +Only shared frameworks are supported, no static ones. + +#### Platforms + +Use `-DCMAKE_PLATFORM_NAME=` to configure the platform. CMake can target only one platform at a time. + +| Apple platform | `CMAKE_SYSTEM_NAME` value | +|-----------------|---------------------------| +| macOS (MacOS X) | `Darwin` | +| iOS | `iOS` | +| tvOS | `tvOS` | +| visionOS | `visionOS` | +| watchOS | `watchOS` | + +#### Universal binaries -CMake 3.14+ natively includes support for iOS and tvOS. SDL binaries may be built -using Xcode or Make, possibly among other build-systems. +A universal binaries, can be built by configuring CMake with +`-DCMAKE_OSX_ARCHITECTURES=`. -When using a recent version of CMake (3.14+), it should be possible to: +For example `-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"` will build binaries that run on both Intel cpus and Apple silicon. -- build SDL for iOS, both static and dynamic -- build SDL test apps (as iOS/tvOS .app bundles) -- generate a working SDL_build_config.h for iOS (using SDL_build_config.h.cmake as a basis) +SDL supports following Apple architectures: -To use, set the following CMake variables when running CMake's configuration stage: +| Platform | `CMAKE_OSX_ARCHITECTURES` value | +|----------------------------|---------------------------------| +| 64-bit ARM (Apple Silicon) | `arm64` | +| x86_64 | `x86_64` | +| 32-bit ARM | `armv7s` | -- `CMAKE_SYSTEM_NAME=` (either `iOS` or `tvOS`) -- `CMAKE_OSX_SYSROOT=` (examples: `iphoneos`, `iphonesimulator`, `iphoneos12.4`, `/full/path/to/iPhoneOS.sdk`, - `appletvos`, `appletvsimulator`, `appletvos12.4`, `/full/path/to/AppleTVOS.sdk`, etc.) -- `CMAKE_OSX_ARCHITECTURES=` (example: "arm64;armv7s;x86_64") +CMake documentation: [link](https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_ARCHITECTURES.html) +#### Simulators and/or non-default maxOS platform SDK + +Use `-DCMAKE_OSX_SYSROOT=` to configure a different platform SDK. +The value can be either the name of the SDK, or a full path to the sdk (e.g. `/full/path/to/iPhoneOS.sdk`). + +| SDK | `CMAKE_OSX_SYSROOT` value | +|----------------------|---------------------------| +| iphone | `iphoneos` | +| iphonesimulator | `iphonesimulator` | +| appleTV | `appletvos` | +| appleTV simulator | `appletvsimulator` | +| visionOS | `xr` | +| visionOS simulator | `xrsimulator` | +| watchOS | `watchos` | +| watchOS simulator | `watchsimulator` | + +Append with a version number to target a specific SDK revision: e.g. `iphoneos12.4`, `appletvos12.4`. + +CMake documentation: [link](https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_SYSROOT.html) #### Examples +- for macOS, building a dylib and/or static library for x86_64 and arm64: + + ```bash + cmake ~/sdl -DCMAKE_SYSTEM_NAME=Darwin -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" + +- for macOS, building an universal framework for x86_64 and arm64: + + ```bash + cmake ~/sdl -DSDL_FRAMEWORK=ON -DCMAKE_SYSTEM_NAME=Darwin -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" + - for iOS-Simulator, using the latest, installed SDK: ```bash @@ -275,7 +337,9 @@ file(WRITE main.c [===========================================[ /* START of source modifications */ #include -#include +/* + * SDL3/SDL_main.h is explicitly not included such that a terminal window would appear on Windows. + */ int main(int argc, char *argv[]) { (void)argc; From e0dadba6f5e06a6c0f5a498878ee7b111783a3b5 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Sun, 25 Feb 2024 22:26:23 +0000 Subject: [PATCH 107/220] Sync SDL3 wiki -> header --- docs/README-cmake.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/README-cmake.md b/docs/README-cmake.md index 9578f87ca869e..73f80f21c3b9a 100644 --- a/docs/README-cmake.md +++ b/docs/README-cmake.md @@ -155,7 +155,7 @@ When using a compatible version of CMake, it should be possible to: #### Frameworks -Configure with `-DSDL_FRAMEWORK=ON` to build a SDL framework instead of a dylib shared library. +Configure with `-DSDL_FRAMEWORK=ON` to build a SDL framework instead of a dylib shared library. Only shared frameworks are supported, no static ones. #### Platforms @@ -185,7 +185,7 @@ SDL supports following Apple architectures: | x86_64 | `x86_64` | | 32-bit ARM | `armv7s` | -CMake documentation: [link](https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_ARCHITECTURES.html) +CMake documentation: [link](https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_ARCHITECTURES.html) #### Simulators and/or non-default maxOS platform SDK @@ -338,7 +338,7 @@ file(WRITE main.c [===========================================[ #include /* - * SDL3/SDL_main.h is explicitly not included such that a terminal window would appear on Windows. + * SDL3/SDL_main.h is explicitly not included such that a terminal window would appear on Windows. */ int main(int argc, char *argv[]) { From be51b7aceaf5e5641915cfb50564b50196005641 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 25 Feb 2024 15:46:38 -0800 Subject: [PATCH 108/220] Use the maximum potential headroom if EDR content isn't currently being displayed. Also document that the HDR properties can change dynamically at runtime. --- include/SDL3/SDL_render.h | 6 +++--- include/SDL3/SDL_video.h | 8 ++++---- src/video/cocoa/SDL_cocoamodes.m | 6 +++++- src/video/uikit/SDL_uikitmodes.m | 6 +++++- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index d45681b4389ae..2d09319b3af5c 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -344,13 +344,13 @@ extern DECLSPEC int SDLCALL SDL_GetRendererInfo(SDL_Renderer *renderer, SDL_Rend * SDL_COLORSPACE_SRGB. * - `SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN`: true if the output colorspace is * SDL_COLORSPACE_SRGB_LINEAR and the renderer is showing on a display with - * HDR enabled. + * HDR enabled. This property can change dynamically when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. * - `SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT`: the value of SDR white in the * SDL_COLORSPACE_SRGB_LINEAR colorspace. When HDR is enabled, this value is - * automatically multiplied into the color scale. + * automatically multiplied into the color scale. This property can change dynamically when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. * - `SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT`: the additional high dynamic range * that can be displayed, in terms of the SDR white point. When HDR is not - * enabled, this will be 1.0. + * enabled, this will be 1.0. This property can change dynamically when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. * - `SDL_PROP_RENDERER_D3D9_DEVICE_POINTER`: the IDirect3DDevice9 associated * with the renderer * - `SDL_PROP_RENDERER_D3D11_DEVICE_POINTER`: the ID3D11Device associated diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h index 1cbc4ec828305..77a03cf81d371 100644 --- a/include/SDL3/SDL_video.h +++ b/include/SDL3/SDL_video.h @@ -355,15 +355,15 @@ extern DECLSPEC SDL_DisplayID SDLCALL SDL_GetPrimaryDisplay(void); * * The following read-only properties are provided by SDL: * - * - `SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN`: true if the display has HDR - * headroom above the SDR white point. + * - `SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN`: true if the display has HDR + * headroom above the SDR white point. This property can change dynamically when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. * - `SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT`: the value of SDR white in the * SDL_COLORSPACE_SRGB_LINEAR colorspace. On Windows this corresponds to the * SDR white level in scRGB colorspace, and on Apple platforms this is - * always 1.0 for EDR content. + * always 1.0 for EDR content. This property can change dynamically when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. * - `SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT`: the additional high dynamic range * that can be displayed, in terms of the SDR white point. When HDR is not - * enabled, this will be 1.0. + * enabled, this will be 1.0. This property can change dynamically when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. * * \param displayID the instance ID of the display to query * \returns a valid property ID on success or 0 on failure; call diff --git a/src/video/cocoa/SDL_cocoamodes.m b/src/video/cocoa/SDL_cocoamodes.m index 42d54a4bd67e8..862bc56b54d5f 100644 --- a/src/video/cocoa/SDL_cocoamodes.m +++ b/src/video/cocoa/SDL_cocoamodes.m @@ -299,7 +299,11 @@ static void Cocoa_GetHDRProperties(CGDirectDisplayID displayID, SDL_HDRDisplayPr if (@available(macOS 10.15, *)) { NSScreen *screen = GetNSScreenForDisplayID(displayID); if (screen) { - HDR->HDR_headroom = screen.maximumExtendedDynamicRangeColorComponentValue; + if (screen.maximumExtendedDynamicRangeColorComponentValue > 1.0f) { + HDR->HDR_headroom = screen.maximumExtendedDynamicRangeColorComponentValue; + } else { + HDR->HDR_headroom = screen.maximumPotentialExtendedDynamicRangeColorComponentValue; + } } } #endif diff --git a/src/video/uikit/SDL_uikitmodes.m b/src/video/uikit/SDL_uikitmodes.m index 8da779e0163ec..b64a9c3304dce 100644 --- a/src/video/uikit/SDL_uikitmodes.m +++ b/src/video/uikit/SDL_uikitmodes.m @@ -247,7 +247,11 @@ int UIKit_AddDisplay(UIScreen *uiscreen, SDL_bool send_event) #ifndef SDL_PLATFORM_TVOS if (@available(iOS 16.0, *)) { - display.HDR.HDR_headroom = uiscreen.currentEDRHeadroom; + if (uiscreen.currentEDRHeadroom > 1.0f) { + display.HDR.HDR_headroom = uiscreen.currentEDRHeadroom; + } else { + display.HDR.HDR_headroom = uiscreen.potentialEDRHeadroom; + } } #endif /* !SDL_PLATFORM_TVOS */ From e223e1d498e3f89d007f226424055863ea64db8f Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 25 Feb 2024 15:54:09 -0800 Subject: [PATCH 109/220] Added SDL_camera.h to the public Framework headers --- Xcode/SDL/SDL.xcodeproj/project.pbxproj | 64 +++++++++++-------------- 1 file changed, 27 insertions(+), 37 deletions(-) diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj index 7ec548712d9a7..759c4b5963421 100644 --- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -397,6 +397,7 @@ F36C7AD1294BA009004D61C3 /* SDL_runapp.c in Sources */ = {isa = PBXBuildFile; fileRef = F36C7AD0294BA009004D61C3 /* SDL_runapp.c */; }; F376F6552559B4E300CFC0BC /* SDL_hidapi.c in Sources */ = {isa = PBXBuildFile; fileRef = A7D8A81423E2513F00DCD162 /* SDL_hidapi.c */; }; F37A8E1A28405AA100C38E95 /* CMake in Resources */ = {isa = PBXBuildFile; fileRef = F37A8E1928405AA100C38E95 /* CMake */; }; + F37E184E2B8C097D0098C111 /* SDL_camera.h in Headers */ = {isa = PBXBuildFile; fileRef = 000084ED0A56E3ED52F90000 /* SDL_camera.h */; settings = {ATTRIBUTES = (Public, ); }; }; F3820713284F3609004DD584 /* controller_type.c in Sources */ = {isa = PBXBuildFile; fileRef = F3820712284F3609004DD584 /* controller_type.c */; }; F382071D284F362F004DD584 /* SDL_guid.c in Sources */ = {isa = PBXBuildFile; fileRef = F382071C284F362F004DD584 /* SDL_guid.c */; }; F386F6E72884663E001840AA /* SDL_log_c.h in Headers */ = {isa = PBXBuildFile; fileRef = F386F6E42884663E001840AA /* SDL_log_c.h */; }; @@ -499,7 +500,6 @@ F3FA5A242B59ACE000FEAD97 /* yuv_rgb_lsx.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FA5A1B2B59ACE000FEAD97 /* yuv_rgb_lsx.h */; }; F3FA5A252B59ACE000FEAD97 /* yuv_rgb_common.h in Headers */ = {isa = PBXBuildFile; fileRef = F3FA5A1C2B59ACE000FEAD97 /* yuv_rgb_common.h */; }; FA73671D19A540EF004122E4 /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA73671C19A540EF004122E4 /* CoreVideo.framework */; platformFilters = (ios, maccatalyst, macos, tvos, watchos, ); }; - 00009F560664255CCB6C0000 /* SDL_camera_mediafoundation.c in Sources */ = {isa = PBXBuildFile; fileRef = 0000C61C247BAAAF757D0000 /* SDL_camera_mediafoundation.c */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1028,7 +1028,6 @@ F59C710600D5CB5801000001 /* SDL.info */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; path = SDL.info; sourceTree = ""; }; F5A2EF3900C6A39A01000001 /* BUGS.txt */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = text; name = BUGS.txt; path = ../../BUGS.txt; sourceTree = SOURCE_ROOT; }; FA73671C19A540EF004122E4 /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; }; - 0000C61C247BAAAF757D0000 /* SDL_camera_mediafoundation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_camera_mediafoundation.c; path = SDL_camera_mediafoundation.c; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1070,7 +1069,6 @@ 0000035D38C3899C7EFD0000 /* SDL_camera.c */, 00009003C7148E1126CA0000 /* SDL_camera_c.h */, 00005D3EB902478835E20000 /* SDL_syscamera.h */, - 0000926F2501CA0BDD650000 /* mediafoundation */, ); path = camera; sourceTree = ""; @@ -1111,6 +1109,7 @@ F3F7D8E72933074E00816151 /* SDL_begin_code.h */, F3F7D8D82933074C00816151 /* SDL_bits.h */, F3F7D8CE2933074C00816151 /* SDL_blendmode.h */, + 000084ED0A56E3ED52F90000 /* SDL_camera.h */, F3F7D8D72933074C00816151 /* SDL_clipboard.h */, F3F7D8E52933074D00816151 /* SDL_close_code.h */, F3F7D8E32933074D00816151 /* SDL_copying.h */, @@ -1172,7 +1171,6 @@ F3F7D8C52933074B00816151 /* SDL_video.h */, F3F7D8D42933074C00816151 /* SDL_vulkan.h */, F3F7D8CF2933074C00816151 /* SDL.h */, - 000084ED0A56E3ED52F90000 /* SDL_camera.h */, ); name = "Public Headers"; path = ../../include; @@ -2178,14 +2176,6 @@ path = resources; sourceTree = ""; }; - 0000926F2501CA0BDD650000 /* mediafoundation */ = { - isa = PBXGroup; - children = ( - 0000C61C247BAAAF757D0000 /* SDL_camera_mediafoundation.c */, - ); - path = mediafoundation; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -2200,8 +2190,11 @@ F3F7D9292933074E00816151 /* SDL_atomic.h in Headers */, F3F7D8ED2933074E00816151 /* SDL_audio.h in Headers */, A7D8B7A023E2514400DCD162 /* SDL_audio_c.h in Headers */, - F3681E812B7AA6240002C6FD /* SDL_cocoashape.h in Headers */, + F32DDACF2AB795A30041EAA5 /* SDL_audio_channel_converters.h in Headers */, + F32DDAD22AB795A30041EAA5 /* SDL_audio_resampler_filter.h in Headers */, A7D8B7B223E2514400DCD162 /* SDL_audiodev_c.h in Headers */, + F32DDAD32AB795A30041EAA5 /* SDL_audioqueue.h in Headers */, + F32DDAD02AB795A30041EAA5 /* SDL_audioresample.h in Headers */, F3F7D9E12933074E00816151 /* SDL_begin_code.h in Headers */, F3F7D9A52933074E00816151 /* SDL_bits.h in Headers */, A7D8BA0123E2514400DCD162 /* SDL_blendfillrect.h in Headers */, @@ -2212,7 +2205,9 @@ A7D8B2BA23E2514200DCD162 /* SDL_blit_auto.h in Headers */, A7D8B39823E2514200DCD162 /* SDL_blit_copy.h in Headers */, A7D8ADEC23E2514100DCD162 /* SDL_blit_slow.h in Headers */, + F37E184E2B8C097D0098C111 /* SDL_camera.h in Headers */, F3F7D9A12933074E00816151 /* SDL_clipboard.h in Headers */, + F3DDCC562AFD42B600B0842B /* SDL_clipboard_c.h in Headers */, A7D8BB6F23E2514500DCD162 /* SDL_clipboardevents_c.h in Headers */, F3F7D9D92933074E00816151 /* SDL_close_code.h in Headers */, A7D8AECA23E2514100DCD162 /* SDL_cocoaclipboard.h in Headers */, @@ -2224,17 +2219,15 @@ A7D8AF1E23E2514100DCD162 /* SDL_cocoamouse.h in Headers */, A7D8AEDC23E2514100DCD162 /* SDL_cocoaopengl.h in Headers */, A7D8AEEE23E2514100DCD162 /* SDL_cocoaopengles.h in Headers */, + F3681E812B7AA6240002C6FD /* SDL_cocoashape.h in Headers */, A7D8AF0023E2514100DCD162 /* SDL_cocoavideo.h in Headers */, A7D8AEE823E2514100DCD162 /* SDL_cocoavulkan.h in Headers */, A7D8AEFA23E2514100DCD162 /* SDL_cocoawindow.h in Headers */, - F32DDACF2AB795A30041EAA5 /* SDL_audio_channel_converters.h in Headers */, F3F7D9D12933074E00816151 /* SDL_copying.h in Headers */, A7D8B8CC23E2514400DCD162 /* SDL_coreaudio.h in Headers */, A7D8A96F23E2514000DCD162 /* SDL_coremotionsensor.h in Headers */, F3F7D9B92933074E00816151 /* SDL_cpuinfo.h in Headers */, - F3990E062A788303000D8759 /* SDL_hidapi_ios.h in Headers */, A7D8B98023E2514400DCD162 /* SDL_d3dmath.h in Headers */, - F362B91A2B3349E200D30B94 /* SDL_gamepad_c.h in Headers */, A7D8B8A223E2514400DCD162 /* SDL_diskaudio.h in Headers */, A7D8BB3F23E2514500DCD162 /* SDL_displayevents_c.h in Headers */, A7D8BA1923E2514400DCD162 /* SDL_draw.h in Headers */, @@ -2244,11 +2237,10 @@ A7D8B79423E2514400DCD162 /* SDL_dummyaudio.h in Headers */, A7D8A96323E2514000DCD162 /* SDL_dummysensor.h in Headers */, A7D8AB0A23E2514100DCD162 /* SDL_dynapi.h in Headers */, - F32DDAD02AB795A30041EAA5 /* SDL_audioresample.h in Headers */, A7D8AB1023E2514100DCD162 /* SDL_dynapi_overrides.h in Headers */, A7D8AB1C23E2514100DCD162 /* SDL_dynapi_procs.h in Headers */, + E4F7981C2AD8D85500669F54 /* SDL_dynapi_unsupported.h in Headers */, F3F7D9252933074E00816151 /* SDL_egl.h in Headers */, - F362B9192B3349E200D30B94 /* controller_list.h in Headers */, A7D8ABD923E2514100DCD162 /* SDL_egl_c.h in Headers */, F3F7D93D2933074E00816151 /* SDL_endian.h in Headers */, F3F7D9352933074E00816151 /* SDL_error.h in Headers */, @@ -2257,6 +2249,7 @@ A7D8BBA523E2514500DCD162 /* SDL_events_c.h in Headers */, F3F7D99D2933074E00816151 /* SDL_filesystem.h in Headers */, F3F7D9852933074E00816151 /* SDL_gamepad.h in Headers */, + F362B91A2B3349E200D30B94 /* SDL_gamepad_c.h in Headers */, A7D8B4AC23E2514300DCD162 /* SDL_gamepad_db.h in Headers */, A7D8BA5523E2514400DCD162 /* SDL_gles2funcs.h in Headers */, A7D8BA7923E2514400DCD162 /* SDL_glfuncs.h in Headers */, @@ -2264,6 +2257,9 @@ F3F7D8F92933074E00816151 /* SDL_haptic.h in Headers */, A7D8AABC23E2514100DCD162 /* SDL_haptic_c.h in Headers */, F3F7D96D2933074E00816151 /* SDL_hidapi.h in Headers */, + F3990E042A788303000D8759 /* SDL_hidapi_c.h in Headers */, + F3990E062A788303000D8759 /* SDL_hidapi_ios.h in Headers */, + F3990E052A788303000D8759 /* SDL_hidapi_mac.h in Headers */, A75FDBC523EA380300529352 /* SDL_hidapi_rumble.h in Headers */, A7D8B55723E2514300DCD162 /* SDL_hidapijoystick_c.h in Headers */, F3F7D9112933074E00816151 /* SDL_hints.h in Headers */, @@ -2272,8 +2268,6 @@ A7D8A99923E2514000DCD162 /* SDL_internal.h in Headers */, F3B38CDF296E2E52005DA6D3 /* SDL_intrin.h in Headers */, F395C1932569C68F00942BFF /* SDL_iokitjoystick_c.h in Headers */, - F3990E052A788303000D8759 /* SDL_hidapi_mac.h in Headers */, - F3FA5A1D2B59ACE000FEAD97 /* yuv_rgb_internal.h in Headers */, F3F7D9912933074E00816151 /* SDL_joystick.h in Headers */, A7D8B58723E2514300DCD162 /* SDL_joystick_c.h in Headers */, F3F7D9512933074E00816151 /* SDL_keyboard.h in Headers */, @@ -2289,7 +2283,6 @@ F3F7D91D2933074E00816151 /* SDL_messagebox.h in Headers */, F3F7D98D2933074E00816151 /* SDL_metal.h in Headers */, F395C1BA2569C6A000942BFF /* SDL_mfijoystick_c.h in Headers */, - F362B91B2B3349E200D30B94 /* SDL_steam_virtual_gamepad.h in Headers */, F3F7D9992933074E00816151 /* SDL_misc.h in Headers */, F3F7D9AD2933074E00816151 /* SDL_mouse.h in Headers */, A7D8BB1B23E2514500DCD162 /* SDL_mouse_c.h in Headers */, @@ -2305,23 +2298,24 @@ F3B38CDB296E2E52005DA6D3 /* SDL_oldnames.h in Headers */, F3F7D9C92933074E00816151 /* SDL_opengl.h in Headers */, F3F7D9452933074E00816151 /* SDL_opengl_glext.h in Headers */, - F3E5A6ED2AD5E10800293D83 /* SDL_properties.h in Headers */, - E4F7981C2AD8D85500669F54 /* SDL_dynapi_unsupported.h in Headers */, F3F7D95D2933074E00816151 /* SDL_opengles.h in Headers */, F3F7D9612933074E00816151 /* SDL_opengles2.h in Headers */, F3F7D8FD2933074E00816151 /* SDL_opengles2_gl2.h in Headers */, F3F7D9392933074E00816151 /* SDL_opengles2_gl2ext.h in Headers */, F3F7D9692933074E00816151 /* SDL_opengles2_gl2platform.h in Headers */, F3F7D9092933074E00816151 /* SDL_opengles2_khrplatform.h in Headers */, - F32DDAD22AB795A30041EAA5 /* SDL_audio_resampler_filter.h in Headers */, + 63134A222A7902CF0021E9A6 /* SDL_pen.h in Headers */, + 63134A252A7902FD0021E9A6 /* SDL_pen_c.h in Headers */, F3F7D9192933074E00816151 /* SDL_pixels.h in Headers */, A7D8B2C023E2514200DCD162 /* SDL_pixels_c.h in Headers */, F3F7D8F12933074E00816151 /* SDL_platform.h in Headers */, F3B38CD3296E2E52005DA6D3 /* SDL_platform_defines.h in Headers */, F3F7D9B12933074E00816151 /* SDL_power.h in Headers */, + F3E5A6ED2AD5E10800293D83 /* SDL_properties.h in Headers */, F3F7D9C12933074E00816151 /* SDL_quit.h in Headers */, F3F7D9CD2933074E00816151 /* SDL_rect.h in Headers */, A7D8AC0323E2514100DCD162 /* SDL_rect_c.h in Headers */, + F3DDCC5D2AFD42B600B0842B /* SDL_rect_impl.h in Headers */, F3F7D9BD2933074E00816151 /* SDL_render.h in Headers */, A7D8B9FB23E2514400DCD162 /* SDL_render_sw_c.h in Headers */, F3F7D9152933074E00816151 /* SDL_revision.h in Headers */, @@ -2337,9 +2331,9 @@ A7D8B99B23E2514400DCD162 /* SDL_shaders_metal_macos.h in Headers */, A7D8B9A123E2514400DCD162 /* SDL_shaders_metal_tvos.h in Headers */, F3F7D8F52933074E00816151 /* SDL_stdinc.h in Headers */, + F362B91B2B3349E200D30B94 /* SDL_steam_virtual_gamepad.h in Headers */, A7D8BBC723E2561500DCD162 /* SDL_steamcontroller.h in Headers */, F3F7D9312933074E00816151 /* SDL_surface.h in Headers */, - F3FA5A252B59ACE000FEAD97 /* yuv_rgb_common.h in Headers */, A7D8B85A23E2514400DCD162 /* SDL_sysaudio.h in Headers */, A7D8AAD423E2514100DCD162 /* SDL_syshaptic.h in Headers */, A7D8AAE023E2514100DCD162 /* SDL_syshaptic_c.h in Headers */, @@ -2362,14 +2356,11 @@ F3F7D9012933074E00816151 /* SDL_touch.h in Headers */, A7D8BB6323E2514500DCD162 /* SDL_touch_c.h in Headers */, A1626A522617008D003F1973 /* SDL_triangle.h in Headers */, - F3DDCC5D2AFD42B600B0842B /* SDL_rect_impl.h in Headers */, A7D8BBD223E2574800DCD162 /* SDL_uikitappdelegate.h in Headers */, A7D8BBD423E2574800DCD162 /* SDL_uikitclipboard.h in Headers */, - F3FA5A202B59ACE000FEAD97 /* yuv_rgb_std.h in Headers */, A7D8BBD623E2574800DCD162 /* SDL_uikitevents.h in Headers */, A7D8BBD823E2574800DCD162 /* SDL_uikitmessagebox.h in Headers */, A7D8BBDA23E2574800DCD162 /* SDL_uikitmetalview.h in Headers */, - F3990E042A788303000D8759 /* SDL_hidapi_c.h in Headers */, A7D8BBDC23E2574800DCD162 /* SDL_uikitmodes.h in Headers */, A7D8BBDE23E2574800DCD162 /* SDL_uikitopengles.h in Headers */, A7D8BBE023E2574800DCD162 /* SDL_uikitopenglview.h in Headers */, @@ -2378,12 +2369,11 @@ A7D8BBE623E2574800DCD162 /* SDL_uikitviewcontroller.h in Headers */, A7D8BBE823E2574800DCD162 /* SDL_uikitvulkan.h in Headers */, A7D8BBEA23E2574800DCD162 /* SDL_uikitwindow.h in Headers */, - F3DDCC5B2AFD42B600B0842B /* SDL_video_c.h in Headers */, F386F6F02884663E001840AA /* SDL_utils_c.h in Headers */, F3973FA228A59BDD00B84553 /* SDL_vacopy.h in Headers */, F3F7D9D52933074E00816151 /* SDL_version.h in Headers */, - F3FA5A1F2B59ACE000FEAD97 /* yuv_rgb_sse.h in Headers */, F3F7D9592933074E00816151 /* SDL_video.h in Headers */, + F3DDCC5B2AFD42B600B0842B /* SDL_video_c.h in Headers */, 75E09163241EA924004729E1 /* SDL_virtualjoystick_c.h in Headers */, F3F7D9952933074E00816151 /* SDL_vulkan.h in Headers */, A7D8AD1D23E2514100DCD162 /* SDL_vulkan_internal.h in Headers */, @@ -2392,6 +2382,7 @@ A7D8B3B023E2514200DCD162 /* SDL_yuv_c.h in Headers */, A7D8B9CB23E2514400DCD162 /* SDL_yuv_sw_c.h in Headers */, A7D8BB4523E2514500DCD162 /* blank_cursor.h in Headers */, + F362B9192B3349E200D30B94 /* controller_list.h in Headers */, A7D8B5B723E2514300DCD162 /* controller_type.h in Headers */, A7D8BB4B23E2514500DCD162 /* default_cursor.h in Headers */, A7D8B23C23E2514200DCD162 /* egl.h in Headers */, @@ -2411,13 +2402,10 @@ A7D8B56F23E2514300DCD162 /* usb_ids.h in Headers */, A7D8B25423E2514200DCD162 /* vk_icd.h in Headers */, A7D8B24E23E2514200DCD162 /* vk_layer.h in Headers */, - F3FA5A242B59ACE000FEAD97 /* yuv_rgb_lsx.h in Headers */, - F3DDCC562AFD42B600B0842B /* SDL_clipboard_c.h in Headers */, A7D8B26623E2514200DCD162 /* vk_platform.h in Headers */, A7D8B2AE23E2514200DCD162 /* vk_sdk_platform.h in Headers */, A7D8B26023E2514200DCD162 /* vulkan.h in Headers */, A7D8B26C23E2514200DCD162 /* vulkan.hpp in Headers */, - F3FA5A1E2B59ACE000FEAD97 /* yuv_rgb_lsx_func.h in Headers */, A7D8B2B423E2514200DCD162 /* vulkan_android.h in Headers */, A7D8B2A823E2514200DCD162 /* vulkan_core.h in Headers */, A7D8B27223E2514200DCD162 /* vulkan_fuchsia.h in Headers */, @@ -2425,17 +2413,20 @@ A7D8B28423E2514200DCD162 /* vulkan_macos.h in Headers */, A7D8B29623E2514200DCD162 /* vulkan_mir.h in Headers */, A7D8B25A23E2514200DCD162 /* vulkan_vi.h in Headers */, - F32DDAD32AB795A30041EAA5 /* SDL_audioqueue.h in Headers */, A7D8B27823E2514200DCD162 /* vulkan_wayland.h in Headers */, A7D8B27E23E2514200DCD162 /* vulkan_win32.h in Headers */, A7D8B29023E2514200DCD162 /* vulkan_xcb.h in Headers */, A7D8B29C23E2514200DCD162 /* vulkan_xlib.h in Headers */, A7D8B28A23E2514200DCD162 /* vulkan_xlib_xrandr.h in Headers */, A7D8B3D423E2514300DCD162 /* yuv_rgb.h in Headers */, + F3FA5A252B59ACE000FEAD97 /* yuv_rgb_common.h in Headers */, + F3FA5A1D2B59ACE000FEAD97 /* yuv_rgb_internal.h in Headers */, + F3FA5A242B59ACE000FEAD97 /* yuv_rgb_lsx.h in Headers */, + F3FA5A1E2B59ACE000FEAD97 /* yuv_rgb_lsx_func.h in Headers */, + F3FA5A1F2B59ACE000FEAD97 /* yuv_rgb_sse.h in Headers */, A7D8B3C823E2514200DCD162 /* yuv_rgb_sse_func.h in Headers */, + F3FA5A202B59ACE000FEAD97 /* yuv_rgb_std.h in Headers */, A7D8B3CE23E2514300DCD162 /* yuv_rgb_std_func.h in Headers */, - 63134A222A7902CF0021E9A6 /* SDL_pen.h in Headers */, - 63134A252A7902FD0021E9A6 /* SDL_pen_c.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2765,7 +2756,6 @@ 000098E9DAA43EF6FF7F0000 /* SDL_camera.c in Sources */, 00001B2471F503DD3C1B0000 /* SDL_camera_dummy.c in Sources */, 00002B20A48E055EB0350000 /* SDL_camera_coremedia.m in Sources */, - 00009F560664255CCB6C0000 /* SDL_camera_mediafoundation.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From fbe7301aba5d03332a428244ea572984000e747a Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Sun, 25 Feb 2024 23:55:23 +0000 Subject: [PATCH 110/220] Sync SDL3 wiki -> header --- include/SDL3/SDL_render.h | 9 ++++++--- include/SDL3/SDL_video.h | 11 +++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 2d09319b3af5c..b02361cf7f5c3 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -344,13 +344,16 @@ extern DECLSPEC int SDLCALL SDL_GetRendererInfo(SDL_Renderer *renderer, SDL_Rend * SDL_COLORSPACE_SRGB. * - `SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN`: true if the output colorspace is * SDL_COLORSPACE_SRGB_LINEAR and the renderer is showing on a display with - * HDR enabled. This property can change dynamically when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. + * HDR enabled. This property can change dynamically when + * SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. * - `SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT`: the value of SDR white in the * SDL_COLORSPACE_SRGB_LINEAR colorspace. When HDR is enabled, this value is - * automatically multiplied into the color scale. This property can change dynamically when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. + * automatically multiplied into the color scale. This property can change + * dynamically when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. * - `SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT`: the additional high dynamic range * that can be displayed, in terms of the SDR white point. When HDR is not - * enabled, this will be 1.0. This property can change dynamically when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. + * enabled, this will be 1.0. This property can change dynamically when + * SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. * - `SDL_PROP_RENDERER_D3D9_DEVICE_POINTER`: the IDirect3DDevice9 associated * with the renderer * - `SDL_PROP_RENDERER_D3D11_DEVICE_POINTER`: the ID3D11Device associated diff --git a/include/SDL3/SDL_video.h b/include/SDL3/SDL_video.h index 77a03cf81d371..7482e255475ae 100644 --- a/include/SDL3/SDL_video.h +++ b/include/SDL3/SDL_video.h @@ -355,15 +355,18 @@ extern DECLSPEC SDL_DisplayID SDLCALL SDL_GetPrimaryDisplay(void); * * The following read-only properties are provided by SDL: * - * - `SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN`: true if the display has HDR - * headroom above the SDR white point. This property can change dynamically when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. + * - `SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN`: true if the display has HDR + * headroom above the SDR white point. This property can change dynamically + * when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. * - `SDL_PROP_DISPLAY_SDR_WHITE_POINT_FLOAT`: the value of SDR white in the * SDL_COLORSPACE_SRGB_LINEAR colorspace. On Windows this corresponds to the * SDR white level in scRGB colorspace, and on Apple platforms this is - * always 1.0 for EDR content. This property can change dynamically when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. + * always 1.0 for EDR content. This property can change dynamically when + * SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. * - `SDL_PROP_DISPLAY_HDR_HEADROOM_FLOAT`: the additional high dynamic range * that can be displayed, in terms of the SDR white point. When HDR is not - * enabled, this will be 1.0. This property can change dynamically when SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. + * enabled, this will be 1.0. This property can change dynamically when + * SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. * * \param displayID the instance ID of the display to query * \returns a valid property ID on success or 0 on failure; call From 1558d52a0a9f2f63f85543488b2a4d9427c0ffc9 Mon Sep 17 00:00:00 2001 From: David Gow Date: Sun, 25 Feb 2024 17:04:19 +0800 Subject: [PATCH 111/220] Vulkan: Only return memory types which are a superset of what we need VULKAN_FindMemoryTypeIndex() tries first to get a perfectly matching memory type, then falls back to selecting any memory type which overlaps with the requested flags. However, some of the flags requested are actually required, so if -- for example -- we request a memory type with VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, but get one without it, all future calls to vkMapMemory() will fail with: ``` vkMapMemory(): Mapping memory without VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT set. Memory has type 0 which has properties VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT. The Vulkan spec states: memory must have been created with a memory type that reports VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT. ``` (This occurs, for instance, on the totally non-conformant hasvk driver for Intel Haswell integrated GPUs, which otherwise works fine.) Instead, make sure that any memory type found has a superset of the requested flags, so it'll always be appropriate. Of course, this makes it _less_ likely for a memory type to be found, so it does make #9130 worse in some ways. See the next patch for details. Signed-off-by: David Gow --- src/render/vulkan/SDL_render_vulkan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 9005d9a33b878..c7f422590db04 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -1172,7 +1172,7 @@ static SDL_bool VULKAN_FindMemoryTypeIndex(VULKAN_RenderData *rendererData, uint if (!foundExactMatch) { for (memoryTypeIndex = 0; memoryTypeIndex < rendererData->physicalDeviceMemoryProperties.memoryTypeCount; memoryTypeIndex++) { if (typeBits & (1 << memoryTypeIndex)) { - if (rendererData->physicalDeviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags & flags) { + if ((rendererData->physicalDeviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags & flags) == flags) { break; } } From c172fb597248b005614dcbe447f24c8c8b8e1f3a Mon Sep 17 00:00:00 2001 From: David Gow Date: Sun, 25 Feb 2024 17:25:59 +0800 Subject: [PATCH 112/220] Vulkan: Support 'desired' vs 'required' memory flags (Fix #9310) When selecting a memory type, there are some property flags we need (e.g., VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, without which we cannot vkMapMemory), and others we'd simply prefer (e.g., VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, which may have a performance impact, but otherwise shouldn't be required). By specifying these separately, we can fall back to a memory type which doesn't have everything we want, but which should still work, rather than giving up. Signed-off-by: David Gow --- src/render/vulkan/SDL_render_vulkan.c | 28 +++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index c7f422590db04..d374f4fb23b33 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -408,7 +408,7 @@ static void VULKAN_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture); static void VULKAN_DestroyBuffer(VULKAN_RenderData *rendererData, VULKAN_Buffer *vulkanBuffer); static void VULKAN_DestroyImage(VULKAN_RenderData *rendererData, VULKAN_Image *vulkanImage); static void VULKAN_ResetCommandList(VULKAN_RenderData *rendererData); -static SDL_bool VULKAN_FindMemoryTypeIndex(VULKAN_RenderData *rendererData, uint32_t typeBits, VkMemoryPropertyFlags flags, uint32_t *memoryTypeIndexOut); +static SDL_bool VULKAN_FindMemoryTypeIndex(VULKAN_RenderData *rendererData, uint32_t typeBits, VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags desiredFlags, uint32_t *memoryTypeIndexOut); static VkResult VULKAN_CreateWindowSizeDependentResources(SDL_Renderer *renderer); static void VULKAN_DestroyAll(SDL_Renderer *renderer) @@ -578,7 +578,7 @@ static void VULKAN_DestroyBuffer(VULKAN_RenderData *rendererData, VULKAN_Buffer SDL_memset(vulkanBuffer, 0, sizeof(VULKAN_Buffer)); } -static VkResult VULKAN_AllocateBuffer(VULKAN_RenderData *rendererData, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags memoryProps, VULKAN_Buffer *bufferOut) +static VkResult VULKAN_AllocateBuffer(VULKAN_RenderData *rendererData, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags requiredMemoryProps, VkMemoryPropertyFlags desiredMemoryProps, VULKAN_Buffer *bufferOut) { VkResult result; VkBufferCreateInfo bufferCreateInfo = { 0 }; @@ -600,7 +600,7 @@ static VkResult VULKAN_AllocateBuffer(VULKAN_RenderData *rendererData, VkDeviceS } uint32_t memoryTypeIndex = 0; - if (!VULKAN_FindMemoryTypeIndex(rendererData, memoryRequirements.memoryTypeBits, memoryProps, &memoryTypeIndex)) { + if (!VULKAN_FindMemoryTypeIndex(rendererData, memoryRequirements.memoryTypeBits, requiredMemoryProps, desiredMemoryProps, &memoryTypeIndex)) { VULKAN_DestroyBuffer(rendererData, bufferOut); SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_FindMemoryTypeIndex failed.\n"); return VK_ERROR_UNKNOWN;; @@ -697,7 +697,7 @@ static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, uint32_t w } uint32_t memoryTypeIndex = 0; - if (!VULKAN_FindMemoryTypeIndex(rendererData, memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memoryTypeIndex)) { + if (!VULKAN_FindMemoryTypeIndex(rendererData, memoryRequirements.memoryTypeBits, 0, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memoryTypeIndex)) { VULKAN_DestroyImage(rendererData, imageOut); SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_FindMemoryTypeIndex failed.\n"); return VK_ERROR_UNKNOWN; @@ -1157,13 +1157,17 @@ static VULKAN_PipelineState *VULKAN_CreatePipelineState(SDL_Renderer *renderer, return &pipelineStates[rendererData->pipelineStateCount - 1]; } -static SDL_bool VULKAN_FindMemoryTypeIndex(VULKAN_RenderData *rendererData, uint32_t typeBits, VkMemoryPropertyFlags flags, uint32_t *memoryTypeIndexOut) +static SDL_bool VULKAN_FindMemoryTypeIndex(VULKAN_RenderData *rendererData, uint32_t typeBits, VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags desiredFlags, uint32_t *memoryTypeIndexOut) { uint32_t memoryTypeIndex = 0; SDL_bool foundExactMatch = SDL_FALSE; + + /* Desired flags must be a superset of required flags. */ + desiredFlags |= requiredFlags; + for (memoryTypeIndex = 0; memoryTypeIndex < rendererData->physicalDeviceMemoryProperties.memoryTypeCount; memoryTypeIndex++) { if (typeBits & (1 << memoryTypeIndex)) { - if (rendererData->physicalDeviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags == flags) { + if (rendererData->physicalDeviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags == desiredFlags) { foundExactMatch = SDL_TRUE; break; } @@ -1172,7 +1176,7 @@ static SDL_bool VULKAN_FindMemoryTypeIndex(VULKAN_RenderData *rendererData, uint if (!foundExactMatch) { for (memoryTypeIndex = 0; memoryTypeIndex < rendererData->physicalDeviceMemoryProperties.memoryTypeCount; memoryTypeIndex++) { if (typeBits & (1 << memoryTypeIndex)) { - if ((rendererData->physicalDeviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags & flags) == flags) { + if ((rendererData->physicalDeviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags & requiredFlags) == requiredFlags) { break; } } @@ -1195,9 +1199,9 @@ static VkResult VULKAN_CreateVertexBuffer(VULKAN_RenderData *rendererData, size_ result = VULKAN_AllocateBuffer(rendererData, size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &rendererData->vertexBuffers[vbidx]); if (result != VK_SUCCESS) { SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_AllocateBuffer(): %s\n", SDL_Vulkan_GetResultString(result)); @@ -2190,9 +2194,9 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) result = VULKAN_AllocateBuffer(rendererData, SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &rendererData->constantBuffers[i]); if (result != VK_SUCCESS) { VULKAN_DestroyAll(renderer); @@ -2488,9 +2492,9 @@ static VkResult VULKAN_UpdateTextureInternal(VULKAN_RenderData *rendererData, Vk result = VULKAN_AllocateBuffer(rendererData, uploadBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, uploadBuffer); if (result != VK_SUCCESS) { return result; @@ -2699,9 +2703,9 @@ static int VULKAN_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, result = VULKAN_AllocateBuffer(rendererData, stagingBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &textureData->stagingBuffer); if (result != VK_SUCCESS) { return SDL_SetError("[Vulkan] VULKAN_AllocateBuffer with result %s", SDL_Vulkan_GetResultString(result)); @@ -3539,9 +3543,9 @@ static SDL_Surface* VULKAN_RenderReadPixels(SDL_Renderer *renderer, const SDL_Re readbackBufferSize = length * rect->h; if (VULKAN_AllocateBuffer(rendererData, readbackBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT, - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &readbackBuffer) != VK_SUCCESS) { SDL_SetError("[Vulkan] Failed to allocate buffer for readback."); return NULL; From f9768816515e3c385c3b9812a3f6f64384f9dfa9 Mon Sep 17 00:00:00 2001 From: David Gow Date: Sat, 24 Feb 2024 22:06:38 +0800 Subject: [PATCH 113/220] Vulkan: Don't invalidate internal state in InvalidateCachedState The VULKAN_InvalidateCachedState() function seems to be meant to invalidate any _cached_ state, i.e. global state of the API which may have been modified outside the renderer. However, at the moment, the Vulkan renderer also resets a number of internal variables which track buffers, offsets, etc, in use. As a result, the renderer can get into an inconsistant state and/or lose data. For example, if VULKAN_InvalidateCachedState() is called in between two calls to VULKAN_UpdateVertexBuffer(), the data from the first call will be overwritten by that from the second, as the number of the next vertex buffer to use will be reset to 0. This can result in rendering errors, as the same vertex data is used incorrectly for several calls. By no longer resetting this 'internal' state here, those glitches disappear. However, I haven't tested this with any applications which mix the Vulkan renderer with their own Vulkan code (do any such applications exist?), so this may be insufficient in case a full flush of the renderer state -- and possibly a wait on the appropriate fence -- could be required. Signed-off-by: David Gow --- src/render/vulkan/SDL_render_vulkan.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index d374f4fb23b33..698e249bba352 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -3372,11 +3372,7 @@ static void VULKAN_InvalidateCachedState(SDL_Renderer *renderer) { VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; rendererData->currentPipelineState = NULL; - rendererData->currentVertexBuffer = 0; - rendererData->issueBatch = SDL_FALSE; rendererData->cliprectDirty = SDL_TRUE; - rendererData->currentDescriptorSetIndex = 0; - rendererData->currentConstantBufferOffset = 0; } static int VULKAN_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) From 35026cdcba90b297ccf064ca46957bd272d7e931 Mon Sep 17 00:00:00 2001 From: danginsburg Date: Mon, 26 Feb 2024 11:02:19 -0500 Subject: [PATCH 114/220] Vulkan Renderer - robustly handle running out of descriptor sets or constant buffer memory. Closes #9131. My previous implementation of descriptor set handling was naive - it attempted to do VULKAN_IssueBatch when running out of descriptor sets or constant buffer space. For one thing, this had a bug and wasn't working (causing the crash), but moreover it would have resulted in having to flush the GPU. Instead, make the descriptor pools and constant buffer mapped buffers be resizeable so that if we need more it will grow to the size that is needed. # Conflicts: # src/render/vulkan/SDL_render_vulkan.c --- src/render/vulkan/SDL_render_vulkan.c | 342 +++++++++++++++++--------- 1 file changed, 231 insertions(+), 111 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 698e249bba352..bce68ca9401f8 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -308,11 +308,15 @@ typedef struct int *currentUploadBuffer; /* Data for updating constants */ - VULKAN_Buffer *constantBuffers; + VULKAN_Buffer **constantBuffers; + uint32_t *numConstantBuffers; + uint32_t currentConstantBufferIndex; int32_t currentConstantBufferOffset; VkSampler samplers[SDL_VULKAN_NUM_SAMPLERS]; - VkDescriptorPool *descriptorPools; + VkDescriptorPool **descriptorPools; + uint32_t *numDescriptorPools; + uint32_t currentDescriptorPoolIndex; uint32_t currentDescriptorSetIndex; int pipelineStateCount; @@ -410,6 +414,7 @@ static void VULKAN_DestroyImage(VULKAN_RenderData *rendererData, VULKAN_Image *v static void VULKAN_ResetCommandList(VULKAN_RenderData *rendererData); static SDL_bool VULKAN_FindMemoryTypeIndex(VULKAN_RenderData *rendererData, uint32_t typeBits, VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags desiredFlags, uint32_t *memoryTypeIndexOut); static VkResult VULKAN_CreateWindowSizeDependentResources(SDL_Renderer *renderer); +static VkDescriptorPool VULKAN_AllocateDescriptorPool(VULKAN_RenderData *rendererData); static void VULKAN_DestroyAll(SDL_Renderer *renderer) { @@ -500,13 +505,17 @@ static void VULKAN_DestroyAll(SDL_Renderer *renderer) rendererData->commandPool = VK_NULL_HANDLE; } if (rendererData->descriptorPools) { + SDL_assert(rendererData->numDescriptorPools); for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { - if (rendererData->descriptorPools[i] != VK_NULL_HANDLE) { - vkDestroyDescriptorPool(rendererData->device, rendererData->descriptorPools[i], NULL); + for (uint32_t j = 0; j < rendererData->numDescriptorPools[i]; j++) { + if (rendererData->descriptorPools[i][j] != VK_NULL_HANDLE) { + vkDestroyDescriptorPool(rendererData->device, rendererData->descriptorPools[i][j], NULL); + } } + SDL_free(rendererData->descriptorPools[i]); } SDL_free(rendererData->descriptorPools); - rendererData->descriptorPools = NULL; + SDL_free(rendererData->numDescriptorPools); } for (uint32_t i = 0; i < NUM_SHADERS; i++) { if (rendererData->vertexShaderModules[i] != VK_NULL_HANDLE) { @@ -544,10 +553,15 @@ static void VULKAN_DestroyAll(SDL_Renderer *renderer) } if (rendererData->constantBuffers) { + SDL_assert(rendererData->numConstantBuffers); for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) { - VULKAN_DestroyBuffer(rendererData, &rendererData->constantBuffers[i]); + for (uint32_t j = 0; j < rendererData->numConstantBuffers[i]; j++) { + VULKAN_DestroyBuffer(rendererData, &rendererData->constantBuffers[i][j]); + } + SDL_free(rendererData->constantBuffers[i]); } SDL_free(rendererData->constantBuffers); + SDL_free(rendererData->numConstantBuffers); rendererData->constantBuffers = NULL; } @@ -899,7 +913,9 @@ static void VULKAN_WaitForGPU(VULKAN_RenderData *rendererData) static void VULKAN_ResetCommandList(VULKAN_RenderData *rendererData) { vkResetCommandBuffer(rendererData->currentCommandBuffer, 0); - vkResetDescriptorPool(rendererData->device, rendererData->descriptorPools[rendererData->currentCommandBufferIndex], 0); + for (uint32_t i = 0; i < rendererData->numDescriptorPools[rendererData->currentCommandBufferIndex]; i++) { + vkResetDescriptorPool(rendererData->device, rendererData->descriptorPools[rendererData->currentCommandBufferIndex][i], 0); + } VkCommandBufferBeginInfo beginInfo = { 0 }; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; @@ -911,7 +927,9 @@ static void VULKAN_ResetCommandList(VULKAN_RenderData *rendererData) rendererData->issueBatch = SDL_FALSE; rendererData->cliprectDirty = SDL_TRUE; rendererData->currentDescriptorSetIndex = 0; + rendererData->currentDescriptorPoolIndex = 0; rendererData->currentConstantBufferOffset = -1; + rendererData->currentConstantBufferIndex = 0; /* Release any upload buffers that were inflight */ for (int i = 0; i < rendererData->currentUploadBuffer[rendererData->currentCommandBufferIndex]; ++i) { @@ -2114,35 +2132,29 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) return result; } - /* Create descriptor pools */ - if (rendererData->descriptorPools) { + /* Create descriptor pools - start by allocating one per swapchain image, let it grow if more are needed */ + if (rendererData->descriptorPools) { + SDL_assert(rendererData->numDescriptorPools); for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { - if (rendererData->descriptorPools[i] != VK_NULL_HANDLE) { - vkDestroyDescriptorPool(rendererData->device, rendererData->descriptorPools[i], NULL); + for (uint32_t j = 0; j < rendererData->numDescriptorPools[i]; j++) { + if (rendererData->descriptorPools[i][j] != VK_NULL_HANDLE) { + vkDestroyDescriptorPool(rendererData->device, rendererData->descriptorPools[i][j], NULL); + } } + SDL_free(rendererData->descriptorPools[i]); } SDL_free(rendererData->descriptorPools); + SDL_free(rendererData->numDescriptorPools); } - rendererData->descriptorPools = SDL_calloc(sizeof(VkDescriptorPool), rendererData->swapchainImageCount); + rendererData->descriptorPools = SDL_calloc(sizeof(VkDescriptorPool*), rendererData->swapchainImageCount); + rendererData->numDescriptorPools = SDL_calloc(sizeof(uint32_t), rendererData->swapchainImageCount); for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { - VkDescriptorPoolSize descriptorPoolSizes[2]; - descriptorPoolSizes[0].descriptorCount = SDL_VULKAN_MAX_DESCRIPTOR_SETS; - descriptorPoolSizes[0].type = VK_DESCRIPTOR_TYPE_SAMPLER; - - /* Allocate enough to hold a maximum of each descriptor set having YUV textures */ - const int numTexturesPerYUV = 3; - descriptorPoolSizes[1].descriptorCount = SDL_VULKAN_MAX_DESCRIPTOR_SETS * numTexturesPerYUV; - descriptorPoolSizes[1].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - - VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = { 0 }; - descriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - descriptorPoolCreateInfo.poolSizeCount = SDL_arraysize(descriptorPoolSizes); - descriptorPoolCreateInfo.pPoolSizes = descriptorPoolSizes; - descriptorPoolCreateInfo.maxSets = SDL_VULKAN_MAX_DESCRIPTOR_SETS; - result = vkCreateDescriptorPool(rendererData->device, &descriptorPoolCreateInfo, NULL, &rendererData->descriptorPools[i]); + /* Start by just allocating one pool, it will grow if needed */ + rendererData->numDescriptorPools[i] = 1; + rendererData->descriptorPools[i] = SDL_calloc(sizeof(VkDescriptorPool), 1); + rendererData->descriptorPools[i][0] = VULKAN_AllocateDescriptorPool(rendererData); if (result != VK_SUCCESS) { VULKAN_DestroyAll(renderer); - SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateDescriptorPool(): %s\n", SDL_Vulkan_GetResultString(result)); return result; } } @@ -2184,20 +2196,30 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) /* Constant buffers */ if (rendererData->constantBuffers) { - for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { - VULKAN_DestroyBuffer(rendererData, &rendererData->constantBuffers[i]); + SDL_assert(rendererData->numConstantBuffers); + for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) { + for (uint32_t j = 0; j < rendererData->numConstantBuffers[i]; j++) { + VULKAN_DestroyBuffer(rendererData, &rendererData->constantBuffers[i][j]); + } + SDL_free(rendererData->constantBuffers[i]); } SDL_free(rendererData->constantBuffers); + SDL_free(rendererData->numConstantBuffers); + rendererData->constantBuffers = NULL; } - rendererData->constantBuffers = SDL_calloc(sizeof(VULKAN_Buffer), rendererData->swapchainImageCount); + rendererData->constantBuffers = SDL_calloc(sizeof(VULKAN_Buffer*), rendererData->swapchainImageCount); + rendererData->numConstantBuffers = SDL_calloc(sizeof(VULKAN_Buffer*), rendererData->swapchainImageCount); for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { + /* Start with just allocating one, will grow if needed */ + rendererData->numConstantBuffers[i] = 1; + rendererData->constantBuffers[i] = SDL_calloc(sizeof(VULKAN_Buffer), 1); result = VULKAN_AllocateBuffer(rendererData, SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - &rendererData->constantBuffers[i]); + &rendererData->constantBuffers[i][0]); if (result != VK_SUCCESS) { VULKAN_DestroyAll(renderer); SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_AllocateBuffer(): %s\n", SDL_Vulkan_GetResultString(result)); @@ -2205,6 +2227,7 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) } } rendererData->currentConstantBufferOffset = -1; + rendererData->currentConstantBufferIndex = 0; VULKAN_AcquireNextSwapchainImage(renderer); @@ -3083,6 +3106,140 @@ static void VULKAN_SetupShaderConstants(SDL_Renderer *renderer, const SDL_Render } } +static VkDescriptorPool VULKAN_AllocateDescriptorPool(VULKAN_RenderData *rendererData) +{ + VkDescriptorPool descriptorPool = VK_NULL_HANDLE; + VkDescriptorPoolSize descriptorPoolSizes[2]; + VkResult result; + descriptorPoolSizes[0].descriptorCount = SDL_VULKAN_MAX_DESCRIPTOR_SETS; + descriptorPoolSizes[0].type = VK_DESCRIPTOR_TYPE_SAMPLER; + + /* Allocate enough to hold a maximum of each descriptor set having YUV textures */ + const int numTexturesPerYUV = 3; + descriptorPoolSizes[1].descriptorCount = SDL_VULKAN_MAX_DESCRIPTOR_SETS * numTexturesPerYUV; + descriptorPoolSizes[1].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + + VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = { 0 }; + descriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descriptorPoolCreateInfo.poolSizeCount = SDL_arraysize(descriptorPoolSizes); + descriptorPoolCreateInfo.pPoolSizes = descriptorPoolSizes; + descriptorPoolCreateInfo.maxSets = SDL_VULKAN_MAX_DESCRIPTOR_SETS; + result = vkCreateDescriptorPool(rendererData->device, &descriptorPoolCreateInfo, NULL, &descriptorPool); + if (result != VK_SUCCESS) { + SDL_SetError("[Vulkan] Unable to allocate descriptor pool vkCreateDescrptorPool: %s.\n", SDL_Vulkan_GetResultString(result)); + return VK_NULL_HANDLE; + } + + return descriptorPool; +} + +static VkDescriptorSet VULKAN_AllocateDescriptorSet(SDL_Renderer *renderer, VULKAN_Shader shader, VkSampler sampler, VkBuffer constantBuffer, VkDeviceSize constantBufferOffset, int imageViewCount, VkImageView *imageViews) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + uint32_t currentDescriptorPoolIndex = rendererData->currentDescriptorPoolIndex; + VkDescriptorPool descriptorPool = rendererData->descriptorPools[rendererData->currentCommandBufferIndex][currentDescriptorPoolIndex]; + + VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = { 0 }; + descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + descriptorSetAllocateInfo.descriptorSetCount = 1; + descriptorSetAllocateInfo.descriptorPool = descriptorPool; + descriptorSetAllocateInfo.pSetLayouts = &rendererData->descriptorSetLayouts[shader]; + + VkDescriptorSet descriptorSet = VK_NULL_HANDLE; + VkResult result = (rendererData->currentDescriptorSetIndex >= SDL_VULKAN_MAX_DESCRIPTOR_SETS) ? VK_ERROR_OUT_OF_DEVICE_MEMORY : VK_SUCCESS; + if (result == VK_SUCCESS) { + result = vkAllocateDescriptorSets(rendererData->device, &descriptorSetAllocateInfo, &descriptorSet); + } + if (result != VK_SUCCESS) { + /* Out of descriptor sets in this pool - see if we have more pools allocated */ + currentDescriptorPoolIndex++; + if (currentDescriptorPoolIndex < rendererData->numDescriptorPools[rendererData->currentCommandBufferIndex]) { + descriptorPool = rendererData->descriptorPools[rendererData->currentCommandBufferIndex][currentDescriptorPoolIndex]; + descriptorSetAllocateInfo.descriptorPool = descriptorPool; + result = vkAllocateDescriptorSets(rendererData->device, &descriptorSetAllocateInfo, &descriptorSet); + if (result != VK_SUCCESS) { + /* This should not fail - we are allocating from the front of the descriptor set */ + SDL_SetError("[Vulkan] Unable to allocate descriptor set."); + return VK_NULL_HANDLE; + } + rendererData->currentDescriptorPoolIndex = currentDescriptorPoolIndex; + rendererData->currentDescriptorSetIndex = 0; + + } + /* We are out of pools, create a new one */ + else { + descriptorPool = VULKAN_AllocateDescriptorPool(rendererData); + if (descriptorPool == VK_NULL_HANDLE) { + /* SDL_SetError called in VULKAN_AllocateDescriptorPool if we failed to allocate a new pool */ + return VK_NULL_HANDLE; + } + rendererData->numDescriptorPools[rendererData->currentCommandBufferIndex]++; + VkDescriptorPool *descriptorPools = SDL_realloc(rendererData->descriptorPools[rendererData->currentCommandBufferIndex], + sizeof(VkDescriptorPool) * rendererData->numDescriptorPools[rendererData->currentCommandBufferIndex]); + descriptorPools[rendererData->numDescriptorPools[rendererData->currentCommandBufferIndex] - 1] = descriptorPool; + rendererData->descriptorPools[rendererData->currentCommandBufferIndex] = descriptorPools; + rendererData->currentDescriptorPoolIndex = currentDescriptorPoolIndex; + rendererData->currentDescriptorSetIndex = 0; + + /* Call recursively to allocate from the new pool */ + return VULKAN_AllocateDescriptorSet(renderer, shader, sampler, constantBuffer, constantBufferOffset, imageViewCount, imageViews); + } + } + rendererData->currentDescriptorSetIndex++; + VkDescriptorImageInfo samplerDescriptor = { 0 }; + samplerDescriptor.sampler = sampler; + + VkDescriptorImageInfo imageDescriptors[3]; + SDL_memset(imageDescriptors, 0, sizeof(imageDescriptors)); + VkDescriptorBufferInfo bufferDescriptor = { 0 }; + bufferDescriptor.buffer = constantBuffer; + bufferDescriptor.offset = constantBufferOffset; + bufferDescriptor.range = sizeof(PixelShaderConstants); + + VkWriteDescriptorSet descriptorWrites[5]; + SDL_memset(descriptorWrites, 0, sizeof(descriptorWrites)); + uint32_t descriptorCount = 1; /* Always have the uniform buffer */ + + descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[0].dstSet = descriptorSet; + descriptorWrites[0].dstBinding = 4; + descriptorWrites[0].dstArrayElement = 0; + descriptorWrites[0].descriptorCount = 1; + descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + descriptorWrites[0].pBufferInfo = &bufferDescriptor; + + if (sampler != VK_NULL_HANDLE) { + descriptorCount++; + descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[1].dstSet = descriptorSet; + descriptorWrites[1].dstBinding = 0; + descriptorWrites[1].dstArrayElement = 0; + descriptorWrites[1].descriptorCount = 1; + descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; + descriptorWrites[1].pImageInfo = &samplerDescriptor; + } + + uint32_t startImageViews = descriptorCount; + for (uint32_t i = 0; i < 3 && imageViewCount > 0; i++) { + descriptorCount++; + imageDescriptors[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + /* There are up to 3 images in the shader, if we haven't specified that many, duplicate the first + one. There is dynamic branching that determines how many actually get fetched, but we need + them all populated for validation. */ + imageDescriptors[i].imageView = (i < imageViewCount) ? imageViews[i] : imageViews[0]; + descriptorWrites[i+startImageViews].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrites[i+startImageViews].dstSet = descriptorSet; + descriptorWrites[i+startImageViews].dstBinding = 1 + i; + descriptorWrites[i+startImageViews].dstArrayElement = 0; + descriptorWrites[i+startImageViews].descriptorCount = 1; + descriptorWrites[i+startImageViews].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + descriptorWrites[i+startImageViews].pImageInfo = &imageDescriptors[i]; + } + vkUpdateDescriptorSets(rendererData->device, descriptorCount, descriptorWrites, 0, NULL); + + return descriptorSet; +} + static SDL_bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, VULKAN_Shader shader, const PixelShaderConstants *shader_constants, VkPrimitiveTopology topology, int imageViewCount, VkImageView *imageViews, VkSampler sampler, const Float4X4 *matrix, VULKAN_DrawStateCache *stateCache) @@ -3093,6 +3250,7 @@ static SDL_bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderComm const Float4X4 *newmatrix = matrix ? matrix : &rendererData->identity; SDL_bool updateConstants = SDL_FALSE; PixelShaderConstants solid_constants; + VkDescriptorSet descriptorSet; VkBuffer constantBuffer; VkDeviceSize constantBufferOffset; int i; @@ -3155,7 +3313,8 @@ static SDL_bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderComm VULKAN_SetupShaderConstants(renderer, cmd, NULL, &solid_constants); shader_constants = &solid_constants; } - constantBuffer = rendererData->constantBuffers[rendererData->currentCommandBufferIndex].buffer; + + constantBuffer = rendererData->constantBuffers[rendererData->currentCommandBufferIndex][rendererData->currentConstantBufferIndex].buffer; constantBufferOffset = (rendererData->currentConstantBufferOffset < 0) ? 0 : rendererData->currentConstantBufferOffset; if (updateConstants || SDL_memcmp(shader_constants, &rendererData->currentPipelineState->shader_constants, sizeof(*shader_constants)) != 0) { @@ -3173,94 +3332,55 @@ static SDL_bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderComm constantBufferOffset = rendererData->currentConstantBufferOffset; } - /* Upload constants to persistently mapped buffer */ - if (rendererData->currentConstantBufferOffset > SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE) { - VULKAN_IssueBatch(rendererData); + /* If we have run out of size in this constant buffer, create another if needed */ + if (rendererData->currentConstantBufferOffset >= SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE) { + uint32_t newConstantBufferIndex = (rendererData->currentConstantBufferIndex + 1); + /* We need a new constant buffer */ + if (newConstantBufferIndex >= rendererData->numConstantBuffers[rendererData->currentCommandBufferIndex]) { + VULKAN_Buffer newConstantBuffer; + VkResult result = VULKAN_AllocateBuffer(rendererData, + SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + &newConstantBuffer); + + if (result != VK_SUCCESS) { + SDL_SetError("[Vulkan] Could not allocate new memory for constant buffer.\n" ); + return SDL_FALSE; + } + + rendererData->numConstantBuffers[rendererData->currentCommandBufferIndex]++; + VULKAN_Buffer *newConstantBuffers = SDL_realloc(rendererData->constantBuffers[rendererData->currentCommandBufferIndex], + sizeof(VULKAN_Buffer) * rendererData->numConstantBuffers[rendererData->currentCommandBufferIndex]); + newConstantBuffers[rendererData->numConstantBuffers[rendererData->currentCommandBufferIndex] - 1] = newConstantBuffer; + rendererData->constantBuffers[rendererData->currentCommandBufferIndex] = newConstantBuffers; + } + rendererData->currentConstantBufferIndex = newConstantBufferIndex; rendererData->currentConstantBufferOffset = 0; constantBufferOffset = 0; + constantBuffer = rendererData->constantBuffers[rendererData->currentCommandBufferIndex][rendererData->currentConstantBufferIndex].buffer; } - uint8_t *dst = rendererData->constantBuffers[rendererData->currentCommandBufferIndex].mappedBufferPtr; + + /* Upload constants to persistently mapped buffer */ + uint8_t *dst = rendererData->constantBuffers[rendererData->currentCommandBufferIndex][rendererData->currentConstantBufferIndex].mappedBufferPtr; dst += constantBufferOffset; SDL_memcpy(dst, &rendererData->currentPipelineState->shader_constants, sizeof(PixelShaderConstants)); SDL_memcpy(&rendererData->currentPipelineState->shader_constants, shader_constants, sizeof(*shader_constants)); } - /* Allocate the descriptor set */ - { - VkDescriptorSetAllocateInfo descriptorSetAllocateInfo = { 0 }; - descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - descriptorSetAllocateInfo.descriptorSetCount = 1; - descriptorSetAllocateInfo.descriptorPool = rendererData->descriptorPools[rendererData->currentCommandBufferIndex]; - descriptorSetAllocateInfo.pSetLayouts = &rendererData->descriptorSetLayouts[shader]; - - VkDescriptorSet descriptorSet = VK_NULL_HANDLE; - VkResult result = (rendererData->currentDescriptorSetIndex >= SDL_VULKAN_MAX_DESCRIPTOR_SETS) ? VK_ERROR_OUT_OF_DEVICE_MEMORY : VK_SUCCESS; - if (result == VK_SUCCESS) { - result = vkAllocateDescriptorSets(rendererData->device, &descriptorSetAllocateInfo, &descriptorSet); - } - // Out of descriptor sets - if (result != VK_SUCCESS) { - VULKAN_IssueBatch(rendererData); - result = vkAllocateDescriptorSets(rendererData->device, &descriptorSetAllocateInfo, &descriptorSet); - if (result != VK_SUCCESS) { - SDL_SetError("[Vulkan] Unable to allocate descriptor set."); - } - } - rendererData->currentDescriptorSetIndex++; - VkDescriptorImageInfo samplerDescriptor = { 0 }; - samplerDescriptor.sampler = sampler; - - VkDescriptorImageInfo imageDescriptors[3]; - SDL_memset(imageDescriptors, 0, sizeof(imageDescriptors)); - VkDescriptorBufferInfo bufferDescriptor = { 0 }; - bufferDescriptor.buffer = constantBuffer; - bufferDescriptor.offset = constantBufferOffset; - bufferDescriptor.range = sizeof(PixelShaderConstants); - - VkWriteDescriptorSet descriptorWrites[5]; - SDL_memset(descriptorWrites, 0, sizeof(descriptorWrites)); - uint32_t descriptorCount = 1; /* Always have the uniform buffer */ - - descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrites[0].dstSet = descriptorSet; - descriptorWrites[0].dstBinding = 4; - descriptorWrites[0].dstArrayElement = 0; - descriptorWrites[0].descriptorCount = 1; - descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - descriptorWrites[0].pBufferInfo = &bufferDescriptor; - - if (sampler != VK_NULL_HANDLE) { - descriptorCount++; - descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrites[1].dstSet = descriptorSet; - descriptorWrites[1].dstBinding = 0; - descriptorWrites[1].dstArrayElement = 0; - descriptorWrites[1].descriptorCount = 1; - descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; - descriptorWrites[1].pImageInfo = &samplerDescriptor; - } - - uint32_t startImageViews = descriptorCount; - for (i = 0; i < 3 && imageViewCount > 0; i++) { - descriptorCount++; - imageDescriptors[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - /* There are up to 3 images in the shader, if we haven't specified that many, duplicate the first - one. There is dynamic branching that determines how many actually get fetched, but we need - them all populated for validation. */ - imageDescriptors[i].imageView = (i < imageViewCount) ? imageViews[i] : imageViews[0]; - descriptorWrites[i+startImageViews].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrites[i+startImageViews].dstSet = descriptorSet; - descriptorWrites[i+startImageViews].dstBinding = 1 + i; - descriptorWrites[i+startImageViews].dstArrayElement = 0; - descriptorWrites[i+startImageViews].descriptorCount = 1; - descriptorWrites[i+startImageViews].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - descriptorWrites[i+startImageViews].pImageInfo = &imageDescriptors[i]; - } - vkUpdateDescriptorSets(rendererData->device, descriptorCount, descriptorWrites, 0, NULL); - vkCmdBindDescriptorSets(rendererData->currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, rendererData->currentPipelineState->pipelineLayout, - 0, 1, &descriptorSet, 0, NULL); + /* Allocate/update descriptor set with the bindings */ + descriptorSet = VULKAN_AllocateDescriptorSet(renderer, shader, sampler, constantBuffer, constantBufferOffset, imageViewCount, imageViews); + if (descriptorSet == VK_NULL_HANDLE) { + return SDL_FALSE; } + + /* Bind the descriptor set with the sampler/UBO/image views */ + vkCmdBindDescriptorSets(rendererData->currentCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, rendererData->currentPipelineState->pipelineLayout, + 0, 1, &descriptorSet, 0, NULL); + return SDL_TRUE; } From e61dfe405f338d4728efdff877124bb064777f4c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Mon, 26 Feb 2024 11:28:11 -0500 Subject: [PATCH 115/220] android: Fixed dead URL in comment. --- src/core/android/SDL_android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c index a8a9cddb731f3..dd53f687b6855 100644 --- a/src/core/android/SDL_android.c +++ b/src/core/android/SDL_android.c @@ -820,7 +820,7 @@ JNIEXPORT int JNICALL SDL_JAVA_INTERFACE(nativeRunMain)(JNIEnv *env, jclass cls, argv = SDL_small_alloc(char *, 1 + len + 1, &isstack); /* !!! FIXME: check for NULL */ argc = 0; /* Use the name "app_process" so PHYSFS_platformCalcBaseDir() works. - https://bitbucket.org/MartinFelis/love-android-sdl2/issue/23/release-build-crash-on-start + https://github.com/love2d/love-android/issues/24 */ argv[argc++] = SDL_strdup("app_process"); for (i = 0; i < len; ++i) { From 935c197059e21736019ce9d559b76ed317a76000 Mon Sep 17 00:00:00 2001 From: danginsburg Date: Mon, 26 Feb 2024 11:48:10 -0500 Subject: [PATCH 116/220] Fix testautomation failures (including clip rect) - closes #9145. During merging for prep'ing the final PR for the Vulkan Renderer, I misordered a memcpy that regressed several of the testautomation test. From now on, I will make sure to run testautomation on any future PRs before submitting. --- src/render/vulkan/SDL_render_vulkan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index bce68ca9401f8..0691dfd0913cb 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -3363,12 +3363,12 @@ static SDL_bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderComm constantBuffer = rendererData->constantBuffers[rendererData->currentCommandBufferIndex][rendererData->currentConstantBufferIndex].buffer; } + SDL_memcpy(&rendererData->currentPipelineState->shader_constants, shader_constants, sizeof(*shader_constants)); + /* Upload constants to persistently mapped buffer */ uint8_t *dst = rendererData->constantBuffers[rendererData->currentCommandBufferIndex][rendererData->currentConstantBufferIndex].mappedBufferPtr; dst += constantBufferOffset; SDL_memcpy(dst, &rendererData->currentPipelineState->shader_constants, sizeof(PixelShaderConstants)); - - SDL_memcpy(&rendererData->currentPipelineState->shader_constants, shader_constants, sizeof(*shader_constants)); } /* Allocate/update descriptor set with the bindings */ From 1f536a1e7799ce44e5e1fb2e41e5fb543435fce7 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Mon, 26 Feb 2024 20:37:07 +0100 Subject: [PATCH 117/220] cmake: fix SDL_RENDER_VULKAN reporting Also fix an error when configuring with `-DSDL_VULKAN=ON -DSDL_RENDER_VULKAN=FALSE`: the vulkan renderer is now correctly disabled. --- CMakeLists.txt | 2 ++ cmake/sdlchecks.cmake | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f504f933f8347..8f3bd5e27192d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2010,6 +2010,7 @@ elseif(WINDOWS) set(HAVE_VULKAN TRUE) if(SDL_RENDER_VULKAN) set(SDL_VIDEO_RENDER_VULKAN 1) + set(HAVE_RENDER_VULKAN TRUE) endif() endif() endif() @@ -2254,6 +2255,7 @@ elseif(APPLE) set(HAVE_VULKAN TRUE) if(SDL_RENDER_VULKAN) set(SDL_VIDEO_RENDER_VULKAN 1) + set(HAVE_RENDER_VULKAN TRUE) endif() endif() if(SDL_METAL) diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake index 4cf36f3e8a30e..7fae2524b4e17 100644 --- a/cmake/sdlchecks.cmake +++ b/cmake/sdlchecks.cmake @@ -725,7 +725,10 @@ macro(CheckVulkan) if(SDL_VULKAN) set(SDL_VIDEO_VULKAN 1) set(HAVE_VULKAN TRUE) - set(SDL_VIDEO_RENDER_VULKAN 1) + if(SDL_RENDER_VULKAN) + set(SDL_VIDEO_RENDER_VULKAN 1) + set(HAVE_RENDER_VULKAN TRUE) + endif() endif() endmacro() From 98b1a59a9598b803e15937aa5dcf8c61baabdddc Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 26 Feb 2024 14:16:41 -0800 Subject: [PATCH 118/220] Document the HDR tone mapping algorithm --- src/video/SDL_blit_slow.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/video/SDL_blit_slow.c b/src/video/SDL_blit_slow.c index d16be66e118b9..9bd59a752af36 100644 --- a/src/video/SDL_blit_slow.c +++ b/src/video/SDL_blit_slow.c @@ -699,6 +699,17 @@ static void TonemapLinear(float *r, float *g, float *b, float scale) *b *= scale; } +/* This uses the same tonemapping algorithm developed by Google for Chrome: + * https://colab.research.google.com/drive/1hI10nq6L6ru_UFvz7-f7xQaQp0qarz_K + * + * Essentially, you use the source headroom and the destination headroom + * to calculate scaling factors: + * tonemap_a = (dst_headroom / (src_headroom * src_headroom)); + * tonemap_b = (1.0f / dst_headroom); + * + * Then you normalize your source color by the HDR whitepoint, + * and calculate a final scaling factor in BT.2020 colorspace. + */ static void TonemapChrome(float *r, float *g, float *b, float tonemap_a, float tonemap_b) { float v1 = *r; From dc9a3c83e25520cbe8da3b338decbe74be6e4fab Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 26 Feb 2024 14:20:11 -0800 Subject: [PATCH 119/220] Use the mastering display metadata to do proper tone mapping for HDR content --- test/testffmpeg.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/test/testffmpeg.c b/test/testffmpeg.c index d151ff05b0193..e4c6727968ed1 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -640,6 +640,7 @@ static SDL_bool GetTextureForD3D11Frame(AVFrame *frame, SDL_Texture **texture) } if (!*texture || (UINT)texture_width != desc.Width || (UINT)texture_height != desc.Height) { Uint32 format; + AVFrameSideData *pSideData; switch (desc.Format) { case DXGI_FORMAT_NV12: @@ -659,6 +660,16 @@ static SDL_bool GetTextureForD3D11Frame(AVFrame *frame, SDL_Texture **texture) SDL_PropertiesID props = SDL_CreateProperties(); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, GetFrameColorspace(frame)); + pSideData = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + if (pSideData) { + /* ITU-R BT.2408-6 recommends using an SDR white point of 203 nits, which is more likely for game content */ + static const float k_flSDRWhitePoint = 203.0f; + + AVMasteringDisplayMetadata *pMasteringDisplayMetadata = (AVMasteringDisplayMetadata *)pSideData->data; + float flMaxLuminance = (float)pMasteringDisplayMetadata->max_luminance.num / pMasteringDisplayMetadata->max_luminance.den; + SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, k_flSDRWhitePoint); + SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, flMaxLuminance / k_flSDRWhitePoint); + } SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, desc.Width); @@ -692,6 +703,7 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex size_t nPixelBufferHeight = CVPixelBufferGetHeightOfPlane(pPixelBuffer, 0); SDL_PropertiesID props; Uint32 format; + AVFrameSideData *pSideData; switch (nPixelBufferType) { case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: @@ -719,6 +731,16 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex props = SDL_CreateProperties(); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, GetFrameColorspace(frame)); + pSideData = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + if (pSideData) { + /* ITU-R BT.2408-6 recommends using an SDR white point of 203 nits, which is more likely for game content */ + static const float k_flSDRWhitePoint = 203.0f; + + AVMasteringDisplayMetadata *pMasteringDisplayMetadata = (AVMasteringDisplayMetadata *)pSideData->data; + float flMaxLuminance = (float)pMasteringDisplayMetadata->max_luminance.num / pMasteringDisplayMetadata->max_luminance.den; + SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, k_flSDRWhitePoint); + SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, flMaxLuminance / k_flSDRWhitePoint); + } SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, nPixelBufferWidth); @@ -754,20 +776,6 @@ static SDL_bool GetTextureForFrame(AVFrame *frame, SDL_Texture **texture) static void DisplayVideoTexture(AVFrame *frame) { -#if 0 /* This data doesn't seem to be valid in any of the videos I've tried */ - AVFrameSideData *sd = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); - if (sd) { - AVMasteringDisplayMetadata *mdm = (AVMasteringDisplayMetadata *)sd->data; - mdm = mdm; - } - - sd = av_frame_get_side_data(frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); - if (sd) { - AVContentLightMetadata *clm = (AVContentLightMetadata *)sd->data; - clm = clm; - } -#endif /* 0 */ - /* Update the video texture */ if (!GetTextureForFrame(frame, &video_texture)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't get texture for frame: %s\n", SDL_GetError()); From 3a796c97227b6d3823e507951e2acab350153e72 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 26 Feb 2024 15:13:10 -0800 Subject: [PATCH 120/220] Allow specifying the render driver in the environment for testffmpeg --- test/testffmpeg.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/testffmpeg.c b/test/testffmpeg.c index e4c6727968ed1..8acdc927f9304 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -1016,6 +1016,9 @@ int main(int argc, char *argv[]) #elif !defined(SDL_PLATFORM_WIN32) window_flags |= SDL_WINDOW_OPENGL; #endif + if (SDL_GetHint(SDL_HINT_RENDER_DRIVER) != NULL) { + CreateWindowAndRenderer(window_flags, SDL_GetHint(SDL_HINT_RENDER_DRIVER)); + } #ifdef HAVE_EGL /* Try to create an EGL compatible window for DRM hardware frame support */ if (!window) { From 80d2ef7384b6cbf1d5974e3d671f2a7d880c006c Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 26 Feb 2024 15:18:23 -0800 Subject: [PATCH 121/220] Fixed uploading Vulkan texture with w*bpp != pitch --- src/render/vulkan/SDL_render_vulkan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 0691dfd0913cb..c5cf45f3f00d8 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -2534,7 +2534,7 @@ static VkResult VULKAN_UpdateTextureInternal(VULKAN_RenderData *rendererData, Vk for (VkDeviceSize row = h; row--; ) { SDL_memcpy(dst, src, length); src += pitch; - dst += pitch; + dst += length; } } From f99143f4374377060c21d85e47d93719155df36a Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 26 Feb 2024 15:20:56 -0800 Subject: [PATCH 122/220] Don't quit testffmpeg when alt-tabbing away --- test/testffmpeg.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/testffmpeg.c b/test/testffmpeg.c index 8acdc927f9304..b801d2d484005 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -1144,7 +1144,8 @@ int main(int argc, char *argv[]) /* Check for events */ while (SDL_PollEvent(&event)) { - if (event.type == SDL_EVENT_QUIT || event.type == SDL_EVENT_KEY_DOWN) { + if (event.type == SDL_EVENT_QUIT || + (event.type == SDL_EVENT_KEY_DOWN && event.key.keysym.sym == SDLK_ESCAPE)) { done = 1; } } From 81608ad077f3d601b255a6cb9220f5f89f7de523 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 26 Feb 2024 15:51:13 -0800 Subject: [PATCH 123/220] Vulkan: fixed creating SDL_PIXELFORMAT_P010 textures --- src/render/vulkan/SDL_render_vulkan.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index c5cf45f3f00d8..d5564cfd33ca1 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -404,6 +404,8 @@ static VkFormat SDLPixelFormatToVkTextureFormat(Uint32 format, Uint32 colorspace case SDL_PIXELFORMAT_NV12: /* Y plane */ case SDL_PIXELFORMAT_NV21: /* Y plane */ return VK_FORMAT_R8_UNORM; + case SDL_PIXELFORMAT_P010: + return VK_FORMAT_R16_UNORM; default: return VK_FORMAT_UNDEFINED; } @@ -2408,7 +2410,7 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD texture->format == SDL_PIXELFORMAT_P010) { int bits_per_pixel; VkFormat uvFormat = VK_FORMAT_R8G8_UNORM; - if (texture->format == SDL_PIXELFORMAT_P010 || texture->format == SDL_PIXELFORMAT_P016) { + if (texture->format == SDL_PIXELFORMAT_P010) { uvFormat = VK_FORMAT_R16G16_UNORM; } textureData->nv12 = SDL_TRUE; @@ -3220,7 +3222,7 @@ static VkDescriptorSet VULKAN_AllocateDescriptorSet(SDL_Renderer *renderer, VULK } uint32_t startImageViews = descriptorCount; - for (uint32_t i = 0; i < 3 && imageViewCount > 0; i++) { + for (int i = 0; i < 3 && imageViewCount > 0; i++) { descriptorCount++; imageDescriptors[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; /* There are up to 3 images in the shader, if we haven't specified that many, duplicate the first From ff18d7cfa065491d5f648d554ee8af24e5c55474 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 26 Feb 2024 15:52:21 -0800 Subject: [PATCH 124/220] testffmpeg: added pixel format mapping for NV12 and NV21 --- test/testffmpeg.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/testffmpeg.c b/test/testffmpeg.c index b801d2d484005..cbd106d83c102 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -259,6 +259,10 @@ static Uint32 GetTextureFormat(enum AVPixelFormat format) return SDL_PIXELFORMAT_YUY2; case AV_PIX_FMT_UYVY422: return SDL_PIXELFORMAT_UYVY; + case AV_PIX_FMT_NV12: + return SDL_PIXELFORMAT_NV12; + case AV_PIX_FMT_NV21: + return SDL_PIXELFORMAT_NV21; default: return SDL_PIXELFORMAT_UNKNOWN; } From 24e021c67eceb047659dcbfe41876f2869ad16e5 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 26 Feb 2024 16:01:23 -0800 Subject: [PATCH 125/220] testffmpeg: refactored texture creation properties into a single function --- test/testffmpeg.c | 79 ++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 46 deletions(-) diff --git a/test/testffmpeg.c b/test/testffmpeg.c index cbd106d83c102..1770dca4506fb 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -432,6 +432,33 @@ static SDL_Colorspace GetFrameColorspace(AVFrame *frame) return colorspace; } +static SDL_PropertiesID CreateVideoTextureProperties(AVFrame *frame, Uint32 format, int access, int w, int h) +{ + AVFrameSideData *pSideData; + SDL_PropertiesID props; + + props = SDL_CreateProperties(); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, GetFrameColorspace(frame)); + pSideData = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + if (pSideData) { + /* ITU-R BT.2408-6 recommends using an SDR white point of 203 nits, which is more likely for game content */ + static const float k_flSDRWhitePoint = 203.0f; + + AVMasteringDisplayMetadata *pMasteringDisplayMetadata = (AVMasteringDisplayMetadata *)pSideData->data; + float flMaxLuminance = (float)pMasteringDisplayMetadata->max_luminance.num / pMasteringDisplayMetadata->max_luminance.den; + if (flMaxLuminance > k_flSDRWhitePoint) { + SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, k_flSDRWhitePoint); + SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, flMaxLuminance / k_flSDRWhitePoint); + } + } + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, access); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, w); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, h); + + return props; +} + static void SDLCALL FreeSwsContextContainer(void *userdata, void *value) { struct SwsContextContainer *sws_container = (struct SwsContextContainer *)value; @@ -457,16 +484,12 @@ static SDL_bool GetTextureForMemoryFrame(AVFrame *frame, SDL_Texture **texture) SDL_DestroyTexture(*texture); } - SDL_PropertiesID props = SDL_CreateProperties(); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, GetFrameColorspace(frame)); + SDL_PropertiesID props; if (frame_format == SDL_PIXELFORMAT_UNKNOWN) { - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, SDL_PIXELFORMAT_ARGB8888); + props = CreateVideoTextureProperties(frame, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, frame->width, frame->height); } else { - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, frame_format); + props = CreateVideoTextureProperties(frame, frame_format, SDL_TEXTUREACCESS_STREAMING, frame->width, frame->height); } - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STREAMING); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, frame->width); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, frame->height); *texture = SDL_CreateTextureWithProperties(renderer, props); SDL_DestroyProperties(props); if (!*texture) { @@ -556,12 +579,7 @@ static SDL_bool GetTextureForDRMFrame(AVFrame *frame, SDL_Texture **texture) SDL_SetHint("SDL_RENDER_OPENGL_NV12_RG_SHADER", "1"); } - props = SDL_CreateProperties(); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, GetFrameColorspace(frame)); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, SDL_PIXELFORMAT_NV12); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, frame->width); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, frame->height); + props = CreateVideoTextureProperties(frame, SDL_PIXELFORMAT_NV12, SDL_TEXTUREACCESS_STATIC, frame->width, frame->height); *texture = SDL_CreateTextureWithProperties(renderer, props); SDL_DestroyProperties(props); if (!*texture) { @@ -644,7 +662,6 @@ static SDL_bool GetTextureForD3D11Frame(AVFrame *frame, SDL_Texture **texture) } if (!*texture || (UINT)texture_width != desc.Width || (UINT)texture_height != desc.Height) { Uint32 format; - AVFrameSideData *pSideData; switch (desc.Format) { case DXGI_FORMAT_NV12: @@ -662,21 +679,7 @@ static SDL_bool GetTextureForD3D11Frame(AVFrame *frame, SDL_Texture **texture) SDL_DestroyTexture(*texture); } - SDL_PropertiesID props = SDL_CreateProperties(); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, GetFrameColorspace(frame)); - pSideData = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); - if (pSideData) { - /* ITU-R BT.2408-6 recommends using an SDR white point of 203 nits, which is more likely for game content */ - static const float k_flSDRWhitePoint = 203.0f; - - AVMasteringDisplayMetadata *pMasteringDisplayMetadata = (AVMasteringDisplayMetadata *)pSideData->data; - float flMaxLuminance = (float)pMasteringDisplayMetadata->max_luminance.num / pMasteringDisplayMetadata->max_luminance.den; - SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, k_flSDRWhitePoint); - SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, flMaxLuminance / k_flSDRWhitePoint); - } - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, desc.Width); + SDL_PropertiesID props = CreateVideoTextureProperties(frame, format, SDL_TEXTUREACCESS_STATIC, desc.Width, desc.Height); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, desc.Height); *texture = SDL_CreateTextureWithProperties(renderer, props); SDL_DestroyProperties(props); @@ -707,7 +710,6 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex size_t nPixelBufferHeight = CVPixelBufferGetHeightOfPlane(pPixelBuffer, 0); SDL_PropertiesID props; Uint32 format; - AVFrameSideData *pSideData; switch (nPixelBufferType) { case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: @@ -733,22 +735,7 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex SDL_DestroyTexture(*texture); } - props = SDL_CreateProperties(); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, GetFrameColorspace(frame)); - pSideData = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); - if (pSideData) { - /* ITU-R BT.2408-6 recommends using an SDR white point of 203 nits, which is more likely for game content */ - static const float k_flSDRWhitePoint = 203.0f; - - AVMasteringDisplayMetadata *pMasteringDisplayMetadata = (AVMasteringDisplayMetadata *)pSideData->data; - float flMaxLuminance = (float)pMasteringDisplayMetadata->max_luminance.num / pMasteringDisplayMetadata->max_luminance.den; - SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, k_flSDRWhitePoint); - SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, flMaxLuminance / k_flSDRWhitePoint); - } - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STATIC); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, nPixelBufferWidth); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, nPixelBufferHeight); + props = CreateVideoTextureProperties(frame, format, SDL_TEXTUREACCESS_STATIC, nPixelBufferWidth, nPixelBufferHeight); SDL_SetProperty(props, SDL_PROP_TEXTURE_CREATE_METAL_PIXELBUFFER_POINTER, pPixelBuffer); *texture = SDL_CreateTextureWithProperties(renderer, props); SDL_DestroyProperties(props); From 0a961915dc2379aa9c28db02e160314584b4c305 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Tue, 27 Feb 2024 12:28:06 +0100 Subject: [PATCH 126/220] cmake: testffmpeg requires link to EGL library for EGL feature --- test/CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e7c12e30cb60a..d09c81e0666f8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -232,12 +232,10 @@ if(FFmpeg_FOUND AND LIBAVUTIL_AVFRAME_HAS_CH_LAYOUT) if(APPLE) target_link_options(testffmpeg PRIVATE "-Wl,-framework,CoreVideo") endif() - if(HAVE_OPENGLES_V2) + if(TARGET OpenGL::EGL) message(DEBUG "Enabling EGL support in testffmpeg") + target_link_libraries(testffmpeg PRIVATE OpenGL::EGL) target_compile_definitions(testffmpeg PRIVATE HAVE_EGL) - if(TARGET OpenGL::EGL) - target_link_libraries(testffmpeg PRIVATE OpenGL::EGL) - endif() endif() target_link_libraries(testffmpeg PRIVATE ${FFMPEG_LIBRARIES}) else() From 84aaf63bd31eebdc071877f1f949fd2bba9e80a1 Mon Sep 17 00:00:00 2001 From: Semphriss <66701383+Semphriss@users.noreply.github.com> Date: Tue, 27 Feb 2024 10:44:09 -0500 Subject: [PATCH 127/220] Fix typo in SDL_filesystem.h --- include/SDL3/SDL_filesystem.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/SDL3/SDL_filesystem.h b/include/SDL3/SDL_filesystem.h index 8ee4d2c27a646..8030c04354a78 100644 --- a/include/SDL3/SDL_filesystem.h +++ b/include/SDL3/SDL_filesystem.h @@ -206,8 +206,8 @@ typedef enum } SDL_Folder; /** - * Finds the most suitable user folder for @p purpose, and returns its path in - * OS-specific notation. + * Finds the most suitable user folder for the specified purpose, and returns + * its path in OS-specific notation. * * Many OSes provide certain standard folders for certain purposes, such as * storing pictures, music or videos for a certain user. This function gives From c259a20f9674610f9537fa90b8e9d42de49fb5c0 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Tue, 27 Feb 2024 12:01:19 -0500 Subject: [PATCH 128/220] wayland: Remove all references to destroyed outputs from windows The removal of a wl_output may not be accompanied by leave events for the surfaces present on it. Ensure that no window continues to hold a reference to a removed output. --- src/video/wayland/SDL_waylandvideo.c | 7 ++++ src/video/wayland/SDL_waylandwindow.c | 60 +++++++++++++++------------ src/video/wayland/SDL_waylandwindow.h | 2 + 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index 0ba46f43e18ee..bd7fd44f407df 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -947,6 +947,13 @@ static void Wayland_free_display(SDL_VideoDisplay *display) if (display) { SDL_DisplayData *display_data = display->driverdata; + /* A preceding surface leave event is not guaranteed when an output is removed, + * so ensure that no window continues to hold a reference to a removed output. + */ + for (SDL_Window *window = SDL_GetVideoDevice()->windows; window; window = window->next) { + Wayland_RemoveOutputFromWindow(window->driverdata, display_data->output); + } + SDL_free(display_data->wl_output_name); if (display_data->xdg_output) { diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index 09f1963d11d59..1aa64b22a3019 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -1244,7 +1244,7 @@ static void Wayland_MaybeUpdateScaleFactor(SDL_WindowData *window) factor = SDL_max(factor, driverdata->scale_factor); } } else { - /* No monitor (somehow)? Just fall back. */ + /* All outputs removed, just fall back. */ factor = window->windowed_scale_factor; } @@ -1304,6 +1304,37 @@ static void Wayland_move_window(SDL_Window *window, SDL_DisplayData *driverdata) } } +void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, struct wl_output *output) +{ + int i, send_move_event = 0; + SDL_DisplayData *driverdata = wl_output_get_user_data(output); + + for (i = 0; i < window->num_outputs; i++) { + if (window->outputs[i] == driverdata) { /* remove this one */ + if (i == (window->num_outputs - 1)) { + window->outputs[i] = NULL; + send_move_event = 1; + } else { + SDL_memmove(&window->outputs[i], + &window->outputs[i + 1], + sizeof(SDL_DisplayData *) * ((window->num_outputs - i) - 1)); + } + window->num_outputs--; + i--; + } + } + + if (window->num_outputs == 0) { + SDL_free(window->outputs); + window->outputs = NULL; + } else if (send_move_event) { + Wayland_move_window(window->sdlwindow, + window->outputs[window->num_outputs - 1]); + } + + Wayland_MaybeUpdateScaleFactor(window); +} + static void handle_surface_enter(void *data, struct wl_surface *surface, struct wl_output *output) { @@ -1332,37 +1363,12 @@ static void handle_surface_leave(void *data, struct wl_surface *surface, struct wl_output *output) { SDL_WindowData *window = data; - int i, send_move_event = 0; - SDL_DisplayData *driverdata = wl_output_get_user_data(output); if (!SDL_WAYLAND_own_output(output) || !SDL_WAYLAND_own_surface(surface)) { return; } - for (i = 0; i < window->num_outputs; i++) { - if (window->outputs[i] == driverdata) { /* remove this one */ - if (i == (window->num_outputs - 1)) { - window->outputs[i] = NULL; - send_move_event = 1; - } else { - SDL_memmove(&window->outputs[i], - &window->outputs[i + 1], - sizeof(SDL_DisplayData *) * ((window->num_outputs - i) - 1)); - } - window->num_outputs--; - i--; - } - } - - if (window->num_outputs == 0) { - SDL_free(window->outputs); - window->outputs = NULL; - } else if (send_move_event) { - Wayland_move_window(window->sdlwindow, - window->outputs[window->num_outputs - 1]); - } - - Wayland_MaybeUpdateScaleFactor(window); + Wayland_RemoveOutputFromWindow(window, output); } static void handle_preferred_buffer_scale(void *data, struct wl_surface *wl_surface, int32_t factor) diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index 1d045f3e9d194..cf8e7ea2a8887 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -206,4 +206,6 @@ extern int Wayland_SetWindowHitTest(SDL_Window *window, SDL_bool enabled); extern int Wayland_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation); extern int Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window); +extern void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, struct wl_output *output); + #endif /* SDL_waylandwindow_h_ */ From 220340e944399a37bb6d84e727a6b27082700cf2 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 27 Feb 2024 12:26:09 -0800 Subject: [PATCH 129/220] Remove SDL_PIXELFORMAT_P010 It's not supported by any renderer or pixel conversion path --- include/SDL3/SDL_pixels.h | 5 +---- src/video/SDL_pixels.c | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/include/SDL3/SDL_pixels.h b/include/SDL3/SDL_pixels.h index e08ac6315431d..0d0325a31c054 100644 --- a/include/SDL3/SDL_pixels.h +++ b/include/SDL3/SDL_pixels.h @@ -166,8 +166,7 @@ typedef enum ((((X) == SDL_PIXELFORMAT_YUY2) || \ ((X) == SDL_PIXELFORMAT_UYVY) || \ ((X) == SDL_PIXELFORMAT_YVYU) || \ - ((X) == SDL_PIXELFORMAT_P010) || \ - ((X) == SDL_PIXELFORMAT_P016)) ? 2 : 1) : (((X) >> 0) & 0xFF)) + ((X) == SDL_PIXELFORMAT_P010)) ? 2 : 1) : (((X) >> 0) & 0xFF)) #define SDL_ISPIXELFORMAT_INDEXED(format) \ (!SDL_ISPIXELFORMAT_FOURCC(format) && \ @@ -418,8 +417,6 @@ typedef enum SDL_DEFINE_PIXELFOURCC('N', 'V', '2', '1'), SDL_PIXELFORMAT_P010 = /**< Planar mode: Y + U/V interleaved (2 planes) */ SDL_DEFINE_PIXELFOURCC('P', '0', '1', '0'), - SDL_PIXELFORMAT_P016 = /**< Planar mode: Y + U/V interleaved (2 planes) */ - SDL_DEFINE_PIXELFOURCC('P', '0', '1', '6'), SDL_PIXELFORMAT_EXTERNAL_OES = /**< Android video texture format */ SDL_DEFINE_PIXELFOURCC('O', 'E', 'S', ' ') } SDL_PixelFormatEnum; diff --git a/src/video/SDL_pixels.c b/src/video/SDL_pixels.c index 58d2064d1a112..9de8dd81a22dd 100644 --- a/src/video/SDL_pixels.c +++ b/src/video/SDL_pixels.c @@ -149,7 +149,6 @@ const char *SDL_GetPixelFormatName(Uint32 format) CASE(SDL_PIXELFORMAT_NV12) CASE(SDL_PIXELFORMAT_NV21) CASE(SDL_PIXELFORMAT_P010) - CASE(SDL_PIXELFORMAT_P016) CASE(SDL_PIXELFORMAT_EXTERNAL_OES) default: From c8372e20d6cba4a6caec20442f76b64464717019 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Tue, 27 Feb 2024 11:21:57 +0100 Subject: [PATCH 130/220] SDLTest_CommonEvent: only set done when it is finished --- src/test/SDL_test_common.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index 229862afd36ab..afbe7f5c8c92f 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -2429,7 +2429,9 @@ int SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const SDL_Event void SDLTest_CommonEvent(SDLTest_CommonState *state, SDL_Event *event, int *done) { - *done = SDLTest_CommonEventMainCallbacks(state, event) ? 1 : 0; + if (SDLTest_CommonEventMainCallbacks(state, event)) { + *done = 1; + } } void SDLTest_CommonQuit(SDLTest_CommonState *state) From e03746b25f485fdf8637cbcb3126dc2c52bd32a7 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Sun, 25 Feb 2024 16:33:46 +0100 Subject: [PATCH 131/220] cmake: add -Wl,-rpath,${libdir} to Libs section of pc file for Apple platforms --- CMakeLists.txt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f3bd5e27192d..f4e675b18c703 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -262,6 +262,12 @@ if(SDL_FRAMEWORK) set(SDL_STATIC_AVAILABLE FALSE) endif() +if(UNIX AND NOT ANDROID AND NOT RISCOS AND NOT SDL_FRAMEWORK) + set(SDL_RPATH_DEFAULT ON) +else() + set(SDL_RPATH_DEFAULT OFF) +endif() + # Allow some projects to be built conditionally. set_option(SDL_DISABLE_INSTALL "Disable installation of SDL3" ${SDL3_SUBPROJECT}) cmake_dependent_option(SDL_DISABLE_INSTALL_CPACK "Create binary SDL3 archive using CPack" ${SDL3_SUBPROJECT} "NOT SDL_DISABLE_INSTALL" ON) @@ -312,7 +318,7 @@ dep_option(SDL_PULSEAUDIO "Use PulseAudio" ${UNIX_SYS} "SDL_AUDIO" OFF) dep_option(SDL_PULSEAUDIO_SHARED "Dynamically load PulseAudio support" ON "SDL_PULSEAUDIO" OFF) dep_option(SDL_SNDIO "Support the sndio audio API" ${UNIX_SYS} "SDL_AUDIO" OFF) dep_option(SDL_SNDIO_SHARED "Dynamically load the sndio audio API" ON "SDL_SNDIO" OFF) -set_option(SDL_RPATH "Use an rpath when linking SDL" ${UNIX_SYS}) +set_option(SDL_RPATH "Use an rpath when linking SDL" ${SDL_RPATH_DEFAULT}) set_option(SDL_CLOCK_GETTIME "Use clock_gettime() instead of gettimeofday()" ${SDL_CLOCK_GETTIME_DEFAULT}) dep_option(SDL_X11 "Use X11 video driver" ${UNIX_SYS} "SDL_VIDEO" OFF) dep_option(SDL_X11_SHARED "Dynamically load X11 support" ON "SDL_X11" OFF) @@ -1763,10 +1769,11 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) else() set(SDL_RLD_FLAGS "-Wl,-rpath,\${libdir}") endif() + set(HAVE_RPATH TRUE) elseif(SOLARIS) set(SDL_RLD_FLAGS "-R\${libdir}") + set(HAVE_RPATH TRUE) endif() - set(HAVE_RPATH TRUE) endif() if(QNX) @@ -2347,6 +2354,11 @@ elseif(APPLE) CheckPTHREAD() + if(SDL_RPATH AND SDL_SHARED) + set(SDL_RLD_FLAGS "-Wl,-rpath,\${libdir}") + set(HAVE_RPATH TRUE) + endif() + elseif(HAIKU) if(SDL_AUDIO) set(SDL_AUDIO_DRIVER_HAIKU 1) From 614630df69c111402291beb955671a2fd6892288 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 27 Feb 2024 18:51:08 -0800 Subject: [PATCH 132/220] Allow using an external Vulkan device with the vulkan renderer --- include/SDL3/SDL_render.h | 71 +++++++--- src/render/vulkan/SDL_render_vulkan.c | 187 ++++++++++++++++---------- 2 files changed, 174 insertions(+), 84 deletions(-) diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index b02361cf7f5c3..52b773a6e39b6 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -250,6 +250,15 @@ extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window *window, co * - `SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_BOOLEAN`: true if you want * present synchronized with the refresh rate * + * With the vulkan renderer: + * + * - `SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER`: the VkInstance to use with the renderer, optional. + * - `SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER`: the VkSurfaceKHR to use with the renderer, optional. + * - `SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER`: the VkPhysicalDevice to use with the renderer, optional. + * - `SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER`: the VkDevice to use with the renderer, optional. + * - `SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER`: the queue family index used for rendering. + * - `SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER`: the queue family index used for presentation. + * * \param props the properties to use * \returns a valid rendering context or NULL if there was an error; call * SDL_GetError() for more information. @@ -263,11 +272,17 @@ extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window *window, co */ extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRendererWithProperties(SDL_PropertiesID props); -#define SDL_PROP_RENDERER_CREATE_NAME_STRING "name" -#define SDL_PROP_RENDERER_CREATE_WINDOW_POINTER "window" -#define SDL_PROP_RENDERER_CREATE_SURFACE_POINTER "surface" -#define SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER "output_colorspace" -#define SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_BOOLEAN "present_vsync" +#define SDL_PROP_RENDERER_CREATE_NAME_STRING "name" +#define SDL_PROP_RENDERER_CREATE_WINDOW_POINTER "window" +#define SDL_PROP_RENDERER_CREATE_SURFACE_POINTER "surface" +#define SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER "output_colorspace" +#define SDL_PROP_RENDERER_CREATE_PRESENT_VSYNC_BOOLEAN "present_vsync" +#define SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER "vulkan.instance" +#define SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER "vulkan.surface" +#define SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER "vulkan.physical_device" +#define SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER "vulkan.device" +#define SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER "vulkan.graphics_queue_family_index" +#define SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER "vulkan.present_queue_family_index" /** * Create a 2D software rendering context for a surface. @@ -354,15 +369,33 @@ extern DECLSPEC int SDLCALL SDL_GetRendererInfo(SDL_Renderer *renderer, SDL_Rend * that can be displayed, in terms of the SDR white point. When HDR is not * enabled, this will be 1.0. This property can change dynamically when * SDL_EVENT_DISPLAY_HDR_STATE_CHANGED is sent. + * + * With the direct3d renderer: + * * - `SDL_PROP_RENDERER_D3D9_DEVICE_POINTER`: the IDirect3DDevice9 associated * with the renderer + * + * With the direct3d11 renderer: + * * - `SDL_PROP_RENDERER_D3D11_DEVICE_POINTER`: the ID3D11Device associated * with the renderer + * + * With the direct3d12 renderer: + * * - `SDL_PROP_RENDERER_D3D12_DEVICE_POINTER`: the ID3D12Device associated * with the renderer * - `SDL_PROP_RENDERER_D3D12_COMMAND_QUEUE_POINTER`: the ID3D12CommandQueue * associated with the renderer * + * With the vulkan renderer: + * + * - `SDL_PROP_RENDERER_VULKAN_INSTANCE_POINTER`: the VkInstance associated with the renderer + * - `SDL_PROP_RENDERER_VULKAN_SURFACE_NUMBER`: the VkSurfaceKHR associated with the renderer + * - `SDL_PROP_RENDERER_VULKAN_PHYSICAL_DEVICE_POINTER`: the VkPhysicalDevice associated with the renderer + * - `SDL_PROP_RENDERER_VULKAN_DEVICE_POINTER`: the VkDevice associated with the renderer + * - `SDL_PROP_RENDERER_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER`: the queue family index used for rendering + * - `SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER`: the queue family index used for presentation + * * \param renderer the rendering context * \returns a valid property ID on success or 0 on failure; call * SDL_GetError() for more information. @@ -374,17 +407,23 @@ extern DECLSPEC int SDLCALL SDL_GetRendererInfo(SDL_Renderer *renderer, SDL_Rend */ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetRendererProperties(SDL_Renderer *renderer); -#define SDL_PROP_RENDERER_NAME_STRING "SDL.renderer.name" -#define SDL_PROP_RENDERER_WINDOW_POINTER "SDL.renderer.window" -#define SDL_PROP_RENDERER_SURFACE_POINTER "SDL.renderer.surface" -#define SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER "SDL.renderer.output_colorspace" -#define SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN "SDL.renderer.HDR_enabled" -#define SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT "SDL.renderer.SDR_white_point" -#define SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT "SDL.renderer.HDR_headroom" -#define SDL_PROP_RENDERER_D3D9_DEVICE_POINTER "SDL.renderer.d3d9.device" -#define SDL_PROP_RENDERER_D3D11_DEVICE_POINTER "SDL.renderer.d3d11.device" -#define SDL_PROP_RENDERER_D3D12_DEVICE_POINTER "SDL.renderer.d3d12.device" -#define SDL_PROP_RENDERER_D3D12_COMMAND_QUEUE_POINTER "SDL.renderer.d3d12.command_queue" +#define SDL_PROP_RENDERER_NAME_STRING "SDL.renderer.name" +#define SDL_PROP_RENDERER_WINDOW_POINTER "SDL.renderer.window" +#define SDL_PROP_RENDERER_SURFACE_POINTER "SDL.renderer.surface" +#define SDL_PROP_RENDERER_OUTPUT_COLORSPACE_NUMBER "SDL.renderer.output_colorspace" +#define SDL_PROP_RENDERER_HDR_ENABLED_BOOLEAN "SDL.renderer.HDR_enabled" +#define SDL_PROP_RENDERER_SDR_WHITE_POINT_FLOAT "SDL.renderer.SDR_white_point" +#define SDL_PROP_RENDERER_HDR_HEADROOM_FLOAT "SDL.renderer.HDR_headroom" +#define SDL_PROP_RENDERER_D3D9_DEVICE_POINTER "SDL.renderer.d3d9.device" +#define SDL_PROP_RENDERER_D3D11_DEVICE_POINTER "SDL.renderer.d3d11.device" +#define SDL_PROP_RENDERER_D3D12_DEVICE_POINTER "SDL.renderer.d3d12.device" +#define SDL_PROP_RENDERER_D3D12_COMMAND_QUEUE_POINTER "SDL.renderer.d3d12.command_queue" +#define SDL_PROP_RENDERER_VULKAN_INSTANCE_POINTER "SDL.renderer.vulkan.instance" +#define SDL_PROP_RENDERER_VULKAN_SURFACE_NUMBER "SDL.renderer.vulkan.surface" +#define SDL_PROP_RENDERER_VULKAN_PHYSICAL_DEVICE_POINTER "SDL.renderer.vulkan.physical_device" +#define SDL_PROP_RENDERER_VULKAN_DEVICE_POINTER "SDL.renderer.vulkan.device" +#define SDL_PROP_RENDERER_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER "SDL.renderer.vulkan.graphics_queue_family_index" +#define SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER "SDL.renderer.vulkan.present_queue_family_index" /** * Get the output size in pixels of a rendering context. diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index d5564cfd33ca1..18f398241405a 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -270,7 +270,9 @@ typedef struct { PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; VkInstance instance; + SDL_bool instance_external; VkSurfaceKHR surface; + SDL_bool surface_external; VkPhysicalDevice physicalDevice; VkPhysicalDeviceProperties physicalDeviceProperties; VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties; @@ -278,6 +280,7 @@ typedef struct VkQueue graphicsQueue; VkQueue presentQueue; VkDevice device; + SDL_bool device_external; uint32_t graphicsQueueFamilyIndex; uint32_t presentQueueFamilyIndex; VkSwapchainKHR swapchain; @@ -567,15 +570,15 @@ static void VULKAN_DestroyAll(SDL_Renderer *renderer) rendererData->constantBuffers = NULL; } - if (rendererData->device != VK_NULL_HANDLE) { + if (rendererData->device != VK_NULL_HANDLE && !rendererData->device_external) { vkDestroyDevice(rendererData->device, NULL); rendererData->device = VK_NULL_HANDLE; } - if (rendererData->surface != VK_NULL_HANDLE) { + if (rendererData->surface != VK_NULL_HANDLE && !rendererData->surface_external) { vkDestroySurfaceKHR(rendererData->instance, rendererData->surface, NULL); rendererData->surface = VK_NULL_HANDLE; } - if (rendererData->instance != VK_NULL_HANDLE) { + if (rendererData->instance != VK_NULL_HANDLE && !rendererData->instance_external) { vkDestroyInstance(rendererData->instance, NULL); rendererData->instance = VK_NULL_HANDLE; } @@ -1524,7 +1527,7 @@ static SDL_bool VULKAN_ValidationLayersFound() } /* Create resources that depend on the device. */ -static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer) +static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_PropertiesID create_props) { VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; SDL_VideoDevice *device = SDL_GetVideoDevice(); @@ -1549,46 +1552,53 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer) return VK_ERROR_UNKNOWN; } - /* Create VkInstance */ - VkInstanceCreateInfo instanceCreateInfo = { 0 }; - VkApplicationInfo appInfo = { 0 }; - appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; - appInfo.apiVersion = VK_API_VERSION_1_0; - instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - instanceCreateInfo.pApplicationInfo = &appInfo; - char const* const* instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount); - rendererData->supportsEXTSwapchainColorspace = VK_FALSE; - + /* Check for colorspace extension */ if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR || renderer->output_colorspace == SDL_COLORSPACE_HDR10) { rendererData->supportsEXTSwapchainColorspace = VULKAN_InstanceExtensionFound(rendererData, VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME); - if (rendererData->supportsEXTSwapchainColorspace == SDL_FALSE) { + if (!rendererData->supportsEXTSwapchainColorspace) { return SDL_SetError("[Vulkan] Using HDR output but %s not supported.", VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME); } } - char **instanceExtensionsCopy = SDL_calloc(sizeof(const char *), instanceCreateInfo.enabledExtensionCount + 1); - for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) { - instanceExtensionsCopy[i] = SDL_strdup(instanceExtensions[i]); - } - if (rendererData->supportsEXTSwapchainColorspace) { - instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount] = SDL_strdup(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME); - instanceCreateInfo.enabledExtensionCount++; - } - instanceCreateInfo.ppEnabledExtensionNames = (const char* const*) instanceExtensionsCopy; - if (createDebug && VULKAN_ValidationLayersFound()) { - instanceCreateInfo.ppEnabledLayerNames = validationLayerName; - instanceCreateInfo.enabledLayerCount = 1; - } - result = vkCreateInstance(&instanceCreateInfo, NULL, &rendererData->instance); - if (result != VK_SUCCESS) { - SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateInstance(): %s\n", SDL_Vulkan_GetResultString(result)); - return result; - } - for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) { - SDL_free(instanceExtensionsCopy[i]); + /* Create VkInstance */ + rendererData->instance = (VkInstance)SDL_GetProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER, NULL); + if (rendererData->instance) { + rendererData->instance_external = SDL_TRUE; + } else { + VkInstanceCreateInfo instanceCreateInfo = { 0 }; + VkApplicationInfo appInfo = { 0 }; + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.apiVersion = VK_API_VERSION_1_0; + instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instanceCreateInfo.pApplicationInfo = &appInfo; + char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount); + + char **instanceExtensionsCopy = SDL_calloc(sizeof(const char *), instanceCreateInfo.enabledExtensionCount + 1); + for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) { + instanceExtensionsCopy[i] = SDL_strdup(instanceExtensions[i]); + } + if (rendererData->supportsEXTSwapchainColorspace) { + instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount] = SDL_strdup(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME); + instanceCreateInfo.enabledExtensionCount++; + } + instanceCreateInfo.ppEnabledExtensionNames = (const char *const *)instanceExtensionsCopy; + if (createDebug && VULKAN_ValidationLayersFound()) { + instanceCreateInfo.ppEnabledLayerNames = validationLayerName; + instanceCreateInfo.enabledLayerCount = 1; + } + result = vkCreateInstance(&instanceCreateInfo, NULL, &rendererData->instance); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateInstance(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) { + SDL_free(instanceExtensionsCopy[i]); + } + SDL_free(instanceExtensionsCopy); } - SDL_free(instanceExtensionsCopy); + /* Load instance Vulkan functions */ if (VULKAN_LoadInstanceFunctions(rendererData) != 0) { VULKAN_DestroyAll(renderer); @@ -1596,45 +1606,78 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer) } /* Create Vulkan surface */ - if (!device->Vulkan_CreateSurface || !device->Vulkan_CreateSurface(device, renderer->window, rendererData->instance, NULL, &rendererData->surface)) { - VULKAN_DestroyAll(renderer); - SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Vulkan_CreateSurface() failed.\n"); - return VK_ERROR_UNKNOWN; + rendererData->surface = (VkSurfaceKHR)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER, 0); + if (rendererData->surface) { + rendererData->surface_external = SDL_TRUE; + } else { + if (!device->Vulkan_CreateSurface || !device->Vulkan_CreateSurface(device, renderer->window, rendererData->instance, NULL, &rendererData->surface)) { + VULKAN_DestroyAll(renderer); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Vulkan_CreateSurface() failed.\n"); + return VK_ERROR_UNKNOWN; + } } /* Choose Vulkan physical device */ - if (VULKAN_FindPhysicalDevice(rendererData) != VK_SUCCESS) { - VULKAN_DestroyAll(renderer); - return VK_ERROR_UNKNOWN; + rendererData->physicalDevice = (VkPhysicalDevice)SDL_GetProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER, NULL); + if (rendererData->physicalDevice) { + vkGetPhysicalDeviceMemoryProperties(rendererData->physicalDevice, &rendererData->physicalDeviceMemoryProperties); + vkGetPhysicalDeviceFeatures(rendererData->physicalDevice, &rendererData->physicalDeviceFeatures); + } else { + if (VULKAN_FindPhysicalDevice(rendererData) != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + return VK_ERROR_UNKNOWN; + } + } + + if (SDL_HasProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER)) { + rendererData->graphicsQueueFamilyIndex = (uint32_t)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER, 0); + } + if (SDL_HasProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER)) { + rendererData->presentQueueFamilyIndex = (uint32_t)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER, 0); } /* Create Vulkan device */ - VkDeviceQueueCreateInfo deviceQueueCreateInfo[1] = { { 0 } }; - static const float queuePriority[] = { 1.0f }; - VkDeviceCreateInfo deviceCreateInfo = { 0 }; - static const char *const deviceExtensionNames[] = { - VK_KHR_SWAPCHAIN_EXTENSION_NAME, - }; + rendererData->device = (VkDevice)SDL_GetProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER, NULL); + if (rendererData->device) { + rendererData->device_external = SDL_TRUE; + } else { + VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = { { 0 }, { 0 } }; + static const float queuePriority[] = { 1.0f }; + VkDeviceCreateInfo deviceCreateInfo = { 0 }; + static const char *const deviceExtensionNames[] = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + }; - deviceQueueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - deviceQueueCreateInfo->queueFamilyIndex = rendererData->graphicsQueueFamilyIndex; - deviceQueueCreateInfo->queueCount = 1; - deviceQueueCreateInfo->pQueuePriorities = &queuePriority[0]; - - deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - deviceCreateInfo.queueCreateInfoCount = 1; - deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo; - deviceCreateInfo.pEnabledFeatures = NULL; - deviceCreateInfo.enabledExtensionCount = SDL_arraysize(deviceExtensionNames); - deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames; - result = vkCreateDevice(rendererData->physicalDevice, &deviceCreateInfo, NULL, &rendererData->device); - if (result != VK_SUCCESS) { - SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateDevice(): %s\n", SDL_Vulkan_GetResultString(result)); - VULKAN_DestroyAll(renderer); - return result; + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceCreateInfo.queueCreateInfoCount = 0; + deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo; + deviceCreateInfo.pEnabledFeatures = NULL; + deviceCreateInfo.enabledExtensionCount = SDL_arraysize(deviceExtensionNames); + deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames; + + deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + deviceQueueCreateInfo[0].queueFamilyIndex = rendererData->graphicsQueueFamilyIndex; + deviceQueueCreateInfo[0].queueCount = 1; + deviceQueueCreateInfo[0].pQueuePriorities = queuePriority; + ++deviceCreateInfo.queueCreateInfoCount; + + if (rendererData->presentQueueFamilyIndex != rendererData->graphicsQueueFamilyIndex) { + deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + deviceQueueCreateInfo[1].queueFamilyIndex = rendererData->presentQueueFamilyIndex; + deviceQueueCreateInfo[1].queueCount = 1; + deviceQueueCreateInfo[1].pQueuePriorities = queuePriority; + ++deviceCreateInfo.queueCreateInfoCount; + } + + result = vkCreateDevice(rendererData->physicalDevice, &deviceCreateInfo, NULL, &rendererData->device); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateDevice(): %s\n", SDL_Vulkan_GetResultString(result)); + VULKAN_DestroyAll(renderer); + return result; + } } - if(VULKAN_LoadDeviceFunctions(rendererData) != 0) { + if (VULKAN_LoadDeviceFunctions(rendererData) != 0) { VULKAN_DestroyAll(renderer); return VK_ERROR_UNKNOWN; } @@ -1789,6 +1832,14 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer) } } + SDL_PropertiesID props = SDL_GetRendererProperties(renderer); + SDL_SetProperty(props, SDL_PROP_RENDERER_VULKAN_INSTANCE_POINTER, rendererData->instance); + SDL_SetNumberProperty(props, SDL_PROP_RENDERER_VULKAN_SURFACE_NUMBER, (Sint64)rendererData->surface); + SDL_SetProperty(props, SDL_PROP_RENDERER_VULKAN_PHYSICAL_DEVICE_POINTER, rendererData->physicalDevice); + SDL_SetProperty(props, SDL_PROP_RENDERER_VULKAN_DEVICE_POINTER, rendererData->device); + SDL_SetNumberProperty(props, SDL_PROP_RENDERER_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER, rendererData->graphicsQueueFamilyIndex); + SDL_SetNumberProperty(props, SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER, rendererData->presentQueueFamilyIndex); + return VK_SUCCESS; } @@ -3880,8 +3931,8 @@ SDL_Renderer *VULKAN_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_ */ renderer->window = window; - /* Initialize Direct3D resources */ - if (VULKAN_CreateDeviceResources(renderer) != VK_SUCCESS) { + /* Initialize Vulkan resources */ + if (VULKAN_CreateDeviceResources(renderer, create_props) != VK_SUCCESS) { VULKAN_DestroyRenderer(renderer); return NULL; } From 0997bdd292db12567f7deba1a76b344a7fd2a73f Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 27 Feb 2024 20:11:45 -0800 Subject: [PATCH 133/220] Fixed SDL_calloc() calls (should be count, size) --- src/render/vulkan/SDL_render_vulkan.c | 34 +++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 18f398241405a..e78bab69dfeca 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -1485,7 +1485,7 @@ static SDL_bool VULKAN_InstanceExtensionFound(VULKAN_RenderData *rendererData, c return SDL_FALSE; } if (extensionCount > 0 ) { - VkExtensionProperties *extensionProperties = SDL_calloc(sizeof(VkExtensionProperties), extensionCount); + VkExtensionProperties *extensionProperties = SDL_calloc(extensionCount, sizeof(VkExtensionProperties)); result = vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensionProperties); if (result != VK_SUCCESS ) { SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkEnumerateInstanceExtensionProperties( NULL, ... ): %s.\n", SDL_Vulkan_GetResultString(result)); @@ -1574,7 +1574,7 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert instanceCreateInfo.pApplicationInfo = &appInfo; char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount); - char **instanceExtensionsCopy = SDL_calloc(sizeof(const char *), instanceCreateInfo.enabledExtensionCount + 1); + char **instanceExtensionsCopy = SDL_calloc(instanceCreateInfo.enabledExtensionCount + 1, sizeof(const char *)); for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) { instanceExtensionsCopy[i] = SDL_strdup(instanceExtensions[i]); } @@ -1997,7 +1997,7 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) return result; } if (presentModeCount > 0) { - VkPresentModeKHR *presentModes = SDL_calloc(sizeof(VkPresentModeKHR), presentModeCount); + VkPresentModeKHR *presentModes = SDL_calloc(presentModeCount, sizeof(VkPresentModeKHR)); result = vkGetPhysicalDeviceSurfacePresentModesKHR(rendererData->physicalDevice, rendererData->surface, &presentModeCount, presentModes); if (result != VK_SUCCESS) { SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkGetPhysicalDeviceSurfacePresentModesKHR(): %s\n", SDL_Vulkan_GetResultString(result)); @@ -2098,9 +2098,9 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) } SDL_free(rendererData->swapchainImageViews); } - rendererData->swapchainImageViews = SDL_calloc(sizeof(VkImageView), rendererData->swapchainImageCount); + rendererData->swapchainImageViews = SDL_calloc(rendererData->swapchainImageCount, sizeof(VkImageView)); SDL_free(rendererData->swapchainImageLayouts); - rendererData->swapchainImageLayouts = SDL_calloc(sizeof(VkImageLayout), rendererData->swapchainImageCount); + rendererData->swapchainImageLayouts = SDL_calloc(rendererData->swapchainImageCount, sizeof(VkImageLayout)); for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { imageViewCreateInfo.image = rendererData->swapchainImages[i]; result = vkCreateImageView(rendererData->device, &imageViewCreateInfo, NULL, &rendererData->swapchainImageViews[i]); @@ -2125,7 +2125,7 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) rendererData->currentCommandBuffer = VK_NULL_HANDLE; rendererData->currentCommandBufferIndex = 0; } - rendererData->commandBuffers = SDL_calloc(sizeof(VkCommandBuffer), rendererData->swapchainImageCount); + rendererData->commandBuffers = SDL_calloc(rendererData->swapchainImageCount, sizeof(VkCommandBuffer)); result = vkAllocateCommandBuffers(rendererData->device, &commandBufferAllocateInfo, rendererData->commandBuffers); if (result != VK_SUCCESS) { VULKAN_DestroyAll(renderer); @@ -2142,7 +2142,7 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) } SDL_free(rendererData->fences); } - rendererData->fences = SDL_calloc(sizeof(VkFence), rendererData->swapchainImageCount); + rendererData->fences = SDL_calloc(rendererData->swapchainImageCount, sizeof(VkFence)); for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { VkFenceCreateInfo fenceCreateInfo = { 0 }; fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; @@ -2170,7 +2170,7 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) rendererData->renderPasses[i] = VK_NULL_HANDLE; } } - rendererData->framebuffers = SDL_calloc(sizeof(VkFramebuffer), rendererData->swapchainImageCount); + rendererData->framebuffers = SDL_calloc(rendererData->swapchainImageCount, sizeof(VkFramebuffer)); result = VULKAN_CreateFramebuffersAndRenderPasses(renderer, rendererData->swapchainSize.width, rendererData->swapchainSize.height, @@ -2199,12 +2199,12 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) SDL_free(rendererData->descriptorPools); SDL_free(rendererData->numDescriptorPools); } - rendererData->descriptorPools = SDL_calloc(sizeof(VkDescriptorPool*), rendererData->swapchainImageCount); - rendererData->numDescriptorPools = SDL_calloc(sizeof(uint32_t), rendererData->swapchainImageCount); + rendererData->descriptorPools = SDL_calloc(rendererData->swapchainImageCount, sizeof(VkDescriptorPool*)); + rendererData->numDescriptorPools = SDL_calloc(rendererData->swapchainImageCount, sizeof(uint32_t)); for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { /* Start by just allocating one pool, it will grow if needed */ rendererData->numDescriptorPools[i] = 1; - rendererData->descriptorPools[i] = SDL_calloc(sizeof(VkDescriptorPool), 1); + rendererData->descriptorPools[i] = SDL_calloc(1, sizeof(VkDescriptorPool)); rendererData->descriptorPools[i][0] = VULKAN_AllocateDescriptorPool(rendererData); if (result != VK_SUCCESS) { VULKAN_DestroyAll(renderer); @@ -2240,12 +2240,12 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) } SDL_free(rendererData->uploadBuffers); } - rendererData->uploadBuffers = SDL_calloc(sizeof(VULKAN_Buffer*), rendererData->swapchainImageCount); + rendererData->uploadBuffers = SDL_calloc(rendererData->swapchainImageCount, sizeof(VULKAN_Buffer*)); for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { - rendererData->uploadBuffers[i] = SDL_calloc(sizeof(VULKAN_Buffer), SDL_VULKAN_NUM_UPLOAD_BUFFERS); + rendererData->uploadBuffers[i] = SDL_calloc(SDL_VULKAN_NUM_UPLOAD_BUFFERS, sizeof(VULKAN_Buffer)); } SDL_free(rendererData->currentUploadBuffer); - rendererData->currentUploadBuffer = SDL_calloc(sizeof(int), rendererData->swapchainImageCount); + rendererData->currentUploadBuffer = SDL_calloc(rendererData->swapchainImageCount, sizeof(int)); /* Constant buffers */ if (rendererData->constantBuffers) { @@ -2260,12 +2260,12 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) SDL_free(rendererData->numConstantBuffers); rendererData->constantBuffers = NULL; } - rendererData->constantBuffers = SDL_calloc(sizeof(VULKAN_Buffer*), rendererData->swapchainImageCount); - rendererData->numConstantBuffers = SDL_calloc(sizeof(VULKAN_Buffer*), rendererData->swapchainImageCount); + rendererData->constantBuffers = SDL_calloc(rendererData->swapchainImageCount, sizeof(VULKAN_Buffer*)); + rendererData->numConstantBuffers = SDL_calloc(rendererData->swapchainImageCount, sizeof(VULKAN_Buffer*)); for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { /* Start with just allocating one, will grow if needed */ rendererData->numConstantBuffers[i] = 1; - rendererData->constantBuffers[i] = SDL_calloc(sizeof(VULKAN_Buffer), 1); + rendererData->constantBuffers[i] = SDL_calloc(1, sizeof(VULKAN_Buffer)); result = VULKAN_AllocateBuffer(rendererData, SDL_VULKAN_CONSTANT_BUFFER_DEFAULT_SIZE, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, From e142bb1b0ce392fe58370f47eb1fdac52c78bc2e Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 27 Feb 2024 20:13:15 -0800 Subject: [PATCH 134/220] The extension strings are const and don't need to be duplicated --- src/render/vulkan/SDL_render_vulkan.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index e78bab69dfeca..61524341ec949 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -1574,12 +1574,12 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert instanceCreateInfo.pApplicationInfo = &appInfo; char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount); - char **instanceExtensionsCopy = SDL_calloc(instanceCreateInfo.enabledExtensionCount + 1, sizeof(const char *)); + const char **instanceExtensionsCopy = SDL_calloc(instanceCreateInfo.enabledExtensionCount + 1, sizeof(const char *)); for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) { - instanceExtensionsCopy[i] = SDL_strdup(instanceExtensions[i]); + instanceExtensionsCopy[i] = instanceExtensions[i]; } if (rendererData->supportsEXTSwapchainColorspace) { - instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount] = SDL_strdup(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME); + instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount] = VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME; instanceCreateInfo.enabledExtensionCount++; } instanceCreateInfo.ppEnabledExtensionNames = (const char *const *)instanceExtensionsCopy; @@ -1592,10 +1592,6 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateInstance(): %s\n", SDL_Vulkan_GetResultString(result)); return result; } - - for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) { - SDL_free(instanceExtensionsCopy[i]); - } SDL_free(instanceExtensionsCopy); } From 68588b232c845da2390a11cc0628238e25017c83 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Wed, 28 Feb 2024 15:13:26 +0000 Subject: [PATCH 135/220] Sync SDL3 wiki -> header --- include/SDL3/SDL_render.h | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 52b773a6e39b6..d577c4dc4c834 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -252,12 +252,18 @@ extern DECLSPEC SDL_Renderer * SDLCALL SDL_CreateRenderer(SDL_Window *window, co * * With the vulkan renderer: * - * - `SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER`: the VkInstance to use with the renderer, optional. - * - `SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER`: the VkSurfaceKHR to use with the renderer, optional. - * - `SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER`: the VkPhysicalDevice to use with the renderer, optional. - * - `SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER`: the VkDevice to use with the renderer, optional. - * - `SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER`: the queue family index used for rendering. - * - `SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER`: the queue family index used for presentation. + * - `SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER`: the VkInstance to use + * with the renderer, optional. + * - `SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER`: the VkSurfaceKHR to use + * with the renderer, optional. + * - `SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER`: the + * VkPhysicalDevice to use with the renderer, optional. + * - `SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER`: the VkDevice to use + * with the renderer, optional. + * - `SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER`: the + * queue family index used for rendering. + * - `SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER`: the + * queue family index used for presentation. * * \param props the properties to use * \returns a valid rendering context or NULL if there was an error; call @@ -389,12 +395,18 @@ extern DECLSPEC int SDLCALL SDL_GetRendererInfo(SDL_Renderer *renderer, SDL_Rend * * With the vulkan renderer: * - * - `SDL_PROP_RENDERER_VULKAN_INSTANCE_POINTER`: the VkInstance associated with the renderer - * - `SDL_PROP_RENDERER_VULKAN_SURFACE_NUMBER`: the VkSurfaceKHR associated with the renderer - * - `SDL_PROP_RENDERER_VULKAN_PHYSICAL_DEVICE_POINTER`: the VkPhysicalDevice associated with the renderer - * - `SDL_PROP_RENDERER_VULKAN_DEVICE_POINTER`: the VkDevice associated with the renderer - * - `SDL_PROP_RENDERER_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER`: the queue family index used for rendering - * - `SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER`: the queue family index used for presentation + * - `SDL_PROP_RENDERER_VULKAN_INSTANCE_POINTER`: the VkInstance associated + * with the renderer + * - `SDL_PROP_RENDERER_VULKAN_SURFACE_NUMBER`: the VkSurfaceKHR associated + * with the renderer + * - `SDL_PROP_RENDERER_VULKAN_PHYSICAL_DEVICE_POINTER`: the VkPhysicalDevice + * associated with the renderer + * - `SDL_PROP_RENDERER_VULKAN_DEVICE_POINTER`: the VkDevice associated with + * the renderer + * - `SDL_PROP_RENDERER_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER`: the queue + * family index used for rendering + * - `SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER`: the queue + * family index used for presentation * * \param renderer the rendering context * \returns a valid property ID on success or 0 on failure; call From 945162c6d90ea8c76ae5dd994413c4ec7b73d6d8 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Wed, 28 Feb 2024 09:57:54 -0500 Subject: [PATCH 136/220] wayland: Small optimization for output removal function In the case where an output is being removed, the SDL_DisplayData is already known, so no need to retrieve it from the wl_output user data. --- src/video/wayland/SDL_waylandvideo.c | 2 +- src/video/wayland/SDL_waylandwindow.c | 21 +++++++++------------ src/video/wayland/SDL_waylandwindow.h | 2 +- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c index bd7fd44f407df..090458503d9ed 100644 --- a/src/video/wayland/SDL_waylandvideo.c +++ b/src/video/wayland/SDL_waylandvideo.c @@ -951,7 +951,7 @@ static void Wayland_free_display(SDL_VideoDisplay *display) * so ensure that no window continues to hold a reference to a removed output. */ for (SDL_Window *window = SDL_GetVideoDevice()->windows; window; window = window->next) { - Wayland_RemoveOutputFromWindow(window->driverdata, display_data->output); + Wayland_RemoveOutputFromWindow(window->driverdata, display_data); } SDL_free(display_data->wl_output_name); diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index 1aa64b22a3019..b4e45398737da 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -1304,16 +1304,15 @@ static void Wayland_move_window(SDL_Window *window, SDL_DisplayData *driverdata) } } -void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, struct wl_output *output) +void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, SDL_DisplayData *display_data) { - int i, send_move_event = 0; - SDL_DisplayData *driverdata = wl_output_get_user_data(output); + SDL_bool send_move_event = SDL_FALSE; - for (i = 0; i < window->num_outputs; i++) { - if (window->outputs[i] == driverdata) { /* remove this one */ + for (int i = 0; i < window->num_outputs; i++) { + if (window->outputs[i] == display_data) { /* remove this one */ if (i == (window->num_outputs - 1)) { window->outputs[i] = NULL; - send_move_event = 1; + send_move_event = SDL_TRUE; } else { SDL_memmove(&window->outputs[i], &window->outputs[i + 1], @@ -1335,8 +1334,7 @@ void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, struct wl_output *ou Wayland_MaybeUpdateScaleFactor(window); } -static void handle_surface_enter(void *data, struct wl_surface *surface, - struct wl_output *output) +static void handle_surface_enter(void *data, struct wl_surface *surface, struct wl_output *output) { SDL_WindowData *window = data; SDL_DisplayData *driverdata = wl_output_get_user_data(output); @@ -1359,16 +1357,15 @@ static void handle_surface_enter(void *data, struct wl_surface *surface, Wayland_MaybeUpdateScaleFactor(window); } -static void handle_surface_leave(void *data, struct wl_surface *surface, - struct wl_output *output) +static void handle_surface_leave(void *data, struct wl_surface *surface, struct wl_output *output) { - SDL_WindowData *window = data; + SDL_WindowData *window = (SDL_WindowData *)data; if (!SDL_WAYLAND_own_output(output) || !SDL_WAYLAND_own_surface(surface)) { return; } - Wayland_RemoveOutputFromWindow(window, output); + Wayland_RemoveOutputFromWindow(window, (SDL_DisplayData *)wl_output_get_user_data(output)); } static void handle_preferred_buffer_scale(void *data, struct wl_surface *wl_surface, int32_t factor) diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index cf8e7ea2a8887..c607c2e803470 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -206,6 +206,6 @@ extern int Wayland_SetWindowHitTest(SDL_Window *window, SDL_bool enabled); extern int Wayland_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation); extern int Wayland_SyncWindow(SDL_VideoDevice *_this, SDL_Window *window); -extern void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, struct wl_output *output); +extern void Wayland_RemoveOutputFromWindow(SDL_WindowData *window, SDL_DisplayData *display_data); #endif /* SDL_waylandwindow_h_ */ From fb3bf1dcbdb81d6b79c8daaedeb10b7617940818 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 28 Feb 2024 08:54:54 -0800 Subject: [PATCH 137/220] Fixed device queue initialization when render and present queue families are different --- test/testvulkan.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/test/testvulkan.c b/test/testvulkan.c index cfade5cd6df69..dcd0ebb1b1886 100644 --- a/test/testvulkan.c +++ b/test/testvulkan.c @@ -426,7 +426,7 @@ static void findPhysicalDevice(void) static void createDevice(void) { - VkDeviceQueueCreateInfo deviceQueueCreateInfo[1] = { { 0 } }; + VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = { { 0 }, { 0 } }; static const float queuePriority[] = { 1.0f }; VkDeviceCreateInfo deviceCreateInfo = { 0 }; static const char *const deviceExtensionNames[] = { @@ -434,17 +434,27 @@ static void createDevice(void) }; VkResult result; - deviceQueueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - deviceQueueCreateInfo->queueFamilyIndex = vulkanContext->graphicsQueueFamilyIndex; - deviceQueueCreateInfo->queueCount = 1; - deviceQueueCreateInfo->pQueuePriorities = &queuePriority[0]; - deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - deviceCreateInfo.queueCreateInfoCount = 1; + deviceCreateInfo.queueCreateInfoCount = 0; deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo; deviceCreateInfo.pEnabledFeatures = NULL; deviceCreateInfo.enabledExtensionCount = SDL_arraysize(deviceExtensionNames); deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames; + + deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + deviceQueueCreateInfo[0].queueFamilyIndex = vulkanContext->graphicsQueueFamilyIndex; + deviceQueueCreateInfo[0].queueCount = 1; + deviceQueueCreateInfo[0].pQueuePriorities = queuePriority; + ++deviceCreateInfo.queueCreateInfoCount; + + if (vulkanContext->presentQueueFamilyIndex != vulkanContext->graphicsQueueFamilyIndex) { + deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + deviceQueueCreateInfo[1].queueFamilyIndex = vulkanContext->presentQueueFamilyIndex; + deviceQueueCreateInfo[1].queueCount = 1; + deviceQueueCreateInfo[1].pQueuePriorities = queuePriority; + ++deviceCreateInfo.queueCreateInfoCount; + } + result = vkCreateDevice(vulkanContext->physicalDevice, &deviceCreateInfo, NULL, &vulkanContext->device); if (result != VK_SUCCESS) { vulkanContext->device = VK_NULL_HANDLE; From ad036d43e97c5cce1d196c0f85b0fc10c69fa70d Mon Sep 17 00:00:00 2001 From: Dan Ginsburg Date: Wed, 28 Feb 2024 11:57:09 -0500 Subject: [PATCH 138/220] Vulkan Renderer - implement YcBcCr using VK_KHR_sampler_ycbcr_conversion. (#9169) * Vulkan Renderer - implement YcBcCr using VK_KHR_sampler_ycbcr_conversion. This simplifies the shader code and will also allow support for additional formats that we don't yet support (such as SDL_PIXELFORMAT_P010 for ffmpeg). The renderer now queries for VK_KHR_sampler_ycbcr_conversion and dependent extensions and will enable it if it's present. It reimplements YUV/NV12 texture support using this extension (these formats are no longer supported if the extension is not present). For each YUV/NV12 texture, a VkSamplerYcbcrConversion object is created from SDL_Colorspace parameters. This is passed to the VkImageView and also an additional sampler is created for Ycbcr. Instead of using 1-3 textures, the shaders now all use 1 texture with a combined image sampler (required by the extension). Further, when using Ycbcr, a separate descriptor set layout is baked with the Ycbcr sampler as an immutable sampler. The code to copy the images now copies to the individual image planes. The swizzling between formats is handled in the VkSamplerYcbcrConversion object. --- src/render/vulkan/SDL_render_vulkan.c | 752 ++++++++++-------- .../vulkan/VULKAN_PixelShader_Advanced.h | 554 ++++++------- src/render/vulkan/VULKAN_PixelShader_Colors.h | 80 +- .../vulkan/VULKAN_PixelShader_Common.incl | 61 +- .../vulkan/VULKAN_PixelShader_Textures.h | 102 ++- src/render/vulkan/compile_shaders.bat | 6 +- 6 files changed, 728 insertions(+), 827 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 61524341ec949..14c9a91033190 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -129,15 +129,26 @@ extern const char *SDL_Vulkan_GetResultString(VkResult result); VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceFormatsKHR) \ VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfacePresentModesKHR) \ VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR) \ - VULKAN_INSTANCE_FUNCTION(vkQueueWaitIdle) - -#define VULKAN_DEVICE_FUNCTION(name) static PFN_##name name = NULL; -#define VULKAN_GLOBAL_FUNCTION(name) static PFN_##name name = NULL; -#define VULKAN_INSTANCE_FUNCTION(name) static PFN_##name name = NULL; + VULKAN_INSTANCE_FUNCTION(vkQueueWaitIdle) \ + VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures2KHR) \ + VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceFormatProperties2KHR) \ + VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceImageFormatProperties2KHR) \ + VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceMemoryProperties2KHR) \ + VULKAN_OPTIONAL_INSTANCE_FUNCTION(vkGetPhysicalDeviceProperties2KHR) \ + VULKAN_OPTIONAL_DEVICE_FUNCTION(vkCreateSamplerYcbcrConversionKHR) \ + VULKAN_OPTIONAL_DEVICE_FUNCTION(vkDestroySamplerYcbcrConversionKHR) \ + +#define VULKAN_DEVICE_FUNCTION(name) static PFN_##name name = NULL; +#define VULKAN_GLOBAL_FUNCTION(name) static PFN_##name name = NULL; +#define VULKAN_INSTANCE_FUNCTION(name) static PFN_##name name = NULL; +#define VULKAN_OPTIONAL_INSTANCE_FUNCTION(name) static PFN_##name name = NULL; +#define VULKAN_OPTIONAL_DEVICE_FUNCTION(name) static PFN_##name name = NULL; VULKAN_FUNCTIONS() #undef VULKAN_DEVICE_FUNCTION #undef VULKAN_GLOBAL_FUNCTION #undef VULKAN_INSTANCE_FUNCTION +#undef VULKAN_OPTIONAL_INSTANCE_FUNCTION +#undef VULKAN_OPTIONAL_DEVICE_FUNCTION /* Renderpass types */ typedef enum { @@ -165,12 +176,6 @@ typedef struct //static const float TONEMAP_LINEAR = 1; static const float TONEMAP_CHROME = 2; -//static const float TEXTURETYPE_NONE = 0; -static const float TEXTURETYPE_RGB = 1; -static const float TEXTURETYPE_NV12 = 2; -static const float TEXTURETYPE_NV21 = 3; -static const float TEXTURETYPE_YUV = 4; - static const float INPUTTYPE_UNSPECIFIED = 0; static const float INPUTTYPE_SRGB = 1; static const float INPUTTYPE_SCRGB = 2; @@ -180,9 +185,9 @@ static const float INPUTTYPE_HDR10 = 3; typedef struct { float scRGB_output; - float texture_type; float input_type; float color_scale; + float unused_pad0; float tonemap_method; float tonemap_factor1; @@ -236,14 +241,14 @@ typedef struct const float *YCbCr_matrix; #if SDL_HAVE_YUV - /* YV12 texture support */ - SDL_bool yuv; - VULKAN_Image mainImageU; - VULKAN_Image mainImageV; - - /* NV12 texture support */ - SDL_bool nv12; - VULKAN_Image mainImageUV; + /* Object passed to VkImageView and VkSampler for doing Ycbcr -> RGB conversion */ + VkSamplerYcbcrConversion samplerYcbcrConversion; + /* Sampler created with samplerYcbcrConversion, passed to PSO as immutable sampler */ + VkSampler samplerYcbcr; + /* Descriptor set layout with samplerYcbcr baked as immutable sampler */ + VkDescriptorSetLayout descriptorSetLayoutYcbcr; + /* Pipeline layout with immutable sampler descriptor set layout */ + VkPipelineLayout pipelineLayoutYcbcr; #endif } VULKAN_TextureData; @@ -257,6 +262,7 @@ typedef struct VkPrimitiveTopology topology; VkFormat format; VkPipelineLayout pipelineLayout; + VkDescriptorSetLayout descriptorSetLayout; VkPipeline pipeline; } VULKAN_PipelineState; @@ -299,8 +305,8 @@ typedef struct VkShaderModule vertexShaderModules[NUM_SHADERS]; VkShaderModule fragmentShaderModules[NUM_SHADERS]; - VkDescriptorSetLayout descriptorSetLayouts[NUM_SHADERS]; - VkPipelineLayout pipelineLayouts[NUM_SHADERS]; + VkDescriptorSetLayout descriptorSetLayout; + VkPipelineLayout pipelineLayout; /* Vertex buffer data */ VULKAN_Buffer vertexBuffers[SDL_VULKAN_NUM_VERTEX_BUFFERS]; @@ -327,6 +333,8 @@ typedef struct VULKAN_PipelineState *currentPipelineState; SDL_bool supportsEXTSwapchainColorspace; + SDL_bool supportsKHRGetPhysicalDeviceProperties2; + SDL_bool supportsKHRSamplerYcBcrConversion; uint32_t surfaceFormatsAllocatedCount; uint32_t surfaceFormatsCount; uint32_t swapchainDesiredImageCount; @@ -357,7 +365,6 @@ typedef struct Uint32 VULKAN_VkFormatToSDLPixelFormat(VkFormat vkFormat) { switch (vkFormat) { - case VK_FORMAT_B8G8R8A8_UNORM: return SDL_PIXELFORMAT_ARGB8888; case VK_FORMAT_A2R10G10B10_UNORM_PACK32: @@ -369,6 +376,18 @@ Uint32 VULKAN_VkFormatToSDLPixelFormat(VkFormat vkFormat) } } +Uint32 VULKAN_VkFormatGetNumPlanes(VkFormat vkFormat) +{ + switch (vkFormat) { + case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM: + return 3; + case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM: + return 2; + default: + return 1; + } +} + VkDeviceSize VULKAN_GetBytesPerPixel(VkFormat vkFormat) { switch (vkFormat) { @@ -402,17 +421,23 @@ static VkFormat SDLPixelFormatToVkTextureFormat(Uint32 format, Uint32 colorspace return VK_FORMAT_B8G8R8A8_SRGB; } return VK_FORMAT_B8G8R8A8_UNORM; + case SDL_PIXELFORMAT_YUY2: + return VK_FORMAT_G8B8G8R8_422_UNORM; + case SDL_PIXELFORMAT_UYVY: + return VK_FORMAT_B8G8R8G8_422_UNORM; case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: - case SDL_PIXELFORMAT_NV12: /* Y plane */ - case SDL_PIXELFORMAT_NV21: /* Y plane */ - return VK_FORMAT_R8_UNORM; + return VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM; + case SDL_PIXELFORMAT_NV12: + case SDL_PIXELFORMAT_NV21: + return VK_FORMAT_G8_B8R8_2PLANE_420_UNORM; case SDL_PIXELFORMAT_P010: - return VK_FORMAT_R16_UNORM; + return VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16; default: return VK_FORMAT_UNDEFINED; } } + static void VULKAN_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture); static void VULKAN_DestroyBuffer(VULKAN_RenderData *rendererData, VULKAN_Buffer *vulkanBuffer); static void VULKAN_DestroyImage(VULKAN_RenderData *rendererData, VULKAN_Image *vulkanImage); @@ -420,6 +445,7 @@ static void VULKAN_ResetCommandList(VULKAN_RenderData *rendererData); static SDL_bool VULKAN_FindMemoryTypeIndex(VULKAN_RenderData *rendererData, uint32_t typeBits, VkMemoryPropertyFlags requiredFlags, VkMemoryPropertyFlags desiredFlags, uint32_t *memoryTypeIndexOut); static VkResult VULKAN_CreateWindowSizeDependentResources(SDL_Renderer *renderer); static VkDescriptorPool VULKAN_AllocateDescriptorPool(VULKAN_RenderData *rendererData); +static VkResult VULKAN_CreateDescriptorSetAndPipelineLayout(VULKAN_RenderData *rendererData, VkSampler samplerYcbcr, VkDescriptorSetLayout *descriptorSetLayoutOut, VkPipelineLayout *pipelineLayoutOut); static void VULKAN_DestroyAll(SDL_Renderer *renderer) { @@ -531,14 +557,14 @@ static void VULKAN_DestroyAll(SDL_Renderer *renderer) vkDestroyShaderModule(rendererData->device, rendererData->fragmentShaderModules[i], NULL); rendererData->fragmentShaderModules[i] = VK_NULL_HANDLE; } - if (rendererData->descriptorSetLayouts[i] != VK_NULL_HANDLE) { - vkDestroyDescriptorSetLayout(rendererData->device, rendererData->descriptorSetLayouts[i], NULL); - rendererData->descriptorSetLayouts[i] = VK_NULL_HANDLE; - } - if (rendererData->pipelineLayouts[i] != VK_NULL_HANDLE) { - vkDestroyPipelineLayout(rendererData->device, rendererData->pipelineLayouts[i], NULL); - rendererData->pipelineLayouts[i] = VK_NULL_HANDLE; - } + } + if (rendererData->descriptorSetLayout != VK_NULL_HANDLE) { + vkDestroyDescriptorSetLayout(rendererData->device, rendererData->descriptorSetLayout, NULL); + rendererData->descriptorSetLayout = VK_NULL_HANDLE; + } + if (rendererData->pipelineLayout != VK_NULL_HANDLE) { + vkDestroyPipelineLayout(rendererData->device, rendererData->pipelineLayout, NULL); + rendererData->pipelineLayout = VK_NULL_HANDLE; } for (int i = 0; i < rendererData->pipelineStateCount; i++) { vkDestroyPipeline(rendererData->device, rendererData->pipelineStates[i].pipeline, NULL); @@ -672,12 +698,16 @@ static void VULKAN_DestroyImage(VULKAN_RenderData *rendererData, VULKAN_Image *v SDL_memset(vulkanImage, 0, sizeof(VULKAN_Image)); } -static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, uint32_t width, uint32_t height, VkFormat format, VkImageUsageFlags imageUsage, VkComponentMapping swizzle, VkImage externalImage, VULKAN_Image *imageOut) +static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, uint32_t width, uint32_t height, VkFormat format, + VkImageUsageFlags imageUsage, VkComponentMapping swizzle, VkImage externalImage, + VkSamplerYcbcrConversionKHR samplerYcbcrConversion, + VULKAN_Image *imageOut) { VkResult result; VkImageCreateInfo imageCreateInfo = { 0 }; + VkSamplerYcbcrConversionInfoKHR samplerYcbcrConversionInfo = { 0 }; - SDL_memset(imageOut, 0, sizeof( VULKAN_Image)); + SDL_memset(imageOut, 0, sizeof(VULKAN_Image)); imageOut->format = format; imageOut->imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; @@ -751,6 +781,14 @@ static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, uint32_t w imageViewCreateInfo.subresourceRange.levelCount = 1; imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; imageViewCreateInfo.subresourceRange.layerCount = 1; + + /* If it's a YcBcBr image, we need to pass the conversion info to the VkImageView (and the VkSampler) */ + if (samplerYcbcrConversion != VK_NULL_HANDLE) { + samplerYcbcrConversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR; + samplerYcbcrConversionInfo.conversion = samplerYcbcrConversion; + imageViewCreateInfo.pNext = &samplerYcbcrConversionInfo; + } + result = vkCreateImageView(rendererData->device, &imageViewCreateInfo, NULL, &imageOut->imageView); if (result != VK_SUCCESS) { VULKAN_DestroyImage(rendererData, imageOut); @@ -1034,7 +1072,7 @@ static VkBlendOp GetBlendOp(SDL_BlendOperation operation) static VULKAN_PipelineState *VULKAN_CreatePipelineState(SDL_Renderer *renderer, - VULKAN_Shader shader, SDL_BlendMode blendMode, VkPrimitiveTopology topology, VkFormat format) + VULKAN_Shader shader, VkPipelineLayout pipelineLayout, VkDescriptorSetLayout descriptorSetLayout, SDL_BlendMode blendMode, VkPrimitiveTopology topology, VkFormat format) { VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; VULKAN_PipelineState *pipelineStates; @@ -1159,7 +1197,7 @@ static VULKAN_PipelineState *VULKAN_CreatePipelineState(SDL_Renderer *renderer, /* Renderpass / layout */ pipelineCreateInfo.renderPass = rendererData->currentRenderPass; pipelineCreateInfo.subpass = 0; - pipelineCreateInfo.layout = rendererData->pipelineLayouts[shader]; + pipelineCreateInfo.layout = pipelineLayout; result = vkCreateGraphicsPipelines(rendererData->device, VK_NULL_HANDLE, 1, &pipelineCreateInfo, NULL, &pipeline); if (result != VK_SUCCESS) { @@ -1173,6 +1211,7 @@ static VULKAN_PipelineState *VULKAN_CreatePipelineState(SDL_Renderer *renderer, pipelineStates[rendererData->pipelineStateCount].topology = topology; pipelineStates[rendererData->pipelineStateCount].format = format; pipelineStates[rendererData->pipelineStateCount].pipeline = pipeline; + pipelineStates[rendererData->pipelineStateCount].descriptorSetLayout = descriptorSetLayout; pipelineStates[rendererData->pipelineStateCount].pipelineLayout = pipelineCreateInfo.layout; rendererData->pipelineStates = pipelineStates; ++rendererData->pipelineStateCount; @@ -1244,10 +1283,14 @@ static int VULKAN_LoadGlobalFunctions(VULKAN_RenderData *rendererData) return -1; \ } #define VULKAN_INSTANCE_FUNCTION(name) +#define VULKAN_OPTIONAL_INSTANCE_FUNCTION(name) +#define VULKAN_OPTIONAL_DEVICE_FUNCTION(name) VULKAN_FUNCTIONS() #undef VULKAN_DEVICE_FUNCTION #undef VULKAN_GLOBAL_FUNCTION #undef VULKAN_INSTANCE_FUNCTION +#undef VULKAN_OPTIONAL_INSTANCE_FUNCTION +#undef VULKAN_OPTIONAL_DEVICE_FUNCTION return 0; } @@ -1263,10 +1306,16 @@ static int VULKAN_LoadInstanceFunctions(VULKAN_RenderData *rendererData) "vkGetInstanceProcAddr(instance, \"" #name "\") failed\n"); \ return -1; \ } +#define VULKAN_OPTIONAL_INSTANCE_FUNCTION(name) \ + name = (PFN_##name)rendererData->vkGetInstanceProcAddr(rendererData->instance, #name); +#define VULKAN_OPTIONAL_DEVICE_FUNCTION(name) + VULKAN_FUNCTIONS() #undef VULKAN_DEVICE_FUNCTION #undef VULKAN_GLOBAL_FUNCTION #undef VULKAN_INSTANCE_FUNCTION +#undef VULKAN_OPTIONAL_INSTANCE_FUNCTION +#undef VULKAN_OPTIONAL_DEVICE_FUNCTION return 0; } @@ -1281,11 +1330,16 @@ static int VULKAN_LoadDeviceFunctions(VULKAN_RenderData *rendererData) return -1; \ } #define VULKAN_GLOBAL_FUNCTION(name) +#define VULKAN_OPTIONAL_DEVICE_FUNCTION(name) \ + name = (PFN_##name)vkGetDeviceProcAddr(rendererData->device, #name); #define VULKAN_INSTANCE_FUNCTION(name) +#define VULKAN_OPTIONAL_INSTANCE_FUNCTION(name) VULKAN_FUNCTIONS() #undef VULKAN_DEVICE_FUNCTION #undef VULKAN_GLOBAL_FUNCTION #undef VULKAN_INSTANCE_FUNCTION +#undef VULKAN_OPTIONAL_INSTANCE_FUNCTION +#undef VULKAN_OPTIONAL_DEVICE_FUNCTION return 0; } @@ -1476,6 +1530,40 @@ static VkSemaphore VULKAN_CreateSemaphore(VULKAN_RenderData *rendererData) return semaphore; } +static SDL_bool VULKAN_DeviceExtensionsFound(VULKAN_RenderData *rendererData, int extensionsToCheck, const char* const* extNames) +{ + uint32_t extensionCount; + SDL_bool foundExtensions = SDL_TRUE; + VkResult result = vkEnumerateDeviceExtensionProperties(rendererData->physicalDevice, NULL, &extensionCount, NULL); + if (result != VK_SUCCESS ) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkEnumerateDeviceExtensionProperties(): %s.\n", SDL_Vulkan_GetResultString(result)); + return SDL_FALSE; + } + if (extensionCount > 0 ) { + VkExtensionProperties *extensionProperties = SDL_calloc(sizeof(VkExtensionProperties), extensionCount); + result = vkEnumerateDeviceExtensionProperties(rendererData->physicalDevice, NULL, &extensionCount, extensionProperties); + if (result != VK_SUCCESS ) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkEnumerateDeviceExtensionProperties): %s.\n", SDL_Vulkan_GetResultString(result)); + SDL_free(extensionProperties); + return SDL_FALSE; + } + for (uint32_t ext = 0; ext < extensionsToCheck && foundExtensions; ext++) { + SDL_bool foundExtension = SDL_FALSE; + for (uint32_t i = 0; i< extensionCount; i++) { + if (SDL_strcmp(extensionProperties[i].extensionName, extNames[ext]) == 0) { + foundExtension = SDL_TRUE; + break; + } + } + foundExtensions &= foundExtension; + } + + SDL_free(extensionProperties); + } + + return foundExtensions; +} + static SDL_bool VULKAN_InstanceExtensionFound(VULKAN_RenderData *rendererData, const char *extName) { uint32_t extensionCount; @@ -1553,6 +1641,7 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert } /* Check for colorspace extension */ + rendererData->supportsEXTSwapchainColorspace = VK_FALSE; if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR || renderer->output_colorspace == SDL_COLORSPACE_HDR10) { rendererData->supportsEXTSwapchainColorspace = VULKAN_InstanceExtensionFound(rendererData, VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME); @@ -1561,6 +1650,9 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert } } + /* Check for VK_KHR_get_physical_device_properties2 */ + rendererData->supportsKHRGetPhysicalDeviceProperties2 = VULKAN_InstanceExtensionFound(rendererData, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); + /* Create VkInstance */ rendererData->instance = (VkInstance)SDL_GetProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER, NULL); if (rendererData->instance) { @@ -1574,7 +1666,7 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert instanceCreateInfo.pApplicationInfo = &appInfo; char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount); - const char **instanceExtensionsCopy = SDL_calloc(instanceCreateInfo.enabledExtensionCount + 1, sizeof(const char *)); + const char **instanceExtensionsCopy = SDL_calloc(instanceCreateInfo.enabledExtensionCount + 2, sizeof(const char *)); for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) { instanceExtensionsCopy[i] = instanceExtensions[i]; } @@ -1582,6 +1674,10 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount] = VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME; instanceCreateInfo.enabledExtensionCount++; } + if (rendererData->supportsKHRGetPhysicalDeviceProperties2) { + instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; + instanceCreateInfo.enabledExtensionCount++; + } instanceCreateInfo.ppEnabledExtensionNames = (const char *const *)instanceExtensionsCopy; if (createDebug && VULKAN_ValidationLayersFound()) { instanceCreateInfo.ppEnabledLayerNames = validationLayerName; @@ -1632,6 +1728,8 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert rendererData->presentQueueFamilyIndex = (uint32_t)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER, 0); } + + /* Create Vulkan device */ rendererData->device = (VkDevice)SDL_GetProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER, NULL); if (rendererData->device) { @@ -1642,13 +1740,24 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert VkDeviceCreateInfo deviceCreateInfo = { 0 }; static const char *const deviceExtensionNames[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, + /* VK_KHR_sampler_ycbcr_conversion + dependent extensions. + Note VULKAN_DeviceExtensionsFound() call below, if these get moved in this + array, update that check too. + */ + VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, + VK_KHR_MAINTENANCE1_EXTENSION_NAME, + VK_KHR_BIND_MEMORY_2_EXTENSION_NAME, + VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, }; - + if (rendererData->supportsKHRGetPhysicalDeviceProperties2 && + VULKAN_DeviceExtensionsFound(rendererData, 4, &deviceExtensionNames[1])) { + rendererData->supportsKHRSamplerYcBcrConversion = SDL_TRUE; + } deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; deviceCreateInfo.queueCreateInfoCount = 0; deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo; deviceCreateInfo.pEnabledFeatures = NULL; - deviceCreateInfo.enabledExtensionCount = SDL_arraysize(deviceExtensionNames); + deviceCreateInfo.enabledExtensionCount = (rendererData->supportsKHRSamplerYcBcrConversion) ? 5 : 1; deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames; deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; @@ -1721,73 +1830,14 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateShaderModule(): %s\n", SDL_Vulkan_GetResultString(result)); return result; } + } - /* Descriptor set layout */ - VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { 0 }; - descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; - descriptorSetLayoutCreateInfo.flags = 0; - VkDescriptorSetLayoutBinding layoutBindings[5]; - /* PixelShaderConstants */ - layoutBindings[0].binding = 4; - layoutBindings[0].descriptorCount = 1; - layoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - layoutBindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - layoutBindings[0].pImmutableSamplers = NULL; - - /* sampler0 */ - layoutBindings[1].binding = 0; - layoutBindings[1].descriptorCount = 1; - layoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; - layoutBindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - layoutBindings[1].pImmutableSamplers = NULL; - - /* texture0 */ - layoutBindings[2].binding = 1; - layoutBindings[2].descriptorCount = 1; - layoutBindings[2].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - layoutBindings[2].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - layoutBindings[2].pImmutableSamplers = NULL; - - /* texture1 */ - layoutBindings[3].binding = 2; - layoutBindings[3].descriptorCount = 1; - layoutBindings[3].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - layoutBindings[3].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - layoutBindings[3].pImmutableSamplers = NULL; - - /* texture2 */ - layoutBindings[4].binding = 3; - layoutBindings[4].descriptorCount = 1; - layoutBindings[4].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - layoutBindings[4].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; - layoutBindings[4].pImmutableSamplers = NULL; - - descriptorSetLayoutCreateInfo.bindingCount = 5; - descriptorSetLayoutCreateInfo.pBindings = layoutBindings; - result = vkCreateDescriptorSetLayout(rendererData->device, &descriptorSetLayoutCreateInfo, NULL, &rendererData->descriptorSetLayouts[i]); - if (result != VK_SUCCESS) { - VULKAN_DestroyAll(renderer); - SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateDescriptorSetLayout(): %s\n", SDL_Vulkan_GetResultString(result)); - return result; - } - - /* Pipeline layout */ - VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { 0 }; - VkPushConstantRange pushConstantRange; - pushConstantRange.size = sizeof( VertexShaderConstants ); - pushConstantRange.offset = 0; - pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - pipelineLayoutCreateInfo.setLayoutCount = 1; - pipelineLayoutCreateInfo.pSetLayouts = &rendererData->descriptorSetLayouts[i]; - pipelineLayoutCreateInfo.pushConstantRangeCount = 1; - pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange; - result = vkCreatePipelineLayout(rendererData->device, &pipelineLayoutCreateInfo, NULL, &rendererData->pipelineLayouts[i]); - if (result != VK_SUCCESS) { - VULKAN_DestroyAll(renderer); - SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreatePipelineLayout(): %s\n", SDL_Vulkan_GetResultString(result)); - return result; - } + /* Descriptor set layout / pipeline layout*/ + result = VULKAN_CreateDescriptorSetAndPipelineLayout(rendererData, VK_NULL_HANDLE, &rendererData->descriptorSetLayout, &rendererData->pipelineLayout); + if (result != VK_SUCCESS) { + VULKAN_DestroyAll(renderer); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_CreateDescriptorSetAndPipelineLayout(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; } /* Create default vertex buffers */ @@ -2367,7 +2417,7 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD VkFormat textureFormat = SDLPixelFormatToVkTextureFormat(texture->format, renderer->output_colorspace); uint32_t width = texture->w; uint32_t height = texture->h; - + VkComponentMapping imageViewSwizzle = rendererData->identitySwizzle; if (textureFormat == VK_FORMAT_UNDEFINED) { return SDL_SetError("%s, An unsupported SDL pixel format (0x%x) was specified", __FUNCTION__, texture->format); } @@ -2384,13 +2434,120 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD } textureData->scaleMode = (texture->scaleMode == SDL_SCALEMODE_NEAREST) ? VK_FILTER_NEAREST : VK_FILTER_LINEAR; - /* NV12 textures must have even width and height */ - if (texture->format == SDL_PIXELFORMAT_NV12 || +#if SDL_HAVE_YUV + /* YUV textures must have even width and height. Also create Ycbcr conversion */ + if (texture->format == SDL_PIXELFORMAT_YV12 || + texture->format == SDL_PIXELFORMAT_IYUV || + texture->format == SDL_PIXELFORMAT_NV12 || texture->format == SDL_PIXELFORMAT_NV21 || texture->format == SDL_PIXELFORMAT_P010) { + + /* Check that we have VK_KHR_sampler_ycbcr_conversion support */ + if (!rendererData->supportsKHRSamplerYcBcrConversion) { + SDL_free(textureData); + return SDL_SetError("[Vulkan] YUV textures require a Vulkan device that supports VK_KHR_sampler_ycbcr_conversion"); + } + VkSamplerYcbcrConversionCreateInfoKHR samplerYcbcrConversionCreateInfo = { 0 }; + samplerYcbcrConversionCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR; + + /* Pad width/height to multiple of 2 */ width = (width + 1) & ~1; height = (height + 1) & ~1; + + /* Create samplerYcbcrConversion which will be used on the VkImageView and VkSampler */ + samplerYcbcrConversionCreateInfo.format = textureFormat; + switch (SDL_COLORSPACEPRIMARIES(texture->colorspace)) { + case SDL_COLOR_PRIMARIES_BT709: + samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR; + break; + case SDL_COLOR_PRIMARIES_BT601: + samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR; + break; + case SDL_COLOR_PRIMARIES_BT2020: + samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR; + default: + VULKAN_DestroyTexture(renderer, texture); + return SDL_SetError("[Vulkan] Unsupported Ycbcr colorspace.\n"); + } + samplerYcbcrConversionCreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + samplerYcbcrConversionCreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + samplerYcbcrConversionCreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + samplerYcbcrConversionCreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + if (texture->format == SDL_PIXELFORMAT_YV12 || + texture->format == SDL_PIXELFORMAT_NV21) { + samplerYcbcrConversionCreateInfo.components.r = VK_COMPONENT_SWIZZLE_B; + samplerYcbcrConversionCreateInfo.components.b = VK_COMPONENT_SWIZZLE_R; + } + + switch (SDL_COLORSPACERANGE(texture->colorspace)) { + case SDL_COLOR_RANGE_LIMITED: + samplerYcbcrConversionCreateInfo.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR; + break; + case SDL_COLOR_RANGE_FULL: + default: + samplerYcbcrConversionCreateInfo.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR; + break; + } + + switch (SDL_COLORSPACECHROMA(texture->colorspace)) { + case SDL_CHROMA_LOCATION_LEFT: + samplerYcbcrConversionCreateInfo.xChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN_KHR; + samplerYcbcrConversionCreateInfo.yChromaOffset = VK_CHROMA_LOCATION_MIDPOINT_KHR; + break; + case SDL_CHROMA_LOCATION_TOPLEFT: + samplerYcbcrConversionCreateInfo.xChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN_KHR; + samplerYcbcrConversionCreateInfo.yChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN_KHR; + break; + case SDL_CHROMA_LOCATION_NONE: + case SDL_CHROMA_LOCATION_CENTER: + default: + samplerYcbcrConversionCreateInfo.xChromaOffset = VK_CHROMA_LOCATION_MIDPOINT_KHR; + samplerYcbcrConversionCreateInfo.yChromaOffset = VK_CHROMA_LOCATION_MIDPOINT_KHR; + break; + } + samplerYcbcrConversionCreateInfo.chromaFilter = VK_FILTER_LINEAR; + samplerYcbcrConversionCreateInfo.forceExplicitReconstruction = VK_FALSE; + + result = vkCreateSamplerYcbcrConversionKHR(rendererData->device, &samplerYcbcrConversionCreateInfo, NULL, &textureData->samplerYcbcrConversion); + if (result != VK_SUCCESS) { + VULKAN_DestroyTexture(renderer, texture); + return SDL_SetError("[Vulkan] vkCreateSamplerYcbcrConversionKHR %s.\n", SDL_Vulkan_GetResultString(result)); + } + + /* Also create VkSampler object which we will need to pass to the PSO as an immutable sampler */ + VkSamplerCreateInfo samplerCreateInfo = { 0 }; + samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerCreateInfo.magFilter = VK_FILTER_NEAREST; + samplerCreateInfo.minFilter = VK_FILTER_NEAREST; + samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerCreateInfo.mipLodBias = 0.0f; + samplerCreateInfo.anisotropyEnable = VK_FALSE; + samplerCreateInfo.maxAnisotropy = 1.0f; + samplerCreateInfo.minLod = 0.0f; + samplerCreateInfo.maxLod = 1000.0f; + + VkSamplerYcbcrConversionInfoKHR samplerYcbcrConversionInfo = { 0 }; + samplerYcbcrConversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR; + samplerYcbcrConversionInfo.conversion = textureData->samplerYcbcrConversion; + samplerCreateInfo.pNext = &samplerYcbcrConversionInfo; + result = vkCreateSampler(rendererData->device, &samplerCreateInfo, NULL, &textureData->samplerYcbcr); + if (result != VK_SUCCESS) { + VULKAN_DestroyTexture(renderer, texture); + return SDL_SetError("[Vulkan] vkCreateSampler %s.\n", SDL_Vulkan_GetResultString(result)); + } + + /* Allocate special descriptor set layout with samplerYcbcr baked as an immutable sampler */ + result = VULKAN_CreateDescriptorSetAndPipelineLayout(rendererData, textureData->samplerYcbcr, + &textureData->descriptorSetLayoutYcbcr, &textureData->pipelineLayoutYcbcr); + if (result != VK_SUCCESS) { + VULKAN_DestroyTexture(renderer, texture); + return SDL_SetError("[Vulkan] VULKAN_CreateDescriptorSetAndPipelineLayout %s.\n", SDL_Vulkan_GetResultString(result)); + } } +#endif textureData->width = width; textureData->height = height; @@ -2403,7 +2560,7 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD return -1; } - result = VULKAN_AllocateImage(rendererData, width, height, textureFormat, usage, rendererData->identitySwizzle, externalImage, &textureData->mainImage); + result = VULKAN_AllocateImage(rendererData, width, height, textureFormat, usage, imageViewSwizzle, externalImage, textureData->samplerYcbcrConversion, &textureData->mainImage); if (result != VK_SUCCESS) { VULKAN_DestroyTexture(renderer, texture); SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_AllocateImage(): %s\n", SDL_Vulkan_GetResultString(result)); @@ -2412,85 +2569,6 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD SDL_SetProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_VULKAN_TEXTURE_POINTER, &textureData->mainImage.image); - -#if SDL_HAVE_YUV - /* YUV Images */ - if (texture->format == SDL_PIXELFORMAT_YV12 || - texture->format == SDL_PIXELFORMAT_IYUV) { - textureData->yuv = SDL_TRUE; - - width = (width + 1) / 2; - height = (height + 1) / 2; - - /* Create U Image */ - if (GetTextureProperty(create_props, "vulkan.texture_u", &externalImage) < 0) { - return -1; - } - - result = VULKAN_AllocateImage(rendererData, width, height, textureFormat, usage, rendererData->identitySwizzle, externalImage, &textureData->mainImageU); - if (result != VK_SUCCESS) { - VULKAN_DestroyTexture(renderer, texture); - SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_AllocateImage(): %s\n", SDL_Vulkan_GetResultString(result)); - return result; - } - SDL_SetProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_VULKAN_TEXTURE_U_POINTER, &textureData->mainImageU.image); - - /* Create V image */ - if (GetTextureProperty(create_props, "vulkan.texture_v", &externalImage) < 0) { - return -1; - } - result = VULKAN_AllocateImage(rendererData, width, height, textureFormat, usage, rendererData->identitySwizzle, externalImage, &textureData->mainImageV); - if (result != VK_SUCCESS) { - VULKAN_DestroyTexture(renderer, texture); - SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_AllocateImage(): %s\n", SDL_Vulkan_GetResultString(result)); - return result; - } - SDL_SetProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_VULKAN_TEXTURE_V_POINTER, &textureData->mainImageV.image); - - textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, 8); - if (!textureData->YCbCr_matrix) { - return SDL_SetError("Unsupported YUV colorspace"); - } - } - else if (texture->format == SDL_PIXELFORMAT_NV12 || - texture->format == SDL_PIXELFORMAT_NV21 || - texture->format == SDL_PIXELFORMAT_P010) { - int bits_per_pixel; - VkFormat uvFormat = VK_FORMAT_R8G8_UNORM; - if (texture->format == SDL_PIXELFORMAT_P010) { - uvFormat = VK_FORMAT_R16G16_UNORM; - } - textureData->nv12 = SDL_TRUE; - - width = (width + 1) / 2; - height = (height + 1) / 2; - - /* Allocate interleaved UV plane as R8G8 */ - result = VULKAN_AllocateImage(rendererData, width, height, uvFormat, usage, rendererData->identitySwizzle, VK_NULL_HANDLE, &textureData->mainImageUV); - if (result != VK_SUCCESS) { - VULKAN_DestroyTexture(renderer, texture); - SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_AllocateImage(): %s\n", SDL_Vulkan_GetResultString(result)); - return result; - } - - switch (texture->format) { - case SDL_PIXELFORMAT_P010: - bits_per_pixel = 10; - break; - default: - bits_per_pixel = 8; - break; - } - - SDL_SetProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_VULKAN_TEXTURE_UV_POINTER, &textureData->mainImageUV.image); - - textureData->YCbCr_matrix = SDL_GetYCbCRtoRGBConversionMatrix(texture->colorspace, texture->w, texture->h, bits_per_pixel); - if (!textureData->YCbCr_matrix) { - return SDL_SetError("Unsupported YUV colorspace"); - } - } -#endif - if (texture->access == SDL_TEXTUREACCESS_TARGET) { result = VULKAN_CreateFramebuffersAndRenderPasses(renderer, texture->w, @@ -2527,9 +2605,22 @@ static void VULKAN_DestroyTexture(SDL_Renderer *renderer, VULKAN_DestroyImage(rendererData, &textureData->mainImage); #if SDL_HAVE_YUV - VULKAN_DestroyImage(rendererData, &textureData->mainImageU); - VULKAN_DestroyImage(rendererData, &textureData->mainImageV); - VULKAN_DestroyImage(rendererData, &textureData->mainImageUV); + if (textureData->samplerYcbcrConversion != VK_NULL_HANDLE) { + vkDestroySamplerYcbcrConversionKHR(rendererData->device, textureData->samplerYcbcrConversion, NULL); + textureData->samplerYcbcrConversion = VK_NULL_HANDLE; + } + if (textureData->samplerYcbcr != VK_NULL_HANDLE) { + vkDestroySampler(rendererData->device, textureData->samplerYcbcr, NULL); + textureData->samplerYcbcr = VK_NULL_HANDLE; + } + if (textureData->pipelineLayoutYcbcr != VK_NULL_HANDLE) { + vkDestroyPipelineLayout(rendererData->device, textureData->pipelineLayoutYcbcr, NULL); + textureData->pipelineLayoutYcbcr = VK_NULL_HANDLE; + } + if (textureData->descriptorSetLayoutYcbcr != VK_NULL_HANDLE) { + vkDestroyDescriptorSetLayout(rendererData->device, textureData->descriptorSetLayoutYcbcr, NULL); + textureData->descriptorSetLayoutYcbcr = VK_NULL_HANDLE; + } #endif VULKAN_DestroyBuffer(rendererData, &textureData->stagingBuffer); @@ -2556,6 +2647,7 @@ static VkResult VULKAN_UpdateTextureInternal(VULKAN_RenderData *rendererData, Vk const Uint8 *src; Uint8 *dst; VkResult result; + Uint32 planeCount = VULKAN_VkFormatGetNumPlanes(format); VULKAN_EnsureCommandBuffer(rendererData); @@ -2604,7 +2696,12 @@ static VkResult VULKAN_UpdateTextureInternal(VULKAN_RenderData *rendererData, Vk region.imageSubresource.baseArrayLayer = 0; region.imageSubresource.layerCount = 1; region.imageSubresource.mipLevel = 0; - region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + if (planeCount <= 1) { + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + } + else { + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT << plane; + } region.imageOffset.x = x; region.imageOffset.y = y; region.imageOffset.z = 0; @@ -2650,59 +2747,28 @@ static int VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, return -1; } #if SDL_HAVE_YUV - if (textureData->yuv) { - /* Skip to the correct offset into the next texture */ - srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch); - - if (VULKAN_UpdateTextureInternal(rendererData, - texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainImageV.image : textureData->mainImageU.image, - textureData->mainImageU.format, - 0, - rect->x / 2, - rect->y / 2, - (rect->w + 1) / 2, - (rect->h + 1) / 2, - srcPixels, - (srcPitch + 1) / 2, - texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainImageV.imageLayout : &textureData->mainImageU.imageLayout) < 0) { - return -1; - } + Uint32 numPlanes = VULKAN_VkFormatGetNumPlanes(textureData->mainImage.format); + /* Skip to the correct offset into the next texture */ + srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch); + // YUV data + if (numPlanes == 3) { + for (Uint32 plane = 1; plane < numPlanes; plane++) { + if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, plane, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) / 2, &textureData->mainImage.imageLayout) < 0) { + return -1; + } - /* Skip to the correct offset into the next texture */ - srcPixels = (const void *)((const Uint8 *)srcPixels + ((rect->h + 1) / 2) * ((srcPitch + 1) / 2)); - if (VULKAN_UpdateTextureInternal(rendererData, - texture->format == SDL_PIXELFORMAT_YV12 ? textureData->mainImageU.image : textureData->mainImageV.image, - textureData->mainImageV.format, - 0, - rect->x / 2, - rect->y / 2, - (rect->w + 1) / 2, - (rect->h + 1) / 2, - srcPixels, - (srcPitch + 1) / 2, - texture->format == SDL_PIXELFORMAT_YV12 ? &textureData->mainImageU.imageLayout : &textureData->mainImageV.imageLayout) < 0) { - return -1; + /* Skip to the correct offset into the next texture */ + srcPixels = (const void *)((const Uint8 *)srcPixels + ((rect->h + 1) / 2) * ((srcPitch + 1) / 2)); } } - if (textureData->nv12) { - /* Skip to the correct offset into the next texture */ - srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch); - - if (VULKAN_UpdateTextureInternal(rendererData, - textureData->mainImageUV.image, - textureData->mainImageUV.format, - 1, - rect->x / 2, - rect->y / 2, - (rect->w + 1) / 2, - (rect->h + 1) / 2, - srcPixels, - srcPitch, - &textureData->mainImageUV.imageLayout) < 0) { - return -1; + // NV12/NV21 data + else if (numPlanes == 2) + { + if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 1, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) & ~1, &textureData->mainImage.imageLayout) < 0) { + return -1; } } -#endif /* SDL_HAVE_YUV */ +#endif return 0; } @@ -2723,10 +2789,10 @@ static int VULKAN_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 0, rect->x, rect->y, rect->w, rect->h, Yplane, Ypitch, &textureData->mainImage.imageLayout) < 0) { return -1; } - if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImageU.image, textureData->mainImageU.format, 0, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Uplane, Upitch, &textureData->mainImageU.imageLayout) < 0) { + if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 1, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Uplane, Upitch, &textureData->mainImage.imageLayout) < 0) { return -1; } - if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImageV.image, textureData->mainImageV.format, 0, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Vplane, Vpitch, &textureData->mainImageV.imageLayout) < 0) { + if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 2, rect->x / 2, rect->y / 2, rect->w / 2, rect->h / 2, Vplane, Vpitch, &textureData->mainImage.imageLayout) < 0) { return -1; } return 0; @@ -2748,7 +2814,7 @@ static int VULKAN_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture, return -1; } - if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImageUV.image, textureData->mainImageUV.format, 1, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, UVplane, UVpitch, &textureData->mainImageUV.imageLayout) < 0) { + if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 1, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, UVplane, UVpitch, &textureData->mainImage.imageLayout) < 0) { return -1; } return 0; @@ -3107,23 +3173,14 @@ static void VULKAN_SetupShaderConstants(SDL_Renderer *renderer, const SDL_Render switch (texture->format) { case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: - constants->texture_type = TEXTURETYPE_YUV; - constants->input_type = INPUTTYPE_SRGB; - break; case SDL_PIXELFORMAT_NV12: - constants->texture_type = TEXTURETYPE_NV12; - constants->input_type = INPUTTYPE_SRGB; - break; case SDL_PIXELFORMAT_NV21: - constants->texture_type = TEXTURETYPE_NV21; constants->input_type = INPUTTYPE_SRGB; break; case SDL_PIXELFORMAT_P010: - constants->texture_type = TEXTURETYPE_NV12; constants->input_type = INPUTTYPE_HDR10; break; default: - constants->texture_type = TEXTURETYPE_RGB; if (texture->colorspace == SDL_COLORSPACE_SRGB_LINEAR) { constants->input_type = INPUTTYPE_SCRGB; } else if (SDL_COLORSPACEPRIMARIES(texture->colorspace) == SDL_COLOR_PRIMARIES_BT2020 && @@ -3163,10 +3220,8 @@ static VkDescriptorPool VULKAN_AllocateDescriptorPool(VULKAN_RenderData *rendere descriptorPoolSizes[0].descriptorCount = SDL_VULKAN_MAX_DESCRIPTOR_SETS; descriptorPoolSizes[0].type = VK_DESCRIPTOR_TYPE_SAMPLER; - /* Allocate enough to hold a maximum of each descriptor set having YUV textures */ - const int numTexturesPerYUV = 3; - descriptorPoolSizes[1].descriptorCount = SDL_VULKAN_MAX_DESCRIPTOR_SETS * numTexturesPerYUV; - descriptorPoolSizes[1].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; + descriptorPoolSizes[1].descriptorCount = SDL_VULKAN_MAX_DESCRIPTOR_SETS; + descriptorPoolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = { 0 }; descriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; @@ -3182,7 +3237,60 @@ static VkDescriptorPool VULKAN_AllocateDescriptorPool(VULKAN_RenderData *rendere return descriptorPool; } -static VkDescriptorSet VULKAN_AllocateDescriptorSet(SDL_Renderer *renderer, VULKAN_Shader shader, VkSampler sampler, VkBuffer constantBuffer, VkDeviceSize constantBufferOffset, int imageViewCount, VkImageView *imageViews) +static VkResult VULKAN_CreateDescriptorSetAndPipelineLayout(VULKAN_RenderData *rendererData, VkSampler samplerYcbcr, VkDescriptorSetLayout *descriptorSetLayoutOut, + VkPipelineLayout *pipelineLayoutOut) +{ + VkResult result; + + /* Descriptor set layout */ + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { 0 }; + descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + descriptorSetLayoutCreateInfo.flags = 0; + VkDescriptorSetLayoutBinding layoutBindings[2]; + /* PixelShaderConstants */ + layoutBindings[0].binding = 1; + layoutBindings[0].descriptorCount = 1; + layoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; + layoutBindings[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + layoutBindings[0].pImmutableSamplers = NULL; + + /* Combined image/sampler */ + layoutBindings[1].binding = 0; + layoutBindings[1].descriptorCount = 1; + layoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + layoutBindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + layoutBindings[1].pImmutableSamplers = (samplerYcbcr != VK_NULL_HANDLE) ? &samplerYcbcr : NULL; + + descriptorSetLayoutCreateInfo.bindingCount = 2; + descriptorSetLayoutCreateInfo.pBindings = layoutBindings; + result = vkCreateDescriptorSetLayout(rendererData->device, &descriptorSetLayoutCreateInfo, NULL, descriptorSetLayoutOut); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateDescriptorSetLayout(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + /* Pipeline layout */ + VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { 0 }; + VkPushConstantRange pushConstantRange; + pushConstantRange.size = sizeof( VertexShaderConstants ); + pushConstantRange.offset = 0; + pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutCreateInfo.setLayoutCount = 1; + pipelineLayoutCreateInfo.pSetLayouts = descriptorSetLayoutOut; + pipelineLayoutCreateInfo.pushConstantRangeCount = 1; + pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange; + result = vkCreatePipelineLayout(rendererData->device, &pipelineLayoutCreateInfo, NULL, pipelineLayoutOut); + if (result != VK_SUCCESS) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreatePipelineLayout(): %s\n", SDL_Vulkan_GetResultString(result)); + return result; + } + + return result; +} + +static VkDescriptorSet VULKAN_AllocateDescriptorSet(SDL_Renderer *renderer, VULKAN_Shader shader, VkDescriptorSetLayout descriptorSetLayout, + VkSampler sampler, VkBuffer constantBuffer, VkDeviceSize constantBufferOffset, VkImageView imageView) { VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; uint32_t currentDescriptorPoolIndex = rendererData->currentDescriptorPoolIndex; @@ -3192,7 +3300,7 @@ static VkDescriptorSet VULKAN_AllocateDescriptorSet(SDL_Renderer *renderer, VULK descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; descriptorSetAllocateInfo.descriptorSetCount = 1; descriptorSetAllocateInfo.descriptorPool = descriptorPool; - descriptorSetAllocateInfo.pSetLayouts = &rendererData->descriptorSetLayouts[shader]; + descriptorSetAllocateInfo.pSetLayouts = &descriptorSetLayout; VkDescriptorSet descriptorSet = VK_NULL_HANDLE; VkResult result = (rendererData->currentDescriptorSetIndex >= SDL_VULKAN_MAX_DESCRIPTOR_SETS) ? VK_ERROR_OUT_OF_DEVICE_MEMORY : VK_SUCCESS; @@ -3231,71 +3339,58 @@ static VkDescriptorSet VULKAN_AllocateDescriptorSet(SDL_Renderer *renderer, VULK rendererData->currentDescriptorSetIndex = 0; /* Call recursively to allocate from the new pool */ - return VULKAN_AllocateDescriptorSet(renderer, shader, sampler, constantBuffer, constantBufferOffset, imageViewCount, imageViews); + return VULKAN_AllocateDescriptorSet(renderer, shader, descriptorSetLayout, sampler, constantBuffer, constantBufferOffset, imageView); } } rendererData->currentDescriptorSetIndex++; - VkDescriptorImageInfo samplerDescriptor = { 0 }; - samplerDescriptor.sampler = sampler; - - VkDescriptorImageInfo imageDescriptors[3]; - SDL_memset(imageDescriptors, 0, sizeof(imageDescriptors)); + VkDescriptorImageInfo combinedImageSamplerDescriptor = { 0 }; VkDescriptorBufferInfo bufferDescriptor = { 0 }; bufferDescriptor.buffer = constantBuffer; bufferDescriptor.offset = constantBufferOffset; bufferDescriptor.range = sizeof(PixelShaderConstants); - VkWriteDescriptorSet descriptorWrites[5]; + VkWriteDescriptorSet descriptorWrites[2]; SDL_memset(descriptorWrites, 0, sizeof(descriptorWrites)); uint32_t descriptorCount = 1; /* Always have the uniform buffer */ descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrites[0].dstSet = descriptorSet; - descriptorWrites[0].dstBinding = 4; + descriptorWrites[0].dstBinding = 1; descriptorWrites[0].dstArrayElement = 0; descriptorWrites[0].descriptorCount = 1; descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; descriptorWrites[0].pBufferInfo = &bufferDescriptor; - if (sampler != VK_NULL_HANDLE) { + if (sampler != VK_NULL_HANDLE && imageView != VK_NULL_HANDLE) { descriptorCount++; descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrites[1].dstSet = descriptorSet; descriptorWrites[1].dstBinding = 0; descriptorWrites[1].dstArrayElement = 0; descriptorWrites[1].descriptorCount = 1; - descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; - descriptorWrites[1].pImageInfo = &samplerDescriptor; - } + descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorWrites[1].pImageInfo = &combinedImageSamplerDescriptor; - uint32_t startImageViews = descriptorCount; - for (int i = 0; i < 3 && imageViewCount > 0; i++) { - descriptorCount++; - imageDescriptors[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - /* There are up to 3 images in the shader, if we haven't specified that many, duplicate the first - one. There is dynamic branching that determines how many actually get fetched, but we need - them all populated for validation. */ - imageDescriptors[i].imageView = (i < imageViewCount) ? imageViews[i] : imageViews[0]; - descriptorWrites[i+startImageViews].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - descriptorWrites[i+startImageViews].dstSet = descriptorSet; - descriptorWrites[i+startImageViews].dstBinding = 1 + i; - descriptorWrites[i+startImageViews].dstArrayElement = 0; - descriptorWrites[i+startImageViews].descriptorCount = 1; - descriptorWrites[i+startImageViews].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - descriptorWrites[i+startImageViews].pImageInfo = &imageDescriptors[i]; + /* Ignore the sampler if we're using YcBcCr data since it will be baked in the descriptor set layout */ + if (descriptorSetLayout == rendererData->descriptorSetLayout) { + combinedImageSamplerDescriptor.sampler = sampler; + } + combinedImageSamplerDescriptor.imageView = imageView; + combinedImageSamplerDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } + vkUpdateDescriptorSets(rendererData->device, descriptorCount, descriptorWrites, 0, NULL); return descriptorSet; } -static SDL_bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, VULKAN_Shader shader, const PixelShaderConstants *shader_constants, - VkPrimitiveTopology topology, int imageViewCount, VkImageView *imageViews, VkSampler sampler, const Float4X4 *matrix, VULKAN_DrawStateCache *stateCache) +static SDL_bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, VULKAN_Shader shader, VkPipelineLayout pipelineLayout, VkDescriptorSetLayout descriptorSetLayout, + const PixelShaderConstants *shader_constants, VkPrimitiveTopology topology, VkImageView imageView, VkSampler sampler, const Float4X4 *matrix, VULKAN_DrawStateCache *stateCache) { VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; const SDL_BlendMode blendMode = cmd->data.draw.blend; - VkFormat format = rendererData->surfaceFormat.format; // TEMP + VkFormat format = rendererData->surfaceFormat.format; const Float4X4 *newmatrix = matrix ? matrix : &rendererData->identity; SDL_bool updateConstants = SDL_FALSE; PixelShaderConstants solid_constants; @@ -3313,7 +3408,9 @@ static SDL_bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderComm rendererData->currentPipelineState->shader != shader || rendererData->currentPipelineState->blendMode != blendMode || rendererData->currentPipelineState->topology != topology || - rendererData->currentPipelineState->format != format) { + rendererData->currentPipelineState->format != format || + rendererData->currentPipelineState->pipelineLayout != pipelineLayout || + rendererData->currentPipelineState->descriptorSetLayout != descriptorSetLayout) { rendererData->currentPipelineState = NULL; for (i = 0; i < rendererData->pipelineStateCount; ++i) { @@ -3321,7 +3418,9 @@ static SDL_bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderComm if (candidatePiplineState->shader == shader && candidatePiplineState->blendMode == blendMode && candidatePiplineState->topology == topology && - candidatePiplineState->format == format) { + candidatePiplineState->format == format && + candidatePiplineState->pipelineLayout == pipelineLayout && + candidatePiplineState->descriptorSetLayout == descriptorSetLayout) { rendererData->currentPipelineState = candidatePiplineState; break; } @@ -3329,7 +3428,7 @@ static SDL_bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderComm /* If we didn't find a match, create a new one -- it must mean the blend mode is non-standard */ if (!rendererData->currentPipelineState) { - rendererData->currentPipelineState = VULKAN_CreatePipelineState(renderer, shader, blendMode, topology, format); + rendererData->currentPipelineState = VULKAN_CreatePipelineState(renderer, shader, pipelineLayout, descriptorSetLayout, blendMode, topology, format); } if (!rendererData->currentPipelineState) { @@ -3421,7 +3520,7 @@ static SDL_bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderComm } /* Allocate/update descriptor set with the bindings */ - descriptorSet = VULKAN_AllocateDescriptorSet(renderer, shader, sampler, constantBuffer, constantBufferOffset, imageViewCount, imageViews); + descriptorSet = VULKAN_AllocateDescriptorSet(renderer, shader, descriptorSetLayout, sampler, constantBuffer, constantBufferOffset, imageView); if (descriptorSet == VK_NULL_HANDLE) { return SDL_FALSE; } @@ -3441,6 +3540,8 @@ static SDL_bool VULKAN_SetCopyState(SDL_Renderer *renderer, const SDL_RenderComm VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->driverdata; VkSampler textureSampler = VK_NULL_HANDLE; PixelShaderConstants constants; + VkDescriptorSetLayout descriptorSetLayout = (textureData->descriptorSetLayoutYcbcr != VK_NULL_HANDLE) ? textureData->descriptorSetLayoutYcbcr : rendererData->descriptorSetLayout; + VkPipelineLayout pipelineLayout = (textureData->pipelineLayoutYcbcr != VK_NULL_HANDLE) ? textureData->pipelineLayoutYcbcr : rendererData->pipelineLayout; VULKAN_SetupShaderConstants(renderer, cmd, texture, &constants); @@ -3455,7 +3556,6 @@ static SDL_bool VULKAN_SetCopyState(SDL_Renderer *renderer, const SDL_RenderComm return SDL_SetError("Unknown scale mode: %d\n", textureData->scaleMode); } - if (textureData->mainImage.imageLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { SDL_bool stoppedRenderPass = SDL_FALSE; if (rendererData->currentRenderPass != VK_NULL_HANDLE) { @@ -3478,57 +3578,7 @@ static SDL_bool VULKAN_SetCopyState(SDL_Renderer *renderer, const SDL_RenderComm } } -#if SDL_HAVE_YUV - if (textureData->yuv) { - - /* Make sure each texture is in the correct state to be accessed by the pixel shader. */ - VULKAN_RecordPipelineImageBarrier(rendererData, - VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - textureData->mainImageU.image, - &textureData->mainImageU.imageLayout); - VULKAN_RecordPipelineImageBarrier(rendererData, - VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - textureData->mainImageV.image, - &textureData->mainImageV.imageLayout); - - VkImageView imageViews[3] = { - textureData->mainImage.imageView, - textureData->mainImageU.imageView, - textureData->mainImageV.imageView - }; - - return VULKAN_SetDrawState(renderer, cmd, textureData->shader, &constants, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, SDL_arraysize(imageViews), imageViews, - textureSampler, matrix, stateCache); - } else if (textureData->nv12) { - - /* Make sure each texture is in the correct state to be accessed by the pixel shader. */ - VULKAN_RecordPipelineImageBarrier(rendererData, - VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT, - VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - textureData->mainImageUV.image, - &textureData->mainImageUV.imageLayout); - - VkImageView imageViews[2] = { - textureData->mainImage.imageView, - textureData->mainImageUV.imageView, - }; - - return VULKAN_SetDrawState(renderer, cmd, textureData->shader, &constants, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, SDL_arraysize(imageViews), imageViews, - textureSampler, matrix, stateCache); - } -#endif - return VULKAN_SetDrawState(renderer, cmd, textureData->shader, &constants, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 1, &textureData->mainImage.imageView, textureSampler, matrix, stateCache); + return VULKAN_SetDrawState(renderer, cmd, textureData->shader, pipelineLayout, descriptorSetLayout, &constants, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, textureData->mainImage.imageView, textureSampler, matrix, stateCache); } static void VULKAN_DrawPrimitives(SDL_Renderer *renderer, VkPrimitiveTopology primitiveTopology, const size_t vertexStart, const size_t vertexCount) @@ -3618,7 +3668,7 @@ static int VULKAN_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd const size_t count = cmd->data.draw.count; const size_t first = cmd->data.draw.first; const size_t start = first / sizeof(VertexPositionColor); - VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, NULL, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, 0, NULL, VK_NULL_HANDLE, NULL, &stateCache); + VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, rendererData->pipelineLayout, rendererData->descriptorSetLayout, NULL, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, VK_NULL_HANDLE, VK_NULL_HANDLE, NULL, &stateCache); VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, start, count); break; } @@ -3629,10 +3679,10 @@ static int VULKAN_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd const size_t first = cmd->data.draw.first; const size_t start = first / sizeof(VertexPositionColor); const VertexPositionColor *verts = (VertexPositionColor *)(((Uint8 *)vertices) + first); - VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, NULL, VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, 0, NULL, VK_NULL_HANDLE, NULL, &stateCache); + VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, rendererData->pipelineLayout, rendererData->descriptorSetLayout, NULL, VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, VK_NULL_HANDLE, VK_NULL_HANDLE, NULL, &stateCache); VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_LINE_STRIP, start, count); if (verts[0].pos[0] != verts[count - 1].pos[0] || verts[0].pos[1] != verts[count - 1].pos[1]) { - VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, NULL, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, 0, NULL, VK_NULL_HANDLE, NULL, &stateCache); + VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, rendererData->pipelineLayout, rendererData->descriptorSetLayout, NULL, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, VK_NULL_HANDLE, VK_NULL_HANDLE, NULL, &stateCache); VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_POINT_LIST, start + (count - 1), 1); } break; @@ -3657,7 +3707,7 @@ static int VULKAN_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd if (texture) { VULKAN_SetCopyState(renderer, cmd, NULL, &stateCache); } else { - VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, NULL, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, NULL, VK_NULL_HANDLE, NULL, &stateCache); + VULKAN_SetDrawState(renderer, cmd, SHADER_SOLID, rendererData->pipelineLayout, rendererData->descriptorSetLayout, NULL, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, VK_NULL_HANDLE, VK_NULL_HANDLE, NULL, &stateCache); } VULKAN_DrawPrimitives(renderer, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, start, count); diff --git a/src/render/vulkan/VULKAN_PixelShader_Advanced.h b/src/render/vulkan/VULKAN_PixelShader_Advanced.h index 9a100f27a0de1..0ac041591c8b4 100644 --- a/src/render/vulkan/VULKAN_PixelShader_Advanced.h +++ b/src/render/vulkan/VULKAN_PixelShader_Advanced.h @@ -1,25 +1,23 @@ // 1113.1.1 #pragma once const uint32_t VULKAN_PixelShader_Advanced[] = { - 0x07230203,0x00010000,0x0008000b,0x000005e4,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x07230203,0x00010000,0x0008000b,0x0000043e,0x00000000,0x00020011,0x00000001,0x0006000b, 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, - 0x0008000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000275,0x00000278,0x0000027c, + 0x0008000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x000001bb,0x000001be,0x000001c2, 0x00030010,0x00000004,0x00000007,0x00030003,0x00000005,0x000001f4,0x00040005,0x00000004, 0x6e69616d,0x00000000,0x00050005,0x00000075,0x736e6f43,0x746e6174,0x00000073,0x00070006, - 0x00000075,0x00000000,0x47526373,0x756f5f42,0x74757074,0x00000000,0x00070006,0x00000075, - 0x00000001,0x74786574,0x5f657275,0x65707974,0x00000000,0x00060006,0x00000075,0x00000002, - 0x75706e69,0x79745f74,0x00006570,0x00060006,0x00000075,0x00000003,0x6f6c6f63,0x63735f72, - 0x00656c61,0x00070006,0x00000075,0x00000004,0x656e6f74,0x5f70616d,0x6874656d,0x0000646f, - 0x00070006,0x00000075,0x00000005,0x656e6f74,0x5f70616d,0x74636166,0x0031726f,0x00070006, - 0x00000075,0x00000006,0x656e6f74,0x5f70616d,0x74636166,0x0032726f,0x00070006,0x00000075, - 0x00000007,0x5f726473,0x74696877,0x6f705f65,0x00746e69,0x00050006,0x00000075,0x00000008, - 0x66666f59,0x00746573,0x00050006,0x00000075,0x00000009,0x656f6352,0x00006666,0x00050006, - 0x00000075,0x0000000a,0x656f6347,0x00006666,0x00050006,0x00000075,0x0000000b,0x656f6342, - 0x00006666,0x00030005,0x00000077,0x00000000,0x00050005,0x000000f2,0x74786574,0x30657275, - 0x00000000,0x00050005,0x000000f6,0x706d6173,0x3072656c,0x00000000,0x00050005,0x0000010d, - 0x74786574,0x31657275,0x00000000,0x00050005,0x00000182,0x74786574,0x32657275,0x00000000, - 0x00050005,0x00000275,0x75706e69,0x65742e74,0x00000078,0x00050005,0x00000278,0x75706e69, - 0x6f632e74,0x00726f6c,0x00070005,0x0000027c,0x746e6540,0x6f507972,0x4f746e69,0x75707475, + 0x00000075,0x00000000,0x47526373,0x756f5f42,0x74757074,0x00000000,0x00060006,0x00000075, + 0x00000001,0x75706e69,0x79745f74,0x00006570,0x00060006,0x00000075,0x00000002,0x6f6c6f63, + 0x63735f72,0x00656c61,0x00060006,0x00000075,0x00000003,0x73756e75,0x705f6465,0x00306461, + 0x00070006,0x00000075,0x00000004,0x656e6f74,0x5f70616d,0x6874656d,0x0000646f,0x00070006, + 0x00000075,0x00000005,0x656e6f74,0x5f70616d,0x74636166,0x0031726f,0x00070006,0x00000075, + 0x00000006,0x656e6f74,0x5f70616d,0x74636166,0x0032726f,0x00070006,0x00000075,0x00000007, + 0x5f726473,0x74696877,0x6f705f65,0x00746e69,0x00050006,0x00000075,0x00000008,0x66666f59, + 0x00746573,0x00050006,0x00000075,0x00000009,0x656f6352,0x00006666,0x00050006,0x00000075, + 0x0000000a,0x656f6347,0x00006666,0x00050006,0x00000075,0x0000000b,0x656f6342,0x00006666, + 0x00030005,0x00000077,0x00000000,0x00050005,0x000000e6,0x74786574,0x30657275,0x00000000, + 0x00050005,0x000001bb,0x75706e69,0x65742e74,0x00000078,0x00050005,0x000001be,0x75706e69, + 0x6f632e74,0x00726f6c,0x00070005,0x000001c2,0x746e6540,0x6f507972,0x4f746e69,0x75707475, 0x00000074,0x00050048,0x00000075,0x00000000,0x00000023,0x00000000,0x00050048,0x00000075, 0x00000001,0x00000023,0x00000004,0x00050048,0x00000075,0x00000002,0x00000023,0x00000008, 0x00050048,0x00000075,0x00000003,0x00000023,0x0000000c,0x00050048,0x00000075,0x00000004, @@ -28,13 +26,10 @@ const uint32_t VULKAN_PixelShader_Advanced[] = { 0x0000001c,0x00050048,0x00000075,0x00000008,0x00000023,0x00000020,0x00050048,0x00000075, 0x00000009,0x00000023,0x00000030,0x00050048,0x00000075,0x0000000a,0x00000023,0x00000040, 0x00050048,0x00000075,0x0000000b,0x00000023,0x00000050,0x00030047,0x00000075,0x00000002, - 0x00040047,0x00000077,0x00000022,0x00000000,0x00040047,0x00000077,0x00000021,0x00000004, - 0x00040047,0x000000f2,0x00000022,0x00000000,0x00040047,0x000000f2,0x00000021,0x00000001, - 0x00040047,0x000000f6,0x00000022,0x00000000,0x00040047,0x000000f6,0x00000021,0x00000000, - 0x00040047,0x0000010d,0x00000022,0x00000000,0x00040047,0x0000010d,0x00000021,0x00000002, - 0x00040047,0x00000182,0x00000022,0x00000000,0x00040047,0x00000182,0x00000021,0x00000003, - 0x00040047,0x00000275,0x0000001e,0x00000000,0x00040047,0x00000278,0x0000001e,0x00000001, - 0x00040047,0x0000027c,0x0000001e,0x00000000,0x00020013,0x00000002,0x00030021,0x00000003, + 0x00040047,0x00000077,0x00000022,0x00000000,0x00040047,0x00000077,0x00000021,0x00000001, + 0x00040047,0x000000e6,0x00000022,0x00000000,0x00040047,0x000000e6,0x00000021,0x00000000, + 0x00040047,0x000001bb,0x0000001e,0x00000000,0x00040047,0x000001be,0x0000001e,0x00000001, + 0x00040047,0x000001c2,0x0000001e,0x00000000,0x00020013,0x00000002,0x00030021,0x00000003, 0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,0x0000000f,0x00000006,0x00000003, 0x00040017,0x00000018,0x00000006,0x00000004,0x00040017,0x00000019,0x00000006,0x00000002, 0x0004002b,0x00000006,0x00000032,0x3d25aee6,0x00020014,0x00000033,0x0004002b,0x00000006, @@ -53,7 +48,7 @@ const uint32_t VULKAN_PixelShader_Advanced[] = { 0x00000079,0x00000007,0x00040020,0x0000007a,0x00000002,0x00000006,0x0004002b,0x00000078, 0x00000081,0x00000004,0x0004002b,0x00000006,0x00000084,0x3f800000,0x0004002b,0x00000078, 0x00000088,0x00000005,0x0004002b,0x00000006,0x00000090,0x40000000,0x0004002b,0x00000078, - 0x00000094,0x00000002,0x00040018,0x0000009b,0x0000000f,0x00000003,0x0004002b,0x00000006, + 0x00000094,0x00000001,0x00040018,0x0000009b,0x0000000f,0x00000003,0x0004002b,0x00000006, 0x0000009c,0x3f209d8c,0x0004002b,0x00000006,0x0000009d,0x3ea897c8,0x0004002b,0x00000006, 0x0000009e,0x3d3168f9,0x0006002c,0x0000000f,0x0000009f,0x0000009c,0x0000009d,0x0000009e, 0x0004002b,0x00000006,0x000000a0,0x3d8d82ba,0x0004002b,0x00000006,0x000000a1,0x3f6b670a, @@ -69,305 +64,214 @@ const uint32_t VULKAN_PixelShader_Advanced[] = { 0x000000d6,0x000000d7,0x0004002b,0x00000006,0x000000d9,0xbc94b7b3,0x0004002b,0x00000006, 0x000000da,0xbdce05cd,0x0004002b,0x00000006,0x000000db,0x3f8f333c,0x0006002c,0x0000000f, 0x000000dc,0x000000d9,0x000000da,0x000000db,0x0006002c,0x0000009b,0x000000dd,0x000000d4, - 0x000000d8,0x000000dc,0x0004002b,0x00000078,0x000000e2,0x00000001,0x0007002c,0x00000018, - 0x000000e9,0x00000084,0x00000084,0x00000084,0x00000084,0x00090019,0x000000f0,0x00000006, - 0x00000001,0x00000000,0x00000000,0x00000000,0x00000001,0x00000000,0x00040020,0x000000f1, - 0x00000000,0x000000f0,0x0004003b,0x000000f1,0x000000f2,0x00000000,0x0002001a,0x000000f4, - 0x00040020,0x000000f5,0x00000000,0x000000f4,0x0004003b,0x000000f5,0x000000f6,0x00000000, - 0x0003001b,0x000000f8,0x000000f0,0x0004003b,0x000000f1,0x0000010d,0x00000000,0x0004002b, - 0x00000078,0x00000119,0x00000008,0x00040020,0x0000011a,0x00000002,0x00000018,0x0004002b, - 0x00000078,0x00000121,0x00000009,0x0004002b,0x00000078,0x00000128,0x0000000a,0x0004002b, - 0x00000078,0x0000012f,0x0000000b,0x0004002b,0x00000006,0x0000013a,0x40400000,0x0004002b, - 0x00000006,0x0000016d,0x40800000,0x0004003b,0x000000f1,0x00000182,0x00000000,0x0004002b, - 0x00000078,0x000001ae,0x00000003,0x0004002b,0x00000078,0x000001be,0x00000000,0x00040020, - 0x00000270,0x00000001,0x00000018,0x00040020,0x00000274,0x00000001,0x00000019,0x0004003b, - 0x00000274,0x00000275,0x00000001,0x0004003b,0x00000270,0x00000278,0x00000001,0x00040020, - 0x0000027b,0x00000003,0x00000018,0x0004003b,0x0000027b,0x0000027c,0x00000003,0x0006002c, - 0x0000000f,0x0000059a,0x0000005d,0x0000005d,0x0000005d,0x0006002c,0x0000000f,0x0000059b, - 0x00000064,0x00000064,0x00000064,0x0006002c,0x0000000f,0x000005a0,0x00000084,0x00000084, - 0x00000084,0x0004002b,0x00000006,0x000005a6,0x3f72a76f,0x0004002b,0x00000006,0x000005a7, - 0x3d9e8391,0x0007002c,0x00000018,0x000005a9,0x00000084,0x00000060,0x00000060,0x00000084, - 0x0004002b,0x00000006,0x000005aa,0xbd6147ae,0x00030001,0x00000018,0x000005e3,0x00050036, - 0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003d,0x00000019, - 0x00000276,0x00000275,0x0004003d,0x00000018,0x00000279,0x00000278,0x00050041,0x0000007a, - 0x000002f9,0x00000077,0x000000e2,0x0004003d,0x00000006,0x000002fa,0x000002f9,0x000500b4, - 0x00000033,0x000002fb,0x000002fa,0x00000060,0x000300f7,0x000003a6,0x00000000,0x000400fa, - 0x000002fb,0x000002fc,0x000002fd,0x000200f8,0x000002fc,0x000200f9,0x000003a6,0x000200f8, - 0x000002fd,0x00050041,0x0000007a,0x000002fe,0x00000077,0x000000e2,0x0004003d,0x00000006, - 0x000002ff,0x000002fe,0x000500b4,0x00000033,0x00000300,0x000002ff,0x00000084,0x000300f7, - 0x000003a5,0x00000000,0x000400fa,0x00000300,0x00000301,0x00000308,0x000200f8,0x00000301, - 0x0004003d,0x000000f0,0x00000302,0x000000f2,0x0004003d,0x000000f4,0x00000303,0x000000f6, - 0x00050056,0x000000f8,0x00000304,0x00000302,0x00000303,0x00050057,0x00000018,0x00000307, - 0x00000304,0x00000276,0x000200f9,0x000003a5,0x000200f8,0x00000308,0x00050041,0x0000007a, - 0x00000309,0x00000077,0x000000e2,0x0004003d,0x00000006,0x0000030a,0x00000309,0x000500b4, - 0x00000033,0x0000030b,0x0000030a,0x00000090,0x000300f7,0x000003a4,0x00000000,0x000400fa, - 0x0000030b,0x0000030c,0x00000338,0x000200f8,0x0000030c,0x0004003d,0x000000f0,0x0000030d, - 0x000000f2,0x0004003d,0x000000f4,0x0000030e,0x000000f6,0x00050056,0x000000f8,0x0000030f, - 0x0000030d,0x0000030e,0x00050057,0x00000018,0x00000312,0x0000030f,0x00000276,0x00050051, - 0x00000006,0x00000313,0x00000312,0x00000000,0x0004003d,0x000000f0,0x00000315,0x0000010d, - 0x0004003d,0x000000f4,0x00000316,0x000000f6,0x00050056,0x000000f8,0x00000317,0x00000315, - 0x00000316,0x00050057,0x00000018,0x0000031a,0x00000317,0x00000276,0x00050051,0x00000006, - 0x0000031d,0x0000031a,0x00000000,0x00050051,0x00000006,0x0000031f,0x0000031a,0x00000001, - 0x00060050,0x0000000f,0x00000598,0x00000313,0x0000031d,0x0000031f,0x00050041,0x0000011a, - 0x00000320,0x00000077,0x00000119,0x0004003d,0x00000018,0x00000321,0x00000320,0x0008004f, - 0x0000000f,0x00000322,0x00000321,0x00000321,0x00000000,0x00000001,0x00000002,0x00050081, - 0x0000000f,0x00000324,0x00000598,0x00000322,0x00050041,0x0000011a,0x00000326,0x00000077, - 0x00000121,0x0004003d,0x00000018,0x00000327,0x00000326,0x0008004f,0x0000000f,0x00000328, - 0x00000327,0x00000327,0x00000000,0x00000001,0x00000002,0x00050094,0x00000006,0x00000329, - 0x00000324,0x00000328,0x00050041,0x0000011a,0x0000032c,0x00000077,0x00000128,0x0004003d, - 0x00000018,0x0000032d,0x0000032c,0x0008004f,0x0000000f,0x0000032e,0x0000032d,0x0000032d, - 0x00000000,0x00000001,0x00000002,0x00050094,0x00000006,0x0000032f,0x00000324,0x0000032e, - 0x00050041,0x0000011a,0x00000332,0x00000077,0x0000012f,0x0004003d,0x00000018,0x00000333, - 0x00000332,0x0008004f,0x0000000f,0x00000334,0x00000333,0x00000333,0x00000000,0x00000001, - 0x00000002,0x00050094,0x00000006,0x00000335,0x00000324,0x00000334,0x00070050,0x00000018, - 0x00000599,0x00000329,0x0000032f,0x00000335,0x00000084,0x000200f9,0x000003a4,0x000200f8, - 0x00000338,0x00050041,0x0000007a,0x00000339,0x00000077,0x000000e2,0x0004003d,0x00000006, - 0x0000033a,0x00000339,0x000500b4,0x00000033,0x0000033b,0x0000033a,0x0000013a,0x000300f7, - 0x000003a3,0x00000000,0x000400fa,0x0000033b,0x0000033c,0x00000368,0x000200f8,0x0000033c, - 0x0004003d,0x000000f0,0x0000033d,0x000000f2,0x0004003d,0x000000f4,0x0000033e,0x000000f6, - 0x00050056,0x000000f8,0x0000033f,0x0000033d,0x0000033e,0x00050057,0x00000018,0x00000342, - 0x0000033f,0x00000276,0x00050051,0x00000006,0x00000343,0x00000342,0x00000000,0x0004003d, - 0x000000f0,0x00000345,0x0000010d,0x0004003d,0x000000f4,0x00000346,0x000000f6,0x00050056, - 0x000000f8,0x00000347,0x00000345,0x00000346,0x00050057,0x00000018,0x0000034a,0x00000347, - 0x00000276,0x00050051,0x00000006,0x0000034d,0x0000034a,0x00000001,0x00050051,0x00000006, - 0x0000034f,0x0000034a,0x00000000,0x00060050,0x0000000f,0x00000596,0x00000343,0x0000034d, - 0x0000034f,0x00050041,0x0000011a,0x00000350,0x00000077,0x00000119,0x0004003d,0x00000018, - 0x00000351,0x00000350,0x0008004f,0x0000000f,0x00000352,0x00000351,0x00000351,0x00000000, - 0x00000001,0x00000002,0x00050081,0x0000000f,0x00000354,0x00000596,0x00000352,0x00050041, - 0x0000011a,0x00000356,0x00000077,0x00000121,0x0004003d,0x00000018,0x00000357,0x00000356, - 0x0008004f,0x0000000f,0x00000358,0x00000357,0x00000357,0x00000000,0x00000001,0x00000002, - 0x00050094,0x00000006,0x00000359,0x00000354,0x00000358,0x00050041,0x0000011a,0x0000035c, - 0x00000077,0x00000128,0x0004003d,0x00000018,0x0000035d,0x0000035c,0x0008004f,0x0000000f, - 0x0000035e,0x0000035d,0x0000035d,0x00000000,0x00000001,0x00000002,0x00050094,0x00000006, - 0x0000035f,0x00000354,0x0000035e,0x00050041,0x0000011a,0x00000362,0x00000077,0x0000012f, - 0x0004003d,0x00000018,0x00000363,0x00000362,0x0008004f,0x0000000f,0x00000364,0x00000363, - 0x00000363,0x00000000,0x00000001,0x00000002,0x00050094,0x00000006,0x00000365,0x00000354, - 0x00000364,0x00070050,0x00000018,0x00000597,0x00000359,0x0000035f,0x00000365,0x00000084, - 0x000200f9,0x000003a3,0x000200f8,0x00000368,0x00050041,0x0000007a,0x00000369,0x00000077, - 0x000000e2,0x0004003d,0x00000006,0x0000036a,0x00000369,0x000500b4,0x00000033,0x0000036b, - 0x0000036a,0x0000016d,0x000300f7,0x000003a2,0x00000000,0x000400fa,0x0000036b,0x0000036c, - 0x0000039d,0x000200f8,0x0000036c,0x0004003d,0x000000f0,0x0000036d,0x000000f2,0x0004003d, - 0x000000f4,0x0000036e,0x000000f6,0x00050056,0x000000f8,0x0000036f,0x0000036d,0x0000036e, - 0x00050057,0x00000018,0x00000372,0x0000036f,0x00000276,0x00050051,0x00000006,0x00000373, - 0x00000372,0x00000000,0x0004003d,0x000000f0,0x00000375,0x0000010d,0x0004003d,0x000000f4, - 0x00000376,0x000000f6,0x00050056,0x000000f8,0x00000377,0x00000375,0x00000376,0x00050057, - 0x00000018,0x0000037a,0x00000377,0x00000276,0x00050051,0x00000006,0x0000037b,0x0000037a, - 0x00000000,0x0004003d,0x000000f0,0x0000037d,0x00000182,0x0004003d,0x000000f4,0x0000037e, - 0x000000f6,0x00050056,0x000000f8,0x0000037f,0x0000037d,0x0000037e,0x00050057,0x00000018, - 0x00000382,0x0000037f,0x00000276,0x00050051,0x00000006,0x00000383,0x00000382,0x00000000, - 0x00060050,0x0000000f,0x00000594,0x00000373,0x0000037b,0x00000383,0x00050041,0x0000011a, - 0x00000385,0x00000077,0x00000119,0x0004003d,0x00000018,0x00000386,0x00000385,0x0008004f, - 0x0000000f,0x00000387,0x00000386,0x00000386,0x00000000,0x00000001,0x00000002,0x00050081, - 0x0000000f,0x00000389,0x00000594,0x00000387,0x00050041,0x0000011a,0x0000038b,0x00000077, - 0x00000121,0x0004003d,0x00000018,0x0000038c,0x0000038b,0x0008004f,0x0000000f,0x0000038d, - 0x0000038c,0x0000038c,0x00000000,0x00000001,0x00000002,0x00050094,0x00000006,0x0000038e, - 0x00000389,0x0000038d,0x00050041,0x0000011a,0x00000391,0x00000077,0x00000128,0x0004003d, - 0x00000018,0x00000392,0x00000391,0x0008004f,0x0000000f,0x00000393,0x00000392,0x00000392, - 0x00000000,0x00000001,0x00000002,0x00050094,0x00000006,0x00000394,0x00000389,0x00000393, - 0x00050041,0x0000011a,0x00000397,0x00000077,0x0000012f,0x0004003d,0x00000018,0x00000398, - 0x00000397,0x0008004f,0x0000000f,0x00000399,0x00000398,0x00000398,0x00000000,0x00000001, - 0x00000002,0x00050094,0x00000006,0x0000039a,0x00000389,0x00000399,0x00070050,0x00000018, - 0x00000595,0x0000038e,0x00000394,0x0000039a,0x00000084,0x000200f9,0x000003a2,0x000200f8, - 0x0000039d,0x000200f9,0x000003a2,0x000200f8,0x000003a2,0x000700f5,0x00000018,0x000005af, - 0x00000595,0x0000036c,0x000005a9,0x0000039d,0x000200f9,0x000003a3,0x000200f8,0x000003a3, - 0x000700f5,0x00000018,0x000005ae,0x00000597,0x0000033c,0x000005af,0x000003a2,0x000200f9, - 0x000003a4,0x000200f8,0x000003a4,0x000700f5,0x00000018,0x000005ad,0x00000599,0x0000030c, - 0x000005ae,0x000003a3,0x000200f9,0x000003a5,0x000200f8,0x000003a5,0x000700f5,0x00000018, - 0x000005ac,0x00000307,0x00000301,0x000005ad,0x000003a4,0x000200f9,0x000003a6,0x000200f8, - 0x000003a6,0x000700f5,0x00000018,0x000005ab,0x000000e9,0x000002fc,0x000005ac,0x000003a5, - 0x00050041,0x0000007a,0x00000292,0x00000077,0x00000094,0x0004003d,0x00000006,0x00000293, - 0x00000292,0x000500b4,0x00000033,0x00000294,0x00000293,0x0000013a,0x000300f7,0x0000029f, - 0x00000000,0x000400fa,0x00000294,0x00000295,0x0000029f,0x000200f8,0x00000295,0x0008004f, - 0x0000000f,0x00000297,0x000005ab,0x000005ab,0x00000000,0x00000001,0x00000002,0x0006000c, - 0x0000000f,0x000003ad,0x00000001,0x00000004,0x00000297,0x0007000c,0x0000000f,0x000003ae, - 0x00000001,0x0000001a,0x000003ad,0x0000005b,0x00050083,0x0000000f,0x000003b0,0x000003ae, - 0x0000059a,0x0007000c,0x0000000f,0x000003b1,0x00000001,0x00000028,0x000003b0,0x00000061, - 0x0006000c,0x0000000f,0x000003b3,0x00000001,0x00000004,0x00000297,0x0007000c,0x0000000f, - 0x000003b4,0x00000001,0x0000001a,0x000003b3,0x0000005b,0x0005008e,0x0000000f,0x000003b5, - 0x000003b4,0x00000065,0x00050083,0x0000000f,0x000003b7,0x0000059b,0x000003b5,0x00050088, - 0x0000000f,0x000003ba,0x000003b1,0x000003b7,0x0006000c,0x0000000f,0x000003bb,0x00000001, - 0x00000004,0x000003ba,0x0007000c,0x0000000f,0x000003bc,0x00000001,0x0000001a,0x000003bb, - 0x00000072,0x0005008e,0x0000000f,0x000003bd,0x000003bc,0x0000006c,0x00050041,0x0000007a, - 0x000003be,0x00000077,0x00000079,0x0004003d,0x00000006,0x000003bf,0x000003be,0x00060050, - 0x0000000f,0x000003c0,0x000003bf,0x000003bf,0x000003bf,0x00050088,0x0000000f,0x000003c1, - 0x000003bd,0x000003c0,0x00050051,0x00000006,0x0000029a,0x000003c1,0x00000000,0x00060052, - 0x00000018,0x00000540,0x0000029a,0x000005ab,0x00000000,0x00050051,0x00000006,0x0000029c, - 0x000003c1,0x00000001,0x00060052,0x00000018,0x00000542,0x0000029c,0x00000540,0x00000001, - 0x00050051,0x00000006,0x0000029e,0x000003c1,0x00000002,0x00060052,0x00000018,0x00000544, - 0x0000029e,0x00000542,0x00000002,0x000200f9,0x0000029f,0x000200f8,0x0000029f,0x000700f5, - 0x00000018,0x000005b0,0x000005ab,0x000003a6,0x00000544,0x00000295,0x00050041,0x0000007a, - 0x000002a0,0x00000077,0x00000081,0x0004003d,0x00000006,0x000002a1,0x000002a0,0x000500b7, - 0x00000033,0x000002a2,0x000002a1,0x00000060,0x000300f7,0x000002ad,0x00000000,0x000400fa, - 0x000002a2,0x000002a3,0x000002ad,0x000200f8,0x000002a3,0x0008004f,0x0000000f,0x000002a5, - 0x000005b0,0x000005b0,0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a,0x000003c6, - 0x00000077,0x00000081,0x0004003d,0x00000006,0x000003c7,0x000003c6,0x000500b4,0x00000033, - 0x000003c8,0x000003c7,0x00000084,0x000300f7,0x000003fc,0x00000000,0x000400fa,0x000003c8, - 0x000003c9,0x000003ce,0x000200f8,0x000003c9,0x00050041,0x0000007a,0x000003ca,0x00000077, - 0x00000088,0x0004003d,0x00000006,0x000003cb,0x000003ca,0x0005008e,0x0000000f,0x000003cd, - 0x000002a5,0x000003cb,0x000200f9,0x000003fc,0x000200f8,0x000003ce,0x00050041,0x0000007a, - 0x000003cf,0x00000077,0x00000081,0x0004003d,0x00000006,0x000003d0,0x000003cf,0x000500b4, - 0x00000033,0x000003d1,0x000003d0,0x00000090,0x000300f7,0x000003fb,0x00000000,0x000400fa, - 0x000003d1,0x000003d2,0x000003fb,0x000200f8,0x000003d2,0x00050041,0x0000007a,0x000003d3, - 0x00000077,0x00000094,0x0004003d,0x00000006,0x000003d4,0x000003d3,0x000500b4,0x00000033, - 0x000003d5,0x000003d4,0x00000090,0x000300f7,0x000003d9,0x00000000,0x000400fa,0x000003d5, - 0x000003d6,0x000003d9,0x000200f8,0x000003d6,0x00050090,0x0000000f,0x000003d8,0x000002a5, - 0x000000a8,0x000200f9,0x000003d9,0x000200f8,0x000003d9,0x000700f5,0x0000000f,0x000005b1, - 0x000002a5,0x000003d2,0x000003d8,0x000003d6,0x00050051,0x00000006,0x000003db,0x000005b1, - 0x00000000,0x00050051,0x00000006,0x000003dd,0x000005b1,0x00000001,0x00050051,0x00000006, - 0x000003df,0x000005b1,0x00000002,0x0007000c,0x00000006,0x000003e0,0x00000001,0x00000028, - 0x000003dd,0x000003df,0x0007000c,0x00000006,0x000003e1,0x00000001,0x00000028,0x000003db, - 0x000003e0,0x000500ba,0x00000033,0x000003e3,0x000003e1,0x00000060,0x000300f7,0x000003f3, - 0x00000000,0x000400fa,0x000003e3,0x000003e4,0x000003f3,0x000200f8,0x000003e4,0x00050041, - 0x0000007a,0x000003e5,0x00000077,0x00000088,0x0004003d,0x00000006,0x000003e6,0x000003e5, - 0x0008000c,0x00000006,0x000003e9,0x00000001,0x00000032,0x000003e6,0x000003e1,0x00000084, - 0x00050041,0x0000007a,0x000003ea,0x00000077,0x000000c1,0x0004003d,0x00000006,0x000003eb, - 0x000003ea,0x0008000c,0x00000006,0x000003ee,0x00000001,0x00000032,0x000003eb,0x000003e1, - 0x00000084,0x00050088,0x00000006,0x000003ef,0x000003e9,0x000003ee,0x0005008e,0x0000000f, - 0x000003f2,0x000005b1,0x000003ef,0x000200f9,0x000003f3,0x000200f8,0x000003f3,0x000700f5, - 0x0000000f,0x000005b2,0x000005b1,0x000003d9,0x000003f2,0x000003e4,0x00050041,0x0000007a, - 0x000003f4,0x00000077,0x00000094,0x0004003d,0x00000006,0x000003f5,0x000003f4,0x000500b4, - 0x00000033,0x000003f6,0x000003f5,0x00000090,0x000300f7,0x000003fa,0x00000000,0x000400fa, - 0x000003f6,0x000003f7,0x000003fa,0x000200f8,0x000003f7,0x00050090,0x0000000f,0x000003f9, - 0x000005b2,0x000000dd,0x000200f9,0x000003fa,0x000200f8,0x000003fa,0x000700f5,0x0000000f, - 0x000005b5,0x000005b2,0x000003f3,0x000003f9,0x000003f7,0x000200f9,0x000003fb,0x000200f8, - 0x000003fb,0x000700f5,0x0000000f,0x000005b4,0x000002a5,0x000003ce,0x000005b5,0x000003fa, - 0x000200f9,0x000003fc,0x000200f8,0x000003fc,0x000700f5,0x0000000f,0x000005b3,0x000003cd, - 0x000003c9,0x000005b4,0x000003fb,0x00050051,0x00000006,0x000002a8,0x000005b3,0x00000000, - 0x00060052,0x00000018,0x00000549,0x000002a8,0x000005b0,0x00000000,0x00050051,0x00000006, - 0x000002aa,0x000005b3,0x00000001,0x00060052,0x00000018,0x0000054b,0x000002aa,0x00000549, - 0x00000001,0x00050051,0x00000006,0x000002ac,0x000005b3,0x00000002,0x00060052,0x00000018, - 0x0000054d,0x000002ac,0x0000054b,0x00000002,0x000200f9,0x000002ad,0x000200f8,0x000002ad, - 0x000700f5,0x00000018,0x000005bb,0x000005b0,0x0000029f,0x0000054d,0x000003fc,0x00050041, - 0x0000007a,0x000002ae,0x00000077,0x00000094,0x0004003d,0x00000006,0x000002af,0x000002ae, - 0x000500b4,0x00000033,0x000002b0,0x000002af,0x00000084,0x000300f7,0x000002ee,0x00000000, - 0x000400fa,0x000002b0,0x000002b1,0x000002be,0x000200f8,0x000002b1,0x0008004f,0x0000000f, - 0x000002b3,0x000005bb,0x000005bb,0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a, - 0x00000404,0x00000077,0x000001be,0x0004003d,0x00000006,0x00000405,0x00000404,0x000500b7, - 0x00000033,0x00000406,0x00000405,0x00000060,0x000300f7,0x00000414,0x00000000,0x000400fa, - 0x00000406,0x00000407,0x00000414,0x000200f8,0x00000407,0x00050051,0x00000006,0x00000409, - 0x000005bb,0x00000000,0x000500bc,0x00000033,0x0000041d,0x00000409,0x00000032,0x000300f7, - 0x00000427,0x00000000,0x000400fa,0x0000041d,0x0000041e,0x00000421,0x000200f8,0x0000041e, - 0x00050085,0x00000006,0x00000420,0x00000409,0x000005a7,0x000200f9,0x00000427,0x000200f8, - 0x00000421,0x00050081,0x00000006,0x00000423,0x00000409,0x0000003c,0x0006000c,0x00000006, - 0x00000424,0x00000001,0x00000004,0x00000423,0x00050085,0x00000006,0x00000425,0x00000424, - 0x000005a6,0x0007000c,0x00000006,0x00000426,0x00000001,0x0000001a,0x00000425,0x00000041, - 0x000200f9,0x00000427,0x000200f8,0x00000427,0x000700f5,0x00000006,0x000005d2,0x00000420, - 0x0000041e,0x00000426,0x00000421,0x00050051,0x00000006,0x0000040d,0x000005bb,0x00000001, - 0x000500bc,0x00000033,0x0000042c,0x0000040d,0x00000032,0x000300f7,0x00000436,0x00000000, - 0x000400fa,0x0000042c,0x0000042d,0x00000430,0x000200f8,0x0000042d,0x00050085,0x00000006, - 0x0000042f,0x0000040d,0x000005a7,0x000200f9,0x00000436,0x000200f8,0x00000430,0x00050081, - 0x00000006,0x00000432,0x0000040d,0x0000003c,0x0006000c,0x00000006,0x00000433,0x00000001, - 0x00000004,0x00000432,0x00050085,0x00000006,0x00000434,0x00000433,0x000005a6,0x0007000c, - 0x00000006,0x00000435,0x00000001,0x0000001a,0x00000434,0x00000041,0x000200f9,0x00000436, - 0x000200f8,0x00000436,0x000700f5,0x00000006,0x000005d4,0x0000042f,0x0000042d,0x00000435, - 0x00000430,0x00050051,0x00000006,0x00000411,0x000005bb,0x00000002,0x000500bc,0x00000033, - 0x0000043b,0x00000411,0x00000032,0x000300f7,0x00000445,0x00000000,0x000400fa,0x0000043b, - 0x0000043c,0x0000043f,0x000200f8,0x0000043c,0x00050085,0x00000006,0x0000043e,0x00000411, - 0x000005a7,0x000200f9,0x00000445,0x000200f8,0x0000043f,0x00050081,0x00000006,0x00000441, - 0x00000411,0x0000003c,0x0006000c,0x00000006,0x00000442,0x00000001,0x00000004,0x00000441, - 0x00050085,0x00000006,0x00000443,0x00000442,0x000005a6,0x0007000c,0x00000006,0x00000444, - 0x00000001,0x0000001a,0x00000443,0x00000041,0x000200f9,0x00000445,0x000200f8,0x00000445, - 0x000700f5,0x00000006,0x000005d6,0x0000043e,0x0000043c,0x00000444,0x0000043f,0x00060050, - 0x0000000f,0x000005e2,0x000005d2,0x000005d4,0x000005d6,0x000200f9,0x00000414,0x000200f8, - 0x00000414,0x000700f5,0x0000000f,0x000005d8,0x000002b3,0x000002b1,0x000005e2,0x00000445, - 0x00050041,0x0000007a,0x00000416,0x00000077,0x000001ae,0x0004003d,0x00000006,0x00000417, - 0x00000416,0x0005008e,0x0000000f,0x00000418,0x000005d8,0x00000417,0x00050051,0x00000006, - 0x000002b6,0x00000418,0x00000000,0x00050051,0x00000006,0x000002b8,0x00000418,0x00000001, - 0x00050051,0x00000006,0x000002ba,0x00000418,0x00000002,0x00050051,0x00000006,0x000002bc, - 0x000005bb,0x00000003,0x00070050,0x00000018,0x000005a8,0x000002b6,0x000002b8,0x000002ba, - 0x000002bc,0x000200f9,0x000002ee,0x000200f8,0x000002be,0x00050041,0x0000007a,0x000002bf, - 0x00000077,0x00000094,0x0004003d,0x00000006,0x000002c0,0x000002bf,0x000500b4,0x00000033, - 0x000002c1,0x000002c0,0x00000090,0x000300f7,0x000002ed,0x00000000,0x000400fa,0x000002c1, - 0x000002c2,0x000002cf,0x000200f8,0x000002c2,0x0008004f,0x0000000f,0x000002c4,0x000005bb, - 0x000005bb,0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a,0x0000044e,0x00000077, - 0x000001ae,0x0004003d,0x00000006,0x0000044f,0x0000044e,0x0005008e,0x0000000f,0x00000450, - 0x000002c4,0x0000044f,0x00050041,0x0000007a,0x00000451,0x00000077,0x000001be,0x0004003d, - 0x00000006,0x00000452,0x00000451,0x000500b7,0x00000033,0x00000453,0x00000452,0x00000060, - 0x000400a8,0x00000033,0x00000454,0x00000453,0x000300f7,0x00000466,0x00000000,0x000400fa, - 0x00000454,0x00000455,0x00000466,0x000200f8,0x00000455,0x00050051,0x00000006,0x00000457, - 0x00000450,0x00000000,0x000500bc,0x00000033,0x0000046b,0x00000457,0x00000047,0x000300f7, - 0x00000475,0x00000000,0x000400fa,0x0000046b,0x0000046c,0x0000046f,0x000200f8,0x0000046c, - 0x00050085,0x00000006,0x0000046e,0x00000457,0x00000038,0x000200f9,0x00000475,0x000200f8, - 0x0000046f,0x0006000c,0x00000006,0x00000471,0x00000001,0x00000004,0x00000457,0x0007000c, - 0x00000006,0x00000472,0x00000001,0x0000001a,0x00000471,0x00000050,0x0008000c,0x00000006, - 0x00000474,0x00000001,0x00000032,0x00000472,0x0000003f,0x000005aa,0x000200f9,0x00000475, - 0x000200f8,0x00000475,0x000700f5,0x00000006,0x000005c7,0x0000046e,0x0000046c,0x00000474, - 0x0000046f,0x00050051,0x00000006,0x0000045b,0x00000450,0x00000001,0x000500bc,0x00000033, - 0x0000047a,0x0000045b,0x00000047,0x000300f7,0x00000484,0x00000000,0x000400fa,0x0000047a, - 0x0000047b,0x0000047e,0x000200f8,0x0000047b,0x00050085,0x00000006,0x0000047d,0x0000045b, - 0x00000038,0x000200f9,0x00000484,0x000200f8,0x0000047e,0x0006000c,0x00000006,0x00000480, - 0x00000001,0x00000004,0x0000045b,0x0007000c,0x00000006,0x00000481,0x00000001,0x0000001a, - 0x00000480,0x00000050,0x0008000c,0x00000006,0x00000483,0x00000001,0x00000032,0x00000481, - 0x0000003f,0x000005aa,0x000200f9,0x00000484,0x000200f8,0x00000484,0x000700f5,0x00000006, - 0x000005c9,0x0000047d,0x0000047b,0x00000483,0x0000047e,0x00050051,0x00000006,0x0000045f, - 0x00000450,0x00000002,0x000500bc,0x00000033,0x00000489,0x0000045f,0x00000047,0x000300f7, - 0x00000493,0x00000000,0x000400fa,0x00000489,0x0000048a,0x0000048d,0x000200f8,0x0000048a, - 0x00050085,0x00000006,0x0000048c,0x0000045f,0x00000038,0x000200f9,0x00000493,0x000200f8, - 0x0000048d,0x0006000c,0x00000006,0x0000048f,0x00000001,0x00000004,0x0000045f,0x0007000c, - 0x00000006,0x00000490,0x00000001,0x0000001a,0x0000048f,0x00000050,0x0008000c,0x00000006, - 0x00000492,0x00000001,0x00000032,0x00000490,0x0000003f,0x000005aa,0x000200f9,0x00000493, - 0x000200f8,0x00000493,0x000700f5,0x00000006,0x000005cb,0x0000048c,0x0000048a,0x00000492, - 0x0000048d,0x00060050,0x0000000f,0x000005e1,0x000005c7,0x000005c9,0x000005cb,0x0008000c, - 0x0000000f,0x00000465,0x00000001,0x0000002b,0x000005e1,0x00000061,0x000005a0,0x000200f9, - 0x00000466,0x000200f8,0x00000466,0x000700f5,0x0000000f,0x000005cd,0x00000450,0x000002c2, - 0x00000465,0x00000493,0x00050051,0x00000006,0x000002c7,0x000005cd,0x00000000,0x00050051, - 0x00000006,0x000002c9,0x000005cd,0x00000001,0x00050051,0x00000006,0x000002cb,0x000005cd, - 0x00000002,0x00050051,0x00000006,0x000002cd,0x000005bb,0x00000003,0x00070050,0x00000018, - 0x000005a5,0x000002c7,0x000002c9,0x000002cb,0x000002cd,0x000200f9,0x000002ed,0x000200f8, - 0x000002cf,0x00050041,0x0000007a,0x000002d0,0x00000077,0x00000094,0x0004003d,0x00000006, - 0x000002d1,0x000002d0,0x000500b4,0x00000033,0x000002d2,0x000002d1,0x0000013a,0x000300f7, - 0x000002ec,0x00000000,0x000400fa,0x000002d2,0x000002d3,0x000002e9,0x000200f8,0x000002d3, - 0x0008004f,0x0000000f,0x000002d5,0x000005bb,0x000005bb,0x00000000,0x00000001,0x00000002, - 0x00050090,0x0000000f,0x000002d6,0x000002d5,0x000000dd,0x00050051,0x00000006,0x000002d8, - 0x000002d6,0x00000000,0x00060052,0x00000018,0x00000573,0x000002d8,0x000005e3,0x00000000, - 0x00050051,0x00000006,0x000002da,0x000002d6,0x00000001,0x00060052,0x00000018,0x00000575, - 0x000002da,0x00000573,0x00000001,0x00050051,0x00000006,0x000002dc,0x000002d6,0x00000002, - 0x00060052,0x00000018,0x00000577,0x000002dc,0x00000575,0x00000002,0x0008004f,0x0000000f, - 0x000002de,0x00000577,0x00000577,0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a, - 0x0000049c,0x00000077,0x000001ae,0x0004003d,0x00000006,0x0000049d,0x0000049c,0x0005008e, - 0x0000000f,0x0000049e,0x000002de,0x0000049d,0x00050041,0x0000007a,0x0000049f,0x00000077, - 0x000001be,0x0004003d,0x00000006,0x000004a0,0x0000049f,0x000500b7,0x00000033,0x000004a1, - 0x000004a0,0x00000060,0x000400a8,0x00000033,0x000004a2,0x000004a1,0x000300f7,0x000004b4, - 0x00000000,0x000400fa,0x000004a2,0x000004a3,0x000004b4,0x000200f8,0x000004a3,0x00050051, - 0x00000006,0x000004a5,0x0000049e,0x00000000,0x000500bc,0x00000033,0x000004b9,0x000004a5, - 0x00000047,0x000300f7,0x000004c3,0x00000000,0x000400fa,0x000004b9,0x000004ba,0x000004bd, - 0x000200f8,0x000004ba,0x00050085,0x00000006,0x000004bc,0x000004a5,0x00000038,0x000200f9, - 0x000004c3,0x000200f8,0x000004bd,0x0006000c,0x00000006,0x000004bf,0x00000001,0x00000004, - 0x000004a5,0x0007000c,0x00000006,0x000004c0,0x00000001,0x0000001a,0x000004bf,0x00000050, - 0x0008000c,0x00000006,0x000004c2,0x00000001,0x00000032,0x000004c0,0x0000003f,0x000005aa, - 0x000200f9,0x000004c3,0x000200f8,0x000004c3,0x000700f5,0x00000006,0x000005bc,0x000004bc, - 0x000004ba,0x000004c2,0x000004bd,0x00050051,0x00000006,0x000004a9,0x0000049e,0x00000001, - 0x000500bc,0x00000033,0x000004c8,0x000004a9,0x00000047,0x000300f7,0x000004d2,0x00000000, - 0x000400fa,0x000004c8,0x000004c9,0x000004cc,0x000200f8,0x000004c9,0x00050085,0x00000006, - 0x000004cb,0x000004a9,0x00000038,0x000200f9,0x000004d2,0x000200f8,0x000004cc,0x0006000c, - 0x00000006,0x000004ce,0x00000001,0x00000004,0x000004a9,0x0007000c,0x00000006,0x000004cf, - 0x00000001,0x0000001a,0x000004ce,0x00000050,0x0008000c,0x00000006,0x000004d1,0x00000001, - 0x00000032,0x000004cf,0x0000003f,0x000005aa,0x000200f9,0x000004d2,0x000200f8,0x000004d2, - 0x000700f5,0x00000006,0x000005be,0x000004cb,0x000004c9,0x000004d1,0x000004cc,0x00050051, - 0x00000006,0x000004ad,0x0000049e,0x00000002,0x000500bc,0x00000033,0x000004d7,0x000004ad, - 0x00000047,0x000300f7,0x000004e1,0x00000000,0x000400fa,0x000004d7,0x000004d8,0x000004db, - 0x000200f8,0x000004d8,0x00050085,0x00000006,0x000004da,0x000004ad,0x00000038,0x000200f9, - 0x000004e1,0x000200f8,0x000004db,0x0006000c,0x00000006,0x000004dd,0x00000001,0x00000004, - 0x000004ad,0x0007000c,0x00000006,0x000004de,0x00000001,0x0000001a,0x000004dd,0x00000050, - 0x0008000c,0x00000006,0x000004e0,0x00000001,0x00000032,0x000004de,0x0000003f,0x000005aa, - 0x000200f9,0x000004e1,0x000200f8,0x000004e1,0x000700f5,0x00000006,0x000005c0,0x000004da, - 0x000004d8,0x000004e0,0x000004db,0x00060050,0x0000000f,0x000005e0,0x000005bc,0x000005be, - 0x000005c0,0x0008000c,0x0000000f,0x000004b3,0x00000001,0x0000002b,0x000005e0,0x00000061, - 0x000005a0,0x000200f9,0x000004b4,0x000200f8,0x000004b4,0x000700f5,0x0000000f,0x000005c2, - 0x0000049e,0x000002d3,0x000004b3,0x000004e1,0x00050051,0x00000006,0x000002e1,0x000005c2, - 0x00000000,0x00050051,0x00000006,0x000002e3,0x000005c2,0x00000001,0x00050051,0x00000006, - 0x000002e5,0x000005c2,0x00000002,0x00050051,0x00000006,0x000002e7,0x000005bb,0x00000003, - 0x00070050,0x00000018,0x000005a1,0x000002e1,0x000002e3,0x000002e5,0x000002e7,0x000200f9, - 0x000002ec,0x000200f8,0x000002e9,0x0008004f,0x0000000f,0x000004e7,0x000005bb,0x000005bb, - 0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a,0x000004e8,0x00000077,0x000001ae, - 0x0004003d,0x00000006,0x000004e9,0x000004e8,0x0005008e,0x0000000f,0x000004ea,0x000004e7, - 0x000004e9,0x00050051,0x00000006,0x000004ec,0x000004ea,0x00000000,0x00050051,0x00000006, - 0x000004ee,0x000004ea,0x00000001,0x00050051,0x00000006,0x000004f0,0x000004ea,0x00000002, - 0x00050051,0x00000006,0x000004f2,0x000005bb,0x00000003,0x00070050,0x00000018,0x0000059c, - 0x000004ec,0x000004ee,0x000004f0,0x000004f2,0x000200f9,0x000002ec,0x000200f8,0x000002ec, - 0x000700f5,0x00000018,0x000005df,0x000005a1,0x000004b4,0x0000059c,0x000002e9,0x000200f9, - 0x000002ed,0x000200f8,0x000002ed,0x000700f5,0x00000018,0x000005de,0x000005a5,0x00000466, - 0x000005df,0x000002ec,0x000200f9,0x000002ee,0x000200f8,0x000002ee,0x000700f5,0x00000018, - 0x000005dd,0x000005a8,0x00000414,0x000005de,0x000002ed,0x00050085,0x00000018,0x000002f2, - 0x000005dd,0x00000279,0x0003003e,0x0000027c,0x000002f2,0x000100fd,0x00010038 + 0x000000d8,0x000000dc,0x00090019,0x000000e3,0x00000006,0x00000001,0x00000000,0x00000000, + 0x00000000,0x00000001,0x00000000,0x0003001b,0x000000e4,0x000000e3,0x00040020,0x000000e5, + 0x00000000,0x000000e4,0x0004003b,0x000000e5,0x000000e6,0x00000000,0x0004002b,0x00000078, + 0x000000f2,0x00000002,0x0004002b,0x00000078,0x00000103,0x00000000,0x0004002b,0x00000006, + 0x00000147,0x40400000,0x00040020,0x000001b6,0x00000001,0x00000018,0x00040020,0x000001ba, + 0x00000001,0x00000019,0x0004003b,0x000001ba,0x000001bb,0x00000001,0x0004003b,0x000001b6, + 0x000001be,0x00000001,0x00040020,0x000001c1,0x00000003,0x00000018,0x0004003b,0x000001c1, + 0x000001c2,0x00000003,0x0006002c,0x0000000f,0x000003fa,0x0000005d,0x0000005d,0x0000005d, + 0x0006002c,0x0000000f,0x000003fb,0x00000064,0x00000064,0x00000064,0x0006002c,0x0000000f, + 0x00000400,0x00000084,0x00000084,0x00000084,0x0004002b,0x00000006,0x00000406,0x3f72a76f, + 0x0004002b,0x00000006,0x00000407,0x3d9e8391,0x0004002b,0x00000006,0x00000409,0xbd6147ae, + 0x00030001,0x00000018,0x0000043d,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003, + 0x000200f8,0x00000005,0x0004003d,0x00000019,0x000001bc,0x000001bb,0x0004003d,0x00000018, + 0x000001bf,0x000001be,0x0004003d,0x000000e4,0x0000023c,0x000000e6,0x00050057,0x00000018, + 0x0000023f,0x0000023c,0x000001bc,0x00050041,0x0000007a,0x000001d8,0x00000077,0x00000094, + 0x0004003d,0x00000006,0x000001d9,0x000001d8,0x000500b4,0x00000033,0x000001da,0x000001d9, + 0x00000147,0x000300f7,0x000001e5,0x00000000,0x000400fa,0x000001da,0x000001db,0x000001e5, + 0x000200f8,0x000001db,0x0008004f,0x0000000f,0x000001dd,0x0000023f,0x0000023f,0x00000000, + 0x00000001,0x00000002,0x0006000c,0x0000000f,0x00000246,0x00000001,0x00000004,0x000001dd, + 0x0007000c,0x0000000f,0x00000247,0x00000001,0x0000001a,0x00000246,0x0000005b,0x00050083, + 0x0000000f,0x00000249,0x00000247,0x000003fa,0x0007000c,0x0000000f,0x0000024a,0x00000001, + 0x00000028,0x00000249,0x00000061,0x0006000c,0x0000000f,0x0000024c,0x00000001,0x00000004, + 0x000001dd,0x0007000c,0x0000000f,0x0000024d,0x00000001,0x0000001a,0x0000024c,0x0000005b, + 0x0005008e,0x0000000f,0x0000024e,0x0000024d,0x00000065,0x00050083,0x0000000f,0x00000250, + 0x000003fb,0x0000024e,0x00050088,0x0000000f,0x00000253,0x0000024a,0x00000250,0x0006000c, + 0x0000000f,0x00000254,0x00000001,0x00000004,0x00000253,0x0007000c,0x0000000f,0x00000255, + 0x00000001,0x0000001a,0x00000254,0x00000072,0x0005008e,0x0000000f,0x00000256,0x00000255, + 0x0000006c,0x00050041,0x0000007a,0x00000257,0x00000077,0x00000079,0x0004003d,0x00000006, + 0x00000258,0x00000257,0x00060050,0x0000000f,0x00000259,0x00000258,0x00000258,0x00000258, + 0x00050088,0x0000000f,0x0000025a,0x00000256,0x00000259,0x00050051,0x00000006,0x000001e0, + 0x0000025a,0x00000000,0x00060052,0x00000018,0x000003a7,0x000001e0,0x0000023f,0x00000000, + 0x00050051,0x00000006,0x000001e2,0x0000025a,0x00000001,0x00060052,0x00000018,0x000003a9, + 0x000001e2,0x000003a7,0x00000001,0x00050051,0x00000006,0x000001e4,0x0000025a,0x00000002, + 0x00060052,0x00000018,0x000003ab,0x000001e4,0x000003a9,0x00000002,0x000200f9,0x000001e5, + 0x000200f8,0x000001e5,0x000700f5,0x00000018,0x0000040a,0x0000023f,0x00000005,0x000003ab, + 0x000001db,0x00050041,0x0000007a,0x000001e6,0x00000077,0x00000081,0x0004003d,0x00000006, + 0x000001e7,0x000001e6,0x000500b7,0x00000033,0x000001e8,0x000001e7,0x00000060,0x000300f7, + 0x000001f3,0x00000000,0x000400fa,0x000001e8,0x000001e9,0x000001f3,0x000200f8,0x000001e9, + 0x0008004f,0x0000000f,0x000001eb,0x0000040a,0x0000040a,0x00000000,0x00000001,0x00000002, + 0x00050041,0x0000007a,0x0000025f,0x00000077,0x00000081,0x0004003d,0x00000006,0x00000260, + 0x0000025f,0x000500b4,0x00000033,0x00000261,0x00000260,0x00000084,0x000300f7,0x00000295, + 0x00000000,0x000400fa,0x00000261,0x00000262,0x00000267,0x000200f8,0x00000262,0x00050041, + 0x0000007a,0x00000263,0x00000077,0x00000088,0x0004003d,0x00000006,0x00000264,0x00000263, + 0x0005008e,0x0000000f,0x00000266,0x000001eb,0x00000264,0x000200f9,0x00000295,0x000200f8, + 0x00000267,0x00050041,0x0000007a,0x00000268,0x00000077,0x00000081,0x0004003d,0x00000006, + 0x00000269,0x00000268,0x000500b4,0x00000033,0x0000026a,0x00000269,0x00000090,0x000300f7, + 0x00000294,0x00000000,0x000400fa,0x0000026a,0x0000026b,0x00000294,0x000200f8,0x0000026b, + 0x00050041,0x0000007a,0x0000026c,0x00000077,0x00000094,0x0004003d,0x00000006,0x0000026d, + 0x0000026c,0x000500b4,0x00000033,0x0000026e,0x0000026d,0x00000090,0x000300f7,0x00000272, + 0x00000000,0x000400fa,0x0000026e,0x0000026f,0x00000272,0x000200f8,0x0000026f,0x00050090, + 0x0000000f,0x00000271,0x000001eb,0x000000a8,0x000200f9,0x00000272,0x000200f8,0x00000272, + 0x000700f5,0x0000000f,0x0000040b,0x000001eb,0x0000026b,0x00000271,0x0000026f,0x00050051, + 0x00000006,0x00000274,0x0000040b,0x00000000,0x00050051,0x00000006,0x00000276,0x0000040b, + 0x00000001,0x00050051,0x00000006,0x00000278,0x0000040b,0x00000002,0x0007000c,0x00000006, + 0x00000279,0x00000001,0x00000028,0x00000276,0x00000278,0x0007000c,0x00000006,0x0000027a, + 0x00000001,0x00000028,0x00000274,0x00000279,0x000500ba,0x00000033,0x0000027c,0x0000027a, + 0x00000060,0x000300f7,0x0000028c,0x00000000,0x000400fa,0x0000027c,0x0000027d,0x0000028c, + 0x000200f8,0x0000027d,0x00050041,0x0000007a,0x0000027e,0x00000077,0x00000088,0x0004003d, + 0x00000006,0x0000027f,0x0000027e,0x0008000c,0x00000006,0x00000282,0x00000001,0x00000032, + 0x0000027f,0x0000027a,0x00000084,0x00050041,0x0000007a,0x00000283,0x00000077,0x000000c1, + 0x0004003d,0x00000006,0x00000284,0x00000283,0x0008000c,0x00000006,0x00000287,0x00000001, + 0x00000032,0x00000284,0x0000027a,0x00000084,0x00050088,0x00000006,0x00000288,0x00000282, + 0x00000287,0x0005008e,0x0000000f,0x0000028b,0x0000040b,0x00000288,0x000200f9,0x0000028c, + 0x000200f8,0x0000028c,0x000700f5,0x0000000f,0x0000040c,0x0000040b,0x00000272,0x0000028b, + 0x0000027d,0x00050041,0x0000007a,0x0000028d,0x00000077,0x00000094,0x0004003d,0x00000006, + 0x0000028e,0x0000028d,0x000500b4,0x00000033,0x0000028f,0x0000028e,0x00000090,0x000300f7, + 0x00000293,0x00000000,0x000400fa,0x0000028f,0x00000290,0x00000293,0x000200f8,0x00000290, + 0x00050090,0x0000000f,0x00000292,0x0000040c,0x000000dd,0x000200f9,0x00000293,0x000200f8, + 0x00000293,0x000700f5,0x0000000f,0x0000040f,0x0000040c,0x0000028c,0x00000292,0x00000290, + 0x000200f9,0x00000294,0x000200f8,0x00000294,0x000700f5,0x0000000f,0x0000040e,0x000001eb, + 0x00000267,0x0000040f,0x00000293,0x000200f9,0x00000295,0x000200f8,0x00000295,0x000700f5, + 0x0000000f,0x0000040d,0x00000266,0x00000262,0x0000040e,0x00000294,0x00050051,0x00000006, + 0x000001ee,0x0000040d,0x00000000,0x00060052,0x00000018,0x000003b0,0x000001ee,0x0000040a, + 0x00000000,0x00050051,0x00000006,0x000001f0,0x0000040d,0x00000001,0x00060052,0x00000018, + 0x000003b2,0x000001f0,0x000003b0,0x00000001,0x00050051,0x00000006,0x000001f2,0x0000040d, + 0x00000002,0x00060052,0x00000018,0x000003b4,0x000001f2,0x000003b2,0x00000002,0x000200f9, + 0x000001f3,0x000200f8,0x000001f3,0x000700f5,0x00000018,0x00000415,0x0000040a,0x000001e5, + 0x000003b4,0x00000295,0x00050041,0x0000007a,0x000001f4,0x00000077,0x00000094,0x0004003d, + 0x00000006,0x000001f5,0x000001f4,0x000500b4,0x00000033,0x000001f6,0x000001f5,0x00000084, + 0x000300f7,0x00000234,0x00000000,0x000400fa,0x000001f6,0x000001f7,0x00000204,0x000200f8, + 0x000001f7,0x0008004f,0x0000000f,0x000001f9,0x00000415,0x00000415,0x00000000,0x00000001, + 0x00000002,0x00050041,0x0000007a,0x0000029d,0x00000077,0x00000103,0x0004003d,0x00000006, + 0x0000029e,0x0000029d,0x000500b7,0x00000033,0x0000029f,0x0000029e,0x00000060,0x000300f7, + 0x000002ad,0x00000000,0x000400fa,0x0000029f,0x000002a0,0x000002ad,0x000200f8,0x000002a0, + 0x00050051,0x00000006,0x000002a2,0x00000415,0x00000000,0x000500bc,0x00000033,0x000002b6, + 0x000002a2,0x00000032,0x000300f7,0x000002c0,0x00000000,0x000400fa,0x000002b6,0x000002b7, + 0x000002ba,0x000200f8,0x000002b7,0x00050085,0x00000006,0x000002b9,0x000002a2,0x00000407, + 0x000200f9,0x000002c0,0x000200f8,0x000002ba,0x00050081,0x00000006,0x000002bc,0x000002a2, + 0x0000003c,0x0006000c,0x00000006,0x000002bd,0x00000001,0x00000004,0x000002bc,0x00050085, + 0x00000006,0x000002be,0x000002bd,0x00000406,0x0007000c,0x00000006,0x000002bf,0x00000001, + 0x0000001a,0x000002be,0x00000041,0x000200f9,0x000002c0,0x000200f8,0x000002c0,0x000700f5, + 0x00000006,0x0000042c,0x000002b9,0x000002b7,0x000002bf,0x000002ba,0x00050051,0x00000006, + 0x000002a6,0x00000415,0x00000001,0x000500bc,0x00000033,0x000002c5,0x000002a6,0x00000032, + 0x000300f7,0x000002cf,0x00000000,0x000400fa,0x000002c5,0x000002c6,0x000002c9,0x000200f8, + 0x000002c6,0x00050085,0x00000006,0x000002c8,0x000002a6,0x00000407,0x000200f9,0x000002cf, + 0x000200f8,0x000002c9,0x00050081,0x00000006,0x000002cb,0x000002a6,0x0000003c,0x0006000c, + 0x00000006,0x000002cc,0x00000001,0x00000004,0x000002cb,0x00050085,0x00000006,0x000002cd, + 0x000002cc,0x00000406,0x0007000c,0x00000006,0x000002ce,0x00000001,0x0000001a,0x000002cd, + 0x00000041,0x000200f9,0x000002cf,0x000200f8,0x000002cf,0x000700f5,0x00000006,0x0000042e, + 0x000002c8,0x000002c6,0x000002ce,0x000002c9,0x00050051,0x00000006,0x000002aa,0x00000415, + 0x00000002,0x000500bc,0x00000033,0x000002d4,0x000002aa,0x00000032,0x000300f7,0x000002de, + 0x00000000,0x000400fa,0x000002d4,0x000002d5,0x000002d8,0x000200f8,0x000002d5,0x00050085, + 0x00000006,0x000002d7,0x000002aa,0x00000407,0x000200f9,0x000002de,0x000200f8,0x000002d8, + 0x00050081,0x00000006,0x000002da,0x000002aa,0x0000003c,0x0006000c,0x00000006,0x000002db, + 0x00000001,0x00000004,0x000002da,0x00050085,0x00000006,0x000002dc,0x000002db,0x00000406, + 0x0007000c,0x00000006,0x000002dd,0x00000001,0x0000001a,0x000002dc,0x00000041,0x000200f9, + 0x000002de,0x000200f8,0x000002de,0x000700f5,0x00000006,0x00000430,0x000002d7,0x000002d5, + 0x000002dd,0x000002d8,0x00060050,0x0000000f,0x0000043c,0x0000042c,0x0000042e,0x00000430, + 0x000200f9,0x000002ad,0x000200f8,0x000002ad,0x000700f5,0x0000000f,0x00000432,0x000001f9, + 0x000001f7,0x0000043c,0x000002de,0x00050041,0x0000007a,0x000002af,0x00000077,0x000000f2, + 0x0004003d,0x00000006,0x000002b0,0x000002af,0x0005008e,0x0000000f,0x000002b1,0x00000432, + 0x000002b0,0x00050051,0x00000006,0x000001fc,0x000002b1,0x00000000,0x00050051,0x00000006, + 0x000001fe,0x000002b1,0x00000001,0x00050051,0x00000006,0x00000200,0x000002b1,0x00000002, + 0x00050051,0x00000006,0x00000202,0x00000415,0x00000003,0x00070050,0x00000018,0x00000408, + 0x000001fc,0x000001fe,0x00000200,0x00000202,0x000200f9,0x00000234,0x000200f8,0x00000204, + 0x00050041,0x0000007a,0x00000205,0x00000077,0x00000094,0x0004003d,0x00000006,0x00000206, + 0x00000205,0x000500b4,0x00000033,0x00000207,0x00000206,0x00000090,0x000300f7,0x00000233, + 0x00000000,0x000400fa,0x00000207,0x00000208,0x00000215,0x000200f8,0x00000208,0x0008004f, + 0x0000000f,0x0000020a,0x00000415,0x00000415,0x00000000,0x00000001,0x00000002,0x00050041, + 0x0000007a,0x000002e7,0x00000077,0x000000f2,0x0004003d,0x00000006,0x000002e8,0x000002e7, + 0x0005008e,0x0000000f,0x000002e9,0x0000020a,0x000002e8,0x00050041,0x0000007a,0x000002ea, + 0x00000077,0x00000103,0x0004003d,0x00000006,0x000002eb,0x000002ea,0x000500b7,0x00000033, + 0x000002ec,0x000002eb,0x00000060,0x000400a8,0x00000033,0x000002ed,0x000002ec,0x000300f7, + 0x000002ff,0x00000000,0x000400fa,0x000002ed,0x000002ee,0x000002ff,0x000200f8,0x000002ee, + 0x00050051,0x00000006,0x000002f0,0x000002e9,0x00000000,0x000500bc,0x00000033,0x00000304, + 0x000002f0,0x00000047,0x000300f7,0x0000030e,0x00000000,0x000400fa,0x00000304,0x00000305, + 0x00000308,0x000200f8,0x00000305,0x00050085,0x00000006,0x00000307,0x000002f0,0x00000038, + 0x000200f9,0x0000030e,0x000200f8,0x00000308,0x0006000c,0x00000006,0x0000030a,0x00000001, + 0x00000004,0x000002f0,0x0007000c,0x00000006,0x0000030b,0x00000001,0x0000001a,0x0000030a, + 0x00000050,0x0008000c,0x00000006,0x0000030d,0x00000001,0x00000032,0x0000030b,0x0000003f, + 0x00000409,0x000200f9,0x0000030e,0x000200f8,0x0000030e,0x000700f5,0x00000006,0x00000421, + 0x00000307,0x00000305,0x0000030d,0x00000308,0x00050051,0x00000006,0x000002f4,0x000002e9, + 0x00000001,0x000500bc,0x00000033,0x00000313,0x000002f4,0x00000047,0x000300f7,0x0000031d, + 0x00000000,0x000400fa,0x00000313,0x00000314,0x00000317,0x000200f8,0x00000314,0x00050085, + 0x00000006,0x00000316,0x000002f4,0x00000038,0x000200f9,0x0000031d,0x000200f8,0x00000317, + 0x0006000c,0x00000006,0x00000319,0x00000001,0x00000004,0x000002f4,0x0007000c,0x00000006, + 0x0000031a,0x00000001,0x0000001a,0x00000319,0x00000050,0x0008000c,0x00000006,0x0000031c, + 0x00000001,0x00000032,0x0000031a,0x0000003f,0x00000409,0x000200f9,0x0000031d,0x000200f8, + 0x0000031d,0x000700f5,0x00000006,0x00000423,0x00000316,0x00000314,0x0000031c,0x00000317, + 0x00050051,0x00000006,0x000002f8,0x000002e9,0x00000002,0x000500bc,0x00000033,0x00000322, + 0x000002f8,0x00000047,0x000300f7,0x0000032c,0x00000000,0x000400fa,0x00000322,0x00000323, + 0x00000326,0x000200f8,0x00000323,0x00050085,0x00000006,0x00000325,0x000002f8,0x00000038, + 0x000200f9,0x0000032c,0x000200f8,0x00000326,0x0006000c,0x00000006,0x00000328,0x00000001, + 0x00000004,0x000002f8,0x0007000c,0x00000006,0x00000329,0x00000001,0x0000001a,0x00000328, + 0x00000050,0x0008000c,0x00000006,0x0000032b,0x00000001,0x00000032,0x00000329,0x0000003f, + 0x00000409,0x000200f9,0x0000032c,0x000200f8,0x0000032c,0x000700f5,0x00000006,0x00000425, + 0x00000325,0x00000323,0x0000032b,0x00000326,0x00060050,0x0000000f,0x0000043b,0x00000421, + 0x00000423,0x00000425,0x0008000c,0x0000000f,0x000002fe,0x00000001,0x0000002b,0x0000043b, + 0x00000061,0x00000400,0x000200f9,0x000002ff,0x000200f8,0x000002ff,0x000700f5,0x0000000f, + 0x00000427,0x000002e9,0x00000208,0x000002fe,0x0000032c,0x00050051,0x00000006,0x0000020d, + 0x00000427,0x00000000,0x00050051,0x00000006,0x0000020f,0x00000427,0x00000001,0x00050051, + 0x00000006,0x00000211,0x00000427,0x00000002,0x00050051,0x00000006,0x00000213,0x00000415, + 0x00000003,0x00070050,0x00000018,0x00000405,0x0000020d,0x0000020f,0x00000211,0x00000213, + 0x000200f9,0x00000233,0x000200f8,0x00000215,0x00050041,0x0000007a,0x00000216,0x00000077, + 0x00000094,0x0004003d,0x00000006,0x00000217,0x00000216,0x000500b4,0x00000033,0x00000218, + 0x00000217,0x00000147,0x000300f7,0x00000232,0x00000000,0x000400fa,0x00000218,0x00000219, + 0x0000022f,0x000200f8,0x00000219,0x0008004f,0x0000000f,0x0000021b,0x00000415,0x00000415, + 0x00000000,0x00000001,0x00000002,0x00050090,0x0000000f,0x0000021c,0x0000021b,0x000000dd, + 0x00050051,0x00000006,0x0000021e,0x0000021c,0x00000000,0x00060052,0x00000018,0x000003da, + 0x0000021e,0x0000043d,0x00000000,0x00050051,0x00000006,0x00000220,0x0000021c,0x00000001, + 0x00060052,0x00000018,0x000003dc,0x00000220,0x000003da,0x00000001,0x00050051,0x00000006, + 0x00000222,0x0000021c,0x00000002,0x00060052,0x00000018,0x000003de,0x00000222,0x000003dc, + 0x00000002,0x0008004f,0x0000000f,0x00000224,0x000003de,0x000003de,0x00000000,0x00000001, + 0x00000002,0x00050041,0x0000007a,0x00000335,0x00000077,0x000000f2,0x0004003d,0x00000006, + 0x00000336,0x00000335,0x0005008e,0x0000000f,0x00000337,0x00000224,0x00000336,0x00050041, + 0x0000007a,0x00000338,0x00000077,0x00000103,0x0004003d,0x00000006,0x00000339,0x00000338, + 0x000500b7,0x00000033,0x0000033a,0x00000339,0x00000060,0x000400a8,0x00000033,0x0000033b, + 0x0000033a,0x000300f7,0x0000034d,0x00000000,0x000400fa,0x0000033b,0x0000033c,0x0000034d, + 0x000200f8,0x0000033c,0x00050051,0x00000006,0x0000033e,0x00000337,0x00000000,0x000500bc, + 0x00000033,0x00000352,0x0000033e,0x00000047,0x000300f7,0x0000035c,0x00000000,0x000400fa, + 0x00000352,0x00000353,0x00000356,0x000200f8,0x00000353,0x00050085,0x00000006,0x00000355, + 0x0000033e,0x00000038,0x000200f9,0x0000035c,0x000200f8,0x00000356,0x0006000c,0x00000006, + 0x00000358,0x00000001,0x00000004,0x0000033e,0x0007000c,0x00000006,0x00000359,0x00000001, + 0x0000001a,0x00000358,0x00000050,0x0008000c,0x00000006,0x0000035b,0x00000001,0x00000032, + 0x00000359,0x0000003f,0x00000409,0x000200f9,0x0000035c,0x000200f8,0x0000035c,0x000700f5, + 0x00000006,0x00000416,0x00000355,0x00000353,0x0000035b,0x00000356,0x00050051,0x00000006, + 0x00000342,0x00000337,0x00000001,0x000500bc,0x00000033,0x00000361,0x00000342,0x00000047, + 0x000300f7,0x0000036b,0x00000000,0x000400fa,0x00000361,0x00000362,0x00000365,0x000200f8, + 0x00000362,0x00050085,0x00000006,0x00000364,0x00000342,0x00000038,0x000200f9,0x0000036b, + 0x000200f8,0x00000365,0x0006000c,0x00000006,0x00000367,0x00000001,0x00000004,0x00000342, + 0x0007000c,0x00000006,0x00000368,0x00000001,0x0000001a,0x00000367,0x00000050,0x0008000c, + 0x00000006,0x0000036a,0x00000001,0x00000032,0x00000368,0x0000003f,0x00000409,0x000200f9, + 0x0000036b,0x000200f8,0x0000036b,0x000700f5,0x00000006,0x00000418,0x00000364,0x00000362, + 0x0000036a,0x00000365,0x00050051,0x00000006,0x00000346,0x00000337,0x00000002,0x000500bc, + 0x00000033,0x00000370,0x00000346,0x00000047,0x000300f7,0x0000037a,0x00000000,0x000400fa, + 0x00000370,0x00000371,0x00000374,0x000200f8,0x00000371,0x00050085,0x00000006,0x00000373, + 0x00000346,0x00000038,0x000200f9,0x0000037a,0x000200f8,0x00000374,0x0006000c,0x00000006, + 0x00000376,0x00000001,0x00000004,0x00000346,0x0007000c,0x00000006,0x00000377,0x00000001, + 0x0000001a,0x00000376,0x00000050,0x0008000c,0x00000006,0x00000379,0x00000001,0x00000032, + 0x00000377,0x0000003f,0x00000409,0x000200f9,0x0000037a,0x000200f8,0x0000037a,0x000700f5, + 0x00000006,0x0000041a,0x00000373,0x00000371,0x00000379,0x00000374,0x00060050,0x0000000f, + 0x0000043a,0x00000416,0x00000418,0x0000041a,0x0008000c,0x0000000f,0x0000034c,0x00000001, + 0x0000002b,0x0000043a,0x00000061,0x00000400,0x000200f9,0x0000034d,0x000200f8,0x0000034d, + 0x000700f5,0x0000000f,0x0000041c,0x00000337,0x00000219,0x0000034c,0x0000037a,0x00050051, + 0x00000006,0x00000227,0x0000041c,0x00000000,0x00050051,0x00000006,0x00000229,0x0000041c, + 0x00000001,0x00050051,0x00000006,0x0000022b,0x0000041c,0x00000002,0x00050051,0x00000006, + 0x0000022d,0x00000415,0x00000003,0x00070050,0x00000018,0x00000401,0x00000227,0x00000229, + 0x0000022b,0x0000022d,0x000200f9,0x00000232,0x000200f8,0x0000022f,0x0008004f,0x0000000f, + 0x00000380,0x00000415,0x00000415,0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a, + 0x00000381,0x00000077,0x000000f2,0x0004003d,0x00000006,0x00000382,0x00000381,0x0005008e, + 0x0000000f,0x00000383,0x00000380,0x00000382,0x00050051,0x00000006,0x00000385,0x00000383, + 0x00000000,0x00050051,0x00000006,0x00000387,0x00000383,0x00000001,0x00050051,0x00000006, + 0x00000389,0x00000383,0x00000002,0x00050051,0x00000006,0x0000038b,0x00000415,0x00000003, + 0x00070050,0x00000018,0x000003fc,0x00000385,0x00000387,0x00000389,0x0000038b,0x000200f9, + 0x00000232,0x000200f8,0x00000232,0x000700f5,0x00000018,0x00000439,0x00000401,0x0000034d, + 0x000003fc,0x0000022f,0x000200f9,0x00000233,0x000200f8,0x00000233,0x000700f5,0x00000018, + 0x00000438,0x00000405,0x000002ff,0x00000439,0x00000232,0x000200f9,0x00000234,0x000200f8, + 0x00000234,0x000700f5,0x00000018,0x00000437,0x00000408,0x000002ad,0x00000438,0x00000233, + 0x00050085,0x00000018,0x00000238,0x00000437,0x000001bf,0x0003003e,0x000001c2,0x00000238, + 0x000100fd,0x00010038 }; diff --git a/src/render/vulkan/VULKAN_PixelShader_Colors.h b/src/render/vulkan/VULKAN_PixelShader_Colors.h index fbf1fab6f36a7..3ea22fbc4d472 100644 --- a/src/render/vulkan/VULKAN_PixelShader_Colors.h +++ b/src/render/vulkan/VULKAN_PixelShader_Colors.h @@ -1,47 +1,47 @@ // 1113.1.1 #pragma once const uint32_t VULKAN_PixelShader_Colors[] = { - 0x07230203,0x00010000,0x0008000b,0x000000a7,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x07230203,0x00010000,0x0008000b,0x000000a1,0x00000000,0x00020011,0x00000001,0x0006000b, 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, - 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000049,0x0000004d,0x00030010, + 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000048,0x0000004c,0x00030010, 0x00000004,0x00000007,0x00030003,0x00000005,0x000001f4,0x00040005,0x00000004,0x6e69616d, 0x00000000,0x00050005,0x00000018,0x736e6f43,0x746e6174,0x00000073,0x00070006,0x00000018, - 0x00000000,0x47526373,0x756f5f42,0x74757074,0x00000000,0x00070006,0x00000018,0x00000001, - 0x74786574,0x5f657275,0x65707974,0x00000000,0x00060006,0x00000018,0x00000002,0x75706e69, - 0x79745f74,0x00006570,0x00060006,0x00000018,0x00000003,0x6f6c6f63,0x63735f72,0x00656c61, - 0x00070006,0x00000018,0x00000004,0x656e6f74,0x5f70616d,0x6874656d,0x0000646f,0x00070006, - 0x00000018,0x00000005,0x656e6f74,0x5f70616d,0x74636166,0x0031726f,0x00070006,0x00000018, - 0x00000006,0x656e6f74,0x5f70616d,0x74636166,0x0032726f,0x00070006,0x00000018,0x00000007, - 0x5f726473,0x74696877,0x6f705f65,0x00746e69,0x00050006,0x00000018,0x00000008,0x66666f59, - 0x00746573,0x00050006,0x00000018,0x00000009,0x656f6352,0x00006666,0x00050006,0x00000018, - 0x0000000a,0x656f6347,0x00006666,0x00050006,0x00000018,0x0000000b,0x656f6342,0x00006666, - 0x00030005,0x0000001a,0x00000000,0x00050005,0x00000049,0x75706e69,0x6f632e74,0x00726f6c, - 0x00070005,0x0000004d,0x746e6540,0x6f507972,0x4f746e69,0x75707475,0x00000074,0x00050048, - 0x00000018,0x00000000,0x00000023,0x00000000,0x00050048,0x00000018,0x00000001,0x00000023, - 0x00000004,0x00050048,0x00000018,0x00000002,0x00000023,0x00000008,0x00050048,0x00000018, - 0x00000003,0x00000023,0x0000000c,0x00050048,0x00000018,0x00000004,0x00000023,0x00000010, - 0x00050048,0x00000018,0x00000005,0x00000023,0x00000014,0x00050048,0x00000018,0x00000006, - 0x00000023,0x00000018,0x00050048,0x00000018,0x00000007,0x00000023,0x0000001c,0x00050048, - 0x00000018,0x00000008,0x00000023,0x00000020,0x00050048,0x00000018,0x00000009,0x00000023, - 0x00000030,0x00050048,0x00000018,0x0000000a,0x00000023,0x00000040,0x00050048,0x00000018, - 0x0000000b,0x00000023,0x00000050,0x00030047,0x00000018,0x00000002,0x00040047,0x0000001a, - 0x00000022,0x00000000,0x00040047,0x0000001a,0x00000021,0x00000004,0x00040047,0x00000049, - 0x0000001e,0x00000001,0x00040047,0x0000004d,0x0000001e,0x00000000,0x00020013,0x00000002, - 0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,0x00000007, - 0x00000006,0x00000004,0x00040017,0x00000015,0x00000006,0x00000003,0x000e001e,0x00000018, - 0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006, - 0x00000007,0x00000007,0x00000007,0x00000007,0x00040020,0x00000019,0x00000002,0x00000018, - 0x0004003b,0x00000019,0x0000001a,0x00000002,0x00040015,0x0000001b,0x00000020,0x00000001, - 0x0004002b,0x0000001b,0x0000001c,0x00000003,0x00040020,0x0000001d,0x00000002,0x00000006, - 0x0004002b,0x00000006,0x00000033,0x3f800000,0x00040020,0x0000003f,0x00000001,0x00000007, - 0x0004003b,0x0000003f,0x00000049,0x00000001,0x00040020,0x0000004c,0x00000003,0x00000007, - 0x0004003b,0x0000004c,0x0000004d,0x00000003,0x0006002c,0x00000015,0x000000a5,0x00000033, - 0x00000033,0x00000033,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8, - 0x00000005,0x0004003d,0x00000007,0x0000004a,0x00000049,0x00050041,0x0000001d,0x00000084, - 0x0000001a,0x0000001c,0x0004003d,0x00000006,0x00000085,0x00000084,0x0005008e,0x00000015, - 0x00000086,0x000000a5,0x00000085,0x00050051,0x00000006,0x00000088,0x00000086,0x00000000, - 0x00050051,0x00000006,0x0000008a,0x00000086,0x00000001,0x00050051,0x00000006,0x0000008c, - 0x00000086,0x00000002,0x00070050,0x00000007,0x000000a6,0x00000088,0x0000008a,0x0000008c, - 0x00000033,0x00050085,0x00000007,0x0000007e,0x000000a6,0x0000004a,0x0003003e,0x0000004d, - 0x0000007e,0x000100fd,0x00010038 + 0x00000000,0x47526373,0x756f5f42,0x74757074,0x00000000,0x00060006,0x00000018,0x00000001, + 0x75706e69,0x79745f74,0x00006570,0x00060006,0x00000018,0x00000002,0x6f6c6f63,0x63735f72, + 0x00656c61,0x00060006,0x00000018,0x00000003,0x73756e75,0x705f6465,0x00306461,0x00070006, + 0x00000018,0x00000004,0x656e6f74,0x5f70616d,0x6874656d,0x0000646f,0x00070006,0x00000018, + 0x00000005,0x656e6f74,0x5f70616d,0x74636166,0x0031726f,0x00070006,0x00000018,0x00000006, + 0x656e6f74,0x5f70616d,0x74636166,0x0032726f,0x00070006,0x00000018,0x00000007,0x5f726473, + 0x74696877,0x6f705f65,0x00746e69,0x00050006,0x00000018,0x00000008,0x66666f59,0x00746573, + 0x00050006,0x00000018,0x00000009,0x656f6352,0x00006666,0x00050006,0x00000018,0x0000000a, + 0x656f6347,0x00006666,0x00050006,0x00000018,0x0000000b,0x656f6342,0x00006666,0x00030005, + 0x0000001a,0x00000000,0x00050005,0x00000048,0x75706e69,0x6f632e74,0x00726f6c,0x00070005, + 0x0000004c,0x746e6540,0x6f507972,0x4f746e69,0x75707475,0x00000074,0x00050048,0x00000018, + 0x00000000,0x00000023,0x00000000,0x00050048,0x00000018,0x00000001,0x00000023,0x00000004, + 0x00050048,0x00000018,0x00000002,0x00000023,0x00000008,0x00050048,0x00000018,0x00000003, + 0x00000023,0x0000000c,0x00050048,0x00000018,0x00000004,0x00000023,0x00000010,0x00050048, + 0x00000018,0x00000005,0x00000023,0x00000014,0x00050048,0x00000018,0x00000006,0x00000023, + 0x00000018,0x00050048,0x00000018,0x00000007,0x00000023,0x0000001c,0x00050048,0x00000018, + 0x00000008,0x00000023,0x00000020,0x00050048,0x00000018,0x00000009,0x00000023,0x00000030, + 0x00050048,0x00000018,0x0000000a,0x00000023,0x00000040,0x00050048,0x00000018,0x0000000b, + 0x00000023,0x00000050,0x00030047,0x00000018,0x00000002,0x00040047,0x0000001a,0x00000022, + 0x00000000,0x00040047,0x0000001a,0x00000021,0x00000001,0x00040047,0x00000048,0x0000001e, + 0x00000001,0x00040047,0x0000004c,0x0000001e,0x00000000,0x00020013,0x00000002,0x00030021, + 0x00000003,0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,0x00000007,0x00000006, + 0x00000004,0x00040017,0x00000015,0x00000006,0x00000003,0x000e001e,0x00000018,0x00000006, + 0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000007, + 0x00000007,0x00000007,0x00000007,0x00040020,0x00000019,0x00000002,0x00000018,0x0004003b, + 0x00000019,0x0000001a,0x00000002,0x00040015,0x0000001b,0x00000020,0x00000001,0x0004002b, + 0x0000001b,0x0000001c,0x00000002,0x00040020,0x0000001d,0x00000002,0x00000006,0x0004002b, + 0x00000006,0x00000033,0x3f800000,0x00040020,0x0000003e,0x00000001,0x00000007,0x0004003b, + 0x0000003e,0x00000048,0x00000001,0x00040020,0x0000004b,0x00000003,0x00000007,0x0004003b, + 0x0000004b,0x0000004c,0x00000003,0x0006002c,0x00000015,0x0000009f,0x00000033,0x00000033, + 0x00000033,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005, + 0x0004003d,0x00000007,0x00000049,0x00000048,0x00050041,0x0000001d,0x0000007e,0x0000001a, + 0x0000001c,0x0004003d,0x00000006,0x0000007f,0x0000007e,0x0005008e,0x00000015,0x00000080, + 0x0000009f,0x0000007f,0x00050051,0x00000006,0x00000082,0x00000080,0x00000000,0x00050051, + 0x00000006,0x00000084,0x00000080,0x00000001,0x00050051,0x00000006,0x00000086,0x00000080, + 0x00000002,0x00070050,0x00000007,0x000000a0,0x00000082,0x00000084,0x00000086,0x00000033, + 0x00050085,0x00000007,0x00000078,0x000000a0,0x00000049,0x0003003e,0x0000004c,0x00000078, + 0x000100fd,0x00010038 }; diff --git a/src/render/vulkan/VULKAN_PixelShader_Common.incl b/src/render/vulkan/VULKAN_PixelShader_Common.incl index 21afb87a2d266..513bd0334d14a 100644 --- a/src/render/vulkan/VULKAN_PixelShader_Common.incl +++ b/src/render/vulkan/VULKAN_PixelShader_Common.incl @@ -1,8 +1,5 @@ - SamplerState sampler0 : register(s0); -Texture2D texture0 : register(t1); -Texture2D texture1 : register(t2); -Texture2D texture2 : register(t3); +Texture2D texture0 : register(t0); struct PixelShaderInput { @@ -11,28 +8,22 @@ struct PixelShaderInput float4 color : COLOR0; }; -// These should mirror the definitions in SDL_render_d3d12.c +// These should mirror the definitions in SDL_render_vulkan.c static const float TONEMAP_NONE = 0; static const float TONEMAP_LINEAR = 1; static const float TONEMAP_CHROME = 2; -static const float TEXTURETYPE_NONE = 0; -static const float TEXTURETYPE_RGB = 1; -static const float TEXTURETYPE_NV12 = 2; -static const float TEXTURETYPE_NV21 = 3; -static const float TEXTURETYPE_YUV = 4; - static const float INPUTTYPE_UNSPECIFIED = 0; static const float INPUTTYPE_SRGB = 1; static const float INPUTTYPE_SCRGB = 2; static const float INPUTTYPE_HDR10 = 3; -cbuffer Constants : register(b4) +cbuffer Constants : register(b1) { float scRGB_output; - float texture_type; float input_type; float color_scale; + float unused_pad0; float tonemap_method; float tonemap_factor1; @@ -118,48 +109,8 @@ float4 GetInputColor(PixelShaderInput input) { float4 rgba; - if (texture_type == TEXTURETYPE_NONE) { - rgba = 1.0; - } else if (texture_type == TEXTURETYPE_RGB) { - rgba = texture0.Sample(sampler0, input.tex); - } else if (texture_type == TEXTURETYPE_NV12) { - float3 yuv; - yuv.x = texture0.Sample(sampler0, input.tex).r; - yuv.yz = texture1.Sample(sampler0, input.tex).rg; - - yuv += Yoffset.xyz; - rgba.r = dot(yuv, Rcoeff.xyz); - rgba.g = dot(yuv, Gcoeff.xyz); - rgba.b = dot(yuv, Bcoeff.xyz); - rgba.a = 1.0; - } else if (texture_type == TEXTURETYPE_NV21) { - float3 yuv; - yuv.x = texture0.Sample(sampler0, input.tex).r; - yuv.yz = texture1.Sample(sampler0, input.tex).gr; - - yuv += Yoffset.xyz; - rgba.r = dot(yuv, Rcoeff.xyz); - rgba.g = dot(yuv, Gcoeff.xyz); - rgba.b = dot(yuv, Bcoeff.xyz); - rgba.a = 1.0; - } else if (texture_type == TEXTURETYPE_YUV) { - float3 yuv; - yuv.x = texture0.Sample(sampler0, input.tex).r; - yuv.y = texture1.Sample(sampler0, input.tex).r; - yuv.z = texture2.Sample(sampler0, input.tex).r; - - yuv += Yoffset.xyz; - rgba.r = dot(yuv, Rcoeff.xyz); - rgba.g = dot(yuv, Gcoeff.xyz); - rgba.b = dot(yuv, Bcoeff.xyz); - rgba.a = 1.0; - } else { - // Error! - rgba.r = 1.0; - rgba.g = 0.0; - rgba.b = 0.0; - rgba.a = 1.0; - } + rgba = texture0.Sample(sampler0, input.tex).rgba; + return rgba; } diff --git a/src/render/vulkan/VULKAN_PixelShader_Textures.h b/src/render/vulkan/VULKAN_PixelShader_Textures.h index 33ae151417c5d..afee9ad948c19 100644 --- a/src/render/vulkan/VULKAN_PixelShader_Textures.h +++ b/src/render/vulkan/VULKAN_PixelShader_Textures.h @@ -1,60 +1,56 @@ // 1113.1.1 #pragma once const uint32_t VULKAN_PixelShader_Textures[] = { - 0x07230203,0x00010000,0x0008000b,0x000000b3,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x07230203,0x00010000,0x0008000b,0x000000a8,0x00000000,0x00020011,0x00000001,0x0006000b, 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, - 0x0008000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000051,0x00000054,0x00000058, + 0x0008000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x0000004b,0x0000004e,0x00000052, 0x00030010,0x00000004,0x00000007,0x00030003,0x00000005,0x000001f4,0x00040005,0x00000004, 0x6e69616d,0x00000000,0x00050005,0x00000018,0x736e6f43,0x746e6174,0x00000073,0x00070006, - 0x00000018,0x00000000,0x47526373,0x756f5f42,0x74757074,0x00000000,0x00070006,0x00000018, - 0x00000001,0x74786574,0x5f657275,0x65707974,0x00000000,0x00060006,0x00000018,0x00000002, - 0x75706e69,0x79745f74,0x00006570,0x00060006,0x00000018,0x00000003,0x6f6c6f63,0x63735f72, - 0x00656c61,0x00070006,0x00000018,0x00000004,0x656e6f74,0x5f70616d,0x6874656d,0x0000646f, - 0x00070006,0x00000018,0x00000005,0x656e6f74,0x5f70616d,0x74636166,0x0031726f,0x00070006, - 0x00000018,0x00000006,0x656e6f74,0x5f70616d,0x74636166,0x0032726f,0x00070006,0x00000018, - 0x00000007,0x5f726473,0x74696877,0x6f705f65,0x00746e69,0x00050006,0x00000018,0x00000008, - 0x66666f59,0x00746573,0x00050006,0x00000018,0x00000009,0x656f6352,0x00006666,0x00050006, - 0x00000018,0x0000000a,0x656f6347,0x00006666,0x00050006,0x00000018,0x0000000b,0x656f6342, - 0x00006666,0x00030005,0x0000001a,0x00000000,0x00050005,0x00000035,0x74786574,0x30657275, - 0x00000000,0x00050005,0x00000039,0x706d6173,0x3072656c,0x00000000,0x00050005,0x00000051, - 0x75706e69,0x65742e74,0x00000078,0x00050005,0x00000054,0x75706e69,0x6f632e74,0x00726f6c, - 0x00070005,0x00000058,0x746e6540,0x6f507972,0x4f746e69,0x75707475,0x00000074,0x00050048, - 0x00000018,0x00000000,0x00000023,0x00000000,0x00050048,0x00000018,0x00000001,0x00000023, - 0x00000004,0x00050048,0x00000018,0x00000002,0x00000023,0x00000008,0x00050048,0x00000018, - 0x00000003,0x00000023,0x0000000c,0x00050048,0x00000018,0x00000004,0x00000023,0x00000010, - 0x00050048,0x00000018,0x00000005,0x00000023,0x00000014,0x00050048,0x00000018,0x00000006, - 0x00000023,0x00000018,0x00050048,0x00000018,0x00000007,0x00000023,0x0000001c,0x00050048, - 0x00000018,0x00000008,0x00000023,0x00000020,0x00050048,0x00000018,0x00000009,0x00000023, - 0x00000030,0x00050048,0x00000018,0x0000000a,0x00000023,0x00000040,0x00050048,0x00000018, - 0x0000000b,0x00000023,0x00000050,0x00030047,0x00000018,0x00000002,0x00040047,0x0000001a, - 0x00000022,0x00000000,0x00040047,0x0000001a,0x00000021,0x00000004,0x00040047,0x00000035, - 0x00000022,0x00000000,0x00040047,0x00000035,0x00000021,0x00000001,0x00040047,0x00000039, - 0x00000022,0x00000000,0x00040047,0x00000039,0x00000021,0x00000000,0x00040047,0x00000051, - 0x0000001e,0x00000000,0x00040047,0x00000054,0x0000001e,0x00000001,0x00040047,0x00000058, - 0x0000001e,0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016, - 0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040017,0x0000000d, - 0x00000006,0x00000002,0x00040017,0x00000015,0x00000006,0x00000003,0x000e001e,0x00000018, - 0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006, - 0x00000007,0x00000007,0x00000007,0x00000007,0x00040020,0x00000019,0x00000002,0x00000018, - 0x0004003b,0x00000019,0x0000001a,0x00000002,0x00040015,0x0000001b,0x00000020,0x00000001, - 0x0004002b,0x0000001b,0x0000001c,0x00000003,0x00040020,0x0000001d,0x00000002,0x00000006, - 0x00090019,0x00000033,0x00000006,0x00000001,0x00000000,0x00000000,0x00000000,0x00000001, - 0x00000000,0x00040020,0x00000034,0x00000000,0x00000033,0x0004003b,0x00000034,0x00000035, - 0x00000000,0x0002001a,0x00000037,0x00040020,0x00000038,0x00000000,0x00000037,0x0004003b, - 0x00000038,0x00000039,0x00000000,0x0003001b,0x0000003b,0x00000033,0x00040020,0x0000004c, - 0x00000001,0x00000007,0x00040020,0x00000050,0x00000001,0x0000000d,0x0004003b,0x00000050, - 0x00000051,0x00000001,0x0004003b,0x0000004c,0x00000054,0x00000001,0x00040020,0x00000057, - 0x00000003,0x00000007,0x0004003b,0x00000057,0x00000058,0x00000003,0x00050036,0x00000002, - 0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003d,0x0000000d,0x00000052, - 0x00000051,0x0004003d,0x00000007,0x00000055,0x00000054,0x0004003d,0x00000033,0x00000081, - 0x00000035,0x0004003d,0x00000037,0x00000082,0x00000039,0x00050056,0x0000003b,0x00000083, - 0x00000081,0x00000082,0x00050057,0x00000007,0x00000086,0x00000083,0x00000052,0x0008004f, - 0x00000015,0x0000008f,0x00000086,0x00000086,0x00000000,0x00000001,0x00000002,0x00050041, - 0x0000001d,0x00000090,0x0000001a,0x0000001c,0x0004003d,0x00000006,0x00000091,0x00000090, - 0x0005008e,0x00000015,0x00000092,0x0000008f,0x00000091,0x00050051,0x00000006,0x00000094, - 0x00000092,0x00000000,0x00050051,0x00000006,0x00000096,0x00000092,0x00000001,0x00050051, - 0x00000006,0x00000098,0x00000092,0x00000002,0x00050051,0x00000006,0x0000009a,0x00000086, - 0x00000003,0x00070050,0x00000007,0x000000b2,0x00000094,0x00000096,0x00000098,0x0000009a, - 0x00050085,0x00000007,0x0000008a,0x000000b2,0x00000055,0x0003003e,0x00000058,0x0000008a, - 0x000100fd,0x00010038 + 0x00000018,0x00000000,0x47526373,0x756f5f42,0x74757074,0x00000000,0x00060006,0x00000018, + 0x00000001,0x75706e69,0x79745f74,0x00006570,0x00060006,0x00000018,0x00000002,0x6f6c6f63, + 0x63735f72,0x00656c61,0x00060006,0x00000018,0x00000003,0x73756e75,0x705f6465,0x00306461, + 0x00070006,0x00000018,0x00000004,0x656e6f74,0x5f70616d,0x6874656d,0x0000646f,0x00070006, + 0x00000018,0x00000005,0x656e6f74,0x5f70616d,0x74636166,0x0031726f,0x00070006,0x00000018, + 0x00000006,0x656e6f74,0x5f70616d,0x74636166,0x0032726f,0x00070006,0x00000018,0x00000007, + 0x5f726473,0x74696877,0x6f705f65,0x00746e69,0x00050006,0x00000018,0x00000008,0x66666f59, + 0x00746573,0x00050006,0x00000018,0x00000009,0x656f6352,0x00006666,0x00050006,0x00000018, + 0x0000000a,0x656f6347,0x00006666,0x00050006,0x00000018,0x0000000b,0x656f6342,0x00006666, + 0x00030005,0x0000001a,0x00000000,0x00050005,0x00000036,0x74786574,0x30657275,0x00000000, + 0x00050005,0x0000004b,0x75706e69,0x65742e74,0x00000078,0x00050005,0x0000004e,0x75706e69, + 0x6f632e74,0x00726f6c,0x00070005,0x00000052,0x746e6540,0x6f507972,0x4f746e69,0x75707475, + 0x00000074,0x00050048,0x00000018,0x00000000,0x00000023,0x00000000,0x00050048,0x00000018, + 0x00000001,0x00000023,0x00000004,0x00050048,0x00000018,0x00000002,0x00000023,0x00000008, + 0x00050048,0x00000018,0x00000003,0x00000023,0x0000000c,0x00050048,0x00000018,0x00000004, + 0x00000023,0x00000010,0x00050048,0x00000018,0x00000005,0x00000023,0x00000014,0x00050048, + 0x00000018,0x00000006,0x00000023,0x00000018,0x00050048,0x00000018,0x00000007,0x00000023, + 0x0000001c,0x00050048,0x00000018,0x00000008,0x00000023,0x00000020,0x00050048,0x00000018, + 0x00000009,0x00000023,0x00000030,0x00050048,0x00000018,0x0000000a,0x00000023,0x00000040, + 0x00050048,0x00000018,0x0000000b,0x00000023,0x00000050,0x00030047,0x00000018,0x00000002, + 0x00040047,0x0000001a,0x00000022,0x00000000,0x00040047,0x0000001a,0x00000021,0x00000001, + 0x00040047,0x00000036,0x00000022,0x00000000,0x00040047,0x00000036,0x00000021,0x00000000, + 0x00040047,0x0000004b,0x0000001e,0x00000000,0x00040047,0x0000004e,0x0000001e,0x00000001, + 0x00040047,0x00000052,0x0000001e,0x00000000,0x00020013,0x00000002,0x00030021,0x00000003, + 0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000004, + 0x00040017,0x0000000d,0x00000006,0x00000002,0x00040017,0x00000015,0x00000006,0x00000003, + 0x000e001e,0x00000018,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006, + 0x00000006,0x00000006,0x00000007,0x00000007,0x00000007,0x00000007,0x00040020,0x00000019, + 0x00000002,0x00000018,0x0004003b,0x00000019,0x0000001a,0x00000002,0x00040015,0x0000001b, + 0x00000020,0x00000001,0x0004002b,0x0000001b,0x0000001c,0x00000002,0x00040020,0x0000001d, + 0x00000002,0x00000006,0x00090019,0x00000033,0x00000006,0x00000001,0x00000000,0x00000000, + 0x00000000,0x00000001,0x00000000,0x0003001b,0x00000034,0x00000033,0x00040020,0x00000035, + 0x00000000,0x00000034,0x0004003b,0x00000035,0x00000036,0x00000000,0x00040020,0x00000046, + 0x00000001,0x00000007,0x00040020,0x0000004a,0x00000001,0x0000000d,0x0004003b,0x0000004a, + 0x0000004b,0x00000001,0x0004003b,0x00000046,0x0000004e,0x00000001,0x00040020,0x00000051, + 0x00000003,0x00000007,0x0004003b,0x00000051,0x00000052,0x00000003,0x00050036,0x00000002, + 0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003d,0x0000000d,0x0000004c, + 0x0000004b,0x0004003d,0x00000007,0x0000004f,0x0000004e,0x0004003d,0x00000034,0x00000078, + 0x00000036,0x00050057,0x00000007,0x0000007b,0x00000078,0x0000004c,0x0008004f,0x00000015, + 0x00000084,0x0000007b,0x0000007b,0x00000000,0x00000001,0x00000002,0x00050041,0x0000001d, + 0x00000085,0x0000001a,0x0000001c,0x0004003d,0x00000006,0x00000086,0x00000085,0x0005008e, + 0x00000015,0x00000087,0x00000084,0x00000086,0x00050051,0x00000006,0x00000089,0x00000087, + 0x00000000,0x00050051,0x00000006,0x0000008b,0x00000087,0x00000001,0x00050051,0x00000006, + 0x0000008d,0x00000087,0x00000002,0x00050051,0x00000006,0x0000008f,0x0000007b,0x00000003, + 0x00070050,0x00000007,0x000000a7,0x00000089,0x0000008b,0x0000008d,0x0000008f,0x00050085, + 0x00000007,0x0000007f,0x000000a7,0x0000004f,0x0003003e,0x00000052,0x0000007f,0x000100fd, + 0x00010038 }; diff --git a/src/render/vulkan/compile_shaders.bat b/src/render/vulkan/compile_shaders.bat index 446020a7aa558..de3f91238602b 100644 --- a/src/render/vulkan/compile_shaders.bat +++ b/src/render/vulkan/compile_shaders.bat @@ -1,5 +1,5 @@ -glslangValidator -D --sep main -e main -S frag --target-env vulkan1.0 --vn VULKAN_PixelShader_Colors -o VULKAN_PixelShader_Colors.h VULKAN_PixelShader_Colors.hlsl -glslangValidator -D --sep main -e main -S frag --target-env vulkan1.0 --vn VULKAN_PixelShader_Textures -o VULKAN_PixelShader_Textures.h VULKAN_PixelShader_Textures.hlsl -glslangValidator -D --sep main -e main -S frag --target-env vulkan1.0 --vn VULKAN_PixelShader_Advanced -o VULKAN_PixelShader_Advanced.h VULKAN_PixelShader_Advanced.hlsl +glslangValidator -D --sep main -e main -S frag --target-env vulkan1.0 --auto-sampled-textures --vn VULKAN_PixelShader_Colors -o VULKAN_PixelShader_Colors.h VULKAN_PixelShader_Colors.hlsl +glslangValidator -D --sep main -e main -S frag --target-env vulkan1.0 --auto-sampled-textures --vn VULKAN_PixelShader_Textures -o VULKAN_PixelShader_Textures.h VULKAN_PixelShader_Textures.hlsl +glslangValidator -D --sep main -e main -S frag --target-env vulkan1.0 --auto-sampled-textures --vn VULKAN_PixelShader_Advanced -o VULKAN_PixelShader_Advanced.h VULKAN_PixelShader_Advanced.hlsl glslangValidator -D --sep mainColor -e main -S vert --iy --target-env vulkan1.0 --vn VULKAN_VertexShader -o VULKAN_VertexShader.h VULKAN_VertexShader.hlsl From ba34025423164685be80306d658e2b9e453673c1 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 28 Feb 2024 09:00:36 -0800 Subject: [PATCH 139/220] Use direct3d11 as the default renderer on Windows The D3D12 renderer initializes but has poor performance or graphical issues on older Intel hardware. Fixes https://github.com/libsdl-org/SDL/issues/7634 Fixes https://github.com/libsdl-org/SDL/issues/9093 --- src/render/SDL_render.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index ae06ab67724e2..8891acf3b1495 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -89,12 +89,12 @@ this should probably be removed at some point in the future. --ryan. */ #ifndef SDL_RENDER_DISABLED static const SDL_RenderDriver *render_drivers[] = { -#if SDL_VIDEO_RENDER_D3D12 - &D3D12_RenderDriver, -#endif #if SDL_VIDEO_RENDER_D3D11 &D3D11_RenderDriver, #endif +#if SDL_VIDEO_RENDER_D3D12 + &D3D12_RenderDriver, +#endif #if SDL_VIDEO_RENDER_D3D &D3D_RenderDriver, #endif From fc94c3634e4ce4d153493ee983979eebe71e4307 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 28 Feb 2024 09:06:21 -0800 Subject: [PATCH 140/220] Fixed signed/unsigned comparison warning --- src/render/vulkan/SDL_render_vulkan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 14c9a91033190..5b3ed358123fb 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -1547,9 +1547,9 @@ static SDL_bool VULKAN_DeviceExtensionsFound(VULKAN_RenderData *rendererData, in SDL_free(extensionProperties); return SDL_FALSE; } - for (uint32_t ext = 0; ext < extensionsToCheck && foundExtensions; ext++) { + for (int ext = 0; ext < extensionsToCheck && foundExtensions; ext++) { SDL_bool foundExtension = SDL_FALSE; - for (uint32_t i = 0; i< extensionCount; i++) { + for (uint32_t i = 0; i < extensionCount; i++) { if (SDL_strcmp(extensionProperties[i].extensionName, extNames[ext]) == 0) { foundExtension = SDL_TRUE; break; From 353e76b40b11bcb8cbc84764d7e2b4483e16b8b6 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 28 Feb 2024 10:03:18 -0800 Subject: [PATCH 141/220] Use the correct colorspace for yuv conversion tests --- test/testyuv.c | 67 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/test/testyuv.c b/test/testyuv.c index f215ee98ef5cc..de9974f56c65e 100644 --- a/test/testyuv.c +++ b/test/testyuv.c @@ -260,10 +260,13 @@ int main(int argc, char **argv) SDL_Texture *output[3]; const char *titles[3] = { "ORIGINAL", "SOFTWARE", "HARDWARE" }; char title[128]; - const char *yuv_name; - const char *yuv_mode; - Uint32 rgb_format = SDL_PIXELFORMAT_RGBX8888; + YUV_CONVERSION_MODE yuv_mode; + const char *yuv_mode_name; Uint32 yuv_format = SDL_PIXELFORMAT_YV12; + const char *yuv_format_name; + Uint32 rgb_format = SDL_PIXELFORMAT_RGBX8888; + SDL_PropertiesID props; + SDL_Colorspace colorspace; int current = 0; int pitch; Uint8 *raw_yuv; @@ -388,10 +391,28 @@ int main(int argc, char **argv) return 3; } + yuv_mode = GetYUVConversionModeForResolution(original->w, original->h); + switch (yuv_mode) { + case YUV_CONVERSION_JPEG: + yuv_mode_name = "JPEG"; + colorspace = SDL_COLORSPACE_JPEG; + break; + case YUV_CONVERSION_BT601: + yuv_mode_name = "BT.601"; + colorspace = SDL_COLORSPACE_BT601_LIMITED; + break; + case YUV_CONVERSION_BT709: + yuv_mode_name = "BT.709"; + colorspace = SDL_COLORSPACE_BT709_LIMITED; + break; + default: + yuv_mode_name = "UNKNOWN"; + colorspace = SDL_COLORSPACE_UNKNOWN; + break; + } + raw_yuv = SDL_calloc(1, MAX_YUV_SURFACE_SIZE(original->w, original->h, 0)); - ConvertRGBtoYUV(yuv_format, original->pixels, original->pitch, raw_yuv, original->w, original->h, - GetYUVConversionModeForResolution(original->w, original->h), - 0, 100); + ConvertRGBtoYUV(yuv_format, original->pixels, original->pitch, raw_yuv, original->w, original->h, yuv_mode, 0, 100); pitch = CalculateYUVPitch(yuv_format, original->w); converted = SDL_CreateSurface(original->w, original->h, rgb_format); @@ -402,7 +423,7 @@ int main(int argc, char **argv) then = SDL_GetTicks(); for (i = 0; i < iterations; ++i) { - SDL_ConvertPixels(original->w, original->h, yuv_format, raw_yuv, pitch, rgb_format, converted->pixels, converted->pitch); + SDL_ConvertPixelsAndColorspace(original->w, original->h, yuv_format, colorspace, 0, raw_yuv, pitch, rgb_format, SDL_COLORSPACE_SRGB, 0, converted->pixels, converted->pitch); } now = SDL_GetTicks(); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%d iterations in %" SDL_PRIu64 " ms, %.2fms each\n", iterations, (now - then), (float)(now - then) / iterations); @@ -421,31 +442,23 @@ int main(int argc, char **argv) output[0] = SDL_CreateTextureFromSurface(renderer, original); output[1] = SDL_CreateTextureFromSurface(renderer, converted); - output[2] = SDL_CreateTexture(renderer, yuv_format, SDL_TEXTUREACCESS_STREAMING, original->w, original->h); + props = SDL_CreateProperties(); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, colorspace); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, yuv_format); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STREAMING); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, original->w); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, original->h); + output[2] = SDL_CreateTextureWithProperties(renderer, props); + SDL_DestroyProperties(props); if (!output[0] || !output[1] || !output[2]) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create texture: %s\n", SDL_GetError()); return 5; } SDL_UpdateTexture(output[2], NULL, raw_yuv, pitch); - yuv_name = SDL_GetPixelFormatName(yuv_format); - if (SDL_strncmp(yuv_name, "SDL_PIXELFORMAT_", 16) == 0) { - yuv_name += 16; - } - - switch (GetYUVConversionModeForResolution(original->w, original->h)) { - case YUV_CONVERSION_JPEG: - yuv_mode = "JPEG"; - break; - case YUV_CONVERSION_BT601: - yuv_mode = "BT.601"; - break; - case YUV_CONVERSION_BT709: - yuv_mode = "BT.709"; - break; - default: - yuv_mode = "UNKNOWN"; - break; + yuv_format_name = SDL_GetPixelFormatName(yuv_format); + if (SDL_strncmp(yuv_format_name, "SDL_PIXELFORMAT_", 16) == 0) { + yuv_format_name += 16; } { @@ -488,7 +501,7 @@ int main(int argc, char **argv) if (current == 0) { SDLTest_DrawString(renderer, 4, 4, titles[current]); } else { - (void)SDL_snprintf(title, sizeof(title), "%s %s %s", titles[current], yuv_name, yuv_mode); + (void)SDL_snprintf(title, sizeof(title), "%s %s %s", titles[current], yuv_format_name, yuv_mode_name); SDLTest_DrawString(renderer, 4, 4, title); } SDL_RenderPresent(renderer); From 7117d545a3af7ec1dfb0d60506035b34920449ab Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 28 Feb 2024 11:43:22 -0800 Subject: [PATCH 142/220] Fixed crash if the controller product name is NULL This happens when the Razer Synapse software emulates a controller with a keyboard --- src/joystick/SDL_joystick.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index 1e0ca44665354..cb95312f27038 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -2581,7 +2581,7 @@ SDL_JoystickGUID SDL_CreateJoystickGUID(Uint16 bus, Uint16 vendor, Uint16 produc *guid16++ = SDL_SwapLE16(bus); *guid16++ = SDL_SwapLE16(crc); - if (vendor && product) { + if (vendor) { *guid16++ = SDL_SwapLE16(vendor); *guid16++ = 0; *guid16++ = SDL_SwapLE16(product); @@ -2597,7 +2597,9 @@ SDL_JoystickGUID SDL_CreateJoystickGUID(Uint16 bus, Uint16 vendor, Uint16 produc guid.data[14] = driver_signature; guid.data[15] = driver_data; } - SDL_strlcpy((char *)guid16, product_name, available_space); + if (product_name) { + SDL_strlcpy((char *)guid16, product_name, available_space); + } } return guid; } From a241cca9e6360dbc1aea73dbd003f1fd85827d23 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 28 Feb 2024 11:45:24 -0800 Subject: [PATCH 143/220] Fixed warning C4090: 'function': different 'const' qualifiers --- src/render/vulkan/SDL_render_vulkan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 5b3ed358123fb..e2f5ce38fef90 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -1688,7 +1688,7 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateInstance(): %s\n", SDL_Vulkan_GetResultString(result)); return result; } - SDL_free(instanceExtensionsCopy); + SDL_free((void *)instanceExtensionsCopy); } /* Load instance Vulkan functions */ From 4513c32bb398238f7b9b022e4611c4e33e2a9398 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 28 Feb 2024 16:55:47 -0800 Subject: [PATCH 144/220] The ycbcrModel should be based on the transfer matrix, not the color primaries --- src/render/vulkan/SDL_render_vulkan.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index e2f5ce38fef90..296c805b8273f 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -2456,14 +2456,16 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD /* Create samplerYcbcrConversion which will be used on the VkImageView and VkSampler */ samplerYcbcrConversionCreateInfo.format = textureFormat; - switch (SDL_COLORSPACEPRIMARIES(texture->colorspace)) { - case SDL_COLOR_PRIMARIES_BT709: - samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR; - break; - case SDL_COLOR_PRIMARIES_BT601: + switch (SDL_COLORSPACEMATRIX(texture->colorspace)) { + case SDL_MATRIX_COEFFICIENTS_BT601: samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR; break; - case SDL_COLOR_PRIMARIES_BT2020: + case SDL_MATRIX_COEFFICIENTS_BT709: + samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR; + break; + /* FIXME: Are these the same? */ + case SDL_MATRIX_COEFFICIENTS_BT2020_NCL: + case SDL_MATRIX_COEFFICIENTS_BT2020_CL: samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR; default: VULKAN_DestroyTexture(renderer, texture); From b30ba1c5d454e8c5cc09623ca60aad6f9af1e3df Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 28 Feb 2024 16:58:30 -0800 Subject: [PATCH 145/220] Updated RGBtoYUV() to use the full YCbCr conversion formula --- test/testyuv_cvt.c | 78 +++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/test/testyuv_cvt.c b/test/testyuv_cvt.c index 230a0b3758554..67a0ab6d88db4 100644 --- a/test/testyuv_cvt.c +++ b/test/testyuv_cvt.c @@ -48,46 +48,54 @@ static float clip3(float x, float y, float z) static void RGBtoYUV(const Uint8 *rgb, int *yuv, YUV_CONVERSION_MODE mode, int monochrome, int luminance) { + /** + * This formula is from Microsoft's documentation: + * https://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx + * L = Kr * R + Kb * B + (1 - Kr - Kb) * G + * Y = floor(2^(M-8) * (219*(L-Z)/S + 16) + 0.5); + * U = clip3(0, (2^M)-1, floor(2^(M-8) * (112*(B-L) / ((1-Kb)*S) + 128) + 0.5)); + * V = clip3(0, (2^M)-1, floor(2^(M-8) * (112*(R-L) / ((1-Kr)*S) + 128) + 0.5)); + */ + SDL_bool studio_RGB = SDL_FALSE; + SDL_bool studio_YUV = SDL_FALSE; + float N, M, S, Z, R, G, B, L, Kr, Kb, Y, U, V; + + N = 8.0f; /* 8 bit RGB */ + M = 8.0f; /* 8 bit YUV */ + if (mode == YUV_CONVERSION_BT709) { + /* BT.709 */ + Kr = 0.2126f; + Kb = 0.0722f; + } else { + /* BT.601 */ + Kr = 0.299f; + Kb = 0.114f; + } + if (mode == YUV_CONVERSION_JPEG) { - /* Full range YUV */ - yuv[0] = (int)(0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]); - yuv[1] = (int)((rgb[2] - yuv[0]) * 0.565 + 128); - yuv[2] = (int)((rgb[0] - yuv[0]) * 0.713 + 128); + studio_YUV = SDL_FALSE; } else { - /** - * This formula is from Microsoft's documentation: - * https://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx - * L = Kr * R + Kb * B + (1 - Kr - Kb) * G - * Y = SDL_floor(2^(M-8) * (219*(L-Z)/S + 16) + 0.5); - * U = clip3(0, (2^M)-1, SDL_floor(2^(M-8) * (112*(B-L) / ((1-Kb)*S) + 128) + 0.5)); - * V = clip3(0, (2^M)-1, SDL_floor(2^(M-8) * (112*(R-L) / ((1-Kr)*S) + 128) + 0.5)); - */ - float S, Z, R, G, B, L, Kr, Kb, Y, U, V; - - if (mode == YUV_CONVERSION_BT709) { - /* BT.709 */ - Kr = 0.2126f; - Kb = 0.0722f; - } else { - /* BT.601 */ - Kr = 0.299f; - Kb = 0.114f; - } + studio_YUV = SDL_TRUE; + } + if (studio_RGB || !studio_YUV) { + S = 219.0f * SDL_powf(2.0f, N - 8); + Z = 16.0f * SDL_powf(2.0f, N - 8); + } else { S = 255.0f; Z = 0.0f; - R = rgb[0]; - G = rgb[1]; - B = rgb[2]; - L = Kr * R + Kb * B + (1 - Kr - Kb) * G; - Y = (Uint8)SDL_floorf((219 * (L - Z) / S + 16) + 0.5f); - U = (Uint8)clip3(0, 255, SDL_floorf((112.0f * (B - L) / ((1.0f - Kb) * S) + 128) + 0.5f)); - V = (Uint8)clip3(0, 255, SDL_floorf((112.0f * (R - L) / ((1.0f - Kr) * S) + 128) + 0.5f)); - - yuv[0] = (Uint8)Y; - yuv[1] = (Uint8)U; - yuv[2] = (Uint8)V; } + R = rgb[0]; + G = rgb[1]; + B = rgb[2]; + L = Kr * R + Kb * B + (1 - Kr - Kb) * G; + Y = SDL_floorf(SDL_powf(2.0f, (M - 8)) * (219.0f * (L - Z) / S + 16) + 0.5f); + U = clip3(0, SDL_powf(2.0f, M) - 1, SDL_floorf(SDL_powf(2.0f, (M - 8)) * (112.0f * (B - L) / ((1.0f - Kb) * S) + 128) + 0.5f)); + V = clip3(0, SDL_powf(2.0f, M) - 1, SDL_floorf(SDL_powf(2.0f, (M - 8)) * (112.0f * (R - L) / ((1.0f - Kr) * S) + 128) + 0.5f)); + + yuv[0] = (Uint8)Y; + yuv[1] = (Uint8)U; + yuv[2] = (Uint8)V; if (monochrome) { yuv[1] = 128; @@ -95,7 +103,7 @@ static void RGBtoYUV(const Uint8 *rgb, int *yuv, YUV_CONVERSION_MODE mode, int m } if (luminance != 100) { - yuv[0] = yuv[0] * luminance / 100; + yuv[0] = (Uint8)SDL_roundf(yuv[0] * (luminance / 100.0f)); if (yuv[0] > 255) { yuv[0] = 255; } From 039144350c4f46e385d00a33540a68a3ee90bd1b Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 28 Feb 2024 17:15:47 -0800 Subject: [PATCH 146/220] Be more precise about what we're testing in testyuv --- test/testyuv.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/test/testyuv.c b/test/testyuv.c index de9974f56c65e..96858f7d79689 100644 --- a/test/testyuv.c +++ b/test/testyuv.c @@ -395,15 +395,30 @@ int main(int argc, char **argv) switch (yuv_mode) { case YUV_CONVERSION_JPEG: yuv_mode_name = "JPEG"; - colorspace = SDL_COLORSPACE_JPEG; + colorspace = SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR, + SDL_COLOR_RANGE_FULL, + SDL_COLOR_PRIMARIES_BT709, + SDL_TRANSFER_CHARACTERISTICS_BT601, + SDL_MATRIX_COEFFICIENTS_BT601, + SDL_CHROMA_LOCATION_CENTER); break; case YUV_CONVERSION_BT601: yuv_mode_name = "BT.601"; - colorspace = SDL_COLORSPACE_BT601_LIMITED; + colorspace = SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR, + SDL_COLOR_RANGE_LIMITED, + SDL_COLOR_PRIMARIES_BT709, + SDL_TRANSFER_CHARACTERISTICS_BT601, + SDL_MATRIX_COEFFICIENTS_BT601, + SDL_CHROMA_LOCATION_CENTER); break; case YUV_CONVERSION_BT709: yuv_mode_name = "BT.709"; - colorspace = SDL_COLORSPACE_BT709_LIMITED; + colorspace = SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR, + SDL_COLOR_RANGE_LIMITED, + SDL_COLOR_PRIMARIES_BT709, + SDL_TRANSFER_CHARACTERISTICS_BT709, + SDL_MATRIX_COEFFICIENTS_BT709, + SDL_CHROMA_LOCATION_CENTER); break; default: yuv_mode_name = "UNKNOWN"; From bf853823a24a94bcbf0f122324efb8cead4ab84b Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 28 Feb 2024 18:38:06 -0800 Subject: [PATCH 147/220] Removed unused YCbCr_matrix from Vulkan shaders --- src/render/vulkan/SDL_render_vulkan.c | 9 - .../vulkan/VULKAN_PixelShader_Advanced.h | 490 +++++++++--------- src/render/vulkan/VULKAN_PixelShader_Colors.h | 54 +- .../vulkan/VULKAN_PixelShader_Common.incl | 5 - .../vulkan/VULKAN_PixelShader_Textures.h | 66 ++- src/render/vulkan/VULKAN_VertexShader.h | 2 +- 6 files changed, 297 insertions(+), 329 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 296c805b8273f..fdc030c99be3b 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -193,8 +193,6 @@ typedef struct float tonemap_factor1; float tonemap_factor2; float sdr_white_point; - - float YCbCr_matrix[16]; } PixelShaderConstants; /* Per-vertex data */ @@ -238,7 +236,6 @@ typedef struct int width; int height; VULKAN_Shader shader; - const float *YCbCr_matrix; #if SDL_HAVE_YUV /* Object passed to VkImageView and VkSampler for doing Ycbcr -> RGB conversion */ @@ -3170,8 +3167,6 @@ static void VULKAN_SetupShaderConstants(SDL_Renderer *renderer, const SDL_Render constants->color_scale = cmd->data.draw.color_scale; if (texture) { - VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->driverdata; - switch (texture->format) { case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: @@ -3207,10 +3202,6 @@ static void VULKAN_SetupShaderConstants(SDL_Renderer *renderer, const SDL_Render constants->tonemap_factor1 = (output_headroom / (texture->HDR_headroom * texture->HDR_headroom)); constants->tonemap_factor2 = (1.0f / output_headroom); } - - if (textureData->YCbCr_matrix) { - SDL_memcpy(constants->YCbCr_matrix, textureData->YCbCr_matrix, sizeof(constants->YCbCr_matrix)); - } } } diff --git a/src/render/vulkan/VULKAN_PixelShader_Advanced.h b/src/render/vulkan/VULKAN_PixelShader_Advanced.h index 0ac041591c8b4..e9cc017589f39 100644 --- a/src/render/vulkan/VULKAN_PixelShader_Advanced.h +++ b/src/render/vulkan/VULKAN_PixelShader_Advanced.h @@ -1,4 +1,4 @@ - // 1113.1.1 + // 1114.0.0 #pragma once const uint32_t VULKAN_PixelShader_Advanced[] = { 0x07230203,0x00010000,0x0008000b,0x0000043e,0x00000000,0x00020011,0x00000001,0x0006000b, @@ -12,20 +12,15 @@ const uint32_t VULKAN_PixelShader_Advanced[] = { 0x00070006,0x00000075,0x00000004,0x656e6f74,0x5f70616d,0x6874656d,0x0000646f,0x00070006, 0x00000075,0x00000005,0x656e6f74,0x5f70616d,0x74636166,0x0031726f,0x00070006,0x00000075, 0x00000006,0x656e6f74,0x5f70616d,0x74636166,0x0032726f,0x00070006,0x00000075,0x00000007, - 0x5f726473,0x74696877,0x6f705f65,0x00746e69,0x00050006,0x00000075,0x00000008,0x66666f59, - 0x00746573,0x00050006,0x00000075,0x00000009,0x656f6352,0x00006666,0x00050006,0x00000075, - 0x0000000a,0x656f6347,0x00006666,0x00050006,0x00000075,0x0000000b,0x656f6342,0x00006666, - 0x00030005,0x00000077,0x00000000,0x00050005,0x000000e6,0x74786574,0x30657275,0x00000000, - 0x00050005,0x000001bb,0x75706e69,0x65742e74,0x00000078,0x00050005,0x000001be,0x75706e69, - 0x6f632e74,0x00726f6c,0x00070005,0x000001c2,0x746e6540,0x6f507972,0x4f746e69,0x75707475, - 0x00000074,0x00050048,0x00000075,0x00000000,0x00000023,0x00000000,0x00050048,0x00000075, - 0x00000001,0x00000023,0x00000004,0x00050048,0x00000075,0x00000002,0x00000023,0x00000008, - 0x00050048,0x00000075,0x00000003,0x00000023,0x0000000c,0x00050048,0x00000075,0x00000004, - 0x00000023,0x00000010,0x00050048,0x00000075,0x00000005,0x00000023,0x00000014,0x00050048, - 0x00000075,0x00000006,0x00000023,0x00000018,0x00050048,0x00000075,0x00000007,0x00000023, - 0x0000001c,0x00050048,0x00000075,0x00000008,0x00000023,0x00000020,0x00050048,0x00000075, - 0x00000009,0x00000023,0x00000030,0x00050048,0x00000075,0x0000000a,0x00000023,0x00000040, - 0x00050048,0x00000075,0x0000000b,0x00000023,0x00000050,0x00030047,0x00000075,0x00000002, + 0x5f726473,0x74696877,0x6f705f65,0x00746e69,0x00030005,0x00000077,0x00000000,0x00050005, + 0x000000e6,0x74786574,0x30657275,0x00000000,0x00050005,0x000001bb,0x75706e69,0x65742e74, + 0x00000078,0x00050005,0x000001be,0x75706e69,0x6f632e74,0x00726f6c,0x00070005,0x000001c2, + 0x746e6540,0x6f507972,0x4f746e69,0x75707475,0x00000074,0x00050048,0x00000075,0x00000000, + 0x00000023,0x00000000,0x00050048,0x00000075,0x00000001,0x00000023,0x00000004,0x00050048, + 0x00000075,0x00000002,0x00000023,0x00000008,0x00050048,0x00000075,0x00000003,0x00000023, + 0x0000000c,0x00050048,0x00000075,0x00000004,0x00000023,0x00000010,0x00050048,0x00000075, + 0x00000005,0x00000023,0x00000014,0x00050048,0x00000075,0x00000006,0x00000023,0x00000018, + 0x00050048,0x00000075,0x00000007,0x00000023,0x0000001c,0x00030047,0x00000075,0x00000002, 0x00040047,0x00000077,0x00000022,0x00000000,0x00040047,0x00000077,0x00000021,0x00000001, 0x00040047,0x000000e6,0x00000022,0x00000000,0x00040047,0x000000e6,0x00000021,0x00000000, 0x00040047,0x000001bb,0x0000001e,0x00000000,0x00040047,0x000001be,0x0000001e,0x00000001, @@ -41,237 +36,236 @@ const uint32_t VULKAN_PixelShader_Advanced[] = { 0x0006002c,0x0000000f,0x00000061,0x00000060,0x00000060,0x00000060,0x0004002b,0x00000006, 0x00000064,0x4196d000,0x0004002b,0x00000006,0x00000065,0x41958000,0x0004002b,0x00000006, 0x0000006c,0x461c4000,0x0004002b,0x00000006,0x00000071,0x40c8e06b,0x0006002c,0x0000000f, - 0x00000072,0x00000071,0x00000071,0x00000071,0x000e001e,0x00000075,0x00000006,0x00000006, - 0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000018,0x00000018, - 0x00000018,0x00000018,0x00040020,0x00000076,0x00000002,0x00000075,0x0004003b,0x00000076, - 0x00000077,0x00000002,0x00040015,0x00000078,0x00000020,0x00000001,0x0004002b,0x00000078, - 0x00000079,0x00000007,0x00040020,0x0000007a,0x00000002,0x00000006,0x0004002b,0x00000078, - 0x00000081,0x00000004,0x0004002b,0x00000006,0x00000084,0x3f800000,0x0004002b,0x00000078, - 0x00000088,0x00000005,0x0004002b,0x00000006,0x00000090,0x40000000,0x0004002b,0x00000078, - 0x00000094,0x00000001,0x00040018,0x0000009b,0x0000000f,0x00000003,0x0004002b,0x00000006, - 0x0000009c,0x3f209d8c,0x0004002b,0x00000006,0x0000009d,0x3ea897c8,0x0004002b,0x00000006, - 0x0000009e,0x3d3168f9,0x0006002c,0x0000000f,0x0000009f,0x0000009c,0x0000009d,0x0000009e, - 0x0004002b,0x00000006,0x000000a0,0x3d8d82ba,0x0004002b,0x00000006,0x000000a1,0x3f6b670a, - 0x0004002b,0x00000006,0x000000a2,0x3c3a27af,0x0006002c,0x0000000f,0x000000a3,0x000000a0, - 0x000000a1,0x000000a2,0x0004002b,0x00000006,0x000000a4,0x3c86466b,0x0004002b,0x00000006, - 0x000000a5,0x3db44029,0x0004002b,0x00000006,0x000000a6,0x3f6545b7,0x0006002c,0x0000000f, - 0x000000a7,0x000000a4,0x000000a5,0x000000a6,0x0006002c,0x0000009b,0x000000a8,0x0000009f, - 0x000000a3,0x000000a7,0x0004002b,0x00000078,0x000000c1,0x00000006,0x0004002b,0x00000006, - 0x000000d1,0x3fd48b22,0x0004002b,0x00000006,0x000000d2,0xbf1670a0,0x0004002b,0x00000006, - 0x000000d3,0xbd952d23,0x0006002c,0x0000000f,0x000000d4,0x000000d1,0x000000d2,0x000000d3, - 0x0004002b,0x00000006,0x000000d5,0xbdff127f,0x0004002b,0x00000006,0x000000d6,0x3f9102b4, - 0x0004002b,0x00000006,0x000000d7,0xbc08c60d,0x0006002c,0x0000000f,0x000000d8,0x000000d5, - 0x000000d6,0x000000d7,0x0004002b,0x00000006,0x000000d9,0xbc94b7b3,0x0004002b,0x00000006, - 0x000000da,0xbdce05cd,0x0004002b,0x00000006,0x000000db,0x3f8f333c,0x0006002c,0x0000000f, - 0x000000dc,0x000000d9,0x000000da,0x000000db,0x0006002c,0x0000009b,0x000000dd,0x000000d4, - 0x000000d8,0x000000dc,0x00090019,0x000000e3,0x00000006,0x00000001,0x00000000,0x00000000, - 0x00000000,0x00000001,0x00000000,0x0003001b,0x000000e4,0x000000e3,0x00040020,0x000000e5, - 0x00000000,0x000000e4,0x0004003b,0x000000e5,0x000000e6,0x00000000,0x0004002b,0x00000078, - 0x000000f2,0x00000002,0x0004002b,0x00000078,0x00000103,0x00000000,0x0004002b,0x00000006, - 0x00000147,0x40400000,0x00040020,0x000001b6,0x00000001,0x00000018,0x00040020,0x000001ba, - 0x00000001,0x00000019,0x0004003b,0x000001ba,0x000001bb,0x00000001,0x0004003b,0x000001b6, - 0x000001be,0x00000001,0x00040020,0x000001c1,0x00000003,0x00000018,0x0004003b,0x000001c1, - 0x000001c2,0x00000003,0x0006002c,0x0000000f,0x000003fa,0x0000005d,0x0000005d,0x0000005d, - 0x0006002c,0x0000000f,0x000003fb,0x00000064,0x00000064,0x00000064,0x0006002c,0x0000000f, - 0x00000400,0x00000084,0x00000084,0x00000084,0x0004002b,0x00000006,0x00000406,0x3f72a76f, - 0x0004002b,0x00000006,0x00000407,0x3d9e8391,0x0004002b,0x00000006,0x00000409,0xbd6147ae, - 0x00030001,0x00000018,0x0000043d,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003, - 0x000200f8,0x00000005,0x0004003d,0x00000019,0x000001bc,0x000001bb,0x0004003d,0x00000018, - 0x000001bf,0x000001be,0x0004003d,0x000000e4,0x0000023c,0x000000e6,0x00050057,0x00000018, - 0x0000023f,0x0000023c,0x000001bc,0x00050041,0x0000007a,0x000001d8,0x00000077,0x00000094, - 0x0004003d,0x00000006,0x000001d9,0x000001d8,0x000500b4,0x00000033,0x000001da,0x000001d9, - 0x00000147,0x000300f7,0x000001e5,0x00000000,0x000400fa,0x000001da,0x000001db,0x000001e5, - 0x000200f8,0x000001db,0x0008004f,0x0000000f,0x000001dd,0x0000023f,0x0000023f,0x00000000, - 0x00000001,0x00000002,0x0006000c,0x0000000f,0x00000246,0x00000001,0x00000004,0x000001dd, - 0x0007000c,0x0000000f,0x00000247,0x00000001,0x0000001a,0x00000246,0x0000005b,0x00050083, - 0x0000000f,0x00000249,0x00000247,0x000003fa,0x0007000c,0x0000000f,0x0000024a,0x00000001, - 0x00000028,0x00000249,0x00000061,0x0006000c,0x0000000f,0x0000024c,0x00000001,0x00000004, - 0x000001dd,0x0007000c,0x0000000f,0x0000024d,0x00000001,0x0000001a,0x0000024c,0x0000005b, - 0x0005008e,0x0000000f,0x0000024e,0x0000024d,0x00000065,0x00050083,0x0000000f,0x00000250, - 0x000003fb,0x0000024e,0x00050088,0x0000000f,0x00000253,0x0000024a,0x00000250,0x0006000c, - 0x0000000f,0x00000254,0x00000001,0x00000004,0x00000253,0x0007000c,0x0000000f,0x00000255, - 0x00000001,0x0000001a,0x00000254,0x00000072,0x0005008e,0x0000000f,0x00000256,0x00000255, - 0x0000006c,0x00050041,0x0000007a,0x00000257,0x00000077,0x00000079,0x0004003d,0x00000006, - 0x00000258,0x00000257,0x00060050,0x0000000f,0x00000259,0x00000258,0x00000258,0x00000258, - 0x00050088,0x0000000f,0x0000025a,0x00000256,0x00000259,0x00050051,0x00000006,0x000001e0, - 0x0000025a,0x00000000,0x00060052,0x00000018,0x000003a7,0x000001e0,0x0000023f,0x00000000, - 0x00050051,0x00000006,0x000001e2,0x0000025a,0x00000001,0x00060052,0x00000018,0x000003a9, - 0x000001e2,0x000003a7,0x00000001,0x00050051,0x00000006,0x000001e4,0x0000025a,0x00000002, - 0x00060052,0x00000018,0x000003ab,0x000001e4,0x000003a9,0x00000002,0x000200f9,0x000001e5, - 0x000200f8,0x000001e5,0x000700f5,0x00000018,0x0000040a,0x0000023f,0x00000005,0x000003ab, - 0x000001db,0x00050041,0x0000007a,0x000001e6,0x00000077,0x00000081,0x0004003d,0x00000006, - 0x000001e7,0x000001e6,0x000500b7,0x00000033,0x000001e8,0x000001e7,0x00000060,0x000300f7, - 0x000001f3,0x00000000,0x000400fa,0x000001e8,0x000001e9,0x000001f3,0x000200f8,0x000001e9, - 0x0008004f,0x0000000f,0x000001eb,0x0000040a,0x0000040a,0x00000000,0x00000001,0x00000002, - 0x00050041,0x0000007a,0x0000025f,0x00000077,0x00000081,0x0004003d,0x00000006,0x00000260, - 0x0000025f,0x000500b4,0x00000033,0x00000261,0x00000260,0x00000084,0x000300f7,0x00000295, - 0x00000000,0x000400fa,0x00000261,0x00000262,0x00000267,0x000200f8,0x00000262,0x00050041, - 0x0000007a,0x00000263,0x00000077,0x00000088,0x0004003d,0x00000006,0x00000264,0x00000263, - 0x0005008e,0x0000000f,0x00000266,0x000001eb,0x00000264,0x000200f9,0x00000295,0x000200f8, - 0x00000267,0x00050041,0x0000007a,0x00000268,0x00000077,0x00000081,0x0004003d,0x00000006, - 0x00000269,0x00000268,0x000500b4,0x00000033,0x0000026a,0x00000269,0x00000090,0x000300f7, - 0x00000294,0x00000000,0x000400fa,0x0000026a,0x0000026b,0x00000294,0x000200f8,0x0000026b, - 0x00050041,0x0000007a,0x0000026c,0x00000077,0x00000094,0x0004003d,0x00000006,0x0000026d, - 0x0000026c,0x000500b4,0x00000033,0x0000026e,0x0000026d,0x00000090,0x000300f7,0x00000272, - 0x00000000,0x000400fa,0x0000026e,0x0000026f,0x00000272,0x000200f8,0x0000026f,0x00050090, - 0x0000000f,0x00000271,0x000001eb,0x000000a8,0x000200f9,0x00000272,0x000200f8,0x00000272, - 0x000700f5,0x0000000f,0x0000040b,0x000001eb,0x0000026b,0x00000271,0x0000026f,0x00050051, - 0x00000006,0x00000274,0x0000040b,0x00000000,0x00050051,0x00000006,0x00000276,0x0000040b, - 0x00000001,0x00050051,0x00000006,0x00000278,0x0000040b,0x00000002,0x0007000c,0x00000006, - 0x00000279,0x00000001,0x00000028,0x00000276,0x00000278,0x0007000c,0x00000006,0x0000027a, - 0x00000001,0x00000028,0x00000274,0x00000279,0x000500ba,0x00000033,0x0000027c,0x0000027a, - 0x00000060,0x000300f7,0x0000028c,0x00000000,0x000400fa,0x0000027c,0x0000027d,0x0000028c, - 0x000200f8,0x0000027d,0x00050041,0x0000007a,0x0000027e,0x00000077,0x00000088,0x0004003d, - 0x00000006,0x0000027f,0x0000027e,0x0008000c,0x00000006,0x00000282,0x00000001,0x00000032, - 0x0000027f,0x0000027a,0x00000084,0x00050041,0x0000007a,0x00000283,0x00000077,0x000000c1, - 0x0004003d,0x00000006,0x00000284,0x00000283,0x0008000c,0x00000006,0x00000287,0x00000001, - 0x00000032,0x00000284,0x0000027a,0x00000084,0x00050088,0x00000006,0x00000288,0x00000282, - 0x00000287,0x0005008e,0x0000000f,0x0000028b,0x0000040b,0x00000288,0x000200f9,0x0000028c, - 0x000200f8,0x0000028c,0x000700f5,0x0000000f,0x0000040c,0x0000040b,0x00000272,0x0000028b, - 0x0000027d,0x00050041,0x0000007a,0x0000028d,0x00000077,0x00000094,0x0004003d,0x00000006, - 0x0000028e,0x0000028d,0x000500b4,0x00000033,0x0000028f,0x0000028e,0x00000090,0x000300f7, - 0x00000293,0x00000000,0x000400fa,0x0000028f,0x00000290,0x00000293,0x000200f8,0x00000290, - 0x00050090,0x0000000f,0x00000292,0x0000040c,0x000000dd,0x000200f9,0x00000293,0x000200f8, - 0x00000293,0x000700f5,0x0000000f,0x0000040f,0x0000040c,0x0000028c,0x00000292,0x00000290, - 0x000200f9,0x00000294,0x000200f8,0x00000294,0x000700f5,0x0000000f,0x0000040e,0x000001eb, - 0x00000267,0x0000040f,0x00000293,0x000200f9,0x00000295,0x000200f8,0x00000295,0x000700f5, - 0x0000000f,0x0000040d,0x00000266,0x00000262,0x0000040e,0x00000294,0x00050051,0x00000006, - 0x000001ee,0x0000040d,0x00000000,0x00060052,0x00000018,0x000003b0,0x000001ee,0x0000040a, - 0x00000000,0x00050051,0x00000006,0x000001f0,0x0000040d,0x00000001,0x00060052,0x00000018, - 0x000003b2,0x000001f0,0x000003b0,0x00000001,0x00050051,0x00000006,0x000001f2,0x0000040d, - 0x00000002,0x00060052,0x00000018,0x000003b4,0x000001f2,0x000003b2,0x00000002,0x000200f9, - 0x000001f3,0x000200f8,0x000001f3,0x000700f5,0x00000018,0x00000415,0x0000040a,0x000001e5, - 0x000003b4,0x00000295,0x00050041,0x0000007a,0x000001f4,0x00000077,0x00000094,0x0004003d, - 0x00000006,0x000001f5,0x000001f4,0x000500b4,0x00000033,0x000001f6,0x000001f5,0x00000084, - 0x000300f7,0x00000234,0x00000000,0x000400fa,0x000001f6,0x000001f7,0x00000204,0x000200f8, - 0x000001f7,0x0008004f,0x0000000f,0x000001f9,0x00000415,0x00000415,0x00000000,0x00000001, - 0x00000002,0x00050041,0x0000007a,0x0000029d,0x00000077,0x00000103,0x0004003d,0x00000006, - 0x0000029e,0x0000029d,0x000500b7,0x00000033,0x0000029f,0x0000029e,0x00000060,0x000300f7, - 0x000002ad,0x00000000,0x000400fa,0x0000029f,0x000002a0,0x000002ad,0x000200f8,0x000002a0, - 0x00050051,0x00000006,0x000002a2,0x00000415,0x00000000,0x000500bc,0x00000033,0x000002b6, - 0x000002a2,0x00000032,0x000300f7,0x000002c0,0x00000000,0x000400fa,0x000002b6,0x000002b7, - 0x000002ba,0x000200f8,0x000002b7,0x00050085,0x00000006,0x000002b9,0x000002a2,0x00000407, - 0x000200f9,0x000002c0,0x000200f8,0x000002ba,0x00050081,0x00000006,0x000002bc,0x000002a2, - 0x0000003c,0x0006000c,0x00000006,0x000002bd,0x00000001,0x00000004,0x000002bc,0x00050085, - 0x00000006,0x000002be,0x000002bd,0x00000406,0x0007000c,0x00000006,0x000002bf,0x00000001, - 0x0000001a,0x000002be,0x00000041,0x000200f9,0x000002c0,0x000200f8,0x000002c0,0x000700f5, - 0x00000006,0x0000042c,0x000002b9,0x000002b7,0x000002bf,0x000002ba,0x00050051,0x00000006, - 0x000002a6,0x00000415,0x00000001,0x000500bc,0x00000033,0x000002c5,0x000002a6,0x00000032, - 0x000300f7,0x000002cf,0x00000000,0x000400fa,0x000002c5,0x000002c6,0x000002c9,0x000200f8, - 0x000002c6,0x00050085,0x00000006,0x000002c8,0x000002a6,0x00000407,0x000200f9,0x000002cf, - 0x000200f8,0x000002c9,0x00050081,0x00000006,0x000002cb,0x000002a6,0x0000003c,0x0006000c, - 0x00000006,0x000002cc,0x00000001,0x00000004,0x000002cb,0x00050085,0x00000006,0x000002cd, - 0x000002cc,0x00000406,0x0007000c,0x00000006,0x000002ce,0x00000001,0x0000001a,0x000002cd, - 0x00000041,0x000200f9,0x000002cf,0x000200f8,0x000002cf,0x000700f5,0x00000006,0x0000042e, - 0x000002c8,0x000002c6,0x000002ce,0x000002c9,0x00050051,0x00000006,0x000002aa,0x00000415, - 0x00000002,0x000500bc,0x00000033,0x000002d4,0x000002aa,0x00000032,0x000300f7,0x000002de, - 0x00000000,0x000400fa,0x000002d4,0x000002d5,0x000002d8,0x000200f8,0x000002d5,0x00050085, - 0x00000006,0x000002d7,0x000002aa,0x00000407,0x000200f9,0x000002de,0x000200f8,0x000002d8, - 0x00050081,0x00000006,0x000002da,0x000002aa,0x0000003c,0x0006000c,0x00000006,0x000002db, - 0x00000001,0x00000004,0x000002da,0x00050085,0x00000006,0x000002dc,0x000002db,0x00000406, - 0x0007000c,0x00000006,0x000002dd,0x00000001,0x0000001a,0x000002dc,0x00000041,0x000200f9, - 0x000002de,0x000200f8,0x000002de,0x000700f5,0x00000006,0x00000430,0x000002d7,0x000002d5, - 0x000002dd,0x000002d8,0x00060050,0x0000000f,0x0000043c,0x0000042c,0x0000042e,0x00000430, - 0x000200f9,0x000002ad,0x000200f8,0x000002ad,0x000700f5,0x0000000f,0x00000432,0x000001f9, - 0x000001f7,0x0000043c,0x000002de,0x00050041,0x0000007a,0x000002af,0x00000077,0x000000f2, - 0x0004003d,0x00000006,0x000002b0,0x000002af,0x0005008e,0x0000000f,0x000002b1,0x00000432, - 0x000002b0,0x00050051,0x00000006,0x000001fc,0x000002b1,0x00000000,0x00050051,0x00000006, - 0x000001fe,0x000002b1,0x00000001,0x00050051,0x00000006,0x00000200,0x000002b1,0x00000002, - 0x00050051,0x00000006,0x00000202,0x00000415,0x00000003,0x00070050,0x00000018,0x00000408, - 0x000001fc,0x000001fe,0x00000200,0x00000202,0x000200f9,0x00000234,0x000200f8,0x00000204, - 0x00050041,0x0000007a,0x00000205,0x00000077,0x00000094,0x0004003d,0x00000006,0x00000206, - 0x00000205,0x000500b4,0x00000033,0x00000207,0x00000206,0x00000090,0x000300f7,0x00000233, - 0x00000000,0x000400fa,0x00000207,0x00000208,0x00000215,0x000200f8,0x00000208,0x0008004f, - 0x0000000f,0x0000020a,0x00000415,0x00000415,0x00000000,0x00000001,0x00000002,0x00050041, - 0x0000007a,0x000002e7,0x00000077,0x000000f2,0x0004003d,0x00000006,0x000002e8,0x000002e7, - 0x0005008e,0x0000000f,0x000002e9,0x0000020a,0x000002e8,0x00050041,0x0000007a,0x000002ea, - 0x00000077,0x00000103,0x0004003d,0x00000006,0x000002eb,0x000002ea,0x000500b7,0x00000033, - 0x000002ec,0x000002eb,0x00000060,0x000400a8,0x00000033,0x000002ed,0x000002ec,0x000300f7, - 0x000002ff,0x00000000,0x000400fa,0x000002ed,0x000002ee,0x000002ff,0x000200f8,0x000002ee, - 0x00050051,0x00000006,0x000002f0,0x000002e9,0x00000000,0x000500bc,0x00000033,0x00000304, - 0x000002f0,0x00000047,0x000300f7,0x0000030e,0x00000000,0x000400fa,0x00000304,0x00000305, - 0x00000308,0x000200f8,0x00000305,0x00050085,0x00000006,0x00000307,0x000002f0,0x00000038, - 0x000200f9,0x0000030e,0x000200f8,0x00000308,0x0006000c,0x00000006,0x0000030a,0x00000001, - 0x00000004,0x000002f0,0x0007000c,0x00000006,0x0000030b,0x00000001,0x0000001a,0x0000030a, - 0x00000050,0x0008000c,0x00000006,0x0000030d,0x00000001,0x00000032,0x0000030b,0x0000003f, - 0x00000409,0x000200f9,0x0000030e,0x000200f8,0x0000030e,0x000700f5,0x00000006,0x00000421, - 0x00000307,0x00000305,0x0000030d,0x00000308,0x00050051,0x00000006,0x000002f4,0x000002e9, - 0x00000001,0x000500bc,0x00000033,0x00000313,0x000002f4,0x00000047,0x000300f7,0x0000031d, - 0x00000000,0x000400fa,0x00000313,0x00000314,0x00000317,0x000200f8,0x00000314,0x00050085, - 0x00000006,0x00000316,0x000002f4,0x00000038,0x000200f9,0x0000031d,0x000200f8,0x00000317, - 0x0006000c,0x00000006,0x00000319,0x00000001,0x00000004,0x000002f4,0x0007000c,0x00000006, - 0x0000031a,0x00000001,0x0000001a,0x00000319,0x00000050,0x0008000c,0x00000006,0x0000031c, - 0x00000001,0x00000032,0x0000031a,0x0000003f,0x00000409,0x000200f9,0x0000031d,0x000200f8, - 0x0000031d,0x000700f5,0x00000006,0x00000423,0x00000316,0x00000314,0x0000031c,0x00000317, - 0x00050051,0x00000006,0x000002f8,0x000002e9,0x00000002,0x000500bc,0x00000033,0x00000322, - 0x000002f8,0x00000047,0x000300f7,0x0000032c,0x00000000,0x000400fa,0x00000322,0x00000323, - 0x00000326,0x000200f8,0x00000323,0x00050085,0x00000006,0x00000325,0x000002f8,0x00000038, - 0x000200f9,0x0000032c,0x000200f8,0x00000326,0x0006000c,0x00000006,0x00000328,0x00000001, - 0x00000004,0x000002f8,0x0007000c,0x00000006,0x00000329,0x00000001,0x0000001a,0x00000328, - 0x00000050,0x0008000c,0x00000006,0x0000032b,0x00000001,0x00000032,0x00000329,0x0000003f, - 0x00000409,0x000200f9,0x0000032c,0x000200f8,0x0000032c,0x000700f5,0x00000006,0x00000425, - 0x00000325,0x00000323,0x0000032b,0x00000326,0x00060050,0x0000000f,0x0000043b,0x00000421, - 0x00000423,0x00000425,0x0008000c,0x0000000f,0x000002fe,0x00000001,0x0000002b,0x0000043b, - 0x00000061,0x00000400,0x000200f9,0x000002ff,0x000200f8,0x000002ff,0x000700f5,0x0000000f, - 0x00000427,0x000002e9,0x00000208,0x000002fe,0x0000032c,0x00050051,0x00000006,0x0000020d, - 0x00000427,0x00000000,0x00050051,0x00000006,0x0000020f,0x00000427,0x00000001,0x00050051, - 0x00000006,0x00000211,0x00000427,0x00000002,0x00050051,0x00000006,0x00000213,0x00000415, - 0x00000003,0x00070050,0x00000018,0x00000405,0x0000020d,0x0000020f,0x00000211,0x00000213, - 0x000200f9,0x00000233,0x000200f8,0x00000215,0x00050041,0x0000007a,0x00000216,0x00000077, - 0x00000094,0x0004003d,0x00000006,0x00000217,0x00000216,0x000500b4,0x00000033,0x00000218, - 0x00000217,0x00000147,0x000300f7,0x00000232,0x00000000,0x000400fa,0x00000218,0x00000219, - 0x0000022f,0x000200f8,0x00000219,0x0008004f,0x0000000f,0x0000021b,0x00000415,0x00000415, - 0x00000000,0x00000001,0x00000002,0x00050090,0x0000000f,0x0000021c,0x0000021b,0x000000dd, - 0x00050051,0x00000006,0x0000021e,0x0000021c,0x00000000,0x00060052,0x00000018,0x000003da, - 0x0000021e,0x0000043d,0x00000000,0x00050051,0x00000006,0x00000220,0x0000021c,0x00000001, - 0x00060052,0x00000018,0x000003dc,0x00000220,0x000003da,0x00000001,0x00050051,0x00000006, - 0x00000222,0x0000021c,0x00000002,0x00060052,0x00000018,0x000003de,0x00000222,0x000003dc, - 0x00000002,0x0008004f,0x0000000f,0x00000224,0x000003de,0x000003de,0x00000000,0x00000001, - 0x00000002,0x00050041,0x0000007a,0x00000335,0x00000077,0x000000f2,0x0004003d,0x00000006, - 0x00000336,0x00000335,0x0005008e,0x0000000f,0x00000337,0x00000224,0x00000336,0x00050041, - 0x0000007a,0x00000338,0x00000077,0x00000103,0x0004003d,0x00000006,0x00000339,0x00000338, - 0x000500b7,0x00000033,0x0000033a,0x00000339,0x00000060,0x000400a8,0x00000033,0x0000033b, - 0x0000033a,0x000300f7,0x0000034d,0x00000000,0x000400fa,0x0000033b,0x0000033c,0x0000034d, - 0x000200f8,0x0000033c,0x00050051,0x00000006,0x0000033e,0x00000337,0x00000000,0x000500bc, - 0x00000033,0x00000352,0x0000033e,0x00000047,0x000300f7,0x0000035c,0x00000000,0x000400fa, - 0x00000352,0x00000353,0x00000356,0x000200f8,0x00000353,0x00050085,0x00000006,0x00000355, - 0x0000033e,0x00000038,0x000200f9,0x0000035c,0x000200f8,0x00000356,0x0006000c,0x00000006, - 0x00000358,0x00000001,0x00000004,0x0000033e,0x0007000c,0x00000006,0x00000359,0x00000001, - 0x0000001a,0x00000358,0x00000050,0x0008000c,0x00000006,0x0000035b,0x00000001,0x00000032, - 0x00000359,0x0000003f,0x00000409,0x000200f9,0x0000035c,0x000200f8,0x0000035c,0x000700f5, - 0x00000006,0x00000416,0x00000355,0x00000353,0x0000035b,0x00000356,0x00050051,0x00000006, - 0x00000342,0x00000337,0x00000001,0x000500bc,0x00000033,0x00000361,0x00000342,0x00000047, - 0x000300f7,0x0000036b,0x00000000,0x000400fa,0x00000361,0x00000362,0x00000365,0x000200f8, - 0x00000362,0x00050085,0x00000006,0x00000364,0x00000342,0x00000038,0x000200f9,0x0000036b, - 0x000200f8,0x00000365,0x0006000c,0x00000006,0x00000367,0x00000001,0x00000004,0x00000342, - 0x0007000c,0x00000006,0x00000368,0x00000001,0x0000001a,0x00000367,0x00000050,0x0008000c, - 0x00000006,0x0000036a,0x00000001,0x00000032,0x00000368,0x0000003f,0x00000409,0x000200f9, - 0x0000036b,0x000200f8,0x0000036b,0x000700f5,0x00000006,0x00000418,0x00000364,0x00000362, - 0x0000036a,0x00000365,0x00050051,0x00000006,0x00000346,0x00000337,0x00000002,0x000500bc, - 0x00000033,0x00000370,0x00000346,0x00000047,0x000300f7,0x0000037a,0x00000000,0x000400fa, - 0x00000370,0x00000371,0x00000374,0x000200f8,0x00000371,0x00050085,0x00000006,0x00000373, - 0x00000346,0x00000038,0x000200f9,0x0000037a,0x000200f8,0x00000374,0x0006000c,0x00000006, - 0x00000376,0x00000001,0x00000004,0x00000346,0x0007000c,0x00000006,0x00000377,0x00000001, - 0x0000001a,0x00000376,0x00000050,0x0008000c,0x00000006,0x00000379,0x00000001,0x00000032, - 0x00000377,0x0000003f,0x00000409,0x000200f9,0x0000037a,0x000200f8,0x0000037a,0x000700f5, - 0x00000006,0x0000041a,0x00000373,0x00000371,0x00000379,0x00000374,0x00060050,0x0000000f, - 0x0000043a,0x00000416,0x00000418,0x0000041a,0x0008000c,0x0000000f,0x0000034c,0x00000001, - 0x0000002b,0x0000043a,0x00000061,0x00000400,0x000200f9,0x0000034d,0x000200f8,0x0000034d, - 0x000700f5,0x0000000f,0x0000041c,0x00000337,0x00000219,0x0000034c,0x0000037a,0x00050051, - 0x00000006,0x00000227,0x0000041c,0x00000000,0x00050051,0x00000006,0x00000229,0x0000041c, - 0x00000001,0x00050051,0x00000006,0x0000022b,0x0000041c,0x00000002,0x00050051,0x00000006, - 0x0000022d,0x00000415,0x00000003,0x00070050,0x00000018,0x00000401,0x00000227,0x00000229, - 0x0000022b,0x0000022d,0x000200f9,0x00000232,0x000200f8,0x0000022f,0x0008004f,0x0000000f, - 0x00000380,0x00000415,0x00000415,0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a, - 0x00000381,0x00000077,0x000000f2,0x0004003d,0x00000006,0x00000382,0x00000381,0x0005008e, - 0x0000000f,0x00000383,0x00000380,0x00000382,0x00050051,0x00000006,0x00000385,0x00000383, - 0x00000000,0x00050051,0x00000006,0x00000387,0x00000383,0x00000001,0x00050051,0x00000006, - 0x00000389,0x00000383,0x00000002,0x00050051,0x00000006,0x0000038b,0x00000415,0x00000003, - 0x00070050,0x00000018,0x000003fc,0x00000385,0x00000387,0x00000389,0x0000038b,0x000200f9, - 0x00000232,0x000200f8,0x00000232,0x000700f5,0x00000018,0x00000439,0x00000401,0x0000034d, - 0x000003fc,0x0000022f,0x000200f9,0x00000233,0x000200f8,0x00000233,0x000700f5,0x00000018, - 0x00000438,0x00000405,0x000002ff,0x00000439,0x00000232,0x000200f9,0x00000234,0x000200f8, - 0x00000234,0x000700f5,0x00000018,0x00000437,0x00000408,0x000002ad,0x00000438,0x00000233, - 0x00050085,0x00000018,0x00000238,0x00000437,0x000001bf,0x0003003e,0x000001c2,0x00000238, - 0x000100fd,0x00010038 + 0x00000072,0x00000071,0x00000071,0x00000071,0x000a001e,0x00000075,0x00000006,0x00000006, + 0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00040020,0x00000076, + 0x00000002,0x00000075,0x0004003b,0x00000076,0x00000077,0x00000002,0x00040015,0x00000078, + 0x00000020,0x00000001,0x0004002b,0x00000078,0x00000079,0x00000007,0x00040020,0x0000007a, + 0x00000002,0x00000006,0x0004002b,0x00000078,0x00000081,0x00000004,0x0004002b,0x00000006, + 0x00000084,0x3f800000,0x0004002b,0x00000078,0x00000088,0x00000005,0x0004002b,0x00000006, + 0x00000090,0x40000000,0x0004002b,0x00000078,0x00000094,0x00000001,0x00040018,0x0000009b, + 0x0000000f,0x00000003,0x0004002b,0x00000006,0x0000009c,0x3f209d8c,0x0004002b,0x00000006, + 0x0000009d,0x3ea897c8,0x0004002b,0x00000006,0x0000009e,0x3d3168f9,0x0006002c,0x0000000f, + 0x0000009f,0x0000009c,0x0000009d,0x0000009e,0x0004002b,0x00000006,0x000000a0,0x3d8d82ba, + 0x0004002b,0x00000006,0x000000a1,0x3f6b670a,0x0004002b,0x00000006,0x000000a2,0x3c3a27af, + 0x0006002c,0x0000000f,0x000000a3,0x000000a0,0x000000a1,0x000000a2,0x0004002b,0x00000006, + 0x000000a4,0x3c86466b,0x0004002b,0x00000006,0x000000a5,0x3db44029,0x0004002b,0x00000006, + 0x000000a6,0x3f6545b7,0x0006002c,0x0000000f,0x000000a7,0x000000a4,0x000000a5,0x000000a6, + 0x0006002c,0x0000009b,0x000000a8,0x0000009f,0x000000a3,0x000000a7,0x0004002b,0x00000078, + 0x000000c1,0x00000006,0x0004002b,0x00000006,0x000000d1,0x3fd48b22,0x0004002b,0x00000006, + 0x000000d2,0xbf1670a0,0x0004002b,0x00000006,0x000000d3,0xbd952d23,0x0006002c,0x0000000f, + 0x000000d4,0x000000d1,0x000000d2,0x000000d3,0x0004002b,0x00000006,0x000000d5,0xbdff127f, + 0x0004002b,0x00000006,0x000000d6,0x3f9102b4,0x0004002b,0x00000006,0x000000d7,0xbc08c60d, + 0x0006002c,0x0000000f,0x000000d8,0x000000d5,0x000000d6,0x000000d7,0x0004002b,0x00000006, + 0x000000d9,0xbc94b7b3,0x0004002b,0x00000006,0x000000da,0xbdce05cd,0x0004002b,0x00000006, + 0x000000db,0x3f8f333c,0x0006002c,0x0000000f,0x000000dc,0x000000d9,0x000000da,0x000000db, + 0x0006002c,0x0000009b,0x000000dd,0x000000d4,0x000000d8,0x000000dc,0x00090019,0x000000e3, + 0x00000006,0x00000001,0x00000000,0x00000000,0x00000000,0x00000001,0x00000000,0x0003001b, + 0x000000e4,0x000000e3,0x00040020,0x000000e5,0x00000000,0x000000e4,0x0004003b,0x000000e5, + 0x000000e6,0x00000000,0x0004002b,0x00000078,0x000000f2,0x00000002,0x0004002b,0x00000078, + 0x00000103,0x00000000,0x0004002b,0x00000006,0x00000147,0x40400000,0x00040020,0x000001b6, + 0x00000001,0x00000018,0x00040020,0x000001ba,0x00000001,0x00000019,0x0004003b,0x000001ba, + 0x000001bb,0x00000001,0x0004003b,0x000001b6,0x000001be,0x00000001,0x00040020,0x000001c1, + 0x00000003,0x00000018,0x0004003b,0x000001c1,0x000001c2,0x00000003,0x0006002c,0x0000000f, + 0x000003fa,0x0000005d,0x0000005d,0x0000005d,0x0006002c,0x0000000f,0x000003fb,0x00000064, + 0x00000064,0x00000064,0x0006002c,0x0000000f,0x00000400,0x00000084,0x00000084,0x00000084, + 0x0004002b,0x00000006,0x00000406,0x3f72a76f,0x0004002b,0x00000006,0x00000407,0x3d9e8391, + 0x0004002b,0x00000006,0x00000409,0xbd6147ae,0x00030001,0x00000018,0x0000043d,0x00050036, + 0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003d,0x00000019, + 0x000001bc,0x000001bb,0x0004003d,0x00000018,0x000001bf,0x000001be,0x0004003d,0x000000e4, + 0x0000023c,0x000000e6,0x00050057,0x00000018,0x0000023f,0x0000023c,0x000001bc,0x00050041, + 0x0000007a,0x000001d8,0x00000077,0x00000094,0x0004003d,0x00000006,0x000001d9,0x000001d8, + 0x000500b4,0x00000033,0x000001da,0x000001d9,0x00000147,0x000300f7,0x000001e5,0x00000000, + 0x000400fa,0x000001da,0x000001db,0x000001e5,0x000200f8,0x000001db,0x0008004f,0x0000000f, + 0x000001dd,0x0000023f,0x0000023f,0x00000000,0x00000001,0x00000002,0x0006000c,0x0000000f, + 0x00000246,0x00000001,0x00000004,0x000001dd,0x0007000c,0x0000000f,0x00000247,0x00000001, + 0x0000001a,0x00000246,0x0000005b,0x00050083,0x0000000f,0x00000249,0x00000247,0x000003fa, + 0x0007000c,0x0000000f,0x0000024a,0x00000001,0x00000028,0x00000249,0x00000061,0x0006000c, + 0x0000000f,0x0000024c,0x00000001,0x00000004,0x000001dd,0x0007000c,0x0000000f,0x0000024d, + 0x00000001,0x0000001a,0x0000024c,0x0000005b,0x0005008e,0x0000000f,0x0000024e,0x0000024d, + 0x00000065,0x00050083,0x0000000f,0x00000250,0x000003fb,0x0000024e,0x00050088,0x0000000f, + 0x00000253,0x0000024a,0x00000250,0x0006000c,0x0000000f,0x00000254,0x00000001,0x00000004, + 0x00000253,0x0007000c,0x0000000f,0x00000255,0x00000001,0x0000001a,0x00000254,0x00000072, + 0x0005008e,0x0000000f,0x00000256,0x00000255,0x0000006c,0x00050041,0x0000007a,0x00000257, + 0x00000077,0x00000079,0x0004003d,0x00000006,0x00000258,0x00000257,0x00060050,0x0000000f, + 0x00000259,0x00000258,0x00000258,0x00000258,0x00050088,0x0000000f,0x0000025a,0x00000256, + 0x00000259,0x00050051,0x00000006,0x000001e0,0x0000025a,0x00000000,0x00060052,0x00000018, + 0x000003a7,0x000001e0,0x0000023f,0x00000000,0x00050051,0x00000006,0x000001e2,0x0000025a, + 0x00000001,0x00060052,0x00000018,0x000003a9,0x000001e2,0x000003a7,0x00000001,0x00050051, + 0x00000006,0x000001e4,0x0000025a,0x00000002,0x00060052,0x00000018,0x000003ab,0x000001e4, + 0x000003a9,0x00000002,0x000200f9,0x000001e5,0x000200f8,0x000001e5,0x000700f5,0x00000018, + 0x0000040a,0x0000023f,0x00000005,0x000003ab,0x000001db,0x00050041,0x0000007a,0x000001e6, + 0x00000077,0x00000081,0x0004003d,0x00000006,0x000001e7,0x000001e6,0x000500b7,0x00000033, + 0x000001e8,0x000001e7,0x00000060,0x000300f7,0x000001f3,0x00000000,0x000400fa,0x000001e8, + 0x000001e9,0x000001f3,0x000200f8,0x000001e9,0x0008004f,0x0000000f,0x000001eb,0x0000040a, + 0x0000040a,0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a,0x0000025f,0x00000077, + 0x00000081,0x0004003d,0x00000006,0x00000260,0x0000025f,0x000500b4,0x00000033,0x00000261, + 0x00000260,0x00000084,0x000300f7,0x00000295,0x00000000,0x000400fa,0x00000261,0x00000262, + 0x00000267,0x000200f8,0x00000262,0x00050041,0x0000007a,0x00000263,0x00000077,0x00000088, + 0x0004003d,0x00000006,0x00000264,0x00000263,0x0005008e,0x0000000f,0x00000266,0x000001eb, + 0x00000264,0x000200f9,0x00000295,0x000200f8,0x00000267,0x00050041,0x0000007a,0x00000268, + 0x00000077,0x00000081,0x0004003d,0x00000006,0x00000269,0x00000268,0x000500b4,0x00000033, + 0x0000026a,0x00000269,0x00000090,0x000300f7,0x00000294,0x00000000,0x000400fa,0x0000026a, + 0x0000026b,0x00000294,0x000200f8,0x0000026b,0x00050041,0x0000007a,0x0000026c,0x00000077, + 0x00000094,0x0004003d,0x00000006,0x0000026d,0x0000026c,0x000500b4,0x00000033,0x0000026e, + 0x0000026d,0x00000090,0x000300f7,0x00000272,0x00000000,0x000400fa,0x0000026e,0x0000026f, + 0x00000272,0x000200f8,0x0000026f,0x00050090,0x0000000f,0x00000271,0x000001eb,0x000000a8, + 0x000200f9,0x00000272,0x000200f8,0x00000272,0x000700f5,0x0000000f,0x0000040b,0x000001eb, + 0x0000026b,0x00000271,0x0000026f,0x00050051,0x00000006,0x00000274,0x0000040b,0x00000000, + 0x00050051,0x00000006,0x00000276,0x0000040b,0x00000001,0x00050051,0x00000006,0x00000278, + 0x0000040b,0x00000002,0x0007000c,0x00000006,0x00000279,0x00000001,0x00000028,0x00000276, + 0x00000278,0x0007000c,0x00000006,0x0000027a,0x00000001,0x00000028,0x00000274,0x00000279, + 0x000500ba,0x00000033,0x0000027c,0x0000027a,0x00000060,0x000300f7,0x0000028c,0x00000000, + 0x000400fa,0x0000027c,0x0000027d,0x0000028c,0x000200f8,0x0000027d,0x00050041,0x0000007a, + 0x0000027e,0x00000077,0x00000088,0x0004003d,0x00000006,0x0000027f,0x0000027e,0x0008000c, + 0x00000006,0x00000282,0x00000001,0x00000032,0x0000027f,0x0000027a,0x00000084,0x00050041, + 0x0000007a,0x00000283,0x00000077,0x000000c1,0x0004003d,0x00000006,0x00000284,0x00000283, + 0x0008000c,0x00000006,0x00000287,0x00000001,0x00000032,0x00000284,0x0000027a,0x00000084, + 0x00050088,0x00000006,0x00000288,0x00000282,0x00000287,0x0005008e,0x0000000f,0x0000028b, + 0x0000040b,0x00000288,0x000200f9,0x0000028c,0x000200f8,0x0000028c,0x000700f5,0x0000000f, + 0x0000040c,0x0000040b,0x00000272,0x0000028b,0x0000027d,0x00050041,0x0000007a,0x0000028d, + 0x00000077,0x00000094,0x0004003d,0x00000006,0x0000028e,0x0000028d,0x000500b4,0x00000033, + 0x0000028f,0x0000028e,0x00000090,0x000300f7,0x00000293,0x00000000,0x000400fa,0x0000028f, + 0x00000290,0x00000293,0x000200f8,0x00000290,0x00050090,0x0000000f,0x00000292,0x0000040c, + 0x000000dd,0x000200f9,0x00000293,0x000200f8,0x00000293,0x000700f5,0x0000000f,0x0000040f, + 0x0000040c,0x0000028c,0x00000292,0x00000290,0x000200f9,0x00000294,0x000200f8,0x00000294, + 0x000700f5,0x0000000f,0x0000040e,0x000001eb,0x00000267,0x0000040f,0x00000293,0x000200f9, + 0x00000295,0x000200f8,0x00000295,0x000700f5,0x0000000f,0x0000040d,0x00000266,0x00000262, + 0x0000040e,0x00000294,0x00050051,0x00000006,0x000001ee,0x0000040d,0x00000000,0x00060052, + 0x00000018,0x000003b0,0x000001ee,0x0000040a,0x00000000,0x00050051,0x00000006,0x000001f0, + 0x0000040d,0x00000001,0x00060052,0x00000018,0x000003b2,0x000001f0,0x000003b0,0x00000001, + 0x00050051,0x00000006,0x000001f2,0x0000040d,0x00000002,0x00060052,0x00000018,0x000003b4, + 0x000001f2,0x000003b2,0x00000002,0x000200f9,0x000001f3,0x000200f8,0x000001f3,0x000700f5, + 0x00000018,0x00000415,0x0000040a,0x000001e5,0x000003b4,0x00000295,0x00050041,0x0000007a, + 0x000001f4,0x00000077,0x00000094,0x0004003d,0x00000006,0x000001f5,0x000001f4,0x000500b4, + 0x00000033,0x000001f6,0x000001f5,0x00000084,0x000300f7,0x00000234,0x00000000,0x000400fa, + 0x000001f6,0x000001f7,0x00000204,0x000200f8,0x000001f7,0x0008004f,0x0000000f,0x000001f9, + 0x00000415,0x00000415,0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a,0x0000029d, + 0x00000077,0x00000103,0x0004003d,0x00000006,0x0000029e,0x0000029d,0x000500b7,0x00000033, + 0x0000029f,0x0000029e,0x00000060,0x000300f7,0x000002ad,0x00000000,0x000400fa,0x0000029f, + 0x000002a0,0x000002ad,0x000200f8,0x000002a0,0x00050051,0x00000006,0x000002a2,0x00000415, + 0x00000000,0x000500bc,0x00000033,0x000002b6,0x000002a2,0x00000032,0x000300f7,0x000002c0, + 0x00000000,0x000400fa,0x000002b6,0x000002b7,0x000002ba,0x000200f8,0x000002b7,0x00050085, + 0x00000006,0x000002b9,0x000002a2,0x00000407,0x000200f9,0x000002c0,0x000200f8,0x000002ba, + 0x00050081,0x00000006,0x000002bc,0x000002a2,0x0000003c,0x0006000c,0x00000006,0x000002bd, + 0x00000001,0x00000004,0x000002bc,0x00050085,0x00000006,0x000002be,0x000002bd,0x00000406, + 0x0007000c,0x00000006,0x000002bf,0x00000001,0x0000001a,0x000002be,0x00000041,0x000200f9, + 0x000002c0,0x000200f8,0x000002c0,0x000700f5,0x00000006,0x0000042c,0x000002b9,0x000002b7, + 0x000002bf,0x000002ba,0x00050051,0x00000006,0x000002a6,0x00000415,0x00000001,0x000500bc, + 0x00000033,0x000002c5,0x000002a6,0x00000032,0x000300f7,0x000002cf,0x00000000,0x000400fa, + 0x000002c5,0x000002c6,0x000002c9,0x000200f8,0x000002c6,0x00050085,0x00000006,0x000002c8, + 0x000002a6,0x00000407,0x000200f9,0x000002cf,0x000200f8,0x000002c9,0x00050081,0x00000006, + 0x000002cb,0x000002a6,0x0000003c,0x0006000c,0x00000006,0x000002cc,0x00000001,0x00000004, + 0x000002cb,0x00050085,0x00000006,0x000002cd,0x000002cc,0x00000406,0x0007000c,0x00000006, + 0x000002ce,0x00000001,0x0000001a,0x000002cd,0x00000041,0x000200f9,0x000002cf,0x000200f8, + 0x000002cf,0x000700f5,0x00000006,0x0000042e,0x000002c8,0x000002c6,0x000002ce,0x000002c9, + 0x00050051,0x00000006,0x000002aa,0x00000415,0x00000002,0x000500bc,0x00000033,0x000002d4, + 0x000002aa,0x00000032,0x000300f7,0x000002de,0x00000000,0x000400fa,0x000002d4,0x000002d5, + 0x000002d8,0x000200f8,0x000002d5,0x00050085,0x00000006,0x000002d7,0x000002aa,0x00000407, + 0x000200f9,0x000002de,0x000200f8,0x000002d8,0x00050081,0x00000006,0x000002da,0x000002aa, + 0x0000003c,0x0006000c,0x00000006,0x000002db,0x00000001,0x00000004,0x000002da,0x00050085, + 0x00000006,0x000002dc,0x000002db,0x00000406,0x0007000c,0x00000006,0x000002dd,0x00000001, + 0x0000001a,0x000002dc,0x00000041,0x000200f9,0x000002de,0x000200f8,0x000002de,0x000700f5, + 0x00000006,0x00000430,0x000002d7,0x000002d5,0x000002dd,0x000002d8,0x00060050,0x0000000f, + 0x0000043c,0x0000042c,0x0000042e,0x00000430,0x000200f9,0x000002ad,0x000200f8,0x000002ad, + 0x000700f5,0x0000000f,0x00000432,0x000001f9,0x000001f7,0x0000043c,0x000002de,0x00050041, + 0x0000007a,0x000002af,0x00000077,0x000000f2,0x0004003d,0x00000006,0x000002b0,0x000002af, + 0x0005008e,0x0000000f,0x000002b1,0x00000432,0x000002b0,0x00050051,0x00000006,0x000001fc, + 0x000002b1,0x00000000,0x00050051,0x00000006,0x000001fe,0x000002b1,0x00000001,0x00050051, + 0x00000006,0x00000200,0x000002b1,0x00000002,0x00050051,0x00000006,0x00000202,0x00000415, + 0x00000003,0x00070050,0x00000018,0x00000408,0x000001fc,0x000001fe,0x00000200,0x00000202, + 0x000200f9,0x00000234,0x000200f8,0x00000204,0x00050041,0x0000007a,0x00000205,0x00000077, + 0x00000094,0x0004003d,0x00000006,0x00000206,0x00000205,0x000500b4,0x00000033,0x00000207, + 0x00000206,0x00000090,0x000300f7,0x00000233,0x00000000,0x000400fa,0x00000207,0x00000208, + 0x00000215,0x000200f8,0x00000208,0x0008004f,0x0000000f,0x0000020a,0x00000415,0x00000415, + 0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a,0x000002e7,0x00000077,0x000000f2, + 0x0004003d,0x00000006,0x000002e8,0x000002e7,0x0005008e,0x0000000f,0x000002e9,0x0000020a, + 0x000002e8,0x00050041,0x0000007a,0x000002ea,0x00000077,0x00000103,0x0004003d,0x00000006, + 0x000002eb,0x000002ea,0x000500b7,0x00000033,0x000002ec,0x000002eb,0x00000060,0x000400a8, + 0x00000033,0x000002ed,0x000002ec,0x000300f7,0x000002ff,0x00000000,0x000400fa,0x000002ed, + 0x000002ee,0x000002ff,0x000200f8,0x000002ee,0x00050051,0x00000006,0x000002f0,0x000002e9, + 0x00000000,0x000500bc,0x00000033,0x00000304,0x000002f0,0x00000047,0x000300f7,0x0000030e, + 0x00000000,0x000400fa,0x00000304,0x00000305,0x00000308,0x000200f8,0x00000305,0x00050085, + 0x00000006,0x00000307,0x000002f0,0x00000038,0x000200f9,0x0000030e,0x000200f8,0x00000308, + 0x0006000c,0x00000006,0x0000030a,0x00000001,0x00000004,0x000002f0,0x0007000c,0x00000006, + 0x0000030b,0x00000001,0x0000001a,0x0000030a,0x00000050,0x0008000c,0x00000006,0x0000030d, + 0x00000001,0x00000032,0x0000030b,0x0000003f,0x00000409,0x000200f9,0x0000030e,0x000200f8, + 0x0000030e,0x000700f5,0x00000006,0x00000421,0x00000307,0x00000305,0x0000030d,0x00000308, + 0x00050051,0x00000006,0x000002f4,0x000002e9,0x00000001,0x000500bc,0x00000033,0x00000313, + 0x000002f4,0x00000047,0x000300f7,0x0000031d,0x00000000,0x000400fa,0x00000313,0x00000314, + 0x00000317,0x000200f8,0x00000314,0x00050085,0x00000006,0x00000316,0x000002f4,0x00000038, + 0x000200f9,0x0000031d,0x000200f8,0x00000317,0x0006000c,0x00000006,0x00000319,0x00000001, + 0x00000004,0x000002f4,0x0007000c,0x00000006,0x0000031a,0x00000001,0x0000001a,0x00000319, + 0x00000050,0x0008000c,0x00000006,0x0000031c,0x00000001,0x00000032,0x0000031a,0x0000003f, + 0x00000409,0x000200f9,0x0000031d,0x000200f8,0x0000031d,0x000700f5,0x00000006,0x00000423, + 0x00000316,0x00000314,0x0000031c,0x00000317,0x00050051,0x00000006,0x000002f8,0x000002e9, + 0x00000002,0x000500bc,0x00000033,0x00000322,0x000002f8,0x00000047,0x000300f7,0x0000032c, + 0x00000000,0x000400fa,0x00000322,0x00000323,0x00000326,0x000200f8,0x00000323,0x00050085, + 0x00000006,0x00000325,0x000002f8,0x00000038,0x000200f9,0x0000032c,0x000200f8,0x00000326, + 0x0006000c,0x00000006,0x00000328,0x00000001,0x00000004,0x000002f8,0x0007000c,0x00000006, + 0x00000329,0x00000001,0x0000001a,0x00000328,0x00000050,0x0008000c,0x00000006,0x0000032b, + 0x00000001,0x00000032,0x00000329,0x0000003f,0x00000409,0x000200f9,0x0000032c,0x000200f8, + 0x0000032c,0x000700f5,0x00000006,0x00000425,0x00000325,0x00000323,0x0000032b,0x00000326, + 0x00060050,0x0000000f,0x0000043b,0x00000421,0x00000423,0x00000425,0x0008000c,0x0000000f, + 0x000002fe,0x00000001,0x0000002b,0x0000043b,0x00000061,0x00000400,0x000200f9,0x000002ff, + 0x000200f8,0x000002ff,0x000700f5,0x0000000f,0x00000427,0x000002e9,0x00000208,0x000002fe, + 0x0000032c,0x00050051,0x00000006,0x0000020d,0x00000427,0x00000000,0x00050051,0x00000006, + 0x0000020f,0x00000427,0x00000001,0x00050051,0x00000006,0x00000211,0x00000427,0x00000002, + 0x00050051,0x00000006,0x00000213,0x00000415,0x00000003,0x00070050,0x00000018,0x00000405, + 0x0000020d,0x0000020f,0x00000211,0x00000213,0x000200f9,0x00000233,0x000200f8,0x00000215, + 0x00050041,0x0000007a,0x00000216,0x00000077,0x00000094,0x0004003d,0x00000006,0x00000217, + 0x00000216,0x000500b4,0x00000033,0x00000218,0x00000217,0x00000147,0x000300f7,0x00000232, + 0x00000000,0x000400fa,0x00000218,0x00000219,0x0000022f,0x000200f8,0x00000219,0x0008004f, + 0x0000000f,0x0000021b,0x00000415,0x00000415,0x00000000,0x00000001,0x00000002,0x00050090, + 0x0000000f,0x0000021c,0x0000021b,0x000000dd,0x00050051,0x00000006,0x0000021e,0x0000021c, + 0x00000000,0x00060052,0x00000018,0x000003da,0x0000021e,0x0000043d,0x00000000,0x00050051, + 0x00000006,0x00000220,0x0000021c,0x00000001,0x00060052,0x00000018,0x000003dc,0x00000220, + 0x000003da,0x00000001,0x00050051,0x00000006,0x00000222,0x0000021c,0x00000002,0x00060052, + 0x00000018,0x000003de,0x00000222,0x000003dc,0x00000002,0x0008004f,0x0000000f,0x00000224, + 0x000003de,0x000003de,0x00000000,0x00000001,0x00000002,0x00050041,0x0000007a,0x00000335, + 0x00000077,0x000000f2,0x0004003d,0x00000006,0x00000336,0x00000335,0x0005008e,0x0000000f, + 0x00000337,0x00000224,0x00000336,0x00050041,0x0000007a,0x00000338,0x00000077,0x00000103, + 0x0004003d,0x00000006,0x00000339,0x00000338,0x000500b7,0x00000033,0x0000033a,0x00000339, + 0x00000060,0x000400a8,0x00000033,0x0000033b,0x0000033a,0x000300f7,0x0000034d,0x00000000, + 0x000400fa,0x0000033b,0x0000033c,0x0000034d,0x000200f8,0x0000033c,0x00050051,0x00000006, + 0x0000033e,0x00000337,0x00000000,0x000500bc,0x00000033,0x00000352,0x0000033e,0x00000047, + 0x000300f7,0x0000035c,0x00000000,0x000400fa,0x00000352,0x00000353,0x00000356,0x000200f8, + 0x00000353,0x00050085,0x00000006,0x00000355,0x0000033e,0x00000038,0x000200f9,0x0000035c, + 0x000200f8,0x00000356,0x0006000c,0x00000006,0x00000358,0x00000001,0x00000004,0x0000033e, + 0x0007000c,0x00000006,0x00000359,0x00000001,0x0000001a,0x00000358,0x00000050,0x0008000c, + 0x00000006,0x0000035b,0x00000001,0x00000032,0x00000359,0x0000003f,0x00000409,0x000200f9, + 0x0000035c,0x000200f8,0x0000035c,0x000700f5,0x00000006,0x00000416,0x00000355,0x00000353, + 0x0000035b,0x00000356,0x00050051,0x00000006,0x00000342,0x00000337,0x00000001,0x000500bc, + 0x00000033,0x00000361,0x00000342,0x00000047,0x000300f7,0x0000036b,0x00000000,0x000400fa, + 0x00000361,0x00000362,0x00000365,0x000200f8,0x00000362,0x00050085,0x00000006,0x00000364, + 0x00000342,0x00000038,0x000200f9,0x0000036b,0x000200f8,0x00000365,0x0006000c,0x00000006, + 0x00000367,0x00000001,0x00000004,0x00000342,0x0007000c,0x00000006,0x00000368,0x00000001, + 0x0000001a,0x00000367,0x00000050,0x0008000c,0x00000006,0x0000036a,0x00000001,0x00000032, + 0x00000368,0x0000003f,0x00000409,0x000200f9,0x0000036b,0x000200f8,0x0000036b,0x000700f5, + 0x00000006,0x00000418,0x00000364,0x00000362,0x0000036a,0x00000365,0x00050051,0x00000006, + 0x00000346,0x00000337,0x00000002,0x000500bc,0x00000033,0x00000370,0x00000346,0x00000047, + 0x000300f7,0x0000037a,0x00000000,0x000400fa,0x00000370,0x00000371,0x00000374,0x000200f8, + 0x00000371,0x00050085,0x00000006,0x00000373,0x00000346,0x00000038,0x000200f9,0x0000037a, + 0x000200f8,0x00000374,0x0006000c,0x00000006,0x00000376,0x00000001,0x00000004,0x00000346, + 0x0007000c,0x00000006,0x00000377,0x00000001,0x0000001a,0x00000376,0x00000050,0x0008000c, + 0x00000006,0x00000379,0x00000001,0x00000032,0x00000377,0x0000003f,0x00000409,0x000200f9, + 0x0000037a,0x000200f8,0x0000037a,0x000700f5,0x00000006,0x0000041a,0x00000373,0x00000371, + 0x00000379,0x00000374,0x00060050,0x0000000f,0x0000043a,0x00000416,0x00000418,0x0000041a, + 0x0008000c,0x0000000f,0x0000034c,0x00000001,0x0000002b,0x0000043a,0x00000061,0x00000400, + 0x000200f9,0x0000034d,0x000200f8,0x0000034d,0x000700f5,0x0000000f,0x0000041c,0x00000337, + 0x00000219,0x0000034c,0x0000037a,0x00050051,0x00000006,0x00000227,0x0000041c,0x00000000, + 0x00050051,0x00000006,0x00000229,0x0000041c,0x00000001,0x00050051,0x00000006,0x0000022b, + 0x0000041c,0x00000002,0x00050051,0x00000006,0x0000022d,0x00000415,0x00000003,0x00070050, + 0x00000018,0x00000401,0x00000227,0x00000229,0x0000022b,0x0000022d,0x000200f9,0x00000232, + 0x000200f8,0x0000022f,0x0008004f,0x0000000f,0x00000380,0x00000415,0x00000415,0x00000000, + 0x00000001,0x00000002,0x00050041,0x0000007a,0x00000381,0x00000077,0x000000f2,0x0004003d, + 0x00000006,0x00000382,0x00000381,0x0005008e,0x0000000f,0x00000383,0x00000380,0x00000382, + 0x00050051,0x00000006,0x00000385,0x00000383,0x00000000,0x00050051,0x00000006,0x00000387, + 0x00000383,0x00000001,0x00050051,0x00000006,0x00000389,0x00000383,0x00000002,0x00050051, + 0x00000006,0x0000038b,0x00000415,0x00000003,0x00070050,0x00000018,0x000003fc,0x00000385, + 0x00000387,0x00000389,0x0000038b,0x000200f9,0x00000232,0x000200f8,0x00000232,0x000700f5, + 0x00000018,0x00000439,0x00000401,0x0000034d,0x000003fc,0x0000022f,0x000200f9,0x00000233, + 0x000200f8,0x00000233,0x000700f5,0x00000018,0x00000438,0x00000405,0x000002ff,0x00000439, + 0x00000232,0x000200f9,0x00000234,0x000200f8,0x00000234,0x000700f5,0x00000018,0x00000437, + 0x00000408,0x000002ad,0x00000438,0x00000233,0x00050085,0x00000018,0x00000238,0x00000437, + 0x000001bf,0x0003003e,0x000001c2,0x00000238,0x000100fd,0x00010038 }; diff --git a/src/render/vulkan/VULKAN_PixelShader_Colors.h b/src/render/vulkan/VULKAN_PixelShader_Colors.h index 3ea22fbc4d472..fb21fd0719828 100644 --- a/src/render/vulkan/VULKAN_PixelShader_Colors.h +++ b/src/render/vulkan/VULKAN_PixelShader_Colors.h @@ -1,4 +1,4 @@ - // 1113.1.1 + // 1114.0.0 #pragma once const uint32_t VULKAN_PixelShader_Colors[] = { 0x07230203,0x00010000,0x0008000b,0x000000a1,0x00000000,0x00020011,0x00000001,0x0006000b, @@ -12,36 +12,30 @@ const uint32_t VULKAN_PixelShader_Colors[] = { 0x00000018,0x00000004,0x656e6f74,0x5f70616d,0x6874656d,0x0000646f,0x00070006,0x00000018, 0x00000005,0x656e6f74,0x5f70616d,0x74636166,0x0031726f,0x00070006,0x00000018,0x00000006, 0x656e6f74,0x5f70616d,0x74636166,0x0032726f,0x00070006,0x00000018,0x00000007,0x5f726473, - 0x74696877,0x6f705f65,0x00746e69,0x00050006,0x00000018,0x00000008,0x66666f59,0x00746573, - 0x00050006,0x00000018,0x00000009,0x656f6352,0x00006666,0x00050006,0x00000018,0x0000000a, - 0x656f6347,0x00006666,0x00050006,0x00000018,0x0000000b,0x656f6342,0x00006666,0x00030005, - 0x0000001a,0x00000000,0x00050005,0x00000048,0x75706e69,0x6f632e74,0x00726f6c,0x00070005, - 0x0000004c,0x746e6540,0x6f507972,0x4f746e69,0x75707475,0x00000074,0x00050048,0x00000018, - 0x00000000,0x00000023,0x00000000,0x00050048,0x00000018,0x00000001,0x00000023,0x00000004, - 0x00050048,0x00000018,0x00000002,0x00000023,0x00000008,0x00050048,0x00000018,0x00000003, - 0x00000023,0x0000000c,0x00050048,0x00000018,0x00000004,0x00000023,0x00000010,0x00050048, - 0x00000018,0x00000005,0x00000023,0x00000014,0x00050048,0x00000018,0x00000006,0x00000023, - 0x00000018,0x00050048,0x00000018,0x00000007,0x00000023,0x0000001c,0x00050048,0x00000018, - 0x00000008,0x00000023,0x00000020,0x00050048,0x00000018,0x00000009,0x00000023,0x00000030, - 0x00050048,0x00000018,0x0000000a,0x00000023,0x00000040,0x00050048,0x00000018,0x0000000b, - 0x00000023,0x00000050,0x00030047,0x00000018,0x00000002,0x00040047,0x0000001a,0x00000022, + 0x74696877,0x6f705f65,0x00746e69,0x00030005,0x0000001a,0x00000000,0x00050005,0x00000048, + 0x75706e69,0x6f632e74,0x00726f6c,0x00070005,0x0000004c,0x746e6540,0x6f507972,0x4f746e69, + 0x75707475,0x00000074,0x00050048,0x00000018,0x00000000,0x00000023,0x00000000,0x00050048, + 0x00000018,0x00000001,0x00000023,0x00000004,0x00050048,0x00000018,0x00000002,0x00000023, + 0x00000008,0x00050048,0x00000018,0x00000003,0x00000023,0x0000000c,0x00050048,0x00000018, + 0x00000004,0x00000023,0x00000010,0x00050048,0x00000018,0x00000005,0x00000023,0x00000014, + 0x00050048,0x00000018,0x00000006,0x00000023,0x00000018,0x00050048,0x00000018,0x00000007, + 0x00000023,0x0000001c,0x00030047,0x00000018,0x00000002,0x00040047,0x0000001a,0x00000022, 0x00000000,0x00040047,0x0000001a,0x00000021,0x00000001,0x00040047,0x00000048,0x0000001e, 0x00000001,0x00040047,0x0000004c,0x0000001e,0x00000000,0x00020013,0x00000002,0x00030021, 0x00000003,0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,0x00000007,0x00000006, - 0x00000004,0x00040017,0x00000015,0x00000006,0x00000003,0x000e001e,0x00000018,0x00000006, - 0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000007, - 0x00000007,0x00000007,0x00000007,0x00040020,0x00000019,0x00000002,0x00000018,0x0004003b, - 0x00000019,0x0000001a,0x00000002,0x00040015,0x0000001b,0x00000020,0x00000001,0x0004002b, - 0x0000001b,0x0000001c,0x00000002,0x00040020,0x0000001d,0x00000002,0x00000006,0x0004002b, - 0x00000006,0x00000033,0x3f800000,0x00040020,0x0000003e,0x00000001,0x00000007,0x0004003b, - 0x0000003e,0x00000048,0x00000001,0x00040020,0x0000004b,0x00000003,0x00000007,0x0004003b, - 0x0000004b,0x0000004c,0x00000003,0x0006002c,0x00000015,0x0000009f,0x00000033,0x00000033, - 0x00000033,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005, - 0x0004003d,0x00000007,0x00000049,0x00000048,0x00050041,0x0000001d,0x0000007e,0x0000001a, - 0x0000001c,0x0004003d,0x00000006,0x0000007f,0x0000007e,0x0005008e,0x00000015,0x00000080, - 0x0000009f,0x0000007f,0x00050051,0x00000006,0x00000082,0x00000080,0x00000000,0x00050051, - 0x00000006,0x00000084,0x00000080,0x00000001,0x00050051,0x00000006,0x00000086,0x00000080, - 0x00000002,0x00070050,0x00000007,0x000000a0,0x00000082,0x00000084,0x00000086,0x00000033, - 0x00050085,0x00000007,0x00000078,0x000000a0,0x00000049,0x0003003e,0x0000004c,0x00000078, - 0x000100fd,0x00010038 + 0x00000004,0x00040017,0x00000015,0x00000006,0x00000003,0x000a001e,0x00000018,0x00000006, + 0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00040020, + 0x00000019,0x00000002,0x00000018,0x0004003b,0x00000019,0x0000001a,0x00000002,0x00040015, + 0x0000001b,0x00000020,0x00000001,0x0004002b,0x0000001b,0x0000001c,0x00000002,0x00040020, + 0x0000001d,0x00000002,0x00000006,0x0004002b,0x00000006,0x00000033,0x3f800000,0x00040020, + 0x0000003e,0x00000001,0x00000007,0x0004003b,0x0000003e,0x00000048,0x00000001,0x00040020, + 0x0000004b,0x00000003,0x00000007,0x0004003b,0x0000004b,0x0000004c,0x00000003,0x0006002c, + 0x00000015,0x0000009f,0x00000033,0x00000033,0x00000033,0x00050036,0x00000002,0x00000004, + 0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003d,0x00000007,0x00000049,0x00000048, + 0x00050041,0x0000001d,0x0000007e,0x0000001a,0x0000001c,0x0004003d,0x00000006,0x0000007f, + 0x0000007e,0x0005008e,0x00000015,0x00000080,0x0000009f,0x0000007f,0x00050051,0x00000006, + 0x00000082,0x00000080,0x00000000,0x00050051,0x00000006,0x00000084,0x00000080,0x00000001, + 0x00050051,0x00000006,0x00000086,0x00000080,0x00000002,0x00070050,0x00000007,0x000000a0, + 0x00000082,0x00000084,0x00000086,0x00000033,0x00050085,0x00000007,0x00000078,0x000000a0, + 0x00000049,0x0003003e,0x0000004c,0x00000078,0x000100fd,0x00010038 }; diff --git a/src/render/vulkan/VULKAN_PixelShader_Common.incl b/src/render/vulkan/VULKAN_PixelShader_Common.incl index 513bd0334d14a..04ca409b48542 100644 --- a/src/render/vulkan/VULKAN_PixelShader_Common.incl +++ b/src/render/vulkan/VULKAN_PixelShader_Common.incl @@ -29,11 +29,6 @@ cbuffer Constants : register(b1) float tonemap_factor1; float tonemap_factor2; float sdr_white_point; - - float4 Yoffset; - float4 Rcoeff; - float4 Gcoeff; - float4 Bcoeff; }; static const float3x3 mat709to2020 = { diff --git a/src/render/vulkan/VULKAN_PixelShader_Textures.h b/src/render/vulkan/VULKAN_PixelShader_Textures.h index afee9ad948c19..c423e4ef67535 100644 --- a/src/render/vulkan/VULKAN_PixelShader_Textures.h +++ b/src/render/vulkan/VULKAN_PixelShader_Textures.h @@ -1,4 +1,4 @@ - // 1113.1.1 + // 1114.0.0 #pragma once const uint32_t VULKAN_PixelShader_Textures[] = { 0x07230203,0x00010000,0x0008000b,0x000000a8,0x00000000,0x00020011,0x00000001,0x0006000b, @@ -12,45 +12,39 @@ const uint32_t VULKAN_PixelShader_Textures[] = { 0x00070006,0x00000018,0x00000004,0x656e6f74,0x5f70616d,0x6874656d,0x0000646f,0x00070006, 0x00000018,0x00000005,0x656e6f74,0x5f70616d,0x74636166,0x0031726f,0x00070006,0x00000018, 0x00000006,0x656e6f74,0x5f70616d,0x74636166,0x0032726f,0x00070006,0x00000018,0x00000007, - 0x5f726473,0x74696877,0x6f705f65,0x00746e69,0x00050006,0x00000018,0x00000008,0x66666f59, - 0x00746573,0x00050006,0x00000018,0x00000009,0x656f6352,0x00006666,0x00050006,0x00000018, - 0x0000000a,0x656f6347,0x00006666,0x00050006,0x00000018,0x0000000b,0x656f6342,0x00006666, - 0x00030005,0x0000001a,0x00000000,0x00050005,0x00000036,0x74786574,0x30657275,0x00000000, - 0x00050005,0x0000004b,0x75706e69,0x65742e74,0x00000078,0x00050005,0x0000004e,0x75706e69, - 0x6f632e74,0x00726f6c,0x00070005,0x00000052,0x746e6540,0x6f507972,0x4f746e69,0x75707475, - 0x00000074,0x00050048,0x00000018,0x00000000,0x00000023,0x00000000,0x00050048,0x00000018, - 0x00000001,0x00000023,0x00000004,0x00050048,0x00000018,0x00000002,0x00000023,0x00000008, - 0x00050048,0x00000018,0x00000003,0x00000023,0x0000000c,0x00050048,0x00000018,0x00000004, - 0x00000023,0x00000010,0x00050048,0x00000018,0x00000005,0x00000023,0x00000014,0x00050048, - 0x00000018,0x00000006,0x00000023,0x00000018,0x00050048,0x00000018,0x00000007,0x00000023, - 0x0000001c,0x00050048,0x00000018,0x00000008,0x00000023,0x00000020,0x00050048,0x00000018, - 0x00000009,0x00000023,0x00000030,0x00050048,0x00000018,0x0000000a,0x00000023,0x00000040, - 0x00050048,0x00000018,0x0000000b,0x00000023,0x00000050,0x00030047,0x00000018,0x00000002, + 0x5f726473,0x74696877,0x6f705f65,0x00746e69,0x00030005,0x0000001a,0x00000000,0x00050005, + 0x00000036,0x74786574,0x30657275,0x00000000,0x00050005,0x0000004b,0x75706e69,0x65742e74, + 0x00000078,0x00050005,0x0000004e,0x75706e69,0x6f632e74,0x00726f6c,0x00070005,0x00000052, + 0x746e6540,0x6f507972,0x4f746e69,0x75707475,0x00000074,0x00050048,0x00000018,0x00000000, + 0x00000023,0x00000000,0x00050048,0x00000018,0x00000001,0x00000023,0x00000004,0x00050048, + 0x00000018,0x00000002,0x00000023,0x00000008,0x00050048,0x00000018,0x00000003,0x00000023, + 0x0000000c,0x00050048,0x00000018,0x00000004,0x00000023,0x00000010,0x00050048,0x00000018, + 0x00000005,0x00000023,0x00000014,0x00050048,0x00000018,0x00000006,0x00000023,0x00000018, + 0x00050048,0x00000018,0x00000007,0x00000023,0x0000001c,0x00030047,0x00000018,0x00000002, 0x00040047,0x0000001a,0x00000022,0x00000000,0x00040047,0x0000001a,0x00000021,0x00000001, 0x00040047,0x00000036,0x00000022,0x00000000,0x00040047,0x00000036,0x00000021,0x00000000, 0x00040047,0x0000004b,0x0000001e,0x00000000,0x00040047,0x0000004e,0x0000001e,0x00000001, 0x00040047,0x00000052,0x0000001e,0x00000000,0x00020013,0x00000002,0x00030021,0x00000003, 0x00000002,0x00030016,0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000004, 0x00040017,0x0000000d,0x00000006,0x00000002,0x00040017,0x00000015,0x00000006,0x00000003, - 0x000e001e,0x00000018,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006, - 0x00000006,0x00000006,0x00000007,0x00000007,0x00000007,0x00000007,0x00040020,0x00000019, - 0x00000002,0x00000018,0x0004003b,0x00000019,0x0000001a,0x00000002,0x00040015,0x0000001b, - 0x00000020,0x00000001,0x0004002b,0x0000001b,0x0000001c,0x00000002,0x00040020,0x0000001d, - 0x00000002,0x00000006,0x00090019,0x00000033,0x00000006,0x00000001,0x00000000,0x00000000, - 0x00000000,0x00000001,0x00000000,0x0003001b,0x00000034,0x00000033,0x00040020,0x00000035, - 0x00000000,0x00000034,0x0004003b,0x00000035,0x00000036,0x00000000,0x00040020,0x00000046, - 0x00000001,0x00000007,0x00040020,0x0000004a,0x00000001,0x0000000d,0x0004003b,0x0000004a, - 0x0000004b,0x00000001,0x0004003b,0x00000046,0x0000004e,0x00000001,0x00040020,0x00000051, - 0x00000003,0x00000007,0x0004003b,0x00000051,0x00000052,0x00000003,0x00050036,0x00000002, - 0x00000004,0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003d,0x0000000d,0x0000004c, - 0x0000004b,0x0004003d,0x00000007,0x0000004f,0x0000004e,0x0004003d,0x00000034,0x00000078, - 0x00000036,0x00050057,0x00000007,0x0000007b,0x00000078,0x0000004c,0x0008004f,0x00000015, - 0x00000084,0x0000007b,0x0000007b,0x00000000,0x00000001,0x00000002,0x00050041,0x0000001d, - 0x00000085,0x0000001a,0x0000001c,0x0004003d,0x00000006,0x00000086,0x00000085,0x0005008e, - 0x00000015,0x00000087,0x00000084,0x00000086,0x00050051,0x00000006,0x00000089,0x00000087, - 0x00000000,0x00050051,0x00000006,0x0000008b,0x00000087,0x00000001,0x00050051,0x00000006, - 0x0000008d,0x00000087,0x00000002,0x00050051,0x00000006,0x0000008f,0x0000007b,0x00000003, - 0x00070050,0x00000007,0x000000a7,0x00000089,0x0000008b,0x0000008d,0x0000008f,0x00050085, - 0x00000007,0x0000007f,0x000000a7,0x0000004f,0x0003003e,0x00000052,0x0000007f,0x000100fd, - 0x00010038 + 0x000a001e,0x00000018,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006,0x00000006, + 0x00000006,0x00000006,0x00040020,0x00000019,0x00000002,0x00000018,0x0004003b,0x00000019, + 0x0000001a,0x00000002,0x00040015,0x0000001b,0x00000020,0x00000001,0x0004002b,0x0000001b, + 0x0000001c,0x00000002,0x00040020,0x0000001d,0x00000002,0x00000006,0x00090019,0x00000033, + 0x00000006,0x00000001,0x00000000,0x00000000,0x00000000,0x00000001,0x00000000,0x0003001b, + 0x00000034,0x00000033,0x00040020,0x00000035,0x00000000,0x00000034,0x0004003b,0x00000035, + 0x00000036,0x00000000,0x00040020,0x00000046,0x00000001,0x00000007,0x00040020,0x0000004a, + 0x00000001,0x0000000d,0x0004003b,0x0000004a,0x0000004b,0x00000001,0x0004003b,0x00000046, + 0x0000004e,0x00000001,0x00040020,0x00000051,0x00000003,0x00000007,0x0004003b,0x00000051, + 0x00000052,0x00000003,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8, + 0x00000005,0x0004003d,0x0000000d,0x0000004c,0x0000004b,0x0004003d,0x00000007,0x0000004f, + 0x0000004e,0x0004003d,0x00000034,0x00000078,0x00000036,0x00050057,0x00000007,0x0000007b, + 0x00000078,0x0000004c,0x0008004f,0x00000015,0x00000084,0x0000007b,0x0000007b,0x00000000, + 0x00000001,0x00000002,0x00050041,0x0000001d,0x00000085,0x0000001a,0x0000001c,0x0004003d, + 0x00000006,0x00000086,0x00000085,0x0005008e,0x00000015,0x00000087,0x00000084,0x00000086, + 0x00050051,0x00000006,0x00000089,0x00000087,0x00000000,0x00050051,0x00000006,0x0000008b, + 0x00000087,0x00000001,0x00050051,0x00000006,0x0000008d,0x00000087,0x00000002,0x00050051, + 0x00000006,0x0000008f,0x0000007b,0x00000003,0x00070050,0x00000007,0x000000a7,0x00000089, + 0x0000008b,0x0000008d,0x0000008f,0x00050085,0x00000007,0x0000007f,0x000000a7,0x0000004f, + 0x0003003e,0x00000052,0x0000007f,0x000100fd,0x00010038 }; diff --git a/src/render/vulkan/VULKAN_VertexShader.h b/src/render/vulkan/VULKAN_VertexShader.h index b6c55d5a19d68..3f5b76741a294 100644 --- a/src/render/vulkan/VULKAN_VertexShader.h +++ b/src/render/vulkan/VULKAN_VertexShader.h @@ -1,4 +1,4 @@ - // 1113.1.1 + // 1114.0.0 #pragma once const uint32_t VULKAN_VertexShader[] = { 0x07230203,0x00010000,0x0008000b,0x000000a1,0x00000000,0x00020011,0x00000001,0x0006000b, From 59bbfc1fddbfa5319f6f99e20370194e5dc8b8bb Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 28 Feb 2024 19:04:00 -0800 Subject: [PATCH 148/220] Vulkan: only advertise YUV formats if the VK_KHR_sampler_ycbcr_conversion extension is available Also check to see if it's available when given an external Vulkan device --- src/render/vulkan/SDL_render_vulkan.c | 55 ++++++++++++++------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index fdc030c99be3b..895c5d11689d3 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -331,7 +331,7 @@ typedef struct SDL_bool supportsEXTSwapchainColorspace; SDL_bool supportsKHRGetPhysicalDeviceProperties2; - SDL_bool supportsKHRSamplerYcBcrConversion; + SDL_bool supportsKHRSamplerYCbCrConversion; uint32_t surfaceFormatsAllocatedCount; uint32_t surfaceFormatsCount; uint32_t swapchainDesiredImageCount; @@ -1614,6 +1614,17 @@ static SDL_bool VULKAN_ValidationLayersFound() /* Create resources that depend on the device. */ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_PropertiesID create_props) { + static const char *const deviceExtensionNames[] = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + /* VK_KHR_sampler_ycbcr_conversion + dependent extensions. + Note VULKAN_DeviceExtensionsFound() call below, if these get moved in this + array, update that check too. + */ + VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, + VK_KHR_MAINTENANCE1_EXTENSION_NAME, + VK_KHR_BIND_MEMORY_2_EXTENSION_NAME, + VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, + }; VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; SDL_VideoDevice *device = SDL_GetVideoDevice(); VkResult result = VK_SUCCESS; @@ -1725,7 +1736,10 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert rendererData->presentQueueFamilyIndex = (uint32_t)SDL_GetNumberProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER, 0); } - + if (rendererData->supportsKHRGetPhysicalDeviceProperties2 && + VULKAN_DeviceExtensionsFound(rendererData, 4, &deviceExtensionNames[1])) { + rendererData->supportsKHRSamplerYCbCrConversion = SDL_TRUE; + } /* Create Vulkan device */ rendererData->device = (VkDevice)SDL_GetProperty(create_props, SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER, NULL); @@ -1735,26 +1749,12 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = { { 0 }, { 0 } }; static const float queuePriority[] = { 1.0f }; VkDeviceCreateInfo deviceCreateInfo = { 0 }; - static const char *const deviceExtensionNames[] = { - VK_KHR_SWAPCHAIN_EXTENSION_NAME, - /* VK_KHR_sampler_ycbcr_conversion + dependent extensions. - Note VULKAN_DeviceExtensionsFound() call below, if these get moved in this - array, update that check too. - */ - VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, - VK_KHR_MAINTENANCE1_EXTENSION_NAME, - VK_KHR_BIND_MEMORY_2_EXTENSION_NAME, - VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, - }; - if (rendererData->supportsKHRGetPhysicalDeviceProperties2 && - VULKAN_DeviceExtensionsFound(rendererData, 4, &deviceExtensionNames[1])) { - rendererData->supportsKHRSamplerYcBcrConversion = SDL_TRUE; - } + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; deviceCreateInfo.queueCreateInfoCount = 0; deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo; deviceCreateInfo.pEnabledFeatures = NULL; - deviceCreateInfo.enabledExtensionCount = (rendererData->supportsKHRSamplerYcBcrConversion) ? 5 : 1; + deviceCreateInfo.enabledExtensionCount = (rendererData->supportsKHRSamplerYCbCrConversion) ? 5 : 1; deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames; deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; @@ -2440,7 +2440,7 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD texture->format == SDL_PIXELFORMAT_P010) { /* Check that we have VK_KHR_sampler_ycbcr_conversion support */ - if (!rendererData->supportsKHRSamplerYcBcrConversion) { + if (!rendererData->supportsKHRSamplerYCbCrConversion) { SDL_free(textureData); return SDL_SetError("[Vulkan] YUV textures require a Vulkan device that supports VK_KHR_sampler_ycbcr_conversion"); } @@ -3980,6 +3980,14 @@ SDL_Renderer *VULKAN_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_ return NULL; } + if (rendererData->supportsKHRSamplerYCbCrConversion) { + renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12; + renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV; + renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_NV12; + renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_NV21; + renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_P010; + } + return renderer; } @@ -3989,17 +3997,12 @@ SDL_RenderDriver VULKAN_RenderDriver = { "vulkan", (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC), /* flags. see SDL_RendererFlags */ - 9, /* num_texture_formats */ + 4, /* num_texture_formats */ { /* texture_formats */ SDL_PIXELFORMAT_ARGB8888, SDL_PIXELFORMAT_XRGB8888, SDL_PIXELFORMAT_XBGR2101010, - SDL_PIXELFORMAT_RGBA64_FLOAT, - SDL_PIXELFORMAT_YV12, - SDL_PIXELFORMAT_IYUV, - SDL_PIXELFORMAT_NV12, - SDL_PIXELFORMAT_NV21, - SDL_PIXELFORMAT_P010 }, + SDL_PIXELFORMAT_RGBA64_FLOAT }, 16384, /* max_texture_width */ 16384 /* max_texture_height */ } From 4017e1370d265f798afc203fa2ee748e610d5370 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 28 Feb 2024 19:11:33 -0800 Subject: [PATCH 149/220] Vulkan: cleaned up error handling --- src/render/vulkan/SDL_render_vulkan.c | 31 +++++++++++---------------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 895c5d11689d3..927f327c6f104 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -1243,7 +1243,7 @@ static SDL_bool VULKAN_FindMemoryTypeIndex(VULKAN_RenderData *rendererData, uint } if (memoryTypeIndex >= rendererData->physicalDeviceMemoryProperties.memoryTypeCount) { - SDL_SetError("[Vulkan] Unable to find memory type for allocation."); + SDL_SetError("[Vulkan] Unable to find memory type for allocation"); return SDL_FALSE; } *memoryTypeIndexOut = memoryTypeIndex; @@ -1654,7 +1654,7 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert renderer->output_colorspace == SDL_COLORSPACE_HDR10) { rendererData->supportsEXTSwapchainColorspace = VULKAN_InstanceExtensionFound(rendererData, VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME); if (!rendererData->supportsEXTSwapchainColorspace) { - return SDL_SetError("[Vulkan] Using HDR output but %s not supported.", VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME); + return SDL_SetError("[Vulkan] Using HDR output but %s not supported", VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME); } } @@ -2441,9 +2441,9 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD /* Check that we have VK_KHR_sampler_ycbcr_conversion support */ if (!rendererData->supportsKHRSamplerYCbCrConversion) { - SDL_free(textureData); return SDL_SetError("[Vulkan] YUV textures require a Vulkan device that supports VK_KHR_sampler_ycbcr_conversion"); } + VkSamplerYcbcrConversionCreateInfoKHR samplerYcbcrConversionCreateInfo = { 0 }; samplerYcbcrConversionCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR; @@ -2464,9 +2464,9 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD case SDL_MATRIX_COEFFICIENTS_BT2020_NCL: case SDL_MATRIX_COEFFICIENTS_BT2020_CL: samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR; + break; default: - VULKAN_DestroyTexture(renderer, texture); - return SDL_SetError("[Vulkan] Unsupported Ycbcr colorspace.\n"); + return SDL_SetError("[Vulkan] Unsupported Ycbcr colorspace: %d", SDL_COLORSPACEMATRIX(texture->colorspace)); } samplerYcbcrConversionCreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; samplerYcbcrConversionCreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; @@ -2509,8 +2509,7 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD result = vkCreateSamplerYcbcrConversionKHR(rendererData->device, &samplerYcbcrConversionCreateInfo, NULL, &textureData->samplerYcbcrConversion); if (result != VK_SUCCESS) { - VULKAN_DestroyTexture(renderer, texture); - return SDL_SetError("[Vulkan] vkCreateSamplerYcbcrConversionKHR %s.\n", SDL_Vulkan_GetResultString(result)); + return SDL_SetError("[Vulkan] vkCreateSamplerYcbcrConversionKHR %s", SDL_Vulkan_GetResultString(result)); } /* Also create VkSampler object which we will need to pass to the PSO as an immutable sampler */ @@ -2534,16 +2533,14 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD samplerCreateInfo.pNext = &samplerYcbcrConversionInfo; result = vkCreateSampler(rendererData->device, &samplerCreateInfo, NULL, &textureData->samplerYcbcr); if (result != VK_SUCCESS) { - VULKAN_DestroyTexture(renderer, texture); - return SDL_SetError("[Vulkan] vkCreateSampler %s.\n", SDL_Vulkan_GetResultString(result)); + return SDL_SetError("[Vulkan] vkCreateSampler %s", SDL_Vulkan_GetResultString(result)); } /* Allocate special descriptor set layout with samplerYcbcr baked as an immutable sampler */ result = VULKAN_CreateDescriptorSetAndPipelineLayout(rendererData, textureData->samplerYcbcr, &textureData->descriptorSetLayoutYcbcr, &textureData->pipelineLayoutYcbcr); if (result != VK_SUCCESS) { - VULKAN_DestroyTexture(renderer, texture); - return SDL_SetError("[Vulkan] VULKAN_CreateDescriptorSetAndPipelineLayout %s.\n", SDL_Vulkan_GetResultString(result)); + return SDL_SetError("[Vulkan] VULKAN_CreateDescriptorSetAndPipelineLayout %s", SDL_Vulkan_GetResultString(result)); } } #endif @@ -2561,7 +2558,6 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD result = VULKAN_AllocateImage(rendererData, width, height, textureFormat, usage, imageViewSwizzle, externalImage, textureData->samplerYcbcrConversion, &textureData->mainImage); if (result != VK_SUCCESS) { - VULKAN_DestroyTexture(renderer, texture); SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_AllocateImage(): %s\n", SDL_Vulkan_GetResultString(result)); return result; } @@ -2578,7 +2574,6 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD &textureData->mainFramebuffer, textureData->mainRenderpasses); if (result != VK_SUCCESS) { - VULKAN_DestroyTexture(renderer, texture); SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_CreateFramebuffersAndRenderPasses(): %s\n", SDL_Vulkan_GetResultString(result)); return result; } @@ -3223,7 +3218,7 @@ static VkDescriptorPool VULKAN_AllocateDescriptorPool(VULKAN_RenderData *rendere descriptorPoolCreateInfo.maxSets = SDL_VULKAN_MAX_DESCRIPTOR_SETS; result = vkCreateDescriptorPool(rendererData->device, &descriptorPoolCreateInfo, NULL, &descriptorPool); if (result != VK_SUCCESS) { - SDL_SetError("[Vulkan] Unable to allocate descriptor pool vkCreateDescrptorPool: %s.\n", SDL_Vulkan_GetResultString(result)); + SDL_SetError("[Vulkan] Unable to allocate descriptor pool vkCreateDescrptorPool: %s", SDL_Vulkan_GetResultString(result)); return VK_NULL_HANDLE; } @@ -3309,7 +3304,7 @@ static VkDescriptorSet VULKAN_AllocateDescriptorSet(SDL_Renderer *renderer, VULK result = vkAllocateDescriptorSets(rendererData->device, &descriptorSetAllocateInfo, &descriptorSet); if (result != VK_SUCCESS) { /* This should not fail - we are allocating from the front of the descriptor set */ - SDL_SetError("[Vulkan] Unable to allocate descriptor set."); + SDL_SetError("[Vulkan] Unable to allocate descriptor set"); return VK_NULL_HANDLE; } rendererData->currentDescriptorPoolIndex = currentDescriptorPoolIndex; @@ -3488,7 +3483,7 @@ static SDL_bool VULKAN_SetDrawState(SDL_Renderer *renderer, const SDL_RenderComm &newConstantBuffer); if (result != VK_SUCCESS) { - SDL_SetError("[Vulkan] Could not allocate new memory for constant buffer.\n" ); + SDL_SetError("[Vulkan] Could not allocate new memory for constant buffer" ); return SDL_FALSE; } @@ -3546,7 +3541,7 @@ static SDL_bool VULKAN_SetCopyState(SDL_Renderer *renderer, const SDL_RenderComm textureSampler = rendererData->samplers[SDL_VULKAN_SAMPLER_LINEAR]; break; default: - return SDL_SetError("Unknown scale mode: %d\n", textureData->scaleMode); + return SDL_SetError("Unknown scale mode: %d", textureData->scaleMode); } if (textureData->mainImage.imageLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { @@ -3755,7 +3750,7 @@ static SDL_Surface* VULKAN_RenderReadPixels(SDL_Renderer *renderer, const SDL_Re VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &readbackBuffer) != VK_SUCCESS) { - SDL_SetError("[Vulkan] Failed to allocate buffer for readback."); + SDL_SetError("[Vulkan] Failed to allocate buffer for readback"); return NULL; } From 0c6a1b636e42b7193027383d75f509a9e5d00214 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 28 Feb 2024 19:16:55 -0800 Subject: [PATCH 150/220] Vulkan: added handling for SDL_MATRIX_COEFFICIENTS_UNSPECIFIED --- src/render/vulkan/SDL_render_vulkan.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 927f327c6f104..d92dce4426c61 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -2438,6 +2438,7 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD texture->format == SDL_PIXELFORMAT_NV12 || texture->format == SDL_PIXELFORMAT_NV21 || texture->format == SDL_PIXELFORMAT_P010) { + const uint32_t YUV_SD_THRESHOLD = 576; /* Check that we have VK_KHR_sampler_ycbcr_conversion support */ if (!rendererData->supportsKHRSamplerYCbCrConversion) { @@ -2465,6 +2466,15 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD case SDL_MATRIX_COEFFICIENTS_BT2020_CL: samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR; break; + case SDL_MATRIX_COEFFICIENTS_UNSPECIFIED: + if (texture->format == SDL_PIXELFORMAT_P010) { + samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR; + } else if (height > YUV_SD_THRESHOLD) { + samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR; + } else { + samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR; + } + break; default: return SDL_SetError("[Vulkan] Unsupported Ycbcr colorspace: %d", SDL_COLORSPACEMATRIX(texture->colorspace)); } From 011502711603074fef225134ec1b44b36e3b5c9e Mon Sep 17 00:00:00 2001 From: danginsburg Date: Thu, 29 Feb 2024 13:57:56 -0500 Subject: [PATCH 151/220] Vulkan Renderer - fix validation errors: * Make sure to always write pointSize in VS (fixes validation error in testsprite) * Fix validation error from acquiring swapchain semaphore more than once * Fix validation error from using incorrect framebuffer size in testautomation Now passes testautomation with validation. --- src/render/vulkan/SDL_render_vulkan.c | 8 +- .../vulkan/VULKAN_PixelShader_Advanced.h | 2 +- src/render/vulkan/VULKAN_PixelShader_Colors.h | 2 +- .../vulkan/VULKAN_PixelShader_Textures.h | 2 +- src/render/vulkan/VULKAN_VertexShader.h | 91 ++++++++++--------- src/render/vulkan/VULKAN_VertexShader.hlsl | 4 + 6 files changed, 58 insertions(+), 51 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index d92dce4426c61..6dd17575eeb56 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -1956,8 +1956,8 @@ static VkResult VULKAN_CreateFramebuffersAndRenderPasses(SDL_Renderer *renderer, framebufferCreateInfo.pNext = NULL; framebufferCreateInfo.renderPass = rendererData->renderPasses[SDL_VULKAN_RENDERPASS_LOAD]; framebufferCreateInfo.attachmentCount = 1; - framebufferCreateInfo.width = rendererData->swapchainSize.width; - framebufferCreateInfo.height = rendererData->swapchainSize.height; + framebufferCreateInfo.width = w; + framebufferCreateInfo.height = h; framebufferCreateInfo.layers = 1; for (int i = 0; i < imageViewCount; i++) { @@ -3882,9 +3882,9 @@ static int VULKAN_RenderPresent(SDL_Renderer *renderer) SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkWaitForFences(): %s\n", SDL_Vulkan_GetResultString(result)); return -1; } - } - VULKAN_AcquireNextSwapchainImage(renderer); + VULKAN_AcquireNextSwapchainImage(renderer); + } return (result == VK_SUCCESS); } diff --git a/src/render/vulkan/VULKAN_PixelShader_Advanced.h b/src/render/vulkan/VULKAN_PixelShader_Advanced.h index e9cc017589f39..f205918c5ce31 100644 --- a/src/render/vulkan/VULKAN_PixelShader_Advanced.h +++ b/src/render/vulkan/VULKAN_PixelShader_Advanced.h @@ -1,4 +1,4 @@ - // 1114.0.0 + // 1113.1.1 #pragma once const uint32_t VULKAN_PixelShader_Advanced[] = { 0x07230203,0x00010000,0x0008000b,0x0000043e,0x00000000,0x00020011,0x00000001,0x0006000b, diff --git a/src/render/vulkan/VULKAN_PixelShader_Colors.h b/src/render/vulkan/VULKAN_PixelShader_Colors.h index fb21fd0719828..e28a83a1a16ce 100644 --- a/src/render/vulkan/VULKAN_PixelShader_Colors.h +++ b/src/render/vulkan/VULKAN_PixelShader_Colors.h @@ -1,4 +1,4 @@ - // 1114.0.0 + // 1113.1.1 #pragma once const uint32_t VULKAN_PixelShader_Colors[] = { 0x07230203,0x00010000,0x0008000b,0x000000a1,0x00000000,0x00020011,0x00000001,0x0006000b, diff --git a/src/render/vulkan/VULKAN_PixelShader_Textures.h b/src/render/vulkan/VULKAN_PixelShader_Textures.h index c423e4ef67535..bc750d43372df 100644 --- a/src/render/vulkan/VULKAN_PixelShader_Textures.h +++ b/src/render/vulkan/VULKAN_PixelShader_Textures.h @@ -1,4 +1,4 @@ - // 1114.0.0 + // 1113.1.1 #pragma once const uint32_t VULKAN_PixelShader_Textures[] = { 0x07230203,0x00010000,0x0008000b,0x000000a8,0x00000000,0x00020011,0x00000001,0x0006000b, diff --git a/src/render/vulkan/VULKAN_VertexShader.h b/src/render/vulkan/VULKAN_VertexShader.h index 3f5b76741a294..cb95262468418 100644 --- a/src/render/vulkan/VULKAN_VertexShader.h +++ b/src/render/vulkan/VULKAN_VertexShader.h @@ -1,48 +1,51 @@ - // 1114.0.0 + // 1113.1.1 #pragma once const uint32_t VULKAN_VertexShader[] = { - 0x07230203,0x00010000,0x0008000b,0x000000a1,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x07230203,0x00010000,0x0008000b,0x000000af,0x00000000,0x00020011,0x00000001,0x0006000b, 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, - 0x000b000f,0x00000000,0x00000004,0x6e69616d,0x00000000,0x0000003c,0x00000040,0x00000044, - 0x00000056,0x00000059,0x0000005c,0x00030003,0x00000005,0x000001f4,0x00040005,0x00000004, - 0x6e69616d,0x00000000,0x00060005,0x0000001e,0x68737570,0x736e6f43,0x746e6174,0x00000073, - 0x00050006,0x0000001e,0x00000000,0x65646f6d,0x0000006c,0x00080006,0x0000001e,0x00000001, - 0x6a6f7270,0x69746365,0x6e416e6f,0x65695664,0x00000077,0x00060005,0x00000020,0x68737570, - 0x736e6f43,0x746e6174,0x00000073,0x00050005,0x0000003c,0x75706e69,0x6f702e74,0x00000073, - 0x00050005,0x00000040,0x75706e69,0x65742e74,0x00000078,0x00050005,0x00000044,0x75706e69, - 0x6f632e74,0x00726f6c,0x00080005,0x00000056,0x746e6540,0x6f507972,0x4f746e69,0x75707475, - 0x6f702e74,0x00000073,0x00080005,0x00000059,0x746e6540,0x6f507972,0x4f746e69,0x75707475, - 0x65742e74,0x00000078,0x00080005,0x0000005c,0x746e6540,0x6f507972,0x4f746e69,0x75707475, - 0x6f632e74,0x00726f6c,0x00040048,0x0000001e,0x00000000,0x00000005,0x00050048,0x0000001e, - 0x00000000,0x00000023,0x00000000,0x00050048,0x0000001e,0x00000000,0x00000007,0x00000010, - 0x00040048,0x0000001e,0x00000001,0x00000005,0x00050048,0x0000001e,0x00000001,0x00000023, - 0x00000040,0x00050048,0x0000001e,0x00000001,0x00000007,0x00000010,0x00030047,0x0000001e, - 0x00000002,0x00040047,0x0000003c,0x0000001e,0x00000000,0x00040047,0x00000040,0x0000001e, - 0x00000001,0x00040047,0x00000044,0x0000001e,0x00000002,0x00040047,0x00000056,0x0000000b, - 0x00000000,0x00040047,0x00000059,0x0000001e,0x00000000,0x00040047,0x0000005c,0x0000001e, - 0x00000001,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006, - 0x00000020,0x00040017,0x00000007,0x00000006,0x00000003,0x00040017,0x00000008,0x00000006, - 0x00000002,0x00040017,0x00000009,0x00000006,0x00000004,0x00040015,0x00000013,0x00000020, - 0x00000001,0x0004002b,0x00000013,0x00000014,0x00000000,0x0004002b,0x00000006,0x00000018, - 0x3f800000,0x00040018,0x0000001d,0x00000009,0x00000004,0x0004001e,0x0000001e,0x0000001d, - 0x0000001d,0x00040020,0x0000001f,0x00000009,0x0000001e,0x0004003b,0x0000001f,0x00000020, - 0x00000009,0x00040020,0x00000021,0x00000009,0x0000001d,0x0004002b,0x00000013,0x00000026, - 0x00000001,0x00040020,0x0000003b,0x00000001,0x00000007,0x0004003b,0x0000003b,0x0000003c, - 0x00000001,0x00040020,0x0000003f,0x00000001,0x00000008,0x0004003b,0x0000003f,0x00000040, - 0x00000001,0x00040020,0x00000043,0x00000001,0x00000009,0x0004003b,0x00000043,0x00000044, - 0x00000001,0x00040020,0x00000055,0x00000003,0x00000009,0x0004003b,0x00000055,0x00000056, - 0x00000003,0x00040020,0x00000058,0x00000003,0x00000008,0x0004003b,0x00000058,0x00000059, - 0x00000003,0x0004003b,0x00000055,0x0000005c,0x00000003,0x00050036,0x00000002,0x00000004, - 0x00000000,0x00000003,0x000200f8,0x00000005,0x0004003d,0x00000007,0x0000003d,0x0000003c, - 0x0004003d,0x00000008,0x00000041,0x00000040,0x0004003d,0x00000009,0x00000045,0x00000044, - 0x00050051,0x00000006,0x00000065,0x0000003d,0x00000000,0x00050051,0x00000006,0x00000066, - 0x0000003d,0x00000001,0x00050051,0x00000006,0x00000067,0x0000003d,0x00000002,0x00070050, - 0x00000009,0x00000068,0x00000065,0x00000066,0x00000067,0x00000018,0x00050041,0x00000021, - 0x00000069,0x00000020,0x00000014,0x0004003d,0x0000001d,0x0000006a,0x00000069,0x00050091, - 0x00000009,0x0000006c,0x0000006a,0x00000068,0x00050041,0x00000021,0x0000006d,0x00000020, - 0x00000026,0x0004003d,0x0000001d,0x0000006e,0x0000006d,0x00050091,0x00000009,0x00000070, - 0x0000006e,0x0000006c,0x00050051,0x00000006,0x00000052,0x00000070,0x00000001,0x0004007f, - 0x00000006,0x00000053,0x00000052,0x00060052,0x00000009,0x000000a0,0x00000053,0x00000070, - 0x00000001,0x0003003e,0x00000056,0x000000a0,0x0003003e,0x00000059,0x00000041,0x0003003e, - 0x0000005c,0x00000045,0x000100fd,0x00010038 + 0x000c000f,0x00000000,0x00000004,0x6e69616d,0x00000000,0x0000003f,0x00000043,0x00000047, + 0x00000058,0x0000005b,0x0000005e,0x00000062,0x00030003,0x00000005,0x000001f4,0x00040005, + 0x00000004,0x6e69616d,0x00000000,0x00060005,0x0000001e,0x68737570,0x736e6f43,0x746e6174, + 0x00000073,0x00050006,0x0000001e,0x00000000,0x65646f6d,0x0000006c,0x00080006,0x0000001e, + 0x00000001,0x6a6f7270,0x69746365,0x6e416e6f,0x65695664,0x00000077,0x00060005,0x00000020, + 0x68737570,0x736e6f43,0x746e6174,0x00000073,0x00050005,0x0000003f,0x75706e69,0x6f702e74, + 0x00000073,0x00050005,0x00000043,0x75706e69,0x65742e74,0x00000078,0x00050005,0x00000047, + 0x75706e69,0x6f632e74,0x00726f6c,0x00080005,0x00000058,0x746e6540,0x6f507972,0x4f746e69, + 0x75707475,0x6f702e74,0x00000073,0x00080005,0x0000005b,0x746e6540,0x6f507972,0x4f746e69, + 0x75707475,0x65742e74,0x00000078,0x00080005,0x0000005e,0x746e6540,0x6f507972,0x4f746e69, + 0x75707475,0x6f632e74,0x00726f6c,0x00090005,0x00000062,0x746e6540,0x6f507972,0x4f746e69, + 0x75707475,0x6f702e74,0x53746e69,0x00657a69,0x00040048,0x0000001e,0x00000000,0x00000005, + 0x00050048,0x0000001e,0x00000000,0x00000023,0x00000000,0x00050048,0x0000001e,0x00000000, + 0x00000007,0x00000010,0x00040048,0x0000001e,0x00000001,0x00000005,0x00050048,0x0000001e, + 0x00000001,0x00000023,0x00000040,0x00050048,0x0000001e,0x00000001,0x00000007,0x00000010, + 0x00030047,0x0000001e,0x00000002,0x00040047,0x0000003f,0x0000001e,0x00000000,0x00040047, + 0x00000043,0x0000001e,0x00000001,0x00040047,0x00000047,0x0000001e,0x00000002,0x00040047, + 0x00000058,0x0000000b,0x00000000,0x00040047,0x0000005b,0x0000001e,0x00000000,0x00040047, + 0x0000005e,0x0000001e,0x00000001,0x00040047,0x00000062,0x0000000b,0x00000001,0x00020013, + 0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006,0x00000020,0x00040017, + 0x00000007,0x00000006,0x00000003,0x00040017,0x00000008,0x00000006,0x00000002,0x00040017, + 0x00000009,0x00000006,0x00000004,0x00040015,0x00000013,0x00000020,0x00000001,0x0004002b, + 0x00000013,0x00000014,0x00000000,0x0004002b,0x00000006,0x00000018,0x3f800000,0x00040018, + 0x0000001d,0x00000009,0x00000004,0x0004001e,0x0000001e,0x0000001d,0x0000001d,0x00040020, + 0x0000001f,0x00000009,0x0000001e,0x0004003b,0x0000001f,0x00000020,0x00000009,0x00040020, + 0x00000021,0x00000009,0x0000001d,0x0004002b,0x00000013,0x00000026,0x00000001,0x00040020, + 0x0000003e,0x00000001,0x00000007,0x0004003b,0x0000003e,0x0000003f,0x00000001,0x00040020, + 0x00000042,0x00000001,0x00000008,0x0004003b,0x00000042,0x00000043,0x00000001,0x00040020, + 0x00000046,0x00000001,0x00000009,0x0004003b,0x00000046,0x00000047,0x00000001,0x00040020, + 0x00000057,0x00000003,0x00000009,0x0004003b,0x00000057,0x00000058,0x00000003,0x00040020, + 0x0000005a,0x00000003,0x00000008,0x0004003b,0x0000005a,0x0000005b,0x00000003,0x0004003b, + 0x00000057,0x0000005e,0x00000003,0x00040020,0x00000061,0x00000003,0x00000006,0x0004003b, + 0x00000061,0x00000062,0x00000003,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003, + 0x000200f8,0x00000005,0x0004003d,0x00000007,0x00000040,0x0000003f,0x0004003d,0x00000008, + 0x00000044,0x00000043,0x0004003d,0x00000009,0x00000048,0x00000047,0x00050051,0x00000006, + 0x0000006b,0x00000040,0x00000000,0x00050051,0x00000006,0x0000006c,0x00000040,0x00000001, + 0x00050051,0x00000006,0x0000006d,0x00000040,0x00000002,0x00070050,0x00000009,0x0000006e, + 0x0000006b,0x0000006c,0x0000006d,0x00000018,0x00050041,0x00000021,0x0000006f,0x00000020, + 0x00000014,0x0004003d,0x0000001d,0x00000070,0x0000006f,0x00050091,0x00000009,0x00000072, + 0x00000070,0x0000006e,0x00050041,0x00000021,0x00000073,0x00000020,0x00000026,0x0004003d, + 0x0000001d,0x00000074,0x00000073,0x00050091,0x00000009,0x00000076,0x00000074,0x00000072, + 0x00050051,0x00000006,0x00000054,0x00000076,0x00000001,0x0004007f,0x00000006,0x00000055, + 0x00000054,0x00060052,0x00000009,0x000000ae,0x00000055,0x00000076,0x00000001,0x0003003e, + 0x00000058,0x000000ae,0x0003003e,0x0000005b,0x00000044,0x0003003e,0x0000005e,0x00000048, + 0x0003003e,0x00000062,0x00000018,0x000100fd,0x00010038 }; diff --git a/src/render/vulkan/VULKAN_VertexShader.hlsl b/src/render/vulkan/VULKAN_VertexShader.hlsl index b7d5e46306043..d376876295a5e 100644 --- a/src/render/vulkan/VULKAN_VertexShader.hlsl +++ b/src/render/vulkan/VULKAN_VertexShader.hlsl @@ -20,6 +20,7 @@ struct VertexShaderOutput float4 pos : SV_POSITION; float2 tex : TEXCOORD0; float4 color : COLOR0; + [[vk::builtin("PointSize")]] float pointSize : SV_PointSize; }; VertexShaderOutput mainColor(VertexShaderInput input) @@ -36,6 +37,9 @@ VertexShaderOutput mainColor(VertexShaderInput input) output.tex = input.tex; output.color = input.color; + // Always output pointSize so that this VS can be used with points + output.pointSize = 1.0; + return output; } From 2adbcce864cc3ee1d697f0edae2f4d6aafcbb422 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Thu, 29 Feb 2024 14:12:09 -0800 Subject: [PATCH 152/220] Vulkan: wait for all queues to be idle before destroying the device --- src/render/vulkan/SDL_render_vulkan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 6dd17575eeb56..19387180a57db 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -1012,7 +1012,7 @@ static void VULKAN_DestroyRenderer(SDL_Renderer *renderer) { VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; if (rendererData->device != VK_NULL_HANDLE) { - VULKAN_WaitForGPU(rendererData); + vkDeviceWaitIdle(rendererData->device); VULKAN_DestroyAll(renderer); } if (rendererData) { From 0454e1fdb427dbb4e4f59f68d7c030e238d0c3f0 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Wed, 28 Feb 2024 21:18:17 -0800 Subject: [PATCH 153/220] Vulkan: added support for wrapping existing textures --- include/SDL3/SDL_render.h | 14 +++-- src/render/vulkan/SDL_render_vulkan.c | 77 +++++++++++---------------- 2 files changed, 42 insertions(+), 49 deletions(-) diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index d577c4dc4c834..8912484183754 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -616,6 +616,10 @@ extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTextureFromSurface(SDL_Renderer * * associated with the V plane of a YUV texture, if you want to wrap an * existing texture. * + * With the vulkan renderer: + * + * - `SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER`: the VkImage with layout VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL associated with the 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 @@ -655,6 +659,7 @@ extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTextureWithProperties(SDL_Rendere #define SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_UV_NUMBER "opengles2.texture_uv" #define SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_U_NUMBER "opengles2.texture_u" #define SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_V_NUMBER "opengles2.texture_v" +#define SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER "vulkan.texture" /** * Get the properties associated with a texture. @@ -734,6 +739,10 @@ extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTextureWithProperties(SDL_Rendere * - `SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_TARGET_NUMBER`: the GLenum for the * texture target (`GL_TEXTURE_2D`, `GL_TEXTURE_EXTERNAL_OES`, etc) * + * With the vulkan renderer: + * + * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_NUMBER`: the VkImage associated with the texture + * * \param texture the texture to query * \returns a valid property ID on success or 0 on failure; call * SDL_GetError() for more information. @@ -766,10 +775,7 @@ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetTextureProperties(SDL_Texture *t #define SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_U_NUMBER "SDL.texture.opengles2.texture_u" #define SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_V_NUMBER "SDL.texture.opengles2.texture_v" #define SDL_PROP_TEXTURE_OPENGLES2_TEXTURE_TARGET_NUMBER "SDL.texture.opengles2.target" -#define SDL_PROP_TEXTURE_VULKAN_TEXTURE_POINTER "SDL.texture.vulkan.texture" -#define SDL_PROP_TEXTURE_VULKAN_TEXTURE_U_POINTER "SDL.texture.vulkan.texture_u" -#define SDL_PROP_TEXTURE_VULKAN_TEXTURE_V_POINTER "SDL.texture.vulkan.texture_v" -#define SDL_PROP_TEXTURE_VULKAN_TEXTURE_UV_POINTER "SDL.texture.vulkan.texture_uv" +#define SDL_PROP_TEXTURE_VULKAN_TEXTURE_NUMBER "SDL.texture.vulkan.texture" /** * Get the renderer that created an SDL_Texture. diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 19387180a57db..4395d882dd4c3 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -689,44 +689,43 @@ static void VULKAN_DestroyImage(VULKAN_RenderData *rendererData, VULKAN_Image *v } if (vulkanImage->deviceMemory != VK_NULL_HANDLE) { - vkFreeMemory(rendererData->device, vulkanImage->deviceMemory, NULL); + if (vulkanImage->allocatedImage) { + vkFreeMemory(rendererData->device, vulkanImage->deviceMemory, NULL); + } vulkanImage->deviceMemory = VK_NULL_HANDLE; } SDL_memset(vulkanImage, 0, sizeof(VULKAN_Image)); } -static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, uint32_t width, uint32_t height, VkFormat format, - VkImageUsageFlags imageUsage, VkComponentMapping swizzle, VkImage externalImage, - VkSamplerYcbcrConversionKHR samplerYcbcrConversion, - VULKAN_Image *imageOut) +static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, SDL_PropertiesID create_props, uint32_t width, uint32_t height, VkFormat format, VkImageUsageFlags imageUsage, VkComponentMapping swizzle, VkSamplerYcbcrConversionKHR samplerYcbcrConversion, VULKAN_Image *imageOut) { VkResult result; - VkImageCreateInfo imageCreateInfo = { 0 }; VkSamplerYcbcrConversionInfoKHR samplerYcbcrConversionInfo = { 0 }; SDL_memset(imageOut, 0, sizeof(VULKAN_Image)); imageOut->format = format; - imageOut->imageLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - imageCreateInfo.flags = 0; - imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; - imageCreateInfo.format = format; - imageCreateInfo.extent.width = width; - imageCreateInfo.extent.height = height; - imageCreateInfo.extent.depth = 1; - imageCreateInfo.mipLevels = 1; - imageCreateInfo.arrayLayers = 1; - imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCreateInfo.usage = imageUsage; - imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCreateInfo.queueFamilyIndexCount = 0; - imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - imageOut->allocatedImage = VK_FALSE; - if (externalImage == VK_NULL_HANDLE) { + imageOut->image = (VkImage)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER, 0); + + if (imageOut->image == VK_NULL_HANDLE) { imageOut->allocatedImage = VK_TRUE; + + VkImageCreateInfo imageCreateInfo = { 0 }; + imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageCreateInfo.flags = 0; + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = format; + imageCreateInfo.extent.width = width; + imageCreateInfo.extent.height = height; + imageCreateInfo.extent.depth = 1; + imageCreateInfo.mipLevels = 1; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.usage = imageUsage; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.queueFamilyIndexCount = 0; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + result = vkCreateImage(rendererData->device, &imageCreateInfo, NULL, &imageOut->image); if (result != VK_SUCCESS) { VULKAN_DestroyImage(rendererData, imageOut); @@ -765,13 +764,15 @@ static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, uint32_t w SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkBindImageMemory(): %s\n", SDL_Vulkan_GetResultString(result)); return result; } + } else { + imageOut->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } VkImageViewCreateInfo imageViewCreateInfo = { 0 }; imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; imageViewCreateInfo.image = imageOut->image; imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; - imageViewCreateInfo.format = imageCreateInfo.format; + imageViewCreateInfo.format = format; imageViewCreateInfo.components = swizzle; imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageViewCreateInfo.subresourceRange.baseMipLevel = 0; @@ -779,7 +780,7 @@ static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, uint32_t w imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; imageViewCreateInfo.subresourceRange.layerCount = 1; - /* If it's a YcBcBr image, we need to pass the conversion info to the VkImageView (and the VkSampler) */ + /* If it's a YCbCr image, we need to pass the conversion info to the VkImageView (and the VkSampler) */ if (samplerYcbcrConversion != VK_NULL_HANDLE) { samplerYcbcrConversionInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR; samplerYcbcrConversionInfo.conversion = samplerYcbcrConversion; @@ -2395,22 +2396,11 @@ static SDL_bool VULKAN_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode b return SDL_TRUE; } -static int GetTextureProperty(SDL_PropertiesID props, const char *name, VkImage *image) -{ - VkImage *propImage = (VkImage*)SDL_GetProperty(props, name, NULL); - if (propImage) { - *image = *propImage; - } - return 0; -} - - static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) { VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; VULKAN_TextureData *textureData; VkResult result; - VkImage externalImage = VK_NULL_HANDLE; VkFormat textureFormat = SDLPixelFormatToVkTextureFormat(texture->format, renderer->output_colorspace); uint32_t width = texture->w; uint32_t height = texture->h; @@ -2562,17 +2552,14 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; } - if (GetTextureProperty(create_props, "vulkan.texture", &externalImage) < 0) { - return -1; - } - - result = VULKAN_AllocateImage(rendererData, width, height, textureFormat, usage, imageViewSwizzle, externalImage, textureData->samplerYcbcrConversion, &textureData->mainImage); + result = VULKAN_AllocateImage(rendererData, create_props, width, height, textureFormat, usage, imageViewSwizzle, textureData->samplerYcbcrConversion, &textureData->mainImage); if (result != VK_SUCCESS) { SDL_LogError(SDL_LOG_CATEGORY_RENDER, "VULKAN_AllocateImage(): %s\n", SDL_Vulkan_GetResultString(result)); return result; } - SDL_SetProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_VULKAN_TEXTURE_POINTER, &textureData->mainImage.image); + SDL_PropertiesID props = SDL_GetTextureProperties(texture); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER, (Sint64)textureData->mainImage.image); if (texture->access == SDL_TEXTUREACCESS_TARGET) { result = VULKAN_CreateFramebuffersAndRenderPasses(renderer, From 7ff9be739827a53763b393897a371674d45b053d Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Fri, 1 Mar 2024 01:39:28 +0000 Subject: [PATCH 154/220] Sync SDL3 wiki -> header --- include/SDL3/SDL_render.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 8912484183754..1e290e1ca1ce1 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -618,7 +618,9 @@ extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTextureFromSurface(SDL_Renderer * * * With the vulkan renderer: * - * - `SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER`: the VkImage with layout VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL associated with the texture, if you want to wrap an existing texture. + * - `SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER`: the VkImage with layout + * VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL associated with the texture, if + * you want to wrap an existing texture. * * \param renderer the rendering context * \param props the properties to use @@ -741,7 +743,8 @@ extern DECLSPEC SDL_Texture *SDLCALL SDL_CreateTextureWithProperties(SDL_Rendere * * With the vulkan renderer: * - * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_NUMBER`: the VkImage associated with the texture + * - `SDL_PROP_TEXTURE_VULKAN_TEXTURE_NUMBER`: the VkImage associated with the + * texture * * \param texture the texture to query * \returns a valid property ID on success or 0 on failure; call From af58ed978e9ccfbaed7fbd2d39c14ebb219dcbd6 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 1 Mar 2024 01:55:43 -0800 Subject: [PATCH 155/220] Fixed the documentation for SDL_GetGamepadMappings() --- include/SDL3/SDL_gamepad.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/SDL3/SDL_gamepad.h b/include/SDL3/SDL_gamepad.h index db38086b7f46b..4be8ee0ba3f61 100644 --- a/include/SDL3/SDL_gamepad.h +++ b/include/SDL3/SDL_gamepad.h @@ -319,7 +319,7 @@ extern DECLSPEC int SDLCALL SDL_AddGamepadMappingsFromFile(const char *file); extern DECLSPEC int SDLCALL SDL_ReloadGamepadMappings(void); /** - * Get the mapping at a particular index. + * Get the current gamepad mappings. * * You must free the returned pointer with SDL_free() when you are done with * it, but you do _not_ free each string in the array. From 1e8b006d43166a77d8311d89938e2082b9febe22 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 28 Feb 2024 20:51:48 -0500 Subject: [PATCH 156/220] stdlib: qsort and bsearch changes. - Always use internal qsort and bsearch implementation. - add "_r" reentrant versions. The reasons for always using the internal versions is that the C runtime versions' callbacks are not mark STDCALL, so we would have add bridge functions for them anyhow, The C runtime qsort_r/qsort_s have different orders of arguments on different platforms, and most importantly: qsort() isn't a stable sort, and isn't guaranteed to give the same ordering for two objects marked as equal by the callback...as such, Visual Studio and glibc can give different sort results for the same data set...in this sense, having one piece of code shared on all platforms makes sense here, for reliabillity. bsearch does not have a standard _r version at all, and suffers from the same SDLCALL concern. Since the code is simple and we would have to work around the C runtime, it's easier to just go with the built-in function and remove all the CMake C runtime tests. Fixes #9159. --- CMakeLists.txt | 3 +- build-scripts/check_stdlib_usage.py | 3 + include/SDL3/SDL_stdinc.h | 3 + include/build_config/SDL_build_config.h.cmake | 2 - .../build_config/SDL_build_config_android.h | 2 - .../SDL_build_config_emscripten.h | 2 - include/build_config/SDL_build_config_ios.h | 2 - include/build_config/SDL_build_config_macos.h | 2 - .../build_config/SDL_build_config_windows.h | 2 - .../build_config/SDL_build_config_wingdk.h | 2 - include/build_config/SDL_build_config_winrt.h | 2 - include/build_config/SDL_build_config_xbox.h | 2 - src/dynapi/SDL_dynapi.sym | 2 + src/dynapi/SDL_dynapi_overrides.h | 2 + src/dynapi/SDL_dynapi_procs.h | 2 + src/stdlib/SDL_qsort.c | 125 +++++++++--------- test/testqsort.c | 20 ++- 17 files changed, 96 insertions(+), 82 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f4e675b18c703..1cce17fc0330c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1058,7 +1058,7 @@ if(SDL_LIBC) set(symbols_to_check abs acos acosf asin asinf atan atan2 atan2f atanf atof atoi - bcopy bsearch + bcopy calloc ceil ceilf copysign copysignf cos cosf _Exit exp expf fabs fabsf floor floorf fmod fmodf fopen64 free fseeko fseeko64 @@ -1067,7 +1067,6 @@ if(SDL_LIBC) log log10 log10f logf lround lroundf _ltoa malloc memcmp memcpy memmove memset modf modff pow powf putenv - qsort realloc rindex round roundf scalbn scalbnf setenv sin sinf sqr sqrt sqrtf sscanf strchr strcmp strlcat strlcpy strlen strncmp strnlen diff --git a/build-scripts/check_stdlib_usage.py b/build-scripts/check_stdlib_usage.py index 5e62ff1876e16..8dea74e14b1f7 100755 --- a/build-scripts/check_stdlib_usage.py +++ b/build-scripts/check_stdlib_usage.py @@ -42,6 +42,7 @@ 'atanf', 'atof', 'atoi', + 'bsearch', 'calloc', 'ceil', 'ceilf', @@ -90,6 +91,8 @@ 'pow', 'powf', 'qsort', + 'qsort_r', + 'qsort_s', 'realloc', 'round', 'roundf', diff --git a/include/SDL3/SDL_stdinc.h b/include/SDL3/SDL_stdinc.h index e8da4026ea66a..281aa56d16094 100644 --- a/include/SDL3/SDL_stdinc.h +++ b/include/SDL3/SDL_stdinc.h @@ -498,6 +498,9 @@ extern DECLSPEC int SDLCALL SDL_setenv(const char *name, const char *value, int extern DECLSPEC void SDLCALL SDL_qsort(void *base, size_t nmemb, size_t size, int (SDLCALL *compare) (const void *, const void *)); extern DECLSPEC void * SDLCALL SDL_bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (SDLCALL *compare) (const void *, const void *)); +extern DECLSPEC void SDLCALL SDL_qsort_r(void *base, size_t nmemb, size_t size, int (SDLCALL *compare) (void *, const void *, const void *), void *userdata); +extern DECLSPEC void * SDLCALL SDL_bsearch_r(const void *key, const void *base, size_t nmemb, size_t size, int (SDLCALL *compare) (void *, const void *, const void *), void *userdata); + extern DECLSPEC int SDLCALL SDL_abs(int x); /* NOTE: these double-evaluate their arguments, so you should never have side effects in the parameters */ diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index be0ac3f460b7f..e6f5d91c31735 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -82,8 +82,6 @@ #cmakedefine HAVE_PUTENV 1 #cmakedefine HAVE_UNSETENV 1 #endif -#cmakedefine HAVE_QSORT 1 -#cmakedefine HAVE_BSEARCH 1 #cmakedefine HAVE_ABS 1 #cmakedefine HAVE_BCOPY 1 #cmakedefine HAVE_MEMSET 1 diff --git a/include/build_config/SDL_build_config_android.h b/include/build_config/SDL_build_config_android.h index f279a40c12698..64f8076e00b01 100644 --- a/include/build_config/SDL_build_config_android.h +++ b/include/build_config/SDL_build_config_android.h @@ -63,8 +63,6 @@ #define HAVE_PUTENV 1 #define HAVE_SETENV 1 #define HAVE_UNSETENV 1 -#define HAVE_QSORT 1 -#define HAVE_BSEARCH 1 #define HAVE_ABS 1 #define HAVE_BCOPY 1 #define HAVE_MEMSET 1 diff --git a/include/build_config/SDL_build_config_emscripten.h b/include/build_config/SDL_build_config_emscripten.h index 9e4ad6aa25ee5..89d5531f30ac6 100644 --- a/include/build_config/SDL_build_config_emscripten.h +++ b/include/build_config/SDL_build_config_emscripten.h @@ -65,8 +65,6 @@ #define HAVE_SETENV 1 #define HAVE_PUTENV 1 #define HAVE_UNSETENV 1 -#define HAVE_QSORT 1 -#define HAVE_BSEARCH 1 #define HAVE_ABS 1 #define HAVE_BCOPY 1 #define HAVE_MEMSET 1 diff --git a/include/build_config/SDL_build_config_ios.h b/include/build_config/SDL_build_config_ios.h index ed3b5480895cd..e130cc4673053 100644 --- a/include/build_config/SDL_build_config_ios.h +++ b/include/build_config/SDL_build_config_ios.h @@ -55,8 +55,6 @@ #define HAVE_PUTENV 1 #define HAVE_SETENV 1 #define HAVE_UNSETENV 1 -#define HAVE_QSORT 1 -#define HAVE_BSEARCH 1 #define HAVE_ABS 1 #define HAVE_BCOPY 1 #define HAVE_MEMSET 1 diff --git a/include/build_config/SDL_build_config_macos.h b/include/build_config/SDL_build_config_macos.h index 7766ddc0932e8..39ee50d4563a4 100644 --- a/include/build_config/SDL_build_config_macos.h +++ b/include/build_config/SDL_build_config_macos.h @@ -59,8 +59,6 @@ #define HAVE_SETENV 1 #define HAVE_PUTENV 1 #define HAVE_UNSETENV 1 -#define HAVE_QSORT 1 -#define HAVE_BSEARCH 1 #define HAVE_ABS 1 #define HAVE_BCOPY 1 #define HAVE_MEMSET 1 diff --git a/include/build_config/SDL_build_config_windows.h b/include/build_config/SDL_build_config_windows.h index 55f4f7c53d3ed..19873dbf17842 100644 --- a/include/build_config/SDL_build_config_windows.h +++ b/include/build_config/SDL_build_config_windows.h @@ -135,8 +135,6 @@ typedef unsigned int uintptr_t; #define HAVE_CALLOC 1 #define HAVE_REALLOC 1 #define HAVE_FREE 1 -#define HAVE_QSORT 1 -#define HAVE_BSEARCH 1 #define HAVE_ABS 1 #define HAVE_MEMSET 1 #define HAVE_MEMCPY 1 diff --git a/include/build_config/SDL_build_config_wingdk.h b/include/build_config/SDL_build_config_wingdk.h index a636694a0e775..992052a819125 100644 --- a/include/build_config/SDL_build_config_wingdk.h +++ b/include/build_config/SDL_build_config_wingdk.h @@ -76,8 +76,6 @@ #define HAVE_CALLOC 1 #define HAVE_REALLOC 1 #define HAVE_FREE 1 -#define HAVE_QSORT 1 -#define HAVE_BSEARCH 1 #define HAVE_ABS 1 #define HAVE_MEMSET 1 #define HAVE_MEMCPY 1 diff --git a/include/build_config/SDL_build_config_winrt.h b/include/build_config/SDL_build_config_winrt.h index 07c656ef34e6c..b5e5725fba10f 100644 --- a/include/build_config/SDL_build_config_winrt.h +++ b/include/build_config/SDL_build_config_winrt.h @@ -76,8 +76,6 @@ #define HAVE_CALLOC 1 #define HAVE_REALLOC 1 #define HAVE_FREE 1 -#define HAVE_QSORT 1 -#define HAVE_BSEARCH 1 #define HAVE_ABS 1 #define HAVE_MEMSET 1 #define HAVE_MEMCPY 1 diff --git a/include/build_config/SDL_build_config_xbox.h b/include/build_config/SDL_build_config_xbox.h index ea050da7d2831..8615036734984 100644 --- a/include/build_config/SDL_build_config_xbox.h +++ b/include/build_config/SDL_build_config_xbox.h @@ -76,8 +76,6 @@ #define HAVE_CALLOC 1 #define HAVE_REALLOC 1 #define HAVE_FREE 1 -#define HAVE_QSORT 1 -#define HAVE_BSEARCH 1 #define HAVE_ABS 1 #define HAVE_MEMSET 1 #define HAVE_MEMCPY 1 diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index e91067cfdc75a..e351d9f0890ea 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -971,6 +971,8 @@ SDL3_0.0.0 { SDL_CloseCamera; SDL_GetCameraPermissionState; SDL_GetCameraDevicePosition; + SDL_qsort_r; + SDL_bsearch_r; # 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 e786e75aa0c8f..ad8030b6c407c 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -996,3 +996,5 @@ #define SDL_CloseCamera SDL_CloseCamera_REAL #define SDL_GetCameraPermissionState SDL_GetCameraPermissionState_REAL #define SDL_GetCameraDevicePosition SDL_GetCameraDevicePosition_REAL +#define SDL_qsort_r SDL_qsort_r_REAL +#define SDL_bsearch_r SDL_bsearch_r_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 98e2ffc2a3f0d..3971dab5711b3 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1021,3 +1021,5 @@ SDL_DYNAPI_PROC(int,SDL_ReleaseCameraFrame,(SDL_Camera *a, SDL_Surface *b),(a,b) SDL_DYNAPI_PROC(void,SDL_CloseCamera,(SDL_Camera *a),(a),) SDL_DYNAPI_PROC(int,SDL_GetCameraPermissionState,(SDL_Camera *a),(a),return) SDL_DYNAPI_PROC(SDL_CameraPosition,SDL_GetCameraDevicePosition,(SDL_CameraDeviceID a),(a),return) +SDL_DYNAPI_PROC(void,SDL_qsort_r,(void *a, size_t b, size_t c, int (SDLCALL *d)(void *, const void *, const void *), void *e),(a,b,c,d,e),) +SDL_DYNAPI_PROC(void*,SDL_bsearch_r,(const void *a, const void *b, size_t c, size_t d, int (SDLCALL *e)(void *, const void *, const void *), void *f),(a,b,c,d,e,f),return) diff --git a/src/stdlib/SDL_qsort.c b/src/stdlib/SDL_qsort.c index 3683e99d29266..771712d203116 100644 --- a/src/stdlib/SDL_qsort.c +++ b/src/stdlib/SDL_qsort.c @@ -20,17 +20,10 @@ */ #include "SDL_internal.h" - -#ifdef HAVE_QSORT -void SDL_qsort(void *base, size_t nmemb, size_t size, int (*compare) (const void *, const void *)) -{ - if (!base) { - return; - } - qsort(base, nmemb, size, compare); -} - -#else +// SDL3 always uses its own internal qsort implementation, below, so +// it can guarantee stable sorts across platforms and not have to +// tapdance to support the various qsort_r interfaces, or bridge from +// the C runtime's non-SDLCALL compare functions. #ifdef assert #undef assert @@ -52,10 +45,6 @@ void SDL_qsort(void *base, size_t nmemb, size_t size, int (*compare) (const void #undef memmove #endif #define memmove SDL_memmove -#ifdef qsortG -#undef qsortG -#endif -#define qsortG SDL_qsort /* This code came from Gareth McCaughan, under the zlib license. @@ -67,6 +56,8 @@ Everything below this comment until the HAVE_QSORT #endif was from Gareth Thank you to Gareth for relicensing this code under the zlib license for our benefit! +Update for SDL3: we have modified this from a qsort function to qsort_r. + --ryan. */ @@ -152,7 +143,7 @@ benefit! #undef DEBUG_QSORT -static char _ID[]=""; +static char _ID[]=""; #endif /* END SDL CHANGE ... commented this out with an #if 0 block. --ryan. */ @@ -274,19 +265,19 @@ typedef struct { char * first; char * last; } stack_entry; /* and so is the pivoting logic (note: last is inclusive): */ #define Pivot(swapper,sz) \ - if ((size_t)(last-first)>PIVOT_THRESHOLD*sz) mid=pivot_big(first,mid,last,sz,compare);\ + if ((size_t)(last-first)>PIVOT_THRESHOLD*sz) mid=pivot_big(first,mid,last,sz,compare,userdata);\ else { \ - if (compare(first,mid)<0) { \ - if (compare(mid,last)>0) { \ + if (compare(userdata,first,mid)<0) { \ + if (compare(userdata,mid,last)>0) { \ swapper(mid,last); \ - if (compare(first,mid)>0) swapper(first,mid);\ + if (compare(userdata,first,mid)>0) swapper(first,mid);\ } \ } \ else { \ - if (compare(mid,last)>0) swapper(first,last)\ + if (compare(userdata,mid,last)>0) swapper(first,last)\ else { \ swapper(first,mid); \ - if (compare(mid,last)>0) swapper(mid,last);\ + if (compare(userdata,mid,last)>0) swapper(mid,last);\ } \ } \ first+=sz; last-=sz; \ @@ -299,8 +290,8 @@ typedef struct { char * first; char * last; } stack_entry; /* and so is the partitioning logic: */ #define Partition(swapper,sz) { \ do { \ - while (compare(first,pivot)<0) first+=sz; \ - while (compare(pivot,last)<0) last-=sz; \ + while (compare(userdata,first,pivot)<0) first+=sz; \ + while (compare(userdata,pivot,last)<0) last-=sz; \ if (firstlimit ? limit : nmemb)-1)*sz;\ while (last!=base) { \ - if (compare(first,last)>0) first=last; \ + if (compare(userdata,first,last)>0) first=last; \ last-=sz; } \ if (first!=base) swapper(first,(char*)base); @@ -334,7 +325,7 @@ typedef struct { char * first; char * last; } stack_entry; char *test; \ /* Find the right place for |first|. \ * My apologies for var reuse. */ \ - for (test=first-size;compare(test,first)>0;test-=size) ; \ + for (test=first-size;compare(userdata,test,first)>0;test-=size) ; \ test+=size; \ if (test!=first) { \ /* Shift everything in [test,first) \ @@ -362,7 +353,7 @@ typedef struct { char * first; char * last; } stack_entry; /* ---------------------------------------------------------------------- */ static char * pivot_big(char *first, char *mid, char *last, size_t size, - int compare(const void *, const void *)) { + int (SDLCALL * compare)(void *, const void *, const void *), void *userdata) { size_t d=(((last-first)/size)>>3)*size; #ifdef DEBUG_QSORT fprintf(stderr, "pivot_big: first=%p last=%p size=%lu n=%lu\n", first, (unsigned long)last, size, (unsigned long)((last-first+1)/size)); @@ -372,38 +363,38 @@ fprintf(stderr, "pivot_big: first=%p last=%p size=%lu n=%lu\n", first, (unsigned #ifdef DEBUG_QSORT fprintf(stderr,"< %d %d %d @ %p %p %p\n",*(int*)a,*(int*)b,*(int*)c, a,b,c); #endif - m1 = compare(a,b)<0 ? - (compare(b,c)<0 ? b : (compare(a,c)<0 ? c : a)) - : (compare(a,c)<0 ? a : (compare(b,c)<0 ? c : b)); + m1 = compare(userdata,a,b)<0 ? + (compare(userdata,b,c)<0 ? b : (compare(userdata,a,c)<0 ? c : a)) + : (compare(userdata,a,c)<0 ? a : (compare(userdata,b,c)<0 ? c : b)); } { char *a=mid-d, *b=mid, *c=mid+d; #ifdef DEBUG_QSORT fprintf(stderr,". %d %d %d @ %p %p %p\n",*(int*)a,*(int*)b,*(int*)c, a,b,c); #endif - m2 = compare(a,b)<0 ? - (compare(b,c)<0 ? b : (compare(a,c)<0 ? c : a)) - : (compare(a,c)<0 ? a : (compare(b,c)<0 ? c : b)); + m2 = compare(userdata,a,b)<0 ? + (compare(userdata,b,c)<0 ? b : (compare(userdata,a,c)<0 ? c : a)) + : (compare(userdata,a,c)<0 ? a : (compare(userdata,b,c)<0 ? c : b)); } { char *a=last-2*d, *b=last-d, *c=last; #ifdef DEBUG_QSORT fprintf(stderr,"> %d %d %d @ %p %p %p\n",*(int*)a,*(int*)b,*(int*)c, a,b,c); #endif - m3 = compare(a,b)<0 ? - (compare(b,c)<0 ? b : (compare(a,c)<0 ? c : a)) - : (compare(a,c)<0 ? a : (compare(b,c)<0 ? c : b)); + m3 = compare(userdata,a,b)<0 ? + (compare(userdata,b,c)<0 ? b : (compare(userdata,a,c)<0 ? c : a)) + : (compare(userdata,a,c)<0 ? a : (compare(userdata,b,c)<0 ? c : b)); } #ifdef DEBUG_QSORT fprintf(stderr,"-> %d %d %d @ %p %p %p\n",*(int*)m1,*(int*)m2,*(int*)m3, m1,m2,m3); #endif - return compare(m1,m2)<0 ? - (compare(m2,m3)<0 ? m2 : (compare(m1,m3)<0 ? m3 : m1)) - : (compare(m1,m3)<0 ? m1 : (compare(m2,m3)<0 ? m3 : m2)); + return compare(userdata,m1,m2)<0 ? + (compare(userdata,m2,m3)<0 ? m2 : (compare(userdata,m1,m3)<0 ? m3 : m1)) + : (compare(userdata,m1,m3)<0 ? m1 : (compare(userdata,m2,m3)<0 ? m3 : m2)); } /* ---------------------------------------------------------------------- */ -static void qsort_nonaligned(void *base, size_t nmemb, size_t size, - int (*compare)(const void *, const void *)) { +static void qsort_r_nonaligned(void *base, size_t nmemb, size_t size, + int (SDLCALL * compare)(void *, const void *, const void *), void *userdata) { stack_entry stack[STACK_SIZE]; int stacktop=0; @@ -433,8 +424,8 @@ static void qsort_nonaligned(void *base, size_t nmemb, size_t size, free(pivot); } -static void qsort_aligned(void *base, size_t nmemb, size_t size, - int (*compare)(const void *, const void *)) { +static void qsort_r_aligned(void *base, size_t nmemb, size_t size, + int (SDLCALL * compare)(void *,const void *, const void *), void *userdata) { stack_entry stack[STACK_SIZE]; int stacktop=0; @@ -464,8 +455,8 @@ static void qsort_aligned(void *base, size_t nmemb, size_t size, free(pivot); } -static void qsort_words(void *base, size_t nmemb, - int (*compare)(const void *, const void *)) { +static void qsort_r_words(void *base, size_t nmemb, + int (SDLCALL * compare)(void *,const void *, const void *), void *userdata) { stack_entry stack[STACK_SIZE]; int stacktop=0; @@ -507,7 +498,7 @@ fprintf(stderr, "after partitioning first=#%lu last=#%lu\n", (first-(char*)base) /* Find the right place for |first|. My apologies for var reuse */ int *pl=(int*)(first-WORD_BYTES),*pr=(int*)first; *(int*)pivot=*(int*)first; - for (;compare(pl,pivot)>0;pr=pl,--pl) { + for (;compare(userdata,pl,pivot)>0;pr=pl,--pl) { *pr=*pl; } if (pr!=(int*)first) *pr=*(int*)pivot; } @@ -516,28 +507,34 @@ fprintf(stderr, "after partitioning first=#%lu last=#%lu\n", (first-(char*)base) /* ---------------------------------------------------------------------- */ -extern void qsortG(void *base, size_t nmemb, size_t size, - int (*compare)(const void *, const void *)) { +extern void SDL_qsort_r(void *base, size_t nmemb, size_t size, + int (SDLCALL * compare)(void *, const void *, const void *), void *userdata) { if (nmemb<=1) return; if (((size_t)base|size)&(WORD_BYTES-1)) - qsort_nonaligned(base,nmemb,size,compare); + qsort_r_nonaligned(base,nmemb,size,compare,userdata); else if (size!=WORD_BYTES) - qsort_aligned(base,nmemb,size,compare); + qsort_r_aligned(base,nmemb,size,compare,userdata); else - qsort_words(base,nmemb,compare); + qsort_r_words(base,nmemb,compare,userdata); } -#endif /* HAVE_QSORT */ +static int SDLCALL qsort_non_r_bridge(void *userdata, const void *a, const void *b) +{ + int (SDLCALL *compare)(const void *, const void *) = (int (SDLCALL *)(const void *, const void *)) userdata; + return compare(a, b); +} -void *SDL_bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compare)(const void *, const void *)) +void SDL_qsort(void *base, size_t nmemb, size_t size, int (SDLCALL *compare) (const void *, const void *)) +{ + SDL_qsort_r(base, nmemb, size, qsort_non_r_bridge, compare); +} + +// Don't use the C runtime for such a simple function, since we want to allow SDLCALL callbacks and userdata. +// SDL's replacement: Taken from the Public Domain C Library (PDCLib): +// Permission is granted to use, modify, and / or redistribute at will. +void *SDL_bsearch_r(const void *key, const void *base, size_t nmemb, size_t size, int (SDLCALL *compare)(void *, const void *, const void *), void *userdata) { -#ifdef HAVE_BSEARCH - return bsearch(key, base, nmemb, size, compare); -#else -/* SDL's replacement: Taken from the Public Domain C Library (PDCLib): - Permission is granted to use, modify, and / or redistribute at will. -*/ const void *pivot; size_t corr; int rc; @@ -547,7 +544,7 @@ void *SDL_bsearch(const void *key, const void *base, size_t nmemb, size_t size, corr = nmemb % 2; nmemb /= 2; pivot = (const char *)base + (nmemb * size); - rc = compare(key, pivot); + rc = compare(userdata, key, pivot); if (rc > 0) { base = (const char *)pivot + size; @@ -559,5 +556,11 @@ void *SDL_bsearch(const void *key, const void *base, size_t nmemb, size_t size, } return NULL; -#endif /* HAVE_BSEARCH */ } + +void *SDL_bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (SDLCALL *compare)(const void *, const void *)) +{ + // qsort_non_r_bridge just happens to match calling conventions, so reuse it. + return SDL_bsearch_r(key, base, nmemb, size, qsort_non_r_bridge, compare); +} + diff --git a/test/testqsort.c b/test/testqsort.c index 08a518851e3de..5fa5db7b8c96a 100644 --- a/test/testqsort.c +++ b/test/testqsort.c @@ -14,6 +14,8 @@ #include #include +static int a_global_var = 77; + static int SDLCALL num_compare(const void *_a, const void *_b) { @@ -22,20 +24,36 @@ num_compare(const void *_a, const void *_b) return (a < b) ? -1 : ((a > b) ? 1 : 0); } +static int SDLCALL +num_compare_r(void *userdata, const void *a, const void *b) +{ + if (userdata != &a_global_var) { + SDL_Log("Uhoh, invalid userdata during qsort!"); + } + return num_compare(a, b); +} + static void test_sort(const char *desc, int *nums, const int arraylen) { + static int nums_copy[1024 * 100]; int i; int prev; + SDL_assert(SDL_arraysize(nums_copy) >= arraylen); + SDL_Log("test: %s arraylen=%d", desc, arraylen); + SDL_memcpy(nums_copy, nums, arraylen * sizeof (*nums)); + SDL_qsort(nums, arraylen, sizeof(nums[0]), num_compare); + SDL_qsort_r(nums_copy, arraylen, sizeof(nums[0]), num_compare_r, &a_global_var); prev = nums[0]; for (i = 1; i < arraylen; i++) { const int val = nums[i]; - if (val < prev) { + const int val2 = nums_copy[i]; + if ((val < prev) || (val != val2)) { SDL_Log("sort is broken!"); return; } From 812e04fb11d150bb4868bff7fa7566151f7f23f2 Mon Sep 17 00:00:00 2001 From: danginsburg Date: Fri, 1 Mar 2024 08:25:00 -0500 Subject: [PATCH 157/220] Vulkan Renderer - fix validation error with VkSemaphore reused before signaling. Have one semaphore per-submit rather than using the same one. --- src/render/vulkan/SDL_render_vulkan.c | 74 ++++++++++++++++++--------- 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 4395d882dd4c3..f38f3f3a65658 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -341,8 +341,8 @@ typedef struct VkImage *swapchainImages; VkImageView *swapchainImageViews; VkImageLayout *swapchainImageLayouts; - VkSemaphore imageAvailableSemaphore; - VkSemaphore renderingFinishedSemaphore; + VkSemaphore *imageAvailableSemaphores; + VkSemaphore *renderingFinishedSemaphores; uint32_t currentSwapchainImageIndex; /* Cached renderer properties */ @@ -515,13 +515,23 @@ static void VULKAN_DestroyAll(SDL_Renderer *renderer) rendererData->renderPasses[i] = VK_NULL_HANDLE; } } - if (rendererData->imageAvailableSemaphore != VK_NULL_HANDLE) { - vkDestroySemaphore(rendererData->device, rendererData->imageAvailableSemaphore, NULL); - rendererData->imageAvailableSemaphore = VK_NULL_HANDLE; + if (rendererData->imageAvailableSemaphores) { + for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) { + if (rendererData->imageAvailableSemaphores[i] != VK_NULL_HANDLE) { + vkDestroySemaphore(rendererData->device, rendererData->imageAvailableSemaphores[i], NULL); + } + } + SDL_free(rendererData->imageAvailableSemaphores); + rendererData->imageAvailableSemaphores = NULL; } - if (rendererData->renderingFinishedSemaphore != VK_NULL_HANDLE) { - vkDestroySemaphore(rendererData->device, rendererData->renderingFinishedSemaphore, NULL); - rendererData->renderingFinishedSemaphore = VK_NULL_HANDLE; + if (rendererData->renderingFinishedSemaphores) { + for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) { + if (rendererData->renderingFinishedSemaphores[i] != VK_NULL_HANDLE) { + vkDestroySemaphore(rendererData->device, rendererData->renderingFinishedSemaphores[i], NULL); + } + } + SDL_free(rendererData->renderingFinishedSemaphores); + rendererData->renderingFinishedSemaphores = NULL; } if (rendererData->commandPool) { if (rendererData->commandBuffers) { @@ -832,7 +842,7 @@ static VkResult VULKAN_AcquireNextSwapchainImage(SDL_Renderer *renderer) VkResult result; result = vkAcquireNextImageKHR(rendererData->device, rendererData->swapchain, UINT64_MAX, - rendererData->imageAvailableSemaphore, VK_NULL_HANDLE, &rendererData->currentSwapchainImageIndex); + rendererData->imageAvailableSemaphores[rendererData->currentCommandBufferIndex], VK_NULL_HANDLE, &rendererData->currentSwapchainImageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_ERROR_SURFACE_LOST_KHR) { result = VULKAN_CreateWindowSizeDependentResources(renderer); return result; @@ -2257,21 +2267,35 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) } /* Create semaphores */ - if (rendererData->imageAvailableSemaphore != VK_NULL_HANDLE) { - vkDestroySemaphore(rendererData->device, rendererData->imageAvailableSemaphore, NULL); - } - rendererData->imageAvailableSemaphore = VULKAN_CreateSemaphore(rendererData); - if (rendererData->imageAvailableSemaphore == VK_NULL_HANDLE) { - VULKAN_DestroyAll(renderer); - return VK_ERROR_UNKNOWN; + if (rendererData->imageAvailableSemaphores) { + for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) { + if (rendererData->imageAvailableSemaphores[i] != VK_NULL_HANDLE) { + vkDestroySemaphore(rendererData->device, rendererData->imageAvailableSemaphores[i], NULL); + } + } + SDL_free(rendererData->imageAvailableSemaphores); } - if (rendererData->renderingFinishedSemaphore != VK_NULL_HANDLE) { - vkDestroySemaphore(rendererData->device, rendererData->renderingFinishedSemaphore, NULL); + if (rendererData->renderingFinishedSemaphores) { + for (uint32_t i = 0; i < rendererData->swapchainImageCount; ++i) { + if (rendererData->renderingFinishedSemaphores[i] != VK_NULL_HANDLE) { + vkDestroySemaphore(rendererData->device, rendererData->renderingFinishedSemaphores[i], NULL); + } + } + SDL_free(rendererData->renderingFinishedSemaphores); } - rendererData->renderingFinishedSemaphore = VULKAN_CreateSemaphore(rendererData); - if (rendererData->renderingFinishedSemaphore == VK_NULL_HANDLE) { - VULKAN_DestroyAll(renderer); - return VK_ERROR_UNKNOWN; + rendererData->imageAvailableSemaphores = SDL_calloc(sizeof(VkSemaphore), rendererData->swapchainImageCount); + rendererData->renderingFinishedSemaphores = SDL_calloc(sizeof(VkSemaphore), rendererData->swapchainImageCount); + for (uint32_t i = 0; i < rendererData->swapchainImageCount; i++) { + rendererData->imageAvailableSemaphores[i] = VULKAN_CreateSemaphore(rendererData); + if (rendererData->imageAvailableSemaphores[i] == VK_NULL_HANDLE) { + VULKAN_DestroyAll(renderer); + return VK_ERROR_UNKNOWN; + } + rendererData->renderingFinishedSemaphores[i] = VULKAN_CreateSemaphore(rendererData); + if (rendererData->renderingFinishedSemaphores[i] == VK_NULL_HANDLE) { + VULKAN_DestroyAll(renderer); + return VK_ERROR_UNKNOWN; + } } /* Upload buffers */ @@ -3835,12 +3859,12 @@ static int VULKAN_RenderPresent(SDL_Renderer *renderer) VkSubmitInfo submitInfo = { 0 }; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = &rendererData->imageAvailableSemaphore; + submitInfo.pWaitSemaphores = &rendererData->imageAvailableSemaphores[rendererData->currentCommandBufferIndex]; submitInfo.pWaitDstStageMask = &waitDestStageMask; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &rendererData->currentCommandBuffer; submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &rendererData->renderingFinishedSemaphore; + submitInfo.pSignalSemaphores = &rendererData->renderingFinishedSemaphores[rendererData->currentCommandBufferIndex]; result = vkQueueSubmit(rendererData->graphicsQueue, 1, &submitInfo, rendererData->fences[rendererData->currentCommandBufferIndex]); if (result != VK_SUCCESS) { SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkQueueSubmit(): %s\n", SDL_Vulkan_GetResultString(result)); @@ -3851,7 +3875,7 @@ static int VULKAN_RenderPresent(SDL_Renderer *renderer) VkPresentInfoKHR presentInfo = { 0 }; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.waitSemaphoreCount = 1; - presentInfo.pWaitSemaphores = &rendererData->renderingFinishedSemaphore; + presentInfo.pWaitSemaphores = &rendererData->renderingFinishedSemaphores[rendererData->currentCommandBufferIndex]; presentInfo.swapchainCount = 1; presentInfo.pSwapchains = &rendererData->swapchain; presentInfo.pImageIndices = &rendererData->currentSwapchainImageIndex; From 991ad27de8a771cb419759dbc689a6dd9dd1cb16 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 1 Mar 2024 02:02:53 -0800 Subject: [PATCH 158/220] testyuv: added GetColorspaceForYUVConversionMode() --- test/testyuv.c | 28 ++-------------------------- test/testyuv_cvt.c | 36 ++++++++++++++++++++++++++++++++++++ test/testyuv_cvt.h | 1 + 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/test/testyuv.c b/test/testyuv.c index 96858f7d79689..4beaee8ec9728 100644 --- a/test/testyuv.c +++ b/test/testyuv.c @@ -116,11 +116,6 @@ static int run_automated_tests(int pattern_size, int extra_pitch) SDL_PIXELFORMAT_UYVY, SDL_PIXELFORMAT_YVYU }; - const SDL_Colorspace colorspaces[] = { - SDL_COLORSPACE_JPEG, - SDL_COLORSPACE_BT601_LIMITED, - SDL_COLORSPACE_BT709_LIMITED - }; int i, j; SDL_Surface *pattern = generate_test_pattern(pattern_size); const int yuv_len = MAX_YUV_SURFACE_SIZE(pattern->w, pattern->h, extra_pitch); @@ -137,8 +132,7 @@ static int run_automated_tests(int pattern_size, int extra_pitch) } mode = GetYUVConversionModeForResolution(pattern->w, pattern->h); - SDL_assert(mode < SDL_arraysize(colorspaces)); - colorspace = colorspaces[mode]; + colorspace = GetColorspaceForYUVConversionMode(mode); /* Verify conversion from YUV formats */ for (i = 0; i < SDL_arraysize(formats); ++i) { @@ -395,36 +389,18 @@ int main(int argc, char **argv) switch (yuv_mode) { case YUV_CONVERSION_JPEG: yuv_mode_name = "JPEG"; - colorspace = SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR, - SDL_COLOR_RANGE_FULL, - SDL_COLOR_PRIMARIES_BT709, - SDL_TRANSFER_CHARACTERISTICS_BT601, - SDL_MATRIX_COEFFICIENTS_BT601, - SDL_CHROMA_LOCATION_CENTER); break; case YUV_CONVERSION_BT601: yuv_mode_name = "BT.601"; - colorspace = SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR, - SDL_COLOR_RANGE_LIMITED, - SDL_COLOR_PRIMARIES_BT709, - SDL_TRANSFER_CHARACTERISTICS_BT601, - SDL_MATRIX_COEFFICIENTS_BT601, - SDL_CHROMA_LOCATION_CENTER); break; case YUV_CONVERSION_BT709: yuv_mode_name = "BT.709"; - colorspace = SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR, - SDL_COLOR_RANGE_LIMITED, - SDL_COLOR_PRIMARIES_BT709, - SDL_TRANSFER_CHARACTERISTICS_BT709, - SDL_MATRIX_COEFFICIENTS_BT709, - SDL_CHROMA_LOCATION_CENTER); break; default: yuv_mode_name = "UNKNOWN"; - colorspace = SDL_COLORSPACE_UNKNOWN; break; } + colorspace = GetColorspaceForYUVConversionMode(yuv_mode); raw_yuv = SDL_calloc(1, MAX_YUV_SURFACE_SIZE(original->w, original->h, 0)); ConvertRGBtoYUV(yuv_format, original->pixels, original->pitch, raw_yuv, original->w, original->h, yuv_mode, 0, 100); diff --git a/test/testyuv_cvt.c b/test/testyuv_cvt.c index 67a0ab6d88db4..dab0709aefe60 100644 --- a/test/testyuv_cvt.c +++ b/test/testyuv_cvt.c @@ -41,6 +41,42 @@ YUV_CONVERSION_MODE GetYUVConversionModeForResolution(int width, int height) return mode; } +SDL_Colorspace GetColorspaceForYUVConversionMode(YUV_CONVERSION_MODE mode) +{ + SDL_Colorspace colorspace; + + switch (mode) { + case YUV_CONVERSION_JPEG: + colorspace = SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR, + SDL_COLOR_RANGE_FULL, + SDL_COLOR_PRIMARIES_BT709, + SDL_TRANSFER_CHARACTERISTICS_BT601, + SDL_MATRIX_COEFFICIENTS_BT601, + SDL_CHROMA_LOCATION_CENTER); + break; + case YUV_CONVERSION_BT601: + colorspace = SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR, + SDL_COLOR_RANGE_LIMITED, + SDL_COLOR_PRIMARIES_BT709, + SDL_TRANSFER_CHARACTERISTICS_BT601, + SDL_MATRIX_COEFFICIENTS_BT601, + SDL_CHROMA_LOCATION_CENTER); + break; + case YUV_CONVERSION_BT709: + colorspace = SDL_DEFINE_COLORSPACE(SDL_COLOR_TYPE_YCBCR, + SDL_COLOR_RANGE_LIMITED, + SDL_COLOR_PRIMARIES_BT709, + SDL_TRANSFER_CHARACTERISTICS_BT709, + SDL_MATRIX_COEFFICIENTS_BT709, + SDL_CHROMA_LOCATION_CENTER); + break; + default: + colorspace = SDL_COLORSPACE_UNKNOWN; + break; + } + return colorspace; +} + static float clip3(float x, float y, float z) { return (z < x) ? x : ((z > y) ? y : z); diff --git a/test/testyuv_cvt.h b/test/testyuv_cvt.h index 62e6ab2abe85a..c8962142bcb8a 100644 --- a/test/testyuv_cvt.h +++ b/test/testyuv_cvt.h @@ -22,5 +22,6 @@ typedef enum extern void SetYUVConversionMode(YUV_CONVERSION_MODE mode); extern YUV_CONVERSION_MODE GetYUVConversionModeForResolution(int width, int height); +extern SDL_Colorspace GetColorspaceForYUVConversionMode(YUV_CONVERSION_MODE mode); extern SDL_bool ConvertRGBtoYUV(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, YUV_CONVERSION_MODE mode, int monochrome, int luminance); extern int CalculateYUVPitch(Uint32 format, int width); From 47be24d22543f4b528221d6e27f046a38e2a8f53 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 1 Mar 2024 02:42:18 -0800 Subject: [PATCH 159/220] testyuv: added --monochrome and --luminance options for interactive mode --- test/testyuv.c | 11 ++++++++++- test/testyuv_cvt.c | 8 ++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/test/testyuv.c b/test/testyuv.c index 4beaee8ec9728..2974164d673f0 100644 --- a/test/testyuv.c +++ b/test/testyuv.c @@ -261,6 +261,8 @@ int main(int argc, char **argv) Uint32 rgb_format = SDL_PIXELFORMAT_RGBX8888; SDL_PropertiesID props; SDL_Colorspace colorspace; + SDL_bool monochrome = SDL_FALSE; + int luminance = 100; int current = 0; int pitch; Uint8 *raw_yuv; @@ -338,6 +340,12 @@ int main(int argc, char **argv) } else if (SDL_strcmp(argv[i], "--bgra") == 0) { rgb_format = SDL_PIXELFORMAT_BGRA8888; consumed = 1; + } else if (SDL_strcmp(argv[i], "--monochrome") == 0) { + monochrome = SDL_TRUE; + consumed = 1; + } else if (SDL_strcmp(argv[i], "--luminance") == 0 && argv[i+1]) { + luminance = SDL_atoi(argv[i+1]); + consumed = 2; } else if (SDL_strcmp(argv[i], "--automated") == 0) { should_run_automated_tests = SDL_TRUE; consumed = 1; @@ -351,6 +359,7 @@ int main(int argc, char **argv) "[--jpeg|--bt601|--bt709|--auto]", "[--yv12|--iyuv|--yuy2|--uyvy|--yvyu|--nv12|--nv21]", "[--rgb555|--rgb565|--rgb24|--argb|--abgr|--rgba|--bgra]", + "[--monochrome] [--luminance N%]", "[--automated]", "[sample.bmp]", NULL, @@ -403,7 +412,7 @@ int main(int argc, char **argv) colorspace = GetColorspaceForYUVConversionMode(yuv_mode); raw_yuv = SDL_calloc(1, MAX_YUV_SURFACE_SIZE(original->w, original->h, 0)); - ConvertRGBtoYUV(yuv_format, original->pixels, original->pitch, raw_yuv, original->w, original->h, yuv_mode, 0, 100); + ConvertRGBtoYUV(yuv_format, original->pixels, original->pitch, raw_yuv, original->w, original->h, yuv_mode, monochrome, luminance); pitch = CalculateYUVPitch(yuv_format, original->w); converted = SDL_CreateSurface(original->w, original->h, rgb_format); diff --git a/test/testyuv_cvt.c b/test/testyuv_cvt.c index dab0709aefe60..c165b3218d6e0 100644 --- a/test/testyuv_cvt.c +++ b/test/testyuv_cvt.c @@ -129,9 +129,9 @@ static void RGBtoYUV(const Uint8 *rgb, int *yuv, YUV_CONVERSION_MODE mode, int m U = clip3(0, SDL_powf(2.0f, M) - 1, SDL_floorf(SDL_powf(2.0f, (M - 8)) * (112.0f * (B - L) / ((1.0f - Kb) * S) + 128) + 0.5f)); V = clip3(0, SDL_powf(2.0f, M) - 1, SDL_floorf(SDL_powf(2.0f, (M - 8)) * (112.0f * (R - L) / ((1.0f - Kr) * S) + 128) + 0.5f)); - yuv[0] = (Uint8)Y; - yuv[1] = (Uint8)U; - yuv[2] = (Uint8)V; + yuv[0] = (int)Y; + yuv[1] = (int)U; + yuv[2] = (int)V; if (monochrome) { yuv[1] = 128; @@ -139,7 +139,7 @@ static void RGBtoYUV(const Uint8 *rgb, int *yuv, YUV_CONVERSION_MODE mode, int m } if (luminance != 100) { - yuv[0] = (Uint8)SDL_roundf(yuv[0] * (luminance / 100.0f)); + yuv[0] = (int)SDL_roundf(yuv[0] * (luminance / 100.0f)); if (yuv[0] > 255) { yuv[0] = 255; } From f2cd361e255d52be073c46b519055c70c4724fcf Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 1 Mar 2024 15:35:25 -0800 Subject: [PATCH 160/220] testyuv: added validation of P010 YUV format Also added conversion between RGB and P010, using XRGB2101010 as a bridging format in PQ space --- include/SDL3/SDL_pixels.h | 5 +- src/video/SDL_yuv.c | 286 ++++++++++++++++++++++++--- src/video/yuv2rgb/yuv_rgb_common.h | 3 +- src/video/yuv2rgb/yuv_rgb_internal.h | 14 +- src/video/yuv2rgb/yuv_rgb_std.c | 21 ++ src/video/yuv2rgb/yuv_rgb_std.h | 6 + src/video/yuv2rgb/yuv_rgb_std_func.h | 73 ++++--- test/testyuv.c | 75 +++++-- test/testyuv_cvt.c | 269 +++++++++++++++++++++---- test/testyuv_cvt.h | 1 + 10 files changed, 637 insertions(+), 116 deletions(-) diff --git a/include/SDL3/SDL_pixels.h b/include/SDL3/SDL_pixels.h index 0d0325a31c054..ac8f1e07aa495 100644 --- a/include/SDL3/SDL_pixels.h +++ b/include/SDL3/SDL_pixels.h @@ -560,8 +560,9 @@ typedef enum #define SDL_ISCOLORSPACE_YUV_BT601(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT601 || SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT470BG) #define SDL_ISCOLORSPACE_YUV_BT709(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT709) -#define SDL_ISCOLORSPACE_LIMITED_RANGE(X) (SDL_COLORSPACERANGE(X) == SDL_COLOR_RANGE_LIMITED) -#define SDL_ISCOLORSPACE_FULL_RANGE(X) (SDL_COLORSPACERANGE(X) == SDL_COLOR_RANGE_LIMITED) +#define SDL_ISCOLORSPACE_YUV_BT2020(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT2020_NCL || SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT2020_CL) +#define SDL_ISCOLORSPACE_LIMITED_RANGE(X) (SDL_COLORSPACERANGE(X) != SDL_COLOR_RANGE_FULL) +#define SDL_ISCOLORSPACE_FULL_RANGE(X) (SDL_COLORSPACERANGE(X) == SDL_COLOR_RANGE_FULL) typedef enum { diff --git a/src/video/SDL_yuv.c b/src/video/SDL_yuv.c index 4a8e00ef3a745..b9dbf7726d9d6 100644 --- a/src/video/SDL_yuv.c +++ b/src/video/SDL_yuv.c @@ -167,22 +167,29 @@ static int GetYUVConversionType(SDL_Colorspace colorspace, YCbCrType *yuv_type) } else { *yuv_type = YCBCR_JPEG; } - } else if (SDL_ISCOLORSPACE_YUV_BT709(colorspace)) { + return 0; + } + + if (SDL_ISCOLORSPACE_YUV_BT709(colorspace)) { if (SDL_ISCOLORSPACE_LIMITED_RANGE(colorspace)) { *yuv_type = YCBCR_709; - } else { - /* BT709 full range isn't supported yet */ - return SDL_SetError("Unsupported YUV colorspace"); + return 0; } - } else { - return SDL_SetError("Unsupported YUV colorspace"); } - return 0; + + if (SDL_ISCOLORSPACE_YUV_BT2020(colorspace)) { + if (SDL_ISCOLORSPACE_FULL_RANGE(colorspace)) { + *yuv_type = YCBCR_2020; + return 0; + } + } + + return SDL_SetError("Unsupported YUV colorspace"); } static SDL_bool IsPlanar2x2Format(Uint32 format) { - return format == SDL_PIXELFORMAT_YV12 || format == SDL_PIXELFORMAT_IYUV || format == SDL_PIXELFORMAT_NV12 || format == SDL_PIXELFORMAT_NV21; + return format == SDL_PIXELFORMAT_YV12 || format == SDL_PIXELFORMAT_IYUV || format == SDL_PIXELFORMAT_NV12 || format == SDL_PIXELFORMAT_NV21 || format == SDL_PIXELFORMAT_P010; } static SDL_bool IsPacked4Format(Uint32 format) @@ -195,6 +202,7 @@ static int GetYUVPlanes(int width, int height, Uint32 format, const void *yuv, i { const Uint8 *planes[3] = { NULL, NULL, NULL }; int pitches[3] = { 0, 0, 0 }; + int uv_width; switch (format) { case SDL_PIXELFORMAT_YV12: @@ -219,6 +227,13 @@ static int GetYUVPlanes(int width, int height, Uint32 format, const void *yuv, i planes[0] = (const Uint8 *)yuv; planes[1] = planes[0] + pitches[0] * height; break; + case SDL_PIXELFORMAT_P010: + pitches[0] = yuv_pitch; + uv_width = ((width + 1) / 2) * 2; + pitches[1] = SDL_max(pitches[0], uv_width * sizeof(Uint16)); + planes[0] = (const Uint8 *)yuv; + planes[1] = planes[0] + pitches[0] * height; + break; default: return SDL_SetError("GetYUVPlanes(): Unsupported YUV format: %s", SDL_GetPixelFormatName(format)); } @@ -273,6 +288,13 @@ static int GetYUVPlanes(int width, int height, Uint32 format, const void *yuv, i *u = *v + 1; *uv_stride = pitches[1]; break; + case SDL_PIXELFORMAT_P010: + *y = planes[0]; + *y_stride = pitches[0]; + *u = planes[1]; + *v = *u + sizeof(Uint16); + *uv_stride = pitches[1]; + break; default: /* Should have caught this above */ return SDL_SetError("GetYUVPlanes[2]: Unsupported YUV format: %s", SDL_GetPixelFormatName(format)); @@ -551,6 +573,14 @@ static SDL_bool yuv_rgb_std( break; } } + + if (src_format == SDL_PIXELFORMAT_P010) { + switch (dst_format) { + case SDL_PIXELFORMAT_XBGR2101010: + yuvp010_xbgr2101010_std(width, height, (const uint16_t *)y, (const uint16_t *)u, (const uint16_t *)v, y_stride, uv_stride, rgb, rgb_stride, yuv_type); + return SDL_TRUE; + } + } return SDL_FALSE; } @@ -586,6 +616,29 @@ int SDL_ConvertPixels_YUV_to_RGB(int width, int height, } /* No fast path for the RGB format, instead convert using an intermediate buffer */ + if (src_format == SDL_PIXELFORMAT_P010 && dst_format != SDL_PIXELFORMAT_XBGR2101010) { + int ret; + void *tmp; + int tmp_pitch = (width * sizeof(Uint32)); + + tmp = SDL_malloc((size_t)tmp_pitch * height); + if (!tmp) { + return -1; + } + + /* convert src/src_format to tmp/XBGR2101010 */ + ret = SDL_ConvertPixels_YUV_to_RGB(width, height, src_format, src_colorspace, src_properties, src, src_pitch, SDL_PIXELFORMAT_XBGR2101010, src_colorspace, src_properties, tmp, tmp_pitch); + if (ret < 0) { + SDL_free(tmp); + return ret; + } + + /* convert tmp/XBGR2101010 to dst/RGB */ + ret = SDL_ConvertPixelsAndColorspace(width, height, SDL_PIXELFORMAT_XBGR2101010, src_colorspace, src_properties, tmp, tmp_pitch, dst_format, dst_colorspace, dst_properties, dst, dst_pitch); + SDL_free(tmp); + return ret; + } + if (dst_format != SDL_PIXELFORMAT_ARGB8888) { int ret; void *tmp; @@ -620,6 +673,37 @@ struct RGB2YUVFactors float v[3]; /* Rfactor, Gfactor, Bfactor */ }; +static struct RGB2YUVFactors RGB2YUVFactorTables[] = { + /* ITU-T T.871 (JPEG) */ + { + 0, + { 0.2990f, 0.5870f, 0.1140f }, + { -0.1687f, -0.3313f, 0.5000f }, + { 0.5000f, -0.4187f, -0.0813f }, + }, + /* ITU-R BT.601-7 */ + { + 16, + { 0.2568f, 0.5041f, 0.0979f }, + { -0.1482f, -0.2910f, 0.4392f }, + { 0.4392f, -0.3678f, -0.0714f }, + }, + /* ITU-R BT.709-6 */ + { + 16, + { 0.1826f, 0.6142f, 0.0620f }, + { -0.1006f, -0.3386f, 0.4392f }, + { 0.4392f, -0.3989f, -0.0403f }, + }, + /* ITU-R BT.2020 10-bit full range */ + { + 0, + { 0.2627f, 0.6780f, 0.0593f }, + { -0.1395f, -0.3600f, 0.4995f }, + { 0.4995f, -0.4593f, -0.0402f }, + }, +}; + static int SDL_ConvertPixels_ARGB8888_to_YUV(int width, int height, const void *src, int src_pitch, Uint32 dst_format, void *dst, int dst_pitch, YCbCrType yuv_type) { const int src_pitch_x_2 = src_pitch * 2; @@ -629,29 +713,6 @@ static int SDL_ConvertPixels_ARGB8888_to_YUV(int width, int height, const void * const int width_remainder = (width & 0x1); int i, j; - static struct RGB2YUVFactors RGB2YUVFactorTables[] = { - /* ITU-T T.871 (JPEG) */ - { - 0, - { 0.2990f, 0.5870f, 0.1140f }, - { -0.1687f, -0.3313f, 0.5000f }, - { 0.5000f, -0.4187f, -0.0813f }, - }, - /* ITU-R BT.601-7 */ - { - 16, - { 0.2568f, 0.5041f, 0.0979f }, - { -0.1482f, -0.2910f, 0.4392f }, - { 0.4392f, -0.3678f, -0.0714f }, - }, - /* ITU-R BT.709-6 */ - { - 16, - { 0.1826f, 0.6142f, 0.0620f }, - { -0.1006f, -0.3386f, 0.4392f }, - { 0.4392f, -0.3989f, -0.0403f }, - }, - }; const struct RGB2YUVFactors *cvt = &RGB2YUVFactorTables[yuv_type]; #define MAKE_Y(r, g, b) (Uint8)((int)(cvt->y[0] * (r) + cvt->y[1] * (g) + cvt->y[2] * (b) + 0.5f) + cvt->y_offset) @@ -934,6 +995,128 @@ static int SDL_ConvertPixels_ARGB8888_to_YUV(int width, int height, const void * return 0; } +static int SDL_ConvertPixels_XBGR2101010_to_P010(int width, int height, const void *src, int src_pitch, Uint32 dst_format, void *dst, int dst_pitch, YCbCrType yuv_type) +{ + const int src_pitch_x_2 = src_pitch * 2; + const int height_half = height / 2; + const int height_remainder = (height & 0x1); + const int width_half = width / 2; + const int width_remainder = (width & 0x1); + int i, j; + + const struct RGB2YUVFactors *cvt = &RGB2YUVFactorTables[yuv_type]; + +#define MAKE_Y(r, g, b) (Uint16)(((int)(cvt->y[0] * (r) + cvt->y[1] * (g) + cvt->y[2] * (b) + 0.5f) + cvt->y_offset) << 6) +#define MAKE_U(r, g, b) (Uint16)(((int)(cvt->u[0] * (r) + cvt->u[1] * (g) + cvt->u[2] * (b) + 0.5f) + 512) << 6) +#define MAKE_V(r, g, b) (Uint16)(((int)(cvt->v[0] * (r) + cvt->v[1] * (g) + cvt->v[2] * (b) + 0.5f) + 512) << 6) + +#define READ_2x2_PIXELS \ + const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \ + const Uint32 p2 = ((const Uint32 *)curr_row)[2 * i + 1]; \ + const Uint32 p3 = ((const Uint32 *)next_row)[2 * i]; \ + const Uint32 p4 = ((const Uint32 *)next_row)[2 * i + 1]; \ + const Uint32 r = ((p1 & 0x000003ff) + (p2 & 0x000003ff) + (p3 & 0x000003ff) + (p4 & 0x000003ff)) >> 2; \ + const Uint32 g = ((p1 & 0x000ffc00) + (p2 & 0x000ffc00) + (p3 & 0x000ffc00) + (p4 & 0x000ffc00)) >> 12; \ + const Uint32 b = ((p1 & 0x3ff00000) + (p2 & 0x3ff00000) + (p3 & 0x3ff00000) + (p4 & 0x3ff00000)) >> 22; + +#define READ_2x1_PIXELS \ + const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \ + const Uint32 p2 = ((const Uint32 *)next_row)[2 * i]; \ + const Uint32 r = ((p1 & 0x000003ff) + (p2 & 0x000003ff)) >> 1; \ + const Uint32 g = ((p1 & 0x000ffc00) + (p2 & 0x000ffc00)) >> 11; \ + const Uint32 b = ((p1 & 0x3ff00000) + (p2 & 0x3ff00000)) >> 21; + +#define READ_1x2_PIXELS \ + const Uint32 p1 = ((const Uint32 *)curr_row)[2 * i]; \ + const Uint32 p2 = ((const Uint32 *)curr_row)[2 * i + 1]; \ + const Uint32 r = ((p1 & 0x000003ff) + (p2 & 0x000003ff)) >> 1; \ + const Uint32 g = ((p1 & 0x000ffc00) + (p2 & 0x000ffc00)) >> 11; \ + const Uint32 b = ((p1 & 0x3ff00000) + (p2 & 0x3ff00000)) >> 21; + +#define READ_1x1_PIXEL \ + const Uint32 p = ((const Uint32 *)curr_row)[2 * i]; \ + const Uint32 r = (p & 0x000003ff); \ + const Uint32 g = (p & 0x000ffc00) >> 10; \ + const Uint32 b = (p & 0x3ff00000) >> 20; + + const Uint8 *curr_row, *next_row; + + Uint16 *plane_y; + Uint16 *plane_u; + Uint16 *plane_v; + Uint16 *plane_interleaved_uv; + Uint32 y_stride, uv_stride, y_skip, uv_skip; + + if (GetYUVPlanes(width, height, dst_format, dst, dst_pitch, + (const Uint8 **)&plane_y, (const Uint8 **)&plane_u, (const Uint8 **)&plane_v, + &y_stride, &uv_stride) != 0) { + return -1; + } + + y_stride /= sizeof(Uint16); + uv_stride /= sizeof(Uint16); + + plane_interleaved_uv = (plane_y + height * y_stride); + y_skip = (y_stride - width); + + curr_row = (const Uint8 *)src; + + /* Write Y plane */ + for (j = 0; j < height; j++) { + for (i = 0; i < width; i++) { + const Uint32 p1 = ((const Uint32 *)curr_row)[i]; + const Uint32 r = (p1 >> 0) & 0x03ff; + const Uint32 g = (p1 >> 10) & 0x03ff; + const Uint32 b = (p1 >> 20) & 0x03ff; + *plane_y++ = MAKE_Y(r, g, b); + } + plane_y += y_skip; + curr_row += src_pitch; + } + + curr_row = (const Uint8 *)src; + next_row = (const Uint8 *)src; + next_row += src_pitch; + + uv_skip = (uv_stride - ((width + 1) / 2) * 2); + for (j = 0; j < height_half; j++) { + for (i = 0; i < width_half; i++) { + READ_2x2_PIXELS; + *plane_interleaved_uv++ = MAKE_U(r, g, b); + *plane_interleaved_uv++ = MAKE_V(r, g, b); + } + if (width_remainder) { + READ_2x1_PIXELS; + *plane_interleaved_uv++ = MAKE_U(r, g, b); + *plane_interleaved_uv++ = MAKE_V(r, g, b); + } + plane_interleaved_uv += uv_skip; + curr_row += src_pitch_x_2; + next_row += src_pitch_x_2; + } + if (height_remainder) { + for (i = 0; i < width_half; i++) { + READ_1x2_PIXELS; + *plane_interleaved_uv++ = MAKE_U(r, g, b); + *plane_interleaved_uv++ = MAKE_V(r, g, b); + } + if (width_remainder) { + READ_1x1_PIXEL; + *plane_interleaved_uv++ = MAKE_U(r, g, b); + *plane_interleaved_uv++ = MAKE_V(r, g, b); + } + } + +#undef MAKE_Y +#undef MAKE_U +#undef MAKE_V +#undef READ_2x2_PIXELS +#undef READ_2x1_PIXELS +#undef READ_1x2_PIXELS +#undef READ_1x1_PIXEL + return 0; +} + int SDL_ConvertPixels_RGB_to_YUV(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch) @@ -967,6 +1150,34 @@ int SDL_ConvertPixels_RGB_to_YUV(int width, int height, return SDL_ConvertPixels_ARGB8888_to_YUV(width, height, src, src_pitch, dst_format, dst, dst_pitch, yuv_type); } + if (dst_format == SDL_PIXELFORMAT_P010) { + if (src_format == SDL_PIXELFORMAT_XBGR2101010) { + return SDL_ConvertPixels_XBGR2101010_to_P010(width, height, src, src_pitch, dst_format, dst, dst_pitch, yuv_type); + } + + /* We currently only support converting from XBGR2101010 to P010 */ + int ret; + void *tmp; + int tmp_pitch = (width * sizeof(Uint32)); + + tmp = SDL_malloc((size_t)tmp_pitch * height); + if (!tmp) { + return -1; + } + + /* convert src/src_format to tmp/XBGR2101010 */ + ret = SDL_ConvertPixelsAndColorspace(width, height, src_format, src_colorspace, src_properties, src, src_pitch, SDL_PIXELFORMAT_XBGR2101010, dst_colorspace, dst_properties, tmp, tmp_pitch); + if (ret == -1) { + SDL_free(tmp); + return ret; + } + + /* convert tmp/XBGR2101010 to dst/P010 */ + ret = SDL_ConvertPixels_XBGR2101010_to_P010(width, height, tmp, tmp_pitch, dst_format, dst, dst_pitch, yuv_type); + SDL_free(tmp); + return ret; + } + /* not ARGB8888 to FOURCC : need an intermediate conversion */ { int ret; @@ -979,7 +1190,7 @@ int SDL_ConvertPixels_RGB_to_YUV(int width, int height, } /* convert src/src_format to tmp/ARGB8888 */ - ret = SDL_ConvertPixels(width, height, src_format, src, src_pitch, SDL_PIXELFORMAT_ARGB8888, tmp, tmp_pitch); + ret = SDL_ConvertPixelsAndColorspace(width, height, src_format, src_colorspace, src_properties, src, src_pitch, SDL_PIXELFORMAT_ARGB8888, dst_colorspace, dst_properties, tmp, tmp_pitch); if (ret == -1) { SDL_free(tmp); return ret; @@ -1027,6 +1238,17 @@ static int SDL_ConvertPixels_YUV_to_YUV_Copy(int width, int height, Uint32 forma src = (const Uint8 *)src + src_pitch; dst = (Uint8 *)dst + dst_pitch; } + } else if (format == SDL_PIXELFORMAT_P010) { + /* U/V plane is half the height of the Y plane, rounded up */ + height = (height + 1) / 2; + width = ((width + 1) / 2) * 2; + src_pitch = ((src_pitch + 1) / 2) * 2; + dst_pitch = ((dst_pitch + 1) / 2) * 2; + for (i = height; i--;) { + SDL_memcpy(dst, src, width * sizeof(Uint16)); + src = (const Uint8 *)src + src_pitch; + dst = (Uint8 *)dst + dst_pitch; + } } return 0; } diff --git a/src/video/yuv2rgb/yuv_rgb_common.h b/src/video/yuv2rgb/yuv_rgb_common.h index ae787ed5f27a6..1ab607d015282 100644 --- a/src/video/yuv2rgb/yuv_rgb_common.h +++ b/src/video/yuv2rgb/yuv_rgb_common.h @@ -7,7 +7,8 @@ typedef enum { YCBCR_JPEG, YCBCR_601, - YCBCR_709 + YCBCR_709, + YCBCR_2020 } YCbCrType; #endif /* YUV_RGB_COMMON_H_ */ diff --git a/src/video/yuv2rgb/yuv_rgb_internal.h b/src/video/yuv2rgb/yuv_rgb_internal.h index 31405a6d05afb..23ae705669bad 100644 --- a/src/video/yuv2rgb/yuv_rgb_internal.h +++ b/src/video/yuv2rgb/yuv_rgb_internal.h @@ -37,24 +37,29 @@ typedef struct // for ITU-T T.871, values can be found in section 7 // for ITU-R BT.601-7 values are derived from equations in sections 2.5.1-2.5.3, assuming RGB is encoded using full range ([0-1]<->[0-255]) // for ITU-R BT.709-6 values are derived from equations in sections 3.2-3.4, assuming RGB is encoded using full range ([0-1]<->[0-255]) +// for ITU-R BT.2020 values are assuming RGB is encoded using full 10-bit range ([0-1]<->[0-1023]) // all values are rounded to the fourth decimal -static const YUV2RGBParam YUV2RGB[3] = { +static const YUV2RGBParam YUV2RGB[4] = { // ITU-T T.871 (JPEG) {/*.y_shift=*/ 0, /*.y_factor=*/ V(1.0), /*.v_r_factor=*/ V(1.402), /*.u_g_factor=*/ -V(0.3441), /*.v_g_factor=*/ -V(0.7141), /*.u_b_factor=*/ V(1.772)}, // ITU-R BT.601-7 {/*.y_shift=*/ 16, /*.y_factor=*/ V(1.1644), /*.v_r_factor=*/ V(1.596), /*.u_g_factor=*/ -V(0.3918), /*.v_g_factor=*/ -V(0.813), /*.u_b_factor=*/ V(2.0172)}, // ITU-R BT.709-6 - {/*.y_shift=*/ 16, /*.y_factor=*/ V(1.1644), /*.v_r_factor=*/ V(1.7927), /*.u_g_factor=*/ -V(0.2132), /*.v_g_factor=*/ -V(0.5329), /*.u_b_factor=*/ V(2.1124)} + {/*.y_shift=*/ 16, /*.y_factor=*/ V(1.1644), /*.v_r_factor=*/ V(1.7927), /*.u_g_factor=*/ -V(0.2132), /*.v_g_factor=*/ -V(0.5329), /*.u_b_factor=*/ V(2.1124)}, + // ITU-R BT.2020 10-bit full range + {/*.y_shift=*/ 0, /*.y_factor=*/ V(1.0), /*.v_r_factor=*/ V(1.4760), /*.u_g_factor=*/ -V(0.1647), /*.v_g_factor=*/ -V(0.5719), /*.u_b_factor=*/ V(1.8832) } }; -static const RGB2YUVParam RGB2YUV[3] = { +static const RGB2YUVParam RGB2YUV[4] = { // ITU-T T.871 (JPEG) {/*.y_shift=*/ 0, /*.matrix=*/ {{V(0.299), V(0.587), V(0.114)}, {-V(0.1687), -V(0.3313), V(0.5)}, {V(0.5), -V(0.4187), -V(0.0813)}}}, // ITU-R BT.601-7 {/*.y_shift=*/ 16, /*.matrix=*/ {{V(0.2568), V(0.5041), V(0.0979)}, {-V(0.1482), -V(0.291), V(0.4392)}, {V(0.4392), -V(0.3678), -V(0.0714)}}}, // ITU-R BT.709-6 - {/*.y_shift=*/ 16, /*.matrix=*/ {{V(0.1826), V(0.6142), V(0.062)}, {-V(0.1006), -V(0.3386), V(0.4392)}, {V(0.4392), -V(0.3989), -V(0.0403)}}} + {/*.y_shift=*/ 16, /*.matrix=*/ {{V(0.1826), V(0.6142), V(0.062)}, {-V(0.1006), -V(0.3386), V(0.4392)}, {V(0.4392), -V(0.3989), -V(0.0403)}}}, + // ITU-R BT.2020 10-bit full range + {/*.y_shift=*/ 0, /*.matrix=*/ {{V(0.2627), V(0.6780), V(0.0593)}, {-V(0.1395), -V(0.3600), V(0.4995)}, {V(0.4995), -V(0.4593), -V(0.0402)}}}, }; #ifdef _MSC_VER @@ -73,3 +78,4 @@ static const RGB2YUVParam RGB2YUV[3] = { #define RGB_FORMAT_BGRA 4 #define RGB_FORMAT_ARGB 5 #define RGB_FORMAT_ABGR 6 +#define RGB_FORMAT_XBGR2101010 7 diff --git a/src/video/yuv2rgb/yuv_rgb_std.c b/src/video/yuv2rgb/yuv_rgb_std.c index 4251b7f206f88..2641fdd1e6391 100644 --- a/src/video/yuv2rgb/yuv_rgb_std.c +++ b/src/video/yuv2rgb/yuv_rgb_std.c @@ -28,6 +28,19 @@ static uint8_t clampU8(int32_t v) return lut[((v+128*PRECISION_FACTOR)>>PRECISION)&511]; } +static uint16_t clamp10(int32_t v) +{ + v >>= PRECISION; + if (v < 0) { + return 0; + } else if (v > 1023) { + return 1023; + } else { + return (uint16_t)v; + } +} + +#define YUV_BITS 8 #define STD_FUNCTION_NAME yuv420_rgb565_std #define YUV_FORMAT YUV_FORMAT_420 @@ -119,6 +132,14 @@ static uint8_t clampU8(int32_t v) #define RGB_FORMAT RGB_FORMAT_ABGR #include "yuv_rgb_std_func.h" +#undef YUV_BITS +#define YUV_BITS 10 + +#define STD_FUNCTION_NAME yuvp010_xbgr2101010_std +#define YUV_FORMAT YUV_FORMAT_NV12 +#define RGB_FORMAT RGB_FORMAT_XBGR2101010 +#include "yuv_rgb_std_func.h" + void rgb24_yuv420_std( uint32_t width, uint32_t height, const uint8_t *RGB, uint32_t RGB_stride, diff --git a/src/video/yuv2rgb/yuv_rgb_std.h b/src/video/yuv2rgb/yuv_rgb_std.h index 5791b7aa217c8..c9f856ba98bd6 100644 --- a/src/video/yuv2rgb/yuv_rgb_std.h +++ b/src/video/yuv2rgb/yuv_rgb_std.h @@ -129,6 +129,12 @@ void yuvnv12_abgr_std( uint8_t *rgb, uint32_t rgb_stride, YCbCrType yuv_type); +void yuvp010_xbgr2101010_std( + uint32_t width, uint32_t height, + const uint16_t *y, const uint16_t *u, const uint16_t *v, uint32_t y_stride, uint32_t uv_stride, + uint8_t *rgb, uint32_t rgb_stride, + YCbCrType yuv_type); + // rgb to yuv, standard c implementation void rgb24_yuv420_std( uint32_t width, uint32_t height, diff --git a/src/video/yuv2rgb/yuv_rgb_std_func.h b/src/video/yuv2rgb/yuv_rgb_std_func.h index f359abae8b444..8091ea9ab7cf4 100644 --- a/src/video/yuv2rgb/yuv_rgb_std_func.h +++ b/src/video/yuv2rgb/yuv_rgb_std_func.h @@ -64,6 +64,16 @@ (((Uint32)clampU8(y_tmp+r_tmp)) << 0); \ rgb_ptr += 4; \ +#elif RGB_FORMAT == RGB_FORMAT_XBGR2101010 + +#define PACK_PIXEL(rgb_ptr) \ + *(Uint32 *)rgb_ptr = \ + 0xC0000000 | \ + (((Uint32)clamp10(y_tmp+b_tmp)) << 20) | \ + (((Uint32)clamp10(y_tmp+g_tmp)) << 10) | \ + (((Uint32)clamp10(y_tmp+r_tmp)) << 0); \ + rgb_ptr += 4; \ + #else #error PACK_PIXEL unimplemented #endif @@ -74,9 +84,25 @@ #pragma warning(disable : 6239) #endif +#undef YUV_TYPE +#if YUV_BITS > 8 +#define YUV_TYPE uint16_t +#else +#define YUV_TYPE uint8_t +#endif +#undef UV_OFFSET +#define UV_OFFSET (1 << ((YUV_BITS)-1)) + +#undef GET +#if YUV_BITS == 10 +#define GET(X) ((X) >> 6) +#else +#define GET(X) (X) +#endif + void STD_FUNCTION_NAME( uint32_t width, uint32_t height, - const uint8_t *Y, const uint8_t *U, const uint8_t *V, uint32_t Y_stride, uint32_t UV_stride, + const YUV_TYPE *Y, const YUV_TYPE *U, const YUV_TYPE *V, uint32_t Y_stride, uint32_t UV_stride, uint8_t *RGB, uint32_t RGB_stride, YCbCrType yuv_type) { @@ -98,29 +124,32 @@ void STD_FUNCTION_NAME( #define uv_y_sample_interval 2 #endif + Y_stride /= sizeof(YUV_TYPE); + UV_stride /= sizeof(YUV_TYPE); + uint32_t x, y; for(y=0; y<(height-(uv_y_sample_interval-1)); y+=uv_y_sample_interval) { - const uint8_t *y_ptr1=Y+y*Y_stride, + const YUV_TYPE *y_ptr1=Y+y*Y_stride, *u_ptr=U+(y/uv_y_sample_interval)*UV_stride, *v_ptr=V+(y/uv_y_sample_interval)*UV_stride; #if uv_y_sample_interval > 1 - const uint8_t *y_ptr2=Y+(y+1)*Y_stride; + const YUV_TYPE *y_ptr2=Y+(y+1)*Y_stride; #endif uint8_t *rgb_ptr1=RGB+y*RGB_stride; #if uv_y_sample_interval > 1 - uint8_t *rgb_ptr2=RGB+(y+1)*RGB_stride; + uint8_t *rgb_ptr2=RGB+(y+1)*RGB_stride; #endif for(x=0; x<(width-(uv_x_sample_interval-1)); x+=uv_x_sample_interval) { // Compute U and V contributions, common to the four pixels - int32_t u_tmp = ((*u_ptr)-128); - int32_t v_tmp = ((*v_ptr)-128); + int32_t u_tmp = (GET(*u_ptr)-UV_OFFSET); + int32_t v_tmp = (GET(*v_ptr)-UV_OFFSET); int32_t r_tmp = (v_tmp*param->v_r_factor); int32_t g_tmp = (u_tmp*param->u_g_factor + v_tmp*param->v_g_factor); @@ -128,17 +157,17 @@ void STD_FUNCTION_NAME( // Compute the Y contribution for each pixel - int32_t y_tmp = ((y_ptr1[0]-param->y_shift)*param->y_factor); + int32_t y_tmp = (GET(y_ptr1[0]-param->y_shift)*param->y_factor); PACK_PIXEL(rgb_ptr1); - y_tmp = ((y_ptr1[y_pixel_stride]-param->y_shift)*param->y_factor); + y_tmp = (GET(y_ptr1[y_pixel_stride]-param->y_shift)*param->y_factor); PACK_PIXEL(rgb_ptr1); #if uv_y_sample_interval > 1 - y_tmp = ((y_ptr2[0]-param->y_shift)*param->y_factor); + y_tmp = (GET(y_ptr2[0]-param->y_shift)*param->y_factor); PACK_PIXEL(rgb_ptr2); - y_tmp = ((y_ptr2[y_pixel_stride]-param->y_shift)*param->y_factor); + y_tmp = (GET(y_ptr2[y_pixel_stride]-param->y_shift)*param->y_factor); PACK_PIXEL(rgb_ptr2); #endif @@ -155,8 +184,8 @@ void STD_FUNCTION_NAME( { // Compute U and V contributions, common to the four pixels - int32_t u_tmp = ((*u_ptr)-128); - int32_t v_tmp = ((*v_ptr)-128); + int32_t u_tmp = (GET(*u_ptr)-UV_OFFSET); + int32_t v_tmp = (GET(*v_ptr)-UV_OFFSET); int32_t r_tmp = (v_tmp*param->v_r_factor); int32_t g_tmp = (u_tmp*param->u_g_factor + v_tmp*param->v_g_factor); @@ -164,11 +193,11 @@ void STD_FUNCTION_NAME( // Compute the Y contribution for each pixel - int32_t y_tmp = ((y_ptr1[0]-param->y_shift)*param->y_factor); + int32_t y_tmp = (GET(y_ptr1[0]-param->y_shift)*param->y_factor); PACK_PIXEL(rgb_ptr1); #if uv_y_sample_interval > 1 - y_tmp = ((y_ptr2[0]-param->y_shift)*param->y_factor); + y_tmp = (GET(y_ptr2[0]-param->y_shift)*param->y_factor); PACK_PIXEL(rgb_ptr2); #endif } @@ -177,7 +206,7 @@ void STD_FUNCTION_NAME( /* Catch the last line, if needed */ if (uv_y_sample_interval == 2 && y == (height-1)) { - const uint8_t *y_ptr1=Y+y*Y_stride, + const YUV_TYPE *y_ptr1=Y+y*Y_stride, *u_ptr=U+(y/uv_y_sample_interval)*UV_stride, *v_ptr=V+(y/uv_y_sample_interval)*UV_stride; @@ -187,8 +216,8 @@ void STD_FUNCTION_NAME( { // Compute U and V contributions, common to the four pixels - int32_t u_tmp = ((*u_ptr)-128); - int32_t v_tmp = ((*v_ptr)-128); + int32_t u_tmp = (GET(*u_ptr)-UV_OFFSET); + int32_t v_tmp = (GET(*v_ptr)-UV_OFFSET); int32_t r_tmp = (v_tmp*param->v_r_factor); int32_t g_tmp = (u_tmp*param->u_g_factor + v_tmp*param->v_g_factor); @@ -196,10 +225,10 @@ void STD_FUNCTION_NAME( // Compute the Y contribution for each pixel - int32_t y_tmp = ((y_ptr1[0]-param->y_shift)*param->y_factor); + int32_t y_tmp = (GET(y_ptr1[0]-param->y_shift)*param->y_factor); PACK_PIXEL(rgb_ptr1); - y_tmp = ((y_ptr1[y_pixel_stride]-param->y_shift)*param->y_factor); + y_tmp = (GET(y_ptr1[y_pixel_stride]-param->y_shift)*param->y_factor); PACK_PIXEL(rgb_ptr1); y_ptr1+=2*y_pixel_stride; @@ -212,8 +241,8 @@ void STD_FUNCTION_NAME( { // Compute U and V contributions, common to the four pixels - int32_t u_tmp = ((*u_ptr)-128); - int32_t v_tmp = ((*v_ptr)-128); + int32_t u_tmp = (GET(*u_ptr)-UV_OFFSET); + int32_t v_tmp = (GET(*v_ptr)-UV_OFFSET); int32_t r_tmp = (v_tmp*param->v_r_factor); int32_t g_tmp = (u_tmp*param->u_g_factor + v_tmp*param->v_g_factor); @@ -221,7 +250,7 @@ void STD_FUNCTION_NAME( // Compute the Y contribution for each pixel - int32_t y_tmp = ((y_ptr1[0]-param->y_shift)*param->y_factor); + int32_t y_tmp = (GET(y_ptr1[0]-param->y_shift)*param->y_factor); PACK_PIXEL(rgb_ptr1); } } diff --git a/test/testyuv.c b/test/testyuv.c index 2974164d673f0..e452466b1fbe3 100644 --- a/test/testyuv.c +++ b/test/testyuv.c @@ -15,8 +15,8 @@ #include "testyuv_cvt.h" #include "testutils.h" -/* 422 (YUY2, etc) formats are the largest */ -#define MAX_YUV_SURFACE_SIZE(W, H, P) (H * 4 * (W + P + 1) / 2) +/* 422 (YUY2, etc) and P010 formats are the largest */ +#define MAX_YUV_SURFACE_SIZE(W, H, P) ((H + 1) * ((W + 1) + P) * 4) /* Return true if the YUV format is packed pixels */ static SDL_bool is_packed_yuv_format(Uint32 format) @@ -65,9 +65,8 @@ static SDL_Surface *generate_test_pattern(int pattern_size) return pattern; } -static SDL_bool verify_yuv_data(Uint32 format, SDL_Colorspace colorspace, const Uint8 *yuv, int yuv_pitch, SDL_Surface *surface) +static SDL_bool verify_yuv_data(Uint32 format, SDL_Colorspace colorspace, SDL_PropertiesID props, const Uint8 *yuv, int yuv_pitch, SDL_Surface *surface, int tolerance) { - const int tolerance = 20; const int size = (surface->h * surface->pitch); Uint8 *rgb; SDL_bool result = SDL_FALSE; @@ -78,7 +77,7 @@ static SDL_bool verify_yuv_data(Uint32 format, SDL_Colorspace colorspace, const return SDL_FALSE; } - if (SDL_ConvertPixelsAndColorspace(surface->w, surface->h, format, colorspace, 0, yuv, yuv_pitch, surface->format->format, SDL_COLORSPACE_SRGB, 0, rgb, surface->pitch) == 0) { + if (SDL_ConvertPixelsAndColorspace(surface->w, surface->h, format, colorspace, props, yuv, yuv_pitch, surface->format->format, SDL_COLORSPACE_SRGB, 0, rgb, surface->pitch) == 0) { int x, y; result = SDL_TRUE; for (y = 0; y < surface->h; ++y) { @@ -124,6 +123,9 @@ static int run_automated_tests(int pattern_size, int extra_pitch) int yuv1_pitch, yuv2_pitch; YUV_CONVERSION_MODE mode; SDL_Colorspace colorspace; + SDL_PropertiesID props; + const int tight_tolerance = 20; + const int loose_tolerance = 333; int result = -1; if (!pattern || !yuv1 || !yuv2) { @@ -134,6 +136,10 @@ static int run_automated_tests(int pattern_size, int extra_pitch) mode = GetYUVConversionModeForResolution(pattern->w, pattern->h); colorspace = GetColorspaceForYUVConversionMode(mode); + /* All tests are being done with SDR content */ + props = SDL_CreateProperties(); + SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, 1.0f); + /* Verify conversion from YUV formats */ for (i = 0; i < SDL_arraysize(formats); ++i) { if (!ConvertRGBtoYUV(formats[i], pattern->pixels, pattern->pitch, yuv1, pattern->w, pattern->h, mode, 0, 100)) { @@ -141,7 +147,7 @@ static int run_automated_tests(int pattern_size, int extra_pitch) goto done; } yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w); - if (!verify_yuv_data(formats[i], colorspace, yuv1, yuv1_pitch, pattern)) { + if (!verify_yuv_data(formats[i], colorspace, props, yuv1, yuv1_pitch, pattern, tight_tolerance)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to RGB\n", SDL_GetPixelFormatName(formats[i])); goto done; } @@ -154,7 +160,7 @@ static int run_automated_tests(int pattern_size, int extra_pitch) SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError()); goto done; } - if (!verify_yuv_data(formats[i], colorspace, yuv1, yuv1_pitch, pattern)) { + if (!verify_yuv_data(formats[i], colorspace, props, yuv1, yuv1_pitch, pattern, tight_tolerance)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from RGB to %s\n", SDL_GetPixelFormatName(formats[i])); goto done; } @@ -173,7 +179,7 @@ static int run_automated_tests(int pattern_size, int extra_pitch) SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError()); goto done; } - if (!verify_yuv_data(formats[j], colorspace, yuv2, yuv2_pitch, pattern)) { + if (!verify_yuv_data(formats[j], colorspace, props, yuv2, yuv2_pitch, pattern, tight_tolerance)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j])); goto done; } @@ -198,13 +204,37 @@ static int run_automated_tests(int pattern_size, int extra_pitch) SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError()); goto done; } - if (!verify_yuv_data(formats[j], colorspace, yuv1, yuv2_pitch, pattern)) { + if (!verify_yuv_data(formats[j], colorspace, props, yuv1, yuv2_pitch, pattern, tight_tolerance)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j])); goto done; } } } + /* Verify round trip through BT.2020 */ + colorspace = SDL_COLORSPACE_BT2020_FULL; + if (!ConvertRGBtoYUV(SDL_PIXELFORMAT_P010, pattern->pixels, pattern->pitch, yuv1, pattern->w, pattern->h, YUV_CONVERSION_BT2020, 0, 100)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ConvertRGBtoYUV() doesn't support converting to %s\n", SDL_GetPixelFormatName(SDL_PIXELFORMAT_P010)); + goto done; + } + yuv1_pitch = CalculateYUVPitch(SDL_PIXELFORMAT_P010, pattern->w); + if (!verify_yuv_data(SDL_PIXELFORMAT_P010, colorspace, props, yuv1, yuv1_pitch, pattern, tight_tolerance)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to RGB\n", SDL_GetPixelFormatName(SDL_PIXELFORMAT_P010)); + goto done; + } + + /* The pitch needs to be Uint16 aligned for P010 pixels */ + yuv1_pitch = CalculateYUVPitch(SDL_PIXELFORMAT_P010, pattern->w) + ((extra_pitch + 1) & ~1); + if (SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, SDL_PIXELFORMAT_P010, colorspace, props, yuv1, yuv1_pitch) < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(SDL_PIXELFORMAT_P010), SDL_GetError()); + goto done; + } + /* Going through XRGB2101010 format during P010 conversion is slightly lossy, so use looser tolerance here */ + if (!verify_yuv_data(SDL_PIXELFORMAT_P010, colorspace, props, yuv1, yuv1_pitch, pattern, loose_tolerance)) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from RGB to %s\n", SDL_GetPixelFormatName(SDL_PIXELFORMAT_P010)); + goto done; + } + result = 0; done: @@ -222,6 +252,8 @@ int main(int argc, char **argv) int pattern_size; int extra_pitch; } automated_test_params[] = { + /* Test: single pixel */ + { SDL_FALSE, 1, 0 }, /* Test: even width and height */ { SDL_FALSE, 2, 0 }, { SDL_FALSE, 4, 0 }, @@ -258,9 +290,10 @@ int main(int argc, char **argv) const char *yuv_mode_name; Uint32 yuv_format = SDL_PIXELFORMAT_YV12; const char *yuv_format_name; + SDL_Colorspace yuv_colorspace; Uint32 rgb_format = SDL_PIXELFORMAT_RGBX8888; + SDL_Colorspace rgb_colorspace = SDL_COLORSPACE_SRGB; SDL_PropertiesID props; - SDL_Colorspace colorspace; SDL_bool monochrome = SDL_FALSE; int luminance = 100; int current = 0; @@ -295,6 +328,9 @@ int main(int argc, char **argv) } else if (SDL_strcmp(argv[i], "--bt709") == 0) { SetYUVConversionMode(YUV_CONVERSION_BT709); consumed = 1; + } else if (SDL_strcmp(argv[i], "--bt2020") == 0) { + SetYUVConversionMode(YUV_CONVERSION_BT2020); + consumed = 1; } else if (SDL_strcmp(argv[i], "--auto") == 0) { SetYUVConversionMode(YUV_CONVERSION_AUTOMATIC); consumed = 1; @@ -356,7 +392,7 @@ int main(int argc, char **argv) } if (consumed <= 0) { static const char *options[] = { - "[--jpeg|--bt601|--bt709|--auto]", + "[--jpeg|--bt601|--bt709|--bt2020|--auto]", "[--yv12|--iyuv|--yuy2|--uyvy|--yvyu|--nv12|--nv21]", "[--rgb555|--rgb565|--rgb24|--argb|--abgr|--rgba|--bgra]", "[--monochrome] [--luminance N%]", @@ -405,11 +441,17 @@ int main(int argc, char **argv) case YUV_CONVERSION_BT709: yuv_mode_name = "BT.709"; break; + case YUV_CONVERSION_BT2020: + yuv_mode_name = "BT.2020"; + yuv_format = SDL_PIXELFORMAT_P010; + rgb_format = SDL_PIXELFORMAT_XBGR2101010; + rgb_colorspace = SDL_COLORSPACE_HDR10; + break; default: yuv_mode_name = "UNKNOWN"; break; } - colorspace = GetColorspaceForYUVConversionMode(yuv_mode); + yuv_colorspace = GetColorspaceForYUVConversionMode(yuv_mode); raw_yuv = SDL_calloc(1, MAX_YUV_SURFACE_SIZE(original->w, original->h, 0)); ConvertRGBtoYUV(yuv_format, original->pixels, original->pitch, raw_yuv, original->w, original->h, yuv_mode, monochrome, luminance); @@ -421,9 +463,13 @@ int main(int argc, char **argv) return 3; } + /* All tests are being done with SDR content */ + props = SDL_GetSurfaceProperties(converted); + SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, 1.0f); + then = SDL_GetTicks(); for (i = 0; i < iterations; ++i) { - SDL_ConvertPixelsAndColorspace(original->w, original->h, yuv_format, colorspace, 0, raw_yuv, pitch, rgb_format, SDL_COLORSPACE_SRGB, 0, converted->pixels, converted->pitch); + SDL_ConvertPixelsAndColorspace(original->w, original->h, yuv_format, yuv_colorspace, props, raw_yuv, pitch, rgb_format, rgb_colorspace, props, converted->pixels, converted->pitch); } now = SDL_GetTicks(); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%d iterations in %" SDL_PRIu64 " ms, %.2fms each\n", iterations, (now - then), (float)(now - then) / iterations); @@ -443,7 +489,8 @@ int main(int argc, char **argv) output[0] = SDL_CreateTextureFromSurface(renderer, original); output[1] = SDL_CreateTextureFromSurface(renderer, converted); props = SDL_CreateProperties(); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, colorspace); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, yuv_colorspace); + SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, 1.0f); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, yuv_format); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STREAMING); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, original->w); diff --git a/test/testyuv_cvt.c b/test/testyuv_cvt.c index c165b3218d6e0..08e8c6ff76108 100644 --- a/test/testyuv_cvt.c +++ b/test/testyuv_cvt.c @@ -70,6 +70,9 @@ SDL_Colorspace GetColorspaceForYUVConversionMode(YUV_CONVERSION_MODE mode) SDL_MATRIX_COEFFICIENTS_BT709, SDL_CHROMA_LOCATION_CENTER); break; + case YUV_CONVERSION_BT2020: + colorspace = SDL_COLORSPACE_BT2020_FULL; + break; default: colorspace = SDL_COLORSPACE_UNKNOWN; break; @@ -82,7 +85,54 @@ static float clip3(float x, float y, float z) return (z < x) ? x : ((z > y) ? y : z); } -static void RGBtoYUV(const Uint8 *rgb, int *yuv, YUV_CONVERSION_MODE mode, int monochrome, int luminance) +static float sRGBtoNits(float v) +{ + /* Normalize from 0..255 */ + v /= 255.0f; + + /* Convert from sRGB */ + v = v <= 0.04045f ? (v / 12.92f) : SDL_powf(((v + 0.055f) / 1.055f), 2.4f); + + /* Convert to nits, using a default SDR whitepoint of 203 */ + v *= 203.0f; + + return v; +} + +static float PQfromNits(float v) +{ + const float c1 = 0.8359375f; + const float c2 = 18.8515625f; + const float c3 = 18.6875f; + const float m1 = 0.1593017578125f; + const float m2 = 78.84375f; + + float y = SDL_clamp(v / 10000.0f, 0.0f, 1.0f); + float num = c1 + c2 * SDL_powf(y, m1); + float den = 1.0f + c3 * SDL_powf(y, m1); + return SDL_powf(num / den, m2); +} + +void ConvertRec709toRec2020(float *fR, float *fG, float *fB) +{ + static const float mat709to2020[] = { + 0.627404f, 0.329283f, 0.043313f, + 0.069097f, 0.919541f, 0.011362f, + 0.016391f, 0.088013f, 0.895595f, + }; + const float *matrix = mat709to2020; + float v[3]; + + v[0] = *fR; + v[1] = *fG; + v[2] = *fB; + + *fR = matrix[0 * 3 + 0] * v[0] + matrix[0 * 3 + 1] * v[1] + matrix[0 * 3 + 2] * v[2]; + *fG = matrix[1 * 3 + 0] * v[0] + matrix[1 * 3 + 1] * v[1] + matrix[1 * 3 + 2] * v[2]; + *fB = matrix[2 * 3 + 0] * v[0] + matrix[2 * 3 + 1] * v[1] + matrix[2 * 3 + 2] * v[2]; +} + +static void RGBtoYUV(const Uint8 *rgb, int rgb_bits, int *yuv, int yuv_bits, YUV_CONVERSION_MODE mode, int monochrome, int luminance) { /** * This formula is from Microsoft's documentation: @@ -93,56 +143,82 @@ static void RGBtoYUV(const Uint8 *rgb, int *yuv, YUV_CONVERSION_MODE mode, int m * V = clip3(0, (2^M)-1, floor(2^(M-8) * (112*(R-L) / ((1-Kr)*S) + 128) + 0.5)); */ SDL_bool studio_RGB = SDL_FALSE; - SDL_bool studio_YUV = SDL_FALSE; + SDL_bool full_range_YUV = SDL_FALSE; float N, M, S, Z, R, G, B, L, Kr, Kb, Y, U, V; - N = 8.0f; /* 8 bit RGB */ - M = 8.0f; /* 8 bit YUV */ - if (mode == YUV_CONVERSION_BT709) { - /* BT.709 */ - Kr = 0.2126f; - Kb = 0.0722f; - } else { + N = (float)rgb_bits; + M = (float)yuv_bits; + switch (mode) { + case YUV_CONVERSION_JPEG: + case YUV_CONVERSION_BT601: /* BT.601 */ Kr = 0.299f; Kb = 0.114f; + break; + case YUV_CONVERSION_BT709: + /* BT.709 */ + Kr = 0.2126f; + Kb = 0.0722f; + break; + case YUV_CONVERSION_BT2020: + /* BT.2020 */ + Kr = 0.2627f; + Kb = 0.0593f; + break; + default: + /* Invalid */ + Kr = 1.0f; + Kb = 1.0f; + break; } - if (mode == YUV_CONVERSION_JPEG) { - studio_YUV = SDL_FALSE; - } else { - studio_YUV = SDL_TRUE; + R = rgb[0]; + G = rgb[1]; + B = rgb[2]; + + if (mode == YUV_CONVERSION_JPEG || mode == YUV_CONVERSION_BT2020) { + full_range_YUV = SDL_TRUE; } - if (studio_RGB || !studio_YUV) { + if (mode == YUV_CONVERSION_BT2020) { + /* Input is sRGB, need to convert to BT.2020 PQ YUV */ + R = sRGBtoNits(R); + G = sRGBtoNits(G); + B = sRGBtoNits(B); + ConvertRec709toRec2020(&R, &G, &B); + R = PQfromNits(R); + G = PQfromNits(G); + B = PQfromNits(B); + S = 1.0f; + Z = 0.0f; + } else if (studio_RGB) { S = 219.0f * SDL_powf(2.0f, N - 8); Z = 16.0f * SDL_powf(2.0f, N - 8); } else { S = 255.0f; Z = 0.0f; } - R = rgb[0]; - G = rgb[1]; - B = rgb[2]; L = Kr * R + Kb * B + (1 - Kr - Kb) * G; - Y = SDL_floorf(SDL_powf(2.0f, (M - 8)) * (219.0f * (L - Z) / S + 16) + 0.5f); - U = clip3(0, SDL_powf(2.0f, M) - 1, SDL_floorf(SDL_powf(2.0f, (M - 8)) * (112.0f * (B - L) / ((1.0f - Kb) * S) + 128) + 0.5f)); - V = clip3(0, SDL_powf(2.0f, M) - 1, SDL_floorf(SDL_powf(2.0f, (M - 8)) * (112.0f * (R - L) / ((1.0f - Kr) * S) + 128) + 0.5f)); + if (monochrome) { + R = L; + B = L; + } + if (full_range_YUV) { + Y = SDL_floorf((SDL_powf(2.0f, M) - 1) * ((L - Z) / S) + 0.5f); + U = clip3(0, SDL_powf(2.0f, M) - 1, SDL_floorf((SDL_powf(2.0f, M) / 2 - 1) * ((B - L) / ((1.0f - Kb) * S)) + SDL_powf(2.0f, M) / 2 + 0.5f)); + V = clip3(0, SDL_powf(2.0f, M) - 1, SDL_floorf((SDL_powf(2.0f, M) / 2 - 1) * ((R - L) / ((1.0f - Kr) * S)) + SDL_powf(2.0f, M) / 2 + 0.5f)); + } else { + Y = SDL_floorf(SDL_powf(2.0f, (M - 8)) * (219.0f * (L - Z) / S + 16) + 0.5f); + U = clip3(0, SDL_powf(2.0f, M) - 1, SDL_floorf(SDL_powf(2.0f, (M - 8)) * (112.0f * (B - L) / ((1.0f - Kb) * S) + 128) + 0.5f)); + V = clip3(0, SDL_powf(2.0f, M) - 1, SDL_floorf(SDL_powf(2.0f, (M - 8)) * (112.0f * (R - L) / ((1.0f - Kr) * S) + 128) + 0.5f)); + } yuv[0] = (int)Y; yuv[1] = (int)U; yuv[2] = (int)V; - if (monochrome) { - yuv[1] = 128; - yuv[2] = 128; - } - if (luminance != 100) { - yuv[0] = (int)SDL_roundf(yuv[0] * (luminance / 100.0f)); - if (yuv[0] > 255) { - yuv[0] = 255; - } + yuv[0] = (int)clip3(0, SDL_powf(2.0f, M) - 1, SDL_roundf(yuv[0] * (luminance / 100.0f))); } } @@ -188,19 +264,19 @@ static void ConvertRGBtoPlanar2x2(Uint32 format, Uint8 *src, int pitch, Uint8 *o for (y = 0; y < (h - 1); y += 2) { for (x = 0; x < (w - 1); x += 2) { - RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance); + RGBtoYUV(rgb1, 8, yuv[0], 8, mode, monochrome, luminance); rgb1 += 3; *Y1++ = (Uint8)yuv[0][0]; - RGBtoYUV(rgb1, yuv[1], mode, monochrome, luminance); + RGBtoYUV(rgb1, 8, yuv[1], 8, mode, monochrome, luminance); rgb1 += 3; *Y1++ = (Uint8)yuv[1][0]; - RGBtoYUV(rgb2, yuv[2], mode, monochrome, luminance); + RGBtoYUV(rgb2, 8, yuv[2], 8, mode, monochrome, luminance); rgb2 += 3; *Y2++ = (Uint8)yuv[2][0]; - RGBtoYUV(rgb2, yuv[3], mode, monochrome, luminance); + RGBtoYUV(rgb2, 8, yuv[3], 8, mode, monochrome, luminance); rgb2 += 3; *Y2++ = (Uint8)yuv[3][0]; @@ -212,11 +288,11 @@ static void ConvertRGBtoPlanar2x2(Uint32 format, Uint8 *src, int pitch, Uint8 *o } /* Last column */ if (x == (w - 1)) { - RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance); + RGBtoYUV(rgb1, 8, yuv[0], 8, mode, monochrome, luminance); rgb1 += 3; *Y1++ = (Uint8)yuv[0][0]; - RGBtoYUV(rgb2, yuv[2], mode, monochrome, luminance); + RGBtoYUV(rgb2, 8, yuv[2], 8, mode, monochrome, luminance); rgb2 += 3; *Y2++ = (Uint8)yuv[2][0]; @@ -234,11 +310,11 @@ static void ConvertRGBtoPlanar2x2(Uint32 format, Uint8 *src, int pitch, Uint8 *o /* Last row */ if (y == (h - 1)) { for (x = 0; x < (w - 1); x += 2) { - RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance); + RGBtoYUV(rgb1, 8, yuv[0], 8, mode, monochrome, luminance); rgb1 += 3; *Y1++ = (Uint8)yuv[0][0]; - RGBtoYUV(rgb1, yuv[1], mode, monochrome, luminance); + RGBtoYUV(rgb1, 8, yuv[1], 8, mode, monochrome, luminance); rgb1 += 3; *Y1++ = (Uint8)yuv[1][0]; @@ -250,7 +326,7 @@ static void ConvertRGBtoPlanar2x2(Uint32 format, Uint8 *src, int pitch, Uint8 *o } /* Last column */ if (x == (w - 1)) { - RGBtoYUV(rgb1, yuv[0], mode, monochrome, luminance); + RGBtoYUV(rgb1, 8, yuv[0], 8, mode, monochrome, luminance); *Y1++ = (Uint8)yuv[0][0]; *U = (Uint8)yuv[0][1]; @@ -262,6 +338,112 @@ static void ConvertRGBtoPlanar2x2(Uint32 format, Uint8 *src, int pitch, Uint8 *o } } +static Uint16 Pack10to16(int v) +{ + return (Uint16)(v << 6); +} + +static void ConvertRGBtoPlanar2x2_P010(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, YUV_CONVERSION_MODE mode, int monochrome, int luminance) +{ + int x, y; + int yuv[4][3]; + Uint16 *Y1, *Y2, *U, *V; + Uint8 *rgb1, *rgb2; + int rgb_row_advance = (pitch - w * 3) + pitch; + int UV_advance; + + rgb1 = src; + rgb2 = src + pitch; + + Y1 = (Uint16 *)out; + Y2 = Y1 + w; + switch (format) { + case SDL_PIXELFORMAT_P010: + U = (Y1 + h * w); + V = U + 1; + UV_advance = 2; + break; + default: + SDL_assert(!"Unsupported planar YUV format"); + return; + } + + for (y = 0; y < (h - 1); y += 2) { + for (x = 0; x < (w - 1); x += 2) { + RGBtoYUV(rgb1, 8, yuv[0], 10, mode, monochrome, luminance); + rgb1 += 3; + *Y1++ = Pack10to16(yuv[0][0]); + + RGBtoYUV(rgb1, 8, yuv[1], 10, mode, monochrome, luminance); + rgb1 += 3; + *Y1++ = Pack10to16(yuv[1][0]); + + RGBtoYUV(rgb2, 8, yuv[2], 10, mode, monochrome, luminance); + rgb2 += 3; + *Y2++ = Pack10to16(yuv[2][0]); + + RGBtoYUV(rgb2, 8, yuv[3], 10, mode, monochrome, luminance); + rgb2 += 3; + *Y2++ = Pack10to16(yuv[3][0]); + + *U = Pack10to16((int)SDL_floorf((yuv[0][1] + yuv[1][1] + yuv[2][1] + yuv[3][1]) / 4.0f + 0.5f)); + U += UV_advance; + + *V = Pack10to16((int)SDL_floorf((yuv[0][2] + yuv[1][2] + yuv[2][2] + yuv[3][2]) / 4.0f + 0.5f)); + V += UV_advance; + } + /* Last column */ + if (x == (w - 1)) { + RGBtoYUV(rgb1, 8, yuv[0], 10, mode, monochrome, luminance); + rgb1 += 3; + *Y1++ = Pack10to16(yuv[0][0]); + + RGBtoYUV(rgb2, 8, yuv[2], 10, mode, monochrome, luminance); + rgb2 += 3; + *Y2++ = Pack10to16(yuv[2][0]); + + *U = Pack10to16((int)SDL_floorf((yuv[0][1] + yuv[2][1]) / 2.0f + 0.5f)); + U += UV_advance; + + *V = Pack10to16((int)SDL_floorf((yuv[0][2] + yuv[2][2]) / 2.0f + 0.5f)); + V += UV_advance; + } + Y1 += w; + Y2 += w; + rgb1 += rgb_row_advance; + rgb2 += rgb_row_advance; + } + /* Last row */ + if (y == (h - 1)) { + for (x = 0; x < (w - 1); x += 2) { + RGBtoYUV(rgb1, 8, yuv[0], 10, mode, monochrome, luminance); + rgb1 += 3; + *Y1++ = Pack10to16(yuv[0][0]); + + RGBtoYUV(rgb1, 8, yuv[1], 10, mode, monochrome, luminance); + rgb1 += 3; + *Y1++ = Pack10to16(yuv[1][0]); + + *U = Pack10to16((int)SDL_floorf((yuv[0][1] + yuv[1][1]) / 2.0f + 0.5f)); + U += UV_advance; + + *V = Pack10to16((int)SDL_floorf((yuv[0][2] + yuv[1][2]) / 2.0f + 0.5f)); + V += UV_advance; + } + /* Last column */ + if (x == (w - 1)) { + RGBtoYUV(rgb1, 8, yuv[0], 10, mode, monochrome, luminance); + *Y1++ = Pack10to16(yuv[0][0]); + + *U = Pack10to16(yuv[0][1]); + U += UV_advance; + + *V = Pack10to16(yuv[0][2]); + V += UV_advance; + } + } +} + static void ConvertRGBtoPacked4(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, YUV_CONVERSION_MODE mode, int monochrome, int luminance) { int x, y; @@ -298,12 +480,12 @@ static void ConvertRGBtoPacked4(Uint32 format, Uint8 *src, int pitch, Uint8 *out for (y = 0; y < h; ++y) { for (x = 0; x < (w - 1); x += 2) { - RGBtoYUV(rgb, yuv[0], mode, monochrome, luminance); + RGBtoYUV(rgb, 8, yuv[0], 8, mode, monochrome, luminance); rgb += 3; *Y1 = (Uint8)yuv[0][0]; Y1 += 4; - RGBtoYUV(rgb, yuv[1], mode, monochrome, luminance); + RGBtoYUV(rgb, 8, yuv[1], 8, mode, monochrome, luminance); rgb += 3; *Y2 = (Uint8)yuv[1][0]; Y2 += 4; @@ -316,7 +498,7 @@ static void ConvertRGBtoPacked4(Uint32 format, Uint8 *src, int pitch, Uint8 *out } /* Last column */ if (x == (w - 1)) { - RGBtoYUV(rgb, yuv[0], mode, monochrome, luminance); + RGBtoYUV(rgb, 8, yuv[0], 8, mode, monochrome, luminance); rgb += 3; *Y2 = *Y1 = (Uint8)yuv[0][0]; Y1 += 4; @@ -335,6 +517,9 @@ static void ConvertRGBtoPacked4(Uint32 format, Uint8 *src, int pitch, Uint8 *out SDL_bool ConvertRGBtoYUV(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w, int h, YUV_CONVERSION_MODE mode, int monochrome, int luminance) { switch (format) { + case SDL_PIXELFORMAT_P010: + ConvertRGBtoPlanar2x2_P010(format, src, pitch, out, w, h, mode, monochrome, luminance); + return SDL_TRUE; case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_NV12: @@ -354,6 +539,8 @@ SDL_bool ConvertRGBtoYUV(Uint32 format, Uint8 *src, int pitch, Uint8 *out, int w int CalculateYUVPitch(Uint32 format, int width) { switch (format) { + case SDL_PIXELFORMAT_P010: + return width * 2; case SDL_PIXELFORMAT_YV12: case SDL_PIXELFORMAT_IYUV: case SDL_PIXELFORMAT_NV12: diff --git a/test/testyuv_cvt.h b/test/testyuv_cvt.h index c8962142bcb8a..36917b720e119 100644 --- a/test/testyuv_cvt.h +++ b/test/testyuv_cvt.h @@ -17,6 +17,7 @@ typedef enum YUV_CONVERSION_JPEG, /**< Full range JPEG */ YUV_CONVERSION_BT601, /**< BT.601 (the default) */ YUV_CONVERSION_BT709, /**< BT.709 */ + YUV_CONVERSION_BT2020, /**< BT.2020 */ YUV_CONVERSION_AUTOMATIC /**< BT.601 for SD content, BT.709 for HD content */ } YUV_CONVERSION_MODE; From 2bedd7f02ec8ee98044db4f14241d7e13a47042b Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 1 Mar 2024 16:22:10 -0800 Subject: [PATCH 161/220] Fixed pitch alignment when updating SDL_PIXELFORMAT_P010 textures --- src/render/direct3d11/SDL_render_d3d11.c | 9 +++++++-- src/render/direct3d12/SDL_render_d3d12.c | 7 ++++++- src/render/vulkan/SDL_render_vulkan.c | 12 +++++++++--- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index 2d6841fae8e10..cef5c816a4371 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -1549,8 +1549,13 @@ static int D3D11_UpdateTextureInternal(D3D11_RenderData *rendererData, ID3D11Tex stagingTextureDesc.Format == DXGI_FORMAT_P010) { /* Copy the UV plane as well */ h = (h + 1) / 2; - length = (length + 1) & ~1; - pitch = (pitch + 1) & ~1; + if (stagingTextureDesc.Format == DXGI_FORMAT_P010) { + length = (length + 3) & ~3; + pitch = (pitch + 3) & ~3; + } else { + length = (length + 1) & ~1; + pitch = (pitch + 1) & ~1; + } dst = (Uint8 *)textureMemory.pData + stagingTextureDesc.Height * textureMemory.RowPitch; for (row = 0; row < h; ++row) { SDL_memcpy(dst, src, length); diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index 470293a1bf7a9..e6b84bf9a5e43 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -1975,7 +1975,12 @@ static int D3D12_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, /* Skip to the correct offset into the next texture */ srcPixels = (const void *)((const Uint8 *)srcPixels + rect->h * srcPitch); - if (D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 1, rect->x, rect->y, (rect->w + 1) & ~1, (rect->h + 1) & ~1, srcPixels, (srcPitch + 1) & ~1, &textureData->mainResourceState) < 0) { + if (texture->format == SDL_PIXELFORMAT_P010) { + srcPitch = (srcPitch + 3) & ~3; + } else { + srcPitch = (srcPitch + 1) & ~1; + } + if (D3D12_UpdateTextureInternal(rendererData, textureData->mainTexture, 1, rect->x, rect->y, (rect->w + 1) & ~1, (rect->h + 1) & ~1, srcPixels, srcPitch, &textureData->mainResourceState) < 0) { return -1; } } diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index f38f3f3a65658..b1f55d0741065 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -187,7 +187,7 @@ typedef struct float scRGB_output; float input_type; float color_scale; - float unused_pad0; + float unused_pad0; float tonemap_method; float tonemap_factor1; @@ -2779,8 +2779,14 @@ static int VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, // NV12/NV21 data else if (numPlanes == 2) { - if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 1, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, (srcPitch + 1) & ~1, &textureData->mainImage.imageLayout) < 0) { - return -1; + if (texture->format == SDL_PIXELFORMAT_P010) { + srcPitch = (srcPitch + 3) & ~3; + } else { + srcPitch = (srcPitch + 1) & ~1; + } + + if (VULKAN_UpdateTextureInternal(rendererData, textureData->mainImage.image, textureData->mainImage.format, 1, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, srcPixels, srcPitch, &textureData->mainImage.imageLayout) < 0) { + return -1; } } #endif From 2bc2840de5048fdd376d6660395ef41a4276a6bb Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 1 Mar 2024 20:23:20 -0800 Subject: [PATCH 162/220] vulkan: VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 is a 2-plane format --- src/render/vulkan/SDL_render_vulkan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index b1f55d0741065..fcce43c198c75 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -379,6 +379,7 @@ Uint32 VULKAN_VkFormatGetNumPlanes(VkFormat vkFormat) case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM: return 3; case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM: + case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16: return 2; default: return 1; From 8d023f98698283087b3ca6d90adb29c396c2538a Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 2 Mar 2024 09:56:29 -0800 Subject: [PATCH 163/220] Updated documentation for new property parameters --- include/SDL3/SDL_surface.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index 2dce9a64b5d7a..42c3aa0c717fd 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -741,6 +741,7 @@ extern DECLSPEC SDL_Surface *SDLCALL SDL_ConvertSurfaceFormat(SDL_Surface *surfa * \param surface the existing SDL_Surface structure to convert * \param pixel_format the new pixel format * \param colorspace the new colorspace + * \param props an SDL_PropertiesID with additional color properties, or 0 * \returns the new SDL_Surface structure that is created or NULL if it fails; * call SDL_GetError() for more information. * @@ -779,11 +780,13 @@ extern DECLSPEC int SDLCALL SDL_ConvertPixels(int width, int height, Uint32 src_ * \param src_format an SDL_PixelFormatEnum value of the `src` pixels format * \param src_colorspace an SDL_ColorSpace value describing the colorspace of * the `src` pixels + * \param src_properties an SDL_PropertiesID with additional source color properties, or 0 * \param src a pointer to the source pixels * \param src_pitch the pitch of the source pixels, in bytes * \param dst_format an SDL_PixelFormatEnum value of the `dst` pixels format * \param dst_colorspace an SDL_ColorSpace value describing the colorspace of * the `dst` pixels + * \param dst_properties an SDL_PropertiesID with additional destination color properties, or 0 * \param dst a pointer to be filled in with new pixel data * \param dst_pitch the pitch of the destination pixels, in bytes * \returns 0 on success or a negative error code on failure; call From 8c015cd3b685f285744af26311575f5431b11971 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Sat, 2 Mar 2024 18:02:26 +0000 Subject: [PATCH 164/220] Sync SDL3 wiki -> header --- include/SDL3/SDL_surface.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index 42c3aa0c717fd..5c8184e44fa57 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -780,13 +780,15 @@ extern DECLSPEC int SDLCALL SDL_ConvertPixels(int width, int height, Uint32 src_ * \param src_format an SDL_PixelFormatEnum value of the `src` pixels format * \param src_colorspace an SDL_ColorSpace value describing the colorspace of * the `src` pixels - * \param src_properties an SDL_PropertiesID with additional source color properties, or 0 + * \param src_properties an SDL_PropertiesID with additional source color + * properties, or 0 * \param src a pointer to the source pixels * \param src_pitch the pitch of the source pixels, in bytes * \param dst_format an SDL_PixelFormatEnum value of the `dst` pixels format * \param dst_colorspace an SDL_ColorSpace value describing the colorspace of * the `dst` pixels - * \param dst_properties an SDL_PropertiesID with additional destination color properties, or 0 + * \param dst_properties an SDL_PropertiesID with additional destination color + * properties, or 0 * \param dst a pointer to be filled in with new pixel data * \param dst_pitch the pitch of the destination pixels, in bytes * \returns 0 on success or a negative error code on failure; call From e1e5d33420f8069469aaf21e61fe58ef37c2e7db Mon Sep 17 00:00:00 2001 From: Jade Macho Date: Fri, 1 Mar 2024 13:04:22 +0100 Subject: [PATCH 165/220] GDK: Partially revert 2670eb44afec9311ee8fbec447703c427db1e1c8 MsgWaitForMultipleObjects is desktop-only. --- src/video/windows/SDL_windowsevents.c | 32 +++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c index 35d0ddb11bbbc..e8cb407380429 100644 --- a/src/video/windows/SDL_windowsevents.c +++ b/src/video/windows/SDL_windowsevents.c @@ -1687,6 +1687,7 @@ void SDL_SetWindowsMessageHook(SDL_WindowsMessageHook callback, void *userdata) int WIN_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) { if (g_WindowsEnableMessageLoop) { +#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) DWORD timeout, ret; timeout = timeoutNS < 0 ? INFINITE : (DWORD)SDL_NS_TO_MS(timeoutNS); ret = MsgWaitForMultipleObjects(0, NULL, FALSE, timeout, QS_ALLINPUT); @@ -1695,6 +1696,37 @@ int WIN_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS) } else { return 0; } +#else + /* MsgWaitForMultipleObjects is desktop-only. */ + MSG msg; + BOOL message_result; + UINT_PTR timer_id = 0; + if (timeoutNS > 0) { + timer_id = SetTimer(NULL, 0, (UINT)SDL_NS_TO_MS(timeoutNS), NULL); + message_result = GetMessage(&msg, 0, 0, 0); + KillTimer(NULL, timer_id); + } else if (timeoutNS == 0) { + message_result = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); + } else { + message_result = GetMessage(&msg, 0, 0, 0); + } + if (message_result) { + if (msg.message == WM_TIMER && !msg.hwnd && msg.wParam == timer_id) { + return 0; + } + if (g_WindowsMessageHook) { + if (!g_WindowsMessageHook(g_WindowsMessageHookData, &msg)) { + return 1; + } + } + /* Always translate the message in case it's a non-SDL window (e.g. with Qt integration) */ + TranslateMessage(&msg); + DispatchMessage(&msg); + return 1; + } else { + return 0; + } +#endif /*!defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)*/ } else { /* Fail the wait so the caller falls back to polling */ return -1; From bcbede31ce5fd263c4b7f8aa2ab1e2b32e59212c Mon Sep 17 00:00:00 2001 From: Jade Macho Date: Fri, 1 Mar 2024 15:39:21 +0100 Subject: [PATCH 166/220] GDK: SDL_video_capture.c is now SDL_camera.c, thus remove from proj --- VisualC-GDK/SDL/SDL.vcxproj | 1 - 1 file changed, 1 deletion(-) diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index 76ea691d79100..283cc8cb5945b 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj +++ b/VisualC-GDK/SDL/SDL.vcxproj @@ -814,7 +814,6 @@ - From 23ace600fcfade74f6bec2f55a92cf29a32a3694 Mon Sep 17 00:00:00 2001 From: Jade Macho Date: Fri, 1 Mar 2024 15:50:57 +0100 Subject: [PATCH 167/220] GDK: Remove old shader sources, unify shader compilation .bats --- .gitignore | 3 +- VisualC-GDK/SDL/SDL.vcxproj | 8 +- .../shaders/D3D12_PixelShader_Colors.hlsl | 19 ---- .../shaders/D3D12_PixelShader_NV12_BT601.hlsl | 43 -------- .../shaders/D3D12_PixelShader_NV12_BT709.hlsl | 43 -------- .../shaders/D3D12_PixelShader_NV12_JPEG.hlsl | 43 -------- .../shaders/D3D12_PixelShader_NV21_BT601.hlsl | 43 -------- .../shaders/D3D12_PixelShader_NV21_BT709.hlsl | 43 -------- .../shaders/D3D12_PixelShader_NV21_JPEG.hlsl | 43 -------- .../shaders/D3D12_PixelShader_Textures.hlsl | 24 ---- .../shaders/D3D12_PixelShader_YUV_BT601.hlsl | 46 -------- .../shaders/D3D12_PixelShader_YUV_BT709.hlsl | 46 -------- .../shaders/D3D12_PixelShader_YUV_JPEG.hlsl | 46 -------- VisualC-GDK/shaders/D3D12_VertexShader.hlsl | 95 ---------------- VisualC-GDK/shaders/buildshaders.bat | 35 ------ .../direct3d12/SDL_shaders_d3d12_xboxone.cpp | 103 ++++++++--------- .../SDL_shaders_d3d12_xboxseries.cpp | 104 ++++++++---------- src/render/direct3d12/compile_shaders.bat | 28 +++-- .../direct3d12/compile_shaders_xbox.bat | 13 +++ 19 files changed, 131 insertions(+), 697 deletions(-) delete mode 100644 VisualC-GDK/shaders/D3D12_PixelShader_Colors.hlsl delete mode 100644 VisualC-GDK/shaders/D3D12_PixelShader_NV12_BT601.hlsl delete mode 100644 VisualC-GDK/shaders/D3D12_PixelShader_NV12_BT709.hlsl delete mode 100644 VisualC-GDK/shaders/D3D12_PixelShader_NV12_JPEG.hlsl delete mode 100644 VisualC-GDK/shaders/D3D12_PixelShader_NV21_BT601.hlsl delete mode 100644 VisualC-GDK/shaders/D3D12_PixelShader_NV21_BT709.hlsl delete mode 100644 VisualC-GDK/shaders/D3D12_PixelShader_NV21_JPEG.hlsl delete mode 100644 VisualC-GDK/shaders/D3D12_PixelShader_Textures.hlsl delete mode 100644 VisualC-GDK/shaders/D3D12_PixelShader_YUV_BT601.hlsl delete mode 100644 VisualC-GDK/shaders/D3D12_PixelShader_YUV_BT709.hlsl delete mode 100644 VisualC-GDK/shaders/D3D12_PixelShader_YUV_JPEG.hlsl delete mode 100644 VisualC-GDK/shaders/D3D12_VertexShader.hlsl delete mode 100644 VisualC-GDK/shaders/buildshaders.bat create mode 100644 src/render/direct3d12/compile_shaders_xbox.bat diff --git a/.gitignore b/.gitignore index 9e577f4984231..87d76dacb93ae 100644 --- a/.gitignore +++ b/.gitignore @@ -79,7 +79,8 @@ VisualC/tests/testscale/sample.bmp VisualC/tests/testsprite/icon.bmp VisualC/tests/testyuv/testyuv.bmp VisualC-GDK/**/Layout -VisualC-GDK/shaders/*.h +src/render/direct3d12/D3D12_*_One.h +src/render/direct3d12/D3D12_*_Series.h # for Android android-project/local.properties diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index 283cc8cb5945b..5cc952d412a86 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj +++ b/VisualC-GDK/SDL/SDL.vcxproj @@ -171,7 +171,7 @@ true - $(SolutionDir)\shaders\buildshaders.bat $(SolutionDir) + $(SolutionDir)..\src\render\direct3d12\compile_shaders_xbox.bat $(SolutionDir) Building shader blobs (Xbox Series) @@ -205,7 +205,7 @@ true - $(SolutionDir)\shaders\buildshaders.bat $(SolutionDir) one + $(SolutionDir)..\src\render\direct3d12\compile_shaders_xbox.bat $(SolutionDir) one Building shader blobs (Xbox One) @@ -271,7 +271,7 @@ true - $(SolutionDir)\shaders\buildshaders.bat $(SolutionDir) + $(SolutionDir)..\src\render\direct3d12\compile_shaders_xbox.bat $(SolutionDir) Building shader blobs (Xbox Series) @@ -306,7 +306,7 @@ true - $(SolutionDir)\shaders\buildshaders.bat $(SolutionDir) one + $(SolutionDir)..\src\render\direct3d12\compile_shaders_xbox.bat $(SolutionDir) one Building shader blobs (Xbox One) diff --git a/VisualC-GDK/shaders/D3D12_PixelShader_Colors.hlsl b/VisualC-GDK/shaders/D3D12_PixelShader_Colors.hlsl deleted file mode 100644 index 47eff4cc2444e..0000000000000 --- a/VisualC-GDK/shaders/D3D12_PixelShader_Colors.hlsl +++ /dev/null @@ -1,19 +0,0 @@ -struct PixelShaderInput -{ - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; -}; - -#define ColorRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - "DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - "DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - "DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0)" - -[RootSignature(ColorRS)] -float4 main(PixelShaderInput input) : SV_TARGET0 -{ - return input.color; -} \ No newline at end of file diff --git a/VisualC-GDK/shaders/D3D12_PixelShader_NV12_BT601.hlsl b/VisualC-GDK/shaders/D3D12_PixelShader_NV12_BT601.hlsl deleted file mode 100644 index cffbc2261822c..0000000000000 --- a/VisualC-GDK/shaders/D3D12_PixelShader_NV12_BT601.hlsl +++ /dev/null @@ -1,43 +0,0 @@ -Texture2D theTextureY : register(t0); -Texture2D theTextureUV : register(t1); -SamplerState theSampler : register(s0); - -struct PixelShaderInput -{ - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; -}; - -#define NVRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - -[RootSignature(NVRS)] -float4 main(PixelShaderInput input) : SV_TARGET -{ - const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; - const float3 Rcoeff = {1.1644, 0.0000, 1.5960}; - const float3 Gcoeff = {1.1644, -0.3918, -0.8130}; - const float3 Bcoeff = {1.1644, 2.0172, 0.0000}; - - float4 Output; - - float3 yuv; - yuv.x = theTextureY.Sample(theSampler, input.tex).r; - yuv.yz = theTextureUV.Sample(theSampler, input.tex).rg; - - yuv += offset; - Output.r = dot(yuv, Rcoeff); - Output.g = dot(yuv, Gcoeff); - Output.b = dot(yuv, Bcoeff); - Output.a = 1.0f; - - return Output * input.color; -} \ No newline at end of file diff --git a/VisualC-GDK/shaders/D3D12_PixelShader_NV12_BT709.hlsl b/VisualC-GDK/shaders/D3D12_PixelShader_NV12_BT709.hlsl deleted file mode 100644 index 81d409c94eb2c..0000000000000 --- a/VisualC-GDK/shaders/D3D12_PixelShader_NV12_BT709.hlsl +++ /dev/null @@ -1,43 +0,0 @@ -Texture2D theTextureY : register(t0); -Texture2D theTextureUV : register(t1); -SamplerState theSampler : register(s0); - -struct PixelShaderInput -{ - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; -}; - -#define NVRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - -[RootSignature(NVRS)] -float4 main(PixelShaderInput input) : SV_TARGET -{ - const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; - const float3 Rcoeff = {1.1644, 0.0000, 1.7927}; - const float3 Gcoeff = {1.1644, -0.2132, -0.5329}; - const float3 Bcoeff = {1.1644, 2.1124, 0.0000}; - - float4 Output; - - float3 yuv; - yuv.x = theTextureY.Sample(theSampler, input.tex).r; - yuv.yz = theTextureUV.Sample(theSampler, input.tex).rg; - - yuv += offset; - Output.r = dot(yuv, Rcoeff); - Output.g = dot(yuv, Gcoeff); - Output.b = dot(yuv, Bcoeff); - Output.a = 1.0f; - - return Output * input.color; -} \ No newline at end of file diff --git a/VisualC-GDK/shaders/D3D12_PixelShader_NV12_JPEG.hlsl b/VisualC-GDK/shaders/D3D12_PixelShader_NV12_JPEG.hlsl deleted file mode 100644 index 494bce5192d3b..0000000000000 --- a/VisualC-GDK/shaders/D3D12_PixelShader_NV12_JPEG.hlsl +++ /dev/null @@ -1,43 +0,0 @@ -Texture2D theTextureY : register(t0); -Texture2D theTextureUV : register(t1); -SamplerState theSampler : register(s0); - -struct PixelShaderInput -{ - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; -}; - -#define NVRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - -[RootSignature(NVRS)] -float4 main(PixelShaderInput input) : SV_TARGET -{ - const float3 offset = {0.0, -0.501960814, -0.501960814}; - const float3 Rcoeff = {1.0000, 0.0000, 1.4020}; - const float3 Gcoeff = {1.0000, -0.3441, -0.7141}; - const float3 Bcoeff = {1.0000, 1.7720, 0.0000}; - - float4 Output; - - float3 yuv; - yuv.x = theTextureY.Sample(theSampler, input.tex).r; - yuv.yz = theTextureUV.Sample(theSampler, input.tex).rg; - - yuv += offset; - Output.r = dot(yuv, Rcoeff); - Output.g = dot(yuv, Gcoeff); - Output.b = dot(yuv, Bcoeff); - Output.a = 1.0f; - - return Output * input.color; -} \ No newline at end of file diff --git a/VisualC-GDK/shaders/D3D12_PixelShader_NV21_BT601.hlsl b/VisualC-GDK/shaders/D3D12_PixelShader_NV21_BT601.hlsl deleted file mode 100644 index 794c763728af1..0000000000000 --- a/VisualC-GDK/shaders/D3D12_PixelShader_NV21_BT601.hlsl +++ /dev/null @@ -1,43 +0,0 @@ -Texture2D theTextureY : register(t0); -Texture2D theTextureUV : register(t1); -SamplerState theSampler : register(s0); - -struct PixelShaderInput -{ - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; -}; - -#define NVRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - -[RootSignature(NVRS)] -float4 main(PixelShaderInput input) : SV_TARGET -{ - const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; - const float3 Rcoeff = {1.1644, 0.0000, 1.5960}; - const float3 Gcoeff = {1.1644, -0.3918, -0.8130}; - const float3 Bcoeff = {1.1644, 2.0172, 0.0000}; - - float4 Output; - - float3 yuv; - yuv.x = theTextureY.Sample(theSampler, input.tex).r; - yuv.yz = theTextureUV.Sample(theSampler, input.tex).gr; - - yuv += offset; - Output.r = dot(yuv, Rcoeff); - Output.g = dot(yuv, Gcoeff); - Output.b = dot(yuv, Bcoeff); - Output.a = 1.0f; - - return Output * input.color; -} \ No newline at end of file diff --git a/VisualC-GDK/shaders/D3D12_PixelShader_NV21_BT709.hlsl b/VisualC-GDK/shaders/D3D12_PixelShader_NV21_BT709.hlsl deleted file mode 100644 index f5b9522c0f8c5..0000000000000 --- a/VisualC-GDK/shaders/D3D12_PixelShader_NV21_BT709.hlsl +++ /dev/null @@ -1,43 +0,0 @@ -Texture2D theTextureY : register(t0); -Texture2D theTextureUV : register(t1); -SamplerState theSampler : register(s0); - -struct PixelShaderInput -{ - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; -}; - -#define NVRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - -[RootSignature(NVRS)] -float4 main(PixelShaderInput input) : SV_TARGET -{ - const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; - const float3 Rcoeff = {1.1644, 0.0000, 1.7927}; - const float3 Gcoeff = {1.1644, -0.2132, -0.5329}; - const float3 Bcoeff = {1.1644, 2.1124, 0.0000}; - - float4 Output; - - float3 yuv; - yuv.x = theTextureY.Sample(theSampler, input.tex).r; - yuv.yz = theTextureUV.Sample(theSampler, input.tex).gr; - - yuv += offset; - Output.r = dot(yuv, Rcoeff); - Output.g = dot(yuv, Gcoeff); - Output.b = dot(yuv, Bcoeff); - Output.a = 1.0f; - - return Output * input.color; -} \ No newline at end of file diff --git a/VisualC-GDK/shaders/D3D12_PixelShader_NV21_JPEG.hlsl b/VisualC-GDK/shaders/D3D12_PixelShader_NV21_JPEG.hlsl deleted file mode 100644 index 1b467b480369e..0000000000000 --- a/VisualC-GDK/shaders/D3D12_PixelShader_NV21_JPEG.hlsl +++ /dev/null @@ -1,43 +0,0 @@ -Texture2D theTextureY : register(t0); -Texture2D theTextureUV : register(t1); -SamplerState theSampler : register(s0); - -struct PixelShaderInput -{ - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; -}; - -#define NVRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - -[RootSignature(NVRS)] -float4 main(PixelShaderInput input) : SV_TARGET -{ - const float3 offset = {0.0, -0.501960814, -0.501960814}; - const float3 Rcoeff = {1.0000, 0.0000, 1.4020}; - const float3 Gcoeff = {1.0000, -0.3441, -0.7141}; - const float3 Bcoeff = {1.0000, 1.7720, 0.0000}; - - float4 Output; - - float3 yuv; - yuv.x = theTextureY.Sample(theSampler, input.tex).r; - yuv.yz = theTextureUV.Sample(theSampler, input.tex).gr; - - yuv += offset; - Output.r = dot(yuv, Rcoeff); - Output.g = dot(yuv, Gcoeff); - Output.b = dot(yuv, Bcoeff); - Output.a = 1.0f; - - return Output * input.color; -} \ No newline at end of file diff --git a/VisualC-GDK/shaders/D3D12_PixelShader_Textures.hlsl b/VisualC-GDK/shaders/D3D12_PixelShader_Textures.hlsl deleted file mode 100644 index 0dcdf89c69bab..0000000000000 --- a/VisualC-GDK/shaders/D3D12_PixelShader_Textures.hlsl +++ /dev/null @@ -1,24 +0,0 @@ -Texture2D theTexture : register(t0); -SamplerState theSampler : register(s0); - -struct PixelShaderInput -{ - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; -}; - -#define TextureRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - -[RootSignature(TextureRS)] -float4 main(PixelShaderInput input) : SV_TARGET -{ - return theTexture.Sample(theSampler, input.tex) * input.color; -} \ No newline at end of file diff --git a/VisualC-GDK/shaders/D3D12_PixelShader_YUV_BT601.hlsl b/VisualC-GDK/shaders/D3D12_PixelShader_YUV_BT601.hlsl deleted file mode 100644 index 09e58943a7f8e..0000000000000 --- a/VisualC-GDK/shaders/D3D12_PixelShader_YUV_BT601.hlsl +++ /dev/null @@ -1,46 +0,0 @@ -Texture2D theTextureY : register(t0); -Texture2D theTextureU : register(t1); -Texture2D theTextureV : register(t2); -SamplerState theSampler : register(s0); - -struct PixelShaderInput -{ - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; -}; - -#define YUVRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t2), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - -[RootSignature(YUVRS)] -float4 main(PixelShaderInput input) : SV_TARGET -{ - const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; - const float3 Rcoeff = {1.1644, 0.0000, 1.5960}; - const float3 Gcoeff = {1.1644, -0.3918, -0.8130}; - const float3 Bcoeff = {1.1644, 2.0172, 0.0000}; - - float4 Output; - - float3 yuv; - yuv.x = theTextureY.Sample(theSampler, input.tex).r; - yuv.y = theTextureU.Sample(theSampler, input.tex).r; - yuv.z = theTextureV.Sample(theSampler, input.tex).r; - - yuv += offset; - Output.r = dot(yuv, Rcoeff); - Output.g = dot(yuv, Gcoeff); - Output.b = dot(yuv, Bcoeff); - Output.a = 1.0f; - - return Output * input.color; -} \ No newline at end of file diff --git a/VisualC-GDK/shaders/D3D12_PixelShader_YUV_BT709.hlsl b/VisualC-GDK/shaders/D3D12_PixelShader_YUV_BT709.hlsl deleted file mode 100644 index f5aa0cd7e4abb..0000000000000 --- a/VisualC-GDK/shaders/D3D12_PixelShader_YUV_BT709.hlsl +++ /dev/null @@ -1,46 +0,0 @@ -Texture2D theTextureY : register(t0); -Texture2D theTextureU : register(t1); -Texture2D theTextureV : register(t2); -SamplerState theSampler : register(s0); - -struct PixelShaderInput -{ - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; -}; - -#define YUVRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t2), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - -[RootSignature(YUVRS)] -float4 main(PixelShaderInput input) : SV_TARGET -{ - const float3 offset = {-0.0627451017, -0.501960814, -0.501960814}; - const float3 Rcoeff = {1.1644, 0.0000, 1.7927}; - const float3 Gcoeff = {1.1644, -0.2132, -0.5329}; - const float3 Bcoeff = {1.1644, 2.1124, 0.0000}; - - float4 Output; - - float3 yuv; - yuv.x = theTextureY.Sample(theSampler, input.tex).r; - yuv.y = theTextureU.Sample(theSampler, input.tex).r; - yuv.z = theTextureV.Sample(theSampler, input.tex).r; - - yuv += offset; - Output.r = dot(yuv, Rcoeff); - Output.g = dot(yuv, Gcoeff); - Output.b = dot(yuv, Bcoeff); - Output.a = 1.0f; - - return Output * input.color; -} \ No newline at end of file diff --git a/VisualC-GDK/shaders/D3D12_PixelShader_YUV_JPEG.hlsl b/VisualC-GDK/shaders/D3D12_PixelShader_YUV_JPEG.hlsl deleted file mode 100644 index 84d09b8bff031..0000000000000 --- a/VisualC-GDK/shaders/D3D12_PixelShader_YUV_JPEG.hlsl +++ /dev/null @@ -1,46 +0,0 @@ -Texture2D theTextureY : register(t0); -Texture2D theTextureU : register(t1); -Texture2D theTextureV : register(t2); -SamplerState theSampler : register(s0); - -struct PixelShaderInput -{ - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; -}; - -#define YUVRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t2), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - -[RootSignature(YUVRS)] -float4 main(PixelShaderInput input) : SV_TARGET -{ - const float3 offset = {0.0, -0.501960814, -0.501960814}; - const float3 Rcoeff = {1.0000, 0.0000, 1.4020}; - const float3 Gcoeff = {1.0000, -0.3441, -0.7141}; - const float3 Bcoeff = {1.0000, 1.7720, 0.0000}; - - float4 Output; - - float3 yuv; - yuv.x = theTextureY.Sample(theSampler, input.tex).r; - yuv.y = theTextureU.Sample(theSampler, input.tex).r; - yuv.z = theTextureV.Sample(theSampler, input.tex).r; - - yuv += offset; - Output.r = dot(yuv, Rcoeff); - Output.g = dot(yuv, Gcoeff); - Output.b = dot(yuv, Bcoeff); - Output.a = 1.0f; - - return Output * input.color; -} \ No newline at end of file diff --git a/VisualC-GDK/shaders/D3D12_VertexShader.hlsl b/VisualC-GDK/shaders/D3D12_VertexShader.hlsl deleted file mode 100644 index e10b488925ef2..0000000000000 --- a/VisualC-GDK/shaders/D3D12_VertexShader.hlsl +++ /dev/null @@ -1,95 +0,0 @@ -#pragma pack_matrix( row_major ) - -struct VertexShaderConstants -{ - matrix model; - matrix projectionAndView; -}; -ConstantBuffer Constants : register(b0); - -struct VertexShaderInput -{ - float3 pos : POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; -}; - -struct VertexShaderOutput -{ - float4 pos : SV_POSITION; - float2 tex : TEXCOORD0; - float4 color : COLOR0; -}; - -#define ColorRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - "DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - "DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - "DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0)" - -#define TextureRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - -#define YUVRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t2), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - -#define NVRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - -[RootSignature(ColorRS)] -VertexShaderOutput mainColor(VertexShaderInput input) -{ - VertexShaderOutput output; - float4 pos = float4(input.pos, 1.0f); - - // Transform the vertex position into projected space. - pos = mul(pos, Constants.model); - pos = mul(pos, Constants.projectionAndView); - output.pos = pos; - - // Pass through texture coordinates and color values without transformation - output.tex = input.tex; - output.color = input.color; - - return output; -} - -[RootSignature(TextureRS)] -VertexShaderOutput mainTexture(VertexShaderInput input) -{ - return mainColor(input); -} - -[RootSignature(YUVRS)] -VertexShaderOutput mainYUV(VertexShaderInput input) -{ - return mainColor(input); -} - -[RootSignature(NVRS)] -VertexShaderOutput mainNV(VertexShaderInput input) -{ - return mainColor(input); -} \ No newline at end of file diff --git a/VisualC-GDK/shaders/buildshaders.bat b/VisualC-GDK/shaders/buildshaders.bat deleted file mode 100644 index 4447b5e2f8267..0000000000000 --- a/VisualC-GDK/shaders/buildshaders.bat +++ /dev/null @@ -1,35 +0,0 @@ -if %2.==one. goto setxboxone -rem Xbox Series compile -set XBOXDXC="%GameDKLatest%\GXDK\bin\Scarlett\DXC.exe" -set SUFFIX=_Series.h -goto startbuild - -:setxboxone -set XBOXDXC="%GameDKLatest%\GXDK\bin\XboxOne\DXC.exe" -set SUFFIX=_One.h - -:startbuild -echo Building with %XBOXDXC% -cd "%1\shaders" -rem Root Signatures -%XBOXDXC% -E ColorRS -T rootsig_1_1 -rootsig-define ColorRS -Fh D3D12_RootSig_Color%SUFFIX% -Vn D3D12_RootSig_Color D3D12_VertexShader.hlsl -%XBOXDXC% -E TextureRS -T rootsig_1_1 -rootsig-define TextureRS -Fh D3D12_RootSig_Texture%SUFFIX% -Vn D3D12_RootSig_Texture D3D12_VertexShader.hlsl -%XBOXDXC% -E YUVRS -T rootsig_1_1 -rootsig-define YUVRS -Fh D3D12_RootSig_YUV%SUFFIX% -Vn D3D12_RootSig_YUV D3D12_VertexShader.hlsl -%XBOXDXC% -E NVRS -T rootsig_1_1 -rootsig-define NVRS -Fh D3D12_RootSig_NV%SUFFIX% -Vn D3D12_RootSig_NV D3D12_VertexShader.hlsl -rem Vertex Shaders -%XBOXDXC% -E mainColor -T vs_6_0 -Fh D3D12_VertexShader_Color%SUFFIX% -Vn D3D12_VertexShader_Color D3D12_VertexShader.hlsl -%XBOXDXC% -E mainTexture -T vs_6_0 -Fh D3D12_VertexShader_Texture%SUFFIX% -Vn D3D12_VertexShader_Texture D3D12_VertexShader.hlsl -%XBOXDXC% -E mainNV -T vs_6_0 -Fh D3D12_VertexShader_NV%SUFFIX% -Vn D3D12_VertexShader_NV D3D12_VertexShader.hlsl -%XBOXDXC% -E mainYUV -T vs_6_0 -Fh D3D12_VertexShader_YUV%SUFFIX% -Vn D3D12_VertexShader_YUV D3D12_VertexShader.hlsl -rem Pixel Shaders -%XBOXDXC% -E main -T ps_6_0 -Fh D3D12_PixelShader_Colors%SUFFIX% -Vn D3D12_PixelShader_Colors D3D12_PixelShader_Colors.hlsl -%XBOXDXC% -E main -T ps_6_0 -Fh D3D12_PixelShader_NV12_BT601%SUFFIX% -Vn D3D12_PixelShader_NV12_BT601 D3D12_PixelShader_NV12_BT601.hlsl -%XBOXDXC% -E main -T ps_6_0 -Fh D3D12_PixelShader_NV12_BT709%SUFFIX% -Vn D3D12_PixelShader_NV12_BT709 D3D12_PixelShader_NV12_BT709.hlsl -%XBOXDXC% -E main -T ps_6_0 -Fh D3D12_PixelShader_NV12_JPEG%SUFFIX% -Vn D3D12_PixelShader_NV12_JPEG D3D12_PixelShader_NV12_JPEG.hlsl -%XBOXDXC% -E main -T ps_6_0 -Fh D3D12_PixelShader_NV21_BT601%SUFFIX% -Vn D3D12_PixelShader_NV21_BT601 D3D12_PixelShader_NV21_BT601.hlsl -%XBOXDXC% -E main -T ps_6_0 -Fh D3D12_PixelShader_NV21_BT709%SUFFIX% -Vn D3D12_PixelShader_NV21_BT709 D3D12_PixelShader_NV21_BT709.hlsl -%XBOXDXC% -E main -T ps_6_0 -Fh D3D12_PixelShader_NV21_JPEG%SUFFIX% -Vn D3D12_PixelShader_NV21_JPEG D3D12_PixelShader_NV21_JPEG.hlsl -%XBOXDXC% -E main -T ps_6_0 -Fh D3D12_PixelShader_Textures%SUFFIX% -Vn D3D12_PixelShader_Textures D3D12_PixelShader_Textures.hlsl -%XBOXDXC% -E main -T ps_6_0 -Fh D3D12_PixelShader_YUV_BT601%SUFFIX% -Vn D3D12_PixelShader_YUV_BT601 D3D12_PixelShader_YUV_BT601.hlsl -%XBOXDXC% -E main -T ps_6_0 -Fh D3D12_PixelShader_YUV_BT709%SUFFIX% -Vn D3D12_PixelShader_YUV_BT709 D3D12_PixelShader_YUV_BT709.hlsl -%XBOXDXC% -E main -T ps_6_0 -Fh D3D12_PixelShader_YUV_JPEG%SUFFIX% -Vn D3D12_PixelShader_YUV_JPEG D3D12_PixelShader_YUV_JPEG.hlsl \ No newline at end of file diff --git a/src/render/direct3d12/SDL_shaders_d3d12_xboxone.cpp b/src/render/direct3d12/SDL_shaders_d3d12_xboxone.cpp index f0f4f72a3fdbe..829e075563398 100644 --- a/src/render/direct3d12/SDL_shaders_d3d12_xboxone.cpp +++ b/src/render/direct3d12/SDL_shaders_d3d12_xboxone.cpp @@ -31,28 +31,46 @@ #define SDL_COMPOSE_ERROR(str) SDL_STRINGIFY_ARG(__FUNCTION__) ", " str -/* Shader blob headers are generated with a pre-build step using buildshaders.bat */ -#include "../VisualC-GDK/shaders/D3D12_PixelShader_Colors_One.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_NV12_BT601_One.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_NV12_BT709_One.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_NV12_JPEG_One.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_NV21_BT601_One.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_NV21_BT709_One.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_NV21_JPEG_One.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_Textures_One.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_YUV_BT601_One.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_YUV_BT709_One.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_YUV_JPEG_One.h" - -#include "../VisualC-GDK/shaders/D3D12_VertexShader_Color_One.h" -#include "../VisualC-GDK/shaders/D3D12_VertexShader_NV_One.h" -#include "../VisualC-GDK/shaders/D3D12_VertexShader_Texture_One.h" -#include "../VisualC-GDK/shaders/D3D12_VertexShader_YUV_One.h" - -#include "../VisualC-GDK/shaders/D3D12_RootSig_Color_One.h" -#include "../VisualC-GDK/shaders/D3D12_RootSig_NV_One.h" -#include "../VisualC-GDK/shaders/D3D12_RootSig_Texture_One.h" -#include "../VisualC-GDK/shaders/D3D12_RootSig_YUV_One.h" +/* Shader blob headers are generated with a pre-build step using compile_shaders_xbox.bat */ + +#define g_main D3D12_PixelShader_Colors +#include "D3D12_PixelShader_Colors_One.h" +#undef g_main + +#define g_main D3D12_PixelShader_Textures +#include "D3D12_PixelShader_Textures_One.h" +#undef g_main + +#define g_main D3D12_PixelShader_Advanced +#include "D3D12_PixelShader_Advanced_One.h" +#undef g_main + + +#define g_mainColor D3D12_VertexShader_Colors +#include "D3D12_VertexShader_Color_One.h" +#undef g_mainColor + +#define g_mainTexture D3D12_VertexShader_Textures +#include "D3D12_VertexShader_Texture_One.h" +#undef g_mainTexture + +#define g_mainAdvanced D3D12_VertexShader_Advanced +#include "D3D12_VertexShader_Advanced_One.h" +#undef g_mainAdvanced + + +#define g_ColorRS D3D12_RootSig_Color +#include "D3D12_RootSig_Color_One.h" +#undef g_ColorRS + +#define g_TextureRS D3D12_RootSig_Texture +#include "D3D12_RootSig_Texture_One.h" +#undef g_TextureRS + +#define g_AdvancedRS D3D12_RootSig_Advanced +#include "D3D12_RootSig_Advanced_One.h" +#undef g_AdvancedRS + static struct { @@ -63,40 +81,14 @@ static struct D3D12_RootSignature root_sig; } D3D12_shaders[NUM_SHADERS] = { { D3D12_PixelShader_Colors, sizeof(D3D12_PixelShader_Colors), - D3D12_VertexShader_Color, sizeof(D3D12_VertexShader_Color), + D3D12_VertexShader_Colors, sizeof(D3D12_VertexShader_Colors), ROOTSIG_COLOR }, { D3D12_PixelShader_Textures, sizeof(D3D12_PixelShader_Textures), - D3D12_VertexShader_Texture, sizeof(D3D12_VertexShader_Texture), + D3D12_VertexShader_Textures, sizeof(D3D12_VertexShader_Textures), ROOTSIG_TEXTURE }, -#if SDL_HAVE_YUV - { D3D12_PixelShader_YUV_JPEG, sizeof(D3D12_PixelShader_YUV_JPEG), - D3D12_VertexShader_YUV, sizeof(D3D12_VertexShader_YUV), - ROOTSIG_YUV }, - { D3D12_PixelShader_YUV_BT601, sizeof(D3D12_PixelShader_YUV_BT601), - D3D12_VertexShader_YUV, sizeof(D3D12_VertexShader_YUV), - ROOTSIG_YUV }, - { D3D12_PixelShader_YUV_BT709, sizeof(D3D12_PixelShader_YUV_BT709), - D3D12_VertexShader_YUV, sizeof(D3D12_VertexShader_YUV), - ROOTSIG_YUV }, - { D3D12_PixelShader_NV12_JPEG, sizeof(D3D12_PixelShader_NV12_JPEG), - D3D12_VertexShader_NV, sizeof(D3D12_VertexShader_NV), - ROOTSIG_NV }, - { D3D12_PixelShader_NV12_BT601, sizeof(D3D12_PixelShader_NV12_BT601), - D3D12_VertexShader_NV, sizeof(D3D12_VertexShader_NV), - ROOTSIG_NV }, - { D3D12_PixelShader_NV12_BT709, sizeof(D3D12_PixelShader_NV12_BT709), - D3D12_VertexShader_NV, sizeof(D3D12_VertexShader_NV), - ROOTSIG_NV }, - { D3D12_PixelShader_NV21_JPEG, sizeof(D3D12_PixelShader_NV21_JPEG), - D3D12_VertexShader_NV, sizeof(D3D12_VertexShader_NV), - ROOTSIG_NV }, - { D3D12_PixelShader_NV21_BT601, sizeof(D3D12_PixelShader_NV21_BT601), - D3D12_VertexShader_NV, sizeof(D3D12_VertexShader_NV), - ROOTSIG_NV }, - { D3D12_PixelShader_NV21_BT709, sizeof(D3D12_PixelShader_NV21_BT709), - D3D12_VertexShader_NV, sizeof(D3D12_VertexShader_NV), - ROOTSIG_NV }, -#endif + { D3D12_PixelShader_Advanced, sizeof(D3D12_PixelShader_Advanced), + D3D12_VertexShader_Advanced, sizeof(D3D12_VertexShader_Advanced), + ROOTSIG_ADVANCED }, }; static struct @@ -106,10 +98,7 @@ static struct } D3D12_rootsigs[NUM_ROOTSIGS] = { { D3D12_RootSig_Color, sizeof(D3D12_RootSig_Color) }, { D3D12_RootSig_Texture, sizeof(D3D12_RootSig_Texture) }, -#if SDL_HAVE_YUV - { D3D12_RootSig_YUV, sizeof(D3D12_RootSig_YUV) }, - { D3D12_RootSig_NV, sizeof(D3D12_RootSig_NV) }, -#endif + { D3D12_RootSig_Advanced, sizeof(D3D12_RootSig_Advanced) }, }; extern "C" void diff --git a/src/render/direct3d12/SDL_shaders_d3d12_xboxseries.cpp b/src/render/direct3d12/SDL_shaders_d3d12_xboxseries.cpp index 9de2d198c36c1..90322edd2ef88 100644 --- a/src/render/direct3d12/SDL_shaders_d3d12_xboxseries.cpp +++ b/src/render/direct3d12/SDL_shaders_d3d12_xboxseries.cpp @@ -31,28 +31,47 @@ #define SDL_COMPOSE_ERROR(str) SDL_STRINGIFY_ARG(__FUNCTION__) ", " str -/* Shader blob headers are generated with a pre-build step using buildshaders.bat */ -#include "../VisualC-GDK/shaders/D3D12_PixelShader_Colors_Series.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_Textures_Series.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_NV12_BT601_Series.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_NV12_BT709_Series.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_NV12_JPEG_Series.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_NV21_BT601_Series.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_NV21_BT709_Series.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_NV21_JPEG_Series.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_YUV_BT601_Series.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_YUV_BT709_Series.h" -#include "../VisualC-GDK/shaders/D3D12_PixelShader_YUV_JPEG_Series.h" - -#include "../VisualC-GDK/shaders/D3D12_VertexShader_Color_Series.h" -#include "../VisualC-GDK/shaders/D3D12_VertexShader_Texture_Series.h" -#include "../VisualC-GDK/shaders/D3D12_VertexShader_NV_Series.h" -#include "../VisualC-GDK/shaders/D3D12_VertexShader_YUV_Series.h" - -#include "../VisualC-GDK/shaders/D3D12_RootSig_Color_Series.h" -#include "../VisualC-GDK/shaders/D3D12_RootSig_Texture_Series.h" -#include "../VisualC-GDK/shaders/D3D12_RootSig_YUV_Series.h" -#include "../VisualC-GDK/shaders/D3D12_RootSig_NV_Series.h" + +/* Shader blob headers are generated with a pre-build step using compile_shaders_xbox.bat */ + +#define g_main D3D12_PixelShader_Colors +#include "D3D12_PixelShader_Colors_Series.h" +#undef g_main + +#define g_main D3D12_PixelShader_Textures +#include "D3D12_PixelShader_Textures_Series.h" +#undef g_main + +#define g_main D3D12_PixelShader_Advanced +#include "D3D12_PixelShader_Advanced_Series.h" +#undef g_main + + +#define g_mainColor D3D12_VertexShader_Colors +#include "D3D12_VertexShader_Color_Series.h" +#undef g_mainColor + +#define g_mainTexture D3D12_VertexShader_Textures +#include "D3D12_VertexShader_Texture_Series.h" +#undef g_mainTexture + +#define g_mainAdvanced D3D12_VertexShader_Advanced +#include "D3D12_VertexShader_Advanced_Series.h" +#undef g_mainAdvanced + + +#define g_ColorRS D3D12_RootSig_Color +#include "D3D12_RootSig_Color_Series.h" +#undef g_ColorRS + +#define g_TextureRS D3D12_RootSig_Texture +#include "D3D12_RootSig_Texture_Series.h" +#undef g_TextureRS + +#define g_AdvancedRS D3D12_RootSig_Advanced +#include "D3D12_RootSig_Advanced_Series.h" +#undef g_AdvancedRS + static struct { @@ -63,40 +82,14 @@ static struct D3D12_RootSignature root_sig; } D3D12_shaders[NUM_SHADERS] = { { D3D12_PixelShader_Colors, sizeof(D3D12_PixelShader_Colors), - D3D12_VertexShader_Color, sizeof(D3D12_VertexShader_Color), + D3D12_VertexShader_Colors, sizeof(D3D12_VertexShader_Colors), ROOTSIG_COLOR }, { D3D12_PixelShader_Textures, sizeof(D3D12_PixelShader_Textures), - D3D12_VertexShader_Texture, sizeof(D3D12_VertexShader_Texture), + D3D12_VertexShader_Textures, sizeof(D3D12_VertexShader_Textures), ROOTSIG_TEXTURE }, -#if SDL_HAVE_YUV - { D3D12_PixelShader_YUV_JPEG, sizeof(D3D12_PixelShader_YUV_JPEG), - D3D12_VertexShader_YUV, sizeof(D3D12_VertexShader_YUV), - ROOTSIG_YUV }, - { D3D12_PixelShader_YUV_BT601, sizeof(D3D12_PixelShader_YUV_BT601), - D3D12_VertexShader_YUV, sizeof(D3D12_VertexShader_YUV), - ROOTSIG_YUV }, - { D3D12_PixelShader_YUV_BT709, sizeof(D3D12_PixelShader_YUV_BT709), - D3D12_VertexShader_YUV, sizeof(D3D12_VertexShader_YUV), - ROOTSIG_YUV }, - { D3D12_PixelShader_NV12_JPEG, sizeof(D3D12_PixelShader_NV12_JPEG), - D3D12_VertexShader_NV, sizeof(D3D12_VertexShader_NV), - ROOTSIG_NV }, - { D3D12_PixelShader_NV12_BT601, sizeof(D3D12_PixelShader_NV12_BT601), - D3D12_VertexShader_NV, sizeof(D3D12_VertexShader_NV), - ROOTSIG_NV }, - { D3D12_PixelShader_NV12_BT709, sizeof(D3D12_PixelShader_NV12_BT709), - D3D12_VertexShader_NV, sizeof(D3D12_VertexShader_NV), - ROOTSIG_NV }, - { D3D12_PixelShader_NV21_JPEG, sizeof(D3D12_PixelShader_NV21_JPEG), - D3D12_VertexShader_NV, sizeof(D3D12_VertexShader_NV), - ROOTSIG_NV }, - { D3D12_PixelShader_NV21_BT601, sizeof(D3D12_PixelShader_NV21_BT601), - D3D12_VertexShader_NV, sizeof(D3D12_VertexShader_NV), - ROOTSIG_NV }, - { D3D12_PixelShader_NV21_BT709, sizeof(D3D12_PixelShader_NV21_BT709), - D3D12_VertexShader_NV, sizeof(D3D12_VertexShader_NV), - ROOTSIG_NV }, -#endif + { D3D12_PixelShader_Advanced, sizeof(D3D12_PixelShader_Advanced), + D3D12_VertexShader_Advanced, sizeof(D3D12_VertexShader_Advanced), + ROOTSIG_ADVANCED }, }; static struct @@ -106,10 +99,7 @@ static struct } D3D12_rootsigs[NUM_ROOTSIGS] = { { D3D12_RootSig_Color, sizeof(D3D12_RootSig_Color) }, { D3D12_RootSig_Texture, sizeof(D3D12_RootSig_Texture) }, -#if SDL_HAVE_YUV - { D3D12_RootSig_YUV, sizeof(D3D12_RootSig_YUV) }, - { D3D12_RootSig_NV, sizeof(D3D12_RootSig_NV) }, -#endif + { D3D12_RootSig_Advanced, sizeof(D3D12_RootSig_Advanced) }, }; extern "C" void diff --git a/src/render/direct3d12/compile_shaders.bat b/src/render/direct3d12/compile_shaders.bat index e733472a66f0f..699fdf3af066b 100644 --- a/src/render/direct3d12/compile_shaders.bat +++ b/src/render/direct3d12/compile_shaders.bat @@ -1,11 +1,21 @@ -dxc -E main -T ps_6_0 -Fh D3D12_PixelShader_Colors.h D3D12_PixelShader_Colors.hlsl -dxc -E main -T ps_6_0 -Fh D3D12_PixelShader_Textures.h D3D12_PixelShader_Textures.hlsl -dxc -E main -T ps_6_0 -Fh D3D12_PixelShader_Advanced.h D3D12_PixelShader_Advanced.hlsl +rem This script runs for the Windows build, but also via the _xbox variant with these vars set. +rem Make sure to default to building for Windows if they're not set. +if %DXC%.==. set DXC=dxc +if %SUFFIX%.==. set SUFFIX=.h -dxc -E mainColor -T vs_6_0 -Fh D3D12_VertexShader_Color.h D3D12_VertexShader.hlsl -dxc -E mainTexture -T vs_6_0 -Fh D3D12_VertexShader_Texture.h D3D12_VertexShader.hlsl -dxc -E mainAdvanced -T vs_6_0 -Fh D3D12_VertexShader_Advanced.h D3D12_VertexShader.hlsl +echo Building with %DXC% +echo Suffix %SUFFIX% -dxc -E ColorRS -T rootsig_1_1 -rootsig-define ColorRS -Fh D3D12_RootSig_Color.h D3D12_VertexShader.hlsl -dxc -E TextureRS -T rootsig_1_1 -rootsig-define TextureRS -Fh D3D12_RootSig_Texture.h D3D12_VertexShader.hlsl -dxc -E AdvancedRS -T rootsig_1_1 -rootsig-define AdvancedRS -Fh D3D12_RootSig_Advanced.h D3D12_VertexShader.hlsl +cd "%~dp0" + +%DXC% -E main -T ps_6_0 -Fh D3D12_PixelShader_Colors%SUFFIX% D3D12_PixelShader_Colors.hlsl +%DXC% -E main -T ps_6_0 -Fh D3D12_PixelShader_Textures%SUFFIX% D3D12_PixelShader_Textures.hlsl +%DXC% -E main -T ps_6_0 -Fh D3D12_PixelShader_Advanced%SUFFIX% D3D12_PixelShader_Advanced.hlsl + +%DXC% -E mainColor -T vs_6_0 -Fh D3D12_VertexShader_Color%SUFFIX% D3D12_VertexShader.hlsl +%DXC% -E mainTexture -T vs_6_0 -Fh D3D12_VertexShader_Texture%SUFFIX% D3D12_VertexShader.hlsl +%DXC% -E mainAdvanced -T vs_6_0 -Fh D3D12_VertexShader_Advanced%SUFFIX% D3D12_VertexShader.hlsl + +%DXC% -E ColorRS -T rootsig_1_1 -rootsig-define ColorRS -Fh D3D12_RootSig_Color%SUFFIX% D3D12_VertexShader.hlsl +%DXC% -E TextureRS -T rootsig_1_1 -rootsig-define TextureRS -Fh D3D12_RootSig_Texture%SUFFIX% D3D12_VertexShader.hlsl +%DXC% -E AdvancedRS -T rootsig_1_1 -rootsig-define AdvancedRS -Fh D3D12_RootSig_Advanced%SUFFIX% D3D12_VertexShader.hlsl diff --git a/src/render/direct3d12/compile_shaders_xbox.bat b/src/render/direct3d12/compile_shaders_xbox.bat new file mode 100644 index 0000000000000..311b172c795fa --- /dev/null +++ b/src/render/direct3d12/compile_shaders_xbox.bat @@ -0,0 +1,13 @@ +if %2.==one. goto setxboxone +rem Xbox Series compile +set DXC="%GameDKLatest%\GXDK\bin\Scarlett\DXC.exe" +set SUFFIX=_Series.h +goto startbuild + +:setxboxone +set DXC="%GameDKLatest%\GXDK\bin\XboxOne\DXC.exe" +set SUFFIX=_One.h + +:startbuild + +call "%~dp0\compile_shaders.bat" From 794f0f1b4230ced606030146bca2465e4f18c60d Mon Sep 17 00:00:00 2001 From: Jade Macho Date: Fri, 1 Mar 2024 15:51:35 +0100 Subject: [PATCH 168/220] GDK: Fix SDL_pixels.c/_c.h linking error caused by C / C++ mismatch --- src/render/direct3d12/SDL_render_d3d12.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index e6b84bf9a5e43..ef7d7ce2b0ed8 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -31,7 +31,6 @@ #include "../../video/windows/SDL_windowswindow.h" #include "../SDL_sysrender.h" #include "../SDL_d3dmath.h" -#include "../../video/SDL_pixels_c.h" #if defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) #include "SDL_render_d3d12_xbox.h" @@ -78,6 +77,9 @@ extern "C" { #endif +/* This must be included here as the function definitions in SDL_pixels.c/_c.h are C, not C++ */ +#include "../../video/SDL_pixels_c.h" + /* !!! FIXME: vertex buffer bandwidth could be lower; only use UV coords when !!! FIXME: textures are needed. */ From 290f64b86c515cb16d27623718db07f05fd8a194 Mon Sep 17 00:00:00 2001 From: Jade Macho Date: Fri, 1 Mar 2024 15:58:53 +0100 Subject: [PATCH 169/220] GDK: Windows shape is desktop-only --- src/video/windows/SDL_windowsshape.c | 4 ++-- src/video/windows/SDL_windowsvideo.c | 2 +- src/video/windows/SDL_windowsvideo.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/video/windows/SDL_windowsshape.c b/src/video/windows/SDL_windowsshape.c index 0b5cac330d40a..ecf502dcf4716 100644 --- a/src/video/windows/SDL_windowsshape.c +++ b/src/video/windows/SDL_windowsshape.c @@ -20,7 +20,7 @@ */ #include "SDL_internal.h" -#ifdef SDL_VIDEO_DRIVER_WINDOWS +#if defined(SDL_VIDEO_DRIVER_WINDOWS) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) #include "SDL_windowsvideo.h" #include "SDL_windowsshape.h" @@ -121,4 +121,4 @@ int WIN_UpdateWindowShape(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surfac return 0; } -#endif /* SDL_VIDEO_DRIVER_WINDOWS */ +#endif /* defined(SDL_VIDEO_DRIVER_WINDOWS) && !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) */ diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index afc016a36e1b2..cb5f0e035c387 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -209,8 +209,8 @@ static SDL_VideoDevice *WIN_CreateDevice(void) device->FlashWindow = WIN_FlashWindow; device->ShowWindowSystemMenu = WIN_ShowWindowSystemMenu; device->SetWindowFocusable = WIN_SetWindowFocusable; -#endif device->UpdateWindowShape = WIN_UpdateWindowShape; +#endif #ifdef SDL_VIDEO_OPENGL_WGL device->GL_LoadLibrary = WIN_GL_LoadLibrary; diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h index 15236acf72ba5..4b3d3c27acbfa 100644 --- a/src/video/windows/SDL_windowsvideo.h +++ b/src/video/windows/SDL_windowsvideo.h @@ -42,9 +42,9 @@ #include "SDL_windowsclipboard.h" #include "SDL_windowsevents.h" #include "SDL_windowsopengl.h" -#include "SDL_windowsshape.h" #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) +#include "SDL_windowsshape.h" #include "SDL_windowskeyboard.h" #include "SDL_windowsmodes.h" #include "SDL_windowsmouse.h" From e2a820926380ad6800924823880336ada51e5ca5 Mon Sep 17 00:00:00 2001 From: Jade Macho Date: Fri, 1 Mar 2024 15:59:30 +0100 Subject: [PATCH 170/220] GDK: Use WIN_IsRectEmpty (IsRectEmpty is desktop-only) --- src/video/windows/SDL_windowswindow.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index d3a03053e5f12..0da83bb0ff2e1 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -453,7 +453,7 @@ static int SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, HWND hwnd } if (!(window->flags & SDL_WINDOW_MINIMIZED)) { RECT rect; - if (GetClientRect(hwnd, &rect) && !IsRectEmpty(&rect)) { + if (GetClientRect(hwnd, &rect) && !WIN_IsRectEmpty(&rect)) { int w = rect.right; int h = rect.bottom; From c74f273848b0259c4902c97213f5dc0760aecce9 Mon Sep 17 00:00:00 2001 From: Jade Macho Date: Fri, 1 Mar 2024 18:21:27 +0100 Subject: [PATCH 171/220] d3d12: Move root sigs to D3D12_Shader_Common.hlsli, fix mismatch --- .../D3D12_PixelShader_Advanced.hlsl | 15 +------- .../direct3d12/D3D12_PixelShader_Colors.hlsl | 9 +---- ...on.incl => D3D12_PixelShader_Common.hlsli} | 1 + .../D3D12_PixelShader_Textures.hlsl | 11 +----- .../direct3d12/D3D12_Shader_Common.hlsli | 37 ++++++++++++++++++ src/render/direct3d12/D3D12_VertexShader.hlsl | 38 +------------------ src/render/direct3d12/SDL_render_d3d12.c | 2 +- 7 files changed, 44 insertions(+), 69 deletions(-) rename src/render/direct3d12/{D3D12_PixelShader_Common.incl => D3D12_PixelShader_Common.hlsli} (99%) create mode 100644 src/render/direct3d12/D3D12_Shader_Common.hlsli diff --git a/src/render/direct3d12/D3D12_PixelShader_Advanced.hlsl b/src/render/direct3d12/D3D12_PixelShader_Advanced.hlsl index ac25bb02be453..1c869b85c21f0 100644 --- a/src/render/direct3d12/D3D12_PixelShader_Advanced.hlsl +++ b/src/render/direct3d12/D3D12_PixelShader_Advanced.hlsl @@ -1,18 +1,7 @@ -#include "D3D12_PixelShader_Common.incl" +#include "D3D12_PixelShader_Common.hlsli" -#define ADVANCEDRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=24, b1),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t2), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - -[RootSignature(ADVANCEDRS)] +[RootSignature(AdvancedRS)] float4 main(PixelShaderInput input) : SV_TARGET { return AdvancedPixelShader(input); diff --git a/src/render/direct3d12/D3D12_PixelShader_Colors.hlsl b/src/render/direct3d12/D3D12_PixelShader_Colors.hlsl index 4383011f0e3e5..834d6298f1afe 100644 --- a/src/render/direct3d12/D3D12_PixelShader_Colors.hlsl +++ b/src/render/direct3d12/D3D12_PixelShader_Colors.hlsl @@ -1,12 +1,5 @@ -#include "D3D12_PixelShader_Common.incl" - -#define ColorRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - "DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - "DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - "DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=24, b1)" +#include "D3D12_PixelShader_Common.hlsli" [RootSignature(ColorRS)] float4 main(PixelShaderInput input) : SV_TARGET0 diff --git a/src/render/direct3d12/D3D12_PixelShader_Common.incl b/src/render/direct3d12/D3D12_PixelShader_Common.hlsli similarity index 99% rename from src/render/direct3d12/D3D12_PixelShader_Common.incl rename to src/render/direct3d12/D3D12_PixelShader_Common.hlsli index 1133c32d9df51..ba32e0c8332f7 100644 --- a/src/render/direct3d12/D3D12_PixelShader_Common.incl +++ b/src/render/direct3d12/D3D12_PixelShader_Common.hlsli @@ -1,3 +1,4 @@ +#include "D3D12_Shader_Common.hlsli" Texture2D texture0 : register(t0); Texture2D texture1 : register(t1); diff --git a/src/render/direct3d12/D3D12_PixelShader_Textures.hlsl b/src/render/direct3d12/D3D12_PixelShader_Textures.hlsl index 6625744f1aaea..f705742ead9f4 100644 --- a/src/render/direct3d12/D3D12_PixelShader_Textures.hlsl +++ b/src/render/direct3d12/D3D12_PixelShader_Textures.hlsl @@ -1,14 +1,5 @@ -#include "D3D12_PixelShader_Common.incl" - -#define TextureRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=24, b1),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" +#include "D3D12_PixelShader_Common.hlsli" [RootSignature(TextureRS)] float4 main(PixelShaderInput input) : SV_TARGET diff --git a/src/render/direct3d12/D3D12_Shader_Common.hlsli b/src/render/direct3d12/D3D12_Shader_Common.hlsli new file mode 100644 index 0000000000000..4bf8074de5e38 --- /dev/null +++ b/src/render/direct3d12/D3D12_Shader_Common.hlsli @@ -0,0 +1,37 @@ +#pragma pack_matrix( row_major ) + +cbuffer VertexShaderConstants : register(b0) +{ + matrix model; + matrix projectionAndView; +}; + +#define ColorRS \ + "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ + "DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ + "DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ + "DENY_HULL_SHADER_ROOT_ACCESS )," \ + "RootConstants(num32BitConstants=32, b0)," \ + "RootConstants(num32BitConstants=24, b1)"\ + +#define TextureRS \ + "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ + " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ + " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ + " DENY_HULL_SHADER_ROOT_ACCESS )," \ + "RootConstants(num32BitConstants=32, b0),"\ + "RootConstants(num32BitConstants=24, b1),"\ + "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ + "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" + +#define AdvancedRS \ + "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ + " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ + " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ + " DENY_HULL_SHADER_ROOT_ACCESS )," \ + "RootConstants(num32BitConstants=32, b0),"\ + "RootConstants(num32BitConstants=24, b1),"\ + "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ + "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ + "DescriptorTable ( SRV(t2), visibility = SHADER_VISIBILITY_PIXEL ),"\ + "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" diff --git a/src/render/direct3d12/D3D12_VertexShader.hlsl b/src/render/direct3d12/D3D12_VertexShader.hlsl index ad08992a08e97..c7d24335260fa 100644 --- a/src/render/direct3d12/D3D12_VertexShader.hlsl +++ b/src/render/direct3d12/D3D12_VertexShader.hlsl @@ -1,10 +1,4 @@ -#pragma pack_matrix( row_major ) - -cbuffer VertexShaderConstants : register(b0) -{ - matrix model; - matrix projectionAndView; -}; +#include "D3D12_Shader_Common.hlsli" struct VertexShaderInput { @@ -20,36 +14,6 @@ struct VertexShaderOutput float4 color : COLOR0; }; -#define ColorRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - "DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - "DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - "DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0)," \ - "RootConstants(num32BitConstants=20, b1)"\ - -#define TextureRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0),"\ - "RootConstants(num32BitConstants=20, b1),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - -#define AdvancedRS \ - "RootFlags ( ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT |" \ - " DENY_DOMAIN_SHADER_ROOT_ACCESS |" \ - " DENY_GEOMETRY_SHADER_ROOT_ACCESS |" \ - " DENY_HULL_SHADER_ROOT_ACCESS )," \ - "RootConstants(num32BitConstants=32, b0),"\ - "RootConstants(num32BitConstants=20, b1),"\ - "DescriptorTable ( SRV(t0), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t1), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( SRV(t2), visibility = SHADER_VISIBILITY_PIXEL ),"\ - "DescriptorTable ( Sampler(s0), visibility = SHADER_VISIBILITY_PIXEL )" - [RootSignature(ColorRS)] VertexShaderOutput mainColor(VertexShaderInput input) { diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index ef7d7ce2b0ed8..aa9a4fc9da9db 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -90,7 +90,7 @@ typedef struct Float4X4 projectionAndView; } VertexShaderConstants; -/* These should mirror the definitions in D3D12_PixelShader_Common.incl */ +/* These should mirror the definitions in D3D12_PixelShader_Common.hlsli */ //static const float TONEMAP_NONE = 0; //static const float TONEMAP_LINEAR = 1; static const float TONEMAP_CHROME = 2; From 7f9ff6277c789bdd4d475601f1a0c45d7c1b3b0b Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 2 Mar 2024 15:02:17 -0800 Subject: [PATCH 172/220] Don't assume HDR headroom for HDR10 surfaces Applications that support HDR will set the correct values for their content. --- include/SDL3/SDL_surface.h | 20 ++------------------ src/video/SDL_surface.c | 5 ----- test/testcolorspace.c | 3 --- test/testffmpeg.c | 23 ++++++++++++++--------- 4 files changed, 16 insertions(+), 35 deletions(-) diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index 5c8184e44fa57..4064354979ca9 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -210,27 +210,13 @@ extern DECLSPEC void SDLCALL SDL_DestroySurface(SDL_Surface *surface); * floating point formats, SDL_COLORSPACE_HDR10 for 10-bit formats, * SDL_COLORSPACE_SRGB for other RGB surfaces and SDL_COLORSPACE_BT709_FULL * for YUV surfaces. - * - `SDL_PROP_SURFACE_MAXCLL_NUMBER`: MaxCLL (Maximum Content Light Level) - * indicates the maximum light level of any single pixel (in cd/m2 or nits) - * of the content. MaxCLL is usually measured off the final delivered - * content after mastering. If one uses the full light level of the HDR - * mastering display and adds a hard clip at its maximum value, MaxCLL would - * be equal to the peak luminance of the mastering monitor. This defaults to - * 400 for HDR10 surfaces. - * - `SDL_PROP_SURFACE_MAXFALL_NUMBER`: MaxFALL (Maximum Frame Average Light - * Level) indicates the maximum value of the frame average light level (in - * cd/m2 or nits) of the content. MaxFALL is calculated by averaging the - * decoded luminance values of all the pixels within a frame. MaxFALL is - * usually much lower than MaxCLL. * - `SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT`: for HDR10 and floating point * surfaces, this defines the value of 100% diffuse white, with higher * values being displayed in the High Dynamic Range headroom. This defaults - * to 100 for HDR10 surfaces and 1.0 for other surfaces. + * to 203 for HDR10 surfaces and 1.0 for floating point surfaces. * - `SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT`: for HDR10 and floating point * surfaces, this defines the maximum dynamic range used by the content, in - * terms of the SDR white point. This defaults to - * SDL_PROP_SURFACE_MAXCLL_NUMBER / SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT, - * or 4.0, for HDR10 surfaces. + * terms of the SDR white point. This defaults to 0.0, which disables tone mapping. * - `SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING`: the tone mapping operator * used when compressing from a surface with high dynamic range to another * with lower dynamic range. Currently this supports "chrome", which uses @@ -250,8 +236,6 @@ extern DECLSPEC void SDLCALL SDL_DestroySurface(SDL_Surface *surface); extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetSurfaceProperties(SDL_Surface *surface); #define SDL_PROP_SURFACE_COLORSPACE_NUMBER "SDL.surface.colorspace" -#define SDL_PROP_SURFACE_MAXCLL_NUMBER "SDL.surface.maxCLL" -#define SDL_PROP_SURFACE_MAXFALL_NUMBER "SDL.surface.maxFALL" #define SDL_PROP_SURFACE_SDR_WHITE_POINT_FLOAT "SDL.surface.SDR_white_point" #define SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT "SDL.surface.HDR_headroom" #define SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING "SDL.surface.tonemap" diff --git a/src/video/SDL_surface.c b/src/video/SDL_surface.c index 16de3758228a7..e003f31e53cfb 100644 --- a/src/video/SDL_surface.c +++ b/src/video/SDL_surface.c @@ -355,11 +355,6 @@ float SDL_GetSurfaceHDRHeadroom(SDL_Surface *surface, SDL_Colorspace colorspace) } else { props = 0; } - if (transfer == SDL_TRANSFER_CHARACTERISTICS_PQ) { - /* The official definition is 10000, but PQ game content is often mastered for 400 or 1000 nits */ - const int DEFAULT_PQ_MAXCLL = 1000; - default_value = (float)SDL_GetNumberProperty(props, SDL_PROP_SURFACE_MAXCLL_NUMBER, DEFAULT_PQ_MAXCLL) / SDL_GetSurfaceSDRWhitePoint(surface, colorspace); - } return SDL_GetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, default_value); } return 1.0f; diff --git a/test/testcolorspace.c b/test/testcolorspace.c index 7fb262b161c31..ee1c610b1e4ff 100644 --- a/test/testcolorspace.c +++ b/test/testcolorspace.c @@ -157,9 +157,6 @@ static SDL_bool ReadPixel(int x, int y, SDL_Color *c) surface = SDL_RenderReadPixels(renderer, &r); if (surface) { - /* We don't want to do any HDR -> SDR tone mapping */ - SDL_SetFloatProperty(SDL_GetSurfaceProperties(surface), SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, 0.0f); - if (SDL_ReadSurfacePixel(surface, 0, 0, &c->r, &c->g, &c->b, &c->a) == 0) { result = SDL_TRUE; } else { diff --git a/test/testffmpeg.c b/test/testffmpeg.c index 1770dca4506fb..4c3ef85b9abd5 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -436,20 +436,25 @@ static SDL_PropertiesID CreateVideoTextureProperties(AVFrame *frame, Uint32 form { AVFrameSideData *pSideData; SDL_PropertiesID props; + SDL_Colorspace colorspace = GetFrameColorspace(frame); + + /* ITU-R BT.2408-6 recommends using an SDR white point of 203 nits, which is more likely for game content */ + static const float k_flSDRWhitePoint = 203.0f; + float flMaxLuminance = k_flSDRWhitePoint; props = SDL_CreateProperties(); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, GetFrameColorspace(frame)); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, colorspace); pSideData = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); if (pSideData) { - /* ITU-R BT.2408-6 recommends using an SDR white point of 203 nits, which is more likely for game content */ - static const float k_flSDRWhitePoint = 203.0f; - AVMasteringDisplayMetadata *pMasteringDisplayMetadata = (AVMasteringDisplayMetadata *)pSideData->data; - float flMaxLuminance = (float)pMasteringDisplayMetadata->max_luminance.num / pMasteringDisplayMetadata->max_luminance.den; - if (flMaxLuminance > k_flSDRWhitePoint) { - SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, k_flSDRWhitePoint); - SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, flMaxLuminance / k_flSDRWhitePoint); - } + flMaxLuminance = (float)pMasteringDisplayMetadata->max_luminance.num / pMasteringDisplayMetadata->max_luminance.den; + } else if (SDL_COLORSPACETRANSFER(colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) { + /* The official definition is 10000, but PQ game content is often mastered for 400 or 1000 nits */ + flMaxLuminance = 1000.0f; + } + if (flMaxLuminance > k_flSDRWhitePoint) { + SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_SDR_WHITE_POINT_FLOAT, k_flSDRWhitePoint); + SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, flMaxLuminance / k_flSDRWhitePoint); } SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, access); From 27389716acaa6d5e3069b607eb2fb15cd4ccde66 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Sat, 2 Mar 2024 23:05:24 +0000 Subject: [PATCH 173/220] Sync SDL3 wiki -> header --- include/SDL3/SDL_surface.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/SDL3/SDL_surface.h b/include/SDL3/SDL_surface.h index 4064354979ca9..abff60facdb67 100644 --- a/include/SDL3/SDL_surface.h +++ b/include/SDL3/SDL_surface.h @@ -216,7 +216,8 @@ extern DECLSPEC void SDLCALL SDL_DestroySurface(SDL_Surface *surface); * to 203 for HDR10 surfaces and 1.0 for floating point surfaces. * - `SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT`: for HDR10 and floating point * surfaces, this defines the maximum dynamic range used by the content, in - * terms of the SDR white point. This defaults to 0.0, which disables tone mapping. + * terms of the SDR white point. This defaults to 0.0, which disables tone + * mapping. * - `SDL_PROP_SURFACE_TONEMAP_OPERATOR_STRING`: the tone mapping operator * used when compressing from a surface with high dynamic range to another * with lower dynamic range. Currently this supports "chrome", which uses From 88dcf74588c54e21faf467886bcb67328a652a31 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 2 Mar 2024 15:05:52 -0800 Subject: [PATCH 174/220] testyuv: we no longer need to override the HDR headroom for the BT.2020 test --- test/testyuv.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/test/testyuv.c b/test/testyuv.c index e452466b1fbe3..d3cc1b7a112b5 100644 --- a/test/testyuv.c +++ b/test/testyuv.c @@ -65,7 +65,7 @@ static SDL_Surface *generate_test_pattern(int pattern_size) return pattern; } -static SDL_bool verify_yuv_data(Uint32 format, SDL_Colorspace colorspace, SDL_PropertiesID props, const Uint8 *yuv, int yuv_pitch, SDL_Surface *surface, int tolerance) +static SDL_bool verify_yuv_data(Uint32 format, SDL_Colorspace colorspace, const Uint8 *yuv, int yuv_pitch, SDL_Surface *surface, int tolerance) { const int size = (surface->h * surface->pitch); Uint8 *rgb; @@ -77,7 +77,7 @@ static SDL_bool verify_yuv_data(Uint32 format, SDL_Colorspace colorspace, SDL_Pr return SDL_FALSE; } - if (SDL_ConvertPixelsAndColorspace(surface->w, surface->h, format, colorspace, props, yuv, yuv_pitch, surface->format->format, SDL_COLORSPACE_SRGB, 0, rgb, surface->pitch) == 0) { + if (SDL_ConvertPixelsAndColorspace(surface->w, surface->h, format, colorspace, 0, yuv, yuv_pitch, surface->format->format, SDL_COLORSPACE_SRGB, 0, rgb, surface->pitch) == 0) { int x, y; result = SDL_TRUE; for (y = 0; y < surface->h; ++y) { @@ -123,7 +123,6 @@ static int run_automated_tests(int pattern_size, int extra_pitch) int yuv1_pitch, yuv2_pitch; YUV_CONVERSION_MODE mode; SDL_Colorspace colorspace; - SDL_PropertiesID props; const int tight_tolerance = 20; const int loose_tolerance = 333; int result = -1; @@ -136,10 +135,6 @@ static int run_automated_tests(int pattern_size, int extra_pitch) mode = GetYUVConversionModeForResolution(pattern->w, pattern->h); colorspace = GetColorspaceForYUVConversionMode(mode); - /* All tests are being done with SDR content */ - props = SDL_CreateProperties(); - SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, 1.0f); - /* Verify conversion from YUV formats */ for (i = 0; i < SDL_arraysize(formats); ++i) { if (!ConvertRGBtoYUV(formats[i], pattern->pixels, pattern->pitch, yuv1, pattern->w, pattern->h, mode, 0, 100)) { @@ -147,7 +142,7 @@ static int run_automated_tests(int pattern_size, int extra_pitch) goto done; } yuv1_pitch = CalculateYUVPitch(formats[i], pattern->w); - if (!verify_yuv_data(formats[i], colorspace, props, yuv1, yuv1_pitch, pattern, tight_tolerance)) { + if (!verify_yuv_data(formats[i], colorspace, yuv1, yuv1_pitch, pattern, tight_tolerance)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to RGB\n", SDL_GetPixelFormatName(formats[i])); goto done; } @@ -160,7 +155,7 @@ static int run_automated_tests(int pattern_size, int extra_pitch) SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(formats[i]), SDL_GetError()); goto done; } - if (!verify_yuv_data(formats[i], colorspace, props, yuv1, yuv1_pitch, pattern, tight_tolerance)) { + if (!verify_yuv_data(formats[i], colorspace, yuv1, yuv1_pitch, pattern, tight_tolerance)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from RGB to %s\n", SDL_GetPixelFormatName(formats[i])); goto done; } @@ -179,7 +174,7 @@ static int run_automated_tests(int pattern_size, int extra_pitch) SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError()); goto done; } - if (!verify_yuv_data(formats[j], colorspace, props, yuv2, yuv2_pitch, pattern, tight_tolerance)) { + if (!verify_yuv_data(formats[j], colorspace, yuv2, yuv2_pitch, pattern, tight_tolerance)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j])); goto done; } @@ -204,7 +199,7 @@ static int run_automated_tests(int pattern_size, int extra_pitch) SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j]), SDL_GetError()); goto done; } - if (!verify_yuv_data(formats[j], colorspace, props, yuv1, yuv2_pitch, pattern, tight_tolerance)) { + if (!verify_yuv_data(formats[j], colorspace, yuv1, yuv2_pitch, pattern, tight_tolerance)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to %s\n", SDL_GetPixelFormatName(formats[i]), SDL_GetPixelFormatName(formats[j])); goto done; } @@ -218,19 +213,19 @@ static int run_automated_tests(int pattern_size, int extra_pitch) goto done; } yuv1_pitch = CalculateYUVPitch(SDL_PIXELFORMAT_P010, pattern->w); - if (!verify_yuv_data(SDL_PIXELFORMAT_P010, colorspace, props, yuv1, yuv1_pitch, pattern, tight_tolerance)) { + if (!verify_yuv_data(SDL_PIXELFORMAT_P010, colorspace, yuv1, yuv1_pitch, pattern, tight_tolerance)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from %s to RGB\n", SDL_GetPixelFormatName(SDL_PIXELFORMAT_P010)); goto done; } /* The pitch needs to be Uint16 aligned for P010 pixels */ yuv1_pitch = CalculateYUVPitch(SDL_PIXELFORMAT_P010, pattern->w) + ((extra_pitch + 1) & ~1); - if (SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, SDL_PIXELFORMAT_P010, colorspace, props, yuv1, yuv1_pitch) < 0) { + if (SDL_ConvertPixelsAndColorspace(pattern->w, pattern->h, pattern->format->format, SDL_COLORSPACE_SRGB, 0, pattern->pixels, pattern->pitch, SDL_PIXELFORMAT_P010, colorspace, 0, yuv1, yuv1_pitch) < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't convert %s to %s: %s\n", SDL_GetPixelFormatName(pattern->format->format), SDL_GetPixelFormatName(SDL_PIXELFORMAT_P010), SDL_GetError()); goto done; } /* Going through XRGB2101010 format during P010 conversion is slightly lossy, so use looser tolerance here */ - if (!verify_yuv_data(SDL_PIXELFORMAT_P010, colorspace, props, yuv1, yuv1_pitch, pattern, loose_tolerance)) { + if (!verify_yuv_data(SDL_PIXELFORMAT_P010, colorspace, yuv1, yuv1_pitch, pattern, loose_tolerance)) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed conversion from RGB to %s\n", SDL_GetPixelFormatName(SDL_PIXELFORMAT_P010)); goto done; } @@ -463,13 +458,9 @@ int main(int argc, char **argv) return 3; } - /* All tests are being done with SDR content */ - props = SDL_GetSurfaceProperties(converted); - SDL_SetFloatProperty(props, SDL_PROP_SURFACE_HDR_HEADROOM_FLOAT, 1.0f); - then = SDL_GetTicks(); for (i = 0; i < iterations; ++i) { - SDL_ConvertPixelsAndColorspace(original->w, original->h, yuv_format, yuv_colorspace, props, raw_yuv, pitch, rgb_format, rgb_colorspace, props, converted->pixels, converted->pitch); + SDL_ConvertPixelsAndColorspace(original->w, original->h, yuv_format, yuv_colorspace, 0, raw_yuv, pitch, rgb_format, rgb_colorspace, 0, converted->pixels, converted->pitch); } now = SDL_GetTicks(); SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%d iterations in %" SDL_PRIu64 " ms, %.2fms each\n", iterations, (now - then), (float)(now - then) / iterations); @@ -490,7 +481,6 @@ int main(int argc, char **argv) output[1] = SDL_CreateTextureFromSurface(renderer, converted); props = SDL_CreateProperties(); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, yuv_colorspace); - SDL_SetFloatProperty(props, SDL_PROP_TEXTURE_CREATE_HDR_HEADROOM_FLOAT, 1.0f); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, yuv_format); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STREAMING); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, original->w); From e524e545f2bca599b00610b7e8f5a59a23bd4d95 Mon Sep 17 00:00:00 2001 From: Nour Fouad Date: Sun, 3 Mar 2024 01:10:02 +0200 Subject: [PATCH 175/220] Add vulkan to SDL_HINT_RENDER_DRIVER --- include/SDL3/SDL_hints.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index 80d4ff3cbc3b9..f67abaf9b660d 100644 --- a/include/SDL3/SDL_hints.h +++ b/include/SDL3/SDL_hints.h @@ -1761,6 +1761,7 @@ extern "C" { * "opengles2" * "opengles" * "metal" + * "vulkan" * "software" * * The default varies by platform, but it's the first one in the list that is available on the current platform. From c6ec9998692e7638b7316c8e9e5a8ecf93bbb299 Mon Sep 17 00:00:00 2001 From: meyraud705 Date: Sun, 3 Mar 2024 09:25:52 +0100 Subject: [PATCH 176/220] Set udev class for accelerometer --- src/core/linux/SDL_udev.c | 5 +++++ src/joystick/linux/SDL_sysjoystick.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/linux/SDL_udev.c b/src/core/linux/SDL_udev.c index 167176fa30972..b8205027d2790 100644 --- a/src/core/linux/SDL_udev.c +++ b/src/core/linux/SDL_udev.c @@ -428,6 +428,11 @@ static int device_class(struct udev_device *dev) devclass |= SDL_UDEV_DEVICE_JOYSTICK; } + val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_ACCELEROMETER"); + if (val && SDL_strcmp(val, "1") == 0) { + devclass |= SDL_UDEV_DEVICE_ACCELEROMETER; + } + val = _this->syms.udev_device_get_property_value(dev, "ID_INPUT_MOUSE"); if (val && SDL_strcmp(val, "1") == 0) { devclass |= SDL_UDEV_DEVICE_MOUSE; diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c index 17935745bb0cb..10f074414364d 100644 --- a/src/joystick/linux/SDL_sysjoystick.c +++ b/src/joystick/linux/SDL_sysjoystick.c @@ -360,7 +360,7 @@ static void joystick_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_clas switch (udev_type) { case SDL_UDEV_DEVICEADDED: - if (!(udev_class & SDL_UDEV_DEVICE_JOYSTICK)) { + if (!(udev_class & (SDL_UDEV_DEVICE_JOYSTICK | SDL_UDEV_DEVICE_ACCELEROMETER))) { return; } if (SDL_classic_joysticks) { From 107e06a92a7099e54a5aee8cabeec2db8a8e6f25 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 3 Mar 2024 09:15:07 -0800 Subject: [PATCH 177/220] Use a valid finger ID when generating touch events from mouse events Fixes https://github.com/libsdl-org/SDL/issues/9185 --- src/events/SDL_mouse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c index a3888371f16c8..0d5e67bb74ce1 100644 --- a/src/events/SDL_mouse.c +++ b/src/events/SDL_mouse.c @@ -571,7 +571,7 @@ static int SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_ if (window) { float normalized_x = x / (float)window->w; float normalized_y = y / (float)window->h; - SDL_SendTouchMotion(timestamp, SDL_MOUSE_TOUCHID, 0, window, normalized_x, normalized_y, 1.0f); + SDL_SendTouchMotion(timestamp, SDL_MOUSE_TOUCHID, SDL_BUTTON_LEFT, window, normalized_x, normalized_y, 1.0f); } } } @@ -754,7 +754,7 @@ static int SDL_PrivateSendMouseButton(Uint64 timestamp, SDL_Window *window, SDL_ if (window) { float normalized_x = mouse->x / (float)window->w; float normalized_y = mouse->y / (float)window->h; - SDL_SendTouch(timestamp, SDL_MOUSE_TOUCHID, 0, window, track_mouse_down, normalized_x, normalized_y, 1.0f); + SDL_SendTouch(timestamp, SDL_MOUSE_TOUCHID, SDL_BUTTON_LEFT, window, track_mouse_down, normalized_x, normalized_y, 1.0f); } } } From eb5a2e7e7f81c2e6af6a06097752f53b511d047c Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 3 Mar 2024 09:25:02 -0800 Subject: [PATCH 178/220] Fixed building with SDL_LEAN_AND_MEAN Fixes https://github.com/libsdl-org/SDL/issues/9173 --- src/render/vulkan/SDL_render_vulkan.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index fcce43c198c75..1222cc8f43831 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -237,7 +237,6 @@ typedef struct int height; VULKAN_Shader shader; -#if SDL_HAVE_YUV /* Object passed to VkImageView and VkSampler for doing Ycbcr -> RGB conversion */ VkSamplerYcbcrConversion samplerYcbcrConversion; /* Sampler created with samplerYcbcrConversion, passed to PSO as immutable sampler */ @@ -246,7 +245,6 @@ typedef struct VkDescriptorSetLayout descriptorSetLayoutYcbcr; /* Pipeline layout with immutable sampler descriptor set layout */ VkPipelineLayout pipelineLayoutYcbcr; -#endif } VULKAN_TextureData; @@ -797,7 +795,7 @@ static VkResult VULKAN_AllocateImage(VULKAN_RenderData *rendererData, SDL_Proper samplerYcbcrConversionInfo.conversion = samplerYcbcrConversion; imageViewCreateInfo.pNext = &samplerYcbcrConversionInfo; } - + result = vkCreateImageView(rendererData->device, &imageViewCreateInfo, NULL, &imageOut->imageView); if (result != VK_SUCCESS) { VULKAN_DestroyImage(rendererData, imageOut); @@ -1566,7 +1564,7 @@ static SDL_bool VULKAN_DeviceExtensionsFound(VULKAN_RenderData *rendererData, in } foundExtensions &= foundExtension; } - + SDL_free(extensionProperties); } @@ -2502,7 +2500,7 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD samplerYcbcrConversionCreateInfo.components.r = VK_COMPONENT_SWIZZLE_B; samplerYcbcrConversionCreateInfo.components.b = VK_COMPONENT_SWIZZLE_R; } - + switch (SDL_COLORSPACERANGE(texture->colorspace)) { case SDL_COLOR_RANGE_LIMITED: samplerYcbcrConversionCreateInfo.ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR; @@ -4003,6 +4001,7 @@ SDL_Renderer *VULKAN_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_ return NULL; } +#if SDL_HAVE_YUV if (rendererData->supportsKHRSamplerYCbCrConversion) { renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_YV12; renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_IYUV; @@ -4010,6 +4009,7 @@ SDL_Renderer *VULKAN_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_ renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_NV21; renderer->info.texture_formats[renderer->info.num_texture_formats++] = SDL_PIXELFORMAT_P010; } +#endif return renderer; } From 86d36a2dc2a1e68647c4445b984507477cafd53f Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sun, 3 Mar 2024 18:31:00 +0100 Subject: [PATCH 179/220] Add missing include --- include/SDL3/SDL_properties.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/SDL3/SDL_properties.h b/include/SDL3/SDL_properties.h index f2fda2810b8d2..33945defae88b 100644 --- a/include/SDL3/SDL_properties.h +++ b/include/SDL3/SDL_properties.h @@ -28,6 +28,8 @@ #ifndef SDL_properties_h_ #define SDL_properties_h_ +#include + #include /* Set up for C function definitions, even when using C++ */ #ifdef __cplusplus From 74f46142891501245fa309e1d10c42a939c053fd Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 3 Mar 2024 09:46:54 -0800 Subject: [PATCH 180/220] Save the native texture parent and return that in SDL_GetRenderTarget() Fixes https://github.com/libsdl-org/SDL/issues/9176 --- src/render/SDL_render.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 8891acf3b1495..a5d1470832f51 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -44,6 +44,7 @@ this should probably be removed at some point in the future. --ryan. */ #endif #define SDL_PROP_WINDOW_RENDERER_POINTER "SDL.internal.window.renderer" +#define SDL_PROP_TEXTURE_PARENT_POINTER "SDL.internal.texture.parent" #define CHECK_RENDERER_MAGIC(renderer, retval) \ if (!(renderer) || (renderer)->magic != &SDL_renderer_magic) { \ @@ -1333,6 +1334,8 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert return NULL; } + SDL_SetProperty(SDL_GetTextureProperties(texture->native), SDL_PROP_TEXTURE_PARENT_POINTER, texture); + /* Swap textures to have texture before texture->native in the list */ texture->native->next = texture->next; if (texture->native->next) { @@ -2356,7 +2359,7 @@ SDL_Texture *SDL_GetRenderTarget(SDL_Renderer *renderer) if (renderer->target == renderer->logical_target) { return NULL; } else { - return renderer->target; + return SDL_GetProperty(SDL_GetTextureProperties(renderer->target), SDL_PROP_TEXTURE_PARENT_POINTER, renderer->target); } } From c1da39a245768a3867cd4377e855980e1f1bc42a Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 3 Mar 2024 11:07:39 -0800 Subject: [PATCH 181/220] SDL_GetGamepadAppleSFSymbolsNameForButton() returns NULL if the symbol isn't found Fixes https://github.com/libsdl-org/SDL/issues/9071 --- src/joystick/SDL_gamepad.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index 6574b66554f78..e7f55880c25e3 100644 --- a/src/joystick/SDL_gamepad.c +++ b/src/joystick/SDL_gamepad.c @@ -3750,10 +3750,11 @@ const char *SDL_GetGamepadAppleSFSymbolsNameForButton(SDL_Gamepad *gamepad, SDL_ } SDL_UnlockJoysticks(); - return retval; -#else - return NULL; + if (retval && *retval) { + return retval; + } #endif + return NULL; } const char *SDL_GetGamepadAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_GamepadAxis axis) From e54001b02809dcebbb822bd0297919c8c76976a1 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 3 Mar 2024 11:32:17 -0800 Subject: [PATCH 182/220] Updating the mouse capture should use the mouse focus window Fixes https://github.com/libsdl-org/SDL/issues/8974 --- src/events/SDL_mouse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c index 0d5e67bb74ce1..c7cf857cb4912 100644 --- a/src/events/SDL_mouse.c +++ b/src/events/SDL_mouse.c @@ -1163,7 +1163,7 @@ int SDL_UpdateMouseCapture(SDL_bool force_release) if (SDL_GetMessageBoxCount() == 0 && (mouse->capture_desired || (mouse->auto_capture && GetButtonState(mouse, SDL_FALSE) != 0))) { if (!mouse->relative_mode) { - capture_window = SDL_GetKeyboardFocus(); + capture_window = mouse->focus; } } } From 2c850529665484657d93cb3b374f8a1ba7125c85 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 3 Mar 2024 11:33:59 -0800 Subject: [PATCH 183/220] Removed unused headers from SDL_rect.h Fixes https://github.com/libsdl-org/SDL/issues/8966 --- include/SDL3/SDL_rect.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/SDL3/SDL_rect.h b/include/SDL3/SDL_rect.h index 4d03884d44197..4b496e348aadc 100644 --- a/include/SDL3/SDL_rect.h +++ b/include/SDL3/SDL_rect.h @@ -30,8 +30,6 @@ #include #include -#include -#include #include /* Set up for C function definitions, even when using C++ */ From 6e03914375e3f698a9b1bbd5aa2d80ec0d4a69d8 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 3 Mar 2024 11:36:23 -0800 Subject: [PATCH 184/220] Fixed signed/unsigned comparison warning --- src/video/SDL_yuv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/SDL_yuv.c b/src/video/SDL_yuv.c index b9dbf7726d9d6..2d1d001b26a53 100644 --- a/src/video/SDL_yuv.c +++ b/src/video/SDL_yuv.c @@ -230,7 +230,7 @@ static int GetYUVPlanes(int width, int height, Uint32 format, const void *yuv, i case SDL_PIXELFORMAT_P010: pitches[0] = yuv_pitch; uv_width = ((width + 1) / 2) * 2; - pitches[1] = SDL_max(pitches[0], uv_width * sizeof(Uint16)); + pitches[1] = SDL_max(pitches[0], (int)(uv_width * sizeof(Uint16))); planes[0] = (const Uint8 *)yuv; planes[1] = planes[0] + pitches[0] * height; break; From 5f372426072410c2082af051129a16903284e992 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 3 Mar 2024 11:48:08 -0800 Subject: [PATCH 185/220] Don't clear the clipboard on quit Fixes https://github.com/libsdl-org/SDL/issues/8830 --- src/video/SDL_video.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 7d56633450faa..e998b0eb478a8 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -3788,9 +3788,6 @@ void SDL_VideoQuit(void) return; } - /* Make sure we don't try to serve clipboard data after this */ - SDL_ClearClipboardData(); - /* Halt event processing before doing anything else */ SDL_QuitTouch(); SDL_QuitMouse(); From 1b86a1c684350b68569b908faa9693aa7aa227eb Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 3 Mar 2024 11:55:40 -0800 Subject: [PATCH 186/220] SDL_GetGamepadAppleSFSymbolsNameForAxis() returns NULL if the symbol isn't found --- src/joystick/SDL_gamepad.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index e7f55880c25e3..7b27ec299067d 100644 --- a/src/joystick/SDL_gamepad.c +++ b/src/joystick/SDL_gamepad.c @@ -3771,8 +3771,9 @@ const char *SDL_GetGamepadAppleSFSymbolsNameForAxis(SDL_Gamepad *gamepad, SDL_Ga } SDL_UnlockJoysticks(); - return retval; -#else - return NULL; + if (retval && *retval) { + return retval; + } #endif + return NULL; } From 8d8076263e702e5cf84e6b2819816684793b599f Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 3 Mar 2024 12:18:57 -0800 Subject: [PATCH 187/220] Removed the mapping for the G-Shark GS-GP702 This uses the same chipset as the DragonRise Inc. Generic USB Joystick, which many manufacturers use for different products with different mappings. In order to add a mapping for a controller using this chipset, we need a unique crc for the device name. --- src/joystick/SDL_gamepad_db.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/joystick/SDL_gamepad_db.h b/src/joystick/SDL_gamepad_db.h index 559f52465f6c3..fc103175bb8e7 100644 --- a/src/joystick/SDL_gamepad_db.h +++ b/src/joystick/SDL_gamepad_db.h @@ -110,7 +110,6 @@ static const char *s_GamepadMappings[] = { "03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,", "03000000852100000201000000000000,FF-GP1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", "030000000d0f00002700000000000000,FIGHTING STICK V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,", - "03000000790000000600000000000000,G-Shark GS-GP702,crc:8e4f,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,", "030000008f0e00000d31000000000000,GAMEPAD 3 TURBO,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,", "03000000300f00000b01000000000000,GGE909 Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,", "03000000790000002201000000000000,Game Controller for PC,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,", From ccd309c43327d1884d27705e4739b2609b2ae911 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 3 Mar 2024 12:23:00 -0800 Subject: [PATCH 188/220] Added a mapping for the Sanwa Supply JY-P76USV controller Fixes https://github.com/libsdl-org/SDL/issues/8644 --- src/joystick/SDL_gamepad_db.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/joystick/SDL_gamepad_db.h b/src/joystick/SDL_gamepad_db.h index fc103175bb8e7..a970e0ba1dcc1 100644 --- a/src/joystick/SDL_gamepad_db.h +++ b/src/joystick/SDL_gamepad_db.h @@ -289,6 +289,7 @@ static const char *s_GamepadMappings[] = { "03000000a30600002106000000000000,Saitek PS1000,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,", "03000000a306000020f6000000000000,Saitek PS2700,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,", "03000000300f00001101000000000000,Saitek Rumble Pad,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,", + "03000000790000000600000000000000,Sanwa Supply JY-P76USV,crc:20f0,+leftx:+a0,+rightx:+a2,+righty:a4,-leftx:-a0,-lefty:-a1,-rightx:-a2,-righty:-a4,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,lefty:+a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b2,y:b3,", "0300000000050000289b000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,", "030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,", "030000008f0e00000800000000000000,SpeedLink Strike FX,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,", From 9be35d46036149a71f7ad0562b11da883a91cbf1 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 3 Mar 2024 14:17:03 -0800 Subject: [PATCH 189/220] Convert mappings using labeled buttons to positional buttons We were accidentally skipping all of the mappings that used the SDL_GAMECONTROLLER_USE_BUTTON_LABELS hint, because they used the '!' negate operator with a default hint value of 1. Instead we just want to use that hint to determine whether the mapping has positional buttons or not, and swap the buttons as needed. Fixes https://github.com/libsdl-org/SDL/issues/9190 --- src/joystick/SDL_gamepad.c | 89 +++++++++++++++++++++++++++++++++----- 1 file changed, 77 insertions(+), 12 deletions(-) diff --git a/src/joystick/SDL_gamepad.c b/src/joystick/SDL_gamepad.c index 7b27ec299067d..b964274d65dfd 100644 --- a/src/joystick/SDL_gamepad.c +++ b/src/joystick/SDL_gamepad.c @@ -1885,17 +1885,59 @@ int SDL_ReloadGamepadMappings(void) return 0; } +static char *SDL_ConvertMappingToPositional(const char *mapping) +{ + /* Add space for '!' and null terminator */ + size_t length = SDL_strlen(mapping) + 1 + 1; + char *remapped = (char *)SDL_malloc(length); + if (remapped) { + char *button_A; + char *button_B; + char *button_X; + char *button_Y; + char *hint; + + SDL_strlcpy(remapped, mapping, length); + button_A = SDL_strstr(remapped, "a:"); + button_B = SDL_strstr(remapped, "b:"); + button_X = SDL_strstr(remapped, "x:"); + button_Y = SDL_strstr(remapped, "y:"); + hint = SDL_strstr(remapped, "hint:SDL_GAMECONTROLLER_USE_BUTTON_LABELS"); + + if (button_A) { + *button_A = 'b'; + } + if (button_B) { + *button_B = 'a'; + } + if (button_X) { + *button_X = 'y'; + } + if (button_Y) { + *button_Y = 'x'; + } + if (hint) { + hint += 5; + SDL_memmove(hint + 1, hint, SDL_strlen(hint) + 1); + *hint = '!'; + } + } + return remapped; +} + /* * Add or update an entry into the Mappings Database with a priority */ static int SDL_PrivateAddGamepadMapping(const char *mappingString, SDL_GamepadMappingPriority priority) { + char *remapped = NULL; char *pchGUID; SDL_JoystickGUID jGUID; SDL_bool is_default_mapping = SDL_FALSE; SDL_bool is_xinput_mapping = SDL_FALSE; SDL_bool existing = SDL_FALSE; GamepadMapping_t *pGamepadMapping; + int retval = -1; SDL_AssertJoysticksLocked(); @@ -1934,12 +1976,27 @@ static int SDL_PrivateAddGamepadMapping(const char *mappingString, SDL_GamepadMa default_value = SDL_FALSE; } - value = SDL_GetHintBoolean(hint, default_value); - if (negate) { - value = !value; - } - if (!value) { - return 0; + if (SDL_strcmp(hint, "SDL_GAMECONTROLLER_USE_BUTTON_LABELS") == 0) { + /* This hint is used to signal whether the mapping uses positional buttons or not */ + if (negate) { + /* This mapping uses positional buttons, we can use it as-is */ + } else { + /* This mapping uses labeled buttons, we need to swap them to positional */ + remapped = SDL_ConvertMappingToPositional(mappingString); + if (!remapped) { + goto done; + } + mappingString = remapped; + } + } else { + value = SDL_GetHintBoolean(hint, default_value); + if (negate) { + value = !value; + } + if (!value) { + retval = 0; + goto done; + } } } } @@ -1952,14 +2009,16 @@ static int SDL_PrivateAddGamepadMapping(const char *mappingString, SDL_GamepadMa if (tmp) { tmp += SDL_GAMEPAD_SDKGE_FIELD_SIZE; if (!(SDL_GetAndroidSDKVersion() >= SDL_atoi(tmp))) { - return SDL_SetError("SDK version %d < minimum version %d", SDL_GetAndroidSDKVersion(), SDL_atoi(tmp)); + SDL_SetError("SDK version %d < minimum version %d", SDL_GetAndroidSDKVersion(), SDL_atoi(tmp)); + goto done; } } tmp = SDL_strstr(mappingString, SDL_GAMEPAD_SDKLE_FIELD); if (tmp) { tmp += SDL_GAMEPAD_SDKLE_FIELD_SIZE; if (!(SDL_GetAndroidSDKVersion() <= SDL_atoi(tmp))) { - return SDL_SetError("SDK version %d > maximum version %d", SDL_GetAndroidSDKVersion(), SDL_atoi(tmp)); + SDL_SetError("SDK version %d > maximum version %d", SDL_GetAndroidSDKVersion(), SDL_atoi(tmp)); + goto done; } } } @@ -1967,7 +2026,8 @@ static int SDL_PrivateAddGamepadMapping(const char *mappingString, SDL_GamepadMa pchGUID = SDL_PrivateGetGamepadGUIDFromMappingString(mappingString); if (!pchGUID) { - return SDL_SetError("Couldn't parse GUID from %s", mappingString); + SDL_SetError("Couldn't parse GUID from %s", mappingString); + goto done; } if (!SDL_strcasecmp(pchGUID, "default")) { is_default_mapping = SDL_TRUE; @@ -1979,19 +2039,24 @@ static int SDL_PrivateAddGamepadMapping(const char *mappingString, SDL_GamepadMa pGamepadMapping = SDL_PrivateAddMappingForGUID(jGUID, mappingString, &existing, priority); if (!pGamepadMapping) { - return -1; + goto done; } if (existing) { - return 0; + retval = 0; } else { if (is_default_mapping) { s_pDefaultMapping = pGamepadMapping; } else if (is_xinput_mapping) { s_pXInputMapping = pGamepadMapping; } - return 1; + retval = 1; + } +done: + if (remapped) { + SDL_free(remapped); } + return retval; } /* From ae4484f4e5dc2d9820b6ad2f1c34fc33cf04cf0d Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Sun, 3 Mar 2024 17:55:47 -0500 Subject: [PATCH 190/220] video: Save pending window events when a window is hidden by the window manager The window manager might hide/unmap the window when it is minimized, in which case the fullscreen and maximized flags must be preserved as pending flags so the window will be restored to the proper state when shown/mapped on restoration. --- src/video/SDL_video.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index e998b0eb478a8..e52e80d2a90fe 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -3456,6 +3456,12 @@ void SDL_OnWindowShown(SDL_Window *window) void SDL_OnWindowHidden(SDL_Window *window) { + /* Store the maximized and fullscreen flags for restoration later, in case + * this was initiated by the window manager due to the window being unmapped + * when minimized. + */ + window->pending_flags = (window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)); + /* The window is already hidden at this point, so just change the mode back if necessary. */ SDL_UpdateFullscreenMode(window, SDL_FALSE, SDL_FALSE); } From a4d7ff675106064f8f2019e6495549f7fbdfc8de Mon Sep 17 00:00:00 2001 From: Robert Edmonds Date: Sun, 3 Mar 2024 17:46:14 -0500 Subject: [PATCH 191/220] testffmpeg: Use EGL_EXT_image_dma_buf_import_modifiers extension If the `EGL_EXT_image_dma_buf_import_modifiers` extension is available, propagate the DRM format modifier from the AVDRMObjectDescriptor to eglCreateImage() on Linux. Some hardware will decode video into a non-linear DRM surface, and passing the DRM format modifier to eglCreateImage() is required in order to display something useful. Fixes https://github.com/libsdl-org/SDL/issues/9075 --- test/testffmpeg.c | 71 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 13 deletions(-) diff --git a/test/testffmpeg.c b/test/testffmpeg.c index 4c3ef85b9abd5..cad60bfb70fb9 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -73,6 +73,7 @@ static SDL_bool software_only; static SDL_bool has_eglCreateImage; #ifdef HAVE_EGL static SDL_bool has_EGL_EXT_image_dma_buf_import; +static SDL_bool has_EGL_EXT_image_dma_buf_import_modifiers; static PFNGLACTIVETEXTUREARBPROC glActiveTextureARBFunc; static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOESFunc; #endif @@ -137,11 +138,32 @@ static SDL_bool CreateWindowAndRenderer(Uint32 window_flags, const char *driver) #ifdef HAVE_EGL if (useEGL) { - const char *extensions = eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS); - if (SDL_strstr(extensions, "EGL_EXT_image_dma_buf_import") != NULL) { - has_EGL_EXT_image_dma_buf_import = SDL_TRUE; + const char *egl_extensions = eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS); + if (!egl_extensions) { + return SDL_FALSE; + } + + char *extensions = SDL_strdup(egl_extensions); + if (!extensions) { + return SDL_FALSE; } + char *saveptr, *token; + token = SDL_strtok_r(extensions, " ", &saveptr); + if (!token) { + free(extensions); + return SDL_FALSE; + } + do { + if (SDL_strcmp(token, "EGL_EXT_image_dma_buf_import") == 0) { + has_EGL_EXT_image_dma_buf_import = SDL_TRUE; + } else if (SDL_strcmp(token, "EGL_EXT_image_dma_buf_import_modifiers") == 0) { + has_EGL_EXT_image_dma_buf_import_modifiers = SDL_TRUE; + } + } while ((token = SDL_strtok_r(NULL, " ", &saveptr)) != NULL); + + free(extensions); + if (SDL_GL_ExtensionSupported("GL_OES_EGL_image")) { glEGLImageTargetTexture2DOESFunc = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES"); } @@ -609,16 +631,39 @@ static SDL_bool GetTextureForDRMFrame(AVFrame *frame, SDL_Texture **texture) static const uint32_t formats[ 2 ] = { DRM_FORMAT_R8, DRM_FORMAT_GR88 }; const AVDRMPlaneDescriptor *plane = &layer->planes[j]; const AVDRMObjectDescriptor *object = &desc->objects[plane->object_index]; - EGLAttrib img_attr[] = { - EGL_LINUX_DRM_FOURCC_EXT, formats[i], - EGL_WIDTH, frame->width / ( image_index + 1 ), /* half size for chroma */ - EGL_HEIGHT, frame->height / ( image_index + 1 ), - EGL_DMA_BUF_PLANE0_FD_EXT, object->fd, - EGL_DMA_BUF_PLANE0_OFFSET_EXT, plane->offset, - EGL_DMA_BUF_PLANE0_PITCH_EXT, plane->pitch, - EGL_NONE - }; - EGLImage pImage = eglCreateImage(display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr); + + EGLAttrib attr[32]; + size_t k = 0; + + attr[k++] = EGL_LINUX_DRM_FOURCC_EXT; + attr[k++] = formats[i]; + + attr[k++] = EGL_WIDTH; + attr[k++] = frame->width / ( image_index + 1 ); /* half size for chroma */ + + attr[k++] = EGL_HEIGHT; + attr[k++] = frame->height / ( image_index + 1 ); + + attr[k++] = EGL_DMA_BUF_PLANE0_FD_EXT; + attr[k++] = object->fd; + + attr[k++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; + attr[k++] = plane->offset; + + attr[k++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; + attr[k++] = plane->pitch; + + if (has_EGL_EXT_image_dma_buf_import_modifiers) { + attr[k++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; + attr[k++] = (object->format_modifier >> 0) & 0xFFFFFFFF; + + attr[k++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; + attr[k++] = (object->format_modifier >> 32) & 0xFFFFFFFF; + } + + attr[k++] = EGL_NONE; + + EGLImage pImage = eglCreateImage(display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attr); glActiveTextureARBFunc(GL_TEXTURE0_ARB + image_index); glBindTexture(GL_TEXTURE_2D, textures[image_index]); From 4189edaeb7ce4da9ab420b98904fa22196fb3dca Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sun, 3 Mar 2024 16:56:29 -0800 Subject: [PATCH 192/220] Uppercase the first letter of the class name to match Java conventions Fixes https://github.com/libsdl-org/SDL/issues/8930 --- build-scripts/androidbuild.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build-scripts/androidbuild.sh b/build-scripts/androidbuild.sh index 814578182432c..ec71cbcad0b61 100755 --- a/build-scripts/androidbuild.sh +++ b/build-scripts/androidbuild.sh @@ -80,7 +80,8 @@ do cd $folder done -ACTIVITY="${folder}Activity" +# Uppercase the first char in the activity class name because it's Java +ACTIVITY="${folder^}Activity" sed -i -e "s|\"SDLActivity\"|\"$ACTIVITY\"|g" $BUILDPATH/app/src/main/AndroidManifest.xml # Fill in a default Activity From 1e790b20c9b44986d405fdbaaeba5ebbf319b386 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Mon, 4 Mar 2024 10:29:43 -0500 Subject: [PATCH 193/220] video: Don't overwrite all the pending flags in the OnWindowHidden handler Other flags may have been set when programmatically hiding a window, so or in the fullscreen and maximized flags to avoid accidentally clearing any others. --- src/video/SDL_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index e52e80d2a90fe..a1fb6b3fb3099 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -3460,7 +3460,7 @@ void SDL_OnWindowHidden(SDL_Window *window) * this was initiated by the window manager due to the window being unmapped * when minimized. */ - window->pending_flags = (window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)); + window->pending_flags |= (window->flags & (SDL_WINDOW_FULLSCREEN | SDL_WINDOW_MAXIMIZED)); /* The window is already hidden at this point, so just change the mode back if necessary. */ SDL_UpdateFullscreenMode(window, SDL_FALSE, SDL_FALSE); From 504d8c2fc00641e1c17863965d730539afc9b823 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 4 Mar 2024 07:46:24 -0800 Subject: [PATCH 194/220] Fixed potential memory leak if vkCreateInstance() fails --- src/render/vulkan/SDL_render_vulkan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 1222cc8f43831..7c3b0fd7a54ca 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -1702,11 +1702,11 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert instanceCreateInfo.enabledLayerCount = 1; } result = vkCreateInstance(&instanceCreateInfo, NULL, &rendererData->instance); + SDL_free((void *)instanceExtensionsCopy); if (result != VK_SUCCESS) { SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateInstance(): %s\n", SDL_Vulkan_GetResultString(result)); return result; } - SDL_free((void *)instanceExtensionsCopy); } /* Load instance Vulkan functions */ From 48471f7dbd96365dd5471d06a92275a80954667d Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 2 Mar 2024 10:03:37 -0800 Subject: [PATCH 195/220] Added SDL_AddVulkanRenderSemaphores() for external synchronization with SDL rendering --- include/SDL3/SDL_render.h | 21 ++++++ src/dynapi/SDL_dynapi.sym | 1 + src/dynapi/SDL_dynapi_overrides.h | 1 + src/dynapi/SDL_dynapi_procs.h | 1 + src/render/SDL_render.c | 10 +++ src/render/SDL_sysrender.h | 2 + src/render/vulkan/SDL_render_vulkan.c | 98 +++++++++++++++++++++++++-- 7 files changed, 129 insertions(+), 5 deletions(-) diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 1e290e1ca1ce1..7c99a40db7327 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -407,6 +407,7 @@ extern DECLSPEC int SDLCALL SDL_GetRendererInfo(SDL_Renderer *renderer, SDL_Rend * family index used for rendering * - `SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER`: the queue * family index used for presentation + * - `SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER`: the number of swapchain images, or potential frames in flight, used by the Vulkan renderer * * \param renderer the rendering context * \returns a valid property ID on success or 0 on failure; call @@ -436,6 +437,7 @@ extern DECLSPEC SDL_PropertiesID SDLCALL SDL_GetRendererProperties(SDL_Renderer #define SDL_PROP_RENDERER_VULKAN_DEVICE_POINTER "SDL.renderer.vulkan.device" #define SDL_PROP_RENDERER_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER "SDL.renderer.vulkan.graphics_queue_family_index" #define SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER "SDL.renderer.vulkan.present_queue_family_index" +#define SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER "SDL.renderer.vulkan.swapchain_image_count" /** * Get the output size in pixels of a rendering context. @@ -2104,6 +2106,25 @@ extern DECLSPEC void *SDLCALL SDL_GetRenderMetalLayer(SDL_Renderer *renderer); */ extern DECLSPEC void *SDLCALL SDL_GetRenderMetalCommandEncoder(SDL_Renderer *renderer); + +/** + * Add a set of synchronization semaphores for the current frame. + * + * The Vulkan renderer will wait for `wait_semaphore` before submitting rendering commands and signal `signal_semaphore` after rendering commands are complete for this frame. + * + * This should be called each frame that you want semaphore synchronization. The Vulkan renderer may have multiple frames in flight on the GPU, so you should have multiple semaphores that are used for synchronization. Querying SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER will give you the maximum number of semaphores you'll need. + * + * \param renderer the rendering context + * \param wait_stage_mask the VkPipelineStageFlags for the wait + * \param wait_semaphore a VkSempahore to wait on before rendering the current frame, or 0 if not needed + * \param signal_semaphore a VkSempahore that SDL will signal when rendering for the current frame is complete, or 0 if not needed + * \returns 0 on success or a negative error code on failure; call + * SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC int SDLCALL SDL_AddVulkanRenderSemaphores(SDL_Renderer *renderer, Uint32 wait_stage_mask, Sint64 wait_semaphore, Sint64 signal_semaphore); + /** * Toggle VSync of the given renderer. * diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index e351d9f0890ea..5d08979e090cd 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -973,6 +973,7 @@ SDL3_0.0.0 { SDL_GetCameraDevicePosition; SDL_qsort_r; SDL_bsearch_r; + SDL_AddVulkanRenderSemaphores; # 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 ad8030b6c407c..c5378f1fd77fb 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -998,3 +998,4 @@ #define SDL_GetCameraDevicePosition SDL_GetCameraDevicePosition_REAL #define SDL_qsort_r SDL_qsort_r_REAL #define SDL_bsearch_r SDL_bsearch_r_REAL +#define SDL_AddVulkanRenderSemaphores SDL_AddVulkanRenderSemaphores_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 3971dab5711b3..b19f615cb83f4 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -1023,3 +1023,4 @@ SDL_DYNAPI_PROC(int,SDL_GetCameraPermissionState,(SDL_Camera *a),(a),return) SDL_DYNAPI_PROC(SDL_CameraPosition,SDL_GetCameraDevicePosition,(SDL_CameraDeviceID a),(a),return) SDL_DYNAPI_PROC(void,SDL_qsort_r,(void *a, size_t b, size_t c, int (SDLCALL *d)(void *, const void *, const void *), void *e),(a,b,c,d,e),) SDL_DYNAPI_PROC(void*,SDL_bsearch_r,(const void *a, const void *b, size_t c, size_t d, int (SDLCALL *e)(void *, const void *, const void *), void *f),(a,b,c,d,e,f),return) +SDL_DYNAPI_PROC(int,SDL_AddVulkanRenderSemaphores,(SDL_Renderer *a, Uint32 b, Sint64 c, Sint64 d),(a,b,c,d),return) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index a5d1470832f51..72f23cd699ee4 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -4565,6 +4565,16 @@ void *SDL_GetRenderMetalCommandEncoder(SDL_Renderer *renderer) return NULL; } +int SDL_AddVulkanRenderSemaphores(SDL_Renderer *renderer, Uint32 wait_stage_mask, Sint64 wait_semaphore, Sint64 signal_semaphore) +{ + CHECK_RENDERER_MAGIC(renderer, -1); + + if (!renderer->AddVulkanRenderSemaphores) { + return SDL_Unsupported(); + } + return renderer->AddVulkanRenderSemaphores(renderer, wait_stage_mask, wait_semaphore, signal_semaphore); +} + static SDL_BlendMode SDL_GetShortBlendMode(SDL_BlendMode blendMode) { if (blendMode == SDL_BLENDMODE_NONE_FULL) { diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 9ab2a7979c4c2..99889f3f88170 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -216,6 +216,8 @@ struct SDL_Renderer void *(*GetMetalLayer)(SDL_Renderer *renderer); void *(*GetMetalCommandEncoder)(SDL_Renderer *renderer); + int (*AddVulkanRenderSemaphores)(SDL_Renderer *renderer, Uint32 wait_stage_mask, Sint64 wait_semaphore, Sint64 signal_semaphore); + /* The current renderer info */ SDL_RendererInfo info; diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 7c3b0fd7a54ca..b80d130dfebd3 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -343,6 +343,14 @@ typedef struct VkSemaphore *renderingFinishedSemaphores; uint32_t currentSwapchainImageIndex; + VkPipelineStageFlags *waitDestStageMasks; + VkSemaphore *waitRenderSemaphores; + uint32_t waitRenderSemaphoreCount; + uint32_t waitRenderSemaphoreMax; + VkSemaphore *signalRenderSemaphores; + uint32_t signalRenderSemaphoreCount; + uint32_t signalRenderSemaphoreMax; + /* Cached renderer properties */ VULKAN_TextureData *textureRenderTarget; SDL_bool cliprectDirty; @@ -454,6 +462,18 @@ static void VULKAN_DestroyAll(SDL_Renderer *renderer) return; } + if (rendererData->waitDestStageMasks) { + SDL_free(rendererData->waitDestStageMasks); + rendererData->waitDestStageMasks = NULL; + } + if (rendererData->waitRenderSemaphores) { + SDL_free(rendererData->waitRenderSemaphores); + rendererData->waitRenderSemaphores = NULL; + } + if (rendererData->signalRenderSemaphores) { + SDL_free(rendererData->signalRenderSemaphores); + rendererData->signalRenderSemaphores = NULL; + } if (rendererData->surfaceFormats != NULL) { SDL_free(rendererData->surfaceFormats); rendererData->surfaceFormats = NULL; @@ -1009,6 +1029,12 @@ static VkResult VULKAN_IssueBatch(VULKAN_RenderData *rendererData) submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &rendererData->currentCommandBuffer; + if (rendererData->waitRenderSemaphoreCount > 0) { + submitInfo.waitSemaphoreCount = rendererData->waitRenderSemaphoreCount; + submitInfo.pWaitSemaphores = rendererData->waitRenderSemaphores; + submitInfo.pWaitDstStageMask = rendererData->waitDestStageMasks; + rendererData->waitRenderSemaphoreCount = 0; + } result = vkQueueSubmit(rendererData->graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); VULKAN_WaitForGPU(rendererData); @@ -2351,6 +2377,9 @@ static VkResult VULKAN_CreateSwapChain(SDL_Renderer *renderer, int w, int h) VULKAN_AcquireNextSwapchainImage(renderer); + SDL_PropertiesID props = SDL_GetRendererProperties(renderer); + SDL_SetNumberProperty(props, SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER, rendererData->swapchainImageCount); + return result; } @@ -3833,6 +3862,48 @@ static SDL_Surface* VULKAN_RenderReadPixels(SDL_Renderer *renderer, const SDL_Re return output; } +static int VULKAN_AddVulkanRenderSemaphores(SDL_Renderer *renderer, Uint32 wait_stage_mask, Sint64 wait_semaphore, Sint64 signal_semaphore) +{ + VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; + + if (wait_semaphore) { + if (rendererData->waitRenderSemaphoreCount == rendererData->waitRenderSemaphoreMax) { + /* Allocate an additional one at the end for the normal present wait */ + VkPipelineStageFlags *waitDestStageMasks = (VkPipelineStageFlags *)SDL_realloc(rendererData->waitDestStageMasks, (rendererData->waitRenderSemaphoreMax + 2) * sizeof(*waitDestStageMasks)); + if (!waitDestStageMasks) { + return -1; + } + rendererData->waitDestStageMasks = waitDestStageMasks; + + VkSemaphore *semaphores = (VkSemaphore *)SDL_realloc(rendererData->waitRenderSemaphores, (rendererData->waitRenderSemaphoreMax + 2) * sizeof(*semaphores)); + if (!semaphores) { + return -1; + } + rendererData->waitRenderSemaphores = semaphores; + ++rendererData->waitRenderSemaphoreMax; + } + rendererData->waitDestStageMasks[rendererData->waitRenderSemaphoreCount] = wait_stage_mask; + rendererData->waitRenderSemaphores[rendererData->waitRenderSemaphoreCount] = (VkSemaphore)wait_semaphore; + ++rendererData->waitRenderSemaphoreCount; + } + + if (signal_semaphore) { + if (rendererData->signalRenderSemaphoreCount == rendererData->signalRenderSemaphoreMax) { + /* Allocate an additional one at the end for the normal present signal */ + VkSemaphore *semaphores = (VkSemaphore *)SDL_realloc(rendererData->signalRenderSemaphores, (rendererData->signalRenderSemaphoreMax + 2) * sizeof(*semaphores)); + if (!semaphores) { + return -1; + } + rendererData->signalRenderSemaphores = semaphores; + ++rendererData->signalRenderSemaphoreMax; + } + rendererData->signalRenderSemaphores[rendererData->signalRenderSemaphoreCount] = (VkSemaphore)signal_semaphore; + ++rendererData->signalRenderSemaphoreCount; + } + + return 0; +} + static int VULKAN_RenderPresent(SDL_Renderer *renderer) { VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->driverdata; @@ -3863,13 +3934,29 @@ static int VULKAN_RenderPresent(SDL_Renderer *renderer) VkPipelineStageFlags waitDestStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo submitInfo = { 0 }; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; - submitInfo.waitSemaphoreCount = 1; - submitInfo.pWaitSemaphores = &rendererData->imageAvailableSemaphores[rendererData->currentCommandBufferIndex]; - submitInfo.pWaitDstStageMask = &waitDestStageMask; + if (rendererData->waitRenderSemaphoreCount > 0) { + submitInfo.waitSemaphoreCount = rendererData->waitRenderSemaphoreCount + 1; + rendererData->waitRenderSemaphores[rendererData->waitRenderSemaphoreCount] = rendererData->imageAvailableSemaphores[rendererData->currentCommandBufferIndex]; + rendererData->waitDestStageMasks[rendererData->waitRenderSemaphoreCount] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + submitInfo.pWaitSemaphores = rendererData->waitRenderSemaphores; + submitInfo.pWaitDstStageMask = rendererData->waitDestStageMasks; + rendererData->waitRenderSemaphoreCount = 0; + } else { + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = &rendererData->imageAvailableSemaphores[rendererData->currentCommandBufferIndex]; + submitInfo.pWaitDstStageMask = &waitDestStageMask; + } submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &rendererData->currentCommandBuffer; - submitInfo.signalSemaphoreCount = 1; - submitInfo.pSignalSemaphores = &rendererData->renderingFinishedSemaphores[rendererData->currentCommandBufferIndex]; + if (rendererData->signalRenderSemaphoreCount > 0) { + submitInfo.signalSemaphoreCount = rendererData->signalRenderSemaphoreCount + 1; + rendererData->signalRenderSemaphores[rendererData->signalRenderSemaphoreCount] = rendererData->renderingFinishedSemaphores[rendererData->currentCommandBufferIndex]; + submitInfo.pSignalSemaphores = rendererData->signalRenderSemaphores; + rendererData->signalRenderSemaphoreCount = 0; + } else { + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = &rendererData->renderingFinishedSemaphores[rendererData->currentCommandBufferIndex]; + } result = vkQueueSubmit(rendererData->graphicsQueue, 1, &submitInfo, rendererData->fences[rendererData->currentCommandBufferIndex]); if (result != VK_SUCCESS) { SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkQueueSubmit(): %s\n", SDL_Vulkan_GetResultString(result)); @@ -3973,6 +4060,7 @@ SDL_Renderer *VULKAN_CreateRenderer(SDL_Window *window, SDL_PropertiesID create_ renderer->InvalidateCachedState = VULKAN_InvalidateCachedState; renderer->RunCommandQueue = VULKAN_RunCommandQueue; renderer->RenderReadPixels = VULKAN_RenderReadPixels; + renderer->AddVulkanRenderSemaphores = VULKAN_AddVulkanRenderSemaphores; renderer->RenderPresent = VULKAN_RenderPresent; renderer->DestroyTexture = VULKAN_DestroyTexture; renderer->DestroyRenderer = VULKAN_DestroyRenderer; From dbec2150d0e52b54498ac78e8d9d2d8d804c85f8 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Sat, 2 Mar 2024 10:05:38 -0800 Subject: [PATCH 196/220] testffmpeg: added support for Vulkan rendering --- test/CMakeLists.txt | 7 +- test/testffmpeg.c | 105 ++++- test/testffmpeg_vulkan.c | 974 +++++++++++++++++++++++++++++++++++++++ test/testffmpeg_vulkan.h | 22 + 4 files changed, 1088 insertions(+), 20 deletions(-) create mode 100644 test/testffmpeg_vulkan.c create mode 100644 test/testffmpeg_vulkan.h diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d09c81e0666f8..4f0fc5331b25e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -224,11 +224,13 @@ include("${SDL3_SOURCE_DIR}/cmake/FindFFmpeg.cmake") if(FFmpeg_FOUND) cmake_push_check_state() list(APPEND CMAKE_REQUIRED_INCLUDES "${FFmpeg_AVUTIL_INCLUDE_DIRS}") + list(APPEND CMAKE_REQUIRED_INCLUDES "${SDL3_SOURCE_DIR}/src/video/khronos") check_struct_has_member("AVFrame" "ch_layout" "libavutil/frame.h" LIBAVUTIL_AVFRAME_HAS_CH_LAYOUT) + check_struct_has_member("AVVulkanFramesContext" "format" "libavutil/hwcontext_vulkan.h" LIBAVUTIL_AVFULKANFRAMESCONTEXT_HAS_FORMAT) cmake_pop_check_state() endif() -if(FFmpeg_FOUND AND LIBAVUTIL_AVFRAME_HAS_CH_LAYOUT) - add_sdl_test_executable(testffmpeg NO_C90 SOURCES testffmpeg.c ${icon_bmp_header}) +if(FFmpeg_FOUND AND LIBAVUTIL_AVFRAME_HAS_CH_LAYOUT AND LIBAVUTIL_AVFULKANFRAMESCONTEXT_HAS_FORMAT) + add_sdl_test_executable(testffmpeg NO_C90 SOURCES testffmpeg.c testffmpeg_vulkan.c ${icon_bmp_header}) if(APPLE) target_link_options(testffmpeg PRIVATE "-Wl,-framework,CoreVideo") endif() @@ -237,6 +239,7 @@ if(FFmpeg_FOUND AND LIBAVUTIL_AVFRAME_HAS_CH_LAYOUT) target_link_libraries(testffmpeg PRIVATE OpenGL::EGL) target_compile_definitions(testffmpeg PRIVATE HAVE_EGL) endif() + target_include_directories(testffmpeg BEFORE PRIVATE ${SDL3_SOURCE_DIR}/src/video/khronos) target_link_libraries(testffmpeg PRIVATE ${FFMPEG_LIBRARIES}) else() message(STATUS "Can't find ffmpeg 5.1.3 or newer, skipping testffmpeg") diff --git a/test/testffmpeg.c b/test/testffmpeg.c index cad60bfb70fb9..35e91265c11b2 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -55,6 +55,8 @@ #include #endif /* SDL_PLATFORM_WIN32 */ +#include "testffmpeg_vulkan.h" + #include "icon.h" @@ -82,6 +84,7 @@ static ID3D11Device *d3d11_device; static ID3D11DeviceContext *d3d11_context; static const GUID SDL_IID_ID3D11Resource = { 0xdc8e63f3, 0xd12b, 0x4952, { 0xb4, 0x7b, 0x5e, 0x45, 0x02, 0x6a, 0x86, 0x2d } }; #endif +static VulkanVideoContext *vulkan_context; struct SwsContextContainer { struct SwsContext *context; @@ -93,35 +96,59 @@ static SDL_bool CreateWindowAndRenderer(Uint32 window_flags, const char *driver) { SDL_PropertiesID props; SDL_RendererInfo info; + SDL_bool useOpenGL = (driver && (SDL_strcmp(driver, "opengl") == 0 || SDL_strcmp(driver, "opengles2") == 0)); SDL_bool useEGL = (driver && SDL_strcmp(driver, "opengles2") == 0); + SDL_bool useVulkan = (driver && SDL_strcmp(driver, "vulkan") == 0); + Uint32 flags = SDL_WINDOW_HIDDEN; + + if (useOpenGL) { + if (useEGL) { + SDL_SetHint(SDL_HINT_VIDEO_FORCE_EGL, "1"); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + } else { + SDL_SetHint(SDL_HINT_VIDEO_FORCE_EGL, "0"); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + } + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); - SDL_SetHint(SDL_HINT_RENDER_DRIVER, driver); - if (useEGL) { - SDL_SetHint(SDL_HINT_VIDEO_FORCE_EGL, "1"); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); - } else { - SDL_SetHint(SDL_HINT_VIDEO_FORCE_EGL, "0"); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, 0); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + flags |= SDL_WINDOW_OPENGL; + } + if (useVulkan) { + flags |= SDL_WINDOW_VULKAN; } - SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); - SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); - SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); /* The window will be resized to the video size when it's loaded, in OpenVideoStream() */ - window = SDL_CreateWindow("testffmpeg", 1920, 1080, 0); + window = SDL_CreateWindow("testffmpeg", 1920, 1080, flags); if (!window) { return SDL_FALSE; } + if (useVulkan) { + vulkan_context = CreateVulkanVideoContext(window); + if (!vulkan_context) { + SDL_DestroyWindow(window); + window = NULL; + return SDL_FALSE; + } + } + props = SDL_CreateProperties(); SDL_SetStringProperty(props, SDL_PROP_RENDERER_CREATE_NAME_STRING, driver); SDL_SetProperty(props, SDL_PROP_RENDERER_CREATE_WINDOW_POINTER, window); - SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB_LINEAR); - renderer = SDL_CreateRendererWithProperties(props); + if (useVulkan) { + SetupVulkanRenderProperties(vulkan_context, props); + } + if (SDL_GetBooleanProperty(SDL_GetDisplayProperties(SDL_GetDisplayForWindow(window)), SDL_PROP_DISPLAY_HDR_ENABLED_BOOLEAN, SDL_FALSE)) { + /* Try to create an HDR capable renderer */ + SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB_LINEAR); + renderer = SDL_CreateRendererWithProperties(props); + } if (!renderer) { /* Try again with the sRGB colorspace */ SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_OUTPUT_COLORSPACE_NUMBER, SDL_COLORSPACE_SRGB); @@ -129,6 +156,8 @@ static SDL_bool CreateWindowAndRenderer(Uint32 window_flags, const char *driver) } SDL_DestroyProperties(props); if (!renderer) { + SDL_DestroyWindow(window); + window = NULL; return SDL_FALSE; } @@ -285,6 +314,8 @@ static Uint32 GetTextureFormat(enum AVPixelFormat format) return SDL_PIXELFORMAT_NV12; case AV_PIX_FMT_NV21: return SDL_PIXELFORMAT_NV21; + case AV_PIX_FMT_P010: + return SDL_PIXELFORMAT_P010; default: return SDL_PIXELFORMAT_UNKNOWN; } @@ -307,6 +338,9 @@ static SDL_bool SupportedPixelFormat(enum AVPixelFormat format) return SDL_TRUE; } #endif + if (format == AV_PIX_FMT_VULKAN) { + return SDL_TRUE; + } } if (GetTextureFormat(format) != SDL_PIXELFORMAT_UNKNOWN) { @@ -409,7 +443,21 @@ static AVCodecContext *OpenVideoStream(AVFormatContext *ic, int stream, const AV } } else #endif - { + if (vulkan_context && type == AV_HWDEVICE_TYPE_VULKAN) { + AVVulkanDeviceContext *device_context; + + context->hw_device_ctx = av_hwdevice_ctx_alloc(type); + + device_context = (AVVulkanDeviceContext *)((AVHWDeviceContext *)context->hw_device_ctx->data)->hwctx; + SetupVulkanDeviceContextData(vulkan_context, device_context); + + result = av_hwdevice_ctx_init(context->hw_device_ctx); + if (result < 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create hardware device context: %s", av_err2str(result)); + } else { + SDL_Log("Using %s hardware acceleration with pixel format %s\n", av_hwdevice_get_type_name(config->device_type), av_get_pix_fmt_name(config->pix_fmt)); + } + } else { result = av_hwdevice_ctx_create(&context->hw_device_ctx, type, NULL, NULL, 0); if (result < 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create hardware device context: %s", av_err2str(result)); @@ -799,6 +847,22 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex #endif } +static SDL_bool GetTextureForVulkanFrame(AVFrame *frame, SDL_Texture **texture) +{ + SDL_PropertiesID props; + if (*texture) { + SDL_DestroyTexture(*texture); + } + + props = CreateVideoTextureProperties(frame, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_STATIC, frame->width, frame->height); + *texture = CreateVulkanVideoTexture(vulkan_context, frame, renderer, props); + SDL_DestroyProperties(props); + if (!*texture) { + return SDL_FALSE; + } + return SDL_TRUE; +} + static SDL_bool GetTextureForFrame(AVFrame *frame, SDL_Texture **texture) { switch (frame->format) { @@ -810,6 +874,8 @@ static SDL_bool GetTextureForFrame(AVFrame *frame, SDL_Texture **texture) return GetTextureForD3D11Frame(frame, texture); case AV_PIX_FMT_VIDEOTOOLBOX: return GetTextureForVideoToolboxFrame(frame, texture); + case AV_PIX_FMT_VULKAN: + return GetTextureForVulkanFrame(frame, texture); default: return GetTextureForMemoryFrame(frame, texture); } @@ -1277,6 +1343,9 @@ int main(int argc, char *argv[]) avcodec_free_context(&video_context); avformat_close_input(&ic); SDL_DestroyRenderer(renderer); + if (vulkan_context) { + DestroyVulkanVideoContext(vulkan_context); + } SDL_DestroyWindow(window); SDL_Quit(); SDLTest_CommonDestroyState(state); diff --git a/test/testffmpeg_vulkan.c b/test/testffmpeg_vulkan.c new file mode 100644 index 0000000000000..5d7fbc5d02788 --- /dev/null +++ b/test/testffmpeg_vulkan.c @@ -0,0 +1,974 @@ +/* + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely. +*/ +#include +#include + +#include +#include + +#include "testffmpeg_vulkan.h" + +#define VULKAN_FUNCTIONS() \ + VULKAN_GLOBAL_FUNCTION(vkCreateInstance) \ + VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceExtensionProperties) \ + VULKAN_GLOBAL_FUNCTION(vkEnumerateInstanceLayerProperties) \ + VULKAN_INSTANCE_FUNCTION(vkCreateDevice) \ + VULKAN_INSTANCE_FUNCTION(vkDestroyInstance) \ + VULKAN_INSTANCE_FUNCTION(vkDestroySurfaceKHR) \ + VULKAN_INSTANCE_FUNCTION(vkEnumerateDeviceExtensionProperties) \ + VULKAN_INSTANCE_FUNCTION(vkEnumeratePhysicalDevices) \ + VULKAN_INSTANCE_FUNCTION(vkGetDeviceProcAddr) \ + VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceFeatures2) \ + VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties) \ + VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR) \ + VULKAN_INSTANCE_FUNCTION(vkQueueWaitIdle) \ + VULKAN_DEVICE_FUNCTION(vkAllocateCommandBuffers) \ + VULKAN_DEVICE_FUNCTION(vkBeginCommandBuffer) \ + VULKAN_DEVICE_FUNCTION(vkCmdPipelineBarrier2) \ + VULKAN_DEVICE_FUNCTION(vkCreateCommandPool) \ + VULKAN_DEVICE_FUNCTION(vkCreateSemaphore) \ + VULKAN_DEVICE_FUNCTION(vkDestroyCommandPool) \ + VULKAN_DEVICE_FUNCTION(vkDestroyDevice) \ + VULKAN_DEVICE_FUNCTION(vkDestroySemaphore) \ + VULKAN_DEVICE_FUNCTION(vkDeviceWaitIdle) \ + VULKAN_DEVICE_FUNCTION(vkEndCommandBuffer) \ + VULKAN_DEVICE_FUNCTION(vkFreeCommandBuffers) \ + VULKAN_DEVICE_FUNCTION(vkGetDeviceQueue) \ + VULKAN_DEVICE_FUNCTION(vkQueueSubmit) \ +\ +VULKAN_INSTANCE_FUNCTION(vkGetPhysicalDeviceVideoFormatPropertiesKHR) \ + +typedef struct +{ + VkPhysicalDeviceFeatures2 device_features; + VkPhysicalDeviceVulkan11Features device_features_1_1; + VkPhysicalDeviceVulkan12Features device_features_1_2; + VkPhysicalDeviceVulkan13Features device_features_1_3; + VkPhysicalDeviceDescriptorBufferFeaturesEXT desc_buf_features; + VkPhysicalDeviceShaderAtomicFloatFeaturesEXT atomic_float_features; + VkPhysicalDeviceCooperativeMatrixFeaturesKHR coop_matrix_features; +} VulkanDeviceFeatures; + +struct VulkanVideoContext +{ + VkInstance instance; + VkSurfaceKHR surface; + VkPhysicalDevice physicalDevice; + int presentQueueFamilyIndex; + int presentQueueCount; + int graphicsQueueFamilyIndex; + int graphicsQueueCount; + int transferQueueFamilyIndex; + int transferQueueCount; + int computeQueueFamilyIndex; + int computeQueueCount; + int decodeQueueFamilyIndex; + int decodeQueueCount; + VkDevice device; + VkQueue graphicsQueue; + VkCommandPool commandPool; + VkCommandBuffer *commandBuffers; + uint32_t commandBufferCount; + uint32_t commandBufferIndex; + VkSemaphore *waitSemaphores; + uint32_t waitSemaphoreCount; + VkSemaphore *signalSemaphores; + uint32_t signalSemaphoreCount; + + const char **instanceExtensions; + int instanceExtensionsCount; + + const char **deviceExtensions; + int deviceExtensionsCount; + + VulkanDeviceFeatures features; + + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; +#define VULKAN_GLOBAL_FUNCTION(name) PFN_##name name; +#define VULKAN_INSTANCE_FUNCTION(name) PFN_##name name; +#define VULKAN_DEVICE_FUNCTION(name) PFN_##name name; + VULKAN_FUNCTIONS() +#undef VULKAN_GLOBAL_FUNCTION +#undef VULKAN_INSTANCE_FUNCTION +#undef VULKAN_DEVICE_FUNCTION +}; + + +static int loadGlobalFunctions(VulkanVideoContext *context) +{ + context->vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr(); + if (!context->vkGetInstanceProcAddr) { + return -1; + } + +#define VULKAN_GLOBAL_FUNCTION(name) \ + context->name = (PFN_##name)context->vkGetInstanceProcAddr(VK_NULL_HANDLE, #name); \ + if (!context->name) { \ + return SDL_SetError("vkGetInstanceProcAddr(VK_NULL_HANDLE, \"" #name "\") failed"); \ + } +#define VULKAN_INSTANCE_FUNCTION(name) +#define VULKAN_DEVICE_FUNCTION(name) + VULKAN_FUNCTIONS() +#undef VULKAN_GLOBAL_FUNCTION +#undef VULKAN_INSTANCE_FUNCTION +#undef VULKAN_DEVICE_FUNCTION + return 0; +} + +static int loadInstanceFunctions(VulkanVideoContext *context) +{ +#define VULKAN_GLOBAL_FUNCTION(name) +#define VULKAN_INSTANCE_FUNCTION(name) \ + context->name = (PFN_##name)context->vkGetInstanceProcAddr(context->instance, #name); \ + if (!context->name) { \ + return SDL_SetError("vkGetInstanceProcAddr(instance, \"" #name "\") failed"); \ + } +#define VULKAN_DEVICE_FUNCTION(name) + VULKAN_FUNCTIONS() +#undef VULKAN_GLOBAL_FUNCTION +#undef VULKAN_INSTANCE_FUNCTION +#undef VULKAN_DEVICE_FUNCTION + return 0; +} + +static int loadDeviceFunctions(VulkanVideoContext *context) +{ +#define VULKAN_GLOBAL_FUNCTION(name) +#define VULKAN_INSTANCE_FUNCTION(name) +#define VULKAN_DEVICE_FUNCTION(name) \ + context->name = (PFN_##name)context->vkGetDeviceProcAddr(context->device, #name); \ + if (!context->name) { \ + return SDL_SetError("vkGetDeviceProcAddr(device, \"" #name "\") failed\n"); \ + } + VULKAN_FUNCTIONS() +#undef VULKAN_GLOBAL_FUNCTION +#undef VULKAN_INSTANCE_FUNCTION +#undef VULKAN_DEVICE_FUNCTION + return 0; +} + +#undef VULKAN_FUNCTIONS + +static const char *getVulkanResultString(VkResult result) +{ + switch ((int)result) { +#define RESULT_CASE(x) \ + case x: \ + return #x + RESULT_CASE(VK_SUCCESS); + RESULT_CASE(VK_NOT_READY); + RESULT_CASE(VK_TIMEOUT); + RESULT_CASE(VK_EVENT_SET); + RESULT_CASE(VK_EVENT_RESET); + RESULT_CASE(VK_INCOMPLETE); + RESULT_CASE(VK_ERROR_OUT_OF_HOST_MEMORY); + RESULT_CASE(VK_ERROR_OUT_OF_DEVICE_MEMORY); + RESULT_CASE(VK_ERROR_INITIALIZATION_FAILED); + RESULT_CASE(VK_ERROR_DEVICE_LOST); + RESULT_CASE(VK_ERROR_MEMORY_MAP_FAILED); + RESULT_CASE(VK_ERROR_LAYER_NOT_PRESENT); + RESULT_CASE(VK_ERROR_EXTENSION_NOT_PRESENT); + RESULT_CASE(VK_ERROR_FEATURE_NOT_PRESENT); + RESULT_CASE(VK_ERROR_INCOMPATIBLE_DRIVER); + RESULT_CASE(VK_ERROR_TOO_MANY_OBJECTS); + RESULT_CASE(VK_ERROR_FORMAT_NOT_SUPPORTED); + RESULT_CASE(VK_ERROR_FRAGMENTED_POOL); + RESULT_CASE(VK_ERROR_SURFACE_LOST_KHR); + RESULT_CASE(VK_ERROR_NATIVE_WINDOW_IN_USE_KHR); + RESULT_CASE(VK_SUBOPTIMAL_KHR); + RESULT_CASE(VK_ERROR_OUT_OF_DATE_KHR); + RESULT_CASE(VK_ERROR_INCOMPATIBLE_DISPLAY_KHR); + RESULT_CASE(VK_ERROR_VALIDATION_FAILED_EXT); + RESULT_CASE(VK_ERROR_OUT_OF_POOL_MEMORY_KHR); + RESULT_CASE(VK_ERROR_INVALID_SHADER_NV); +#undef RESULT_CASE + default: + break; + } + return (result < 0) ? "VK_ERROR_" : "VK_"; +} + +static int createInstance(VulkanVideoContext *context) +{ + static const char *optional_extensions[] = { + VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME, + VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME + }; + VkApplicationInfo appInfo = { 0 }; + VkInstanceCreateInfo instanceCreateInfo = { 0 }; + VkResult result; + char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount); + + appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + appInfo.apiVersion = VK_API_VERSION_1_3; + instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instanceCreateInfo.pApplicationInfo = &appInfo; + + const char **instanceExtensionsCopy = SDL_calloc(instanceCreateInfo.enabledExtensionCount + SDL_arraysize(optional_extensions), sizeof(const char *)); + for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) { + instanceExtensionsCopy[i] = instanceExtensions[i]; + } + + // Get the rest of the optional extensions + { + uint32_t extensionCount; + if (context->vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, NULL) == VK_SUCCESS && extensionCount > 0) { + VkExtensionProperties *extensionProperties = SDL_calloc(sizeof(VkExtensionProperties), extensionCount); + if (context->vkEnumerateInstanceExtensionProperties(NULL, &extensionCount, extensionProperties) == VK_SUCCESS) { + for (uint32_t i = 0; i < SDL_arraysize(optional_extensions); ++i) { + for (uint32_t j = 0; j < extensionCount; ++j) { + if (SDL_strcmp(extensionProperties[j].extensionName, optional_extensions[i]) == 0) { + instanceExtensionsCopy[instanceCreateInfo.enabledExtensionCount++] = optional_extensions[i]; + break; + } + } + } + } + SDL_free(extensionProperties); + } + } + instanceCreateInfo.ppEnabledExtensionNames = instanceExtensionsCopy; + + context->instanceExtensions = instanceExtensionsCopy; + context->instanceExtensionsCount = instanceCreateInfo.enabledExtensionCount; + + result = context->vkCreateInstance(&instanceCreateInfo, NULL, &context->instance); + SDL_free((void *)instanceExtensionsCopy); + if (result != VK_SUCCESS) { + context->instance = VK_NULL_HANDLE; + return SDL_SetError("vkCreateInstance(): %s\n", getVulkanResultString(result)); + } + if (loadInstanceFunctions(context) < 0) { + return -1; + } + return 0; +} + +static int createSurface(VulkanVideoContext *context, SDL_Window *window) +{ + if (!SDL_Vulkan_CreateSurface(window, + context->instance, + NULL, + &context->surface)) { + context->surface = VK_NULL_HANDLE; + return -1; + } + return 0; +} + +// Use the same queue scoring algorithm as ffmpeg to make sure we get the same device configuration +static int selectQueueFamily(VkQueueFamilyProperties *queueFamiliesProperties, uint32_t queueFamiliesCount, VkQueueFlagBits flags, int *queueCount) +{ + uint32_t queueFamilyIndex; + uint32_t selectedQueueFamilyIndex = queueFamiliesCount; + uint32_t min_score = ~0u; + + for (queueFamilyIndex = 0; queueFamilyIndex < queueFamiliesCount; ++queueFamilyIndex) { + VkQueueFlagBits current_flags = queueFamiliesProperties[queueFamilyIndex].queueFlags; + if (current_flags & flags) { + uint32_t score = av_popcount(current_flags) + queueFamiliesProperties[queueFamilyIndex].timestampValidBits; + if (score < min_score) { + selectedQueueFamilyIndex = queueFamilyIndex; + min_score = score; + } + } + } + + if (selectedQueueFamilyIndex != queueFamiliesCount) { + VkQueueFamilyProperties *selectedQueueFamily = &queueFamiliesProperties[selectedQueueFamilyIndex]; + *queueCount = (int)selectedQueueFamily->queueCount; + ++selectedQueueFamily->timestampValidBits; + return (int)selectedQueueFamilyIndex; + } else { + *queueCount = 0; + return -1; + } +} + +static int findPhysicalDevice(VulkanVideoContext *context) +{ + uint32_t physicalDeviceCount = 0; + VkPhysicalDevice *physicalDevices; + VkQueueFamilyProperties *queueFamiliesProperties = NULL; + uint32_t queueFamiliesPropertiesAllocatedSize = 0; + VkExtensionProperties *deviceExtensions = NULL; + uint32_t deviceExtensionsAllocatedSize = 0; + uint32_t physicalDeviceIndex; + VkResult result; + + result = context->vkEnumeratePhysicalDevices(context->instance, &physicalDeviceCount, NULL); + if (result != VK_SUCCESS) { + return SDL_SetError("vkEnumeratePhysicalDevices(): %s", getVulkanResultString(result)); + } + if (physicalDeviceCount == 0) { + return SDL_SetError("vkEnumeratePhysicalDevices(): no physical devices"); + } + physicalDevices = (VkPhysicalDevice *)SDL_malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount); + if (!physicalDevices) { + return -1; + } + result = context->vkEnumeratePhysicalDevices(context->instance, &physicalDeviceCount, physicalDevices); + if (result != VK_SUCCESS) { + SDL_free(physicalDevices); + return SDL_SetError("vkEnumeratePhysicalDevices(): %s", getVulkanResultString(result)); + } + context->physicalDevice = NULL; + for (physicalDeviceIndex = 0; physicalDeviceIndex < physicalDeviceCount; physicalDeviceIndex++) { + uint32_t queueFamiliesCount = 0; + uint32_t queueFamilyIndex; + uint32_t deviceExtensionCount = 0; + SDL_bool hasSwapchainExtension = SDL_FALSE; + uint32_t i; + + VkPhysicalDevice physicalDevice = physicalDevices[physicalDeviceIndex]; + context->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, NULL); + if (queueFamiliesCount == 0) { + continue; + } + if (queueFamiliesPropertiesAllocatedSize < queueFamiliesCount) { + SDL_free(queueFamiliesProperties); + queueFamiliesPropertiesAllocatedSize = queueFamiliesCount; + queueFamiliesProperties = (VkQueueFamilyProperties *)SDL_malloc(sizeof(VkQueueFamilyProperties) * queueFamiliesPropertiesAllocatedSize); + if (!queueFamiliesProperties) { + SDL_free(physicalDevices); + SDL_free(deviceExtensions); + return -1; + } + } + context->vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamiliesCount, queueFamiliesProperties); + + // Initialize timestampValidBits for scoring in selectQueueFamily + for (queueFamilyIndex = 0; queueFamilyIndex < queueFamiliesCount; queueFamilyIndex++) { + queueFamiliesProperties[queueFamilyIndex].timestampValidBits = 0; + } + context->presentQueueFamilyIndex = -1; + context->graphicsQueueFamilyIndex = -1; + for (queueFamilyIndex = 0; queueFamilyIndex < queueFamiliesCount; queueFamilyIndex++) { + VkBool32 supported = 0; + + if (queueFamiliesProperties[queueFamilyIndex].queueCount == 0) { + continue; + } + + if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + context->graphicsQueueFamilyIndex = queueFamilyIndex; + } + + result = context->vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, context->surface, &supported); + if (result == VK_SUCCESS) { + if (supported) { + context->presentQueueFamilyIndex = queueFamilyIndex; + if (queueFamiliesProperties[queueFamilyIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + break; // use this queue because it can present and do graphics + } + } + } + } + if (context->presentQueueFamilyIndex < 0 || context->graphicsQueueFamilyIndex < 0) { + // We can't render and present on this device + continue; + } + + context->presentQueueCount = queueFamiliesProperties[context->presentQueueFamilyIndex].queueCount; + ++queueFamiliesProperties[context->presentQueueFamilyIndex].timestampValidBits; + context->graphicsQueueCount = queueFamiliesProperties[context->graphicsQueueFamilyIndex].queueCount; + ++queueFamiliesProperties[context->graphicsQueueFamilyIndex].timestampValidBits; + + context->transferQueueFamilyIndex = selectQueueFamily(queueFamiliesProperties, queueFamiliesCount, VK_QUEUE_TRANSFER_BIT, &context->transferQueueCount); + context->computeQueueFamilyIndex = selectQueueFamily(queueFamiliesProperties, queueFamiliesCount, VK_QUEUE_COMPUTE_BIT, &context->computeQueueCount); + context->decodeQueueFamilyIndex = selectQueueFamily(queueFamiliesProperties, queueFamiliesCount, VK_QUEUE_VIDEO_DECODE_BIT_KHR, &context->decodeQueueCount); + if (context->transferQueueFamilyIndex < 0) { + // ffmpeg can fall back to the compute or graphics queues for this + context->transferQueueFamilyIndex = selectQueueFamily(queueFamiliesProperties, queueFamiliesCount, VK_QUEUE_COMPUTE_BIT, &context->transferQueueCount); + if (context->transferQueueFamilyIndex < 0) { + context->transferQueueFamilyIndex = selectQueueFamily(queueFamiliesProperties, queueFamiliesCount, VK_QUEUE_GRAPHICS_BIT, &context->transferQueueCount); + } + } + + if (context->transferQueueFamilyIndex < 0 || + context->computeQueueFamilyIndex < 0) { + // This device doesn't have the queues we need for video decoding + continue; + } + + result = context->vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, NULL); + if (result != VK_SUCCESS) { + SDL_free(physicalDevices); + SDL_free(queueFamiliesProperties); + SDL_free(deviceExtensions); + return SDL_SetError("vkEnumerateDeviceExtensionProperties(): %s", getVulkanResultString(result)); + } + if (deviceExtensionCount == 0) { + continue; + } + if (deviceExtensionsAllocatedSize < deviceExtensionCount) { + SDL_free(deviceExtensions); + deviceExtensionsAllocatedSize = deviceExtensionCount; + deviceExtensions = SDL_malloc(sizeof(VkExtensionProperties) * deviceExtensionsAllocatedSize); + if (!deviceExtensions) { + SDL_free(physicalDevices); + SDL_free(queueFamiliesProperties); + return -1; + } + } + result = context->vkEnumerateDeviceExtensionProperties(physicalDevice, NULL, &deviceExtensionCount, deviceExtensions); + if (result != VK_SUCCESS) { + SDL_free(physicalDevices); + SDL_free(queueFamiliesProperties); + SDL_free(deviceExtensions); + return SDL_SetError("vkEnumerateDeviceExtensionProperties(): %s", getVulkanResultString(result)); + } + for (i = 0; i < deviceExtensionCount; i++) { + if (SDL_strcmp(deviceExtensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) { + hasSwapchainExtension = SDL_TRUE; + break; + } + } + if (!hasSwapchainExtension) { + continue; + } + context->physicalDevice = physicalDevice; + break; + } + SDL_free(physicalDevices); + SDL_free(queueFamiliesProperties); + SDL_free(deviceExtensions); + if (!context->physicalDevice) { + return SDL_SetError("Vulkan: no viable physical devices found"); + } + return 0; +} + +static void initDeviceFeatures(VulkanDeviceFeatures *features) +{ + features->device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + features->device_features.pNext = &features->device_features_1_1; + features->device_features_1_1.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; + features->device_features_1_1.pNext = &features->device_features_1_2; + features->device_features_1_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; + features->device_features_1_2.pNext = &features->device_features_1_3; + features->device_features_1_3.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; + features->device_features_1_3.pNext = &features->desc_buf_features; + features->desc_buf_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_FEATURES_EXT; + features->desc_buf_features.pNext = &features->atomic_float_features; + features->atomic_float_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_FEATURES_EXT; + features->atomic_float_features.pNext = &features->coop_matrix_features; + features->coop_matrix_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_KHR; + features->coop_matrix_features.pNext = NULL; +} + +static void copyDeviceFeatures(VulkanDeviceFeatures *supported_features, VulkanDeviceFeatures *requested_features) +{ +#define COPY_OPTIONAL_FEATURE(X) requested_features->X = supported_features->X + COPY_OPTIONAL_FEATURE(device_features.features.shaderImageGatherExtended); + COPY_OPTIONAL_FEATURE(device_features.features.shaderStorageImageReadWithoutFormat); + COPY_OPTIONAL_FEATURE(device_features.features.shaderStorageImageWriteWithoutFormat); + COPY_OPTIONAL_FEATURE(device_features.features.fragmentStoresAndAtomics); + COPY_OPTIONAL_FEATURE(device_features.features.vertexPipelineStoresAndAtomics); + COPY_OPTIONAL_FEATURE(device_features.features.shaderInt64); + COPY_OPTIONAL_FEATURE(device_features.features.shaderInt16); + COPY_OPTIONAL_FEATURE(device_features.features.shaderFloat64); + COPY_OPTIONAL_FEATURE(device_features_1_1.samplerYcbcrConversion); + COPY_OPTIONAL_FEATURE(device_features_1_1.storagePushConstant16); + COPY_OPTIONAL_FEATURE(device_features_1_2.bufferDeviceAddress); + COPY_OPTIONAL_FEATURE(device_features_1_2.hostQueryReset); + COPY_OPTIONAL_FEATURE(device_features_1_2.storagePushConstant8); + COPY_OPTIONAL_FEATURE(device_features_1_2.shaderInt8); + COPY_OPTIONAL_FEATURE(device_features_1_2.storageBuffer8BitAccess); + COPY_OPTIONAL_FEATURE(device_features_1_2.uniformAndStorageBuffer8BitAccess); + COPY_OPTIONAL_FEATURE(device_features_1_2.shaderFloat16); + COPY_OPTIONAL_FEATURE(device_features_1_2.shaderSharedInt64Atomics); + COPY_OPTIONAL_FEATURE(device_features_1_2.vulkanMemoryModel); + COPY_OPTIONAL_FEATURE(device_features_1_2.vulkanMemoryModelDeviceScope); + COPY_OPTIONAL_FEATURE(device_features_1_2.hostQueryReset); + COPY_OPTIONAL_FEATURE(device_features_1_3.dynamicRendering); + COPY_OPTIONAL_FEATURE(device_features_1_3.maintenance4); + COPY_OPTIONAL_FEATURE(device_features_1_3.synchronization2); + COPY_OPTIONAL_FEATURE(device_features_1_3.computeFullSubgroups); + COPY_OPTIONAL_FEATURE(device_features_1_3.shaderZeroInitializeWorkgroupMemory); + COPY_OPTIONAL_FEATURE(desc_buf_features.descriptorBuffer); + COPY_OPTIONAL_FEATURE(desc_buf_features.descriptorBufferPushDescriptors); + COPY_OPTIONAL_FEATURE(atomic_float_features.shaderBufferFloat32Atomics); + COPY_OPTIONAL_FEATURE(atomic_float_features.shaderBufferFloat32AtomicAdd); + COPY_OPTIONAL_FEATURE(coop_matrix_features.cooperativeMatrix); +#undef COPY_OPTIONAL_FEATURE + + // timeline semaphores is required by ffmpeg + requested_features->device_features_1_2.timelineSemaphore = 1; +} + +static int addQueueFamily(VkDeviceQueueCreateInfo **pQueueCreateInfos, uint32_t *pQueueCreateInfoCount, uint32_t queueFamilyIndex, uint32_t queueCount) +{ + VkDeviceQueueCreateInfo *queueCreateInfo; + VkDeviceQueueCreateInfo *queueCreateInfos = *pQueueCreateInfos; + uint32_t queueCreateInfoCount = *pQueueCreateInfoCount; + float *queuePriorities; + + if (queueCount == 0) { + return 0; + } + + for (uint32_t i = 0; i < queueCreateInfoCount; ++i) { + if (queueCreateInfos[i].queueFamilyIndex == queueFamilyIndex) { + return 0; + } + } + + queueCreateInfos = (VkDeviceQueueCreateInfo *)SDL_realloc(queueCreateInfos, (queueCreateInfoCount + 1) * sizeof(*queueCreateInfos)); + if (!queueCreateInfos) { + return -1; + } + + queuePriorities = (float *)SDL_malloc(queueCount * sizeof(*queuePriorities)); + if (!queuePriorities) { + return -1; + } + + for (uint32_t i = 0; i < queueCount; ++i) { + queuePriorities[i] = 1.0f / queueCount; + } + + queueCreateInfo = &queueCreateInfos[queueCreateInfoCount++]; + SDL_zerop(queueCreateInfo); + queueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo->queueFamilyIndex = queueFamilyIndex; + queueCreateInfo->queueCount = queueCount; + queueCreateInfo->pQueuePriorities = queuePriorities; + + *pQueueCreateInfos = queueCreateInfos; + *pQueueCreateInfoCount = queueCreateInfoCount; + return 0; +} + +static int createDevice(VulkanVideoContext *context) +{ + static const char *const deviceExtensionNames[] = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, + VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, + VK_KHR_MAINTENANCE1_EXTENSION_NAME, + VK_KHR_BIND_MEMORY_2_EXTENSION_NAME, + VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, + }; + static const char *optional_extensions[] = { + VK_KHR_VIDEO_QUEUE_EXTENSION_NAME, + VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME, + VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME, + VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME, + "VK_MESA_video_decode_av1" + }; + VkDeviceCreateInfo deviceCreateInfo = { 0 }; + VkDeviceQueueCreateInfo *queueCreateInfos = NULL; + uint32_t queueCreateInfoCount = 0; + VulkanDeviceFeatures supported_features; + VkResult result = VK_ERROR_UNKNOWN; + + if (addQueueFamily(&queueCreateInfos, &queueCreateInfoCount, context->presentQueueFamilyIndex, context->presentQueueCount) < 0 || + addQueueFamily(&queueCreateInfos, &queueCreateInfoCount, context->graphicsQueueFamilyIndex, context->graphicsQueueCount) < 0 || + addQueueFamily(&queueCreateInfos, &queueCreateInfoCount, context->transferQueueFamilyIndex, context->transferQueueCount) < 0 || + addQueueFamily(&queueCreateInfos, &queueCreateInfoCount, context->computeQueueFamilyIndex, context->computeQueueCount) < 0 || + addQueueFamily(&queueCreateInfos, &queueCreateInfoCount, context->decodeQueueFamilyIndex, context->decodeQueueCount) < 0) { + goto done; + } + + initDeviceFeatures(&supported_features); + initDeviceFeatures(&context->features); + context->vkGetPhysicalDeviceFeatures2(context->physicalDevice, &supported_features.device_features); + copyDeviceFeatures(&supported_features, &context->features); + + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceCreateInfo.queueCreateInfoCount = queueCreateInfoCount; + deviceCreateInfo.pQueueCreateInfos = queueCreateInfos; + deviceCreateInfo.pEnabledFeatures = NULL; + deviceCreateInfo.enabledExtensionCount = SDL_arraysize(deviceExtensionNames); + deviceCreateInfo.pNext = &context->features.device_features; + + const char **deviceExtensionsCopy = SDL_calloc(deviceCreateInfo.enabledExtensionCount + SDL_arraysize(optional_extensions), sizeof(const char *)); + for (uint32_t i = 0; i < deviceCreateInfo.enabledExtensionCount; i++) { + deviceExtensionsCopy[i] = deviceExtensionNames[i]; + } + + // Get the rest of the optional extensions + { + uint32_t extensionCount; + if (context->vkEnumerateDeviceExtensionProperties(context->physicalDevice, NULL, &extensionCount, NULL) == VK_SUCCESS && extensionCount > 0) { + VkExtensionProperties *extensionProperties = SDL_calloc(sizeof(VkExtensionProperties), extensionCount); + if (context->vkEnumerateDeviceExtensionProperties(context->physicalDevice, NULL, &extensionCount, extensionProperties) == VK_SUCCESS) { + for (uint32_t i = 0; i < SDL_arraysize(optional_extensions); ++i) { + for (uint32_t j = 0; j < extensionCount; ++j) { + if (SDL_strcmp(extensionProperties[j].extensionName, optional_extensions[i]) == 0) { + deviceExtensionsCopy[deviceCreateInfo.enabledExtensionCount++] = optional_extensions[i]; + break; + } + } + } + } + SDL_free(extensionProperties); + } + } + deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionsCopy; + + context->deviceExtensions = deviceExtensionsCopy; + context->deviceExtensionsCount = deviceCreateInfo.enabledExtensionCount; + + result = context->vkCreateDevice(context->physicalDevice, &deviceCreateInfo, NULL, &context->device); + if (result != VK_SUCCESS) { + SDL_SetError("vkCreateDevice(): %s", getVulkanResultString(result)); + goto done; + } + + if (loadDeviceFunctions(context) < 0) { + result = VK_ERROR_UNKNOWN; + context->device = VK_NULL_HANDLE; + goto done; + } + + // Get the graphics queue that SDL will use + context->vkGetDeviceQueue(context->device, context->graphicsQueueFamilyIndex, 0, &context->graphicsQueue); + + // Create a command pool + VkCommandPoolCreateInfo commandPoolCreateInfo = { 0 }; + commandPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + commandPoolCreateInfo.queueFamilyIndex = context->graphicsQueueFamilyIndex; + result = context->vkCreateCommandPool(context->device, &commandPoolCreateInfo, NULL, &context->commandPool); + if (result != VK_SUCCESS) { + SDL_SetError("vkCreateCommandPool(): %s", getVulkanResultString(result)); + goto done; + } + +done: + for (uint32_t i = 0; i < queueCreateInfoCount; ++i) { + SDL_free((void *)queueCreateInfos[i].pQueuePriorities); + } + SDL_free(queueCreateInfos); + + if (result != VK_SUCCESS) { + return -1; + } + return 0; +} + +VulkanVideoContext *CreateVulkanVideoContext(SDL_Window *window) +{ + VulkanVideoContext *context = SDL_calloc(1, sizeof(*context)); + if (!context) { + return NULL; + } + if (loadGlobalFunctions(context) < 0 || + createInstance(context) < 0 || + createSurface(context, window) < 0 || + findPhysicalDevice(context) < 0 || + createDevice(context) < 0) { + DestroyVulkanVideoContext(context); + return NULL; + } + return context; +} + +void SetupVulkanRenderProperties(VulkanVideoContext *context, SDL_PropertiesID props) +{ + SDL_SetProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_INSTANCE_POINTER, context->instance); + SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_SURFACE_NUMBER, (Sint64)context->surface); + SDL_SetProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_PHYSICAL_DEVICE_POINTER, context->physicalDevice); + SDL_SetProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_DEVICE_POINTER, context->device); + SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER, context->presentQueueFamilyIndex); + SDL_SetNumberProperty(props, SDL_PROP_RENDERER_CREATE_VULKAN_GRAPHICS_QUEUE_FAMILY_INDEX_NUMBER, context->graphicsQueueFamilyIndex); +} + +void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx) +{ + ctx->get_proc_addr = context->vkGetInstanceProcAddr; + ctx->inst = context->instance; + ctx->phys_dev = context->physicalDevice; + ctx->act_dev = context->device; + ctx->device_features = context->features.device_features; + ctx->enabled_inst_extensions = context->instanceExtensions; + ctx->nb_enabled_inst_extensions = context->instanceExtensionsCount; + ctx->enabled_dev_extensions = context->deviceExtensions; + ctx->nb_enabled_dev_extensions = context->deviceExtensionsCount; + ctx->queue_family_index = context->graphicsQueueFamilyIndex; + ctx->nb_graphics_queues = context->graphicsQueueCount; + ctx->queue_family_tx_index = context->transferQueueFamilyIndex; + ctx->nb_tx_queues = context->transferQueueCount; + ctx->queue_family_comp_index = context->computeQueueFamilyIndex; + ctx->nb_comp_queues = context->computeQueueCount; + ctx->queue_family_encode_index = -1; + ctx->nb_encode_queues = 0; + ctx->queue_family_decode_index = context->decodeQueueFamilyIndex; + ctx->nb_decode_queues = context->decodeQueueCount; +} + +static int CreateCommandBuffers(VulkanVideoContext *context, SDL_Renderer *renderer) +{ + uint32_t commandBufferCount = (uint32_t)SDL_GetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER, 1); + + if (commandBufferCount > context->waitSemaphoreCount) { + VkSemaphore *semaphores = (VkSemaphore *)SDL_realloc(context->waitSemaphores, commandBufferCount * sizeof(*semaphores)); + if (!semaphores) { + return -1; + } + context->waitSemaphores = semaphores; + + VkSemaphoreCreateInfo semaphoreCreateInfo = { 0 }; + semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + while (context->waitSemaphoreCount < commandBufferCount) { + VkResult result = context->vkCreateSemaphore(context->device, &semaphoreCreateInfo, NULL, &context->waitSemaphores[context->waitSemaphoreCount]); + if (result != VK_SUCCESS) { + SDL_SetError("vkCreateSemaphore(): %s", getVulkanResultString(result)); + return -1; + } + ++context->waitSemaphoreCount; + } + } + + if (commandBufferCount > context->signalSemaphoreCount) { + VkSemaphore *semaphores = (VkSemaphore *)SDL_realloc(context->signalSemaphores, commandBufferCount * sizeof(*semaphores)); + if (!semaphores) { + return -1; + } + context->signalSemaphores = semaphores; + + VkSemaphoreCreateInfo semaphoreCreateInfo = { 0 }; + semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + while (context->signalSemaphoreCount < commandBufferCount) { + VkResult result = context->vkCreateSemaphore(context->device, &semaphoreCreateInfo, NULL, &context->signalSemaphores[context->signalSemaphoreCount]); + if (result != VK_SUCCESS) { + SDL_SetError("vkCreateSemaphore(): %s", getVulkanResultString(result)); + return -1; + } + ++context->signalSemaphoreCount; + } + } + + if (commandBufferCount > context->commandBufferCount) { + uint32_t needed = (commandBufferCount - context->commandBufferCount); + VkCommandBuffer *commandBuffers = (VkCommandBuffer *)SDL_realloc(context->commandBuffers, commandBufferCount * sizeof(*commandBuffers)); + if (!commandBuffers) { + return -1; + } + context->commandBuffers = commandBuffers; + + VkCommandBufferAllocateInfo commandBufferAllocateInfo = { 0 }; + commandBufferAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + commandBufferAllocateInfo.commandPool = context->commandPool; + commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + commandBufferAllocateInfo.commandBufferCount = needed; + VkResult result = context->vkAllocateCommandBuffers(context->device, &commandBufferAllocateInfo, &context->commandBuffers[context->commandBufferCount]); + if (result != VK_SUCCESS) { + SDL_SetError("vkAllocateCommandBuffers(): %s", getVulkanResultString(result)); + return -1; + } + + context->commandBufferCount = commandBufferCount; + } + return 0; +} + +static int ScheduleFrameTransferToSDL(VulkanVideoContext *context, AVFrame *frame) +{ + AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0]; + + VkTimelineSemaphoreSubmitInfo timeline = { 0 }; + timeline.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO; + timeline.waitSemaphoreValueCount = 1; + timeline.pWaitSemaphoreValues = pVkFrame->sem_value; + + VkPipelineStageFlags pipelineStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + VkSubmitInfo submitInfo = { 0 }; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = pVkFrame->sem; + submitInfo.pWaitDstStageMask = &pipelineStageMask; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = &context->waitSemaphores[context->commandBufferIndex]; + submitInfo.pNext = &timeline; + + if (pVkFrame->layout[0] != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + VkCommandBuffer commandBuffer = context->commandBuffers[context->commandBufferIndex]; + + VkCommandBufferBeginInfo beginInfo = { 0 }; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.flags = 0; + context->vkBeginCommandBuffer(commandBuffer, &beginInfo); + + VkImageMemoryBarrier2 barrier = { 0 }; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; + barrier.srcAccessMask = VK_ACCESS_2_NONE; + barrier.dstAccessMask = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT; + barrier.oldLayout = pVkFrame->layout[0]; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.image = pVkFrame->img[0]; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.layerCount = 1; + barrier.srcQueueFamilyIndex = pVkFrame->queue_family[0]; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + barrier.dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + + VkDependencyInfo dep = { 0 }; + dep.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; + dep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + dep.imageMemoryBarrierCount = 1; + dep.pImageMemoryBarriers = &barrier; + context->vkCmdPipelineBarrier2(commandBuffer, &dep); + + context->vkEndCommandBuffer(commandBuffer); + + // Add the image barrier to the submit info + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &context->commandBuffers[context->commandBufferIndex]; + + pVkFrame->layout[0] = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + pVkFrame->queue_family[0] = VK_QUEUE_FAMILY_IGNORED; + } + + VkResult result = context->vkQueueSubmit(context->graphicsQueue, 1, &submitInfo, 0); + if (result != VK_SUCCESS) { + return SDL_SetError("vkQueueSubmit(): %s", getVulkanResultString(result)); + } + return 0; +} + +static int ScheduleFrameTransferToFFMPEG(VulkanVideoContext *context, AVFrame *frame) +{ + AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0]; + + ++pVkFrame->sem_value[0]; + + VkTimelineSemaphoreSubmitInfo timeline = { 0 }; + timeline.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO; + timeline.signalSemaphoreValueCount = 1; + timeline.pSignalSemaphoreValues = pVkFrame->sem_value; + + VkPipelineStageFlags pipelineStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; + VkSubmitInfo submitInfo = { 0 }; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.waitSemaphoreCount = 1; + submitInfo.pWaitSemaphores = &context->signalSemaphores[context->commandBufferIndex]; + submitInfo.pWaitDstStageMask = &pipelineStageMask; + submitInfo.signalSemaphoreCount = 1; + submitInfo.pSignalSemaphores = pVkFrame->sem; + submitInfo.pNext = &timeline; + + VkResult result = context->vkQueueSubmit(context->graphicsQueue, 1, &submitInfo, 0); + if (result != VK_SUCCESS) { + return SDL_SetError("vkQueueSubmit(): %s", getVulkanResultString(result)); + } + return 0; +} + +static int PrepareFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer) +{ + AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data); + AVVulkanFramesContext *vk = (AVVulkanFramesContext *)(frames->hwctx); + AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0]; + + if (CreateCommandBuffers(context, renderer) < 0) { + return -1; + } + + vk->lock_frame(frames, pVkFrame); + ScheduleFrameTransferToSDL(context, frame); + ScheduleFrameTransferToFFMPEG(context, frame); + vk->unlock_frame(frames, pVkFrame); + + SDL_AddVulkanRenderSemaphores(renderer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, (Sint64)context->waitSemaphores[context->commandBufferIndex], (Sint64)context->signalSemaphores[context->commandBufferIndex]); + + context->commandBufferIndex = (context->commandBufferIndex + 1) % context->commandBufferCount; + + return 0; +} + +SDL_Texture *CreateVulkanVideoTexture(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer, SDL_PropertiesID props) +{ + AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data); + AVVulkanFramesContext *vk = (AVVulkanFramesContext *)(frames->hwctx); + AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0]; + Uint32 format; + + if (PrepareFrameRendering(context, frame, renderer) < 0) { + return NULL; + } + + switch (vk->format[0]) { + case VK_FORMAT_G8B8G8R8_422_UNORM: + format = SDL_PIXELFORMAT_YUY2; + break; + case VK_FORMAT_B8G8R8G8_422_UNORM: + format = SDL_PIXELFORMAT_UYVY; + break; + case VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM: + format = SDL_PIXELFORMAT_IYUV; + break; + case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM: + format = SDL_PIXELFORMAT_NV12; + break; + case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16: + format = SDL_PIXELFORMAT_P010; + break; + default: + format = SDL_PIXELFORMAT_UNKNOWN; + break; + } + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_VULKAN_TEXTURE_NUMBER, (Sint64)pVkFrame->img[0]); + return SDL_CreateTextureWithProperties(renderer, props); +} + +void DestroyVulkanVideoContext(VulkanVideoContext *context) +{ + if (context) { + if (context->device) { + context->vkDeviceWaitIdle(context->device); + } + if (context->instanceExtensions) { + SDL_free(context->instanceExtensions); + } + if (context->deviceExtensions) { + SDL_free(context->deviceExtensions); + } + if (context->waitSemaphores) { + for (uint32_t i = 0; i < context->waitSemaphoreCount; ++i) { + context->vkDestroySemaphore(context->device, context->waitSemaphores[i], NULL); + } + SDL_free(context->waitSemaphores); + context->waitSemaphores = NULL; + } + if (context->signalSemaphores) { + for (uint32_t i = 0; i < context->signalSemaphoreCount; ++i) { + context->vkDestroySemaphore(context->device, context->signalSemaphores[i], NULL); + } + SDL_free(context->signalSemaphores); + context->signalSemaphores = NULL; + } + if (context->commandBuffers) { + context->vkFreeCommandBuffers(context->device, context->commandPool, context->commandBufferCount, context->commandBuffers); + SDL_free(context->commandBuffers); + context->commandBuffers = NULL; + } + if (context->commandPool) { + context->vkDestroyCommandPool(context->device, context->commandPool, NULL); + context->commandPool = VK_NULL_HANDLE; + } + if (context->device) { + context->vkDestroyDevice(context->device, NULL); + } + if (context->surface) { + context->vkDestroySurfaceKHR(context->instance, context->surface, NULL); + } + if (context->instance) { + context->vkDestroyInstance(context->instance, NULL); + } + SDL_free(context); + } +} diff --git a/test/testffmpeg_vulkan.h b/test/testffmpeg_vulkan.h new file mode 100644 index 0000000000000..2ae046dfa387b --- /dev/null +++ b/test/testffmpeg_vulkan.h @@ -0,0 +1,22 @@ +/* + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely. +*/ + +#include + + +typedef struct VulkanVideoContext VulkanVideoContext; + +VulkanVideoContext *CreateVulkanVideoContext(SDL_Window *window); +void SetupVulkanRenderProperties(VulkanVideoContext *context, SDL_PropertiesID props); +void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx); +SDL_Texture *CreateVulkanVideoTexture(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer, SDL_PropertiesID props); +void DestroyVulkanVideoContext(VulkanVideoContext *context); From 97f97109d1bc875ec166175626c71d023531e8a8 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 4 Mar 2024 08:26:41 -0800 Subject: [PATCH 197/220] testffmpeg: fixed Vulkan validation errors The semaphores need to be submitted in order --- test/testffmpeg.c | 22 ++++++++++++++++++++ test/testffmpeg_vulkan.c | 45 ++++++++++++++++++---------------------- test/testffmpeg_vulkan.h | 12 ++++++----- 3 files changed, 49 insertions(+), 30 deletions(-) diff --git a/test/testffmpeg.c b/test/testffmpeg.c index 35e91265c11b2..03807a9ae5a2a 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -881,6 +881,22 @@ static SDL_bool GetTextureForFrame(AVFrame *frame, SDL_Texture **texture) } } +static int BeginFrameRendering(AVFrame *frame) +{ + if (frame->format == AV_PIX_FMT_VULKAN) { + return BeginVulkanFrameRendering(vulkan_context, frame, renderer); + } + return 0; +} + +static int FinishFrameRendering(AVFrame *frame) +{ + if (frame->format == AV_PIX_FMT_VULKAN) { + return FinishVulkanFrameRendering(vulkan_context, frame, renderer); + } + return 0; +} + static void DisplayVideoTexture(AVFrame *frame) { /* Update the video texture */ @@ -913,6 +929,10 @@ static void HandleVideoFrame(AVFrame *frame, double pts) now = (double)(SDL_GetTicks() - video_start) / 1000.0; } + if (BeginFrameRendering(frame) < 0) { + return; + } + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); @@ -922,6 +942,8 @@ static void HandleVideoFrame(AVFrame *frame, double pts) MoveSprite(); SDL_RenderPresent(renderer); + + FinishFrameRendering(frame); } static AVCodecContext *OpenAudioStream(AVFormatContext *ic, int stream, const AVCodec *codec) diff --git a/test/testffmpeg_vulkan.c b/test/testffmpeg_vulkan.c index 5d7fbc5d02788..467100b298e59 100644 --- a/test/testffmpeg_vulkan.c +++ b/test/testffmpeg_vulkan.c @@ -773,10 +773,18 @@ static int CreateCommandBuffers(VulkanVideoContext *context, SDL_Renderer *rende return 0; } -static int ScheduleFrameTransferToSDL(VulkanVideoContext *context, AVFrame *frame) +int BeginVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer) { + AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data); + AVVulkanFramesContext *vk = (AVVulkanFramesContext *)(frames->hwctx); AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0]; + if (CreateCommandBuffers(context, renderer) < 0) { + return -1; + } + + vk->lock_frame(frames, pVkFrame); + VkTimelineSemaphoreSubmitInfo timeline = { 0 }; timeline.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO; timeline.waitSemaphoreValueCount = 1; @@ -834,15 +842,22 @@ static int ScheduleFrameTransferToSDL(VulkanVideoContext *context, AVFrame *fram VkResult result = context->vkQueueSubmit(context->graphicsQueue, 1, &submitInfo, 0); if (result != VK_SUCCESS) { - return SDL_SetError("vkQueueSubmit(): %s", getVulkanResultString(result)); + /* Don't return an error here, we need to complete the frame operation */ + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION , "vkQueueSubmit(): %s", getVulkanResultString(result)); } + + SDL_AddVulkanRenderSemaphores(renderer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, (Sint64)context->waitSemaphores[context->commandBufferIndex], (Sint64)context->signalSemaphores[context->commandBufferIndex]); + return 0; } -static int ScheduleFrameTransferToFFMPEG(VulkanVideoContext *context, AVFrame *frame) +int FinishVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer) { + AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data); + AVVulkanFramesContext *vk = (AVVulkanFramesContext *)(frames->hwctx); AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0]; + /* Transition the frame back to ffmpeg */ ++pVkFrame->sem_value[0]; VkTimelineSemaphoreSubmitInfo timeline = { 0 }; @@ -862,28 +877,12 @@ static int ScheduleFrameTransferToFFMPEG(VulkanVideoContext *context, AVFrame *f VkResult result = context->vkQueueSubmit(context->graphicsQueue, 1, &submitInfo, 0); if (result != VK_SUCCESS) { - return SDL_SetError("vkQueueSubmit(): %s", getVulkanResultString(result)); + /* Don't return an error here, we need to complete the frame operation */ + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkQueueSubmit(): %s", getVulkanResultString(result)); } - return 0; -} -static int PrepareFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer) -{ - AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data); - AVVulkanFramesContext *vk = (AVVulkanFramesContext *)(frames->hwctx); - AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0]; - - if (CreateCommandBuffers(context, renderer) < 0) { - return -1; - } - - vk->lock_frame(frames, pVkFrame); - ScheduleFrameTransferToSDL(context, frame); - ScheduleFrameTransferToFFMPEG(context, frame); vk->unlock_frame(frames, pVkFrame); - SDL_AddVulkanRenderSemaphores(renderer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, (Sint64)context->waitSemaphores[context->commandBufferIndex], (Sint64)context->signalSemaphores[context->commandBufferIndex]); - context->commandBufferIndex = (context->commandBufferIndex + 1) % context->commandBufferCount; return 0; @@ -896,10 +895,6 @@ SDL_Texture *CreateVulkanVideoTexture(VulkanVideoContext *context, AVFrame *fram AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0]; Uint32 format; - if (PrepareFrameRendering(context, frame, renderer) < 0) { - return NULL; - } - switch (vk->format[0]) { case VK_FORMAT_G8B8G8R8_422_UNORM: format = SDL_PIXELFORMAT_YUY2; diff --git a/test/testffmpeg_vulkan.h b/test/testffmpeg_vulkan.h index 2ae046dfa387b..78cabced44a4a 100644 --- a/test/testffmpeg_vulkan.h +++ b/test/testffmpeg_vulkan.h @@ -15,8 +15,10 @@ typedef struct VulkanVideoContext VulkanVideoContext; -VulkanVideoContext *CreateVulkanVideoContext(SDL_Window *window); -void SetupVulkanRenderProperties(VulkanVideoContext *context, SDL_PropertiesID props); -void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx); -SDL_Texture *CreateVulkanVideoTexture(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer, SDL_PropertiesID props); -void DestroyVulkanVideoContext(VulkanVideoContext *context); +extern VulkanVideoContext *CreateVulkanVideoContext(SDL_Window *window); +extern void SetupVulkanRenderProperties(VulkanVideoContext *context, SDL_PropertiesID props); +extern void SetupVulkanDeviceContextData(VulkanVideoContext *context, AVVulkanDeviceContext *ctx); +extern SDL_Texture *CreateVulkanVideoTexture(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer, SDL_PropertiesID props); +extern int BeginVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer); +extern int FinishVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_Renderer *renderer); +extern void DestroyVulkanVideoContext(VulkanVideoContext *context); From ffef13e1e175a889b5a417e6e5de9e1a3d01d52c Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Mon, 4 Mar 2024 17:30:25 +0000 Subject: [PATCH 198/220] Sync SDL3 wiki -> header --- include/SDL3/SDL_render.h | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index 7c99a40db7327..b52087e713b2c 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -407,7 +407,9 @@ extern DECLSPEC int SDLCALL SDL_GetRendererInfo(SDL_Renderer *renderer, SDL_Rend * family index used for rendering * - `SDL_PROP_RENDERER_VULKAN_PRESENT_QUEUE_FAMILY_INDEX_NUMBER`: the queue * family index used for presentation - * - `SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER`: the number of swapchain images, or potential frames in flight, used by the Vulkan renderer + * - `SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER`: the number of + * swapchain images, or potential frames in flight, used by the Vulkan + * renderer * * \param renderer the rendering context * \returns a valid property ID on success or 0 on failure; call @@ -2110,14 +2112,23 @@ extern DECLSPEC void *SDLCALL SDL_GetRenderMetalCommandEncoder(SDL_Renderer *ren /** * Add a set of synchronization semaphores for the current frame. * - * The Vulkan renderer will wait for `wait_semaphore` before submitting rendering commands and signal `signal_semaphore` after rendering commands are complete for this frame. + * The Vulkan renderer will wait for `wait_semaphore` before submitting + * rendering commands and signal `signal_semaphore` after rendering commands + * are complete for this frame. * - * This should be called each frame that you want semaphore synchronization. The Vulkan renderer may have multiple frames in flight on the GPU, so you should have multiple semaphores that are used for synchronization. Querying SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER will give you the maximum number of semaphores you'll need. + * This should be called each frame that you want semaphore synchronization. + * The Vulkan renderer may have multiple frames in flight on the GPU, so you + * should have multiple semaphores that are used for synchronization. Querying + * SDL_PROP_RENDERER_VULKAN_SWAPCHAIN_IMAGE_COUNT_NUMBER will give you the + * maximum number of semaphores you'll need. * * \param renderer the rendering context * \param wait_stage_mask the VkPipelineStageFlags for the wait - * \param wait_semaphore a VkSempahore to wait on before rendering the current frame, or 0 if not needed - * \param signal_semaphore a VkSempahore that SDL will signal when rendering for the current frame is complete, or 0 if not needed + * \param wait_semaphore a VkSempahore to wait on before rendering the current + * frame, or 0 if not needed + * \param signal_semaphore a VkSempahore that SDL will signal when rendering + * for the current frame is complete, or 0 if not + * needed * \returns 0 on success or a negative error code on failure; call * SDL_GetError() for more information. * From 759ade8c90ecc35638ba370c709f1fc7a94a1c6e Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 4 Mar 2024 10:26:28 -0800 Subject: [PATCH 199/220] vulkan: enable samplerYcbcrConversion when creating the device --- src/render/vulkan/SDL_render_vulkan.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index b80d130dfebd3..7f9490b7bb0d9 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -1782,10 +1782,11 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert if (rendererData->device) { rendererData->device_external = SDL_TRUE; } else { + VkPhysicalDeviceSamplerYcbcrConversionFeatures deviceSamplerYcbcrConversionFeatures = { 0 }; VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = { { 0 }, { 0 } }; static const float queuePriority[] = { 1.0f }; - VkDeviceCreateInfo deviceCreateInfo = { 0 }; + VkDeviceCreateInfo deviceCreateInfo = { 0 }; deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; deviceCreateInfo.queueCreateInfoCount = 0; deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo; @@ -1807,6 +1808,13 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert ++deviceCreateInfo.queueCreateInfoCount; } + if (rendererData->supportsKHRSamplerYCbCrConversion) { + deviceSamplerYcbcrConversionFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES; + deviceSamplerYcbcrConversionFeatures.samplerYcbcrConversion = VK_TRUE; + deviceSamplerYcbcrConversionFeatures.pNext = (void *)deviceCreateInfo.pNext; + deviceCreateInfo.pNext = &deviceSamplerYcbcrConversionFeatures; + } + result = vkCreateDevice(rendererData->physicalDevice, &deviceCreateInfo, NULL, &rendererData->device); if (result != VK_SUCCESS) { SDL_LogError(SDL_LOG_CATEGORY_RENDER, "vkCreateDevice(): %s\n", SDL_Vulkan_GetResultString(result)); From 2d4105ba8b2cf43ead334b27b219bf575a4204f0 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 4 Mar 2024 10:51:58 -0800 Subject: [PATCH 200/220] testffmpeg: only enable AV_PIX_FMT_VULKAN if we have a Vulkan renderer --- test/testffmpeg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testffmpeg.c b/test/testffmpeg.c index 03807a9ae5a2a..c9811d63a5aec 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -338,7 +338,7 @@ static SDL_bool SupportedPixelFormat(enum AVPixelFormat format) return SDL_TRUE; } #endif - if (format == AV_PIX_FMT_VULKAN) { + if (vulkan_context && format == AV_PIX_FMT_VULKAN) { return SDL_TRUE; } } From 180dd0bb39ee129cec2f0c0899c6b6814335eb56 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 4 Mar 2024 10:27:28 -0800 Subject: [PATCH 201/220] testffmpeg: don't free the instance extensions, we hold onto them in the context --- test/testffmpeg_vulkan.c | 1 - 1 file changed, 1 deletion(-) diff --git a/test/testffmpeg_vulkan.c b/test/testffmpeg_vulkan.c index 467100b298e59..6f1ebca923f1c 100644 --- a/test/testffmpeg_vulkan.c +++ b/test/testffmpeg_vulkan.c @@ -242,7 +242,6 @@ static int createInstance(VulkanVideoContext *context) context->instanceExtensionsCount = instanceCreateInfo.enabledExtensionCount; result = context->vkCreateInstance(&instanceCreateInfo, NULL, &context->instance); - SDL_free((void *)instanceExtensionsCopy); if (result != VK_SUCCESS) { context->instance = VK_NULL_HANDLE; return SDL_SetError("vkCreateInstance(): %s\n", getVulkanResultString(result)); From fb87f8f15c11477f294b6486f967ea167dcc20d6 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 4 Mar 2024 10:49:38 -0800 Subject: [PATCH 202/220] testffmpeg: fixed mismatch between frame size and frame texture size The AVHWFramesContext associated with the frame has the texture size, and the frame width and height are the displayed size and can be smaller than the texture size --- test/testffmpeg.c | 93 +++++++++++++++++++---------------------------- 1 file changed, 38 insertions(+), 55 deletions(-) diff --git a/test/testffmpeg.c b/test/testffmpeg.c index c9811d63a5aec..d158aa658aee1 100644 --- a/test/testffmpeg.c +++ b/test/testffmpeg.c @@ -502,16 +502,32 @@ static SDL_Colorspace GetFrameColorspace(AVFrame *frame) return colorspace; } -static SDL_PropertiesID CreateVideoTextureProperties(AVFrame *frame, Uint32 format, int access, int w, int h) +static SDL_PropertiesID CreateVideoTextureProperties(AVFrame *frame, Uint32 format, int access) { AVFrameSideData *pSideData; SDL_PropertiesID props; + int width = frame->width; + int height = frame->height; SDL_Colorspace colorspace = GetFrameColorspace(frame); /* ITU-R BT.2408-6 recommends using an SDR white point of 203 nits, which is more likely for game content */ static const float k_flSDRWhitePoint = 203.0f; float flMaxLuminance = k_flSDRWhitePoint; + if (frame->hw_frames_ctx) { + AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data); + + width = frames->width; + height = frames->height; + if (format == SDL_PIXELFORMAT_UNKNOWN) { + format = GetTextureFormat(frames->sw_format); + } + } else { + if (format == SDL_PIXELFORMAT_UNKNOWN) { + format = GetTextureFormat(frame->format); + } + } + props = SDL_CreateProperties(); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, colorspace); pSideData = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); @@ -528,8 +544,8 @@ static SDL_PropertiesID CreateVideoTextureProperties(AVFrame *frame, Uint32 form } SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER, format); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, access); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, w); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, h); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, width); + SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, height); return props; } @@ -561,9 +577,9 @@ static SDL_bool GetTextureForMemoryFrame(AVFrame *frame, SDL_Texture **texture) SDL_PropertiesID props; if (frame_format == SDL_PIXELFORMAT_UNKNOWN) { - props = CreateVideoTextureProperties(frame, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, frame->width, frame->height); + props = CreateVideoTextureProperties(frame, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING); } else { - props = CreateVideoTextureProperties(frame, frame_format, SDL_TEXTUREACCESS_STREAMING, frame->width, frame->height); + props = CreateVideoTextureProperties(frame, frame_format, SDL_TEXTUREACCESS_STREAMING); } *texture = SDL_CreateTextureWithProperties(renderer, props); SDL_DestroyProperties(props); @@ -630,6 +646,7 @@ static SDL_bool GetTextureForMemoryFrame(AVFrame *frame, SDL_Texture **texture) static SDL_bool GetTextureForDRMFrame(AVFrame *frame, SDL_Texture **texture) { #ifdef HAVE_EGL + AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data); const AVDRMFrameDescriptor *desc = (const AVDRMFrameDescriptor *)frame->data[0]; int i, j, image_index, num_planes; EGLDisplay display = eglGetCurrentDisplay(); @@ -654,7 +671,7 @@ static SDL_bool GetTextureForDRMFrame(AVFrame *frame, SDL_Texture **texture) SDL_SetHint("SDL_RENDER_OPENGL_NV12_RG_SHADER", "1"); } - props = CreateVideoTextureProperties(frame, SDL_PIXELFORMAT_NV12, SDL_TEXTUREACCESS_STATIC, frame->width, frame->height); + props = CreateVideoTextureProperties(frame, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_STATIC); *texture = SDL_CreateTextureWithProperties(renderer, props); SDL_DestroyProperties(props); if (!*texture) { @@ -687,10 +704,10 @@ static SDL_bool GetTextureForDRMFrame(AVFrame *frame, SDL_Texture **texture) attr[k++] = formats[i]; attr[k++] = EGL_WIDTH; - attr[k++] = frame->width / ( image_index + 1 ); /* half size for chroma */ + attr[k++] = frames->width / ( image_index + 1 ); /* half size for chroma */ attr[k++] = EGL_HEIGHT; - attr[k++] = frame->height / ( image_index + 1 ); + attr[k++] = frames->height / ( image_index + 1 ); attr[k++] = EGL_DMA_BUF_PLANE0_FD_EXT; attr[k++] = object->fd; @@ -747,38 +764,20 @@ static SDL_bool GetTextureForVAAPIFrame(AVFrame *frame, SDL_Texture **texture) static SDL_bool GetTextureForD3D11Frame(AVFrame *frame, SDL_Texture **texture) { #ifdef SDL_PLATFORM_WIN32 + AVHWFramesContext *frames = (AVHWFramesContext *)(frame->hw_frames_ctx->data); int texture_width = 0, texture_height = 0; ID3D11Texture2D *pTexture = (ID3D11Texture2D *)frame->data[0]; UINT iSliceIndex = (UINT)(uintptr_t)frame->data[1]; - D3D11_TEXTURE2D_DESC desc; - SDL_zero(desc); - ID3D11Texture2D_GetDesc(pTexture, &desc); - if (*texture) { SDL_QueryTexture(*texture, NULL, NULL, &texture_width, &texture_height); } - if (!*texture || (UINT)texture_width != desc.Width || (UINT)texture_height != desc.Height) { - Uint32 format; - - switch (desc.Format) { - case DXGI_FORMAT_NV12: - format = SDL_PIXELFORMAT_NV12; - break; - case DXGI_FORMAT_P010: - format = SDL_PIXELFORMAT_P010; - break; - default: - SDL_SetError("Unsupported texture format %d", desc.Format); - return SDL_FALSE; - } - + if (!*texture || texture_width != frames->width || texture_height != frames->height) { if (*texture) { SDL_DestroyTexture(*texture); } - SDL_PropertiesID props = CreateVideoTextureProperties(frame, format, SDL_TEXTUREACCESS_STATIC, desc.Width, desc.Height); - SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, desc.Height); + SDL_PropertiesID props = CreateVideoTextureProperties(frame, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_STATIC); *texture = SDL_CreateTextureWithProperties(renderer, props); SDL_DestroyProperties(props); if (!*texture) { @@ -803,29 +802,7 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex { #ifdef SDL_PLATFORM_APPLE CVPixelBufferRef pPixelBuffer = (CVPixelBufferRef)frame->data[3]; - OSType nPixelBufferType = CVPixelBufferGetPixelFormatType(pPixelBuffer); - size_t nPixelBufferWidth = CVPixelBufferGetWidthOfPlane(pPixelBuffer, 0); - size_t nPixelBufferHeight = CVPixelBufferGetHeightOfPlane(pPixelBuffer, 0); SDL_PropertiesID props; - Uint32 format; - - switch (nPixelBufferType) { - case kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange: - case kCVPixelFormatType_420YpCbCr8BiPlanarFullRange: - format = SDL_PIXELFORMAT_NV12; - break; - case kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange: - case kCVPixelFormatType_420YpCbCr10BiPlanarFullRange: - format = SDL_PIXELFORMAT_P010; - break; - default: - SDL_SetError("Unsupported texture format %c%c%c%c", - (char)((nPixelBufferType >> 24) & 0xFF), - (char)((nPixelBufferType >> 16) & 0xFF), - (char)((nPixelBufferType >> 8) & 0xFF), - (char)((nPixelBufferType >> 0) & 0xFF)); - return SDL_FALSE; - } if (*texture) { /* Free the previous texture now that we're about to render a new one */ @@ -833,7 +810,7 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex SDL_DestroyTexture(*texture); } - props = CreateVideoTextureProperties(frame, format, SDL_TEXTUREACCESS_STATIC, nPixelBufferWidth, nPixelBufferHeight); + props = CreateVideoTextureProperties(frame, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_STATIC); SDL_SetProperty(props, SDL_PROP_TEXTURE_CREATE_METAL_PIXELBUFFER_POINTER, pPixelBuffer); *texture = SDL_CreateTextureWithProperties(renderer, props); SDL_DestroyProperties(props); @@ -850,11 +827,12 @@ static SDL_bool GetTextureForVideoToolboxFrame(AVFrame *frame, SDL_Texture **tex static SDL_bool GetTextureForVulkanFrame(AVFrame *frame, SDL_Texture **texture) { SDL_PropertiesID props; + if (*texture) { SDL_DestroyTexture(*texture); } - props = CreateVideoTextureProperties(frame, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_STATIC, frame->width, frame->height); + props = CreateVideoTextureProperties(frame, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_STATIC); *texture = CreateVulkanVideoTexture(vulkan_context, frame, renderer, props); SDL_DestroyProperties(props); if (!*texture) { @@ -905,10 +883,15 @@ static void DisplayVideoTexture(AVFrame *frame) return; } + SDL_FRect src; + src.x = 0.0f; + src.y = 0.0f; + src.w = (float)frame->width; + src.h = (float)frame->height; if (frame->linesize[0] < 0) { - SDL_RenderTextureRotated(renderer, video_texture, NULL, NULL, 0.0, NULL, SDL_FLIP_VERTICAL); + SDL_RenderTextureRotated(renderer, video_texture, &src, NULL, 0.0, NULL, SDL_FLIP_VERTICAL); } else { - SDL_RenderTexture(renderer, video_texture, NULL, NULL); + SDL_RenderTexture(renderer, video_texture, &src, NULL); } } From 26e3ca7387e7f477a173e3a35c8457a8df6a4bc1 Mon Sep 17 00:00:00 2001 From: Ozkan Sezer Date: Tue, 5 Mar 2024 14:33:06 +0300 Subject: [PATCH 203/220] hidapi: minor sync with mainstream (for sake of symmetry, only.) --- src/hidapi/BUILD.cmake.md | 2 +- src/hidapi/CMakeLists.txt | 2 +- src/hidapi/hidtest/CMakeLists.txt | 2 +- src/hidapi/libusb/CMakeLists.txt | 2 +- src/hidapi/linux/CMakeLists.txt | 2 +- src/hidapi/mac/CMakeLists.txt | 2 +- src/hidapi/netbsd/CMakeLists.txt | 2 +- src/hidapi/subprojects/hidapi_build_cmake/CMakeLists.txt | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/hidapi/BUILD.cmake.md b/src/hidapi/BUILD.cmake.md index 5555fd2ea6a63..573f910dec030 100644 --- a/src/hidapi/BUILD.cmake.md +++ b/src/hidapi/BUILD.cmake.md @@ -159,7 +159,7 @@ endif() HIDAPI can be easily used as a subdirectory of a larger CMake project: ```cmake # root CMakeLists.txt -cmake_minimum_required(VERSION 3.4.3 FATAL_ERROR) +cmake_minimum_required(VERSION 3.4.3...3.25 FATAL_ERROR) add_subdirectory(hidapi) add_subdirectory(my_application) diff --git a/src/hidapi/CMakeLists.txt b/src/hidapi/CMakeLists.txt index b4c99be51cc52..d7086813cdf82 100644 --- a/src/hidapi/CMakeLists.txt +++ b/src/hidapi/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.3 FATAL_ERROR) +cmake_minimum_required(VERSION 3.1.3...3.25 FATAL_ERROR) if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) add_subdirectory(src) diff --git a/src/hidapi/hidtest/CMakeLists.txt b/src/hidapi/hidtest/CMakeLists.txt index 701a4fb9110e0..19c50e1fb52f8 100644 --- a/src/hidapi/hidtest/CMakeLists.txt +++ b/src/hidapi/hidtest/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.3 FATAL_ERROR) +cmake_minimum_required(VERSION 3.1.3...3.25 FATAL_ERROR) project(hidtest C) if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) diff --git a/src/hidapi/libusb/CMakeLists.txt b/src/hidapi/libusb/CMakeLists.txt index 617cd551a159b..4c458c5690a6b 100644 --- a/src/hidapi/libusb/CMakeLists.txt +++ b/src/hidapi/libusb/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.6.3 FATAL_ERROR) +cmake_minimum_required(VERSION 3.6.3...3.25 FATAL_ERROR) list(APPEND HIDAPI_PUBLIC_HEADERS "hidapi_libusb.h") diff --git a/src/hidapi/linux/CMakeLists.txt b/src/hidapi/linux/CMakeLists.txt index 0970ac3f1cfaa..9c627087f1546 100644 --- a/src/hidapi/linux/CMakeLists.txt +++ b/src/hidapi/linux/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.6.3 FATAL_ERROR) +cmake_minimum_required(VERSION 3.6.3...3.25 FATAL_ERROR) add_library(hidapi_hidraw ${HIDAPI_PUBLIC_HEADERS} diff --git a/src/hidapi/mac/CMakeLists.txt b/src/hidapi/mac/CMakeLists.txt index ccb0b91d43f37..0a1c1d95b6366 100644 --- a/src/hidapi/mac/CMakeLists.txt +++ b/src/hidapi/mac/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.4.3 FATAL_ERROR) +cmake_minimum_required(VERSION 3.4.3...3.25 FATAL_ERROR) list(APPEND HIDAPI_PUBLIC_HEADERS "hidapi_darwin.h") diff --git a/src/hidapi/netbsd/CMakeLists.txt b/src/hidapi/netbsd/CMakeLists.txt index 86067f895fd87..3b3e4d049bfb4 100644 --- a/src/hidapi/netbsd/CMakeLists.txt +++ b/src/hidapi/netbsd/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.6.3 FATAL_ERROR) +cmake_minimum_required(VERSION 3.6.3...3.25 FATAL_ERROR) add_library(hidapi_netbsd ${HIDAPI_PUBLIC_HEADERS} diff --git a/src/hidapi/subprojects/hidapi_build_cmake/CMakeLists.txt b/src/hidapi/subprojects/hidapi_build_cmake/CMakeLists.txt index 80aed67d556d5..4586ce6a6e20b 100644 --- a/src/hidapi/subprojects/hidapi_build_cmake/CMakeLists.txt +++ b/src/hidapi/subprojects/hidapi_build_cmake/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.1.3 FATAL_ERROR) +cmake_minimum_required(VERSION 3.1.3...3.25 FATAL_ERROR) project(hidapi LANGUAGES C) file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/root") From 98bec6749f202176238416ca650c11b5ab0eb1d2 Mon Sep 17 00:00:00 2001 From: Ozkan Sezer Date: Tue, 5 Mar 2024 11:11:04 +0300 Subject: [PATCH 204/220] hidapi, windows: sync with mainstream: change MAX_STRING_WCHARS to 126. This merges mainstream commit https://github.com/libusb/hidapi/commit/4f2e91bae80cc48e567a80bd9ae3dc53dc5b73c6 (authored by Vladimir Gladkov) into ours. From the original commit log: Win32 HID API doc says: For USB devices, the maximum string length is 126 wide characters (not including the terminating NULL character). For certain USB devices, using a buffer larger or equal to 127 wchars results in successful completion of HID API functions, but a broken string is stored in the output buffer. This behaviour persists even if HID API is bypassed and HID IOCTLs are passed to the HID driver directly (IOCTL_HID_GET_MANUFACTURER_STRING, IOCTL_HID_GET_PRODUCT_STRING, etc). So, the buffer MUST NOT exceed 126 wchars. windows: refactor ULONGLONG hid_internal_get_info(...) -> hid_internal_detect_bus_type_result hid_internal_detect_bus_type(...) hid_internal_detect_bus_type is now only responsible for detection of the bus type; rename it accordingly. Also, mixing an internal flag and DEV_INST into an ULONGLONG retval feels kinda hackish; use a cleaner approach instead (add an internal flag to help distinguishing between BLUETOOTH and BLE devices, then clear it once we are done). --- src/hidapi/windows/hid.c | 86 ++++++++++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/src/hidapi/windows/hid.c b/src/hidapi/windows/hid.c index b602fb7c2ee94..660c20cefae05 100644 --- a/src/hidapi/windows/hid.c +++ b/src/hidapi/windows/hid.c @@ -72,6 +72,15 @@ typedef LONG NTSTATUS; /* BLUETOOTH_DEVICE_NAME_SIZE from bluetoothapis.h is 256 */ #define MAX_STRING_WCHARS 256 +/* For certain USB devices, using a buffer larger or equal to 127 wchars results + in successful completion of HID API functions, but a broken string is stored + in the output buffer. This behaviour persists even if HID API is bypassed and + HID IOCTLs are passed to the HID driver directly. Therefore, for USB devices, + the buffer MUST NOT exceed 126 WCHARs. +*/ + +#define MAX_STRING_WCHARS_USB 126 + static struct hid_api_version api_version = { .major = HID_API_VERSION_MAJOR, .minor = HID_API_VERSION_MINOR, @@ -719,11 +728,22 @@ static hid_bus_type get_bus_type(const wchar_t* interface_path) } #endif /* HIDAPI_IGNORE_DEVICE */ -static void hid_internal_get_info(const wchar_t* interface_path, struct hid_device_info* dev) +/* Unfortunately, HID_API_BUS_xxx constants alone aren't enough to distinguish between BLUETOOTH and BLE */ + +#define HID_API_BUS_FLAG_BLE 0x01 + +typedef struct hid_internal_detect_bus_type_result_ { + DEVINST dev_node; + hid_bus_type bus_type; + unsigned int bus_flags; +} hid_internal_detect_bus_type_result; + +static hid_internal_detect_bus_type_result hid_internal_detect_bus_type(const wchar_t* interface_path) { wchar_t *device_id = NULL, *compatible_ids = NULL; CONFIGRET cr; DEVINST dev_node; + hid_internal_detect_bus_type_result result = { 0 }; /* Get the device id from interface path */ device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING); @@ -754,42 +774,45 @@ static void hid_internal_get_info(const wchar_t* interface_path, struct hid_devi https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers */ if (wcsstr(compatible_id, L"USB") != NULL) { - dev->bus_type = HID_API_BUS_USB; - hid_internal_get_usb_info(dev, dev_node); + result.bus_type = HID_API_BUS_USB; break; } /* Bluetooth devices https://docs.microsoft.com/windows-hardware/drivers/bluetooth/installing-a-bluetooth-device */ if (wcsstr(compatible_id, L"BTHENUM") != NULL) { - dev->bus_type = HID_API_BUS_BLUETOOTH; + result.bus_type = HID_API_BUS_BLUETOOTH; break; } /* Bluetooth LE devices */ if (wcsstr(compatible_id, L"BTHLEDEVICE") != NULL) { - dev->bus_type = HID_API_BUS_BLUETOOTH; - hid_internal_get_ble_info(dev, dev_node); + result.bus_type = HID_API_BUS_BLUETOOTH; + result.bus_flags |= HID_API_BUS_FLAG_BLE; break; } /* I2C devices https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support-and-power-management */ if (wcsstr(compatible_id, L"PNP0C50") != NULL) { - dev->bus_type = HID_API_BUS_I2C; + result.bus_type = HID_API_BUS_I2C; break; } /* SPI devices https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-for-spi */ if (wcsstr(compatible_id, L"PNP0C51") != NULL) { - dev->bus_type = HID_API_BUS_SPI; + result.bus_type = HID_API_BUS_SPI; break; } } + + result.dev_node = dev_node; + end: free(device_id); free(compatible_ids); + return result; } static char *hid_internal_UTF16toUTF8(const wchar_t *src) @@ -836,7 +859,10 @@ static struct hid_device_info *hid_internal_get_device_info(const wchar_t *path, HIDD_ATTRIBUTES attrib; PHIDP_PREPARSED_DATA pp_data = NULL; HIDP_CAPS caps; - wchar_t string[MAX_STRING_WCHARS]; + wchar_t string[MAX_STRING_WCHARS + 1]; + ULONG len; + ULONG size; + hid_internal_detect_bus_type_result detect_bus_type_result; /* Create the record. */ dev = (struct hid_device_info*)calloc(1, sizeof(struct hid_device_info)); @@ -870,25 +896,46 @@ static struct hid_device_info *hid_internal_get_device_info(const wchar_t *path, HidD_FreePreparsedData(pp_data); } + /* detect bus type before reading string descriptors */ + detect_bus_type_result = hid_internal_detect_bus_type(path); + dev->bus_type = detect_bus_type_result.bus_type; + + len = dev->bus_type == HID_API_BUS_USB ? MAX_STRING_WCHARS_USB : MAX_STRING_WCHARS; + string[len] = L'\0'; + size = len * sizeof(wchar_t); + /* Serial Number */ string[0] = L'\0'; - HidD_GetSerialNumberString(handle, string, sizeof(string)); - string[MAX_STRING_WCHARS - 1] = L'\0'; + HidD_GetSerialNumberString(handle, string, size); dev->serial_number = _wcsdup(string); /* Manufacturer String */ string[0] = L'\0'; - HidD_GetManufacturerString(handle, string, sizeof(string)); - string[MAX_STRING_WCHARS - 1] = L'\0'; + HidD_GetManufacturerString(handle, string, size); dev->manufacturer_string = _wcsdup(string); /* Product String */ string[0] = L'\0'; - HidD_GetProductString(handle, string, sizeof(string)); - string[MAX_STRING_WCHARS - 1] = L'\0'; + HidD_GetProductString(handle, string, size); dev->product_string = _wcsdup(string); - hid_internal_get_info(path, dev); + /* now, the portion that depends on string descriptors */ + switch (dev->bus_type) { + case HID_API_BUS_USB: + hid_internal_get_usb_info(dev, detect_bus_type_result.dev_node); + break; + + case HID_API_BUS_BLUETOOTH: + if (detect_bus_type_result.bus_flags & HID_API_BUS_FLAG_BLE) + hid_internal_get_ble_info(dev, detect_bus_type_result.dev_node); + break; + + case HID_API_BUS_UNKNOWN: + case HID_API_BUS_SPI: + case HID_API_BUS_I2C: + /* shut down -Wswitch */ + break; + } return dev; } @@ -1572,7 +1619,12 @@ int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int { BOOL res; - res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * (DWORD) MIN(maxlen, MAX_STRING_WCHARS)); + if (dev->device_info && dev->device_info->bus_type == HID_API_BUS_USB && maxlen > MAX_STRING_WCHARS_USB) { + string[MAX_STRING_WCHARS_USB] = L'\0'; + maxlen = MAX_STRING_WCHARS_USB; + } + + res = HidD_GetIndexedString(dev->device_handle, string_index, string, (ULONG)maxlen * sizeof(wchar_t)); if (!res) { register_winapi_error(dev, L"HidD_GetIndexedString"); return -1; From d4f4aa745a9d9c280d5016322a2473c9d2e077a0 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 5 Mar 2024 04:46:21 -0800 Subject: [PATCH 205/220] Use C++ style comments in new code --- test/testffmpeg_vulkan.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/testffmpeg_vulkan.c b/test/testffmpeg_vulkan.c index 6f1ebca923f1c..397b77919cfbb 100644 --- a/test/testffmpeg_vulkan.c +++ b/test/testffmpeg_vulkan.c @@ -841,7 +841,7 @@ int BeginVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_R VkResult result = context->vkQueueSubmit(context->graphicsQueue, 1, &submitInfo, 0); if (result != VK_SUCCESS) { - /* Don't return an error here, we need to complete the frame operation */ + // Don't return an error here, we need to complete the frame operation SDL_LogError(SDL_LOG_CATEGORY_APPLICATION , "vkQueueSubmit(): %s", getVulkanResultString(result)); } @@ -856,7 +856,7 @@ int FinishVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_ AVVulkanFramesContext *vk = (AVVulkanFramesContext *)(frames->hwctx); AVVkFrame *pVkFrame = (AVVkFrame *)frame->data[0]; - /* Transition the frame back to ffmpeg */ + // Transition the frame back to ffmpeg ++pVkFrame->sem_value[0]; VkTimelineSemaphoreSubmitInfo timeline = { 0 }; @@ -876,7 +876,7 @@ int FinishVulkanFrameRendering(VulkanVideoContext *context, AVFrame *frame, SDL_ VkResult result = context->vkQueueSubmit(context->graphicsQueue, 1, &submitInfo, 0); if (result != VK_SUCCESS) { - /* Don't return an error here, we need to complete the frame operation */ + // Don't return an error here, we need to complete the frame operation SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "vkQueueSubmit(): %s", getVulkanResultString(result)); } From 4898505f23f835445b4a0a618cd7ca525d734def Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Tue, 5 Mar 2024 20:38:00 +0100 Subject: [PATCH 206/220] cmake: add winres to the list of potential name rc compilers --- build-scripts/cmake-toolchain-mingw64-i686.cmake | 2 +- build-scripts/cmake-toolchain-mingw64-x86_64.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-scripts/cmake-toolchain-mingw64-i686.cmake b/build-scripts/cmake-toolchain-mingw64-i686.cmake index e7892ee8cb188..8be7b3a80fe07 100644 --- a/build-scripts/cmake-toolchain-mingw64-i686.cmake +++ b/build-scripts/cmake-toolchain-mingw64-i686.cmake @@ -3,7 +3,7 @@ set(CMAKE_SYSTEM_PROCESSOR x86) find_program(CMAKE_C_COMPILER NAMES i686-w64-mingw32-gcc) find_program(CMAKE_CXX_COMPILER NAMES i686-w64-mingw32-g++) -find_program(CMAKE_RC_COMPILER NAMES i686-w64-mingw32-windres) +find_program(CMAKE_RC_COMPILER NAMES i686-w64-mingw32-windres windres) if(NOT CMAKE_C_COMPILER) message(FATAL_ERROR "Failed to find CMAKE_C_COMPILER.") diff --git a/build-scripts/cmake-toolchain-mingw64-x86_64.cmake b/build-scripts/cmake-toolchain-mingw64-x86_64.cmake index be3efd9429e41..8bf436695e1fd 100644 --- a/build-scripts/cmake-toolchain-mingw64-x86_64.cmake +++ b/build-scripts/cmake-toolchain-mingw64-x86_64.cmake @@ -3,7 +3,7 @@ set(CMAKE_SYSTEM_PROCESSOR x86_64) find_program(CMAKE_C_COMPILER NAMES x86_64-w64-mingw32-gcc) find_program(CMAKE_CXX_COMPILER NAMES x86_64-w64-mingw32-g++) -find_program(CMAKE_RC_COMPILER NAMES x86_64-w64-mingw32-windres) +find_program(CMAKE_RC_COMPILER NAMES x86_64-w64-mingw32-windres windres) if(NOT CMAKE_C_COMPILER) message(FATAL_ERROR "Failed to find CMAKE_C_COMPILER.") From 1e0bac288bcf53ee368dad510479e42320e87382 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 5 Mar 2024 12:46:44 -0800 Subject: [PATCH 207/220] Use the hlsli extension for shader includes --- src/render/direct3d11/D3D11_PixelShader_Advanced.hlsl | 2 +- src/render/direct3d11/D3D11_PixelShader_Colors.hlsl | 2 +- ..._PixelShader_Common.incl => D3D11_PixelShader_Common.hlsli} | 0 src/render/direct3d11/D3D11_PixelShader_Textures.hlsl | 2 +- src/render/direct3d11/SDL_render_d3d11.c | 2 +- src/render/vulkan/SDL_render_vulkan.c | 2 +- src/render/vulkan/VULKAN_PixelShader_Advanced.hlsl | 3 ++- src/render/vulkan/VULKAN_PixelShader_Colors.hlsl | 2 +- ...PixelShader_Common.incl => VULKAN_PixelShader_Common.hlsli} | 0 src/render/vulkan/VULKAN_PixelShader_Textures.hlsl | 3 ++- 10 files changed, 10 insertions(+), 8 deletions(-) rename src/render/direct3d11/{D3D11_PixelShader_Common.incl => D3D11_PixelShader_Common.hlsli} (100%) rename src/render/vulkan/{VULKAN_PixelShader_Common.incl => VULKAN_PixelShader_Common.hlsli} (100%) diff --git a/src/render/direct3d11/D3D11_PixelShader_Advanced.hlsl b/src/render/direct3d11/D3D11_PixelShader_Advanced.hlsl index 39dda47bca849..aad7b77a112e0 100644 --- a/src/render/direct3d11/D3D11_PixelShader_Advanced.hlsl +++ b/src/render/direct3d11/D3D11_PixelShader_Advanced.hlsl @@ -1,5 +1,5 @@ -#include "D3D11_PixelShader_Common.incl" +#include "D3D11_PixelShader_Common.hlsli" float4 main(PixelShaderInput input) : SV_TARGET { diff --git a/src/render/direct3d11/D3D11_PixelShader_Colors.hlsl b/src/render/direct3d11/D3D11_PixelShader_Colors.hlsl index b8a26e9177327..5757491b31db0 100644 --- a/src/render/direct3d11/D3D11_PixelShader_Colors.hlsl +++ b/src/render/direct3d11/D3D11_PixelShader_Colors.hlsl @@ -1,5 +1,5 @@ -#include "D3D11_PixelShader_Common.incl" +#include "D3D11_PixelShader_Common.hlsli" float4 main(PixelShaderInput input) : SV_TARGET { diff --git a/src/render/direct3d11/D3D11_PixelShader_Common.incl b/src/render/direct3d11/D3D11_PixelShader_Common.hlsli similarity index 100% rename from src/render/direct3d11/D3D11_PixelShader_Common.incl rename to src/render/direct3d11/D3D11_PixelShader_Common.hlsli diff --git a/src/render/direct3d11/D3D11_PixelShader_Textures.hlsl b/src/render/direct3d11/D3D11_PixelShader_Textures.hlsl index fd8ae345036a6..f2ecf18a8a138 100644 --- a/src/render/direct3d11/D3D11_PixelShader_Textures.hlsl +++ b/src/render/direct3d11/D3D11_PixelShader_Textures.hlsl @@ -1,7 +1,7 @@ Texture2D theTexture : register(t0); SamplerState theSampler : register(s0); -#include "D3D11_PixelShader_Common.incl" +#include "D3D11_PixelShader_Common.hlsli" float4 main(PixelShaderInput input) : SV_TARGET { diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index cef5c816a4371..7297e632e5b3f 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -75,7 +75,7 @@ typedef struct Float4X4 projectionAndView; } VertexShaderConstants; -/* These should mirror the definitions in D3D11_PixelShader_Common.incl */ +/* These should mirror the definitions in D3D11_PixelShader_Common.hlsli */ //static const float TONEMAP_NONE = 0; //static const float TONEMAP_LINEAR = 1; static const float TONEMAP_CHROME = 2; diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 7f9490b7bb0d9..08b5c7dc6f068 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -171,7 +171,7 @@ typedef struct Float4X4 projectionAndView; } VertexShaderConstants; -/* These should mirror the definitions in VULKAN_PixelShader_Common.incl */ +/* These should mirror the definitions in VULKAN_PixelShader_Common.hlsli */ //static const float TONEMAP_NONE = 0; //static const float TONEMAP_LINEAR = 1; static const float TONEMAP_CHROME = 2; diff --git a/src/render/vulkan/VULKAN_PixelShader_Advanced.hlsl b/src/render/vulkan/VULKAN_PixelShader_Advanced.hlsl index 6269d0b5b2858..f3a0a61337974 100644 --- a/src/render/vulkan/VULKAN_PixelShader_Advanced.hlsl +++ b/src/render/vulkan/VULKAN_PixelShader_Advanced.hlsl @@ -1,4 +1,5 @@ -#include "VULKAN_PixelShader_Common.incl" + +#include "VULKAN_PixelShader_Common.hlsli" float4 main(PixelShaderInput input) : SV_TARGET { diff --git a/src/render/vulkan/VULKAN_PixelShader_Colors.hlsl b/src/render/vulkan/VULKAN_PixelShader_Colors.hlsl index e84234859c773..b754dde105943 100644 --- a/src/render/vulkan/VULKAN_PixelShader_Colors.hlsl +++ b/src/render/vulkan/VULKAN_PixelShader_Colors.hlsl @@ -1,5 +1,5 @@ -#include "VULKAN_PixelShader_Common.incl" +#include "VULKAN_PixelShader_Common.hlsli" float4 main(PixelShaderInput input) : SV_TARGET0 { diff --git a/src/render/vulkan/VULKAN_PixelShader_Common.incl b/src/render/vulkan/VULKAN_PixelShader_Common.hlsli similarity index 100% rename from src/render/vulkan/VULKAN_PixelShader_Common.incl rename to src/render/vulkan/VULKAN_PixelShader_Common.hlsli diff --git a/src/render/vulkan/VULKAN_PixelShader_Textures.hlsl b/src/render/vulkan/VULKAN_PixelShader_Textures.hlsl index c48aee7c03a7e..e665291bc3f12 100644 --- a/src/render/vulkan/VULKAN_PixelShader_Textures.hlsl +++ b/src/render/vulkan/VULKAN_PixelShader_Textures.hlsl @@ -1,4 +1,5 @@ -#include "VULKAN_PixelShader_Common.incl" + +#include "VULKAN_PixelShader_Common.hlsli" float4 main(PixelShaderInput input) : SV_TARGET { From cea717e5d31dbe25dfbd1b891e51c7d06221a310 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 5 Mar 2024 21:11:32 +0100 Subject: [PATCH 208/220] Removed some uneeded 'unsigned': renderer.num_texture_format and SDL_Vulkan_GetInstanceExtensions() prototype --- include/SDL3/SDL_render.h | 2 +- include/SDL3/SDL_vulkan.h | 6 +++--- src/dynapi/SDL_dynapi_procs.h | 2 +- src/render/SDL_render.c | 16 ++++++++-------- src/render/vulkan/SDL_render_vulkan.c | 2 +- src/test/SDL_test_common.c | 4 ++-- src/video/SDL_video.c | 6 +++--- test/testffmpeg_vulkan.c | 2 +- test/testvulkan.c | 2 +- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h index b52087e713b2c..e2bf9e317b170 100644 --- a/include/SDL3/SDL_render.h +++ b/include/SDL3/SDL_render.h @@ -79,7 +79,7 @@ typedef struct SDL_RendererInfo { const char *name; /**< The name of the renderer */ Uint32 flags; /**< Supported ::SDL_RendererFlags */ - Uint32 num_texture_formats; /**< The number of available texture formats */ + int num_texture_formats; /**< The number of available texture formats */ Uint32 texture_formats[16]; /**< The available texture formats */ int max_texture_width; /**< The maximum texture width */ int max_texture_height; /**< The maximum texture height */ diff --git a/include/SDL3/SDL_vulkan.h b/include/SDL3/SDL_vulkan.h index 98590786e56fc..58fe118836285 100644 --- a/include/SDL3/SDL_vulkan.h +++ b/include/SDL3/SDL_vulkan.h @@ -141,7 +141,7 @@ extern DECLSPEC void SDLCALL SDL_Vulkan_UnloadLibrary(void); * This should be called after either calling SDL_Vulkan_LoadLibrary() or * creating an SDL_Window with the `SDL_WINDOW_VULKAN` flag. * - * On return, the variable pointed to by `pCount` will be set to the number of + * On return, the variable pointed to by `count` will be set to the number of * elements returned, suitable for using with * VkInstanceCreateInfo::enabledExtensionCount, and the returned array can be * used with VkInstanceCreateInfo::ppEnabledExtensionNames, for calling @@ -149,7 +149,7 @@ extern DECLSPEC void SDLCALL SDL_Vulkan_UnloadLibrary(void); * * You should not free the returned array; it is owned by SDL. * - * \param pCount A pointer to Uint32 that will be filled with the number of + * \param count A pointer to an int that will be filled with the number of * extensions returned. * \returns An array of extension name strings on success, NULL on error. * @@ -157,7 +157,7 @@ extern DECLSPEC void SDLCALL SDL_Vulkan_UnloadLibrary(void); * * \sa SDL_Vulkan_CreateSurface */ -extern DECLSPEC char const* const* SDLCALL SDL_Vulkan_GetInstanceExtensions(Uint32 *pCount); +extern DECLSPEC char const* const* SDLCALL SDL_Vulkan_GetInstanceExtensions(int *count); /** * Create a Vulkan rendering surface for a window. diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index b19f615cb83f4..cae0a6394af0a 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -688,7 +688,7 @@ SDL_DYNAPI_PROC(int,SDL_UpdateWindowSurface,(SDL_Window *a),(a),return) SDL_DYNAPI_PROC(int,SDL_UpdateWindowSurfaceRects,(SDL_Window *a, const SDL_Rect *b, int c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_UpdateYUVTexture,(SDL_Texture *a, const SDL_Rect *b, const Uint8 *c, int d, const Uint8 *e, int f, const Uint8 *g, int h),(a,b,c,d,e,f,g,h),return) SDL_DYNAPI_PROC(SDL_bool,SDL_Vulkan_CreateSurface,(SDL_Window *a, VkInstance b, const struct VkAllocationCallbacks *c, VkSurfaceKHR *d),(a,b,c,d),return) -SDL_DYNAPI_PROC(char const* const*,SDL_Vulkan_GetInstanceExtensions,(Uint32 *a),(a),return) +SDL_DYNAPI_PROC(char const* const*,SDL_Vulkan_GetInstanceExtensions,(int *a),(a),return) SDL_DYNAPI_PROC(SDL_FunctionPointer,SDL_Vulkan_GetVkGetInstanceProcAddr,(void),(),return) SDL_DYNAPI_PROC(int,SDL_Vulkan_LoadLibrary,(const char *a),(a),return) SDL_DYNAPI_PROC(void,SDL_Vulkan_UnloadLibrary,(void),(),) diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 72f23cd699ee4..a6a785bbf7ff3 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -1187,7 +1187,7 @@ static SDL_bool IsSupportedBlendMode(SDL_Renderer *renderer, SDL_BlendMode blend static SDL_bool IsSupportedFormat(SDL_Renderer *renderer, Uint32 format) { - Uint32 i; + int i; for (i = 0; i < renderer->info.num_texture_formats; ++i) { if (renderer->info.texture_formats[i] == format) { @@ -1199,7 +1199,7 @@ static SDL_bool IsSupportedFormat(SDL_Renderer *renderer, Uint32 format) static Uint32 GetClosestSupportedFormat(SDL_Renderer *renderer, Uint32 format) { - Uint32 i; + int i; if (SDL_ISPIXELFORMAT_FOURCC(format)) { /* Look for an exact match */ @@ -1438,14 +1438,14 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s /* No alpha, but a colorkey => promote to alpha */ if (!fmt->Amask && SDL_SurfaceHasColorKey(surface)) { if (fmt->format == SDL_PIXELFORMAT_XRGB8888) { - for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) { + for (i = 0; i < renderer->info.num_texture_formats; ++i) { if (renderer->info.texture_formats[i] == SDL_PIXELFORMAT_ARGB8888) { format = SDL_PIXELFORMAT_ARGB8888; break; } } } else if (fmt->format == SDL_PIXELFORMAT_XBGR8888) { - for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) { + for (i = 0; i < renderer->info.num_texture_formats; ++i) { if (renderer->info.texture_formats[i] == SDL_PIXELFORMAT_ABGR8888) { format = SDL_PIXELFORMAT_ABGR8888; break; @@ -1454,7 +1454,7 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s } } else { /* Exact match would be fine */ - for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) { + for (i = 0; i < renderer->info.num_texture_formats; ++i) { if (renderer->info.texture_formats[i] == fmt->format) { format = fmt->format; break; @@ -1464,7 +1464,7 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s /* Look for 10-bit pixel formats if needed */ if (format == SDL_PIXELFORMAT_UNKNOWN && SDL_ISPIXELFORMAT_10BIT(fmt->format)) { - for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) { + for (i = 0; i < renderer->info.num_texture_formats; ++i) { if (SDL_ISPIXELFORMAT_10BIT(renderer->info.texture_formats[i])) { format = renderer->info.texture_formats[i]; break; @@ -1475,7 +1475,7 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s /* Look for floating point pixel formats if needed */ if (format == SDL_PIXELFORMAT_UNKNOWN && (SDL_ISPIXELFORMAT_10BIT(fmt->format) || SDL_ISPIXELFORMAT_FLOAT(fmt->format))) { - for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) { + for (i = 0; i < renderer->info.num_texture_formats; ++i) { if (SDL_ISPIXELFORMAT_FLOAT(renderer->info.texture_formats[i])) { format = renderer->info.texture_formats[i]; break; @@ -1486,7 +1486,7 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s /* Fallback, choose a valid pixel format */ if (format == SDL_PIXELFORMAT_UNKNOWN) { format = renderer->info.texture_formats[0]; - for (i = 0; i < (int)renderer->info.num_texture_formats; ++i) { + for (i = 0; i < renderer->info.num_texture_formats; ++i) { if (!SDL_ISPIXELFORMAT_FOURCC(renderer->info.texture_formats[i]) && SDL_ISPIXELFORMAT_ALPHA(renderer->info.texture_formats[i]) == needAlpha) { format = renderer->info.texture_formats[i]; diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 08b5c7dc6f068..71545a7b21b93 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -1708,7 +1708,7 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert appInfo.apiVersion = VK_API_VERSION_1_0; instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instanceCreateInfo.pApplicationInfo = &appInfo; - char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount); + char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions((int *)&instanceCreateInfo.enabledExtensionCount); const char **instanceExtensionsCopy = SDL_calloc(instanceCreateInfo.enabledExtensionCount + 2, sizeof(const char *)); for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) { diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c index afbe7f5c8c92f..f463aac3c056c 100644 --- a/src/test/SDL_test_common.c +++ b/src/test/SDL_test_common.c @@ -1048,8 +1048,8 @@ static void SDLTest_PrintRenderer(SDL_RendererInfo *info) SDL_snprintfcat(text, sizeof(text), ")"); SDL_Log("%s\n", text); - (void)SDL_snprintf(text, sizeof(text), " Texture formats (%" SDL_PRIu32 "): ", info->num_texture_formats); - for (i = 0; i < (int)info->num_texture_formats; ++i) { + (void)SDL_snprintf(text, sizeof(text), " Texture formats (%d): ", info->num_texture_formats); + for (i = 0; i < info->num_texture_formats; ++i) { if (i > 0) { SDL_snprintfcat(text, sizeof(text), ", "); } diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index a1fb6b3fb3099..2dc709448b1f7 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -313,7 +313,7 @@ static int SDL_CreateWindowTexture(SDL_VideoDevice *_this, SDL_Window *window, U /* Find the first format with or without an alpha channel */ *format = info.texture_formats[0]; - for (i = 0; i < (int)info.num_texture_formats; ++i) { + for (i = 0; i < info.num_texture_formats; ++i) { if (!SDL_ISPIXELFORMAT_FOURCC(info.texture_formats[i]) && transparent == SDL_ISPIXELFORMAT_ALPHA(info.texture_formats[i])) { *format = info.texture_formats[i]; @@ -5217,9 +5217,9 @@ void SDL_Vulkan_UnloadLibrary(void) } } -char const* const* SDL_Vulkan_GetInstanceExtensions(Uint32 *count) +char const* const* SDL_Vulkan_GetInstanceExtensions(int *count) { - return _this->Vulkan_GetInstanceExtensions(_this, count); + return _this->Vulkan_GetInstanceExtensions(_this, (Uint32 *)count); } SDL_bool SDL_Vulkan_CreateSurface(SDL_Window *window, diff --git a/test/testffmpeg_vulkan.c b/test/testffmpeg_vulkan.c index 397b77919cfbb..3bf92ce3860cf 100644 --- a/test/testffmpeg_vulkan.c +++ b/test/testffmpeg_vulkan.c @@ -206,7 +206,7 @@ static int createInstance(VulkanVideoContext *context) VkApplicationInfo appInfo = { 0 }; VkInstanceCreateInfo instanceCreateInfo = { 0 }; VkResult result; - char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount); + char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions((int *)&instanceCreateInfo.enabledExtensionCount); appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.apiVersion = VK_API_VERSION_1_3; diff --git a/test/testvulkan.c b/test/testvulkan.c index dcd0ebb1b1886..732a23fd0cbe5 100644 --- a/test/testvulkan.c +++ b/test/testvulkan.c @@ -229,7 +229,7 @@ static void createInstance(void) instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instanceCreateInfo.pApplicationInfo = &appInfo; - instanceCreateInfo.ppEnabledExtensionNames = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount); + instanceCreateInfo.ppEnabledExtensionNames = SDL_Vulkan_GetInstanceExtensions((int *)&instanceCreateInfo.enabledExtensionCount); result = vkCreateInstance(&instanceCreateInfo, NULL, &vulkanContext->instance); if (result != VK_SUCCESS) { vulkanContext->instance = VK_NULL_HANDLE; From 0b4a195f4babcfc2f7af6e686b9f3b0029511b04 Mon Sep 17 00:00:00 2001 From: SDL Wiki Bot Date: Tue, 5 Mar 2024 21:32:25 +0000 Subject: [PATCH 209/220] Sync SDL3 wiki -> header --- include/SDL3/SDL_vulkan.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/SDL3/SDL_vulkan.h b/include/SDL3/SDL_vulkan.h index 58fe118836285..958baf84630bd 100644 --- a/include/SDL3/SDL_vulkan.h +++ b/include/SDL3/SDL_vulkan.h @@ -150,7 +150,7 @@ extern DECLSPEC void SDLCALL SDL_Vulkan_UnloadLibrary(void); * You should not free the returned array; it is owned by SDL. * * \param count A pointer to an int that will be filled with the number of - * extensions returned. + * extensions returned. * \returns An array of extension name strings on success, NULL on error. * * \since This function is available since SDL 3.0.0. From c36f773eb47693ad360dd3c97c347e9ab07cfa4c Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 5 Mar 2024 13:10:27 -0800 Subject: [PATCH 210/220] Fixed "${folder^}Activity: bad substitution" with bash 3.2 on macOS --- build-scripts/androidbuild.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-scripts/androidbuild.sh b/build-scripts/androidbuild.sh index ec71cbcad0b61..1a107e27dee25 100755 --- a/build-scripts/androidbuild.sh +++ b/build-scripts/androidbuild.sh @@ -81,7 +81,7 @@ do done # Uppercase the first char in the activity class name because it's Java -ACTIVITY="${folder^}Activity" +ACTIVITY="$(echo $folder | awk '{$1=toupper(substr($1,0,1))substr($1,2)}1')Activity" sed -i -e "s|\"SDLActivity\"|\"$ACTIVITY\"|g" $BUILDPATH/app/src/main/AndroidManifest.xml # Fill in a default Activity From edbcef11ffdecab0ac8044c5fef021f5fbeed388 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 5 Mar 2024 13:34:37 -0800 Subject: [PATCH 211/220] Keep track of whether the Android on-screen keyboard was opened by the application Fixes https://github.com/libsdl-org/SDL/issues/9202 --- src/video/android/SDL_androidevents.c | 10 ++-------- src/video/android/SDL_androidkeyboard.c | 11 +++++++++++ src/video/android/SDL_androidkeyboard.h | 1 + 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/video/android/SDL_androidevents.c b/src/video/android/SDL_androidevents.c index 95ad8ead48e56..2c3444df2c40e 100644 --- a/src/video/android/SDL_androidevents.c +++ b/src/video/android/SDL_androidevents.c @@ -130,10 +130,7 @@ void Android_PumpEvents_Blocking(SDL_VideoDevice *_this) #endif /* Make sure SW Keyboard is restored when an app becomes foreground */ - if (SDL_TextInputActive() && - SDL_GetHintBoolean(SDL_HINT_ENABLE_SCREEN_KEYBOARD, SDL_TRUE)) { - Android_ShowScreenKeyboard(_this, Android_Window); /* Only showTextInput */ - } + Android_RestoreScreenKeyboardOnResume(_this, Android_Window); SDL_SendAppEvent(SDL_EVENT_DID_ENTER_FOREGROUND); SDL_SendWindowEvent(Android_Window, SDL_EVENT_WINDOW_RESTORED, 0, 0); @@ -210,10 +207,7 @@ void Android_PumpEvents_NonBlocking(SDL_VideoDevice *_this) #endif /* Make sure SW Keyboard is restored when an app becomes foreground */ - if (SDL_TextInputActive() && - SDL_GetHintBoolean(SDL_HINT_ENABLE_SCREEN_KEYBOARD, SDL_TRUE)) { - Android_ShowScreenKeyboard(_this, Android_Window); /* Only showTextInput */ - } + Android_RestoreScreenKeyboardOnResume(_this, Android_Window); SDL_SendAppEvent(SDL_EVENT_DID_ENTER_FOREGROUND); SDL_SendWindowEvent(Android_Window, SDL_EVENT_WINDOW_RESTORED, 0, 0); diff --git a/src/video/android/SDL_androidkeyboard.c b/src/video/android/SDL_androidkeyboard.c index 4d80397f001b5..c77ae57a3c10b 100644 --- a/src/video/android/SDL_androidkeyboard.c +++ b/src/video/android/SDL_androidkeyboard.c @@ -313,6 +313,8 @@ static SDL_Scancode Android_Keycodes[] = { SDL_SCANCODE_PASTE, /* AKEYCODE_PASTE */ }; +static bool SDL_screen_keyboard_shown; + static SDL_Scancode TranslateKeycode(int keycode) { SDL_Scancode scancode = SDL_SCANCODE_UNKNOWN; @@ -345,11 +347,20 @@ void Android_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) { SDL_VideoData *videodata = _this->driverdata; Android_JNI_ShowScreenKeyboard(&videodata->textRect); + SDL_screen_keyboard_shown = SDL_TRUE; } void Android_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window) { Android_JNI_HideScreenKeyboard(); + SDL_screen_keyboard_shown = SDL_FALSE; +} + +void Android_RestoreScreenKeyboardOnResume(SDL_VideoDevice *_this, SDL_Window *window) +{ + if (SDL_screen_keyboard_shown) { + Android_ShowScreenKeyboard(_this, window); + } } SDL_bool Android_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window) diff --git a/src/video/android/SDL_androidkeyboard.h b/src/video/android/SDL_androidkeyboard.h index 2693fa0e182e0..48de9d481f2c9 100644 --- a/src/video/android/SDL_androidkeyboard.h +++ b/src/video/android/SDL_androidkeyboard.h @@ -28,5 +28,6 @@ extern int Android_OnKeyUp(int keycode); extern SDL_bool Android_HasScreenKeyboardSupport(SDL_VideoDevice *_this); extern void Android_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); extern void Android_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); +extern void Android_RestoreScreenKeyboardOnResume(SDL_VideoDevice *_this, SDL_Window *window); extern SDL_bool Android_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window); extern int Android_SetTextInputRect(SDL_VideoDevice *_this, const SDL_Rect *rect); From 9db68f97f9d66f84c3beee5cb4bdeab48080a3b1 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 5 Mar 2024 13:39:42 -0800 Subject: [PATCH 212/220] Reverted SDL_Vulkan_GetInstanceExtensions() API change This function is commonly used with Vulkan structures that use uint32_t, so we should keep the Uint32 signature. --- include/SDL3/SDL_vulkan.h | 5 ++--- src/dynapi/SDL_dynapi_procs.h | 2 +- src/render/vulkan/SDL_render_vulkan.c | 2 +- src/video/SDL_video.c | 4 ++-- test/testffmpeg_vulkan.c | 2 +- test/testvulkan.c | 2 +- 6 files changed, 8 insertions(+), 9 deletions(-) diff --git a/include/SDL3/SDL_vulkan.h b/include/SDL3/SDL_vulkan.h index 958baf84630bd..64544c8b699cd 100644 --- a/include/SDL3/SDL_vulkan.h +++ b/include/SDL3/SDL_vulkan.h @@ -149,15 +149,14 @@ extern DECLSPEC void SDLCALL SDL_Vulkan_UnloadLibrary(void); * * You should not free the returned array; it is owned by SDL. * - * \param count A pointer to an int that will be filled with the number of - * extensions returned. + * \param count a pointer filled in with the number of extensions returned. * \returns An array of extension name strings on success, NULL on error. * * \since This function is available since SDL 3.0.0. * * \sa SDL_Vulkan_CreateSurface */ -extern DECLSPEC char const* const* SDLCALL SDL_Vulkan_GetInstanceExtensions(int *count); +extern DECLSPEC char const* const* SDLCALL SDL_Vulkan_GetInstanceExtensions(Uint32 *count); /** * Create a Vulkan rendering surface for a window. diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index cae0a6394af0a..b19f615cb83f4 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -688,7 +688,7 @@ SDL_DYNAPI_PROC(int,SDL_UpdateWindowSurface,(SDL_Window *a),(a),return) SDL_DYNAPI_PROC(int,SDL_UpdateWindowSurfaceRects,(SDL_Window *a, const SDL_Rect *b, int c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_UpdateYUVTexture,(SDL_Texture *a, const SDL_Rect *b, const Uint8 *c, int d, const Uint8 *e, int f, const Uint8 *g, int h),(a,b,c,d,e,f,g,h),return) SDL_DYNAPI_PROC(SDL_bool,SDL_Vulkan_CreateSurface,(SDL_Window *a, VkInstance b, const struct VkAllocationCallbacks *c, VkSurfaceKHR *d),(a,b,c,d),return) -SDL_DYNAPI_PROC(char const* const*,SDL_Vulkan_GetInstanceExtensions,(int *a),(a),return) +SDL_DYNAPI_PROC(char const* const*,SDL_Vulkan_GetInstanceExtensions,(Uint32 *a),(a),return) SDL_DYNAPI_PROC(SDL_FunctionPointer,SDL_Vulkan_GetVkGetInstanceProcAddr,(void),(),return) SDL_DYNAPI_PROC(int,SDL_Vulkan_LoadLibrary,(const char *a),(a),return) SDL_DYNAPI_PROC(void,SDL_Vulkan_UnloadLibrary,(void),(),) diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 71545a7b21b93..08b5c7dc6f068 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -1708,7 +1708,7 @@ static VkResult VULKAN_CreateDeviceResources(SDL_Renderer *renderer, SDL_Propert appInfo.apiVersion = VK_API_VERSION_1_0; instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instanceCreateInfo.pApplicationInfo = &appInfo; - char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions((int *)&instanceCreateInfo.enabledExtensionCount); + char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount); const char **instanceExtensionsCopy = SDL_calloc(instanceCreateInfo.enabledExtensionCount + 2, sizeof(const char *)); for (uint32_t i = 0; i < instanceCreateInfo.enabledExtensionCount; i++) { diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 2dc709448b1f7..4ac4c5766d9f3 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -5217,9 +5217,9 @@ void SDL_Vulkan_UnloadLibrary(void) } } -char const* const* SDL_Vulkan_GetInstanceExtensions(int *count) +char const* const* SDL_Vulkan_GetInstanceExtensions(Uint32 *count) { - return _this->Vulkan_GetInstanceExtensions(_this, (Uint32 *)count); + return _this->Vulkan_GetInstanceExtensions(_this, count); } SDL_bool SDL_Vulkan_CreateSurface(SDL_Window *window, diff --git a/test/testffmpeg_vulkan.c b/test/testffmpeg_vulkan.c index 3bf92ce3860cf..397b77919cfbb 100644 --- a/test/testffmpeg_vulkan.c +++ b/test/testffmpeg_vulkan.c @@ -206,7 +206,7 @@ static int createInstance(VulkanVideoContext *context) VkApplicationInfo appInfo = { 0 }; VkInstanceCreateInfo instanceCreateInfo = { 0 }; VkResult result; - char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions((int *)&instanceCreateInfo.enabledExtensionCount); + char const *const *instanceExtensions = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount); appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.apiVersion = VK_API_VERSION_1_3; diff --git a/test/testvulkan.c b/test/testvulkan.c index 732a23fd0cbe5..dcd0ebb1b1886 100644 --- a/test/testvulkan.c +++ b/test/testvulkan.c @@ -229,7 +229,7 @@ static void createInstance(void) instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instanceCreateInfo.pApplicationInfo = &appInfo; - instanceCreateInfo.ppEnabledExtensionNames = SDL_Vulkan_GetInstanceExtensions((int *)&instanceCreateInfo.enabledExtensionCount); + instanceCreateInfo.ppEnabledExtensionNames = SDL_Vulkan_GetInstanceExtensions(&instanceCreateInfo.enabledExtensionCount); result = vkCreateInstance(&instanceCreateInfo, NULL, &vulkanContext->instance); if (result != VK_SUCCESS) { vulkanContext->instance = VK_NULL_HANDLE; From ae0caeef8d6fdd3c702a19d8a84231af52c27e92 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 5 Mar 2024 13:54:17 -0800 Subject: [PATCH 213/220] Fixed build --- src/video/android/SDL_androidkeyboard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/android/SDL_androidkeyboard.c b/src/video/android/SDL_androidkeyboard.c index c77ae57a3c10b..a63d0b68a9c7e 100644 --- a/src/video/android/SDL_androidkeyboard.c +++ b/src/video/android/SDL_androidkeyboard.c @@ -313,7 +313,7 @@ static SDL_Scancode Android_Keycodes[] = { SDL_SCANCODE_PASTE, /* AKEYCODE_PASTE */ }; -static bool SDL_screen_keyboard_shown; +static SDL_bool SDL_screen_keyboard_shown; static SDL_Scancode TranslateKeycode(int keycode) { From 4c0601b93faa47700513b58fb015f0097592242b Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Tue, 5 Mar 2024 15:26:14 -0500 Subject: [PATCH 214/220] video: Return a failure code if the video driver fails to grab the mouse or keyboard Alter the video driver grab/confinement function signatures to return an int, set and return an error if the grab request fails, and clear the grab flags from the window if the mouse and/or keyboard wasn't actually grabbed. --- src/video/SDL_sysvideo.h | 6 ++--- src/video/SDL_video.c | 32 ++++++++++++++++++++++----- src/video/cocoa/SDL_cocoawindow.h | 4 ++-- src/video/cocoa/SDL_cocoawindow.m | 7 ++++-- src/video/haiku/SDL_bwindow.cc | 3 ++- src/video/haiku/SDL_bwindow.h | 2 +- src/video/vita/SDL_vitavideo.c | 3 ++- src/video/vita/SDL_vitavideo.h | 2 +- src/video/wayland/SDL_waylandevents.c | 6 ++--- src/video/wayland/SDL_waylandwindow.c | 20 +++++++++-------- src/video/wayland/SDL_waylandwindow.h | 6 ++--- src/video/windows/SDL_windowswindow.c | 10 ++++++--- src/video/windows/SDL_windowswindow.h | 6 ++--- src/video/x11/SDL_x11window.c | 21 ++++++++++++------ src/video/x11/SDL_x11window.h | 4 ++-- src/video/x11/SDL_x11xfixes.c | 4 +++- src/video/x11/SDL_x11xfixes.h | 2 +- 17 files changed, 89 insertions(+), 49 deletions(-) diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index dbae19bffd315..fe95eb4c71351 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -246,9 +246,9 @@ struct SDL_VideoDevice int (*SetWindowFullscreen)(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen); void *(*GetWindowICCProfile)(SDL_VideoDevice *_this, SDL_Window *window, size_t *size); SDL_DisplayID (*GetDisplayForWindow)(SDL_VideoDevice *_this, SDL_Window *window); - void (*SetWindowMouseRect)(SDL_VideoDevice *_this, SDL_Window *window); - void (*SetWindowMouseGrab)(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); - void (*SetWindowKeyboardGrab)(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); + int (*SetWindowMouseRect)(SDL_VideoDevice *_this, SDL_Window *window); + int (*SetWindowMouseGrab)(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); + int (*SetWindowKeyboardGrab)(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); void (*DestroyWindow)(SDL_VideoDevice *_this, SDL_Window *window); int (*CreateWindowFramebuffer)(SDL_VideoDevice *_this, SDL_Window *window, Uint32 *format, void **pixels, int *pitch); int (*UpdateWindowFramebuffer)(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rects, int numrects); diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 4ac4c5766d9f3..0f1c140198489 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -3309,24 +3309,36 @@ void SDL_UpdateWindowGrab(SDL_Window *window) } if (_this->SetWindowMouseGrab) { - _this->SetWindowMouseGrab(_this, window, mouse_grabbed); + if (_this->SetWindowMouseGrab(_this, window, mouse_grabbed) < 0) { + window->flags &= ~SDL_WINDOW_MOUSE_GRABBED; + } } if (_this->SetWindowKeyboardGrab) { - _this->SetWindowKeyboardGrab(_this, window, keyboard_grabbed); + if (_this->SetWindowKeyboardGrab(_this, window, keyboard_grabbed) < 0) { + window->flags &= ~SDL_WINDOW_KEYBOARD_GRABBED; + } + } + + if (_this->grabbed_window && !(_this->grabbed_window->flags & (SDL_WINDOW_MOUSE_GRABBED | SDL_WINDOW_KEYBOARD_GRABBED))) { + _this->grabbed_window = NULL; } } int SDL_SetWindowGrab(SDL_Window *window, SDL_bool grabbed) { + int ret_mouse_grab = 0; + int ret_keyboard_grab = 0; + CHECK_WINDOW_MAGIC(window, -1); CHECK_WINDOW_NOT_POPUP(window, -1); - SDL_SetWindowMouseGrab(window, grabbed); + ret_mouse_grab = SDL_SetWindowMouseGrab(window, grabbed); if (SDL_GetHintBoolean(SDL_HINT_GRAB_KEYBOARD, SDL_FALSE)) { - SDL_SetWindowKeyboardGrab(window, grabbed); + ret_keyboard_grab = SDL_SetWindowKeyboardGrab(window, grabbed); } - return 0; + + return (!ret_mouse_grab && !ret_keyboard_grab) ? 0 : -1; } int SDL_SetWindowKeyboardGrab(SDL_Window *window, SDL_bool grabbed) @@ -3352,6 +3364,10 @@ int SDL_SetWindowKeyboardGrab(SDL_Window *window, SDL_bool grabbed) window->flags &= ~SDL_WINDOW_KEYBOARD_GRABBED; } SDL_UpdateWindowGrab(window); + + if (grabbed && !(window->flags & SDL_WINDOW_KEYBOARD_GRABBED)) { + return -1; + } return 0; } @@ -3378,6 +3394,10 @@ int SDL_SetWindowMouseGrab(SDL_Window *window, SDL_bool grabbed) window->flags &= ~SDL_WINDOW_MOUSE_GRABBED; } SDL_UpdateWindowGrab(window); + + if (grabbed && !(window->flags & SDL_WINDOW_MOUSE_GRABBED)) { + return -1; + } return 0; } @@ -3419,7 +3439,7 @@ int SDL_SetWindowMouseRect(SDL_Window *window, const SDL_Rect *rect) } if (_this->SetWindowMouseRect) { - _this->SetWindowMouseRect(_this, window); + return _this->SetWindowMouseRect(_this, window); } return 0; } diff --git a/src/video/cocoa/SDL_cocoawindow.h b/src/video/cocoa/SDL_cocoawindow.h index ea1f739d62abf..59c7ce14d91aa 100644 --- a/src/video/cocoa/SDL_cocoawindow.h +++ b/src/video/cocoa/SDL_cocoawindow.h @@ -172,8 +172,8 @@ extern void Cocoa_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *windo extern int Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen); extern void *Cocoa_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size); extern SDL_DisplayID Cocoa_GetDisplayForWindow(SDL_VideoDevice *_this, SDL_Window *window); -extern void Cocoa_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window); -extern void Cocoa_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); +extern int Cocoa_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window); +extern int Cocoa_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); extern void Cocoa_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window); extern int Cocoa_SetWindowHitTest(SDL_Window *window, SDL_bool enabled); extern void Cocoa_AcceptDragAndDrop(SDL_Window *window, SDL_bool accept); diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m index e4466486323f4..585216b17c8f9 100644 --- a/src/video/cocoa/SDL_cocoawindow.m +++ b/src/video/cocoa/SDL_cocoawindow.m @@ -2753,12 +2753,13 @@ SDL_DisplayID Cocoa_GetDisplayForWindow(SDL_VideoDevice *_this, SDL_Window *wind } } -void Cocoa_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window) +int Cocoa_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window) { Cocoa_UpdateClipCursor(window); + return 0; } -void Cocoa_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) +int Cocoa_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) { @autoreleasepool { SDL_CocoaWindowData *data = (__bridge SDL_CocoaWindowData *)window->driverdata; @@ -2777,6 +2778,8 @@ void Cocoa_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bo } } } + + return 0; } void Cocoa_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) diff --git a/src/video/haiku/SDL_bwindow.cc b/src/video/haiku/SDL_bwindow.cc index e3ca49fc72b80..86436a0db84e9 100644 --- a/src/video/haiku/SDL_bwindow.cc +++ b/src/video/haiku/SDL_bwindow.cc @@ -166,8 +166,9 @@ void HAIKU_SetWindowMinimumSize(SDL_VideoDevice *_this, SDL_Window * window) { _ToBeWin(window)->PostMessage(&msg); } -void HAIKU_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window * window, SDL_bool grabbed) { +int HAIKU_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window * window, SDL_bool grabbed) { /* TODO: Implement this! */ + return SDL_Unsupported(); } void HAIKU_DestroyWindow(SDL_VideoDevice *_this, SDL_Window * window) { diff --git a/src/video/haiku/SDL_bwindow.h b/src/video/haiku/SDL_bwindow.h index 0e0325acf2763..93f7de15e3f81 100644 --- a/src/video/haiku/SDL_bwindow.h +++ b/src/video/haiku/SDL_bwindow.h @@ -38,7 +38,7 @@ extern void HAIKU_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window); extern void HAIKU_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool bordered); extern void HAIKU_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool resizable); extern int HAIKU_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen); -extern void HAIKU_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); +extern int HAIKU_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); extern void HAIKU_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window); #endif diff --git a/src/video/vita/SDL_vitavideo.c b/src/video/vita/SDL_vitavideo.c index 76bb9c777533a..bed1954776a5b 100644 --- a/src/video/vita/SDL_vitavideo.c +++ b/src/video/vita/SDL_vitavideo.c @@ -319,8 +319,9 @@ void VITA_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window) void VITA_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window) { } -void VITA_SetWindowGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) +int VITA_SetWindowGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) { + return 0; } void VITA_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) diff --git a/src/video/vita/SDL_vitavideo.h b/src/video/vita/SDL_vitavideo.h index 8f609425cfb88..610d0c0dee11d 100644 --- a/src/video/vita/SDL_vitavideo.h +++ b/src/video/vita/SDL_vitavideo.h @@ -72,7 +72,7 @@ void VITA_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window); void VITA_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window); void VITA_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window); void VITA_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window); -void VITA_SetWindowGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); +int VITA_SetWindowGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); void VITA_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window); #ifdef SDL_VIDEO_DRIVER_VITA diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c index b85c35919eb24..ae277a77edd11 100644 --- a/src/video/wayland/SDL_waylandevents.c +++ b/src/video/wayland/SDL_waylandevents.c @@ -3303,11 +3303,11 @@ int Wayland_input_confine_pointer(struct SDL_WaylandInput *input, SDL_Window *wi struct wl_region *confine_rect; if (!d->pointer_constraints) { - return -1; + return SDL_SetError("Failed to confine pointer: compositor lacks support for the required zwp_pointer_constraints_v1 protocol"); } if (!input->pointer) { - return -1; + return SDL_SetError("No pointer to confine"); } /* A confine may already be active, in which case we should destroy it and @@ -3375,7 +3375,7 @@ int Wayland_input_grab_keyboard(SDL_Window *window, struct SDL_WaylandInput *inp SDL_VideoData *d = input->display; if (!d->key_inhibitor_manager) { - return -1; + return SDL_SetError("Failed to grab keyboard: compositor lacks support for the required zwp_keyboard_shortcuts_inhibit_manager_v1 protocol"); } if (w->key_inhibitor) { diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index b4e45398737da..e211a7b1fa0c1 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -2095,7 +2095,7 @@ void Wayland_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window) } } -void Wayland_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window) +int Wayland_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window) { SDL_VideoData *data = _this->driverdata; @@ -2108,31 +2108,33 @@ void Wayland_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window) * lets you confine without a rect. */ if (SDL_RectEmpty(&window->mouse_rect) && !(window->flags & SDL_WINDOW_MOUSE_GRABBED)) { - Wayland_input_unconfine_pointer(data->input, window); + return Wayland_input_unconfine_pointer(data->input, window); } else { - Wayland_input_confine_pointer(data->input, window); + return Wayland_input_confine_pointer(data->input, window); } } -void Wayland_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) +int Wayland_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) { SDL_VideoData *data = _this->driverdata; if (grabbed) { - Wayland_input_confine_pointer(data->input, window); + return Wayland_input_confine_pointer(data->input, window); } else if (SDL_RectEmpty(&window->mouse_rect)) { - Wayland_input_unconfine_pointer(data->input, window); + return Wayland_input_unconfine_pointer(data->input, window); } + + return 0; } -void Wayland_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) +int Wayland_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) { SDL_VideoData *data = _this->driverdata; if (grabbed) { - Wayland_input_grab_keyboard(window, data->input); + return Wayland_input_grab_keyboard(window, data->input); } else { - Wayland_input_ungrab_keyboard(window); + return Wayland_input_ungrab_keyboard(window); } } diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h index c607c2e803470..afd348c2085d5 100644 --- a/src/video/wayland/SDL_waylandwindow.h +++ b/src/video/wayland/SDL_waylandwindow.h @@ -184,9 +184,9 @@ extern int Wayland_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *windo SDL_bool fullscreen); extern void Wayland_MaximizeWindow(SDL_VideoDevice *_this, SDL_Window *window); extern void Wayland_MinimizeWindow(SDL_VideoDevice *_this, SDL_Window *window); -extern void Wayland_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window); -extern void Wayland_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); -extern void Wayland_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); +extern int Wayland_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window); +extern int Wayland_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); +extern int Wayland_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); extern void Wayland_RestoreWindow(SDL_VideoDevice *_this, SDL_Window *window); extern void Wayland_SetWindowBordered(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool bordered); extern void Wayland_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool resizable); diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c index 0da83bb0ff2e1..572ee376dbefb 100644 --- a/src/video/windows/SDL_windowswindow.c +++ b/src/video/windows/SDL_windowswindow.c @@ -1376,23 +1376,27 @@ void WIN_UngrabKeyboard(SDL_Window *window) } } -void WIN_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window) +int WIN_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window) { WIN_UpdateClipCursor(window); + return 0; } -void WIN_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) +int WIN_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) { WIN_UpdateClipCursor(window); + return 0; } -void WIN_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) +int WIN_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) { if (grabbed) { WIN_GrabKeyboard(window); } else { WIN_UngrabKeyboard(window); } + + return 0; } #endif /*!defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)*/ diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h index 1e8fded4879b2..e498a92343086 100644 --- a/src/video/windows/SDL_windowswindow.h +++ b/src/video/windows/SDL_windowswindow.h @@ -111,9 +111,9 @@ extern void WIN_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, extern int WIN_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen); extern void WIN_UpdateWindowICCProfile(SDL_Window *window, SDL_bool send_event); extern void *WIN_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size); -extern void WIN_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window); -extern void WIN_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); -extern void WIN_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); +extern int WIN_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window); +extern int WIN_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); +extern int WIN_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); extern void WIN_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window); extern void WIN_OnWindowEnter(SDL_VideoDevice *_this, SDL_Window *window); extern void WIN_UpdateClipCursor(SDL_Window *window); diff --git a/src/video/x11/SDL_x11window.c b/src/video/x11/SDL_x11window.c index f055473601557..7fa3d3fe7f93d 100644 --- a/src/video/x11/SDL_x11window.c +++ b/src/video/x11/SDL_x11window.c @@ -1705,13 +1705,13 @@ void *X11_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t return ret_icc_profile_data; } -void X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) +int X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) { SDL_WindowData *data = window->driverdata; Display *display; if (!data) { - return; + return SDL_SetError("Invalid window data"); } data->mouse_grabbed = SDL_FALSE; @@ -1722,7 +1722,7 @@ void X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool so when we get a MapNotify later, we'll try to update the grab as appropriate. */ if (window->flags & SDL_WINDOW_HIDDEN) { - return; + return 0; } /* Try to grab the mouse */ @@ -1743,7 +1743,6 @@ void X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool } if (result != GrabSuccess) { - SDL_LogWarn(SDL_LOG_CATEGORY_VIDEO, "The X server refused to let us grab the mouse. You might experience input bugs."); data->videodata->broken_pointer_grab = SDL_TRUE; /* don't try again. */ } } @@ -1758,15 +1757,21 @@ void X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool X11_Xinput2UngrabTouch(_this, window); } X11_XSync(display, False); + + if (!data->videodata->broken_pointer_grab) { + return 0; + } else { + return SDL_SetError("The X server refused to let us grab the mouse. You might experience input bugs."); + } } -void X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) +int X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed) { SDL_WindowData *data = window->driverdata; Display *display; if (!data) { - return; + return SDL_SetError("Invalid window data"); } display = data->videodata->display; @@ -1776,7 +1781,7 @@ void X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_b so when we get a MapNotify later, we'll try to update the grab as appropriate. */ if (window->flags & SDL_WINDOW_HIDDEN) { - return; + return 0; } X11_XGrabKeyboard(display, data->xwindow, True, GrabModeAsync, @@ -1785,6 +1790,8 @@ void X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_b X11_XUngrabKeyboard(display, CurrentTime); } X11_XSync(display, False); + + return 0; } void X11_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window) diff --git a/src/video/x11/SDL_x11window.h b/src/video/x11/SDL_x11window.h index 84d92e59168d9..e0f7549b94d58 100644 --- a/src/video/x11/SDL_x11window.h +++ b/src/video/x11/SDL_x11window.h @@ -126,8 +126,8 @@ extern void X11_SetWindowResizable(SDL_VideoDevice *_this, SDL_Window *window, S extern void X11_SetWindowAlwaysOnTop(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool on_top); extern int X11_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen); extern void *X11_GetWindowICCProfile(SDL_VideoDevice *_this, SDL_Window *window, size_t *size); -extern void X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); -extern void X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); +extern int X11_SetWindowMouseGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); +extern int X11_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window, SDL_bool grabbed); extern void X11_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window); extern int X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled); extern void X11_AcceptDragAndDrop(SDL_Window *window, SDL_bool accept); diff --git a/src/video/x11/SDL_x11xfixes.c b/src/video/x11/SDL_x11xfixes.c index 362069f27dc78..d4fb65e68d8dd 100644 --- a/src/video/x11/SDL_x11xfixes.c +++ b/src/video/x11/SDL_x11xfixes.c @@ -84,7 +84,7 @@ int X11_GetXFixesSelectionNotifyEvent() return xfixes_selection_notify_event; } -void X11_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window) +int X11_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window) { if (SDL_RectEmpty(&window->mouse_rect)) { X11_ConfineCursorWithFlags(_this, window, NULL, 0); @@ -100,6 +100,8 @@ void X11_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window) wdata->pointer_barrier_active = SDL_TRUE; } } + + return 0; } int X11_ConfineCursorWithFlags(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rect, int flags) diff --git a/src/video/x11/SDL_x11xfixes.h b/src/video/x11/SDL_x11xfixes.h index c1daf4d3e6dcd..7c2fa90585a32 100644 --- a/src/video/x11/SDL_x11xfixes.h +++ b/src/video/x11/SDL_x11xfixes.h @@ -30,7 +30,7 @@ extern void X11_InitXfixes(SDL_VideoDevice *_this); extern int X11_XfixesIsInitialized(void); -extern void X11_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window); +extern int X11_SetWindowMouseRect(SDL_VideoDevice *_this, SDL_Window *window); extern int X11_ConfineCursorWithFlags(SDL_VideoDevice *_this, SDL_Window *window, const SDL_Rect *rect, int flags); extern void X11_DestroyPointerBarrier(SDL_VideoDevice *_this, SDL_Window *window); extern int X11_GetXFixesSelectionNotifyEvent(void); From b3858ec5f7f173991134efe21e35ad32a6b793ad Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 5 Mar 2024 15:48:29 -0800 Subject: [PATCH 215/220] BT2020_CL is very different from BT2020_NCL, and not currently supported --- include/SDL3/SDL_pixels.h | 2 +- src/render/metal/SDL_render_metal.m | 2 -- src/render/vulkan/SDL_render_vulkan.c | 2 -- src/video/SDL_pixels.c | 2 -- 4 files changed, 1 insertion(+), 7 deletions(-) diff --git a/include/SDL3/SDL_pixels.h b/include/SDL3/SDL_pixels.h index ac8f1e07aa495..868d1595134c8 100644 --- a/include/SDL3/SDL_pixels.h +++ b/include/SDL3/SDL_pixels.h @@ -560,7 +560,7 @@ typedef enum #define SDL_ISCOLORSPACE_YUV_BT601(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT601 || SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT470BG) #define SDL_ISCOLORSPACE_YUV_BT709(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT709) -#define SDL_ISCOLORSPACE_YUV_BT2020(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT2020_NCL || SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT2020_CL) +#define SDL_ISCOLORSPACE_YUV_BT2020(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT2020_NCL) #define SDL_ISCOLORSPACE_LIMITED_RANGE(X) (SDL_COLORSPACERANGE(X) != SDL_COLOR_RANGE_FULL) #define SDL_ISCOLORSPACE_FULL_RANGE(X) (SDL_COLORSPACERANGE(X) == SDL_COLOR_RANGE_FULL) diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index cddbfcfb9c2b6..bf5db74faf528 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -596,9 +596,7 @@ size_t GetYCbCRtoRGBConversionMatrix(SDL_Colorspace colorspace, int w, int h, in case SDL_MATRIX_COEFFICIENTS_BT709: return GetBT709ConversionMatrix(colorspace); - /* FIXME: Are these the same? */ case SDL_MATRIX_COEFFICIENTS_BT2020_NCL: - case SDL_MATRIX_COEFFICIENTS_BT2020_CL: return GetBT2020ConversionMatrix(colorspace); case SDL_MATRIX_COEFFICIENTS_UNSPECIFIED: diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 08b5c7dc6f068..d495cd5cc6784 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -2511,9 +2511,7 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD case SDL_MATRIX_COEFFICIENTS_BT709: samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR; break; - /* FIXME: Are these the same? */ case SDL_MATRIX_COEFFICIENTS_BT2020_NCL: - case SDL_MATRIX_COEFFICIENTS_BT2020_CL: samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR; break; case SDL_MATRIX_COEFFICIENTS_UNSPECIFIED: diff --git a/src/video/SDL_pixels.c b/src/video/SDL_pixels.c index 9de8dd81a22dd..c321d7ce30204 100644 --- a/src/video/SDL_pixels.c +++ b/src/video/SDL_pixels.c @@ -882,9 +882,7 @@ const float *SDL_GetYCbCRtoRGBConversionMatrix(SDL_Colorspace colorspace, int w, case SDL_MATRIX_COEFFICIENTS_BT709: return SDL_GetBT709ConversionMatrix(colorspace); - /* FIXME: Are these the same? */ case SDL_MATRIX_COEFFICIENTS_BT2020_NCL: - case SDL_MATRIX_COEFFICIENTS_BT2020_CL: return SDL_GetBT2020ConversionMatrix(colorspace); case SDL_MATRIX_COEFFICIENTS_UNSPECIFIED: From db2456038733940fc9d199f1ae800ee7885b63fa Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 5 Mar 2024 16:47:36 -0800 Subject: [PATCH 216/220] Additional colorspace clarification Note that SDL_MATRIX_COEFFICIENTS_BT470BG is functionally equivalent to SDL_MATRIX_COEFFICIENTS_BT601 --- include/SDL3/SDL_pixels.h | 60 +++++++++++++-------------- src/render/metal/SDL_render_metal.m | 1 + src/render/vulkan/SDL_render_vulkan.c | 1 + src/video/SDL_pixels.c | 1 + src/video/SDL_yuv.c | 18 ++++---- src/video/yuv2rgb/yuv_rgb_common.h | 8 ++-- 6 files changed, 46 insertions(+), 43 deletions(-) diff --git a/include/SDL3/SDL_pixels.h b/include/SDL3/SDL_pixels.h index 868d1595134c8..d3def2c41bf08 100644 --- a/include/SDL3/SDL_pixels.h +++ b/include/SDL3/SDL_pixels.h @@ -471,18 +471,18 @@ typedef enum typedef enum { SDL_COLOR_PRIMARIES_UNKNOWN = 0, - SDL_COLOR_PRIMARIES_BT709 = 1, + SDL_COLOR_PRIMARIES_BT709 = 1, /**< ITU-R BT.709-6 */ SDL_COLOR_PRIMARIES_UNSPECIFIED = 2, - SDL_COLOR_PRIMARIES_BT470M = 4, - SDL_COLOR_PRIMARIES_BT470BG = 5, - SDL_COLOR_PRIMARIES_BT601 = 6, - SDL_COLOR_PRIMARIES_SMPTE240 = 7, - SDL_COLOR_PRIMARIES_GENERIC_FILM = 8, - SDL_COLOR_PRIMARIES_BT2020 = 9, - SDL_COLOR_PRIMARIES_XYZ = 10, - SDL_COLOR_PRIMARIES_SMPTE431 = 11, - SDL_COLOR_PRIMARIES_SMPTE432 = 12, /* DCI P3 */ - SDL_COLOR_PRIMARIES_EBU3213 = 22, + SDL_COLOR_PRIMARIES_BT470M = 4, /**< ITU-R BT.470-6 System M */ + SDL_COLOR_PRIMARIES_BT470BG = 5, /**< ITU-R BT.470-6 System B, G / ITU-R BT.601-7 625 */ + SDL_COLOR_PRIMARIES_BT601 = 6, /**< ITU-R BT.601-7 525 */ + SDL_COLOR_PRIMARIES_SMPTE240 = 7, /**< SMPTE 240M, functionally the same as SDL_COLOR_PRIMARIES_BT601 */ + SDL_COLOR_PRIMARIES_GENERIC_FILM = 8, /**< Generic film (color filters using Illuminant C) */ + SDL_COLOR_PRIMARIES_BT2020 = 9, /**< ITU-R BT.2020-2 / ITU-R BT.2100-0 */ + SDL_COLOR_PRIMARIES_XYZ = 10, /**< SMPTE ST 428-1 */ + SDL_COLOR_PRIMARIES_SMPTE431 = 11, /**< SMPTE RP 431-2 */ + SDL_COLOR_PRIMARIES_SMPTE432 = 12, /**< SMPTE EG 432-1 / DCI P3 */ + SDL_COLOR_PRIMARIES_EBU3213 = 22, /**< EBU Tech. 3213-E */ SDL_COLOR_PRIMARIES_CUSTOM = 31 } SDL_ColorPrimaries; @@ -492,11 +492,11 @@ typedef enum typedef enum { SDL_TRANSFER_CHARACTERISTICS_UNKNOWN = 0, - SDL_TRANSFER_CHARACTERISTICS_BT709 = 1, /**< ITU-R BT1361 */ + SDL_TRANSFER_CHARACTERISTICS_BT709 = 1, /**< Rec. ITU-R BT.709-6 / ITU-R BT1361 */ SDL_TRANSFER_CHARACTERISTICS_UNSPECIFIED = 2, - SDL_TRANSFER_CHARACTERISTICS_GAMMA22 = 4, /**< ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM */ - SDL_TRANSFER_CHARACTERISTICS_GAMMA28 = 5, /**< ITU-R BT470BG */ - SDL_TRANSFER_CHARACTERISTICS_BT601 = 6, /**< SMPTE ST 170M */ + SDL_TRANSFER_CHARACTERISTICS_GAMMA22 = 4, /**< ITU-R BT.470-6 System M / ITU-R BT1700 625 PAL & SECAM */ + SDL_TRANSFER_CHARACTERISTICS_GAMMA28 = 5, /**< ITU-R BT.470-6 System B, G */ + SDL_TRANSFER_CHARACTERISTICS_BT601 = 6, /**< SMPTE ST 170M / ITU-R BT.601-7 525 or 625 */ SDL_TRANSFER_CHARACTERISTICS_SMPTE240 = 7, /**< SMPTE ST 240M */ SDL_TRANSFER_CHARACTERISTICS_LINEAR = 8, SDL_TRANSFER_CHARACTERISTICS_LOG100 = 9, @@ -508,7 +508,7 @@ typedef enum SDL_TRANSFER_CHARACTERISTICS_BT2020_12BIT = 15, /**< ITU-R BT2020 for 12-bit system */ SDL_TRANSFER_CHARACTERISTICS_PQ = 16, /**< SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems */ SDL_TRANSFER_CHARACTERISTICS_SMPTE428 = 17, /**< SMPTE ST 428-1 */ - SDL_TRANSFER_CHARACTERISTICS_HLG = 18, /**< ARIB STD-B67, known as "Hybrid log-gamma" */ + SDL_TRANSFER_CHARACTERISTICS_HLG = 18, /**< ARIB STD-B67, known as "hybrid log-gamma" (HLG) */ SDL_TRANSFER_CHARACTERISTICS_CUSTOM = 31 } SDL_TransferCharacteristics; @@ -518,19 +518,19 @@ typedef enum typedef enum { SDL_MATRIX_COEFFICIENTS_IDENTITY = 0, - SDL_MATRIX_COEFFICIENTS_BT709 = 1, + SDL_MATRIX_COEFFICIENTS_BT709 = 1, /**< ITU-R BT.709-6 */ SDL_MATRIX_COEFFICIENTS_UNSPECIFIED = 2, - SDL_MATRIX_COEFFICIENTS_FCC = 4, - SDL_MATRIX_COEFFICIENTS_BT470BG = 5, - SDL_MATRIX_COEFFICIENTS_BT601 = 6, - SDL_MATRIX_COEFFICIENTS_SMPTE240 = 7, + SDL_MATRIX_COEFFICIENTS_FCC = 4, /**< US FCC */ + SDL_MATRIX_COEFFICIENTS_BT470BG = 5, /**< ITU-R BT.470-6 System B, G / ITU-R BT.601-7 625, functionally the same as SDL_MATRIX_COEFFICIENTS_BT601 */ + SDL_MATRIX_COEFFICIENTS_BT601 = 6, /**< ITU-R BT.601-7 525 */ + SDL_MATRIX_COEFFICIENTS_SMPTE240 = 7, /**< SMPTE 240M */ SDL_MATRIX_COEFFICIENTS_YCGCO = 8, - SDL_MATRIX_COEFFICIENTS_BT2020_NCL = 9, - SDL_MATRIX_COEFFICIENTS_BT2020_CL = 10, - SDL_MATRIX_COEFFICIENTS_SMPTE2085 = 11, + SDL_MATRIX_COEFFICIENTS_BT2020_NCL = 9, /**< ITU-R BT.2020-2 non-constant luminance */ + SDL_MATRIX_COEFFICIENTS_BT2020_CL = 10, /**< ITU-R BT.2020-2 constant luminance */ + SDL_MATRIX_COEFFICIENTS_SMPTE2085 = 11, /**< SMPTE ST 2085 */ SDL_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL = 12, SDL_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL = 13, - SDL_MATRIX_COEFFICIENTS_ICTCP = 14, + SDL_MATRIX_COEFFICIENTS_ICTCP = 14, /**< ITU-R BT.2100-0 ICTCP */ SDL_MATRIX_COEFFICIENTS_CUSTOM = 31 } SDL_MatrixCoefficients; @@ -558,11 +558,11 @@ typedef enum #define SDL_COLORSPACETRANSFER(X) (SDL_TransferCharacteristics)(((X) >> 5) & 0x1F) #define SDL_COLORSPACEMATRIX(X) (SDL_MatrixCoefficients)((X) & 0x1F) -#define SDL_ISCOLORSPACE_YUV_BT601(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT601 || SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT470BG) -#define SDL_ISCOLORSPACE_YUV_BT709(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT709) -#define SDL_ISCOLORSPACE_YUV_BT2020(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT2020_NCL) -#define SDL_ISCOLORSPACE_LIMITED_RANGE(X) (SDL_COLORSPACERANGE(X) != SDL_COLOR_RANGE_FULL) -#define SDL_ISCOLORSPACE_FULL_RANGE(X) (SDL_COLORSPACERANGE(X) == SDL_COLOR_RANGE_FULL) +#define SDL_ISCOLORSPACE_MATRIX_BT601(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT601 || SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT470BG) +#define SDL_ISCOLORSPACE_MATRIX_BT709(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT709) +#define SDL_ISCOLORSPACE_MATRIX_BT2020_NCL(X) (SDL_COLORSPACEMATRIX(X) == SDL_MATRIX_COEFFICIENTS_BT2020_NCL) +#define SDL_ISCOLORSPACE_LIMITED_RANGE(X) (SDL_COLORSPACERANGE(X) != SDL_COLOR_RANGE_FULL) +#define SDL_ISCOLORSPACE_FULL_RANGE(X) (SDL_COLORSPACERANGE(X) == SDL_COLOR_RANGE_FULL) typedef enum { diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index bf5db74faf528..392e3e8158757 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -590,6 +590,7 @@ size_t GetYCbCRtoRGBConversionMatrix(SDL_Colorspace colorspace, int w, int h, in const int YUV_SD_THRESHOLD = 576; switch (SDL_COLORSPACEMATRIX(colorspace)) { + case SDL_MATRIX_COEFFICIENTS_BT470BG: case SDL_MATRIX_COEFFICIENTS_BT601: return GetBT601ConversionMatrix(colorspace); diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index d495cd5cc6784..078707f70163d 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -2505,6 +2505,7 @@ static int VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD /* Create samplerYcbcrConversion which will be used on the VkImageView and VkSampler */ samplerYcbcrConversionCreateInfo.format = textureFormat; switch (SDL_COLORSPACEMATRIX(texture->colorspace)) { + case SDL_MATRIX_COEFFICIENTS_BT470BG: case SDL_MATRIX_COEFFICIENTS_BT601: samplerYcbcrConversionCreateInfo.ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR; break; diff --git a/src/video/SDL_pixels.c b/src/video/SDL_pixels.c index c321d7ce30204..6553914eb4f8a 100644 --- a/src/video/SDL_pixels.c +++ b/src/video/SDL_pixels.c @@ -877,6 +877,7 @@ const float *SDL_GetYCbCRtoRGBConversionMatrix(SDL_Colorspace colorspace, int w, switch (SDL_COLORSPACEMATRIX(colorspace)) { case SDL_MATRIX_COEFFICIENTS_BT601: + case SDL_MATRIX_COEFFICIENTS_BT470BG: return SDL_GetBT601ConversionMatrix(colorspace); case SDL_MATRIX_COEFFICIENTS_BT709: diff --git a/src/video/SDL_yuv.c b/src/video/SDL_yuv.c index 2d1d001b26a53..127bc016612a7 100644 --- a/src/video/SDL_yuv.c +++ b/src/video/SDL_yuv.c @@ -161,25 +161,25 @@ int SDL_CalculateYUVSize(Uint32 format, int w, int h, size_t *size, size_t *pitc static int GetYUVConversionType(SDL_Colorspace colorspace, YCbCrType *yuv_type) { - if (SDL_ISCOLORSPACE_YUV_BT601(colorspace)) { + if (SDL_ISCOLORSPACE_MATRIX_BT601(colorspace)) { if (SDL_ISCOLORSPACE_LIMITED_RANGE(colorspace)) { - *yuv_type = YCBCR_601; + *yuv_type = YCBCR_601_LIMITED; } else { - *yuv_type = YCBCR_JPEG; + *yuv_type = YCBCR_601_FULL; } return 0; } - if (SDL_ISCOLORSPACE_YUV_BT709(colorspace)) { + if (SDL_ISCOLORSPACE_MATRIX_BT709(colorspace)) { if (SDL_ISCOLORSPACE_LIMITED_RANGE(colorspace)) { - *yuv_type = YCBCR_709; + *yuv_type = YCBCR_709_LIMITED; return 0; } } - if (SDL_ISCOLORSPACE_YUV_BT2020(colorspace)) { + if (SDL_ISCOLORSPACE_MATRIX_BT2020_NCL(colorspace)) { if (SDL_ISCOLORSPACE_FULL_RANGE(colorspace)) { - *yuv_type = YCBCR_2020; + *yuv_type = YCBCR_2020_NCL_FULL; return 0; } } @@ -593,7 +593,7 @@ int SDL_ConvertPixels_YUV_to_RGB(int width, int height, const Uint8 *v = NULL; Uint32 y_stride = 0; Uint32 uv_stride = 0; - YCbCrType yuv_type = YCBCR_601; + YCbCrType yuv_type = YCBCR_601_LIMITED; if (GetYUVPlanes(width, height, src_format, src, src_pitch, &y, &u, &v, &y_stride, &uv_stride) < 0) { return -1; @@ -1121,7 +1121,7 @@ int SDL_ConvertPixels_RGB_to_YUV(int width, int height, Uint32 src_format, SDL_Colorspace src_colorspace, SDL_PropertiesID src_properties, const void *src, int src_pitch, Uint32 dst_format, SDL_Colorspace dst_colorspace, SDL_PropertiesID dst_properties, void *dst, int dst_pitch) { - YCbCrType yuv_type = YCBCR_601; + YCbCrType yuv_type = YCBCR_601_LIMITED; if (GetYUVConversionType(dst_colorspace, &yuv_type) < 0) { return -1; diff --git a/src/video/yuv2rgb/yuv_rgb_common.h b/src/video/yuv2rgb/yuv_rgb_common.h index 1ab607d015282..774292b0cd0fd 100644 --- a/src/video/yuv2rgb/yuv_rgb_common.h +++ b/src/video/yuv2rgb/yuv_rgb_common.h @@ -5,10 +5,10 @@ typedef enum { - YCBCR_JPEG, - YCBCR_601, - YCBCR_709, - YCBCR_2020 + YCBCR_601_FULL, + YCBCR_601_LIMITED, + YCBCR_709_LIMITED, + YCBCR_2020_NCL_FULL, } YCbCrType; #endif /* YUV_RGB_COMMON_H_ */ From e268cdbec67cd52dd835797d75ec1eef2e326dac Mon Sep 17 00:00:00 2001 From: Susko3 Date: Wed, 6 Mar 2024 01:52:15 +0100 Subject: [PATCH 217/220] Use specific types in public headers (#9205) Uses specific typedef'd types instead of generic integral types where applicable. --- include/SDL3/SDL_events.h | 62 +++++++++++++++++++-------------------- include/SDL3/SDL_pen.h | 4 +-- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index c60b351e6bd2e..a36a863f8bfa3 100644 --- a/include/SDL3/SDL_events.h +++ b/include/SDL3/SDL_events.h @@ -571,18 +571,18 @@ typedef struct SDL_TouchFingerEvent */ typedef struct SDL_PenTipEvent { - Uint32 type; /**< ::SDL_EVENT_PEN_DOWN or ::SDL_EVENT_PEN_UP */ + Uint32 type; /**< ::SDL_EVENT_PEN_DOWN or ::SDL_EVENT_PEN_UP */ Uint32 reserved; - Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ - Uint32 windowID; /**< The window with pen focus, if any */ - SDL_PenID which; /**< The pen instance id */ - Uint8 tip; /**< ::SDL_PEN_TIP_INK when using a regular pen tip, or ::SDL_PEN_TIP_ERASER if the pen is being used as an eraser (e.g., flipped to use the eraser tip) */ - Uint8 state; /**< ::SDL_PRESSED on ::SDL_EVENT_PEN_DOWN and ::SDL_RELEASED on ::SDL_EVENT_PEN_UP */ - Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.), - ::SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and - ::SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */ - float x; /**< X coordinate, relative to window */ - float y; /**< Y coordinate, relative to window */ + Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ + SDL_WindowID windowID; /**< The window with pen focus, if any */ + SDL_PenID which; /**< The pen instance id */ + Uint8 tip; /**< ::SDL_PEN_TIP_INK when using a regular pen tip, or ::SDL_PEN_TIP_ERASER if the pen is being used as an eraser (e.g., flipped to use the eraser tip) */ + Uint8 state; /**< ::SDL_PRESSED on ::SDL_EVENT_PEN_DOWN and ::SDL_RELEASED on ::SDL_EVENT_PEN_UP */ + Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.), + ::SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and + ::SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */ + float x; /**< X coordinate, relative to window */ + float y; /**< Y coordinate, relative to window */ float axes[SDL_PEN_NUM_AXES]; /**< Pen axes such as pressure and tilt (ordered as per ::SDL_PenAxis) */ } SDL_PenTipEvent; @@ -591,18 +591,18 @@ typedef struct SDL_PenTipEvent */ typedef struct SDL_PenMotionEvent { - Uint32 type; /**< ::SDL_EVENT_PEN_MOTION */ + Uint32 type; /**< ::SDL_EVENT_PEN_MOTION */ Uint32 reserved; - Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ - Uint32 windowID; /**< The window with pen focus, if any */ - SDL_PenID which; /**< The pen instance id */ + Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ + SDL_WindowID windowID; /**< The window with pen focus, if any */ + SDL_PenID which; /**< The pen instance id */ Uint8 padding1; Uint8 padding2; - Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.), - ::SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and - ::SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */ - float x; /**< X coordinate, relative to window */ - float y; /**< Y coordinate, relative to window */ + Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.), + ::SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and + ::SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */ + float x; /**< X coordinate, relative to window */ + float y; /**< Y coordinate, relative to window */ float axes[SDL_PEN_NUM_AXES]; /**< Pen axes such as pressure and tilt (ordered as per ::SDL_PenAxis) */ } SDL_PenMotionEvent; @@ -611,18 +611,18 @@ typedef struct SDL_PenMotionEvent */ typedef struct SDL_PenButtonEvent { - Uint32 type; /**< ::SDL_EVENT_PEN_BUTTON_DOWN or ::SDL_EVENT_PEN_BUTTON_UP */ + Uint32 type; /**< ::SDL_EVENT_PEN_BUTTON_DOWN or ::SDL_EVENT_PEN_BUTTON_UP */ Uint32 reserved; - Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ - Uint32 windowID; /**< The window with pen focus, if any */ - SDL_PenID which; /**< The pen instance id */ - Uint8 button; /**< The pen button index (1 represents the pen tip for compatibility with mouse events) */ - Uint8 state; /**< ::SDL_PRESSED or ::SDL_RELEASED */ - Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.), - ::SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and - ::SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */ - float x; /**< X coordinate, relative to window */ - float y; /**< Y coordinate, relative to window */ + Uint64 timestamp; /**< In nanoseconds, populated using SDL_GetTicksNS() */ + SDL_WindowID windowID; /**< The window with pen focus, if any */ + SDL_PenID which; /**< The pen instance id */ + Uint8 button; /**< The pen button index (1 represents the pen tip for compatibility with mouse events) */ + Uint8 state; /**< ::SDL_PRESSED or ::SDL_RELEASED */ + Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.), + ::SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and + ::SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */ + float x; /**< X coordinate, relative to window */ + float y; /**< Y coordinate, relative to window */ float axes[SDL_PEN_NUM_AXES]; /**< Pen axes such as pressure and tilt (ordered as per ::SDL_PenAxis) */ } SDL_PenButtonEvent; diff --git a/include/SDL3/SDL_pen.h b/include/SDL3/SDL_pen.h index 36c67b7cf7d29..469c04ce69d3a 100644 --- a/include/SDL3/SDL_pen.h +++ b/include/SDL3/SDL_pen.h @@ -56,9 +56,9 @@ extern "C" { typedef Uint32 SDL_PenID; /**< SDL_PenIDs identify pens uniquely within a session */ -#define SDL_PEN_INVALID ((Uint32)0) /**< Reserved invalid ::SDL_PenID is valid */ +#define SDL_PEN_INVALID ((SDL_PenID)0) /**< Reserved invalid ::SDL_PenID is valid */ -#define SDL_PEN_MOUSEID ((Uint32)-2) /**< Device ID for mouse events triggered by pen events */ +#define SDL_PEN_MOUSEID ((SDL_PenID)-2) /**< Device ID for mouse events triggered by pen events */ #define SDL_PEN_INFO_UNKNOWN (-1) /**< Marks unknown information when querying the pen */ From 95fbbc6f074c6842491156857fe3ec18d1e8e742 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 5 Mar 2024 16:55:26 -0800 Subject: [PATCH 218/220] Fixed accidental use of tabs --- include/SDL3/SDL_events.h | 12 +++--------- include/SDL3/SDL_pen.h | 4 ++-- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/include/SDL3/SDL_events.h b/include/SDL3/SDL_events.h index a36a863f8bfa3..80709f9ec71f6 100644 --- a/include/SDL3/SDL_events.h +++ b/include/SDL3/SDL_events.h @@ -578,9 +578,7 @@ typedef struct SDL_PenTipEvent SDL_PenID which; /**< The pen instance id */ Uint8 tip; /**< ::SDL_PEN_TIP_INK when using a regular pen tip, or ::SDL_PEN_TIP_ERASER if the pen is being used as an eraser (e.g., flipped to use the eraser tip) */ Uint8 state; /**< ::SDL_PRESSED on ::SDL_EVENT_PEN_DOWN and ::SDL_RELEASED on ::SDL_EVENT_PEN_UP */ - Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.), - ::SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and - ::SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */ + Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.), ::SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and ::SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */ float x; /**< X coordinate, relative to window */ float y; /**< Y coordinate, relative to window */ float axes[SDL_PEN_NUM_AXES]; /**< Pen axes such as pressure and tilt (ordered as per ::SDL_PenAxis) */ @@ -598,9 +596,7 @@ typedef struct SDL_PenMotionEvent SDL_PenID which; /**< The pen instance id */ Uint8 padding1; Uint8 padding2; - Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.), - ::SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and - ::SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */ + Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.), ::SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and ::SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */ float x; /**< X coordinate, relative to window */ float y; /**< Y coordinate, relative to window */ float axes[SDL_PEN_NUM_AXES]; /**< Pen axes such as pressure and tilt (ordered as per ::SDL_PenAxis) */ @@ -618,9 +614,7 @@ typedef struct SDL_PenButtonEvent SDL_PenID which; /**< The pen instance id */ Uint8 button; /**< The pen button index (1 represents the pen tip for compatibility with mouse events) */ Uint8 state; /**< ::SDL_PRESSED or ::SDL_RELEASED */ - Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.), - ::SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and - ::SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */ + Uint16 pen_state; /**< Pen button masks (where SDL_BUTTON(1) is the first button, SDL_BUTTON(2) is the second button etc.), ::SDL_PEN_DOWN_MASK is set if the pen is touching the surface, and ::SDL_PEN_ERASER_MASK is set if the pen is (used as) an eraser. */ float x; /**< X coordinate, relative to window */ float y; /**< Y coordinate, relative to window */ float axes[SDL_PEN_NUM_AXES]; /**< Pen axes such as pressure and tilt (ordered as per ::SDL_PenAxis) */ diff --git a/include/SDL3/SDL_pen.h b/include/SDL3/SDL_pen.h index 469c04ce69d3a..dd1b1970f614e 100644 --- a/include/SDL3/SDL_pen.h +++ b/include/SDL3/SDL_pen.h @@ -78,9 +78,9 @@ typedef enum { SDL_PEN_AXIS_PRESSURE = 0, /**< Pen pressure. Unidirectional: 0..1.0 */ SDL_PEN_AXIS_XTILT, /**< Pen horizontal tilt angle. Bidirectional: -90.0..90.0 (left-to-right). - The physical max/min tilt may be smaller than -90.0 / 90.0, cf. SDL_PenCapabilityInfo */ + The physical max/min tilt may be smaller than -90.0 / 90.0, cf. SDL_PenCapabilityInfo */ SDL_PEN_AXIS_YTILT, /**< Pen vertical tilt angle. Bidirectional: -90.0..90.0 (top-to-down). - The physical max/min tilt may be smaller than -90.0 / 90.0, cf. SDL_PenCapabilityInfo */ + The physical max/min tilt may be smaller than -90.0 / 90.0, cf. SDL_PenCapabilityInfo */ SDL_PEN_AXIS_DISTANCE, /**< Pen distance to drawing surface. Unidirectional: 0.0..1.0 */ SDL_PEN_AXIS_ROTATION, /**< Pen barrel rotation. Bidirectional: -180..179.9 (clockwise, 0 is facing up, -180.0 is facing down). */ SDL_PEN_AXIS_SLIDER, /**< Pen finger wheel or slider (e.g., Airbrush Pen). Unidirectional: 0..1.0 */ From 4545c77c9ef8d62c922174880b60aa31f19e97f0 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 5 Mar 2024 16:56:58 -0800 Subject: [PATCH 219/220] Updated Vita renderer with colorspace clarification --- src/render/vitagxm/SDL_render_vita_gxm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/render/vitagxm/SDL_render_vita_gxm.c b/src/render/vitagxm/SDL_render_vita_gxm.c index ea93cb1f39ba6..9cb743ca4f65c 100644 --- a/src/render/vitagxm/SDL_render_vita_gxm.c +++ b/src/render/vitagxm/SDL_render_vita_gxm.c @@ -340,13 +340,13 @@ static void VITA_GXM_SetYUVProfile(SDL_Renderer *renderer, SDL_Texture *texture) { VITA_GXM_RenderData *data = (VITA_GXM_RenderData *)renderer->driverdata; int ret = 0; - if (SDL_ISCOLORSPACE_YUV_BT601(texture->colorspace)) { + if (SDL_ISCOLORSPACE_MATRIX_BT601(texture->colorspace)) { if (SDL_ISCOLORSPACE_LIMITED_RANGE(texture->colorspace)) { ret = sceGxmSetYuvProfile(data->gxm_context, 0, SCE_GXM_YUV_PROFILE_BT601_STANDARD); } else { ret = sceGxmSetYuvProfile(data->gxm_context, 0, SCE_GXM_YUV_PROFILE_BT601_FULL_RANGE); } - } else if (SDL_ISCOLORSPACE_YUV_BT709(texture->colorspace)) { + } else if (SDL_ISCOLORSPACE_MATRIX_BT709(texture->colorspace)) { if (SDL_ISCOLORSPACE_LIMITED_RANGE(texture->colorspace)) { ret = sceGxmSetYuvProfile(data->gxm_context, 0, SCE_GXM_YUV_PROFILE_BT709_STANDARD); } else { From ee87132385014449c4cd33236c661d57539071c1 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 5 Mar 2024 17:43:08 -0800 Subject: [PATCH 220/220] Make sure fast path RGB <-> YUV conversions are using the same color primaries --- src/video/SDL_yuv.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/video/SDL_yuv.c b/src/video/SDL_yuv.c index 127bc016612a7..93a97b5ed86fd 100644 --- a/src/video/SDL_yuv.c +++ b/src/video/SDL_yuv.c @@ -593,26 +593,29 @@ int SDL_ConvertPixels_YUV_to_RGB(int width, int height, const Uint8 *v = NULL; Uint32 y_stride = 0; Uint32 uv_stride = 0; - YCbCrType yuv_type = YCBCR_601_LIMITED; if (GetYUVPlanes(width, height, src_format, src, src_pitch, &y, &u, &v, &y_stride, &uv_stride) < 0) { return -1; } - if (GetYUVConversionType(src_colorspace, &yuv_type) < 0) { - return -1; - } + if (SDL_COLORSPACEPRIMARIES(src_colorspace) == SDL_COLORSPACEPRIMARIES(dst_colorspace)) { + YCbCrType yuv_type = YCBCR_601_LIMITED; - if (yuv_rgb_sse(src_format, dst_format, width, height, y, u, v, y_stride, uv_stride, (Uint8 *)dst, dst_pitch, yuv_type)) { - return 0; - } + if (GetYUVConversionType(src_colorspace, &yuv_type) < 0) { + return -1; + } - if (yuv_rgb_lsx(src_format, dst_format, width, height, y, u, v, y_stride, uv_stride, (Uint8 *)dst, dst_pitch, yuv_type)) { - return 0; - } + if (yuv_rgb_sse(src_format, dst_format, width, height, y, u, v, y_stride, uv_stride, (Uint8 *)dst, dst_pitch, yuv_type)) { + return 0; + } - if (yuv_rgb_std(src_format, dst_format, width, height, y, u, v, y_stride, uv_stride, (Uint8 *)dst, dst_pitch, yuv_type)) { - return 0; + if (yuv_rgb_lsx(src_format, dst_format, width, height, y, u, v, y_stride, uv_stride, (Uint8 *)dst, dst_pitch, yuv_type)) { + return 0; + } + + if (yuv_rgb_std(src_format, dst_format, width, height, y, u, v, y_stride, uv_stride, (Uint8 *)dst, dst_pitch, yuv_type)) { + return 0; + } } /* No fast path for the RGB format, instead convert using an intermediate buffer */ @@ -1146,12 +1149,14 @@ int SDL_ConvertPixels_RGB_to_YUV(int width, int height, #endif /* ARGB8888 to FOURCC */ - if (src_format == SDL_PIXELFORMAT_ARGB8888) { + if (src_format == SDL_PIXELFORMAT_ARGB8888 && + SDL_COLORSPACEPRIMARIES(src_colorspace) == SDL_COLORSPACEPRIMARIES(dst_colorspace)) { return SDL_ConvertPixels_ARGB8888_to_YUV(width, height, src, src_pitch, dst_format, dst, dst_pitch, yuv_type); } if (dst_format == SDL_PIXELFORMAT_P010) { - if (src_format == SDL_PIXELFORMAT_XBGR2101010) { + if (src_format == SDL_PIXELFORMAT_XBGR2101010 && + SDL_COLORSPACEPRIMARIES(src_colorspace) == SDL_COLORSPACEPRIMARIES(dst_colorspace)) { return SDL_ConvertPixels_XBGR2101010_to_P010(width, height, src, src_pitch, dst_format, dst, dst_pitch, yuv_type); }